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  ocxl_afu *my_afu = (ocxl_afu *) afu;
63 
64  return my_afu->pasid;
65 }
66 
67 
78 {
79  ocxl_afu *my_afu = (ocxl_afu *) afu;
80 
81  return &my_afu->identifier;
82 }
83 
96 {
97  ocxl_afu *my_afu = (ocxl_afu *) afu;
98 
99  return my_afu->device_path;
100 }
101 
112 {
113  ocxl_afu *my_afu = (ocxl_afu *) afu;
114 
115  return my_afu->sysfs_path;
116 }
117 
129 void ocxl_afu_get_version(ocxl_afu_h afu, uint8_t *major, uint8_t *minor)
130 {
131  ocxl_afu *my_afu = (ocxl_afu *) afu;
132 
133  *major = my_afu->version_major;
134  *minor = my_afu->version_minor;
135 }
136 
161 void ocxl_afu_enable_messages(ocxl_afu_h afu, uint64_t sources)
162 {
163  ocxl_afu *my_afu = (ocxl_afu *) afu;
164 
165  my_afu->verbose_errors = !!(sources & OCXL_ERRORS);
166  my_afu->tracing = !!(sources & OCXL_TRACING);
167 }
168 
188  const char *message))
189 {
190  ocxl_afu *my_afu = (ocxl_afu *) afu;
191 
192  my_afu->error_handler = handler;
193 }
194 
222 static void afu_init(ocxl_afu *afu)
223 {
224  memset((char *)afu->identifier.afu_name, '\0', sizeof(afu->identifier.afu_name));
225  afu->device_path = NULL;
226  afu->sysfs_path = NULL;
227  afu->version_major = 0;
228  afu->version_minor = 0;
229  afu->fd = -1;
230  afu->fd_info.type = EPOLL_SOURCE_OCXL;
231  afu->fd_info.irq = NULL;
232  afu->epoll_fd = -1;
233  afu->epoll_events = NULL;
234  afu->epoll_event_count = 0;
235  afu->global_mmio_fd = -1;
236 
237  afu->global_mmio.start = NULL;
238  afu->global_mmio.length = 0;
239  afu->global_mmio.type = OCXL_GLOBAL_MMIO;
240 
241  afu->per_pasid_mmio.start = NULL;
242  afu->per_pasid_mmio.length = 0;
243  afu->per_pasid_mmio.type = OCXL_PER_PASID_MMIO;
244 
245  afu->page_size = sysconf(_SC_PAGESIZE);
246 
247  afu->irqs = NULL;
248  afu->irq_count = 0;
249  afu->irq_max_count = 0;
250 
251  afu->mmios = NULL;
252  afu->mmio_count = 0;
253  afu->mmio_max_count = 0;
254 
255  afu->pasid = UINT32_MAX;
256 
257  afu->verbose_errors = false;
258  afu->error_handler = ocxl_default_afu_error_handler;
259 
260  afu->tracing = false;
261 
262 #ifdef _ARCH_PPC64
263  afu->ppc64_amr = 0;
264 #endif
265 }
266 
277 static ocxl_err afu_alloc(ocxl_afu_h *afu_out)
278 {
279  ocxl_afu *afu = malloc(sizeof(ocxl_afu));
280  if (afu == NULL) {
281  ocxl_err rc = OCXL_NO_MEM;
282  errmsg(NULL, rc, "Could not allocate %d bytes for AFU", sizeof(ocxl_afu));
283  return rc;
284  }
285 
286  afu_init(afu);
287 
288  *afu_out = (ocxl_afu_h) afu;
289 
290  return OCXL_OK;
291 }
292 
302 static bool device_matches(int dirfd, char *dev_name, dev_t dev)
303 {
304  struct stat sb;
305 
306  if (fstatat(dirfd, dev_name, &sb, 0) == -1) {
307  return false;
308  }
309 
310  if (!S_ISCHR(sb.st_mode)) {
311  return false;
312  }
313 
314  return dev == sb.st_rdev;
315 }
316 
325 static bool populate_metadata(dev_t dev, ocxl_afu *afu)
326 {
327  DIR *dev_dir;
328  struct dirent *dev_ent;
329 
330  dev_dir = opendir(DEVICE_PATH);
331 
332  if (dev_dir == NULL) {
333  return false;
334  }
335 
336  int fd = dirfd(dev_dir);
337  do {
338  if (!(dev_ent = readdir(dev_dir))) {
339  closedir(dev_dir);
340  return false;
341  }
342  } while (!device_matches(fd, dev_ent->d_name, dev));
343 
344  char *physical_function = strchr(dev_ent->d_name, '.');
345  if (physical_function == NULL) {
346  errmsg(NULL, OCXL_INTERNAL_ERROR, "Could not extract physical function from device name '%s', missing initial '.'",
347  dev_ent->d_name);
348  return false;
349  }
350  int afu_name_len = physical_function - dev_ent->d_name;
351  if (afu_name_len > AFU_NAME_MAX) {
352  errmsg(NULL, OCXL_INTERNAL_ERROR,"AFU name '%-.*s' exceeds maximum length of %d", afu_name_len, dev_ent->d_name);
353  return false;
354  }
355 
356  physical_function++;
357  uint16_t domain;
358  uint8_t bus, device, function;
359  int found = sscanf(physical_function, "%hu:%hhu:%hhu.%hhu.%hhu",
360  &domain, &bus, &device, &function, &afu->identifier.afu_index);
361 
362  if (found != 5) {
363  errmsg(NULL, OCXL_INTERNAL_ERROR, "Could not parse physical function '%s', only got %d components", physical_function,
364  found);
365  return false;
366  }
367 
368  memcpy((char *)afu->identifier.afu_name, dev_ent->d_name, afu_name_len);
369  ((char *)afu->identifier.afu_name)[afu_name_len] = '\0';
370 
371  size_t dev_path_len = strlen(DEVICE_PATH) + 1 + strlen(dev_ent->d_name) + 1;
372  afu->device_path = malloc(dev_path_len);
373  if (NULL == afu->device_path) {
374  errmsg(NULL, OCXL_INTERNAL_ERROR, "Could not allocate %llu bytes for device path", dev_path_len);
375  return false;
376  }
377  (void)snprintf(afu->device_path, dev_path_len, "%s/%s", DEVICE_PATH, dev_ent->d_name);
378 
379  size_t sysfs_path_len = strlen(SYS_PATH) + 1 + strlen(dev_ent->d_name) + 1;
380  afu->sysfs_path = malloc(sysfs_path_len);
381  if (NULL == afu->sysfs_path) {
382  errmsg(NULL, OCXL_INTERNAL_ERROR, "Could not allocate %llu bytes for sysfs path", sysfs_path_len);
383  return false;
384  }
385  (void)snprintf(afu->sysfs_path, sysfs_path_len, "%s/%s", SYS_PATH, dev_ent->d_name);
386 
387  return true;
388 }
389 
395 static void trace_metadata(ocxl_afu *afu)
396 {
397  TRACE(afu, "device path=\"%s\"", afu->device_path);
398  TRACE(afu, "sysfs path=\"%s\"", afu->sysfs_path);
399  TRACE(afu, "AFU Name=\"%s\"", afu->identifier.afu_name);
400  TRACE(afu, "AFU Index=%u", afu->identifier.afu_index);
401  TRACE(afu, "AFU Version=%u:%u", afu->version_major, afu->version_minor);
402  TRACE(afu, "Global MMIO size=%llu", afu->global_mmio.length);
403  TRACE(afu, "Per PASID MMIO size=%llu", afu->per_pasid_mmio.length);
404  TRACE(afu, "Page Size=%llu", afu->page_size);
405  TRACE(afu, "PASID=%lu", afu->pasid);
406 }
407 
418 static ocxl_err afu_open(ocxl_afu *afu)
419 {
420  if (afu->fd != -1) {
421  return OCXL_ALREADY_DONE;
422  }
423 
424  ocxl_err rc;
425 
426  int fd = open(afu->device_path, O_RDWR | O_CLOEXEC | O_NONBLOCK);
427  if (fd < 0) {
428  if (errno == ENOSPC) {
430  errmsg(afu, rc, "Could not open AFU device '%s', the maximum number of contexts has been reached: Error %d: %s",
431  afu->device_path, errno, strerror(errno));
432  return rc;
433  }
434 
435  rc = OCXL_NO_DEV;
436  errmsg(afu, rc, "Could not open AFU device '%s': Error %d: %s", afu->device_path, errno, strerror(errno));
437  return rc;
438  }
439 
440  afu->fd = fd;
441 
442  rc = global_mmio_open(afu);
443  if (rc != OCXL_OK) {
444  errmsg(afu, rc, "Could not open global MMIO descriptor");
445  return rc;
446  }
447 
448  fd = epoll_create1(EPOLL_CLOEXEC);
449  if (fd < 0) {
450  ocxl_err rc = OCXL_NO_DEV;
451  errmsg(afu, rc, "Could not create epoll descriptor. Error %d: %s",
452  errno, strerror(errno));
453  return rc;
454  }
455  afu->epoll_fd = fd;
456 
457  struct epoll_event ev;
458  ev.events = EPOLLIN;
459  ev.data.ptr = &afu->fd_info; // Already set up in afu_init
460  if (epoll_ctl(afu->epoll_fd, EPOLL_CTL_ADD, afu->fd, &ev) == -1) {
461  ocxl_err rc = OCXL_NO_DEV;
462  errmsg(afu, rc, "Could not add device fd %d to epoll fd %d: %d: '%s'",
463  afu->fd, afu->epoll_fd, errno, strerror(errno));
464  return rc;
465  }
466 
467  struct ocxl_ioctl_metadata metadata;
468  if (ioctl(afu->fd, OCXL_IOCTL_GET_METADATA, &metadata)) {
469  ocxl_err rc = OCXL_NO_DEV;
470  errmsg(afu, rc, "OCXL_IOCTL_GET_METADATA failed %d:%s", errno, strerror(errno));
471  return rc;
472  }
473 
474  // Metadata version 0, always available
475  afu->version_major = metadata.afu_version_major;
476  afu->version_minor = metadata.afu_version_minor;
477  afu->per_pasid_mmio.length = metadata.pp_mmio_size;
478  afu->global_mmio.length = metadata.global_mmio_size;
479  afu->pasid = metadata.pasid;
480 
481  if (afu->tracing) {
482  trace_metadata(afu);
483  }
484 
485  return OCXL_OK;
486 }
487 
498 static ocxl_err get_afu_by_path(const char *path, ocxl_afu_h *afu)
499 {
500  ocxl_afu_h afu_h;
501  ocxl_err rc = afu_alloc(&afu_h);
502  if (rc != OCXL_OK) {
503  *afu = OCXL_INVALID_AFU;
504  return rc;
505  }
506 
507  ocxl_afu *my_afu = (ocxl_afu *) afu_h;
508 
509  struct stat dev_stats;
510  if (stat(path, &dev_stats)) {
511  ocxl_err rc = OCXL_NO_DEV;
512  errmsg(NULL, rc, "Could not stat AFU device '%s': Error %d: %s", path, errno, strerror(errno));
513  *afu = OCXL_INVALID_AFU;
514  return rc;
515  }
516 
517  if (!populate_metadata(dev_stats.st_rdev, my_afu)) {
518  ocxl_err rc = OCXL_NO_DEV;
519  errmsg(NULL, rc, "Could not find OCXL device for '%s', major=%d, minor=%d, device expected in '%s'",
520  path, major(dev_stats.st_rdev), minor(dev_stats.st_rdev), DEVICE_PATH);
521  *afu = OCXL_INVALID_AFU;
522  return rc;
523  }
524 
525  *afu = afu_h;
526 
527  return OCXL_OK;
528 }
529 
542 {
543  ocxl_err rc = get_afu_by_path(path, afu);
544 
545  if (rc != OCXL_OK) {
546  *afu = OCXL_INVALID_AFU;
547  return rc;
548  }
549 
550  rc = afu_open((ocxl_afu *)*afu);
551  if (rc != OCXL_OK) {
552  ocxl_afu_close(*afu);
553  *afu = OCXL_INVALID_AFU;
554  return rc;
555  }
556 
557  return OCXL_OK;
558 }
559 
573 ocxl_err ocxl_afu_open_specific(const char *name, const char *physical_function, int16_t afu_index, ocxl_afu_h *afu)
574 {
575  char pattern[PATH_MAX];
576  glob_t glob_data;
578  *afu = OCXL_INVALID_AFU;
579 
580  if (afu_index == -1) {
581  snprintf(pattern, sizeof(pattern), "%s/%s.%s.*",
582  DEVICE_PATH, name,
583  physical_function ? physical_function : "*");
584  } else {
585  snprintf(pattern, sizeof(pattern), "%s/%s.%s.%d",
586  DEVICE_PATH, name,
587  physical_function ? physical_function : "*",
588  afu_index);
589  }
590 
591  int rc = glob(pattern, GLOB_ERR, NULL, &glob_data);
592  switch (rc) {
593  case 0:
594  break;
595  case GLOB_NOSPACE:
596  ret = OCXL_NO_MEM;
597  errmsg(NULL, ret, "No memory for glob while listing AFUs");
598  goto end;
599  case GLOB_NOMATCH:
600  ret = OCXL_NO_DEV;
601  errmsg(NULL, ret, "No OCXL devices found in '%s' with pattern '%s'", DEVICE_PATH, pattern);
602  goto end;
603  default:
604  errmsg(NULL, ret, "Glob error %d while listing AFUs", rc);
605  goto end;
606  }
607 
608  for (size_t dev = 0; dev < glob_data.gl_pathc; dev++) {
609  const char *dev_path = glob_data.gl_pathv[dev];
610  ret = ocxl_afu_open_from_dev(dev_path, afu);
611 
612  switch (ret) {
613  case OCXL_OK:
614  goto end;
615 
617  continue;
618 
619  default:
620  goto end;
621  }
622  }
623 
624 end:
625  globfree(&glob_data);
626  return ret;
627 }
628 
640 ocxl_err ocxl_afu_open(const char *name, ocxl_afu_h *afu)
641 {
642  return ocxl_afu_open_specific(name, NULL, -1, afu);
643 }
644 
663 ocxl_err ocxl_afu_attach(ocxl_afu_h afu, __attribute__((unused)) uint64_t flags)
664 {
665  ocxl_afu *my_afu = (ocxl_afu *) afu;
666 
667  if (my_afu->fd == -1) {
669  errmsg(my_afu, rc, "Attempted to attach a closed AFU context");
670  return rc;
671  }
672 
673  struct ocxl_ioctl_attach attach_args;
674  memset(&attach_args, '\0', sizeof(attach_args));
675 #ifdef _ARCH_PPC64
676  attach_args.amr = my_afu->ppc64_amr;
677 #endif
678 
679  if (ioctl(my_afu->fd, OCXL_IOCTL_ATTACH, &attach_args)) {
681  errmsg(my_afu, rc, "OCXL_IOCTL_ATTACH failed %d:%s", errno, strerror(errno));
682  return rc;
683  }
684 
685  return OCXL_OK;
686 }
687 
702 {
703  ocxl_afu *my_afu = (ocxl_afu *) afu;
704 
705  if (my_afu->fd < 0) {
706  return OCXL_ALREADY_DONE;
707  }
708 
709  for (uint16_t mmio_idx = 0; mmio_idx < my_afu->mmio_count; mmio_idx++) {
710  ocxl_mmio_unmap((ocxl_mmio_h)&my_afu->mmios[mmio_idx]);
711  }
712 
713  if (my_afu->global_mmio_fd) {
714  close(my_afu->global_mmio_fd);
715  my_afu->global_mmio_fd = -1;
716  }
717 
718  if (my_afu->irqs) {
719  for (uint16_t irq = 0; irq < my_afu->irq_count; irq++) {
720  irq_dealloc(my_afu, &my_afu->irqs[irq]);
721  }
722 
723  free(my_afu->irqs);
724  my_afu->irqs = NULL;
725  my_afu->irq_count = 0;
726  my_afu->irq_max_count = 0;
727  }
728 
729  if (my_afu->epoll_events) {
730  free(my_afu->epoll_events);
731  my_afu->epoll_event_count = 0;
732  }
733 
734  close(my_afu->epoll_fd);
735  my_afu->epoll_fd = -1;
736 
737  close(my_afu->fd);
738  my_afu->fd = -1;
739 
740  if (my_afu->device_path) {
741  free(my_afu->device_path);
742  my_afu->device_path = NULL;
743  }
744 
745  if (my_afu->sysfs_path) {
746  free(my_afu->sysfs_path);
747  my_afu->sysfs_path = NULL;
748  }
749 
750  free(my_afu);
751 
752  return OCXL_OK;
753 }
754 
765 #ifdef _ARCH_PPC64
766 
780 {
781  ocxl_afu *my_afu = (ocxl_afu *) afu;
782 
783  my_afu->ppc64_amr = amr;
784 
785  return OCXL_OK;
786 }
787 #endif
788 
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:779
#define AFU_NAME_MAX
The maximum length of an AFU name.
Definition: libocxl.h:53
const ocxl_identifier * ocxl_afu_get_identifier(ocxl_afu_h afu)
Get the identifier of the AFU.
Definition: afu.c:77
ocxl_err
Potential return values from ocxl_* functions.
Definition: libocxl.h:84
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:663
The call requires an open context on the AFU.
Definition: libocxl.h:88
#define OCXL_ERRORS
Error messages requested.
Definition: libocxl.h:32
void ocxl_mmio_unmap(ocxl_mmio_h region)
Unmap an MMIO region from an AFU.
Definition: mmio.c:303
uint32_t ocxl_afu_get_pasid(ocxl_afu_h afu)
Get the PASID for the currently open context.
Definition: afu.c:60
void * ocxl_mmio_h
A handle for an MMIO region on an AFU.
Definition: libocxl.h:78
an internal error has occurred
Definition: libocxl.h:90
ocxl_err ocxl_afu_open(const char *name, ocxl_afu_h *afu)
Open an AFU context with a specified name.
Definition: afu.c:640
const char * ocxl_afu_get_device_path(ocxl_afu_h afu)
Get the canonical device path of the AFU.
Definition: afu.c:95
The OpenCAPI device is not available.
Definition: libocxl.h:87
#define OCXL_INVALID_AFU
An invalid AFU handle.
Definition: libocxl.h:68
void ocxl_afu_enable_messages(ocxl_afu_h afu, uint64_t sources)
Enable messages from an AFU.
Definition: afu.c:161
AFU identification information.
Definition: libocxl.h:58
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:541
void ocxl_afu_get_version(ocxl_afu_h afu, uint8_t *major, uint8_t *minor)
Get the version of the AFU.
Definition: afu.c:129
const char * ocxl_afu_get_sysfs_path(ocxl_afu_h afu)
Get the canonical sysfs path of the AFU.
Definition: afu.c:111
ocxl_err ocxl_afu_close(ocxl_afu_h afu)
Close an AFU and detach it from the context.
Definition: afu.c:701
ocxl_err global_mmio_open(ocxl_afu *afu)
Open the global MMIO descriptor on an AFU.
Definition: mmio.c:93
void * ocxl_afu_h
A handle for an AFU.
Definition: libocxl.h:66
An out of memory error occurred.
Definition: libocxl.h:86
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:187
#define OCXL_TRACING
Tracing requested.
Definition: libocxl.h:33
No more contexts can be opened on the AFU.
Definition: libocxl.h:93
The action requested has already been performed.
Definition: libocxl.h:91
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:573
The call succeeded.
Definition: libocxl.h:85