199a2dd95SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause
299a2dd95SBruce Richardson * Copyright(c) 2010-2018 Intel Corporation
399a2dd95SBruce Richardson */
499a2dd95SBruce Richardson
599a2dd95SBruce Richardson #include <string.h>
699a2dd95SBruce Richardson #include <sys/types.h>
799a2dd95SBruce Richardson #include <sys/event.h>
899a2dd95SBruce Richardson #include <sys/queue.h>
999a2dd95SBruce Richardson #include <unistd.h>
1099a2dd95SBruce Richardson
1145a685caSAnkur Dwivedi #include <eal_trace_internal.h>
1299a2dd95SBruce Richardson #include <rte_errno.h>
1399a2dd95SBruce Richardson #include <rte_lcore.h>
1499a2dd95SBruce Richardson #include <rte_spinlock.h>
1599a2dd95SBruce Richardson #include <rte_common.h>
1699a2dd95SBruce Richardson #include <rte_interrupts.h>
1799a2dd95SBruce Richardson
1899a2dd95SBruce Richardson #include "eal_private.h"
1999a2dd95SBruce Richardson #include "eal_alarm_private.h"
2099a2dd95SBruce Richardson
2199a2dd95SBruce Richardson #define MAX_INTR_EVENTS 16
2299a2dd95SBruce Richardson
2399a2dd95SBruce Richardson /**
2499a2dd95SBruce Richardson * union buffer for reading on different devices
2599a2dd95SBruce Richardson */
2699a2dd95SBruce Richardson union rte_intr_read_buffer {
2799a2dd95SBruce Richardson char charbuf[16]; /* for others */
2899a2dd95SBruce Richardson };
2999a2dd95SBruce Richardson
3099a2dd95SBruce Richardson TAILQ_HEAD(rte_intr_cb_list, rte_intr_callback);
3199a2dd95SBruce Richardson TAILQ_HEAD(rte_intr_source_list, rte_intr_source);
3299a2dd95SBruce Richardson
3399a2dd95SBruce Richardson struct rte_intr_callback {
3499a2dd95SBruce Richardson TAILQ_ENTRY(rte_intr_callback) next;
3599a2dd95SBruce Richardson rte_intr_callback_fn cb_fn; /**< callback address */
3699a2dd95SBruce Richardson void *cb_arg; /**< parameter for callback */
3799a2dd95SBruce Richardson uint8_t pending_delete; /**< delete after callback is called */
3899a2dd95SBruce Richardson rte_intr_unregister_callback_fn ucb_fn; /**< fn to call before cb is deleted */
3999a2dd95SBruce Richardson };
4099a2dd95SBruce Richardson
4199a2dd95SBruce Richardson struct rte_intr_source {
4299a2dd95SBruce Richardson TAILQ_ENTRY(rte_intr_source) next;
43bbbac4cdSHarman Kalra struct rte_intr_handle *intr_handle; /**< interrupt handle */
4499a2dd95SBruce Richardson struct rte_intr_cb_list callbacks; /**< user callbacks */
4599a2dd95SBruce Richardson uint32_t active;
4699a2dd95SBruce Richardson };
4799a2dd95SBruce Richardson
4899a2dd95SBruce Richardson /* global spinlock for interrupt data operation */
4999a2dd95SBruce Richardson static rte_spinlock_t intr_lock = RTE_SPINLOCK_INITIALIZER;
5099a2dd95SBruce Richardson
5199a2dd95SBruce Richardson /* interrupt sources list */
5299a2dd95SBruce Richardson static struct rte_intr_source_list intr_sources;
5399a2dd95SBruce Richardson
5499a2dd95SBruce Richardson /* interrupt handling thread */
551c1abf17SThomas Monjalon static rte_thread_t intr_thread;
5699a2dd95SBruce Richardson
5799a2dd95SBruce Richardson static volatile int kq = -1;
5899a2dd95SBruce Richardson
5999a2dd95SBruce Richardson static int
intr_source_to_kevent(const struct rte_intr_handle * ih,struct kevent * ke)6099a2dd95SBruce Richardson intr_source_to_kevent(const struct rte_intr_handle *ih, struct kevent *ke)
6199a2dd95SBruce Richardson {
6299a2dd95SBruce Richardson /* alarm callbacks are special case */
63bbbac4cdSHarman Kalra if (rte_intr_type_get(ih) == RTE_INTR_HANDLE_ALARM) {
6499a2dd95SBruce Richardson uint64_t timeout_ns;
6599a2dd95SBruce Richardson
6699a2dd95SBruce Richardson /* get soonest alarm timeout */
6799a2dd95SBruce Richardson if (eal_alarm_get_timeout_ns(&timeout_ns) < 0)
6899a2dd95SBruce Richardson return -1;
6999a2dd95SBruce Richardson
7099a2dd95SBruce Richardson ke->filter = EVFILT_TIMER;
7199a2dd95SBruce Richardson /* timers are one shot */
7299a2dd95SBruce Richardson ke->flags |= EV_ONESHOT;
7399a2dd95SBruce Richardson ke->fflags = NOTE_NSECONDS;
7499a2dd95SBruce Richardson ke->data = timeout_ns;
7599a2dd95SBruce Richardson } else {
7699a2dd95SBruce Richardson ke->filter = EVFILT_READ;
7799a2dd95SBruce Richardson }
78bbbac4cdSHarman Kalra ke->ident = rte_intr_fd_get(ih);
7999a2dd95SBruce Richardson
8099a2dd95SBruce Richardson return 0;
8199a2dd95SBruce Richardson }
8299a2dd95SBruce Richardson
8399a2dd95SBruce Richardson int
rte_intr_callback_register(const struct rte_intr_handle * intr_handle,rte_intr_callback_fn cb,void * cb_arg)8499a2dd95SBruce Richardson rte_intr_callback_register(const struct rte_intr_handle *intr_handle,
8599a2dd95SBruce Richardson rte_intr_callback_fn cb, void *cb_arg)
8699a2dd95SBruce Richardson {
8799a2dd95SBruce Richardson struct rte_intr_callback *callback;
8899a2dd95SBruce Richardson struct rte_intr_source *src;
8999a2dd95SBruce Richardson int ret = 0, add_event = 0;
9099a2dd95SBruce Richardson
9199a2dd95SBruce Richardson /* first do parameter checking */
92bbbac4cdSHarman Kalra if (rte_intr_fd_get(intr_handle) < 0 || cb == NULL) {
93*ae67895bSDavid Marchand EAL_LOG(ERR,
94*ae67895bSDavid Marchand "Registering with invalid input parameter");
9599a2dd95SBruce Richardson return -EINVAL;
9699a2dd95SBruce Richardson }
9799a2dd95SBruce Richardson if (kq < 0) {
98*ae67895bSDavid Marchand EAL_LOG(ERR, "Kqueue is not active: %d", kq);
9999a2dd95SBruce Richardson return -ENODEV;
10099a2dd95SBruce Richardson }
10199a2dd95SBruce Richardson
10299a2dd95SBruce Richardson rte_spinlock_lock(&intr_lock);
10399a2dd95SBruce Richardson
10499a2dd95SBruce Richardson /* find the source for this intr_handle */
10599a2dd95SBruce Richardson TAILQ_FOREACH(src, &intr_sources, next) {
106bbbac4cdSHarman Kalra if (rte_intr_fd_get(src->intr_handle) == rte_intr_fd_get(intr_handle))
10799a2dd95SBruce Richardson break;
10899a2dd95SBruce Richardson }
10999a2dd95SBruce Richardson
11099a2dd95SBruce Richardson /* if this is an alarm interrupt and it already has a callback,
11199a2dd95SBruce Richardson * then we don't want to create a new callback because the only
11299a2dd95SBruce Richardson * thing on the list should be eal_alarm_callback() and we may
11399a2dd95SBruce Richardson * be called just to reset the timer.
11499a2dd95SBruce Richardson */
115bbbac4cdSHarman Kalra if (src != NULL &&
116bbbac4cdSHarman Kalra rte_intr_type_get(src->intr_handle) == RTE_INTR_HANDLE_ALARM &&
11799a2dd95SBruce Richardson !TAILQ_EMPTY(&src->callbacks)) {
11899a2dd95SBruce Richardson callback = NULL;
11999a2dd95SBruce Richardson } else {
12099a2dd95SBruce Richardson /* allocate a new interrupt callback entity */
12199a2dd95SBruce Richardson callback = calloc(1, sizeof(*callback));
12299a2dd95SBruce Richardson if (callback == NULL) {
123*ae67895bSDavid Marchand EAL_LOG(ERR, "Can not allocate memory");
12499a2dd95SBruce Richardson ret = -ENOMEM;
12599a2dd95SBruce Richardson goto fail;
12699a2dd95SBruce Richardson }
12799a2dd95SBruce Richardson callback->cb_fn = cb;
12899a2dd95SBruce Richardson callback->cb_arg = cb_arg;
12999a2dd95SBruce Richardson callback->pending_delete = 0;
13099a2dd95SBruce Richardson callback->ucb_fn = NULL;
13199a2dd95SBruce Richardson
13299a2dd95SBruce Richardson if (src == NULL) {
13399a2dd95SBruce Richardson src = calloc(1, sizeof(*src));
13499a2dd95SBruce Richardson if (src == NULL) {
135*ae67895bSDavid Marchand EAL_LOG(ERR, "Can not allocate memory");
13699a2dd95SBruce Richardson ret = -ENOMEM;
13799a2dd95SBruce Richardson goto fail;
13899a2dd95SBruce Richardson } else {
139bbbac4cdSHarman Kalra src->intr_handle = rte_intr_instance_dup(intr_handle);
140bbbac4cdSHarman Kalra if (src->intr_handle == NULL) {
141*ae67895bSDavid Marchand EAL_LOG(ERR, "Can not create intr instance");
142bbbac4cdSHarman Kalra ret = -ENOMEM;
143bbbac4cdSHarman Kalra free(src);
144bbbac4cdSHarman Kalra src = NULL;
145bbbac4cdSHarman Kalra goto fail;
146bbbac4cdSHarman Kalra }
14799a2dd95SBruce Richardson TAILQ_INIT(&src->callbacks);
14899a2dd95SBruce Richardson TAILQ_INSERT_TAIL(&intr_sources, src, next);
14999a2dd95SBruce Richardson }
15099a2dd95SBruce Richardson }
15199a2dd95SBruce Richardson
15299a2dd95SBruce Richardson /* we had no interrupts for this */
15399a2dd95SBruce Richardson if (TAILQ_EMPTY(&src->callbacks))
15499a2dd95SBruce Richardson add_event = 1;
15599a2dd95SBruce Richardson
15699a2dd95SBruce Richardson TAILQ_INSERT_TAIL(&(src->callbacks), callback, next);
15799a2dd95SBruce Richardson }
15899a2dd95SBruce Richardson
15999a2dd95SBruce Richardson /* add events to the queue. timer events are special as we need to
16099a2dd95SBruce Richardson * re-set the timer.
16199a2dd95SBruce Richardson */
162bbbac4cdSHarman Kalra if (add_event ||
163bbbac4cdSHarman Kalra rte_intr_type_get(src->intr_handle) == RTE_INTR_HANDLE_ALARM) {
16499a2dd95SBruce Richardson struct kevent ke;
16599a2dd95SBruce Richardson
16699a2dd95SBruce Richardson memset(&ke, 0, sizeof(ke));
16799a2dd95SBruce Richardson ke.flags = EV_ADD; /* mark for addition to the queue */
16899a2dd95SBruce Richardson
16999a2dd95SBruce Richardson if (intr_source_to_kevent(intr_handle, &ke) < 0) {
170*ae67895bSDavid Marchand EAL_LOG(ERR, "Cannot convert interrupt handle to kevent");
17199a2dd95SBruce Richardson ret = -ENODEV;
17299a2dd95SBruce Richardson goto fail;
17399a2dd95SBruce Richardson }
17499a2dd95SBruce Richardson
17599a2dd95SBruce Richardson /**
17699a2dd95SBruce Richardson * add the intr file descriptor into wait list.
17799a2dd95SBruce Richardson */
17899a2dd95SBruce Richardson if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) {
17999a2dd95SBruce Richardson /* currently, nic_uio does not support interrupts, so
18099a2dd95SBruce Richardson * this error will always be triggered and output to the
18199a2dd95SBruce Richardson * user. so, don't output it unless debug log level set.
18299a2dd95SBruce Richardson */
18399a2dd95SBruce Richardson if (errno == ENODEV)
184*ae67895bSDavid Marchand EAL_LOG(DEBUG, "Interrupt handle %d not supported",
185bbbac4cdSHarman Kalra rte_intr_fd_get(src->intr_handle));
18699a2dd95SBruce Richardson else
187*ae67895bSDavid Marchand EAL_LOG(ERR, "Error adding fd %d kevent, %s",
188bbbac4cdSHarman Kalra rte_intr_fd_get(src->intr_handle),
18999a2dd95SBruce Richardson strerror(errno));
19099a2dd95SBruce Richardson ret = -errno;
19199a2dd95SBruce Richardson goto fail;
19299a2dd95SBruce Richardson }
19399a2dd95SBruce Richardson }
19499a2dd95SBruce Richardson rte_eal_trace_intr_callback_register(intr_handle, cb, cb_arg, ret);
19599a2dd95SBruce Richardson rte_spinlock_unlock(&intr_lock);
19699a2dd95SBruce Richardson
19799a2dd95SBruce Richardson return 0;
19899a2dd95SBruce Richardson fail:
19999a2dd95SBruce Richardson /* clean up */
20099a2dd95SBruce Richardson if (src != NULL) {
20199a2dd95SBruce Richardson if (callback != NULL)
20299a2dd95SBruce Richardson TAILQ_REMOVE(&(src->callbacks), callback, next);
20399a2dd95SBruce Richardson if (TAILQ_EMPTY(&(src->callbacks))) {
20499a2dd95SBruce Richardson TAILQ_REMOVE(&intr_sources, src, next);
20599a2dd95SBruce Richardson free(src);
20699a2dd95SBruce Richardson }
20799a2dd95SBruce Richardson }
20899a2dd95SBruce Richardson free(callback);
20999a2dd95SBruce Richardson rte_eal_trace_intr_callback_register(intr_handle, cb, cb_arg, ret);
21099a2dd95SBruce Richardson rte_spinlock_unlock(&intr_lock);
21199a2dd95SBruce Richardson return ret;
21299a2dd95SBruce Richardson }
21399a2dd95SBruce Richardson
21499a2dd95SBruce Richardson int
rte_intr_callback_unregister_pending(const struct rte_intr_handle * intr_handle,rte_intr_callback_fn cb_fn,void * cb_arg,rte_intr_unregister_callback_fn ucb_fn)21599a2dd95SBruce Richardson rte_intr_callback_unregister_pending(const struct rte_intr_handle *intr_handle,
21699a2dd95SBruce Richardson rte_intr_callback_fn cb_fn, void *cb_arg,
21799a2dd95SBruce Richardson rte_intr_unregister_callback_fn ucb_fn)
21899a2dd95SBruce Richardson {
21999a2dd95SBruce Richardson int ret;
22099a2dd95SBruce Richardson struct rte_intr_source *src;
22199a2dd95SBruce Richardson struct rte_intr_callback *cb, *next;
22299a2dd95SBruce Richardson
22399a2dd95SBruce Richardson /* do parameter checking first */
224bbbac4cdSHarman Kalra if (rte_intr_fd_get(intr_handle) < 0) {
225*ae67895bSDavid Marchand EAL_LOG(ERR,
226*ae67895bSDavid Marchand "Unregistering with invalid input parameter");
22799a2dd95SBruce Richardson return -EINVAL;
22899a2dd95SBruce Richardson }
22999a2dd95SBruce Richardson
23099a2dd95SBruce Richardson if (kq < 0) {
231*ae67895bSDavid Marchand EAL_LOG(ERR, "Kqueue is not active");
23299a2dd95SBruce Richardson return -ENODEV;
23399a2dd95SBruce Richardson }
23499a2dd95SBruce Richardson
23599a2dd95SBruce Richardson rte_spinlock_lock(&intr_lock);
23699a2dd95SBruce Richardson
2377be78d02SJosh Soref /* check if the interrupt source for the fd is existent */
23899a2dd95SBruce Richardson TAILQ_FOREACH(src, &intr_sources, next)
239bbbac4cdSHarman Kalra if (rte_intr_fd_get(src->intr_handle) == rte_intr_fd_get(intr_handle))
24099a2dd95SBruce Richardson break;
24199a2dd95SBruce Richardson
24299a2dd95SBruce Richardson /* No interrupt source registered for the fd */
24399a2dd95SBruce Richardson if (src == NULL) {
24499a2dd95SBruce Richardson ret = -ENOENT;
24599a2dd95SBruce Richardson
24699a2dd95SBruce Richardson /* only usable if the source is active */
24799a2dd95SBruce Richardson } else if (src->active == 0) {
24899a2dd95SBruce Richardson ret = -EAGAIN;
24999a2dd95SBruce Richardson
25099a2dd95SBruce Richardson } else {
25199a2dd95SBruce Richardson ret = 0;
25299a2dd95SBruce Richardson
25399a2dd95SBruce Richardson /* walk through the callbacks and mark all that match. */
25499a2dd95SBruce Richardson for (cb = TAILQ_FIRST(&src->callbacks); cb != NULL; cb = next) {
25599a2dd95SBruce Richardson next = TAILQ_NEXT(cb, next);
25699a2dd95SBruce Richardson if (cb->cb_fn == cb_fn && (cb_arg == (void *)-1 ||
25799a2dd95SBruce Richardson cb->cb_arg == cb_arg)) {
25899a2dd95SBruce Richardson cb->pending_delete = 1;
25999a2dd95SBruce Richardson cb->ucb_fn = ucb_fn;
26099a2dd95SBruce Richardson ret++;
26199a2dd95SBruce Richardson }
26299a2dd95SBruce Richardson }
26399a2dd95SBruce Richardson }
26499a2dd95SBruce Richardson
26599a2dd95SBruce Richardson rte_spinlock_unlock(&intr_lock);
26699a2dd95SBruce Richardson
26799a2dd95SBruce Richardson return ret;
26899a2dd95SBruce Richardson }
26999a2dd95SBruce Richardson
27099a2dd95SBruce Richardson int
rte_intr_callback_unregister(const struct rte_intr_handle * intr_handle,rte_intr_callback_fn cb_fn,void * cb_arg)27199a2dd95SBruce Richardson rte_intr_callback_unregister(const struct rte_intr_handle *intr_handle,
27299a2dd95SBruce Richardson rte_intr_callback_fn cb_fn, void *cb_arg)
27399a2dd95SBruce Richardson {
27499a2dd95SBruce Richardson int ret;
27599a2dd95SBruce Richardson struct rte_intr_source *src;
27699a2dd95SBruce Richardson struct rte_intr_callback *cb, *next;
27799a2dd95SBruce Richardson
27899a2dd95SBruce Richardson /* do parameter checking first */
279bbbac4cdSHarman Kalra if (rte_intr_fd_get(intr_handle) < 0) {
280*ae67895bSDavid Marchand EAL_LOG(ERR,
281*ae67895bSDavid Marchand "Unregistering with invalid input parameter");
28299a2dd95SBruce Richardson return -EINVAL;
28399a2dd95SBruce Richardson }
28499a2dd95SBruce Richardson if (kq < 0) {
285*ae67895bSDavid Marchand EAL_LOG(ERR, "Kqueue is not active");
28699a2dd95SBruce Richardson return -ENODEV;
28799a2dd95SBruce Richardson }
28899a2dd95SBruce Richardson
28999a2dd95SBruce Richardson rte_spinlock_lock(&intr_lock);
29099a2dd95SBruce Richardson
2917be78d02SJosh Soref /* check if the interrupt source for the fd is existent */
29299a2dd95SBruce Richardson TAILQ_FOREACH(src, &intr_sources, next)
293bbbac4cdSHarman Kalra if (rte_intr_fd_get(src->intr_handle) == rte_intr_fd_get(intr_handle))
29499a2dd95SBruce Richardson break;
29599a2dd95SBruce Richardson
29699a2dd95SBruce Richardson /* No interrupt source registered for the fd */
29799a2dd95SBruce Richardson if (src == NULL) {
29899a2dd95SBruce Richardson ret = -ENOENT;
29999a2dd95SBruce Richardson
30099a2dd95SBruce Richardson /* interrupt source has some active callbacks right now. */
30199a2dd95SBruce Richardson } else if (src->active != 0) {
30299a2dd95SBruce Richardson ret = -EAGAIN;
30399a2dd95SBruce Richardson
30499a2dd95SBruce Richardson /* ok to remove. */
30599a2dd95SBruce Richardson } else {
30699a2dd95SBruce Richardson struct kevent ke;
30799a2dd95SBruce Richardson
30899a2dd95SBruce Richardson ret = 0;
30999a2dd95SBruce Richardson
31099a2dd95SBruce Richardson /* remove it from the kqueue */
31199a2dd95SBruce Richardson memset(&ke, 0, sizeof(ke));
31299a2dd95SBruce Richardson ke.flags = EV_DELETE; /* mark for deletion from the queue */
31399a2dd95SBruce Richardson
31499a2dd95SBruce Richardson if (intr_source_to_kevent(intr_handle, &ke) < 0) {
315*ae67895bSDavid Marchand EAL_LOG(ERR, "Cannot convert to kevent");
31699a2dd95SBruce Richardson ret = -ENODEV;
31799a2dd95SBruce Richardson goto out;
31899a2dd95SBruce Richardson }
31999a2dd95SBruce Richardson
32099a2dd95SBruce Richardson /**
32199a2dd95SBruce Richardson * remove intr file descriptor from wait list.
32299a2dd95SBruce Richardson */
32399a2dd95SBruce Richardson if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) {
324*ae67895bSDavid Marchand EAL_LOG(ERR, "Error removing fd %d kevent, %s",
325bbbac4cdSHarman Kalra rte_intr_fd_get(src->intr_handle),
326bbbac4cdSHarman Kalra strerror(errno));
32799a2dd95SBruce Richardson /* removing non-existent even is an expected condition
32899a2dd95SBruce Richardson * in some circumstances (e.g. oneshot events).
32999a2dd95SBruce Richardson */
33099a2dd95SBruce Richardson }
33199a2dd95SBruce Richardson
33299a2dd95SBruce Richardson /*walk through the callbacks and remove all that match. */
33399a2dd95SBruce Richardson for (cb = TAILQ_FIRST(&src->callbacks); cb != NULL; cb = next) {
33499a2dd95SBruce Richardson next = TAILQ_NEXT(cb, next);
33599a2dd95SBruce Richardson if (cb->cb_fn == cb_fn && (cb_arg == (void *)-1 ||
33699a2dd95SBruce Richardson cb->cb_arg == cb_arg)) {
33799a2dd95SBruce Richardson TAILQ_REMOVE(&src->callbacks, cb, next);
33899a2dd95SBruce Richardson free(cb);
33999a2dd95SBruce Richardson ret++;
34099a2dd95SBruce Richardson }
34199a2dd95SBruce Richardson }
34299a2dd95SBruce Richardson
34399a2dd95SBruce Richardson /* all callbacks for that source are removed. */
34499a2dd95SBruce Richardson if (TAILQ_EMPTY(&src->callbacks)) {
34599a2dd95SBruce Richardson TAILQ_REMOVE(&intr_sources, src, next);
34699a2dd95SBruce Richardson free(src);
34799a2dd95SBruce Richardson }
34899a2dd95SBruce Richardson }
34999a2dd95SBruce Richardson out:
35099a2dd95SBruce Richardson rte_eal_trace_intr_callback_unregister(intr_handle, cb_fn, cb_arg,
35199a2dd95SBruce Richardson ret);
35299a2dd95SBruce Richardson rte_spinlock_unlock(&intr_lock);
35399a2dd95SBruce Richardson
35499a2dd95SBruce Richardson return ret;
35599a2dd95SBruce Richardson }
35699a2dd95SBruce Richardson
35799a2dd95SBruce Richardson int
rte_intr_callback_unregister_sync(const struct rte_intr_handle * intr_handle,rte_intr_callback_fn cb_fn,void * cb_arg)35899a2dd95SBruce Richardson rte_intr_callback_unregister_sync(const struct rte_intr_handle *intr_handle,
35999a2dd95SBruce Richardson rte_intr_callback_fn cb_fn, void *cb_arg)
36099a2dd95SBruce Richardson {
36199a2dd95SBruce Richardson int ret = 0;
36299a2dd95SBruce Richardson
36399a2dd95SBruce Richardson while ((ret = rte_intr_callback_unregister(intr_handle, cb_fn, cb_arg)) == -EAGAIN)
36499a2dd95SBruce Richardson rte_pause();
36599a2dd95SBruce Richardson
36699a2dd95SBruce Richardson return ret;
36799a2dd95SBruce Richardson }
36899a2dd95SBruce Richardson
36999a2dd95SBruce Richardson int
rte_intr_enable(const struct rte_intr_handle * intr_handle)37099a2dd95SBruce Richardson rte_intr_enable(const struct rte_intr_handle *intr_handle)
37199a2dd95SBruce Richardson {
37299a2dd95SBruce Richardson int rc = 0;
37399a2dd95SBruce Richardson
37499a2dd95SBruce Richardson if (intr_handle == NULL)
37599a2dd95SBruce Richardson return -1;
37699a2dd95SBruce Richardson
377bbbac4cdSHarman Kalra if (rte_intr_type_get(intr_handle) == RTE_INTR_HANDLE_VDEV) {
37899a2dd95SBruce Richardson rc = 0;
37999a2dd95SBruce Richardson goto out;
38099a2dd95SBruce Richardson }
38199a2dd95SBruce Richardson
382bbbac4cdSHarman Kalra if (rte_intr_fd_get(intr_handle) < 0 ||
383bbbac4cdSHarman Kalra rte_intr_dev_fd_get(intr_handle) < 0) {
38499a2dd95SBruce Richardson rc = -1;
38599a2dd95SBruce Richardson goto out;
38699a2dd95SBruce Richardson }
38799a2dd95SBruce Richardson
388bbbac4cdSHarman Kalra switch (rte_intr_type_get(intr_handle)) {
38999a2dd95SBruce Richardson /* not used at this moment */
39099a2dd95SBruce Richardson case RTE_INTR_HANDLE_ALARM:
39199a2dd95SBruce Richardson rc = -1;
39299a2dd95SBruce Richardson break;
39399a2dd95SBruce Richardson /* not used at this moment */
39499a2dd95SBruce Richardson case RTE_INTR_HANDLE_DEV_EVENT:
39599a2dd95SBruce Richardson rc = -1;
39699a2dd95SBruce Richardson break;
39799a2dd95SBruce Richardson /* unknown handle type */
39899a2dd95SBruce Richardson default:
399*ae67895bSDavid Marchand EAL_LOG(ERR, "Unknown handle type of fd %d",
400bbbac4cdSHarman Kalra rte_intr_fd_get(intr_handle));
40199a2dd95SBruce Richardson rc = -1;
40299a2dd95SBruce Richardson break;
40399a2dd95SBruce Richardson }
40499a2dd95SBruce Richardson
40599a2dd95SBruce Richardson out:
40699a2dd95SBruce Richardson rte_eal_trace_intr_enable(intr_handle, rc);
40799a2dd95SBruce Richardson return rc;
40899a2dd95SBruce Richardson }
40999a2dd95SBruce Richardson
41099a2dd95SBruce Richardson int
rte_intr_disable(const struct rte_intr_handle * intr_handle)41199a2dd95SBruce Richardson rte_intr_disable(const struct rte_intr_handle *intr_handle)
41299a2dd95SBruce Richardson {
41399a2dd95SBruce Richardson int rc = 0;
41499a2dd95SBruce Richardson
41599a2dd95SBruce Richardson if (intr_handle == NULL)
41699a2dd95SBruce Richardson return -1;
41799a2dd95SBruce Richardson
418bbbac4cdSHarman Kalra if (rte_intr_type_get(intr_handle) == RTE_INTR_HANDLE_VDEV) {
41999a2dd95SBruce Richardson rc = 0;
42099a2dd95SBruce Richardson goto out;
42199a2dd95SBruce Richardson }
42299a2dd95SBruce Richardson
423bbbac4cdSHarman Kalra if (rte_intr_fd_get(intr_handle) < 0 ||
424bbbac4cdSHarman Kalra rte_intr_dev_fd_get(intr_handle) < 0) {
42599a2dd95SBruce Richardson rc = -1;
42699a2dd95SBruce Richardson goto out;
42799a2dd95SBruce Richardson }
42899a2dd95SBruce Richardson
429bbbac4cdSHarman Kalra switch (rte_intr_type_get(intr_handle)) {
43099a2dd95SBruce Richardson /* not used at this moment */
43199a2dd95SBruce Richardson case RTE_INTR_HANDLE_ALARM:
43299a2dd95SBruce Richardson rc = -1;
43399a2dd95SBruce Richardson break;
43499a2dd95SBruce Richardson /* not used at this moment */
43599a2dd95SBruce Richardson case RTE_INTR_HANDLE_DEV_EVENT:
43699a2dd95SBruce Richardson rc = -1;
43799a2dd95SBruce Richardson break;
43899a2dd95SBruce Richardson /* unknown handle type */
43999a2dd95SBruce Richardson default:
440*ae67895bSDavid Marchand EAL_LOG(ERR, "Unknown handle type of fd %d",
441bbbac4cdSHarman Kalra rte_intr_fd_get(intr_handle));
44299a2dd95SBruce Richardson rc = -1;
44399a2dd95SBruce Richardson break;
44499a2dd95SBruce Richardson }
44599a2dd95SBruce Richardson out:
44699a2dd95SBruce Richardson rte_eal_trace_intr_disable(intr_handle, rc);
44799a2dd95SBruce Richardson return rc;
44899a2dd95SBruce Richardson }
44999a2dd95SBruce Richardson
45099a2dd95SBruce Richardson int
rte_intr_ack(const struct rte_intr_handle * intr_handle)45199a2dd95SBruce Richardson rte_intr_ack(const struct rte_intr_handle *intr_handle)
45299a2dd95SBruce Richardson {
453bbbac4cdSHarman Kalra if (rte_intr_type_get(intr_handle) == RTE_INTR_HANDLE_VDEV)
45499a2dd95SBruce Richardson return 0;
45599a2dd95SBruce Richardson
45699a2dd95SBruce Richardson return -1;
45799a2dd95SBruce Richardson }
45899a2dd95SBruce Richardson
45999a2dd95SBruce Richardson static void
eal_intr_process_interrupts(struct kevent * events,int nfds)46099a2dd95SBruce Richardson eal_intr_process_interrupts(struct kevent *events, int nfds)
46199a2dd95SBruce Richardson {
46299a2dd95SBruce Richardson struct rte_intr_callback active_cb;
46399a2dd95SBruce Richardson union rte_intr_read_buffer buf;
46499a2dd95SBruce Richardson struct rte_intr_callback *cb, *next;
46599a2dd95SBruce Richardson struct rte_intr_source *src;
46699a2dd95SBruce Richardson bool call = false;
46799a2dd95SBruce Richardson int n, bytes_read;
46899a2dd95SBruce Richardson struct kevent ke;
46999a2dd95SBruce Richardson
47099a2dd95SBruce Richardson for (n = 0; n < nfds; n++) {
47199a2dd95SBruce Richardson int event_fd = events[n].ident;
47299a2dd95SBruce Richardson
47399a2dd95SBruce Richardson rte_spinlock_lock(&intr_lock);
47499a2dd95SBruce Richardson TAILQ_FOREACH(src, &intr_sources, next)
475bbbac4cdSHarman Kalra if (rte_intr_fd_get(src->intr_handle) == event_fd)
47699a2dd95SBruce Richardson break;
47799a2dd95SBruce Richardson if (src == NULL) {
47899a2dd95SBruce Richardson rte_spinlock_unlock(&intr_lock);
47999a2dd95SBruce Richardson continue;
48099a2dd95SBruce Richardson }
48199a2dd95SBruce Richardson
48299a2dd95SBruce Richardson /* mark this interrupt source as active and release the lock. */
48399a2dd95SBruce Richardson src->active = 1;
48499a2dd95SBruce Richardson rte_spinlock_unlock(&intr_lock);
48599a2dd95SBruce Richardson
48699a2dd95SBruce Richardson /* set the length to be read dor different handle type */
487bbbac4cdSHarman Kalra switch (rte_intr_type_get(src->intr_handle)) {
48899a2dd95SBruce Richardson case RTE_INTR_HANDLE_ALARM:
48999a2dd95SBruce Richardson bytes_read = 0;
49099a2dd95SBruce Richardson call = true;
49199a2dd95SBruce Richardson break;
49299a2dd95SBruce Richardson case RTE_INTR_HANDLE_VDEV:
49399a2dd95SBruce Richardson case RTE_INTR_HANDLE_EXT:
49499a2dd95SBruce Richardson bytes_read = 0;
49599a2dd95SBruce Richardson call = true;
49699a2dd95SBruce Richardson break;
49799a2dd95SBruce Richardson case RTE_INTR_HANDLE_DEV_EVENT:
49899a2dd95SBruce Richardson bytes_read = 0;
49999a2dd95SBruce Richardson call = true;
50099a2dd95SBruce Richardson break;
50199a2dd95SBruce Richardson default:
50299a2dd95SBruce Richardson bytes_read = 1;
50399a2dd95SBruce Richardson break;
50499a2dd95SBruce Richardson }
50599a2dd95SBruce Richardson
50699a2dd95SBruce Richardson if (bytes_read > 0) {
50799a2dd95SBruce Richardson /**
50899a2dd95SBruce Richardson * read out to clear the ready-to-be-read flag
50999a2dd95SBruce Richardson * for epoll_wait.
51099a2dd95SBruce Richardson */
51199a2dd95SBruce Richardson bytes_read = read(event_fd, &buf, bytes_read);
51299a2dd95SBruce Richardson if (bytes_read < 0) {
51399a2dd95SBruce Richardson if (errno == EINTR || errno == EWOULDBLOCK)
51499a2dd95SBruce Richardson continue;
51599a2dd95SBruce Richardson
516*ae67895bSDavid Marchand EAL_LOG(ERR, "Error reading from file "
517*ae67895bSDavid Marchand "descriptor %d: %s",
51899a2dd95SBruce Richardson event_fd,
51999a2dd95SBruce Richardson strerror(errno));
52099a2dd95SBruce Richardson } else if (bytes_read == 0)
521*ae67895bSDavid Marchand EAL_LOG(ERR, "Read nothing from file "
522*ae67895bSDavid Marchand "descriptor %d", event_fd);
52399a2dd95SBruce Richardson else
52499a2dd95SBruce Richardson call = true;
52599a2dd95SBruce Richardson }
52699a2dd95SBruce Richardson
52799a2dd95SBruce Richardson /* grab a lock, again to call callbacks and update status. */
52899a2dd95SBruce Richardson rte_spinlock_lock(&intr_lock);
52999a2dd95SBruce Richardson
53099a2dd95SBruce Richardson if (call) {
53199a2dd95SBruce Richardson /* Finally, call all callbacks. */
53299a2dd95SBruce Richardson TAILQ_FOREACH(cb, &src->callbacks, next) {
53399a2dd95SBruce Richardson
53499a2dd95SBruce Richardson /* make a copy and unlock. */
53599a2dd95SBruce Richardson active_cb = *cb;
53699a2dd95SBruce Richardson rte_spinlock_unlock(&intr_lock);
53799a2dd95SBruce Richardson
53899a2dd95SBruce Richardson /* call the actual callback */
53999a2dd95SBruce Richardson active_cb.cb_fn(active_cb.cb_arg);
54099a2dd95SBruce Richardson
54199a2dd95SBruce Richardson /*get the lock back. */
54299a2dd95SBruce Richardson rte_spinlock_lock(&intr_lock);
54399a2dd95SBruce Richardson }
54499a2dd95SBruce Richardson }
54599a2dd95SBruce Richardson
54699a2dd95SBruce Richardson /* we done with that interrupt source, release it. */
54799a2dd95SBruce Richardson src->active = 0;
54899a2dd95SBruce Richardson
54999a2dd95SBruce Richardson /* check if any callback are supposed to be removed */
55099a2dd95SBruce Richardson for (cb = TAILQ_FIRST(&src->callbacks); cb != NULL; cb = next) {
55199a2dd95SBruce Richardson next = TAILQ_NEXT(cb, next);
55299a2dd95SBruce Richardson if (cb->pending_delete) {
55399a2dd95SBruce Richardson /* remove it from the kqueue */
55499a2dd95SBruce Richardson memset(&ke, 0, sizeof(ke));
55599a2dd95SBruce Richardson /* mark for deletion from the queue */
55699a2dd95SBruce Richardson ke.flags = EV_DELETE;
55799a2dd95SBruce Richardson
558bbbac4cdSHarman Kalra if (intr_source_to_kevent(src->intr_handle, &ke) < 0) {
559*ae67895bSDavid Marchand EAL_LOG(ERR, "Cannot convert to kevent");
56099a2dd95SBruce Richardson rte_spinlock_unlock(&intr_lock);
56199a2dd95SBruce Richardson return;
56299a2dd95SBruce Richardson }
56399a2dd95SBruce Richardson
56499a2dd95SBruce Richardson /**
56599a2dd95SBruce Richardson * remove intr file descriptor from wait list.
56699a2dd95SBruce Richardson */
56799a2dd95SBruce Richardson if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) {
568*ae67895bSDavid Marchand EAL_LOG(ERR, "Error removing fd %d kevent, %s",
569bbbac4cdSHarman Kalra rte_intr_fd_get(src->intr_handle),
57099a2dd95SBruce Richardson strerror(errno));
57199a2dd95SBruce Richardson /* removing non-existent even is an expected
57299a2dd95SBruce Richardson * condition in some circumstances
57399a2dd95SBruce Richardson * (e.g. oneshot events).
57499a2dd95SBruce Richardson */
57599a2dd95SBruce Richardson }
57699a2dd95SBruce Richardson
57799a2dd95SBruce Richardson TAILQ_REMOVE(&src->callbacks, cb, next);
57899a2dd95SBruce Richardson if (cb->ucb_fn)
579bbbac4cdSHarman Kalra cb->ucb_fn(src->intr_handle, cb->cb_arg);
58099a2dd95SBruce Richardson free(cb);
58199a2dd95SBruce Richardson }
58299a2dd95SBruce Richardson }
58399a2dd95SBruce Richardson
58499a2dd95SBruce Richardson /* all callbacks for that source are removed. */
58599a2dd95SBruce Richardson if (TAILQ_EMPTY(&src->callbacks)) {
58699a2dd95SBruce Richardson TAILQ_REMOVE(&intr_sources, src, next);
58799a2dd95SBruce Richardson free(src);
58899a2dd95SBruce Richardson }
58999a2dd95SBruce Richardson
59099a2dd95SBruce Richardson rte_spinlock_unlock(&intr_lock);
59199a2dd95SBruce Richardson }
59299a2dd95SBruce Richardson }
59399a2dd95SBruce Richardson
5941c1abf17SThomas Monjalon static uint32_t
eal_intr_thread_main(void * arg __rte_unused)59599a2dd95SBruce Richardson eal_intr_thread_main(void *arg __rte_unused)
59699a2dd95SBruce Richardson {
59799a2dd95SBruce Richardson struct kevent events[MAX_INTR_EVENTS];
59899a2dd95SBruce Richardson int nfds;
59999a2dd95SBruce Richardson
60099a2dd95SBruce Richardson /* host thread, never break out */
60199a2dd95SBruce Richardson for (;;) {
60299a2dd95SBruce Richardson /* do not change anything, just wait */
60399a2dd95SBruce Richardson nfds = kevent(kq, NULL, 0, events, MAX_INTR_EVENTS, NULL);
60499a2dd95SBruce Richardson
60599a2dd95SBruce Richardson /* kevent fail */
60699a2dd95SBruce Richardson if (nfds < 0) {
60799a2dd95SBruce Richardson if (errno == EINTR)
60899a2dd95SBruce Richardson continue;
609*ae67895bSDavid Marchand EAL_LOG(ERR,
610*ae67895bSDavid Marchand "kevent returns with fail");
61199a2dd95SBruce Richardson break;
61299a2dd95SBruce Richardson }
61399a2dd95SBruce Richardson /* kevent timeout, will never happen here */
61499a2dd95SBruce Richardson else if (nfds == 0)
61599a2dd95SBruce Richardson continue;
61699a2dd95SBruce Richardson
61799a2dd95SBruce Richardson /* kevent has at least one fd ready to read */
61899a2dd95SBruce Richardson eal_intr_process_interrupts(events, nfds);
61999a2dd95SBruce Richardson }
62099a2dd95SBruce Richardson close(kq);
62199a2dd95SBruce Richardson kq = -1;
6221c1abf17SThomas Monjalon return 0;
62399a2dd95SBruce Richardson }
62499a2dd95SBruce Richardson
62599a2dd95SBruce Richardson int
rte_eal_intr_init(void)62699a2dd95SBruce Richardson rte_eal_intr_init(void)
62799a2dd95SBruce Richardson {
62899a2dd95SBruce Richardson int ret = 0;
62999a2dd95SBruce Richardson
63099a2dd95SBruce Richardson /* init the global interrupt source head */
63199a2dd95SBruce Richardson TAILQ_INIT(&intr_sources);
63299a2dd95SBruce Richardson
63399a2dd95SBruce Richardson kq = kqueue();
63499a2dd95SBruce Richardson if (kq < 0) {
635*ae67895bSDavid Marchand EAL_LOG(ERR, "Cannot create kqueue instance");
63699a2dd95SBruce Richardson return -1;
63799a2dd95SBruce Richardson }
63899a2dd95SBruce Richardson
63999a2dd95SBruce Richardson /* create the host thread to wait/handle the interrupt */
6401c1abf17SThomas Monjalon ret = rte_thread_create_internal_control(&intr_thread, "intr",
64199a2dd95SBruce Richardson eal_intr_thread_main, NULL);
64299a2dd95SBruce Richardson if (ret != 0) {
64399a2dd95SBruce Richardson rte_errno = -ret;
644*ae67895bSDavid Marchand EAL_LOG(ERR,
645*ae67895bSDavid Marchand "Failed to create thread for interrupt handling");
64699a2dd95SBruce Richardson }
64799a2dd95SBruce Richardson
64899a2dd95SBruce Richardson return ret;
64999a2dd95SBruce Richardson }
65099a2dd95SBruce Richardson
65199a2dd95SBruce Richardson int
rte_intr_rx_ctl(struct rte_intr_handle * intr_handle,int epfd,int op,unsigned int vec,void * data)65299a2dd95SBruce Richardson rte_intr_rx_ctl(struct rte_intr_handle *intr_handle,
65399a2dd95SBruce Richardson int epfd, int op, unsigned int vec, void *data)
65499a2dd95SBruce Richardson {
65599a2dd95SBruce Richardson RTE_SET_USED(intr_handle);
65699a2dd95SBruce Richardson RTE_SET_USED(epfd);
65799a2dd95SBruce Richardson RTE_SET_USED(op);
65899a2dd95SBruce Richardson RTE_SET_USED(vec);
65999a2dd95SBruce Richardson RTE_SET_USED(data);
66099a2dd95SBruce Richardson
66199a2dd95SBruce Richardson return -ENOTSUP;
66299a2dd95SBruce Richardson }
66399a2dd95SBruce Richardson
66499a2dd95SBruce Richardson int
rte_intr_efd_enable(struct rte_intr_handle * intr_handle,uint32_t nb_efd)66599a2dd95SBruce Richardson rte_intr_efd_enable(struct rte_intr_handle *intr_handle, uint32_t nb_efd)
66699a2dd95SBruce Richardson {
66799a2dd95SBruce Richardson RTE_SET_USED(intr_handle);
66899a2dd95SBruce Richardson RTE_SET_USED(nb_efd);
66999a2dd95SBruce Richardson
67099a2dd95SBruce Richardson return 0;
67199a2dd95SBruce Richardson }
67299a2dd95SBruce Richardson
67399a2dd95SBruce Richardson void
rte_intr_efd_disable(struct rte_intr_handle * intr_handle)67499a2dd95SBruce Richardson rte_intr_efd_disable(struct rte_intr_handle *intr_handle)
67599a2dd95SBruce Richardson {
67699a2dd95SBruce Richardson RTE_SET_USED(intr_handle);
67799a2dd95SBruce Richardson }
67899a2dd95SBruce Richardson
67999a2dd95SBruce Richardson int
rte_intr_dp_is_en(struct rte_intr_handle * intr_handle)68099a2dd95SBruce Richardson rte_intr_dp_is_en(struct rte_intr_handle *intr_handle)
68199a2dd95SBruce Richardson {
68299a2dd95SBruce Richardson RTE_SET_USED(intr_handle);
68399a2dd95SBruce Richardson return 0;
68499a2dd95SBruce Richardson }
68599a2dd95SBruce Richardson
68699a2dd95SBruce Richardson int
rte_intr_allow_others(struct rte_intr_handle * intr_handle)68799a2dd95SBruce Richardson rte_intr_allow_others(struct rte_intr_handle *intr_handle)
68899a2dd95SBruce Richardson {
68999a2dd95SBruce Richardson RTE_SET_USED(intr_handle);
69099a2dd95SBruce Richardson return 1;
69199a2dd95SBruce Richardson }
69299a2dd95SBruce Richardson
69399a2dd95SBruce Richardson int
rte_intr_cap_multiple(struct rte_intr_handle * intr_handle)69499a2dd95SBruce Richardson rte_intr_cap_multiple(struct rte_intr_handle *intr_handle)
69599a2dd95SBruce Richardson {
69699a2dd95SBruce Richardson RTE_SET_USED(intr_handle);
69799a2dd95SBruce Richardson return 0;
69899a2dd95SBruce Richardson }
69999a2dd95SBruce Richardson
70099a2dd95SBruce Richardson int
rte_epoll_wait(int epfd,struct rte_epoll_event * events,int maxevents,int timeout)70199a2dd95SBruce Richardson rte_epoll_wait(int epfd, struct rte_epoll_event *events,
70299a2dd95SBruce Richardson int maxevents, int timeout)
70399a2dd95SBruce Richardson {
70499a2dd95SBruce Richardson RTE_SET_USED(epfd);
70599a2dd95SBruce Richardson RTE_SET_USED(events);
70699a2dd95SBruce Richardson RTE_SET_USED(maxevents);
70799a2dd95SBruce Richardson RTE_SET_USED(timeout);
70899a2dd95SBruce Richardson
70999a2dd95SBruce Richardson return -ENOTSUP;
71099a2dd95SBruce Richardson }
71199a2dd95SBruce Richardson
71299a2dd95SBruce Richardson int
rte_epoll_wait_interruptible(int epfd,struct rte_epoll_event * events,int maxevents,int timeout)71399a2dd95SBruce Richardson rte_epoll_wait_interruptible(int epfd, struct rte_epoll_event *events,
71499a2dd95SBruce Richardson int maxevents, int timeout)
71599a2dd95SBruce Richardson {
71699a2dd95SBruce Richardson RTE_SET_USED(epfd);
71799a2dd95SBruce Richardson RTE_SET_USED(events);
71899a2dd95SBruce Richardson RTE_SET_USED(maxevents);
71999a2dd95SBruce Richardson RTE_SET_USED(timeout);
72099a2dd95SBruce Richardson
72199a2dd95SBruce Richardson return -ENOTSUP;
72299a2dd95SBruce Richardson }
72399a2dd95SBruce Richardson
72499a2dd95SBruce Richardson int
rte_epoll_ctl(int epfd,int op,int fd,struct rte_epoll_event * event)72599a2dd95SBruce Richardson rte_epoll_ctl(int epfd, int op, int fd, struct rte_epoll_event *event)
72699a2dd95SBruce Richardson {
72799a2dd95SBruce Richardson RTE_SET_USED(epfd);
72899a2dd95SBruce Richardson RTE_SET_USED(op);
72999a2dd95SBruce Richardson RTE_SET_USED(fd);
73099a2dd95SBruce Richardson RTE_SET_USED(event);
73199a2dd95SBruce Richardson
73299a2dd95SBruce Richardson return -ENOTSUP;
73399a2dd95SBruce Richardson }
73499a2dd95SBruce Richardson
73599a2dd95SBruce Richardson int
rte_intr_tls_epfd(void)73699a2dd95SBruce Richardson rte_intr_tls_epfd(void)
73799a2dd95SBruce Richardson {
73899a2dd95SBruce Richardson return -ENOTSUP;
73999a2dd95SBruce Richardson }
74099a2dd95SBruce Richardson
74199a2dd95SBruce Richardson void
rte_intr_free_epoll_fd(struct rte_intr_handle * intr_handle)74299a2dd95SBruce Richardson rte_intr_free_epoll_fd(struct rte_intr_handle *intr_handle)
74399a2dd95SBruce Richardson {
74499a2dd95SBruce Richardson RTE_SET_USED(intr_handle);
74599a2dd95SBruce Richardson }
74699a2dd95SBruce Richardson
rte_thread_is_intr(void)74799a2dd95SBruce Richardson int rte_thread_is_intr(void)
74899a2dd95SBruce Richardson {
7491c1abf17SThomas Monjalon return rte_thread_equal(intr_thread, rte_thread_self());
75099a2dd95SBruce Richardson }
751