libocxl
afu.c
Go to the documentation of this file.
1 /*
2  * Copyright 2017 International Business Machines
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "libocxl_internal.h"
18 #include <dirent.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <stdbool.h>
22 #include <unistd.h>
23 #include <misc/ocxl.h>
24 #include <sys/eventfd.h>
25 #include <sys/ioctl.h>
26 #include <sys/mman.h>
27 #include <sys/select.h>
28 #include <sys/stat.h>
29 #include <sys/sysmacros.h>
30 #include <sys/types.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <sys/epoll.h>
34 #include <glob.h>
35 #include <ctype.h>
36 
61 {
62  return afu->pasid;
63 }
64 
65 
76 {
77  return &afu->identifier;
78 }
79 
92 {
93  return afu->device_path;
94 }
95 
106 {
107  return afu->sysfs_path;
108 }
109 
121 void ocxl_afu_get_version(ocxl_afu_h afu, uint8_t *major, uint8_t *minor)
122 {
123  *major = afu->version_major;
124  *minor = afu->version_minor;
125 }
126 
151 void ocxl_afu_enable_messages(ocxl_afu_h afu, uint64_t sources)
152 {
153  afu->verbose_errors = !!(sources & OCXL_ERRORS);
154  afu->tracing = !!(sources & OCXL_TRACING);
155 }
156 
176  const char *message))
177 {
178  afu->error_handler = handler;
179 }
180 
208 static void afu_init(ocxl_afu *afu)
209 {
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;
215  afu->fd = -1;
216  afu->fd_info.type = EPOLL_SOURCE_OCXL;
217  afu->fd_info.irq = NULL;
218  afu->epoll_fd = -1;
219  afu->epoll_events = NULL;
220  afu->epoll_event_count = 0;
221  afu->global_mmio_fd = -1;
222 
223  afu->global_mmio.start = NULL;
224  afu->global_mmio.length = 0;
225  afu->global_mmio.type = OCXL_GLOBAL_MMIO;
226 
227  afu->per_pasid_mmio.start = NULL;
228  afu->per_pasid_mmio.length = 0;
229  afu->per_pasid_mmio.type = OCXL_PER_PASID_MMIO;
230 
231  afu->page_size = sysconf(_SC_PAGESIZE);
232 
233  afu->irqs = NULL;
234  afu->irq_count = 0;
235  afu->irq_max_count = 0;
236 
237  afu->mmios = NULL;
238  afu->mmio_count = 0;
239  afu->mmio_max_count = 0;
240 
241  afu->pasid = UINT32_MAX;
242 
243  afu->verbose_errors = verbose_errors_all;
244  afu->error_handler = ocxl_default_afu_error_handler;
245 
246  afu->tracing = tracing_all;
247 
248  afu->attached = false;
249 
250 #ifdef _ARCH_PPC64
251  afu->ppc64_amr = 0;
252 #endif
253 }
254 
265 static ocxl_err afu_alloc(ocxl_afu_h *afu_out)
266 {
267  ocxl_afu *afu = malloc(sizeof(ocxl_afu));
268  if (afu == NULL) {
269  ocxl_err rc = OCXL_NO_MEM;
270  errmsg(NULL, rc, "Could not allocate %d bytes for AFU", sizeof(ocxl_afu));
271  return rc;
272  }
273 
274  afu_init(afu);
275 
276  *afu_out = afu;
277 
278  return OCXL_OK;
279 }
280 
290 static bool device_matches(int dirfd, char *dev_name, dev_t dev)
291 {
292  struct stat sb;
293 
294  if (fstatat(dirfd, dev_name, &sb, 0) == -1) {
295  return false;
296  }
297 
298  if (!S_ISCHR(sb.st_mode)) {
299  return false;
300  }
301 
302  return dev == sb.st_rdev;
303 }
304 
313 static bool populate_metadata(dev_t dev, ocxl_afu *afu)
314 {
315  DIR *dev_dir;
316  struct dirent *dev_ent;
317 
318  dev_dir = opendir(DEVICE_PATH);
319 
320  if (dev_dir == NULL) {
321  return false;
322  }
323 
324  int fd = dirfd(dev_dir);
325  do {
326  if (!(dev_ent = readdir(dev_dir))) {
327  closedir(dev_dir);
328  return false;
329  }
330  } while (!device_matches(fd, dev_ent->d_name, dev));
331  closedir(dev_dir);
332 
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 '.'",
336  dev_ent->d_name);
337  return false;
338  }
339  int afu_name_len = physical_function - dev_ent->d_name;
340  if (afu_name_len > AFU_NAME_MAX) {
341  errmsg(NULL, OCXL_INTERNAL_ERROR,"AFU name '%-.*s' exceeds maximum length of %d", afu_name_len, dev_ent->d_name);
342  return false;
343  }
344 
345  physical_function++;
346  uint16_t domain;
347  uint8_t bus, device, function;
348  int found = sscanf(physical_function, "%hu:%hhu:%hhu.%hhu.%hhu",
349  &domain, &bus, &device, &function, &afu->identifier.afu_index);
350 
351  if (found != 5) {
352  errmsg(NULL, OCXL_INTERNAL_ERROR, "Could not parse physical function '%s', only got %d components", physical_function,
353  found);
354  return false;
355  }
356 
357  memcpy((char *)afu->identifier.afu_name, dev_ent->d_name, afu_name_len);
358  ((char *)afu->identifier.afu_name)[afu_name_len] = '\0';
359 
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);
364  return false;
365  }
366  (void)snprintf(afu->device_path, dev_path_len, "%s/%s", DEVICE_PATH, dev_ent->d_name);
367 
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);
372  return false;
373  }
374  (void)snprintf(afu->sysfs_path, sysfs_path_len, "%s/%s", SYS_PATH, dev_ent->d_name);
375 
376  return true;
377 }
378 
384 static void trace_metadata(ocxl_afu *afu)
385 {
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);
395 }
396 
407 static ocxl_err afu_open(ocxl_afu *afu)
408 {
409  if (afu->fd != -1) {
410  return OCXL_ALREADY_DONE;
411  }
412 
413  ocxl_err rc;
414 
415  int fd = open(afu->device_path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
416  if (fd < 0) {
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));
421  return rc;
422  }
423 
424  rc = OCXL_NO_DEV;
425  errmsg(NULL, rc, "Could not open AFU device '%s': Error %d: %s", afu->device_path, errno, strerror(errno));
426  return rc;
427  }
428 
429  afu->fd = fd;
430 
431  rc = global_mmio_open(afu);
432  if (rc != OCXL_OK) {
433  errmsg(NULL, rc, "Could not open global MMIO descriptor");
434  return rc;
435  }
436 
437  fd = epoll_create1(EPOLL_CLOEXEC);
438  if (fd < 0) {
439  ocxl_err rc = OCXL_NO_DEV;
440  errmsg(NULL, rc, "Could not create epoll descriptor. Error %d: %s",
441  errno, strerror(errno));
442  return rc;
443  }
444  afu->epoll_fd = fd;
445 
446  struct epoll_event ev;
447  ev.events = EPOLLIN;
448  ev.data.ptr = &afu->fd_info; // Already set up in afu_init
449  if (epoll_ctl(afu->epoll_fd, EPOLL_CTL_ADD, afu->fd, &ev) == -1) {
450  ocxl_err rc = OCXL_NO_DEV;
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));
453  return rc;
454  }
455 
456  struct ocxl_ioctl_metadata metadata;
457  if (ioctl(afu->fd, OCXL_IOCTL_GET_METADATA, &metadata)) {
458  ocxl_err rc = OCXL_NO_DEV;
459  errmsg(NULL, rc, "OCXL_IOCTL_GET_METADATA failed %d:%s", errno, strerror(errno));
460  return rc;
461  }
462 
463  // Metadata version 0, always available
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;
469 
470  trace_metadata(afu);
471 
472  return OCXL_OK;
473 }
474 
485 static ocxl_err get_afu_by_path(const char *path, ocxl_afu_h *afu)
486 {
487  ocxl_afu_h afu_h;
488  ocxl_err rc = afu_alloc(&afu_h);
489  if (rc != OCXL_OK)
490  goto err;
491 
492  struct stat dev_stats;
493  if (stat(path, &dev_stats)) {
494  rc = OCXL_NO_DEV;
495  errmsg(NULL, rc, "Could not stat AFU device '%s': Error %d: %s", path, errno, strerror(errno));
496  goto err_free;
497  }
498 
499  if (!populate_metadata(dev_stats.st_rdev, afu_h)) {
500  rc = OCXL_NO_DEV;
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);
503  goto err_free;
504  }
505 
506  *afu = afu_h;
507 
508  return OCXL_OK;
509 
510 err_free:
511  free(afu_h);
512 err:
513  *afu = OCXL_INVALID_AFU;
514  return rc;
515 }
516 
529 {
530  libocxl_init();
531 
532  ocxl_err rc = get_afu_by_path(path, afu);
533 
534  if (rc != OCXL_OK) {
535  *afu = OCXL_INVALID_AFU;
536  return rc;
537  }
538 
539  rc = afu_open((ocxl_afu *)*afu);
540  if (rc != OCXL_OK) {
541  ocxl_afu_close(*afu);
542  *afu = OCXL_INVALID_AFU;
543  return rc;
544  }
545 
546  return OCXL_OK;
547 }
548 
562 ocxl_err ocxl_afu_open_specific(const char *name, const char *physical_function, int16_t afu_index, ocxl_afu_h *afu)
563 {
564  char pattern[PATH_MAX];
565  glob_t glob_data;
567  *afu = OCXL_INVALID_AFU;
568 
569  libocxl_init();
570 
571  if (afu_index == -1) {
572  snprintf(pattern, sizeof(pattern), "%s/%s.%s.*",
573  DEVICE_PATH, name,
574  physical_function ? physical_function : "*");
575  } else {
576  snprintf(pattern, sizeof(pattern), "%s/%s.%s.%d",
577  DEVICE_PATH, name,
578  physical_function ? physical_function : "*",
579  afu_index);
580  }
581 
582  int rc = glob(pattern, GLOB_ERR, NULL, &glob_data);
583  switch (rc) {
584  case 0:
585  break;
586  case GLOB_NOSPACE:
587  ret = OCXL_NO_MEM;
588  errmsg(NULL, ret, "No memory for glob while listing AFUs");
589  goto end;
590  case GLOB_NOMATCH:
591  ret = OCXL_NO_DEV;
592  errmsg(NULL, ret, "No OCXL devices found in '%s' with pattern '%s'", DEVICE_PATH, pattern);
593  goto end;
594  default:
595  errmsg(NULL, ret, "Glob error %d while listing AFUs", rc);
596  goto end;
597  }
598 
599  for (size_t dev = 0; dev < glob_data.gl_pathc; dev++) {
600  const char *dev_path = glob_data.gl_pathv[dev];
601  ret = ocxl_afu_open_from_dev(dev_path, afu);
602 
603  switch (ret) {
604  case OCXL_OK:
605  goto end;
606 
608  continue;
609 
610  default:
611  goto end;
612  }
613  }
614 
615 end:
616  globfree(&glob_data);
617  return ret;
618 }
619 
631 ocxl_err ocxl_afu_open(const char *name, ocxl_afu_h *afu)
632 {
633  return ocxl_afu_open_specific(name, NULL, -1, afu);
634 }
635 
654 ocxl_err ocxl_afu_attach(ocxl_afu_h afu, __attribute__((unused)) uint64_t flags)
655 {
656  if (afu->fd == -1) {
658  errmsg(afu, rc, "Attempted to attach a closed AFU context");
659  return rc;
660  }
661 
662  struct ocxl_ioctl_attach attach_args;
663  memset(&attach_args, '\0', sizeof(attach_args));
664 #ifdef _ARCH_PPC64
665  attach_args.amr = afu->ppc64_amr;
666 #endif
667 
668  if (ioctl(afu->fd, OCXL_IOCTL_ATTACH, &attach_args)) {
670  errmsg(afu, rc, "OCXL_IOCTL_ATTACH failed %d:%s", errno, strerror(errno));
671  return rc;
672  }
673 
674  afu->attached = true;
675 
676  return OCXL_OK;
677 }
678 
693 {
694  if (afu->fd < 0) {
695  return OCXL_ALREADY_DONE;
696  }
697 
698  for (uint16_t mmio_idx = 0; mmio_idx < afu->mmio_count; mmio_idx++) {
699  ocxl_mmio_unmap((ocxl_mmio_h)&afu->mmios[mmio_idx]);
700  }
701 
702  if (afu->global_mmio_fd != -1) {
703  close(afu->global_mmio_fd);
704  afu->global_mmio_fd = -1;
705  }
706 
707  if (afu->irqs) {
708  for (uint16_t irq = 0; irq < afu->irq_count; irq++) {
709  irq_dealloc(afu, &afu->irqs[irq]);
710  }
711 
712  free(afu->irqs);
713  afu->irqs = NULL;
714  afu->irq_count = 0;
715  afu->irq_max_count = 0;
716  }
717 
718  if (afu->epoll_events) {
719  free(afu->epoll_events);
720  afu->epoll_event_count = 0;
721  }
722 
723  close(afu->epoll_fd);
724  afu->epoll_fd = -1;
725 
726  close(afu->fd);
727  afu->fd = -1;
728  afu->attached = false;
729 
730  if (afu->device_path) {
731  free(afu->device_path);
732  afu->device_path = NULL;
733  }
734 
735  if (afu->sysfs_path) {
736  free(afu->sysfs_path);
737  afu->sysfs_path = NULL;
738  }
739 
740  free(afu);
741 
742  return OCXL_OK;
743 }
744 
755 #ifdef _ARCH_PPC64
756 
770 {
771  afu->ppc64_amr = amr;
772 
773  return OCXL_OK;
774 }
775 #endif
776 
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.
Definition: afu.c:769
#define AFU_NAME_MAX
The maximum length of an AFU name.
Definition: libocxl.h:61
const ocxl_identifier * ocxl_afu_get_identifier(ocxl_afu_h afu)
Get the identifier of the AFU.
Definition: afu.c:75
ocxl_err
Potential return values from ocxl_* functions.
Definition: libocxl.h:92
void irq_dealloc(ocxl_afu *afu, ocxl_irq *irq)
Deallocate a single IRQ.
Definition: irq.c:42
ocxl_err ocxl_afu_attach(ocxl_afu_h afu, __attribute__((unused)) uint64_t flags)
Attach the calling process&#39;s memory to an open AFU context.
Definition: afu.c:654
The call requires an open context on the AFU.
Definition: libocxl.h:96
#define OCXL_ERRORS
Error messages requested.
Definition: libocxl.h:40
void ocxl_mmio_unmap(ocxl_mmio_h region)
Unmap an MMIO region from an AFU.
Definition: mmio.c:333
uint32_t ocxl_afu_get_pasid(ocxl_afu_h afu)
Get the PASID for the currently open context.
Definition: afu.c:60
an internal error has occurred
Definition: libocxl.h:98
ocxl_err ocxl_afu_open(const char *name, ocxl_afu_h *afu)
Open an AFU context with a specified name.
Definition: afu.c:631
const char * ocxl_afu_get_device_path(ocxl_afu_h afu)
Get the canonical device path of the AFU.
Definition: afu.c:91
The OpenCAPI device is not available.
Definition: libocxl.h:95
#define OCXL_INVALID_AFU
An invalid AFU handle.
Definition: libocxl.h:76
void ocxl_afu_enable_messages(ocxl_afu_h afu, uint64_t sources)
Enable messages from an AFU.
Definition: afu.c:151
AFU identification information.
Definition: libocxl.h:66
ocxl_err ocxl_afu_open_from_dev(const char *path, ocxl_afu_h *afu)
Open an AFU context at a specified path.
Definition: afu.c:528
void ocxl_afu_get_version(ocxl_afu_h afu, uint8_t *major, uint8_t *minor)
Get the version of the AFU.
Definition: afu.c:121
const char * ocxl_afu_get_sysfs_path(ocxl_afu_h afu)
Get the canonical sysfs path of the AFU.
Definition: afu.c:105
ocxl_err ocxl_afu_close(ocxl_afu_h afu)
Close an AFU and detach it from the context.
Definition: afu.c:692
struct ocxl_afu * ocxl_afu_h
A handle for an AFU.
Definition: libocxl.h:74
ocxl_err global_mmio_open(ocxl_afu *afu)
Open the global MMIO descriptor on an AFU.
Definition: mmio.c:96
An out of memory error occurred.
Definition: libocxl.h:94
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.
Definition: afu.c:175
#define OCXL_TRACING
Tracing requested.
Definition: libocxl.h:41
No more contexts can be opened on the AFU.
Definition: libocxl.h:101
struct ocxl_mmio_area * ocxl_mmio_h
A handle for an MMIO region on an AFU.
Definition: libocxl.h:86
The action requested has already been performed.
Definition: libocxl.h:99
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.
Definition: afu.c:562
The call succeeded.
Definition: libocxl.h:93