Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2010-2014 Intel Corporation
3 : : */
4 : :
5 : : #include <stdio.h>
6 : : #include <stdint.h>
7 : : #include <stdlib.h>
8 : : #include <sys/queue.h>
9 : : #include <unistd.h>
10 : : #include <string.h>
11 : : #include <errno.h>
12 : : #include <sys/epoll.h>
13 : : #include <sys/ioctl.h>
14 : : #include <sys/eventfd.h>
15 : : #include <assert.h>
16 : : #include <stdbool.h>
17 : :
18 : : #include <eal_trace_internal.h>
19 : : #include <rte_common.h>
20 : : #include <rte_interrupts.h>
21 : : #include <rte_thread.h>
22 : : #include <rte_per_lcore.h>
23 : : #include <rte_lcore.h>
24 : : #include <rte_branch_prediction.h>
25 : : #include <rte_debug.h>
26 : : #include <rte_log.h>
27 : : #include <rte_errno.h>
28 : : #include <rte_spinlock.h>
29 : : #include <rte_pause.h>
30 : : #include <rte_vfio.h>
31 : :
32 : : #include "eal_private.h"
33 : :
34 : : #define EAL_INTR_EPOLL_WAIT_FOREVER (-1)
35 : : #define NB_OTHER_INTR 1
36 : :
37 : : #define MAX_ITER_EVNUM RTE_EVENT_ETH_INTR_RING_SIZE
38 : :
39 : : static RTE_DEFINE_PER_LCORE(int, _epfd) = -1; /**< epoll fd per thread */
40 : :
41 : : /**
42 : : * union for pipe fds.
43 : : */
44 : : union intr_pipefds{
45 : : struct {
46 : : int pipefd[2];
47 : : };
48 : : struct {
49 : : int readfd;
50 : : int writefd;
51 : : };
52 : : };
53 : :
54 : : /**
55 : : * union buffer for reading on different devices
56 : : */
57 : : union rte_intr_read_buffer {
58 : : int uio_intr_count; /* for uio device */
59 : : #ifdef VFIO_PRESENT
60 : : uint64_t vfio_intr_count; /* for vfio device */
61 : : #endif
62 : : uint64_t timerfd_num; /* for timerfd */
63 : : char charbuf[16]; /* for others */
64 : : };
65 : :
66 : : TAILQ_HEAD(rte_intr_cb_list, rte_intr_callback);
67 : : TAILQ_HEAD(rte_intr_source_list, rte_intr_source);
68 : :
69 : : struct rte_intr_callback {
70 : : TAILQ_ENTRY(rte_intr_callback) next;
71 : : rte_intr_callback_fn cb_fn; /**< callback address */
72 : : void *cb_arg; /**< parameter for callback */
73 : : uint8_t pending_delete; /**< delete after callback is called */
74 : : rte_intr_unregister_callback_fn ucb_fn; /**< fn to call before cb is deleted */
75 : : };
76 : :
77 : : struct rte_intr_source {
78 : : TAILQ_ENTRY(rte_intr_source) next;
79 : : struct rte_intr_handle *intr_handle; /**< interrupt handle */
80 : : struct rte_intr_cb_list callbacks; /**< user callbacks */
81 : : uint32_t active;
82 : : };
83 : :
84 : : /* global spinlock for interrupt data operation */
85 : : static rte_spinlock_t intr_lock = RTE_SPINLOCK_INITIALIZER;
86 : :
87 : : /* union buffer for pipe read/write */
88 : : static union intr_pipefds intr_pipe;
89 : :
90 : : /* interrupt sources list */
91 : : static struct rte_intr_source_list intr_sources;
92 : :
93 : : /* interrupt handling thread */
94 : : static rte_thread_t intr_thread;
95 : :
96 : : /* VFIO interrupts */
97 : : #ifdef VFIO_PRESENT
98 : :
99 : : #define IRQ_SET_BUF_LEN (sizeof(struct vfio_irq_set) + sizeof(int))
100 : : /* irq set buffer length for queue interrupts and LSC interrupt */
101 : : #define MSIX_IRQ_SET_BUF_LEN (sizeof(struct vfio_irq_set) + \
102 : : sizeof(int) * (RTE_MAX_RXTX_INTR_VEC_ID + 1))
103 : :
104 : : /* enable legacy (INTx) interrupts */
105 : : static int
106 : 0 : vfio_enable_intx(const struct rte_intr_handle *intr_handle) {
107 : : struct vfio_irq_set *irq_set;
108 : : char irq_set_buf[IRQ_SET_BUF_LEN];
109 : : int len, ret, vfio_dev_fd;
110 : : int *fd_ptr;
111 : :
112 : : len = sizeof(irq_set_buf);
113 : :
114 : : /* enable INTx */
115 : : irq_set = (struct vfio_irq_set *) irq_set_buf;
116 : 0 : irq_set->argsz = len;
117 : 0 : irq_set->count = 1;
118 : 0 : irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
119 : 0 : irq_set->index = VFIO_PCI_INTX_IRQ_INDEX;
120 : 0 : irq_set->start = 0;
121 : : fd_ptr = (int *) &irq_set->data;
122 : 0 : *fd_ptr = rte_intr_fd_get(intr_handle);
123 : :
124 : 0 : vfio_dev_fd = rte_intr_dev_fd_get(intr_handle);
125 : 0 : ret = ioctl(vfio_dev_fd, VFIO_DEVICE_SET_IRQS, irq_set);
126 : :
127 [ # # ]: 0 : if (ret) {
128 : 0 : EAL_LOG(ERR, "Error enabling INTx interrupts for fd %d",
129 : : rte_intr_fd_get(intr_handle));
130 : 0 : return -1;
131 : : }
132 : :
133 : : /* unmask INTx after enabling */
134 : : memset(irq_set, 0, len);
135 : : len = sizeof(struct vfio_irq_set);
136 : 0 : irq_set->argsz = len;
137 : 0 : irq_set->count = 1;
138 : 0 : irq_set->flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK;
139 : : irq_set->index = VFIO_PCI_INTX_IRQ_INDEX;
140 : : irq_set->start = 0;
141 : :
142 : 0 : ret = ioctl(vfio_dev_fd, VFIO_DEVICE_SET_IRQS, irq_set);
143 : :
144 [ # # ]: 0 : if (ret) {
145 : 0 : EAL_LOG(ERR, "Error unmasking INTx interrupts for fd %d",
146 : : rte_intr_fd_get(intr_handle));
147 : 0 : return -1;
148 : : }
149 : : return 0;
150 : : }
151 : :
152 : : /* disable legacy (INTx) interrupts */
153 : : static int
154 : 0 : vfio_disable_intx(const struct rte_intr_handle *intr_handle) {
155 : : struct vfio_irq_set *irq_set;
156 : : char irq_set_buf[IRQ_SET_BUF_LEN];
157 : : int len, ret, vfio_dev_fd;
158 : :
159 : : len = sizeof(struct vfio_irq_set);
160 : :
161 : : /* mask interrupts before disabling */
162 : : irq_set = (struct vfio_irq_set *) irq_set_buf;
163 : 0 : irq_set->argsz = len;
164 : 0 : irq_set->count = 1;
165 : 0 : irq_set->flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_MASK;
166 : 0 : irq_set->index = VFIO_PCI_INTX_IRQ_INDEX;
167 : 0 : irq_set->start = 0;
168 : :
169 : 0 : vfio_dev_fd = rte_intr_dev_fd_get(intr_handle);
170 : 0 : ret = ioctl(vfio_dev_fd, VFIO_DEVICE_SET_IRQS, irq_set);
171 : :
172 [ # # ]: 0 : if (ret) {
173 : 0 : EAL_LOG(ERR, "Error masking INTx interrupts for fd %d",
174 : : rte_intr_fd_get(intr_handle));
175 : 0 : return -1;
176 : : }
177 : :
178 : : /* disable INTx*/
179 : : memset(irq_set, 0, len);
180 : 0 : irq_set->argsz = len;
181 : : irq_set->count = 0;
182 : 0 : irq_set->flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER;
183 : : irq_set->index = VFIO_PCI_INTX_IRQ_INDEX;
184 : : irq_set->start = 0;
185 : :
186 : 0 : ret = ioctl(vfio_dev_fd, VFIO_DEVICE_SET_IRQS, irq_set);
187 : :
188 [ # # ]: 0 : if (ret) {
189 : 0 : EAL_LOG(ERR, "Error disabling INTx interrupts for fd %d",
190 : : rte_intr_fd_get(intr_handle));
191 : 0 : return -1;
192 : : }
193 : : return 0;
194 : : }
195 : :
196 : : /* unmask/ack legacy (INTx) interrupts */
197 : : static int
198 : 0 : vfio_ack_intx(const struct rte_intr_handle *intr_handle)
199 : : {
200 : : struct vfio_irq_set irq_set;
201 : : int vfio_dev_fd;
202 : :
203 : : /* unmask INTx */
204 : : memset(&irq_set, 0, sizeof(irq_set));
205 : 0 : irq_set.argsz = sizeof(irq_set);
206 : 0 : irq_set.count = 1;
207 : 0 : irq_set.flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK;
208 : : irq_set.index = VFIO_PCI_INTX_IRQ_INDEX;
209 : : irq_set.start = 0;
210 : :
211 : 0 : vfio_dev_fd = rte_intr_dev_fd_get(intr_handle);
212 [ # # ]: 0 : if (ioctl(vfio_dev_fd, VFIO_DEVICE_SET_IRQS, &irq_set)) {
213 : 0 : EAL_LOG(ERR, "Error unmasking INTx interrupts for fd %d",
214 : : rte_intr_fd_get(intr_handle));
215 : 0 : return -1;
216 : : }
217 : : return 0;
218 : : }
219 : :
220 : : /* enable MSI interrupts */
221 : : static int
222 : 0 : vfio_enable_msi(const struct rte_intr_handle *intr_handle) {
223 : : int len, ret;
224 : : char irq_set_buf[IRQ_SET_BUF_LEN];
225 : : struct vfio_irq_set *irq_set;
226 : : int *fd_ptr, vfio_dev_fd;
227 : :
228 : : len = sizeof(irq_set_buf);
229 : :
230 : : irq_set = (struct vfio_irq_set *) irq_set_buf;
231 : 0 : irq_set->argsz = len;
232 : 0 : irq_set->count = 1;
233 : 0 : irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
234 : 0 : irq_set->index = VFIO_PCI_MSI_IRQ_INDEX;
235 : 0 : irq_set->start = 0;
236 : : fd_ptr = (int *) &irq_set->data;
237 : 0 : *fd_ptr = rte_intr_fd_get(intr_handle);
238 : :
239 : 0 : vfio_dev_fd = rte_intr_dev_fd_get(intr_handle);
240 : 0 : ret = ioctl(vfio_dev_fd, VFIO_DEVICE_SET_IRQS, irq_set);
241 : :
242 [ # # ]: 0 : if (ret) {
243 : 0 : EAL_LOG(ERR, "Error enabling MSI interrupts for fd %d",
244 : : rte_intr_fd_get(intr_handle));
245 : 0 : return -1;
246 : : }
247 : : return 0;
248 : : }
249 : :
250 : : /* disable MSI interrupts */
251 : : static int
252 : 0 : vfio_disable_msi(const struct rte_intr_handle *intr_handle) {
253 : : struct vfio_irq_set *irq_set;
254 : : char irq_set_buf[IRQ_SET_BUF_LEN];
255 : : int len, ret, vfio_dev_fd;
256 : :
257 : : len = sizeof(struct vfio_irq_set);
258 : :
259 : : irq_set = (struct vfio_irq_set *) irq_set_buf;
260 : 0 : irq_set->argsz = len;
261 : 0 : irq_set->count = 0;
262 : 0 : irq_set->flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER;
263 : 0 : irq_set->index = VFIO_PCI_MSI_IRQ_INDEX;
264 : 0 : irq_set->start = 0;
265 : :
266 : 0 : vfio_dev_fd = rte_intr_dev_fd_get(intr_handle);
267 : 0 : ret = ioctl(vfio_dev_fd, VFIO_DEVICE_SET_IRQS, irq_set);
268 [ # # ]: 0 : if (ret)
269 : 0 : EAL_LOG(ERR, "Error disabling MSI interrupts for fd %d",
270 : : rte_intr_fd_get(intr_handle));
271 : :
272 : 0 : return ret;
273 : : }
274 : :
275 : : /* enable MSI-X interrupts */
276 : : static int
277 : 0 : vfio_enable_msix(const struct rte_intr_handle *intr_handle) {
278 : : int len, ret;
279 : : char irq_set_buf[MSIX_IRQ_SET_BUF_LEN];
280 : : struct vfio_irq_set *irq_set;
281 : : int *fd_ptr, vfio_dev_fd, i;
282 : :
283 : : len = sizeof(irq_set_buf);
284 : :
285 : : irq_set = (struct vfio_irq_set *) irq_set_buf;
286 : 0 : irq_set->argsz = len;
287 : : /* 0 < irq_set->count < RTE_MAX_RXTX_INTR_VEC_ID + 1 */
288 : 0 : irq_set->count = rte_intr_max_intr_get(intr_handle) ?
289 : 0 : (rte_intr_max_intr_get(intr_handle) >
290 : : RTE_MAX_RXTX_INTR_VEC_ID + 1 ? RTE_MAX_RXTX_INTR_VEC_ID + 1 :
291 [ # # # # ]: 0 : rte_intr_max_intr_get(intr_handle)) : 1;
292 : :
293 : 0 : irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | VFIO_IRQ_SET_ACTION_TRIGGER;
294 : 0 : irq_set->index = VFIO_PCI_MSIX_IRQ_INDEX;
295 : 0 : irq_set->start = 0;
296 : : fd_ptr = (int *) &irq_set->data;
297 : : /* INTR vector offset 0 reserve for non-efds mapping */
298 : 0 : fd_ptr[RTE_INTR_VEC_ZERO_OFFSET] = rte_intr_fd_get(intr_handle);
299 [ # # ]: 0 : for (i = 0; i < rte_intr_nb_efd_get(intr_handle); i++) {
300 : 0 : fd_ptr[RTE_INTR_VEC_RXTX_OFFSET + i] =
301 : 0 : rte_intr_efds_index_get(intr_handle, i);
302 : : }
303 : :
304 : 0 : vfio_dev_fd = rte_intr_dev_fd_get(intr_handle);
305 : 0 : ret = ioctl(vfio_dev_fd, VFIO_DEVICE_SET_IRQS, irq_set);
306 : :
307 [ # # ]: 0 : if (ret) {
308 : 0 : EAL_LOG(ERR, "Error enabling MSI-X interrupts for fd %d",
309 : : rte_intr_fd_get(intr_handle));
310 : 0 : return -1;
311 : : }
312 : :
313 : : return 0;
314 : : }
315 : :
316 : : /* disable MSI-X interrupts */
317 : : static int
318 : 0 : vfio_disable_msix(const struct rte_intr_handle *intr_handle) {
319 : : struct vfio_irq_set *irq_set;
320 : : char irq_set_buf[MSIX_IRQ_SET_BUF_LEN];
321 : : int len, ret, vfio_dev_fd;
322 : :
323 : : len = sizeof(struct vfio_irq_set);
324 : :
325 : : irq_set = (struct vfio_irq_set *) irq_set_buf;
326 : 0 : irq_set->argsz = len;
327 : 0 : irq_set->count = 0;
328 : 0 : irq_set->flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER;
329 : 0 : irq_set->index = VFIO_PCI_MSIX_IRQ_INDEX;
330 : 0 : irq_set->start = 0;
331 : :
332 : 0 : vfio_dev_fd = rte_intr_dev_fd_get(intr_handle);
333 : 0 : ret = ioctl(vfio_dev_fd, VFIO_DEVICE_SET_IRQS, irq_set);
334 : :
335 [ # # ]: 0 : if (ret)
336 : 0 : EAL_LOG(ERR, "Error disabling MSI-X interrupts for fd %d",
337 : : rte_intr_fd_get(intr_handle));
338 : :
339 : 0 : return ret;
340 : : }
341 : :
342 : : #ifdef HAVE_VFIO_DEV_REQ_INTERFACE
343 : : /* enable req notifier */
344 : : static int
345 : 0 : vfio_enable_req(const struct rte_intr_handle *intr_handle)
346 : : {
347 : : int len, ret;
348 : : char irq_set_buf[IRQ_SET_BUF_LEN];
349 : : struct vfio_irq_set *irq_set;
350 : : int *fd_ptr, vfio_dev_fd;
351 : :
352 : : len = sizeof(irq_set_buf);
353 : :
354 : : irq_set = (struct vfio_irq_set *) irq_set_buf;
355 : 0 : irq_set->argsz = len;
356 : 0 : irq_set->count = 1;
357 : 0 : irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD |
358 : : VFIO_IRQ_SET_ACTION_TRIGGER;
359 : 0 : irq_set->index = VFIO_PCI_REQ_IRQ_INDEX;
360 : 0 : irq_set->start = 0;
361 : : fd_ptr = (int *) &irq_set->data;
362 : 0 : *fd_ptr = rte_intr_fd_get(intr_handle);
363 : :
364 : 0 : vfio_dev_fd = rte_intr_dev_fd_get(intr_handle);
365 : 0 : ret = ioctl(vfio_dev_fd, VFIO_DEVICE_SET_IRQS, irq_set);
366 : :
367 [ # # ]: 0 : if (ret) {
368 : 0 : EAL_LOG(ERR, "Error enabling req interrupts for fd %d",
369 : : rte_intr_fd_get(intr_handle));
370 : 0 : return -1;
371 : : }
372 : :
373 : : return 0;
374 : : }
375 : :
376 : : /* disable req notifier */
377 : : static int
378 : 0 : vfio_disable_req(const struct rte_intr_handle *intr_handle)
379 : : {
380 : : struct vfio_irq_set *irq_set;
381 : : char irq_set_buf[IRQ_SET_BUF_LEN];
382 : : int len, ret, vfio_dev_fd;
383 : :
384 : : len = sizeof(struct vfio_irq_set);
385 : :
386 : : irq_set = (struct vfio_irq_set *) irq_set_buf;
387 : 0 : irq_set->argsz = len;
388 : 0 : irq_set->count = 0;
389 : 0 : irq_set->flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER;
390 : 0 : irq_set->index = VFIO_PCI_REQ_IRQ_INDEX;
391 : 0 : irq_set->start = 0;
392 : :
393 : 0 : vfio_dev_fd = rte_intr_dev_fd_get(intr_handle);
394 : 0 : ret = ioctl(vfio_dev_fd, VFIO_DEVICE_SET_IRQS, irq_set);
395 : :
396 [ # # ]: 0 : if (ret)
397 : 0 : EAL_LOG(ERR, "Error disabling req interrupts for fd %d",
398 : : rte_intr_fd_get(intr_handle));
399 : :
400 : 0 : return ret;
401 : : }
402 : : #endif
403 : : #endif
404 : :
405 : : static int
406 : 0 : uio_intx_intr_disable(const struct rte_intr_handle *intr_handle)
407 : : {
408 : : unsigned char command_high;
409 : : int uio_cfg_fd;
410 : :
411 : : /* use UIO config file descriptor for uio_pci_generic */
412 : 0 : uio_cfg_fd = rte_intr_dev_fd_get(intr_handle);
413 [ # # # # ]: 0 : if (uio_cfg_fd < 0 || pread(uio_cfg_fd, &command_high, 1, 5) != 1) {
414 : 0 : EAL_LOG(ERR,
415 : : "Error reading interrupts status for fd %d",
416 : : uio_cfg_fd);
417 : 0 : return -1;
418 : : }
419 : : /* disable interrupts */
420 : 0 : command_high |= 0x4;
421 [ # # ]: 0 : if (pwrite(uio_cfg_fd, &command_high, 1, 5) != 1) {
422 : 0 : EAL_LOG(ERR,
423 : : "Error disabling interrupts for fd %d",
424 : : uio_cfg_fd);
425 : 0 : return -1;
426 : : }
427 : :
428 : : return 0;
429 : : }
430 : :
431 : : static int
432 : 0 : uio_intx_intr_enable(const struct rte_intr_handle *intr_handle)
433 : : {
434 : : unsigned char command_high;
435 : : int uio_cfg_fd;
436 : :
437 : : /* use UIO config file descriptor for uio_pci_generic */
438 : 0 : uio_cfg_fd = rte_intr_dev_fd_get(intr_handle);
439 [ # # # # ]: 0 : if (uio_cfg_fd < 0 || pread(uio_cfg_fd, &command_high, 1, 5) != 1) {
440 : 0 : EAL_LOG(ERR,
441 : : "Error reading interrupts status for fd %d",
442 : : uio_cfg_fd);
443 : 0 : return -1;
444 : : }
445 : : /* enable interrupts */
446 : 0 : command_high &= ~0x4;
447 [ # # ]: 0 : if (pwrite(uio_cfg_fd, &command_high, 1, 5) != 1) {
448 : 0 : EAL_LOG(ERR,
449 : : "Error enabling interrupts for fd %d",
450 : : uio_cfg_fd);
451 : 0 : return -1;
452 : : }
453 : :
454 : : return 0;
455 : : }
456 : :
457 : : static int
458 : 2 : uio_intr_disable(const struct rte_intr_handle *intr_handle)
459 : : {
460 : 2 : const int value = 0;
461 : :
462 [ + - + + ]: 4 : if (rte_intr_fd_get(intr_handle) < 0 ||
463 : 2 : write(rte_intr_fd_get(intr_handle), &value, sizeof(value)) < 0) {
464 : 1 : EAL_LOG(ERR, "Error disabling interrupts for fd %d (%s)",
465 : : rte_intr_fd_get(intr_handle), strerror(errno));
466 : 1 : return -1;
467 : : }
468 : : return 0;
469 : : }
470 : :
471 : : static int
472 : 2 : uio_intr_enable(const struct rte_intr_handle *intr_handle)
473 : : {
474 : 2 : const int value = 1;
475 : :
476 [ + - + + ]: 4 : if (rte_intr_fd_get(intr_handle) < 0 ||
477 : 2 : write(rte_intr_fd_get(intr_handle), &value, sizeof(value)) < 0) {
478 : 1 : EAL_LOG(ERR, "Error enabling interrupts for fd %d (%s)",
479 : : rte_intr_fd_get(intr_handle), strerror(errno));
480 : 1 : return -1;
481 : : }
482 : : return 0;
483 : : }
484 : :
485 : : int
486 : 11 : rte_intr_callback_register(const struct rte_intr_handle *intr_handle,
487 : : rte_intr_callback_fn cb, void *cb_arg)
488 : : {
489 : : int ret, wake_thread;
490 : : struct rte_intr_source *src;
491 : : struct rte_intr_callback *callback;
492 : :
493 : : wake_thread = 0;
494 : :
495 : : /* first do parameter checking */
496 [ + + + + ]: 11 : if (rte_intr_fd_get(intr_handle) < 0 || cb == NULL) {
497 : 3 : EAL_LOG(ERR, "Registering with invalid input parameter");
498 : 3 : return -EINVAL;
499 : : }
500 : :
501 : : /* allocate a new interrupt callback entity */
502 : 8 : callback = calloc(1, sizeof(*callback));
503 [ - + ]: 8 : if (callback == NULL) {
504 : 0 : EAL_LOG(ERR, "Can not allocate memory");
505 : 0 : return -ENOMEM;
506 : : }
507 : 8 : callback->cb_fn = cb;
508 : 8 : callback->cb_arg = cb_arg;
509 : 8 : callback->pending_delete = 0;
510 : 8 : callback->ucb_fn = NULL;
511 : :
512 : : rte_spinlock_lock(&intr_lock);
513 : :
514 : : /* check if there is at least one callback registered for the fd */
515 [ + + ]: 8 : TAILQ_FOREACH(src, &intr_sources, next) {
516 [ + - ]: 1 : if (rte_intr_fd_get(src->intr_handle) == rte_intr_fd_get(intr_handle)) {
517 : : /* we had no interrupts for this */
518 [ - + ]: 1 : if (TAILQ_EMPTY(&src->callbacks))
519 : : wake_thread = 1;
520 : :
521 : 1 : TAILQ_INSERT_TAIL(&(src->callbacks), callback, next);
522 : : ret = 0;
523 : 1 : break;
524 : : }
525 : : }
526 : :
527 : : /* no existing callbacks for this - add new source */
528 [ + + ]: 8 : if (src == NULL) {
529 : 7 : src = calloc(1, sizeof(*src));
530 [ - + ]: 7 : if (src == NULL) {
531 : 0 : EAL_LOG(ERR, "Can not allocate memory");
532 : : ret = -ENOMEM;
533 : 0 : free(callback);
534 : : callback = NULL;
535 : : } else {
536 : 7 : src->intr_handle = rte_intr_instance_dup(intr_handle);
537 [ - + ]: 7 : if (src->intr_handle == NULL) {
538 : 0 : EAL_LOG(ERR, "Can not create intr instance");
539 : : ret = -ENOMEM;
540 : 0 : free(callback);
541 : : callback = NULL;
542 : 0 : free(src);
543 : : src = NULL;
544 : : } else {
545 : 7 : TAILQ_INIT(&src->callbacks);
546 : 7 : TAILQ_INSERT_TAIL(&(src->callbacks), callback,
547 : : next);
548 : 7 : TAILQ_INSERT_TAIL(&intr_sources, src, next);
549 : : wake_thread = 1;
550 : : ret = 0;
551 : : }
552 : : }
553 : : }
554 : :
555 : : rte_spinlock_unlock(&intr_lock);
556 : :
557 : : /**
558 : : * check if need to notify the pipe fd waited by epoll_wait to
559 : : * rebuild the wait list.
560 : : */
561 [ + + ]: 8 : if (wake_thread)
562 [ - + ]: 7 : if (write(intr_pipe.writefd, "1", 1) < 0)
563 : : ret = -EPIPE;
564 : :
565 : 8 : rte_eal_trace_intr_callback_register(intr_handle, cb, cb_arg, ret);
566 : 8 : return ret;
567 : : }
568 : :
569 : : int
570 : 0 : rte_intr_callback_unregister_pending(const struct rte_intr_handle *intr_handle,
571 : : rte_intr_callback_fn cb_fn, void *cb_arg,
572 : : rte_intr_unregister_callback_fn ucb_fn)
573 : : {
574 : : int ret;
575 : : struct rte_intr_source *src;
576 : : struct rte_intr_callback *cb, *next;
577 : :
578 : : /* do parameter checking first */
579 [ # # ]: 0 : if (rte_intr_fd_get(intr_handle) < 0) {
580 : 0 : EAL_LOG(ERR, "Unregistering with invalid input parameter");
581 : 0 : return -EINVAL;
582 : : }
583 : :
584 : : rte_spinlock_lock(&intr_lock);
585 : :
586 : : /* check if the interrupt source for the fd is existent */
587 [ # # ]: 0 : TAILQ_FOREACH(src, &intr_sources, next) {
588 [ # # ]: 0 : if (rte_intr_fd_get(src->intr_handle) == rte_intr_fd_get(intr_handle))
589 : : break;
590 : : }
591 : :
592 : : /* No interrupt source registered for the fd */
593 [ # # ]: 0 : if (src == NULL) {
594 : : ret = -ENOENT;
595 : :
596 : : /* only usable if the source is active */
597 [ # # ]: 0 : } else if (src->active == 0) {
598 : : ret = -EAGAIN;
599 : :
600 : : } else {
601 : : ret = 0;
602 : :
603 : : /* walk through the callbacks and mark all that match. */
604 [ # # ]: 0 : for (cb = TAILQ_FIRST(&src->callbacks); cb != NULL; cb = next) {
605 : 0 : next = TAILQ_NEXT(cb, next);
606 [ # # # # ]: 0 : if (cb->cb_fn == cb_fn && (cb_arg == (void *)-1 ||
607 [ # # ]: 0 : cb->cb_arg == cb_arg)) {
608 : 0 : cb->pending_delete = 1;
609 : 0 : cb->ucb_fn = ucb_fn;
610 : 0 : ret++;
611 : : }
612 : : }
613 : : }
614 : :
615 : : rte_spinlock_unlock(&intr_lock);
616 : :
617 : 0 : return ret;
618 : : }
619 : :
620 : : int
621 : 1499516 : rte_intr_callback_unregister(const struct rte_intr_handle *intr_handle,
622 : : rte_intr_callback_fn cb_fn, void *cb_arg)
623 : : {
624 : : int ret;
625 : : struct rte_intr_source *src;
626 : : struct rte_intr_callback *cb, *next;
627 : :
628 : : /* do parameter checking first */
629 [ + + ]: 1499516 : if (rte_intr_fd_get(intr_handle) < 0) {
630 : 2 : EAL_LOG(ERR, "Unregistering with invalid input parameter");
631 : 2 : return -EINVAL;
632 : : }
633 : :
634 : : rte_spinlock_lock(&intr_lock);
635 : :
636 : : /* check if the interrupt source for the fd is existent */
637 [ + + ]: 1499514 : TAILQ_FOREACH(src, &intr_sources, next)
638 [ - + ]: 1499506 : if (rte_intr_fd_get(src->intr_handle) == rte_intr_fd_get(intr_handle))
639 : : break;
640 : :
641 : : /* No interrupt source registered for the fd */
642 [ + + ]: 1499514 : if (src == NULL) {
643 : : ret = -ENOENT;
644 : :
645 : : /* interrupt source has some active callbacks right now. */
646 [ + + ]: 1499506 : } else if (src->active != 0) {
647 : : ret = -EAGAIN;
648 : :
649 : : /* ok to remove. */
650 : : } else {
651 : : ret = 0;
652 : :
653 : : /*walk through the callbacks and remove all that match. */
654 [ + + ]: 16 : for (cb = TAILQ_FIRST(&src->callbacks); cb != NULL; cb = next) {
655 : :
656 : 9 : next = TAILQ_NEXT(cb, next);
657 : :
658 [ + + + + ]: 9 : if (cb->cb_fn == cb_fn && (cb_arg == (void *)-1 ||
659 [ + + ]: 6 : cb->cb_arg == cb_arg)) {
660 [ + + ]: 6 : TAILQ_REMOVE(&src->callbacks, cb, next);
661 : 6 : free(cb);
662 : 6 : ret++;
663 : : }
664 : : }
665 : :
666 : : /* all callbacks for that source are removed. */
667 [ + + ]: 7 : if (TAILQ_EMPTY(&src->callbacks)) {
668 [ - + ]: 5 : TAILQ_REMOVE(&intr_sources, src, next);
669 : 5 : rte_intr_instance_free(src->intr_handle);
670 : 5 : free(src);
671 : : }
672 : : }
673 : :
674 : : rte_spinlock_unlock(&intr_lock);
675 : :
676 : : /* notify the pipe fd waited by epoll_wait to rebuild the wait list */
677 [ + + - + ]: 1499514 : if (ret >= 0 && write(intr_pipe.writefd, "1", 1) < 0) {
678 : : ret = -EPIPE;
679 : : }
680 : :
681 : 1499514 : rte_eal_trace_intr_callback_unregister(intr_handle, cb_fn, cb_arg,
682 : : ret);
683 : 1499514 : return ret;
684 : : }
685 : :
686 : : int
687 : 0 : rte_intr_callback_unregister_sync(const struct rte_intr_handle *intr_handle,
688 : : rte_intr_callback_fn cb_fn, void *cb_arg)
689 : : {
690 : : int ret = 0;
691 : :
692 [ # # ]: 0 : while ((ret = rte_intr_callback_unregister(intr_handle, cb_fn, cb_arg)) == -EAGAIN)
693 : : rte_pause();
694 : :
695 : 0 : return ret;
696 : : }
697 : :
698 : : int
699 : 7 : rte_intr_enable(const struct rte_intr_handle *intr_handle)
700 : : {
701 : : int rc = 0, uio_cfg_fd;
702 : :
703 [ + + ]: 7 : if (intr_handle == NULL)
704 : : return -1;
705 : :
706 [ - + ]: 6 : if (rte_intr_type_get(intr_handle) == RTE_INTR_HANDLE_VDEV) {
707 : : rc = 0;
708 : 0 : goto out;
709 : : }
710 : :
711 : 6 : uio_cfg_fd = rte_intr_dev_fd_get(intr_handle);
712 [ + + - + ]: 6 : if (rte_intr_fd_get(intr_handle) < 0 || uio_cfg_fd < 0) {
713 : : rc = -1;
714 : 1 : goto out;
715 : : }
716 : :
717 [ + - - - : 5 : switch (rte_intr_type_get(intr_handle)) {
- - + + ]
718 : : /* write to the uio fd to enable the interrupt */
719 : 2 : case RTE_INTR_HANDLE_UIO:
720 [ + + ]: 2 : if (uio_intr_enable(intr_handle))
721 : : rc = -1;
722 : : break;
723 : 0 : case RTE_INTR_HANDLE_UIO_INTX:
724 [ # # ]: 0 : if (uio_intx_intr_enable(intr_handle))
725 : : rc = -1;
726 : : break;
727 : : /* not used at this moment */
728 : : case RTE_INTR_HANDLE_ALARM:
729 : : rc = -1;
730 : : break;
731 : : #ifdef VFIO_PRESENT
732 : 0 : case RTE_INTR_HANDLE_VFIO_MSIX:
733 [ # # ]: 0 : if (vfio_enable_msix(intr_handle))
734 : : rc = -1;
735 : : break;
736 : 0 : case RTE_INTR_HANDLE_VFIO_MSI:
737 [ # # ]: 0 : if (vfio_enable_msi(intr_handle))
738 : : rc = -1;
739 : : break;
740 : 0 : case RTE_INTR_HANDLE_VFIO_LEGACY:
741 [ # # ]: 0 : if (vfio_enable_intx(intr_handle))
742 : : rc = -1;
743 : : break;
744 : : #ifdef HAVE_VFIO_DEV_REQ_INTERFACE
745 : 0 : case RTE_INTR_HANDLE_VFIO_REQ:
746 [ # # ]: 0 : if (vfio_enable_req(intr_handle))
747 : : rc = -1;
748 : : break;
749 : : #endif
750 : : #endif
751 : : /* not used at this moment */
752 : : case RTE_INTR_HANDLE_DEV_EVENT:
753 : : rc = -1;
754 : : break;
755 : : /* unknown handle type */
756 : 1 : default:
757 : 1 : EAL_LOG(ERR, "Unknown handle type of fd %d",
758 : : rte_intr_fd_get(intr_handle));
759 : : rc = -1;
760 : 1 : break;
761 : : }
762 [ - + ]: 6 : out:
763 : 6 : rte_eal_trace_intr_enable(intr_handle, rc);
764 : 6 : return rc;
765 : : }
766 : :
767 : : /**
768 : : * PMD generally calls this function at the end of its IRQ callback.
769 : : * Internally, it unmasks the interrupt if possible.
770 : : *
771 : : * For INTx, unmasking is required as the interrupt is auto-masked prior to
772 : : * invoking callback.
773 : : *
774 : : * For MSI/MSI-X, unmasking is typically not needed as the interrupt is not
775 : : * auto-masked. In fact, for interrupt handle types VFIO_MSIX and VFIO_MSI,
776 : : * this function is no-op.
777 : : */
778 : : int
779 : 0 : rte_intr_ack(const struct rte_intr_handle *intr_handle)
780 : : {
781 : : int uio_cfg_fd;
782 : :
783 [ # # ]: 0 : if (rte_intr_type_get(intr_handle) == RTE_INTR_HANDLE_VDEV)
784 : : return 0;
785 : :
786 : 0 : uio_cfg_fd = rte_intr_dev_fd_get(intr_handle);
787 [ # # # # ]: 0 : if (rte_intr_fd_get(intr_handle) < 0 || uio_cfg_fd < 0)
788 : : return -1;
789 : :
790 [ # # # # : 0 : switch (rte_intr_type_get(intr_handle)) {
# # ]
791 : : /* Both acking and enabling are same for UIO */
792 : 0 : case RTE_INTR_HANDLE_UIO:
793 [ # # ]: 0 : if (uio_intr_enable(intr_handle))
794 : 0 : return -1;
795 : : break;
796 : 0 : case RTE_INTR_HANDLE_UIO_INTX:
797 [ # # ]: 0 : if (uio_intx_intr_enable(intr_handle))
798 : 0 : return -1;
799 : : break;
800 : : /* not used at this moment */
801 : : case RTE_INTR_HANDLE_ALARM:
802 : : return -1;
803 : : #ifdef VFIO_PRESENT
804 : : /* VFIO MSI* is implicitly acked unlike INTx, nothing to do */
805 : 0 : case RTE_INTR_HANDLE_VFIO_MSIX:
806 : : case RTE_INTR_HANDLE_VFIO_MSI:
807 : 0 : return 0;
808 : 0 : case RTE_INTR_HANDLE_VFIO_LEGACY:
809 [ # # ]: 0 : if (vfio_ack_intx(intr_handle))
810 : 0 : return -1;
811 : : break;
812 : : #ifdef HAVE_VFIO_DEV_REQ_INTERFACE
813 : : case RTE_INTR_HANDLE_VFIO_REQ:
814 : : return -1;
815 : : #endif
816 : : #endif
817 : : /* not used at this moment */
818 : : case RTE_INTR_HANDLE_DEV_EVENT:
819 : : return -1;
820 : : /* unknown handle type */
821 : 0 : default:
822 : 0 : EAL_LOG(ERR, "Unknown handle type of fd %d",
823 : : rte_intr_fd_get(intr_handle));
824 : 0 : return -1;
825 : : }
826 : :
827 : : return 0;
828 : : }
829 : :
830 : : int
831 : 7 : rte_intr_disable(const struct rte_intr_handle *intr_handle)
832 : : {
833 : : int rc = 0, uio_cfg_fd;
834 : :
835 [ + + ]: 7 : if (intr_handle == NULL)
836 : : return -1;
837 : :
838 [ - + ]: 6 : if (rte_intr_type_get(intr_handle) == RTE_INTR_HANDLE_VDEV) {
839 : : rc = 0;
840 : 0 : goto out;
841 : : }
842 : :
843 : 6 : uio_cfg_fd = rte_intr_dev_fd_get(intr_handle);
844 [ + + - + ]: 6 : if (rte_intr_fd_get(intr_handle) < 0 || uio_cfg_fd < 0) {
845 : : rc = -1;
846 : 1 : goto out;
847 : : }
848 : :
849 [ + - - - : 5 : switch (rte_intr_type_get(intr_handle)) {
- - + + ]
850 : : /* write to the uio fd to disable the interrupt */
851 : 2 : case RTE_INTR_HANDLE_UIO:
852 [ + + ]: 2 : if (uio_intr_disable(intr_handle))
853 : : rc = -1;
854 : : break;
855 : 0 : case RTE_INTR_HANDLE_UIO_INTX:
856 [ # # ]: 0 : if (uio_intx_intr_disable(intr_handle))
857 : : rc = -1;
858 : : break;
859 : : /* not used at this moment */
860 : : case RTE_INTR_HANDLE_ALARM:
861 : : rc = -1;
862 : : break;
863 : : #ifdef VFIO_PRESENT
864 : 0 : case RTE_INTR_HANDLE_VFIO_MSIX:
865 [ # # ]: 0 : if (vfio_disable_msix(intr_handle))
866 : : rc = -1;
867 : : break;
868 : 0 : case RTE_INTR_HANDLE_VFIO_MSI:
869 [ # # ]: 0 : if (vfio_disable_msi(intr_handle))
870 : : rc = -1;
871 : : break;
872 : 0 : case RTE_INTR_HANDLE_VFIO_LEGACY:
873 [ # # ]: 0 : if (vfio_disable_intx(intr_handle))
874 : : rc = -1;
875 : : break;
876 : : #ifdef HAVE_VFIO_DEV_REQ_INTERFACE
877 : 0 : case RTE_INTR_HANDLE_VFIO_REQ:
878 [ # # ]: 0 : if (vfio_disable_req(intr_handle))
879 : : rc = -1;
880 : : break;
881 : : #endif
882 : : #endif
883 : : /* not used at this moment */
884 : : case RTE_INTR_HANDLE_DEV_EVENT:
885 : : rc = -1;
886 : : break;
887 : : /* unknown handle type */
888 : 1 : default:
889 : 1 : EAL_LOG(ERR, "Unknown handle type of fd %d",
890 : : rte_intr_fd_get(intr_handle));
891 : : rc = -1;
892 : 1 : break;
893 : : }
894 [ - + ]: 6 : out:
895 : 6 : rte_eal_trace_intr_disable(intr_handle, rc);
896 : 6 : return rc;
897 : : }
898 : :
899 : : static int
900 : 104385 : eal_intr_process_interrupts(struct epoll_event *events, int nfds)
901 : : {
902 : : bool call = false;
903 : : int n, bytes_read, rv;
904 : : struct rte_intr_source *src;
905 : : struct rte_intr_callback *cb, *next;
906 : : union rte_intr_read_buffer buf;
907 : : struct rte_intr_callback active_cb;
908 : :
909 [ + + ]: 208757 : for (n = 0; n < nfds; n++) {
910 : :
911 : : /**
912 : : * if the pipe fd is ready to read, return out to
913 : : * rebuild the wait list.
914 : : */
915 [ + + ]: 104385 : if (events[n].data.fd == intr_pipe.readfd){
916 : : int r = read(intr_pipe.readfd, buf.charbuf,
917 : : sizeof(buf.charbuf));
918 : : RTE_SET_USED(r);
919 : 13 : return -1;
920 : : }
921 : : rte_spinlock_lock(&intr_lock);
922 [ + - ]: 104372 : TAILQ_FOREACH(src, &intr_sources, next)
923 [ - + ]: 104372 : if (rte_intr_fd_get(src->intr_handle) == events[n].data.fd)
924 : : break;
925 [ - + ]: 104372 : if (src == NULL){
926 : : rte_spinlock_unlock(&intr_lock);
927 : 0 : continue;
928 : : }
929 : :
930 : : /* mark this interrupt source as active and release the lock. */
931 : 104372 : src->active = 1;
932 : : rte_spinlock_unlock(&intr_lock);
933 : :
934 : : /* set the length to be read dor different handle type */
935 [ + - + + : 104372 : switch (rte_intr_type_get(src->intr_handle)) {
+ ]
936 : : case RTE_INTR_HANDLE_UIO:
937 : : case RTE_INTR_HANDLE_UIO_INTX:
938 : : bytes_read = sizeof(buf.uio_intr_count);
939 : : break;
940 : : case RTE_INTR_HANDLE_ALARM:
941 : : bytes_read = sizeof(buf.timerfd_num);
942 : : break;
943 : : #ifdef VFIO_PRESENT
944 : : #ifdef HAVE_VFIO_DEV_REQ_INTERFACE
945 : : case RTE_INTR_HANDLE_VFIO_REQ:
946 : : #endif
947 : : case RTE_INTR_HANDLE_VFIO_MSIX:
948 : : case RTE_INTR_HANDLE_VFIO_MSI:
949 : : case RTE_INTR_HANDLE_VFIO_LEGACY:
950 : : bytes_read = sizeof(buf.vfio_intr_count);
951 : : break;
952 : : #endif
953 : : case RTE_INTR_HANDLE_VDEV:
954 : : case RTE_INTR_HANDLE_EXT:
955 : : bytes_read = 0;
956 : : call = true;
957 : : break;
958 : : case RTE_INTR_HANDLE_DEV_EVENT:
959 : : bytes_read = 0;
960 : : call = true;
961 : : break;
962 : : default:
963 : : bytes_read = 1;
964 : : break;
965 : : }
966 : :
967 : : if (bytes_read > 0) {
968 : : /**
969 : : * read out to clear the ready-to-be-read flag
970 : : * for epoll_wait.
971 : : */
972 : 4 : bytes_read = read(events[n].data.fd, &buf, bytes_read);
973 [ - + ]: 4 : if (bytes_read < 0) {
974 [ # # ]: 0 : if (errno == EINTR || errno == EWOULDBLOCK)
975 : 0 : continue;
976 : :
977 : 0 : EAL_LOG(ERR, "Error reading from file "
978 : : "descriptor %d: %s",
979 : : events[n].data.fd,
980 : : strerror(errno));
981 : : /*
982 : : * The device is unplugged or buggy, remove
983 : : * it as an interrupt source and return to
984 : : * force the wait list to be rebuilt.
985 : : */
986 : : rte_spinlock_lock(&intr_lock);
987 [ # # ]: 0 : TAILQ_REMOVE(&intr_sources, src, next);
988 : : rte_spinlock_unlock(&intr_lock);
989 : :
990 [ # # ]: 0 : for (cb = TAILQ_FIRST(&src->callbacks); cb;
991 : : cb = next) {
992 : 0 : next = TAILQ_NEXT(cb, next);
993 [ # # ]: 0 : TAILQ_REMOVE(&src->callbacks, cb, next);
994 : 0 : free(cb);
995 : : }
996 : 0 : rte_intr_instance_free(src->intr_handle);
997 : 0 : free(src);
998 : 0 : return -1;
999 [ - + ]: 4 : } else if (bytes_read == 0)
1000 : 0 : EAL_LOG(ERR, "Read nothing from file "
1001 : : "descriptor %d", events[n].data.fd);
1002 : : else
1003 : : call = true;
1004 : : }
1005 : :
1006 : : /* grab a lock, again to call callbacks and update status. */
1007 : : rte_spinlock_lock(&intr_lock);
1008 : :
1009 [ + - ]: 104372 : if (call) {
1010 : :
1011 : : /* Finally, call all callbacks. */
1012 [ + + ]: 208744 : TAILQ_FOREACH(cb, &src->callbacks, next) {
1013 : :
1014 : : /* make a copy and unlock. */
1015 : 104372 : active_cb = *cb;
1016 : : rte_spinlock_unlock(&intr_lock);
1017 : :
1018 : : /* call the actual callback */
1019 : 104372 : active_cb.cb_fn(active_cb.cb_arg);
1020 : :
1021 : : /*get the lock back. */
1022 : : rte_spinlock_lock(&intr_lock);
1023 : : }
1024 : : }
1025 : : /* we done with that interrupt source, release it. */
1026 : 104372 : src->active = 0;
1027 : :
1028 : : rv = 0;
1029 : :
1030 : : /* check if any callback are supposed to be removed */
1031 [ + + ]: 208744 : for (cb = TAILQ_FIRST(&src->callbacks); cb != NULL; cb = next) {
1032 : 104372 : next = TAILQ_NEXT(cb, next);
1033 [ - + ]: 104372 : if (cb->pending_delete) {
1034 [ # # ]: 0 : TAILQ_REMOVE(&src->callbacks, cb, next);
1035 [ # # ]: 0 : if (cb->ucb_fn)
1036 : 0 : cb->ucb_fn(src->intr_handle, cb->cb_arg);
1037 : 0 : free(cb);
1038 : 0 : rv++;
1039 : : }
1040 : : }
1041 : :
1042 : : /* all callbacks for that source are removed. */
1043 [ - + ]: 104372 : if (TAILQ_EMPTY(&src->callbacks)) {
1044 [ # # ]: 0 : TAILQ_REMOVE(&intr_sources, src, next);
1045 : 0 : rte_intr_instance_free(src->intr_handle);
1046 : 0 : free(src);
1047 : : }
1048 : :
1049 : : /* notify the pipe fd waited by epoll_wait to rebuild the wait list */
1050 [ - + - - ]: 104372 : if (rv > 0 && write(intr_pipe.writefd, "1", 1) < 0) {
1051 : : rte_spinlock_unlock(&intr_lock);
1052 : 0 : return -EPIPE;
1053 : : }
1054 : :
1055 : : rte_spinlock_unlock(&intr_lock);
1056 : : }
1057 : :
1058 : : return 0;
1059 : : }
1060 : :
1061 : : /**
1062 : : * It handles all the interrupts.
1063 : : *
1064 : : * @param pfd
1065 : : * epoll file descriptor.
1066 : : * @param totalfds
1067 : : * The number of file descriptors added in epoll.
1068 : : *
1069 : : * @return
1070 : : * void
1071 : : */
1072 : : static void
1073 : 198 : eal_intr_handle_interrupts(int pfd, unsigned totalfds)
1074 : : {
1075 : 198 : struct epoll_event *events = alloca(sizeof(struct epoll_event) * totalfds);
1076 : : int nfds = 0;
1077 : :
1078 : : for(;;) {
1079 : 104570 : nfds = epoll_wait(pfd, events, totalfds,
1080 : : EAL_INTR_EPOLL_WAIT_FOREVER);
1081 : : /* epoll_wait fail */
1082 [ - + ]: 104385 : if (nfds < 0) {
1083 [ # # ]: 0 : if (errno == EINTR)
1084 : 0 : continue;
1085 : 0 : EAL_LOG(ERR,
1086 : : "epoll_wait returns with fail");
1087 : 0 : return;
1088 : : }
1089 : : /* epoll_wait timeout, will never happens here */
1090 [ - + ]: 104385 : else if (nfds == 0)
1091 : 0 : continue;
1092 : : /* epoll_wait has at least one fd ready to read */
1093 [ + + ]: 104385 : if (eal_intr_process_interrupts(events, nfds) < 0)
1094 : : return;
1095 : : }
1096 : : }
1097 : :
1098 : : /**
1099 : : * It builds/rebuilds up the epoll file descriptor with all the
1100 : : * file descriptors being waited on. Then handles the interrupts.
1101 : : *
1102 : : * @param arg
1103 : : * pointer. (unused)
1104 : : *
1105 : : * @return
1106 : : * never return;
1107 : : */
1108 : : static __rte_noreturn uint32_t
1109 : 185 : eal_intr_thread_main(__rte_unused void *arg)
1110 : : {
1111 : : /* host thread, never break out */
1112 : 13 : for (;;) {
1113 : : /* build up the epoll fd with all descriptors we are to
1114 : : * wait on then pass it to the handle_interrupts function
1115 : : */
1116 : : static struct epoll_event pipe_event = {
1117 : : .events = EPOLLIN | EPOLLPRI,
1118 : : };
1119 : : struct rte_intr_source *src;
1120 : : unsigned numfds = 0;
1121 : :
1122 : : /* create epoll fd */
1123 : 198 : int pfd = epoll_create(1);
1124 [ - + ]: 198 : if (pfd < 0)
1125 : 0 : rte_panic("Cannot create epoll instance\n");
1126 : :
1127 : 198 : pipe_event.data.fd = intr_pipe.readfd;
1128 : : /**
1129 : : * add pipe fd into wait list, this pipe is used to
1130 : : * rebuild the wait list.
1131 : : */
1132 [ - + ]: 198 : if (epoll_ctl(pfd, EPOLL_CTL_ADD, intr_pipe.readfd,
1133 : : &pipe_event) < 0) {
1134 : 0 : rte_panic("Error adding fd to %d epoll_ctl, %s\n",
1135 : : intr_pipe.readfd, strerror(errno));
1136 : : }
1137 : : numfds++;
1138 : :
1139 : : rte_spinlock_lock(&intr_lock);
1140 : :
1141 [ + + ]: 207 : TAILQ_FOREACH(src, &intr_sources, next) {
1142 : : struct epoll_event ev;
1143 : :
1144 [ - + ]: 9 : if (src->callbacks.tqh_first == NULL)
1145 : 0 : continue; /* skip those with no callbacks */
1146 : : memset(&ev, 0, sizeof(ev));
1147 : 9 : ev.events = EPOLLIN | EPOLLPRI | EPOLLRDHUP | EPOLLHUP;
1148 : 9 : ev.data.fd = rte_intr_fd_get(src->intr_handle);
1149 : :
1150 : : /**
1151 : : * add all the uio device file descriptor
1152 : : * into wait list.
1153 : : */
1154 [ - + ]: 9 : if (epoll_ctl(pfd, EPOLL_CTL_ADD,
1155 : 9 : rte_intr_fd_get(src->intr_handle), &ev) < 0) {
1156 : 0 : rte_panic("Error adding fd %d epoll_ctl, %s\n",
1157 : : rte_intr_fd_get(src->intr_handle),
1158 : : strerror(errno));
1159 : : }
1160 : : else
1161 : 9 : numfds++;
1162 : : }
1163 : : rte_spinlock_unlock(&intr_lock);
1164 : : /* serve the interrupt */
1165 : 198 : eal_intr_handle_interrupts(pfd, numfds);
1166 : :
1167 : : /**
1168 : : * when we return, we need to rebuild the
1169 : : * list of fds to monitor.
1170 : : */
1171 : 13 : close(pfd);
1172 : : }
1173 : : }
1174 : :
1175 : : int
1176 : 185 : rte_eal_intr_init(void)
1177 : : {
1178 : : int ret = 0;
1179 : :
1180 : : /* init the global interrupt source head */
1181 : 185 : TAILQ_INIT(&intr_sources);
1182 : :
1183 : : /**
1184 : : * create a pipe which will be waited by epoll and notified to
1185 : : * rebuild the wait list of epoll.
1186 : : */
1187 [ - + ]: 185 : if (pipe(intr_pipe.pipefd) < 0) {
1188 : 0 : rte_errno = errno;
1189 : 0 : return -1;
1190 : : }
1191 : :
1192 : : /* create the host thread to wait/handle the interrupt */
1193 : 185 : ret = rte_thread_create_internal_control(&intr_thread, "intr",
1194 : : eal_intr_thread_main, NULL);
1195 [ - + ]: 185 : if (ret != 0) {
1196 : 0 : rte_errno = -ret;
1197 : 0 : EAL_LOG(ERR,
1198 : : "Failed to create thread for interrupt handling");
1199 : : }
1200 : :
1201 : : return ret;
1202 : : }
1203 : :
1204 : : static void
1205 : 0 : eal_intr_proc_rxtx_intr(int fd, const struct rte_intr_handle *intr_handle)
1206 : : {
1207 : : union rte_intr_read_buffer buf;
1208 : : int bytes_read = 0;
1209 : : int nbytes;
1210 : :
1211 [ # # # # : 0 : switch (rte_intr_type_get(intr_handle)) {
# ]
1212 : : case RTE_INTR_HANDLE_UIO:
1213 : : case RTE_INTR_HANDLE_UIO_INTX:
1214 : : bytes_read = sizeof(buf.uio_intr_count);
1215 : : break;
1216 : : #ifdef VFIO_PRESENT
1217 : : case RTE_INTR_HANDLE_VFIO_MSIX:
1218 : : case RTE_INTR_HANDLE_VFIO_MSI:
1219 : : case RTE_INTR_HANDLE_VFIO_LEGACY:
1220 : : bytes_read = sizeof(buf.vfio_intr_count);
1221 : : break;
1222 : : #endif
1223 : 0 : case RTE_INTR_HANDLE_VDEV:
1224 : 0 : bytes_read = rte_intr_efd_counter_size_get(intr_handle);
1225 : : /* For vdev, number of bytes to read is set by driver */
1226 : : break;
1227 : : case RTE_INTR_HANDLE_EXT:
1228 : : return;
1229 : 0 : default:
1230 : : bytes_read = 1;
1231 : 0 : EAL_LOG(INFO, "unexpected intr type");
1232 : : break;
1233 : : }
1234 : :
1235 : : /**
1236 : : * read out to clear the ready-to-be-read flag
1237 : : * for epoll_wait.
1238 : : */
1239 [ # # ]: 0 : if (bytes_read == 0)
1240 : : return;
1241 : : do {
1242 [ # # ]: 0 : nbytes = read(fd, &buf, bytes_read);
1243 [ # # ]: 0 : if (nbytes < 0) {
1244 [ # # ]: 0 : if (errno == EINTR || errno == EWOULDBLOCK ||
1245 : : errno == EAGAIN)
1246 : : continue;
1247 : 0 : EAL_LOG(ERR,
1248 : : "Error reading from fd %d: %s",
1249 : : fd, strerror(errno));
1250 [ # # ]: 0 : } else if (nbytes == 0)
1251 : 0 : EAL_LOG(ERR, "Read nothing from fd %d", fd);
1252 : : return;
1253 : : } while (1);
1254 : : }
1255 : :
1256 : : static int
1257 : 0 : eal_epoll_process_event(struct epoll_event *evs, unsigned int n,
1258 : : struct rte_epoll_event *events)
1259 : : {
1260 : : unsigned int i, count = 0;
1261 : : struct rte_epoll_event *rev;
1262 : : uint32_t valid_status;
1263 : :
1264 [ # # ]: 0 : for (i = 0; i < n; i++) {
1265 : 0 : rev = evs[i].data.ptr;
1266 : : valid_status = RTE_EPOLL_VALID;
1267 : : /* ACQUIRE memory ordering here pairs with RELEASE
1268 : : * ordering below acting as a lock to synchronize
1269 : : * the event data updating.
1270 : : */
1271 [ # # # # ]: 0 : if (!rev || !rte_atomic_compare_exchange_strong_explicit(&rev->status,
1272 : : &valid_status, RTE_EPOLL_EXEC,
1273 : : rte_memory_order_acquire, rte_memory_order_relaxed))
1274 : 0 : continue;
1275 : :
1276 : 0 : events[count].status = RTE_EPOLL_VALID;
1277 : 0 : events[count].fd = rev->fd;
1278 : 0 : events[count].epfd = rev->epfd;
1279 : 0 : events[count].epdata.event = evs[i].events;
1280 : 0 : events[count].epdata.data = rev->epdata.data;
1281 [ # # ]: 0 : if (rev->epdata.cb_fun)
1282 : 0 : rev->epdata.cb_fun(rev->fd,
1283 : : rev->epdata.cb_arg);
1284 : :
1285 : : /* the status update should be observed after
1286 : : * the other fields change.
1287 : : */
1288 : 0 : rte_atomic_store_explicit(&rev->status, RTE_EPOLL_VALID,
1289 : : rte_memory_order_release);
1290 : 0 : count++;
1291 : : }
1292 : 0 : return count;
1293 : : }
1294 : :
1295 : : static inline int
1296 : 0 : eal_init_tls_epfd(void)
1297 : : {
1298 : 0 : int pfd = epoll_create(255);
1299 : :
1300 [ # # ]: 0 : if (pfd < 0) {
1301 : 0 : EAL_LOG(ERR,
1302 : : "Cannot create epoll instance");
1303 : 0 : return -1;
1304 : : }
1305 : : return pfd;
1306 : : }
1307 : :
1308 : : int
1309 : 0 : rte_intr_tls_epfd(void)
1310 : : {
1311 [ # # ]: 0 : if (RTE_PER_LCORE(_epfd) == -1)
1312 : 0 : RTE_PER_LCORE(_epfd) = eal_init_tls_epfd();
1313 : :
1314 : 0 : return RTE_PER_LCORE(_epfd);
1315 : : }
1316 : :
1317 : : static int
1318 : 0 : eal_epoll_wait(int epfd, struct rte_epoll_event *events,
1319 : : int maxevents, int timeout, bool interruptible)
1320 : : {
1321 : : int rc;
1322 : : uint32_t i, k, n, num;
1323 : : struct epoll_event evs[MAX_ITER_EVNUM];
1324 : :
1325 [ # # ]: 0 : if (!events) {
1326 : 0 : EAL_LOG(ERR, "rte_epoll_event can't be NULL");
1327 : 0 : return -1;
1328 : : }
1329 : :
1330 : : /* using per thread epoll fd */
1331 [ # # ]: 0 : if (epfd == RTE_EPOLL_PER_THREAD)
1332 : 0 : epfd = rte_intr_tls_epfd();
1333 : :
1334 : 0 : num = maxevents;
1335 : 0 : n = RTE_MIN(RTE_DIM(evs), num);
1336 : :
1337 : : /* Process events in chunks of MAX_ITER_EVNUM */
1338 : :
1339 : : while (1) {
1340 : 0 : rc = epoll_wait(epfd, evs, n, timeout);
1341 [ # # ]: 0 : if (likely(rc > 0)) {
1342 : :
1343 : : /* epoll_wait has at least one fd ready to read */
1344 [ # # ]: 0 : for (i = 0, k = 0; rc > 0;) {
1345 : 0 : k += rc;
1346 : 0 : rc = eal_epoll_process_event(evs, rc,
1347 : 0 : events + i);
1348 : 0 : i += rc;
1349 : :
1350 : : /*
1351 : : * try to read more events that are already
1352 : : * available (up to maxevents in total).
1353 : : */
1354 : 0 : n = RTE_MIN(RTE_DIM(evs), num - k);
1355 [ # # ]: 0 : rc = (n == 0) ? 0 : epoll_wait(epfd, evs, n, 0);
1356 : : }
1357 : 0 : return i;
1358 : :
1359 [ # # ]: 0 : } else if (rc < 0) {
1360 [ # # ]: 0 : if (errno == EINTR) {
1361 [ # # ]: 0 : if (interruptible)
1362 : : return -1;
1363 : : else
1364 : : continue;
1365 : : }
1366 : : /* epoll_wait fail */
1367 : 0 : EAL_LOG(ERR, "epoll_wait returns with fail %s",
1368 : : strerror(errno));
1369 : : rc = -1;
1370 : 0 : break;
1371 : : } else {
1372 : : /* rc == 0, epoll_wait timed out */
1373 : : break;
1374 : : }
1375 : : }
1376 : :
1377 : : return rc;
1378 : : }
1379 : :
1380 : : int
1381 : 0 : rte_epoll_wait(int epfd, struct rte_epoll_event *events,
1382 : : int maxevents, int timeout)
1383 : : {
1384 : 0 : return eal_epoll_wait(epfd, events, maxevents, timeout, false);
1385 : : }
1386 : :
1387 : : int
1388 : 0 : rte_epoll_wait_interruptible(int epfd, struct rte_epoll_event *events,
1389 : : int maxevents, int timeout)
1390 : : {
1391 : 0 : return eal_epoll_wait(epfd, events, maxevents, timeout, true);
1392 : : }
1393 : :
1394 : : static inline void
1395 : 0 : eal_epoll_data_safe_free(struct rte_epoll_event *ev)
1396 : : {
1397 : : uint32_t valid_status = RTE_EPOLL_VALID;
1398 : :
1399 [ # # ]: 0 : while (!rte_atomic_compare_exchange_strong_explicit(&ev->status, &valid_status,
1400 : : RTE_EPOLL_INVALID, rte_memory_order_acquire, rte_memory_order_relaxed)) {
1401 : 0 : while (rte_atomic_load_explicit(&ev->status,
1402 [ # # ]: 0 : rte_memory_order_relaxed) != RTE_EPOLL_VALID)
1403 : : rte_pause();
1404 : : valid_status = RTE_EPOLL_VALID;
1405 : : }
1406 : 0 : memset(&ev->epdata, 0, sizeof(ev->epdata));
1407 : 0 : ev->fd = -1;
1408 : 0 : ev->epfd = -1;
1409 : 0 : }
1410 : :
1411 : : int
1412 : 0 : rte_epoll_ctl(int epfd, int op, int fd,
1413 : : struct rte_epoll_event *event)
1414 : : {
1415 : : struct epoll_event ev;
1416 : :
1417 [ # # ]: 0 : if (!event) {
1418 : 0 : EAL_LOG(ERR, "rte_epoll_event can't be NULL");
1419 : 0 : return -1;
1420 : : }
1421 : :
1422 : : /* using per thread epoll fd */
1423 [ # # ]: 0 : if (epfd == RTE_EPOLL_PER_THREAD)
1424 : 0 : epfd = rte_intr_tls_epfd();
1425 : :
1426 [ # # ]: 0 : if (op == EPOLL_CTL_ADD) {
1427 : 0 : rte_atomic_store_explicit(&event->status, RTE_EPOLL_VALID,
1428 : : rte_memory_order_relaxed);
1429 : 0 : event->fd = fd; /* ignore fd in event */
1430 : 0 : event->epfd = epfd;
1431 : 0 : ev.data.ptr = (void *)event;
1432 : : }
1433 : :
1434 : 0 : ev.events = event->epdata.event;
1435 [ # # ]: 0 : if (epoll_ctl(epfd, op, fd, &ev) < 0) {
1436 : 0 : EAL_LOG(ERR, "Error op %d fd %d epoll_ctl, %s",
1437 : : op, fd, strerror(errno));
1438 [ # # ]: 0 : if (op == EPOLL_CTL_ADD)
1439 : : /* rollback status when CTL_ADD fail */
1440 : 0 : rte_atomic_store_explicit(&event->status, RTE_EPOLL_INVALID,
1441 : : rte_memory_order_relaxed);
1442 : 0 : return -1;
1443 : : }
1444 : :
1445 [ # # # # ]: 0 : if (op == EPOLL_CTL_DEL && rte_atomic_load_explicit(&event->status,
1446 : : rte_memory_order_relaxed) != RTE_EPOLL_INVALID)
1447 : 0 : eal_epoll_data_safe_free(event);
1448 : :
1449 : : return 0;
1450 : : }
1451 : :
1452 : : int
1453 : 0 : rte_intr_rx_ctl(struct rte_intr_handle *intr_handle, int epfd,
1454 : : int op, unsigned int vec, void *data)
1455 : : {
1456 : : struct rte_epoll_event *rev;
1457 : : struct rte_epoll_data *epdata;
1458 : : int epfd_op;
1459 : : unsigned int efd_idx;
1460 : : int rc = 0;
1461 : :
1462 : : efd_idx = (vec >= RTE_INTR_VEC_RXTX_OFFSET) ?
1463 [ # # ]: 0 : (vec - RTE_INTR_VEC_RXTX_OFFSET) : vec;
1464 : :
1465 [ # # # # ]: 0 : if (intr_handle == NULL || rte_intr_nb_efd_get(intr_handle) == 0 ||
1466 [ # # ]: 0 : efd_idx >= (unsigned int)rte_intr_nb_efd_get(intr_handle)) {
1467 : 0 : EAL_LOG(ERR, "Wrong intr vector number.");
1468 : 0 : return -EPERM;
1469 : : }
1470 : :
1471 [ # # # ]: 0 : switch (op) {
1472 : 0 : case RTE_INTR_EVENT_ADD:
1473 : : epfd_op = EPOLL_CTL_ADD;
1474 : 0 : rev = rte_intr_elist_index_get(intr_handle, efd_idx);
1475 [ # # ]: 0 : if (rte_atomic_load_explicit(&rev->status,
1476 : : rte_memory_order_relaxed) != RTE_EPOLL_INVALID) {
1477 : 0 : EAL_LOG(INFO, "Event already been added.");
1478 : 0 : return -EEXIST;
1479 : : }
1480 : :
1481 : : /* attach to intr vector fd */
1482 : : epdata = &rev->epdata;
1483 : 0 : epdata->event = EPOLLIN | EPOLLPRI | EPOLLET;
1484 : 0 : epdata->data = data;
1485 : 0 : epdata->cb_fun = (rte_intr_event_cb_t)eal_intr_proc_rxtx_intr;
1486 : 0 : epdata->cb_arg = (void *)intr_handle;
1487 : 0 : rc = rte_epoll_ctl(epfd, epfd_op,
1488 : : rte_intr_efds_index_get(intr_handle, efd_idx), rev);
1489 [ # # ]: 0 : if (!rc)
1490 : 0 : EAL_LOG(DEBUG,
1491 : : "efd %d associated with vec %d added on epfd %d",
1492 : : rev->fd, vec, epfd);
1493 : : else
1494 : : rc = -EPERM;
1495 : : break;
1496 : 0 : case RTE_INTR_EVENT_DEL:
1497 : : epfd_op = EPOLL_CTL_DEL;
1498 : 0 : rev = rte_intr_elist_index_get(intr_handle, efd_idx);
1499 [ # # ]: 0 : if (rte_atomic_load_explicit(&rev->status,
1500 : : rte_memory_order_relaxed) == RTE_EPOLL_INVALID) {
1501 : 0 : EAL_LOG(INFO, "Event does not exist.");
1502 : 0 : return -EPERM;
1503 : : }
1504 : :
1505 : 0 : rc = rte_epoll_ctl(rev->epfd, epfd_op, rev->fd, rev);
1506 [ # # ]: 0 : if (rc)
1507 : : rc = -EPERM;
1508 : : break;
1509 : 0 : default:
1510 : 0 : EAL_LOG(ERR, "event op type mismatch");
1511 : : rc = -EPERM;
1512 : : }
1513 : :
1514 : : return rc;
1515 : : }
1516 : :
1517 : : void
1518 : 0 : rte_intr_free_epoll_fd(struct rte_intr_handle *intr_handle)
1519 : : {
1520 : : uint32_t i;
1521 : : struct rte_epoll_event *rev;
1522 : :
1523 [ # # ]: 0 : for (i = 0; i < (uint32_t)rte_intr_nb_efd_get(intr_handle); i++) {
1524 : 0 : rev = rte_intr_elist_index_get(intr_handle, i);
1525 [ # # ]: 0 : if (rte_atomic_load_explicit(&rev->status,
1526 : : rte_memory_order_relaxed) == RTE_EPOLL_INVALID)
1527 : 0 : continue;
1528 [ # # ]: 0 : if (rte_epoll_ctl(rev->epfd, EPOLL_CTL_DEL, rev->fd, rev)) {
1529 : : /* force free if the entry valid */
1530 : 0 : eal_epoll_data_safe_free(rev);
1531 : : }
1532 : : }
1533 : 0 : }
1534 : :
1535 : : int
1536 : 0 : rte_intr_efd_enable(struct rte_intr_handle *intr_handle, uint32_t nb_efd)
1537 : : {
1538 : : uint32_t i;
1539 : : int fd;
1540 : 0 : uint32_t n = RTE_MIN(nb_efd, (uint32_t)RTE_MAX_RXTX_INTR_VEC_ID);
1541 : :
1542 [ # # ]: 0 : assert(nb_efd != 0);
1543 : :
1544 [ # # ]: 0 : if (rte_intr_type_get(intr_handle) == RTE_INTR_HANDLE_VFIO_MSIX) {
1545 [ # # ]: 0 : for (i = 0; i < n; i++) {
1546 : 0 : fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
1547 [ # # ]: 0 : if (fd < 0) {
1548 : 0 : EAL_LOG(ERR,
1549 : : "can't setup eventfd, error %i (%s)",
1550 : : errno, strerror(errno));
1551 : 0 : return -errno;
1552 : : }
1553 : :
1554 [ # # ]: 0 : if (rte_intr_efds_index_set(intr_handle, i, fd))
1555 : 0 : return -rte_errno;
1556 : : }
1557 : :
1558 [ # # ]: 0 : if (rte_intr_nb_efd_set(intr_handle, n))
1559 : 0 : return -rte_errno;
1560 : :
1561 [ # # ]: 0 : if (rte_intr_max_intr_set(intr_handle, NB_OTHER_INTR + n))
1562 : 0 : return -rte_errno;
1563 [ # # ]: 0 : } else if (rte_intr_type_get(intr_handle) == RTE_INTR_HANDLE_VDEV) {
1564 : : /* only check, initialization would be done in vdev driver.*/
1565 [ # # ]: 0 : if ((uint64_t)rte_intr_efd_counter_size_get(intr_handle) >
1566 : : sizeof(union rte_intr_read_buffer)) {
1567 : 0 : EAL_LOG(ERR, "the efd_counter_size is oversized");
1568 : 0 : return -EINVAL;
1569 : : }
1570 : : } else {
1571 [ # # ]: 0 : if (rte_intr_efds_index_set(intr_handle, 0, rte_intr_fd_get(intr_handle)))
1572 : 0 : return -rte_errno;
1573 [ # # ]: 0 : if (rte_intr_nb_efd_set(intr_handle, RTE_MIN(nb_efd, 1U)))
1574 : 0 : return -rte_errno;
1575 [ # # ]: 0 : if (rte_intr_max_intr_set(intr_handle, NB_OTHER_INTR))
1576 : 0 : return -rte_errno;
1577 : : }
1578 : :
1579 : : return 0;
1580 : : }
1581 : :
1582 : : void
1583 : 0 : rte_intr_efd_disable(struct rte_intr_handle *intr_handle)
1584 : : {
1585 : : uint32_t i;
1586 : :
1587 : 0 : rte_intr_free_epoll_fd(intr_handle);
1588 [ # # ]: 0 : if (rte_intr_max_intr_get(intr_handle) > rte_intr_nb_efd_get(intr_handle)) {
1589 [ # # ]: 0 : for (i = 0; i < (uint32_t)rte_intr_nb_efd_get(intr_handle); i++)
1590 : 0 : close(rte_intr_efds_index_get(intr_handle, i));
1591 : : }
1592 : 0 : rte_intr_nb_efd_set(intr_handle, 0);
1593 : 0 : rte_intr_max_intr_set(intr_handle, 0);
1594 : 0 : }
1595 : :
1596 : : int
1597 : 0 : rte_intr_dp_is_en(struct rte_intr_handle *intr_handle)
1598 : : {
1599 : 0 : return !(!rte_intr_nb_efd_get(intr_handle));
1600 : : }
1601 : :
1602 : : int
1603 : 0 : rte_intr_allow_others(struct rte_intr_handle *intr_handle)
1604 : : {
1605 [ # # ]: 0 : if (!rte_intr_dp_is_en(intr_handle))
1606 : : return 1;
1607 : : else
1608 : 0 : return !!(rte_intr_max_intr_get(intr_handle) -
1609 : 0 : rte_intr_nb_efd_get(intr_handle));
1610 : : }
1611 : :
1612 : : int
1613 : 0 : rte_intr_cap_multiple(struct rte_intr_handle *intr_handle)
1614 : : {
1615 [ # # ]: 0 : if (rte_intr_type_get(intr_handle) == RTE_INTR_HANDLE_VFIO_MSIX)
1616 : : return 1;
1617 : :
1618 [ # # ]: 0 : if (rte_intr_type_get(intr_handle) == RTE_INTR_HANDLE_VDEV)
1619 : 0 : return 1;
1620 : :
1621 : : return 0;
1622 : : }
1623 : :
1624 : 0 : int rte_thread_is_intr(void)
1625 : : {
1626 : 0 : return rte_thread_equal(intr_thread, rte_thread_self());
1627 : : }
|