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