Skip to content

Commit 38b54b4

Browse files
committed
Refactor MMIO API
- Allow multiple regions to be mapped - Expose the MMIO pointer to allow the protection to be altered and to facilitate unsafe accesses - Expose MMIO file descriptors to allow manual MMAP calls - Specify endianess at access time rather than when mapped to allow or differing endianess registers - Combine global/per-PASID MMIO map calls Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
1 parent 70e8b22 commit 38b54b4

10 files changed

Lines changed: 700 additions & 554 deletions

File tree

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ A typical use of libocxl will follow this pattern:
5050
handle in which the caller can store additional information. This is not used by OpenCAPI,
5151
but is passed as part of the event information to provide additional context to the IRQ handler.
5252
4. **Configure global MMIO:** Some AFUs may have a global MMIO area, which will contain configuration
53-
information that will affect all PASIDs on the AFU. Use ocxl\_global\_mmio\_map to make the area available,
54-
then use ocxl\_global\_mmio\_write32() and ocxl\_global\_mmio\_write64() to write the information.
53+
information that will affect all PASIDs on the AFU. Use ocxl\_mmio\_map to make the area available,
54+
then use ocxl\_mmio\_write32() and ocxl\_mmio\_write64() to write the information.
5555
5. **Configure the per-PASID MMIO:** Some AFUs support multiple contexts, and each context will
5656
get it's own MMIO area for configuration and communication. Typical information that may
5757
be communicated across the MMIO interface include IRQ handles (obtained with
@@ -64,10 +64,10 @@ A typical use of libocxl will follow this pattern:
6464
8. **Handle AFU IRQs:** Pending IRQs can be queried using ocxl\_afu\_event\_check(). An IRQ event
6565
contains the IRQ number, the info pointer assigned when activated, the 64 bit IRQ handle, and
6666
the number of times the IRQ has been triggered since last checked.
67-
9. **Read results:** Work completion may be signally by the AFU via an IRQ, or by writing to
67+
9. **Read results:** Work completion may be signalled by the AFU via an IRQ, or by writing to
6868
the MMIO area. Typically, bulk data should be written to a pointer passed to the AFU, however,
69-
small quantities of data may be read from the MMIO area using ocxl\_global\_mmio\_read32() and
70-
ocxl\_global\_mmio\_read64().
69+
small quantities of data may be read from an MMIO area using ocxl\_mmio\_read32() and
70+
ocxl\_mmio\_read64().
7171
10. **Termination:** ocxl\_afu\_free() will free all resources associated with an AFU handle.
7272

7373
# Development

src/afu.c

Lines changed: 26 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -116,44 +116,6 @@ void ocxl_afu_get_version(ocxl_afu_h afu, uint8_t *major, uint8_t *minor)
116116
*minor = my_afu->version_minor;
117117
}
118118

