libocxl
Loading...
Searching...
No Matches
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
121void 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
151void 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
208static 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
265static ocxl_err afu_alloc(ocxl_afu_h *afu_out)
266{
267 ocxl_afu *afu = malloc(sizeof(ocxl_afu));
268 if (afu == NULL) {
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
290static 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
313static 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 uint32_t domain;
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);
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
384static 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
407static 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) {
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) {
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)) {
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
485static 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
510err_free:
511 free(afu_h);
512err:
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
562ocxl_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
615end:
616 globfree(&glob_data);
617 return ret;
618}
619
631ocxl_err ocxl_afu_open(const char *name, ocxl_afu_h *afu)
632{
633 return ocxl_afu_open_specific(name, NULL, -1, afu);
634}
635
654ocxl_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
770{
771 afu->ppc64_amr = amr;
772
773 return OCXL_OK;
774}
775#endif
776
uint32_t ocxl_afu_get_pasid(ocxl_afu_h afu)
Get the PASID for the currently open context.
Definition afu.c:60
const char * ocxl_afu_get_sysfs_path(ocxl_afu_h afu)
Get the canonical sysfs path of the AFU.
Definition afu.c:105
const char * ocxl_afu_get_device_path(ocxl_afu_h afu)
Get the canonical device path of the AFU.
Definition afu.c:91
const ocxl_identifier * ocxl_afu_get_identifier(ocxl_afu_h afu)
Get the identifier of the AFU.
Definition afu.c:75
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
ocxl_err ocxl_afu_open(const char *name, ocxl_afu_h *afu)
Open an AFU context with a specified name.
Definition afu.c:631
ocxl_err ocxl_afu_close(ocxl_afu_h afu)
Close an AFU and detach it from the context.
Definition afu.c:692
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
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.
Definition afu.c:654
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
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
void ocxl_afu_enable_messages(ocxl_afu_h afu, uint64_t sources)
Enable messages from an AFU.
Definition afu.c:151
void ocxl_mmio_unmap(ocxl_mmio_h region)
Unmap an MMIO region from an AFU.
Definition mmio.c:336
ocxl_err global_mmio_open(ocxl_afu *afu)
Open the global MMIO descriptor on an AFU.
Definition mmio.c:99
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
void irq_dealloc(ocxl_afu *afu, ocxl_irq *irq)
Deallocate a single IRQ.
Definition irq.c:42
ocxl_err
Potential return values from ocxl_* functions.
Definition libocxl.h:92
@ OCXL_NO_MORE_CONTEXTS
No more contexts can be opened on the AFU.
Definition libocxl.h:101
@ OCXL_NO_DEV
The OpenCAPI device is not available.
Definition libocxl.h:95
@ OCXL_INTERNAL_ERROR
an internal error has occurred
Definition libocxl.h:98
@ OCXL_OK
The call succeeded.
Definition libocxl.h:93
@ OCXL_ALREADY_DONE
The action requested has already been performed.
Definition libocxl.h:99
@ OCXL_NO_MEM
An out of memory error occurred.
Definition libocxl.h:94
@ OCXL_NO_CONTEXT
The call requires an open context on the AFU.
Definition libocxl.h:96
@ OCXL_GLOBAL_MMIO
Definition libocxl.h:57
@ OCXL_PER_PASID_MMIO
Definition libocxl.h:58
struct ocxl_afu * ocxl_afu_h
A handle for an AFU.
Definition libocxl.h:74
#define OCXL_INVALID_AFU
An invalid AFU handle.
Definition libocxl.h:76
struct ocxl_mmio_area * ocxl_mmio_h
A handle for an MMIO region on an AFU.
Definition libocxl.h:86
#define OCXL_TRACING
Tracing requested.
Definition libocxl.h:41
#define AFU_NAME_MAX
The maximum length of an AFU name.
Definition libocxl.h:61
#define OCXL_ERRORS
Error messages requested.
Definition libocxl.h:40
AFU identification information.
Definition libocxl.h:66