TAILQ_ENTRY(rte_intr_callback) next;
rte_intr_callback_fn cb_fn; /**< callback address */
void *cb_arg; /**< parameter for callback */
+ uint8_t pending_delete; /**< delete after callback is called */
+ rte_intr_unregister_callback_fn ucb_fn; /**< fn to call before cb is deleted */
};
struct rte_intr_source {
rte_intr_callback_register(const struct rte_intr_handle *intr_handle,
rte_intr_callback_fn cb, void *cb_arg)
{
- struct rte_intr_callback *callback = NULL;
- struct rte_intr_source *src = NULL;
- int ret, add_event;
+ struct rte_intr_callback *callback;
+ struct rte_intr_source *src;
+ int ret, add_event = 0;
/* first do parameter checking */
if (intr_handle == NULL || intr_handle->fd < 0 || cb == NULL) {
return -ENODEV;
}
- /* allocate a new interrupt callback entity */
- callback = calloc(1, sizeof(*callback));
- if (callback == NULL) {
- RTE_LOG(ERR, EAL, "Can not allocate memory\n");
- return -ENOMEM;
- }
- callback->cb_fn = cb;
- callback->cb_arg = cb_arg;
-
rte_spinlock_lock(&intr_lock);
- /* check if there is at least one callback registered for the fd */
+ /* find the source for this intr_handle */
TAILQ_FOREACH(src, &intr_sources, next) {
- if (src->intr_handle.fd == intr_handle->fd) {
- /* we had no interrupts for this */
- if (TAILQ_EMPTY(&src->callbacks))
- add_event = 1;
-
- TAILQ_INSERT_TAIL(&(src->callbacks), callback, next);
- ret = 0;
+ if (src->intr_handle.fd == intr_handle->fd)
break;
- }
}
- /* no existing callbacks for this - add new source */
- if (src == NULL) {
- src = calloc(1, sizeof(*src));
- if (src == NULL) {
+ /* if this is an alarm interrupt and it already has a callback,
+ * then we don't want to create a new callback because the only
+ * thing on the list should be eal_alarm_callback() and we may
+ * be called just to reset the timer.
+ */
+ if (src != NULL && src->intr_handle.type == RTE_INTR_HANDLE_ALARM &&
+ !TAILQ_EMPTY(&src->callbacks)) {
+ callback = NULL;
+ } else {
+ /* allocate a new interrupt callback entity */
+ callback = calloc(1, sizeof(*callback));
+ if (callback == NULL) {
RTE_LOG(ERR, EAL, "Can not allocate memory\n");
ret = -ENOMEM;
goto fail;
- } else {
- src->intr_handle = *intr_handle;
- TAILQ_INIT(&src->callbacks);
- TAILQ_INSERT_TAIL(&(src->callbacks), callback, next);
- TAILQ_INSERT_TAIL(&intr_sources, src, next);
- add_event = 1;
- ret = 0;
}
+ callback->cb_fn = cb;
+ callback->cb_arg = cb_arg;
+ callback->pending_delete = 0;
+ callback->ucb_fn = NULL;
+
+ if (src == NULL) {
+ src = calloc(1, sizeof(*src));
+ if (src == NULL) {
+ RTE_LOG(ERR, EAL, "Can not allocate memory\n");
+ ret = -ENOMEM;
+ goto fail;
+ } else {
+ src->intr_handle = *intr_handle;
+ TAILQ_INIT(&src->callbacks);
+ TAILQ_INSERT_TAIL(&intr_sources, src, next);
+ }
+ }
+
+ /* we had no interrupts for this */
+ if (TAILQ_EMPTY(&src->callbacks))
+ add_event = 1;
+
+ TAILQ_INSERT_TAIL(&(src->callbacks), callback, next);
}
/* add events to the queue. timer events are special as we need to
}
rte_spinlock_unlock(&intr_lock);
- return ret;
+ return 0;
fail:
/* clean up */
if (src != NULL) {
- TAILQ_REMOVE(&(src->callbacks), callback, next);
+ if (callback != NULL)
+ TAILQ_REMOVE(&(src->callbacks), callback, next);
if (TAILQ_EMPTY(&(src->callbacks))) {
TAILQ_REMOVE(&intr_sources, src, next);
free(src);
return ret;
}
+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)
+{
+ int ret;
+ struct rte_intr_source *src;
+ struct rte_intr_callback *cb, *next;
+
+ /* do parameter checking first */
+ if (intr_handle == NULL || intr_handle->fd < 0) {
+ RTE_LOG(ERR, EAL,
+ "Unregistering with invalid input parameter\n");
+ return -EINVAL;
+ }
+
+ if (kq < 0) {
+ RTE_LOG(ERR, EAL, "Kqueue is not active\n");
+ return -ENODEV;
+ }
+
+ rte_spinlock_lock(&intr_lock);
+
+ /* check if the insterrupt source for the fd is existent */
+ TAILQ_FOREACH(src, &intr_sources, next)
+ if (src->intr_handle.fd == intr_handle->fd)
+ break;
+
+ /* No interrupt source registered for the fd */
+ if (src == NULL) {
+ ret = -ENOENT;
+
+ /* only usable if the source is active */
+ } else if (src->active == 0) {
+ ret = -EAGAIN;
+
+ } else {
+ ret = 0;
+
+ /* walk through the callbacks and mark all that match. */
+ for (cb = TAILQ_FIRST(&src->callbacks); cb != NULL; cb = next) {
+ next = TAILQ_NEXT(cb, next);
+ if (cb->cb_fn == cb_fn && (cb_arg == (void *)-1 ||
+ cb->cb_arg == cb_arg)) {
+ cb->pending_delete = 1;
+ cb->ucb_fn = ucb_fn;
+ ret++;
+ }
+ }
+ }
+
+ rte_spinlock_unlock(&intr_lock);
+
+ return ret;
+}
+
int
rte_intr_callback_unregister(const struct rte_intr_handle *intr_handle,
rte_intr_callback_fn cb_fn, void *cb_arg)
return 0;
}
+int
+rte_intr_ack(const struct rte_intr_handle *intr_handle)
+{
+ if (intr_handle && intr_handle->type == RTE_INTR_HANDLE_VDEV)
+ return 0;
+
+ return -1;
+}
+
static void
eal_intr_process_interrupts(struct kevent *events, int nfds)
{
struct rte_intr_callback active_cb;
union rte_intr_read_buffer buf;
- struct rte_intr_callback *cb;
+ struct rte_intr_callback *cb, *next;
struct rte_intr_source *src;
bool call = false;
int n, bytes_read;
+ struct kevent ke;
for (n = 0; n < nfds; n++) {
int event_fd = events[n].ident;
/* we done with that interrupt source, release it. */
src->active = 0;
+
+ /* check if any callback are supposed to be removed */
+ for (cb = TAILQ_FIRST(&src->callbacks); cb != NULL; cb = next) {
+ next = TAILQ_NEXT(cb, next);
+ if (cb->pending_delete) {
+ /* remove it from the kqueue */
+ memset(&ke, 0, sizeof(ke));
+ /* mark for deletion from the queue */
+ ke.flags = EV_DELETE;
+
+ if (intr_source_to_kevent(&src->intr_handle, &ke) < 0) {
+ RTE_LOG(ERR, EAL, "Cannot convert to kevent\n");
+ rte_spinlock_unlock(&intr_lock);
+ return;
+ }
+
+ /**
+ * remove intr file descriptor from wait list.
+ */
+ if (kevent(kq, &ke, 1, NULL, 0, NULL) < 0) {
+ RTE_LOG(ERR, EAL, "Error removing fd %d kevent, "
+ "%s\n", src->intr_handle.fd,
+ strerror(errno));
+ /* removing non-existent even is an expected
+ * condition in some circumstances
+ * (e.g. oneshot events).
+ */
+ }
+
+ TAILQ_REMOVE(&src->callbacks, cb, next);
+ if (cb->ucb_fn)
+ cb->ucb_fn(&src->intr_handle, cb->cb_arg);
+ free(cb);
+ }
+ }
+
+ /* all callbacks for that source are removed. */
+ if (TAILQ_EMPTY(&src->callbacks)) {
+ TAILQ_REMOVE(&intr_sources, src, next);
+ free(src);
+ }
+
rte_spinlock_unlock(&intr_lock);
}
}
{
RTE_SET_USED(intr_handle);
}
+
+int rte_thread_is_intr(void)
+{
+ return pthread_equal(intr_thread, pthread_self());
+}