119-
/**
120-
* Get the file descriptor of an opened AFU
121-
* @param afu The AFU to get the descriptor of
122-
* @return the file descriptor
123-
*/
124-
int ocxl_afu_get_fd(ocxl_afu_h afu)
125-
{
126-
ocxl_afu *my_afu = (ocxl_afu *) afu;
127-
128-
return my_afu->fd;
129-
}
130-
131-
/**
132-
* Get the size of the global MMIO region for an AFU
133-
*
134-
* @param afu the AFU to get the MMIO size of
135-
* @return the size of the global MMIO region
136-
*/
137-
size_t ocxl_afu_get_global_mmio_size(ocxl_afu_h afu)
138-
{
139-
ocxl_afu *my_afu = (ocxl_afu *) afu;
140-
141-
return my_afu->global_mmio.length;
142-
}
143-
144-
/**
145-
* Get the size of the per-PASID MMIO region for an AFU
146-
*
147-
* @param afu the AFU to get the MMIO size of
148-
* @return the size of the per-PASID MMIO region
149-
*/
150-
size_t ocxl_afu_get_mmio_size(ocxl_afu_h afu)
151-
{
152-
ocxl_afu *my_afu = (ocxl_afu *) afu;
153-
154-
return my_afu->per_pasid_mmio.length;
155-
}
156-
157119
/**
158120
* @}
159121
*
@@ -250,19 +212,23 @@ static void afu_init(ocxl_afu * afu)
250212
afu->epoll_event_count = 0;
251213
afu->global_mmio_fd = -1;
252214

253-
afu->global_mmio.endianess = OCXL_MMIO_HOST_ENDIAN;
254215
afu->global_mmio.start = NULL;
255216
afu->global_mmio.length = 0;
217+
afu->global_mmio.type = OCXL_GLOBAL_MMIO;
256218

257-
afu->per_pasid_mmio.endianess = OCXL_MMIO_HOST_ENDIAN;
258219
afu->per_pasid_mmio.start = NULL;
259220
afu->per_pasid_mmio.length = 0;
221+
afu->per_pasid_mmio.type = OCXL_PER_PASID_MMIO;
260222

261223
afu->page_size = sysconf(_SC_PAGESIZE);
262224

263225
afu->irqs = NULL;
264226
afu->irq_count = 0;
265-
afu->irq_size = 0;
227+
afu->irq_max_count = 0;
228+
229+
afu->mmios = NULL;
230+
afu->mmio_count = 0;
231+
afu->mmio_max_count = 0;
266232

267233
afu->pasid = UINT32_MAX;
268234

@@ -422,22 +388,30 @@ static ocxl_err afu_open(ocxl_afu *afu)
422388
return OCXL_ALREADY_DONE;
423389
}
424390

391+
ocxl_err rc;
392+
425393
int fd = open(afu->device_path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
426394
if (fd < 0) {
427395
if (errno == ENOSPC) {
428-
ocxl_err rc = OCXL_NO_MORE_CONTEXTS;
396+
rc = OCXL_NO_MORE_CONTEXTS;
429397
errmsg(afu, rc, "Could not open AFU device '%s', the maximum number of contexts has been reached: Error %d: %s",
430398
afu->device_path, errno, strerror(errno));
431399
return rc;
432400
}
433401

434-
ocxl_err rc = OCXL_NO_DEV;
402+
rc = OCXL_NO_DEV;
435403
errmsg(afu, rc, "Could not open AFU device '%s': Error %d: %s", afu->device_path, errno, strerror(errno));
436404
return rc;
437405
}
438406

439407
afu->fd = fd;
440408

409+
rc = global_mmio_open(afu);
410+
if (rc != OCXL_OK) {
411+
errmsg(afu, rc, "Could not open global MMIO descriptor");
412+
return rc;
413+
}
414+
441415
fd = epoll_create1(EPOLL_CLOEXEC);
442416
if (fd < 0) {
443417
ocxl_err rc = OCXL_NO_DEV;
@@ -597,7 +571,6 @@ ocxl_err ocxl_afu_open_specific(const char *name, const char *physical_function,
597571
goto end;
598572
}
599573

600-
601574
for (int dev = 0; dev < glob_data.gl_pathc; dev++) {
602575
const char *dev_path = glob_data.gl_pathv[dev];
603576
ret = ocxl_afu_open_from_dev(dev_path, afu);
@@ -684,8 +657,14 @@ ocxl_err ocxl_afu_close(ocxl_afu_h afu)
684657
return OCXL_ALREADY_DONE;
685658
}
686659

687-
ocxl_mmio_unmap(afu);
688-
ocxl_global_mmio_unmap(afu);
660+
for (uint16_t mmio_idx = 0; mmio_idx < my_afu->mmio_count; mmio_idx++) {
661+
ocxl_mmio_unmap((ocxl_mmio_h)&my_afu->mmios[mmio_idx]);
662+
}
663+
664+
if (my_afu->global_mmio_fd) {
665+
close(my_afu->global_mmio_fd);
666+
my_afu->global_mmio_fd = -1;
667+
}
689668

690669
if (my_afu->irqs) {
691670
for (uint16_t irq = 0; irq < my_afu->irq_count; irq++) {
@@ -695,7 +674,7 @@ ocxl_err ocxl_afu_close(ocxl_afu_h afu)
695674
free(my_afu->irqs);
696675
my_afu->irqs = NULL;
697676
my_afu->irq_count = 0;
698-
my_afu->irq_size = 0;
677+
my_afu->irq_max_count = 0;
699678
}
700679

701680
if (my_afu->epoll_events) {

src/include/libocxl.h

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
#include <stdio.h>
2222
#include <limits.h>
2323
#include <sys/select.h>
24+
#include <sys/mman.h> // Required for PROT_* for MMIO map calls
25+
#include <endian.h> // Required for htobe32 & friends in MMIO access wrappers
2426

2527
#ifdef __cplusplus
2628
extern "C" {
@@ -40,6 +42,14 @@ typedef enum {
4042
OCXL_MMIO_HOST_ENDIAN = 2, /**< AFU data is the same endianess as the host */
4143
} ocxl_endian;
4244

