eal/freebsd: fix queuing duplicate alarm callbacks
[dpdk.git] / lib / librte_eal / freebsd / eal / eal_interrupts.c
index 2feee2d..00991f2 100644 (file)
@@ -33,6 +33,8 @@ struct rte_intr_callback {
        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 {
@@ -81,9 +83,9 @@ int
 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) {
@@ -96,45 +98,53 @@ rte_intr_callback_register(const struct rte_intr_handle *intr_handle,
                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
@@ -174,11 +184,12 @@ rte_intr_callback_register(const struct rte_intr_handle *intr_handle,
        }
        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);
@@ -189,6 +200,62 @@ fail:
        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)
@@ -327,15 +394,25 @@ rte_intr_disable(const struct rte_intr_handle *intr_handle)
        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;
@@ -415,6 +492,48 @@ eal_intr_process_interrupts(struct kevent *events, int nfds)
 
                /* 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);
        }
 }
@@ -559,3 +678,8 @@ rte_intr_free_epoll_fd(struct rte_intr_handle *intr_handle)
 {
        RTE_SET_USED(intr_handle);
 }
+
+int rte_thread_is_intr(void)
+{
+       return pthread_equal(intr_thread, pthread_self());
+}