libocxl
Loading...
Searching...
No Matches
irq.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 <sys/types.h>
19#include <sys/stat.h>
20#include <sys/select.h>
21#include <fcntl.h>
22#include <errno.h>
23#include <stdbool.h>
24#include <unistd.h>
25#include <dirent.h>
26#include <sys/eventfd.h>
27#include <misc/ocxl.h>
28#include <sys/mman.h>
29#include <sys/ioctl.h>
30#include <string.h>
31#include <stdlib.h>
32#include <sys/epoll.h>
33
34#define MAX_EVENT_SIZE (16*sizeof(uint64_t))
35
42void irq_dealloc(ocxl_afu *afu, ocxl_irq *irq)
43{
44 if (irq->addr) {
45 if (munmap(irq->addr, afu->page_size)) {
46 errmsg(afu, OCXL_INTERNAL_ERROR, "Could not unmap IRQ page: %d: '%s'",
47 errno, strerror(errno));
48 }
49 irq->addr = NULL;
50 }
51
52 if (irq->event.irq_offset) {
53 int rc = ioctl(afu->fd, OCXL_IOCTL_IRQ_FREE, &irq->event.irq_offset);
54 if (rc) {
55 errmsg(afu, OCXL_INTERNAL_ERROR, "Could not free IRQ in kernel: %d", rc);
56 }
57 irq->event.irq_offset = 0;
58 }
59
60 if (irq->event.eventfd >= 0) {
61 close(irq->event.eventfd);
62 irq->event.eventfd = -1;
63 }
64
65 irq->info = NULL;
66}
67
93static ocxl_err irq_allocate(ocxl_afu *afu, ocxl_irq *irq, void *info)
94{
95 irq->event.irq_offset = 0;
96 irq->event.eventfd = -1;
97 irq->event.reserved = 0;
98 irq->irq_number = UINT16_MAX;
99 irq->addr = NULL;
100 irq->info = info;
101 irq->fd_info.type = EPOLL_SOURCE_IRQ;
102 irq->fd_info.irq = irq;
103
105
106 int fd = eventfd(0, EFD_CLOEXEC);
107 if (fd < 0) {
108 errmsg(afu, ret, "Could not open eventfd : %d: '%s'", errno, strerror(errno));
109 goto errend;
110 }
111 irq->event.eventfd = fd;
112
113 int rc = ioctl(afu->fd, OCXL_IOCTL_IRQ_ALLOC, &irq->event.irq_offset);
114 if (rc) {
115 errmsg(afu, ret, "Could not allocate IRQ in kernel: %d: '%s'", errno, strerror(errno));
116 goto errend;
117 }
118
119 rc = ioctl(afu->fd, OCXL_IOCTL_IRQ_SET_FD, &irq->event);
120 if (rc) {
121 errmsg(afu, ret, "Could not set event descriptor in kernel: %d: '%s'", errno, strerror(errno));
122 goto errend;
123 }
124
125 irq->addr = mmap(NULL, afu->page_size, PROT_WRITE, MAP_SHARED,
126 afu->fd, irq->event.irq_offset);
127 if (irq->addr == MAP_FAILED) {
128 errmsg(afu, ret, "mmap for IRQ failed: %d: '%s'", errno, strerror(errno));
129 goto errend;
130 }
131
132 struct epoll_event ev;
133 ev.events = EPOLLIN;
134 ev.data.ptr = &irq->fd_info;
135 if (epoll_ctl(afu->epoll_fd, EPOLL_CTL_ADD, irq->event.eventfd, &ev) == -1) {
136 errmsg(afu, ret, "Could not add IRQ fd %d to epoll fd %d: %d: '%s'",
137 irq->event.eventfd, afu->epoll_fd, errno, strerror(errno));
138 goto errend;
139 }
140
141 return OCXL_OK;
142
143errend:
144 irq_dealloc(afu, irq);
145 return ret;
146}
147
165{
166 if (afu->irq_count == afu->irq_max_count) {
167 ocxl_err rc = grow_buffer(afu, (void **)&afu->irqs, &afu->irq_max_count, sizeof(ocxl_irq), INITIAL_IRQ_COUNT);
168 if (rc != OCXL_OK) {
169 errmsg(afu, rc, "Could not grow IRQ buffer");
170 return rc;
171 }
172 }
173
174 ocxl_err rc = irq_allocate(afu, &afu->irqs[afu->irq_count], info);
175 if (rc != OCXL_OK) {
176 errmsg(afu, rc, "Could not allocate IRQ");
177 return rc;
178 }
179 afu->irqs[afu->irq_count].irq_number = afu->irq_count;
180
181 *irq = (ocxl_irq_h)afu->irq_count;
182 afu->irq_count++;
183
184 return OCXL_OK;
185}
186
198{
199 if (irq > afu->irq_count) {
200 return 0;
201 }
202
203 return (uint64_t)afu->irqs[irq].addr;
204}
205
217{
218 if (irq > afu->irq_count) {
219 return -1;
220 }
221
222 return afu->irqs[irq].event.eventfd;
223}
224
225
240{
241 return afu->fd;
242}
243
246
255static void populate_xsl_fault_error(ocxl_afu *afu, ocxl_event *event, void *body)
256{
258
259 event->type = OCXL_EVENT_TRANSLATION_FAULT;
260 event->translation_fault.addr = (void *)err->addr;
261#ifdef _ARCH_PPC64
262 event->translation_fault.dsisr = err->dsisr;
263 TRACE(afu, "Translation fault error received, addr=%p, dsisr=%llx, count=%llu",
264 event->translation_fault.addr, event->translation_fault.dsisr, err->count);
265#else
266 TRACE(afu, "Translation fault error received, addr=%p, count=%llu",
267 event->translation_fault.addr, err->count);
268#endif
269 event->translation_fault.count = err->count;
270}
271
305static ocxl_event_action read_afu_event(ocxl_afu_h afu, uint16_t event_api_version, ocxl_event *event, int *last)
306{
307 size_t event_size = sizeof(ocxl_kernel_event_header);
308 *last = 0;
309
310 uint16_t max_supported_event = 0;
311
312 switch (event_api_version) {
313 case 0:
314 event_size += sizeof(ocxl_kernel_event_xsl_fault_error);
315 max_supported_event = OCXL_AFU_EVENT_XSL_FAULT_ERROR;
316 break;
317 default:
318 errmsg(afu, OCXL_INTERNAL_ERROR, "Unsupported event API version %u, your libocxl library may be too old",
319 event_api_version);
320 return OCXL_EVENT_ACTION_FAIL;
321 }
322
323 char buf[event_size];
324
325 ssize_t buf_used;
326 if ((buf_used = read(afu->fd, buf, event_size)) < 0) {
327 if (errno == EAGAIN || errno == EWOULDBLOCK) {
328 *last = 1;
329 return OCXL_EVENT_ACTION_NONE;
330 }
331
332 errmsg(afu, OCXL_INTERNAL_ERROR, "read of event header from fd %d failed: %d: %s",
333 afu->fd, errno, strerror(errno));
334 return OCXL_EVENT_ACTION_FAIL;
335 } else if (buf_used < (ssize_t)sizeof(ocxl_kernel_event_header)) {
336 errmsg(afu, OCXL_INTERNAL_ERROR, "short read of event header from fd %d", afu->fd);
337 return OCXL_EVENT_ACTION_FAIL;
338 }
339
341
342 if (header->type > max_supported_event) {
343 TRACE(afu, "Unknown event received from kernel of type %u", header->type);
344 *last = !! (header->flags & OCXL_KERNEL_EVENT_FLAG_LAST);
345 return OCXL_EVENT_ACTION_IGNORE;
346 }
347
348 switch (header->type) {
349 case OCXL_AFU_EVENT_XSL_FAULT_ERROR:
350 if (buf_used != sizeof(ocxl_kernel_event_header) + sizeof(ocxl_kernel_event_xsl_fault_error)) {
351 errmsg(afu, OCXL_INTERNAL_ERROR,
352 "Incorrectly sized buffer received from kernel for XSL fault error, expected %d, got %d",
354 buf_used);
355 return OCXL_EVENT_ACTION_FAIL;
356 }
357 populate_xsl_fault_error(afu, event, buf + sizeof(ocxl_kernel_event_header));
358 break;
359
360 default:
361 errmsg(afu, OCXL_INTERNAL_ERROR, "Unknown event %d, max_supported_event %d",
362 header->type, max_supported_event);
363 return OCXL_EVENT_ACTION_FAIL;
364 }
365
366 *last = !! (header->flags & OCXL_KERNEL_EVENT_FLAG_LAST);
367 return OCXL_EVENT_ACTION_SUCCESS;
368}
369
395int ocxl_afu_event_check_versioned(ocxl_afu_h afu, int timeout, ocxl_event *events, uint16_t event_count,
396 uint16_t event_api_version)
397{
398 TRACE(afu, "Waiting up to %dms for AFU events", timeout);
399
400 if (event_count > afu->epoll_event_count) {
401 free(afu->epoll_events);
402 afu->epoll_events = NULL;
403 afu->epoll_event_count = 0;
404
405 struct epoll_event *events = malloc(event_count * sizeof(*events));
406 if (events == NULL) {
407 errmsg(afu, OCXL_NO_MEM, "Could not allocate space for %d events", event_count);
408 return -1;
409 }
410
411 afu->epoll_events = events;
412 afu->epoll_event_count = event_count;
413 }
414
415 int count;
416 if ((count = epoll_wait(afu->epoll_fd, afu->epoll_events, event_count, timeout)) == -1) {
417 errmsg(afu, OCXL_INTERNAL_ERROR, "epoll_wait failed waiting for AFU events: %d: '%s'",
418 errno, strerror(errno));
419 return -1;
420 }
421
422 uint16_t triggered = 0;
423 for (int event = 0; event < count; event++) {
424 epoll_fd_source *info = (epoll_fd_source *)afu->epoll_events[event].data.ptr;
425 ocxl_event_action ret;
426 ssize_t buf_used;
427 uint64_t count;
428 int last;
429
430 switch (info->type) {
431 case EPOLL_SOURCE_OCXL:
432 while ((ret = read_afu_event(afu, event_api_version, &events[triggered], &last)),
433 ret == OCXL_EVENT_ACTION_SUCCESS || ret == OCXL_EVENT_ACTION_IGNORE) {
434 if (ret == OCXL_EVENT_ACTION_SUCCESS) {
435 triggered++;
436 }
437
438 if (last) {
439 break;
440 }
441 }
442
443 if (ret == OCXL_EVENT_ACTION_FAIL) {
444 return -1;
445 }
446
447 break;
448
449 case EPOLL_SOURCE_IRQ:
450 buf_used = read(info->irq->event.eventfd, &count, sizeof(count));
451 if (buf_used < 0) {
452 errmsg(afu, OCXL_INTERNAL_ERROR, "read of eventfd %d IRQ %d failed: %d: %s",
453 info->irq->event.eventfd, info->irq->irq_number, errno, strerror(errno));
454 continue;
455 } else if (buf_used != (ssize_t)sizeof(count)) {
456 errmsg(afu, OCXL_INTERNAL_ERROR, "short read of eventfd %d IRQ %d");
457 continue;
458 }
459 events[triggered].type = OCXL_EVENT_IRQ;
460 events[triggered].irq.irq = info->irq->irq_number;
461 events[triggered].irq.handle = (uint64_t)info->irq->addr;
462 events[triggered].irq.info = info->irq->info;
463 events[triggered++].irq.count = count;
464
465 TRACE(afu, "IRQ received, irq=%u id=%llx info=%p count=%llu",
466 info->irq->irq_number, (uint64_t)info->irq->addr, info->irq->info, count);
467
468 break;
469 }
470 }
471
472 TRACE(afu, "%u events reported", triggered);
473
474 return triggered;
475}
476
498{
499 ocxl_afu *my_afu = (ocxl_afu *)afu;
500
501 struct ocxl_ioctl_features features;
502
503 int rc = ioctl(my_afu->fd, OCXL_IOCTL_GET_FEATURES, &features);
504 if (rc) {
505 errmsg(afu, OCXL_NO_DEV, "Could not identify platform: %d %s",
506 errno, strerror(errno));
507 return OCXL_NO_DEV;
508 }
509
510 if (!(features.flags[0] & OCXL_IOCTL_FEATURES_FLAGS0_P9_WAIT)) {
511 errmsg(afu, OCXL_NO_DEV, "Power 9 wait is not available on this machine");
512 return OCXL_NO_DEV;
513 }
514
515 struct ocxl_ioctl_p9_wait wait_data;
516
517 rc = ioctl(my_afu->fd, OCXL_IOCTL_ENABLE_P9_WAIT, &wait_data);
518 if (rc) {
519 errmsg(afu, OCXL_NO_DEV, "Could not enable wait in kernel: %d %s",
520 errno, strerror(errno));
521 return OCXL_NO_DEV;
522 }
523
524 *thread_id = wait_data.thread_id;
525
526 return OCXL_OK;
527}
528
int ocxl_afu_event_check_versioned(ocxl_afu_h afu, int timeout, ocxl_event *events, uint16_t event_count, uint16_t event_api_version)
Check for pending IRQs and other events.
Definition irq.c:395
ocxl_err ocxl_afu_get_p9_thread_id(ocxl_afu_h afu, uint16_t *thread_id)
Get the thread ID required to wake up a Power 9 wait instruction.
Definition irq.c:497
uint64_t ocxl_irq_get_handle(ocxl_afu_h afu, ocxl_irq_h irq)
Get the 64 bit IRQ handle for an IRQ.
Definition irq.c:197
struct ocxl_kernel_event_xsl_fault_error ocxl_kernel_event_xsl_fault_error
Definition irq.c:245
ocxl_err ocxl_irq_alloc(ocxl_afu_h afu, void *info, ocxl_irq_h *irq)
Allocate an IRQ for an open AFU.
Definition irq.c:164
int ocxl_irq_get_fd(ocxl_afu_h afu, ocxl_irq_h irq)
Get the file descriptor associated with an IRQ.
Definition irq.c:216
int ocxl_afu_get_event_fd(ocxl_afu_h afu)
Get a descriptor that will trigger a poll when an AFU event occurs.
Definition irq.c:239
struct ocxl_kernel_event_header ocxl_kernel_event_header
Definition irq.c:244
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_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_NO_MEM
An out of memory error occurred.
Definition libocxl.h:94
struct ocxl_afu * ocxl_afu_h
A handle for an AFU.
Definition libocxl.h:74
ocxl_event_type type
Definition libocxl.h:155
uint16_t ocxl_irq_h
A handle for an IRQ on an AFU.
Definition libocxl.h:81
@ OCXL_EVENT_TRANSLATION_FAULT
A memory translation fault occurred on the AFU.
Definition libocxl.h:111
@ OCXL_EVENT_IRQ
An AFU IRQ.
Definition libocxl.h:110
An OCXL event.
Definition libocxl.h:154