45+
/**
46+
* Defines the type of an MMIO area
47+
*/
48+
typedef enum {
49+
OCXL_GLOBAL_MMIO,
50+
OCXL_PER_PASID_MMIO
51+
} ocxl_mmio_type;
52+
4353
#define AFU_NAME_MAX 24 /**< The maximum length of an AFU name */
4454

4555
/**
@@ -62,6 +72,12 @@ typedef void *ocxl_afu_h;
6272
*/
6373
typedef uint16_t ocxl_irq_h;
6474

75+
/**
76+
* A handle for an MMIO region on an AFU
77+
*/
78+
typedef void *ocxl_mmio_h;
79+
80+
6581
/**
6682
* Potential return values from ocxl_* functions
6783
*/
@@ -75,6 +91,7 @@ typedef enum {
7591
OCXL_ALREADY_DONE = -6, /**< The action requested has already been performed */
7692
OCXL_OUT_OF_BOUNDS = -7, /**< The action requested falls outside the permitted area */
7793
OCXL_NO_MORE_CONTEXTS = -8, /**< No more contexts can be opened on the AFU */
94+
OCXL_INVALID_ARGS = -9, /**< One or more arguments are invalid */
7895
/* Adding something? Update setup.c: ocxl_err_to_string too */
7996
} ocxl_err;
8097

@@ -134,9 +151,6 @@ const ocxl_identifier *ocxl_afu_get_identifier(ocxl_afu_h afu);
134151
const char *ocxl_afu_get_device_path(ocxl_afu_h afu);
135152
const char *ocxl_afu_get_sysfs_path(ocxl_afu_h afu);
136153
void ocxl_afu_get_version(ocxl_afu_h afu, uint8_t *major, uint8_t *minor);
137-
int ocxl_afu_get_fd(ocxl_afu_h afu);
138-
size_t ocxl_afu_get_global_mmio_size(ocxl_afu_h afu);
139-
size_t ocxl_afu_get_mmio_size(ocxl_afu_h afu);
140154
uint32_t ocxl_afu_get_pasid(ocxl_afu_h afu);
141155

142156
/* AFU operations */
@@ -154,6 +168,7 @@ ocxl_err ocxl_afu_attach(ocxl_afu_h afu);
154168
/* AFU IRQ functions */
155169
ocxl_err ocxl_afu_irq_alloc(ocxl_afu_h afu, void *info, ocxl_irq_h * irq_handle);
156170
uint64_t ocxl_afu_irq_get_id(ocxl_afu_h afu, ocxl_irq_h irq);
171+
int ocxl_afu_get_event_fd(ocxl_afu_h afu);
157172
int ocxl_afu_event_check_versioned(ocxl_afu_h afu, int timeout, ocxl_event *events, uint16_t event_count,
158173
uint16_t event_api_version);
159174

@@ -188,22 +203,18 @@ ocxl_err ocxl_afu_set_ppc64_amr(ocxl_afu_h afu, uint64_t amr);
188203
#endif
189204

