libocxl
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 
42 void 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 
93 static 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", rc);
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", rc);
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 
143 errend:
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 
255 static 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 
305 static 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 
395 int 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 
OCXL_EVENT_TRANSLATION_FAULT
@ OCXL_EVENT_TRANSLATION_FAULT
A memory translation fault occurred on the AFU.
Definition: libocxl.h:111
ocxl_err
ocxl_err
Potential return values from ocxl_* functions.
Definition: libocxl.h:92
ocxl_irq_get_handle
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
OCXL_NO_MEM
@ OCXL_NO_MEM
An out of memory error occurred.
Definition: libocxl.h:94
ocxl_kernel_event_header
struct ocxl_kernel_event_header ocxl_kernel_event_header
Definition: irq.c:244
ocxl_irq_h
uint16_t ocxl_irq_h
A handle for an IRQ on an AFU.
Definition: libocxl.h:81
irq_dealloc
void irq_dealloc(ocxl_afu *afu, ocxl_irq *irq)
Deallocate a single IRQ.
Definition: irq.c:42
ocxl_irq_alloc
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
OCXL_NO_DEV
@ OCXL_NO_DEV
The OpenCAPI device is not available.
Definition: libocxl.h:95
OCXL_INTERNAL_ERROR
@ OCXL_INTERNAL_ERROR
an internal error has occurred
Definition: libocxl.h:98
ocxl_event::type
ocxl_event_type type
Definition: libocxl.h:155
OCXL_OK
@ OCXL_OK
The call succeeded.
Definition: libocxl.h:93
ocxl_irq_get_fd
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
OCXL_EVENT_IRQ
@ OCXL_EVENT_IRQ
An AFU IRQ.
Definition: libocxl.h:110
ocxl_event
An OCXL event.
Definition: libocxl.h:154
ocxl_kernel_event_xsl_fault_error
struct ocxl_kernel_event_xsl_fault_error ocxl_kernel_event_xsl_fault_error
Definition: irq.c:245
ocxl_afu_h
struct ocxl_afu * ocxl_afu_h
A handle for an AFU.
Definition: libocxl.h:74
ocxl_afu_get_p9_thread_id
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
ocxl_afu_get_event_fd
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
ocxl_afu_event_check_versioned
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