17#include "libocxl_internal.h"
24#include <sys/eventfd.h>
27#include <sys/select.h>
29#include <sys/sysmacros.h>
77 return &afu->identifier;
93 return afu->device_path;
107 return afu->sysfs_path;
123 *major = afu->version_major;
124 *minor = afu->version_minor;
176 const char *message))
178 afu->error_handler = handler;
208static void afu_init(ocxl_afu *afu)
210 memset((
char *)afu->identifier.afu_name,
'\0',
sizeof(afu->identifier.afu_name));
211 afu->device_path = NULL;
212 afu->sysfs_path = NULL;
213 afu->version_major = 0;
214 afu->version_minor = 0;
216 afu->fd_info.type = EPOLL_SOURCE_OCXL;
217 afu->fd_info.irq = NULL;
219 afu->epoll_events = NULL;
220 afu->epoll_event_count = 0;
221 afu->global_mmio_fd = -1;
223 afu->global_mmio.start = NULL;
224 afu->global_mmio.length = 0;
227 afu->per_pasid_mmio.start = NULL;
228 afu->per_pasid_mmio.length = 0;
231 afu->page_size = sysconf(_SC_PAGESIZE);
235 afu->irq_max_count = 0;
239 afu->mmio_max_count = 0;
241 afu->pasid = UINT32_MAX;
243 afu->verbose_errors = verbose_errors_all;
244 afu->error_handler = ocxl_default_afu_error_handler;
246 afu->tracing = tracing_all;
248 afu->attached =
false;
267 ocxl_afu *afu = malloc(
sizeof(ocxl_afu));
270 errmsg(NULL, rc,
"Could not allocate %d bytes for AFU",
sizeof(ocxl_afu));
290static bool device_matches(
int dirfd,
char *dev_name, dev_t dev)
294 if (fstatat(dirfd, dev_name, &sb, 0) == -1) {
298 if (!S_ISCHR(sb.st_mode)) {
302 return dev == sb.st_rdev;
313static bool populate_metadata(dev_t dev, ocxl_afu *afu)
316 struct dirent *dev_ent;
318 dev_dir = opendir(DEVICE_PATH);
320 if (dev_dir == NULL) {
324 int fd = dirfd(dev_dir);
326 if (!(dev_ent = readdir(dev_dir))) {
330 }
while (!device_matches(fd, dev_ent->d_name, dev));
333 char *physical_function = strchr(dev_ent->d_name,
'.');
334 if (physical_function == NULL) {
335 errmsg(NULL,
OCXL_INTERNAL_ERROR,
"Could not extract physical function from device name '%s', missing initial '.'",
339 int afu_name_len = physical_function - dev_ent->d_name;
341 errmsg(NULL,
OCXL_INTERNAL_ERROR,
"AFU name '%-.*s' exceeds maximum length of %d", afu_name_len, dev_ent->d_name);
347 uint8_t bus, device, function;
348 int found = sscanf(physical_function,
"%x:%hhu:%hhu.%hhu.%hhu",
349 &domain, &bus, &device, &function, &afu->identifier.afu_index);
352 errmsg(NULL,
OCXL_INTERNAL_ERROR,
"Could not parse physical function '%s', only got %d components", physical_function,
357 memcpy((
char *)afu->identifier.afu_name, dev_ent->d_name, afu_name_len);
358 ((
char *)afu->identifier.afu_name)[afu_name_len] =
'\0';
360 size_t dev_path_len = strlen(DEVICE_PATH) + 1 + strlen(dev_ent->d_name) + 1;
361 afu->device_path = malloc(dev_path_len);
362 if (NULL == afu->device_path) {
363 errmsg(NULL,
OCXL_INTERNAL_ERROR,
"Could not allocate %llu bytes for device path", dev_path_len);
366 (void)snprintf(afu->device_path, dev_path_len,
"%s/%s", DEVICE_PATH, dev_ent->d_name);
368 size_t sysfs_path_len = strlen(SYS_PATH) + 1 + strlen(dev_ent->d_name) + 1;
369 afu->sysfs_path = malloc(sysfs_path_len);
370 if (NULL == afu->sysfs_path) {
371 errmsg(NULL,
OCXL_INTERNAL_ERROR,
"Could not allocate %llu bytes for sysfs path", sysfs_path_len);
374 (void)snprintf(afu->sysfs_path, sysfs_path_len,
"%s/%s", SYS_PATH, dev_ent->d_name);
384static void trace_metadata(ocxl_afu *afu)
386 TRACE_OPEN(
"device path=\"%s\"", afu->device_path);
387 TRACE_OPEN(
"sysfs path=\"%s\"", afu->sysfs_path);
388 TRACE_OPEN(
"AFU Name=\"%s\"", afu->identifier.afu_name);
389 TRACE_OPEN(
"AFU Index=%u", afu->identifier.afu_index);
390 TRACE_OPEN(
"AFU Version=%u:%u", afu->version_major, afu->version_minor);
391 TRACE_OPEN(
"Global MMIO size=%llu", afu->global_mmio.length);
392 TRACE_OPEN(
"Per PASID MMIO size=%llu", afu->per_pasid_mmio.length);
393 TRACE_OPEN(
"Page Size=%llu", afu->page_size);
394 TRACE_OPEN(
"PASID=%lu", afu->pasid);
407static ocxl_err afu_open(ocxl_afu *afu)
415 int fd = open(afu->device_path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
417 if (errno == ENOSPC) {
419 errmsg(NULL, rc,
"Could not open AFU device '%s', the maximum number of contexts has been reached: Error %d: %s",
420 afu->device_path, errno, strerror(errno));
425 errmsg(NULL, rc,
"Could not open AFU device '%s': Error %d: %s", afu->device_path, errno, strerror(errno));
433 errmsg(NULL, rc,
"Could not open global MMIO descriptor");
437 fd = epoll_create1(EPOLL_CLOEXEC);
440 errmsg(NULL, rc,
"Could not create epoll descriptor. Error %d: %s",
441 errno, strerror(errno));
446 struct epoll_event ev;
448 ev.data.ptr = &afu->fd_info;
449 if (epoll_ctl(afu->epoll_fd, EPOLL_CTL_ADD, afu->fd, &ev) == -1) {
451 errmsg(NULL, rc,
"Could not add device fd %d to epoll fd %d: %d: '%s'",
452 afu->fd, afu->epoll_fd, errno, strerror(errno));
456 struct ocxl_ioctl_metadata metadata;
457 if (ioctl(afu->fd, OCXL_IOCTL_GET_METADATA, &metadata)) {
459 errmsg(NULL, rc,
"OCXL_IOCTL_GET_METADATA failed %d:%s", errno, strerror(errno));
464 afu->version_major = metadata.afu_version_major;
465 afu->version_minor = metadata.afu_version_minor;
466 afu->per_pasid_mmio.length = metadata.pp_mmio_size;
467 afu->global_mmio.length = metadata.global_mmio_size;
468 afu->pasid = metadata.pasid;
492 struct stat dev_stats;
493 if (stat(path, &dev_stats)) {
495 errmsg(NULL, rc,
"Could not stat AFU device '%s': Error %d: %s", path, errno, strerror(errno));
499 if (!populate_metadata(dev_stats.st_rdev, afu_h)) {
501 errmsg(NULL, rc,
"Could not find OCXL device for '%s', major=%d, minor=%d, device expected in '%s'",
502 path, major(dev_stats.st_rdev), minor(dev_stats.st_rdev), DEVICE_PATH);
532 ocxl_err rc = get_afu_by_path(path, afu);
539 rc = afu_open((ocxl_afu *)*afu);
564 char pattern[PATH_MAX];
571 if (afu_index == -1) {
572 snprintf(pattern,
sizeof(pattern),
"%s/%s.%s.*",
574 physical_function ? physical_function :
"*");
576 snprintf(pattern,
sizeof(pattern),
"%s/%s.%s.%d",
578 physical_function ? physical_function :
"*",
582 int rc = glob(pattern, GLOB_ERR, NULL, &glob_data);
588 errmsg(NULL, ret,
"No memory for glob while listing AFUs");
592 errmsg(NULL, ret,
"No OCXL devices found in '%s' with pattern '%s'", DEVICE_PATH, pattern);
595 errmsg(NULL, ret,
"Glob error %d while listing AFUs", rc);
599 for (
size_t dev = 0; dev < glob_data.gl_pathc; dev++) {
600 const char *dev_path = glob_data.gl_pathv[dev];
616 globfree(&glob_data);
658 errmsg(afu, rc,
"Attempted to attach a closed AFU context");
662 struct ocxl_ioctl_attach attach_args;
663 memset(&attach_args,
'\0',
sizeof(attach_args));
665 attach_args.amr = afu->ppc64_amr;
668 if (ioctl(afu->fd, OCXL_IOCTL_ATTACH, &attach_args)) {
670 errmsg(afu, rc,
"OCXL_IOCTL_ATTACH failed %d:%s", errno, strerror(errno));
674 afu->attached =
true;
698 for (uint16_t mmio_idx = 0; mmio_idx < afu->mmio_count; mmio_idx++) {
702 if (afu->global_mmio_fd != -1) {
703 close(afu->global_mmio_fd);
704 afu->global_mmio_fd = -1;
708 for (uint16_t irq = 0; irq < afu->irq_count; irq++) {
715 afu->irq_max_count = 0;
718 if (afu->epoll_events) {
719 free(afu->epoll_events);
720 afu->epoll_event_count = 0;
723 close(afu->epoll_fd);
728 afu->attached =
false;
730 if (afu->device_path) {
731 free(afu->device_path);
732 afu->device_path = NULL;
735 if (afu->sysfs_path) {
736 free(afu->sysfs_path);
737 afu->sysfs_path = NULL;
771 afu->ppc64_amr = amr;
uint32_t ocxl_afu_get_pasid(ocxl_afu_h afu)
Get the PASID for the currently open context.
const char * ocxl_afu_get_sysfs_path(ocxl_afu_h afu)
Get the canonical sysfs path of the AFU.
const char * ocxl_afu_get_device_path(ocxl_afu_h afu)
Get the canonical device path of the AFU.
const ocxl_identifier * ocxl_afu_get_identifier(ocxl_afu_h afu)
Get the identifier of the AFU.
void ocxl_afu_get_version(ocxl_afu_h afu, uint8_t *major, uint8_t *minor)
Get the version of the AFU.
ocxl_err ocxl_afu_open(const char *name, ocxl_afu_h *afu)
Open an AFU context with a specified name.
ocxl_err ocxl_afu_close(ocxl_afu_h afu)
Close an AFU and detach it from the context.
ocxl_err ocxl_afu_open_from_dev(const char *path, ocxl_afu_h *afu)
Open an AFU context at a specified path.
ocxl_err ocxl_afu_attach(ocxl_afu_h afu, __attribute__((unused)) uint64_t flags)
Attach the calling process's memory to an open AFU context.
ocxl_err ocxl_afu_open_specific(const char *name, const char *physical_function, int16_t afu_index, ocxl_afu_h *afu)
Open an AFU context with a specified name on a specific card/afu index.
void ocxl_afu_set_error_message_handler(ocxl_afu_h afu, void(*handler)(ocxl_afu_h afu, ocxl_err error, const char *message))
Override the default handler for emitting error messages for an AFU.
void ocxl_afu_enable_messages(ocxl_afu_h afu, uint64_t sources)
Enable messages from an AFU.
void ocxl_mmio_unmap(ocxl_mmio_h region)
Unmap an MMIO region from an AFU.
ocxl_err global_mmio_open(ocxl_afu *afu)
Open the global MMIO descriptor on an AFU.
ocxl_err ocxl_afu_set_ppc64_amr(ocxl_afu_h afu, uint64_t amr)
Set the PPC64-specific PSL AMR register value for restricting access to the AFU.
void irq_dealloc(ocxl_afu *afu, ocxl_irq *irq)
Deallocate a single IRQ.
ocxl_err
Potential return values from ocxl_* functions.
@ OCXL_NO_MORE_CONTEXTS
No more contexts can be opened on the AFU.
@ OCXL_NO_DEV
The OpenCAPI device is not available.
@ OCXL_INTERNAL_ERROR
an internal error has occurred
@ OCXL_OK
The call succeeded.
@ OCXL_ALREADY_DONE
The action requested has already been performed.
@ OCXL_NO_MEM
An out of memory error occurred.
@ OCXL_NO_CONTEXT
The call requires an open context on the AFU.
struct ocxl_afu * ocxl_afu_h
A handle for an AFU.
#define OCXL_INVALID_AFU
An invalid AFU handle.
struct ocxl_mmio_area * ocxl_mmio_h
A handle for an MMIO region on an AFU.
#define OCXL_TRACING
Tracing requested.
#define AFU_NAME_MAX
The maximum length of an AFU name.
#define OCXL_ERRORS
Error messages requested.
AFU identification information.