190205
/* mmio.c */
191-
ocxl_err ocxl_global_mmio_map(ocxl_afu_h afu, ocxl_endian endian);
192-
ocxl_err ocxl_mmio_map(ocxl_afu_h afu, ocxl_endian endian);
193-
194-
ocxl_err ocxl_global_mmio_read32(ocxl_afu_h afu, size_t offset, uint32_t * out);
195-
ocxl_err ocxl_global_mmio_read64(ocxl_afu_h afu, size_t offset, uint64_t * out);
196-
ocxl_err ocxl_global_mmio_write32(ocxl_afu_h afu, size_t offset, uint32_t val);
197-
ocxl_err ocxl_global_mmio_write64(ocxl_afu_h afu, size_t offset, uint64_t val);
198-
199-
ocxl_err ocxl_mmio_read32(ocxl_afu_h afu, size_t offset, uint32_t * out);
200-
ocxl_err ocxl_mmio_read64(ocxl_afu_h afu, size_t offset, uint64_t * out);
201-
ocxl_err ocxl_mmio_write32(ocxl_afu_h afu, size_t offset, uint32_t val);
202-
ocxl_err ocxl_mmio_write64(ocxl_afu_h afu, size_t offset, uint64_t val);
203-
204-
/* MMIO low level API */
205-
void ocxl_global_mmio_unmap(ocxl_afu_h afu);
206-
void ocxl_mmio_unmap(ocxl_afu_h afu);
206+
size_t ocxl_afu_get_mmio_size(ocxl_afu_h afu, ocxl_mmio_type type);
207+
ocxl_err ocxl_mmio_map_advanced(ocxl_afu_h afu, ocxl_mmio_type type, size_t size, int prot, uint64_t flags,
208+
off_t offset, ocxl_mmio_h *region);
209+
ocxl_err ocxl_mmio_map(ocxl_afu_h afu, ocxl_mmio_type type, ocxl_mmio_h *region);
210+
void ocxl_mmio_unmap(ocxl_mmio_h region);
211+
int ocxl_mmio_get_fd(ocxl_afu_h afu, ocxl_mmio_type type);
212+
size_t ocxl_mmio_size(ocxl_afu_h afu, ocxl_mmio_type type);
213+
ocxl_err ocxl_mmio_get_info(ocxl_mmio_h region, void **address, size_t *size);
214+
ocxl_err ocxl_mmio_read32(ocxl_mmio_h mmio, off_t offset, ocxl_endian endian, uint32_t * out);
215+
ocxl_err ocxl_mmio_read64(ocxl_mmio_h mmio, off_t offset, ocxl_endian endian, uint64_t * out);
216+
ocxl_err ocxl_mmio_write32(ocxl_mmio_h mmio, off_t offset, ocxl_endian endian, uint32_t value);
217+
ocxl_err ocxl_mmio_write64(ocxl_mmio_h mmio, off_t offset, ocxl_endian endian, uint64_t value);
207218

208219
#ifdef __cplusplus
209220
}

src/internal.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,29 @@ void ocxl_default_afu_error_handler(ocxl_afu_h afu, ocxl_err error, const char *
131131
fprintf(stderr, "ERROR: %s\t%s: %s\n", dev, ocxl_err_to_string(error), message);
132132
pthread_mutex_unlock(&stderr_mutex);
133133
}
134+
135+
/**
136+
* Grow a buffer geometrically
137+
*
138+
* @param afu the AFU that owns the buffer
139+
* @param buffer [in/out] the buffer to grow
140+
* @param [in/out] the number of elements in the buffer
141+
* @param size the size of a buffer element
142+
* @param initial_count the initial number of elements in the buffer
143+
*/
144+
ocxl_err grow_buffer(ocxl_afu *afu, void **buffer, uint16_t *count, size_t size, size_t initial_count)
145+
{
146+
size_t new_count = (*count > 0) ? 2 * *count : initial_count;
147+
void *temp = realloc(*buffer, new_count * size);
148+
if (temp == NULL) {
149+
ocxl_err rc = OCXL_NO_MEM;
150+
errmsg(afu, rc, "Could not realloc buffer to %lu elements of %lu bytes (%lu bytes total): %d '%s'",
151+
new_count, size, new_count * size, errno, strerror(errno));
152+
return rc;
153+
}
154+
155+
*buffer = temp;
156+
*count = new_count;
157+
158+
return OCXL_OK;
159+
}

