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 
92 static ocxl_err irq_allocate(ocxl_afu *afu, ocxl_irq *irq, void *info)
93 {
94  ocxl_afu *my_afu = (ocxl_afu *) afu;
95 
96  irq->event.irq_offset = 0;
97  irq->event.eventfd = -1;
98  irq->event.reserved = 0;
99  irq->irq_number = UINT16_MAX;
100  irq->addr = NULL;
101  irq->info = info;
102  irq->fd_info.type = EPOLL_SOURCE_IRQ;
103  irq->fd_info.irq = irq;
104 
106 
107  int fd = eventfd(0, EFD_CLOEXEC);
108  if (fd < 0) {
109  errmsg(afu, ret, "Could not open eventfd : %d: '%s'", errno, strerror(errno));
110  goto errend;
111  }
112  irq->event.eventfd = fd;
113 
114  int rc = ioctl(afu->fd, OCXL_IOCTL_IRQ_ALLOC, &irq->event.irq_offset);
115  if (rc) {
116  errmsg(afu, ret, "Could not allocate IRQ in kernel: %d", rc);
117  goto errend;
118  }
119 
120  rc = ioctl(my_afu->fd, OCXL_IOCTL_IRQ_SET_FD, &irq->event);
121  if (rc) {
122  errmsg(afu, ret, "Could not set event descriptor in kernel: %d", rc);
123  goto errend;
124  }
125 
126  irq->addr = mmap(NULL, afu->page_size, PROT_WRITE, MAP_SHARED,
127  my_afu->fd, irq->event.irq_offset);
128  if (irq->addr == MAP_FAILED) {
129  errmsg(afu, ret, "mmap for IRQ failed: %d: '%s'", errno, strerror(errno));
130  goto errend;
131  }
132 
133  struct epoll_event ev;
134  ev.events = EPOLLIN;
135  ev.data.ptr = &irq->fd_info;
136  if (epoll_ctl(my_afu->epoll_fd, EPOLL_CTL_ADD, irq->event.eventfd, &ev) == -1) {
137  errmsg(afu, ret, "Could not add IRQ fd %d to epoll fd %d: %d: '%s'",
138  irq->event.eventfd, my_afu->epoll_fd, errno, strerror(errno));
139  goto errend;
140  }
141 
142  return OCXL_OK;
143 
144 errend:
145  irq_dealloc(my_afu, irq);
146  return ret;
147 }
148 
166 {
167  ocxl_afu *my_afu = (ocxl_afu *) afu;
168 
169  if (my_afu->irq_count == my_afu->irq_max_count) {
170  ocxl_err rc = grow_buffer(my_afu, (void **)&my_afu->irqs, &my_afu->irq_max_count, sizeof(ocxl_irq), INITIAL_IRQ_COUNT);
171  if (rc != OCXL_OK) {
172  errmsg(my_afu, rc, "Could not grow IRQ buffer");
173  return rc;
174  }
175  }
176 
177  ocxl_err rc = irq_allocate(my_afu, &my_afu->irqs[my_afu->irq_count], info);
178  if (rc != OCXL_OK) {
179  errmsg(my_afu, rc, "Could not allocate IRQ");
180  return rc;
181  }
182  my_afu->irqs[my_afu->irq_count].irq_number = my_afu->irq_count;
183 
184  *irq = (ocxl_irq_h)my_afu->irq_count;
185  my_afu->irq_count++;
186 
187  return OCXL_OK;
188 }
189 
201 {
202  ocxl_afu *my_afu = (ocxl_afu *) afu;
203 
204  if (irq > my_afu->irq_count) {
205  return 0;
206  }
207 
208  return (uint64_t)my_afu->irqs[irq].addr;
209 }
210 
222 {
223  ocxl_afu *my_afu = (ocxl_afu *) afu;
224 
225  if (irq > my_afu->irq_count) {
226  return -1;
227  }
228 
229  return my_afu->irqs[irq].event.eventfd;
230 }
231 
232 
247 {
248  ocxl_afu *my_afu = (ocxl_afu *) afu;
249 
250  return my_afu->fd;
251 }
252 
255 
264 static void populate_xsl_fault_error(ocxl_afu *afu, ocxl_event *event, void *body)
265 {
267 
268  event->type = OCXL_EVENT_TRANSLATION_FAULT;
269  event->translation_fault.addr = (void *)err->addr;
270 #ifdef _ARCH_PPC64
271  event->translation_fault.dsisr = err->dsisr;
272  TRACE(afu, "Translation fault error received, addr=%p, dsisr=%llx, count=%llu",
273  event->translation_fault.addr, event->translation_fault.dsisr, err->count);
274 #else
275  TRACE(afu, "Translation fault error received, addr=%p, count=%llu",
276  event->translation_fault.addr, err->count);
277 #endif
278  event->translation_fault.count = err->count;
279 }
280 
314 static ocxl_event_action read_afu_event(ocxl_afu_h afu, uint16_t event_api_version, ocxl_event *event, int *last)
315 {
316  ocxl_afu *my_afu = (ocxl_afu *) afu;
317 
318  size_t event_size = sizeof(ocxl_kernel_event_header);
319  *last = 0;
320 
321  uint16_t max_supported_event = 0;
322 
323  switch (event_api_version) {
324  case 0:
325  event_size += sizeof(ocxl_kernel_event_xsl_fault_error);
326  max_supported_event = OCXL_AFU_EVENT_XSL_FAULT_ERROR;
327  break;
328  default:
329  errmsg(afu, OCXL_INTERNAL_ERROR, "Unsupported event API version %u, your libocxl library may be too old",
330  event_api_version);
331  return OCXL_EVENT_ACTION_FAIL;
332  }
333 
334  char buf[event_size];
335 
336  ssize_t buf_used;
337  if ((buf_used = read(my_afu->fd, buf, event_size)) < 0) {
338  if (errno == EAGAIN || errno == EWOULDBLOCK) {
339  *last = 1;
340  return OCXL_EVENT_ACTION_NONE;
341  }
342 
343  errmsg(afu, OCXL_INTERNAL_ERROR, "read of event header from fd %d failed: %d: %s",
344  my_afu->fd, errno, strerror(errno));
345  return OCXL_EVENT_ACTION_FAIL;
346  } else if (buf_used < (ssize_t)sizeof(ocxl_kernel_event_header)) {
347  errmsg(afu, OCXL_INTERNAL_ERROR, "short read of event header from fd %d", my_afu->fd);
348  return OCXL_EVENT_ACTION_FAIL;
349  }
350 
352 
353  if (header->type > max_supported_event) {
354  TRACE(my_afu, "Unknown event received from kernel of type %u", header->type);
355  *last = !! (header->flags & OCXL_KERNEL_EVENT_FLAG_LAST);
356  return OCXL_EVENT_ACTION_IGNORE;
357  }
358 
359  switch (header->type) {
360  case OCXL_AFU_EVENT_XSL_FAULT_ERROR:
361  if (buf_used != sizeof(ocxl_kernel_event_header) + sizeof(ocxl_kernel_event_xsl_fault_error)) {
362  errmsg(my_afu, OCXL_INTERNAL_ERROR,
363  "Incorrectly sized buffer received from kernel for XSL fault error, expected %d, got %d",
365  buf_used);
366  return OCXL_EVENT_ACTION_FAIL;
367  }
368  populate_xsl_fault_error(afu, event, buf + sizeof(ocxl_kernel_event_header));
369  break;
370 
371  default:
372  errmsg(my_afu, OCXL_INTERNAL_ERROR, "Unknown event %d, max_supported_event %d",
373  header->type, max_supported_event);
374  return OCXL_EVENT_ACTION_FAIL;
375  }
376 
377  *last = !! (header->flags & OCXL_KERNEL_EVENT_FLAG_LAST);
378  return OCXL_EVENT_ACTION_SUCCESS;
379 }
380 
406 int ocxl_afu_event_check_versioned(ocxl_afu_h afu, int timeout, ocxl_event *events, uint16_t event_count,
407  uint16_t event_api_version)
408 {
409  ocxl_afu *my_afu = (ocxl_afu *) afu;
410  TRACE(my_afu, "Waiting up to %dms for AFU events", timeout);
411 
412  if (event_count > my_afu->epoll_event_count) {
413  free(my_afu->epoll_events);
414  my_afu->epoll_events = NULL;
415  my_afu->epoll_event_count = 0;
416 
417  struct epoll_event *events = malloc(event_count * sizeof(*events));
418  if (events == NULL) {
419  errmsg(my_afu, OCXL_NO_MEM, "Could not allocate space for %d events", event_count);
420  return -1;
421  }
422 
423  my_afu->epoll_events = events;
424  my_afu->epoll_event_count = event_count;
425  }
426 
427  int count;
428  if ((count = epoll_wait(my_afu->epoll_fd, my_afu->epoll_events, event_count, timeout)) == -1) {
429  errmsg(my_afu, OCXL_INTERNAL_ERROR, "epoll_wait failed waiting for AFU events: %d: '%s'",
430  errno, strerror(errno));
431  return -1;
432  }
433 
434  uint16_t triggered = 0;
435  for (int event = 0; event < count; event++) {
436  epoll_fd_source *info = (epoll_fd_source *)my_afu->epoll_events[event].data.ptr;
437  ocxl_event_action ret;
438  uint64_t count;
439  int last;
440 
441  switch (info->type) {
442  case EPOLL_SOURCE_OCXL:
443  while ((ret = read_afu_event(my_afu, event_api_version, &events[triggered], &last)),
444  ret == OCXL_EVENT_ACTION_SUCCESS || ret == OCXL_EVENT_ACTION_IGNORE) {
445  if (ret == OCXL_EVENT_ACTION_SUCCESS) {
446  triggered++;
447  }
448 
449  if (last) {
450  break;
451  }
452  }
453 
454  if (ret == OCXL_EVENT_ACTION_FAIL) {
455  return -1;
456  }
457 
458  break;
459 
460  case EPOLL_SOURCE_IRQ:
461  if (read(info->irq->event.eventfd, &count, sizeof(count)) < 0) {
462  errmsg(my_afu, OCXL_INTERNAL_ERROR, "read of eventfd %d IRQ %d failed: %d: %s",
463  info->irq->event.eventfd, info->irq->irq_number, errno, strerror(errno));
464  continue;
465  }
466  events[triggered].type = OCXL_EVENT_IRQ;
467  events[triggered].irq.irq = info->irq->irq_number;
468  events[triggered].irq.handle = (uint64_t)info->irq->addr;
469  events[triggered].irq.info = info->irq->info;
470  events[triggered++].irq.count = count;
471 
472  TRACE(my_afu, "IRQ received, irq=%u id=%llx info=%p count=%llu",
473  info->irq->irq_number, (uint64_t)info->irq->addr, info->irq->info, count);
474 
475  break;
476  }
477  }
478 
479  TRACE(my_afu, "%u events reported", triggered);
480 
481  return triggered;
482 }
483 
An AFU IRQ.
Definition: libocxl.h:107
int ocxl_irq_get_fd(ocxl_afu_h afu, ocxl_irq_h irq)
Get the file descriptor associated with an IRQ.
Definition: irq.c:221
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:246
ocxl_err
Potential return values from ocxl_* functions.
Definition: libocxl.h:89
uint16_t ocxl_irq_h
A handle for an IRQ on an AFU.
Definition: libocxl.h:78
void irq_dealloc(ocxl_afu *afu, ocxl_irq *irq)
Deallocate a single IRQ.
Definition: irq.c:42
an internal error has occurred
Definition: libocxl.h:95
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:165
ocxl_event_type type
Definition: libocxl.h:141
A memory translation fault occurred on the AFU.
Definition: libocxl.h:108
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:200
struct ocxl_kernel_event_header ocxl_kernel_event_header
Definition: irq.c:253
void * ocxl_afu_h
A handle for an AFU.
Definition: libocxl.h:71
An out of memory error occurred.
Definition: libocxl.h:91
An OCXL event.
Definition: libocxl.h:140
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:406
struct ocxl_kernel_event_xsl_fault_error ocxl_kernel_event_xsl_fault_error
Definition: irq.c:254
The call succeeded.
Definition: libocxl.h:90