src/irq.c

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -163,17 +163,12 @@ ocxl_err ocxl_afu_irq_alloc(ocxl_afu_h afu, void *info, ocxl_irq_h * irq)
163163
{
164164
ocxl_afu *my_afu = (ocxl_afu *) afu;
165165

166-
if (my_afu->irq_count == my_afu->irq_size) {
167-
size_t new_size = (my_afu->irq_size > 0) ? 2 * my_afu->irq_size : INITIAL_IRQ_COUNT;
168-
ocxl_irq *irqs = realloc(my_afu->irqs, new_size * sizeof(ocxl_irq));
169-
if (irqs == NULL) {
170-
ocxl_err rc = OCXL_NO_MEM;
171-
errmsg(my_afu, rc, "Could not realloc IRQs for afu '%s' to %d IRQs",
172-
my_afu->identifier.afu_name, new_size);
166+
if (my_afu->irq_count == my_afu->irq_max_count) {
167+
ocxl_err rc = grow_buffer(my_afu, (void **)&my_afu->irqs, &my_afu->irq_max_count, sizeof(ocxl_irq), INITIAL_IRQ_COUNT);
168+
if (rc != OCXL_OK) {
169+
errmsg(my_afu, rc, "Could not grow IRQ buffer for AFU '%s'", my_afu->identifier.afu_name);
173170
return rc;
174171
}
175-
my_afu->irqs = irqs;
176-
my_afu->irq_size = new_size;
177172
}
178173

179174
ocxl_err rc = irq_allocate(my_afu, &my_afu->irqs[my_afu->irq_count], info);
@@ -209,6 +204,23 @@ uint64_t ocxl_afu_irq_get_id(ocxl_afu_h afu, ocxl_irq_h irq)
209204
return (uint64_t)my_afu->irqs[irq].addr;
210205
}
211206

207+
/**
208+
* Get a descriptor that will trigger a poll when an AFU event occurs
209+
*
210+
* When triggered, call ocxl_read_afu_event() to extract the event information
211+
*
212+
* @pre the AFU has been opened
213+
* @see ocxl_afu_event_check()
214+
* @param afu the AFU the IRQ belongs to
215+
* @return the handle
216+
*/
217+
int ocxl_afu_get_event_fd(ocxl_afu_h afu)
218+
{
219+
ocxl_afu *my_afu = (ocxl_afu *) afu;
220+
221+
return my_afu->fd;
222+
}
223+
212224
typedef struct ocxl_kernel_event_header ocxl_kernel_event_header;
213225
typedef struct ocxl_kernel_event_xsl_fault_error ocxl_kernel_event_xsl_fault_error;
214226

src/libocxl_internal.h

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ void trace_message(const char *label, const char *file, int line, const char *fu
6363
void errmsg(ocxl_afu *afu, ocxl_err error, const char *format, ...);
6464
void ocxl_default_error_handler(ocxl_err error, const char *message);
6565
void ocxl_default_afu_error_handler(ocxl_afu_h afu, ocxl_err error, const char *message);
66+
ocxl_err grow_buffer(ocxl_afu *afu, void **buffer, uint16_t *count, size_t size, size_t initial_count);
67+
ocxl_err global_mmio_open(ocxl_afu *afu);
6668

6769
extern const char *sys_path;
6870
#define SYS_PATH_DEFAULT "/sys/class/ocxl"
@@ -73,6 +75,7 @@ extern const char *dev_path;
7375
#define DEVICE_PATH ((UNLIKELY(dev_path != NULL)) ? dev_path : DEV_PATH_DEFAULT)
7476

7577
#define INITIAL_IRQ_COUNT 64
78+
#define INITIAL_MMIO_COUNT 4
7679

7780
/**
7881
* @internal
@@ -81,7 +84,8 @@ extern const char *dev_path;
8184
typedef struct ocxl_mmio_area {
8285
char *start; /**< The first addressable byte of the area */
8386
size_t length; /**< The size of the area in bytes */
84-
ocxl_endian endianess; /**< The endianess of the MMIO area */
87+
ocxl_mmio_type type; /**< The type of the area */
88+
ocxl_afu *afu; /**< The AFU this MMIO area belongs to */
8589
} ocxl_mmio_area;
8690

8791

@@ -109,7 +113,7 @@ typedef struct epoll_fd_source {
109113
struct ocxl_irq {
110114
struct ocxl_ioctl_irq_fd event; /**< The event descriptor */
111115
uint16_t irq_number; /**< The 0 indexed IRQ number */
112-
void *addr; /**< The mmaped address of the IRQ page */
116+
void *addr; /**< The mmapped address of the IRQ page */
113117
void *info; /**< Additional info to pass to the user */
114118
epoll_fd_source fd_info; /**< Epoll information for this IRQ */
115119
};
@@ -137,7 +141,12 @@ struct ocxl_afu {
137141
size_t page_size;
138142
ocxl_irq *irqs;
139143
uint16_t irq_count; /**< The number of valid IRQs */
140-
uint16_t irq_size; /**< The maximum number of IRQs available */
144+
uint16_t irq_max_count; /**< The maximum number of IRQs available */
145+
146+
ocxl_mmio_area *mmios;
147+
uint16_t mmio_count; /**< The number of valid MMIO regions */
148+
uint16_t mmio_max_count; /**< The maximum number of MMIO regions available */
149+
141150
uint32_t pasid;
142151

143152
bool verbose_errors;
@@ -162,7 +171,6 @@ typedef enum ocxl_event_action {
162171
OCXL_EVENT_ACTION_IGNORE, /**< The event read was successful, but should be ignored */
163172
} ocxl_event_action;
164173

165-
bool ocxl_afu_mmio_sizes(ocxl_afu * afu);
166174
void irq_dealloc(ocxl_afu * afu, ocxl_irq * irq);
167175

168176
#endif /* _LIBOCXL_INTERNAL_H */

0 commit comments

Comments
 (0)