The FD leak warnings recently added have revealed an interesting issue.

An interrupt handle is allocated and living usually in a bus device object
(or some standalone code, like the alarm code).
All the logic around the referenced FDs in this object are specific to
the callers, and those FDs must then be closed in the callers context.

On the other hand, the interrupt subsystem works with copies of interrupt
handle objects passed to it via rte_intr_callback_register/unregister.

When releasing this clone of the interrupt handle, the FDs are reported
as leaked, though there is no way for the interrupt subsystem itself to
know what to do with those FDs.

Instead of creating a clone, reference the interrupt handle directly.
The callers are responsible for unregistering those handles,
closing the FDs and freeing the handles.

This change also switches the interrupt source lookup from FD based
matching to pointer based matching.
The original FD based matching allowed different intr_handle objects
with the same FD to share an interrupt source.
However, this was problematic:
* the cloned handle stored in the source came from the first registrant,
* the ucb_fn callback would receive this clone, not the (const)
  caller's handle,
* with multiple handles for the same FD, later registrants would get
  the wrong handle in their callback,

In practice, DPDK components use one intr_handle per FD, so pointer
based matching simply enforces this existing invariant.
The interrupt dispatch code continues to use FD based matching since
epoll returns the FD, not the handle pointer.

The unregister callback (ucb_fn) signature is updated to take a const
pointer, reflecting that the interrupt subsystem no longer owns the
handle. Note that with this change, the intr_handle parameter passed
to ucb_fn is the same pointer the caller registered with, callers
can simply use their own handle from cb_arg instead.

Signed-off-by: David Marchand <[email protected]>
---
 doc/guides/rel_notes/release_26_11.rst |  1 +
 drivers/common/cnxk/roc_platform.h     |  1 -
 drivers/net/memif/memif_socket.c       |  5 ++--
 lib/eal/common/eal_common_interrupts.c | 26 --------------------
 lib/eal/freebsd/eal_interrupts.c       | 17 ++++---------
 lib/eal/include/rte_interrupts.h       | 17 +------------
 lib/eal/linux/eal_interrupts.c         | 33 ++++++++------------------
 7 files changed, 20 insertions(+), 80 deletions(-)

diff --git a/doc/guides/rel_notes/release_26_11.rst 
b/doc/guides/rel_notes/release_26_11.rst
index be758a0c02..78f9bb54aa 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -404,6 +404,7 @@ API Changes
     to invalid values instead of zero.
   * Added ``rte_intr_fd_close()`` and ``rte_intr_dev_fd_close()``
     helpers to close file descriptors and reset them to -1.
+  * ``rte_intr_unregister_callback_fn`` now receives a ``const`` interrupt 
handle.
 
 * mempool: When creating a mempool, the cache size can be freely specified
   (although still not exceed the number of elements),
diff --git a/drivers/common/cnxk/roc_platform.h 
b/drivers/common/cnxk/roc_platform.h
index aba9782023..149522608e 100644
--- a/drivers/common/cnxk/roc_platform.h
+++ b/drivers/common/cnxk/roc_platform.h
@@ -178,7 +178,6 @@ plt_thread_is_valid(plt_thread_t thr)
 #define plt_intr_type_get              rte_intr_type_get
 #define plt_intr_type_set              rte_intr_type_set
 #define plt_intr_instance_alloc                rte_intr_instance_alloc
-#define plt_intr_instance_dup          rte_intr_instance_dup
 #define plt_intr_instance_free         rte_intr_instance_free
 #define plt_intr_event_list_update     rte_intr_event_list_update
 #define plt_intr_max_intr_get          rte_intr_max_intr_get
diff --git a/drivers/net/memif/memif_socket.c b/drivers/net/memif/memif_socket.c
index 87b23aa8b4..2d16decbef 100644
--- a/drivers/net/memif/memif_socket.c
+++ b/drivers/net/memif/memif_socket.c
@@ -572,13 +572,14 @@ memif_msg_enq_connected(struct rte_eth_dev *dev)
 }
 
 static void
-memif_intr_unregister_handler(struct rte_intr_handle *intr_handle, void *arg)
+memif_intr_unregister_handler(const struct rte_intr_handle *intr_handle, void 
*arg)
 {
        struct memif_msg_queue_elt *elt;
        struct memif_control_channel *cc = arg;
 
+       RTE_SET_USED(intr_handle);
        /* close control channel fd */
-       rte_intr_fd_close(intr_handle);
+       rte_intr_fd_close(cc->intr_handle);
        /* clear message queue */
        while ((elt = TAILQ_FIRST(&cc->msg_queue)) != NULL) {
                TAILQ_REMOVE(&cc->msg_queue, elt, next);
diff --git a/lib/eal/common/eal_common_interrupts.c 
b/lib/eal/common/eal_common_interrupts.c
index 6368ef1771..1208a2597d 100644
--- a/lib/eal/common/eal_common_interrupts.c
+++ b/lib/eal/common/eal_common_interrupts.c
@@ -103,32 +103,6 @@ struct rte_intr_handle *rte_intr_instance_alloc(uint32_t 
flags)
        return NULL;
 }
 
-RTE_EXPORT_INTERNAL_SYMBOL(rte_intr_instance_dup)
-struct rte_intr_handle *rte_intr_instance_dup(const struct rte_intr_handle 
*src)
-{
-       struct rte_intr_handle *intr_handle;
-
-       if (src == NULL) {
-               EAL_LOG(DEBUG, "Source interrupt instance unallocated");
-               rte_errno = EINVAL;
-               return NULL;
-       }
-
-       intr_handle = rte_intr_instance_alloc(src->alloc_flags);
-       if (intr_handle != NULL) {
-               intr_handle->fd = src->fd;
-               intr_handle->dev_fd = src->dev_fd;
-               intr_handle->type = src->type;
-               intr_handle->max_intr = src->max_intr;
-               intr_handle->nb_efd = src->nb_efd;
-               intr_handle->efd_counter_size = src->efd_counter_size;
-               memcpy(intr_handle->efds, src->efds, src->nb_intr);
-               memcpy(intr_handle->elist, src->elist, src->nb_intr);
-       }
-
-       return intr_handle;
-}
-
 RTE_EXPORT_INTERNAL_SYMBOL(rte_intr_event_list_update)
 int rte_intr_event_list_update(struct rte_intr_handle *intr_handle, int size)
 {
diff --git a/lib/eal/freebsd/eal_interrupts.c b/lib/eal/freebsd/eal_interrupts.c
index bb2bc529f2..b29a2b174e 100644
--- a/lib/eal/freebsd/eal_interrupts.c
+++ b/lib/eal/freebsd/eal_interrupts.c
@@ -41,7 +41,7 @@ struct rte_intr_callback {
 
 struct rte_intr_source {
        TAILQ_ENTRY(rte_intr_source) next;
-       struct rte_intr_handle *intr_handle; /**< interrupt handle */
+       const struct rte_intr_handle *intr_handle; /**< interrupt handle */
        struct rte_intr_cb_list callbacks;  /**< user callbacks */
        uint32_t active;
 };
@@ -105,7 +105,7 @@ rte_intr_callback_register(const struct rte_intr_handle 
*intr_handle,
 
        /* find the source for this intr_handle */
        TAILQ_FOREACH(src, &intr_sources, next) {
-               if (rte_intr_fd_get(src->intr_handle) == 
rte_intr_fd_get(intr_handle))
+               if (src->intr_handle == intr_handle)
                        break;
        }
 
@@ -138,14 +138,7 @@ rte_intr_callback_register(const struct rte_intr_handle 
*intr_handle,
                                ret = -ENOMEM;
                                goto fail;
                        } else {
-                               src->intr_handle = 
rte_intr_instance_dup(intr_handle);
-                               if (src->intr_handle == NULL) {
-                                       EAL_LOG(ERR, "Can not create intr 
instance");
-                                       ret = -ENOMEM;
-                                       free(src);
-                                       src = NULL;
-                                       goto fail;
-                               }
+                               src->intr_handle = intr_handle;
                                TAILQ_INIT(&src->callbacks);
                                TAILQ_INSERT_TAIL(&intr_sources, src, next);
                        }
@@ -239,7 +232,7 @@ rte_intr_callback_unregister_pending(const struct 
rte_intr_handle *intr_handle,
 
        /* check if the interrupt source for the fd is existent */
        TAILQ_FOREACH(src, &intr_sources, next)
-               if (rte_intr_fd_get(src->intr_handle) == 
rte_intr_fd_get(intr_handle))
+               if (src->intr_handle == intr_handle)
                        break;
 
        /* No interrupt source registered for the fd */
@@ -294,7 +287,7 @@ rte_intr_callback_unregister(const struct rte_intr_handle 
*intr_handle,
 
        /* check if the interrupt source for the fd is existent */
        TAILQ_FOREACH(src, &intr_sources, next)
-               if (rte_intr_fd_get(src->intr_handle) == 
rte_intr_fd_get(intr_handle))
+               if (src->intr_handle == intr_handle)
                        break;
 
        /* No interrupt source registered for the fd */
diff --git a/lib/eal/include/rte_interrupts.h b/lib/eal/include/rte_interrupts.h
index d04c0c7b15..bb06180bea 100644
--- a/lib/eal/include/rte_interrupts.h
+++ b/lib/eal/include/rte_interrupts.h
@@ -70,7 +70,7 @@ typedef void (*rte_intr_callback_fn)(void *cb_arg);
  * Function to call after a callback is unregistered.
  * Can be used to close fd and free cb_arg.
  */
-typedef void (*rte_intr_unregister_callback_fn)(struct rte_intr_handle 
*intr_handle,
+typedef void (*rte_intr_unregister_callback_fn)(const struct rte_intr_handle 
*intr_handle,
                                                void *cb_arg);
 
 /**
@@ -432,21 +432,6 @@ __rte_internal
 int
 rte_intr_cap_multiple(struct rte_intr_handle *intr_handle);
 
-/**
- * @internal
- * Creates a clone of src by allocating a new handle and copying src content.
- *
- * @param src
- *  Source interrupt handle to be cloned.
- *
- * @return
- *  - On success, address of interrupt handle.
- *  - On failure, NULL.
- */
-__rte_internal
-struct rte_intr_handle *
-rte_intr_instance_dup(const struct rte_intr_handle *src);
-
 /**
  * @internal
  * Set the device fd field of interrupt handle with user
diff --git a/lib/eal/linux/eal_interrupts.c b/lib/eal/linux/eal_interrupts.c
index 4cfaeba7fe..dcc2c0efdb 100644
--- a/lib/eal/linux/eal_interrupts.c
+++ b/lib/eal/linux/eal_interrupts.c
@@ -78,7 +78,7 @@ struct rte_intr_callback {
 
 struct rte_intr_source {
        TAILQ_ENTRY(rte_intr_source) next;
-       struct rte_intr_handle *intr_handle; /**< interrupt handle */
+       const struct rte_intr_handle *intr_handle; /**< interrupt handle */
        struct rte_intr_cb_list callbacks;  /**< user callbacks */
        uint32_t active;
 };
@@ -511,7 +511,7 @@ rte_intr_callback_register(const struct rte_intr_handle 
*intr_handle,
 
        /* check if there is at least one callback registered for the fd */
        TAILQ_FOREACH(src, &intr_sources, next) {
-               if (rte_intr_fd_get(src->intr_handle) == 
rte_intr_fd_get(intr_handle)) {
+               if (src->intr_handle == intr_handle) {
                        /* we had no interrupts for this */
                        if (TAILQ_EMPTY(&src->callbacks))
                                wake_thread = 1;
@@ -531,22 +531,12 @@ rte_intr_callback_register(const struct rte_intr_handle 
*intr_handle,
                        free(callback);
                        callback = NULL;
                } else {
-                       src->intr_handle = rte_intr_instance_dup(intr_handle);
-                       if (src->intr_handle == NULL) {
-                               EAL_LOG(ERR, "Can not create intr instance");
-                               ret = -ENOMEM;
-                               free(callback);
-                               callback = NULL;
-                               free(src);
-                               src = NULL;
-                       } else {
-                               TAILQ_INIT(&src->callbacks);
-                               TAILQ_INSERT_TAIL(&(src->callbacks), callback,
-                                                 next);
-                               TAILQ_INSERT_TAIL(&intr_sources, src, next);
-                               wake_thread = 1;
-                               ret = 0;
-                       }
+                       src->intr_handle = intr_handle;
+                       TAILQ_INIT(&src->callbacks);
+                       TAILQ_INSERT_TAIL(&(src->callbacks), callback, next);
+                       TAILQ_INSERT_TAIL(&intr_sources, src, next);
+                       wake_thread = 1;
+                       ret = 0;
                }
        }
 
@@ -584,7 +574,7 @@ rte_intr_callback_unregister_pending(const struct 
rte_intr_handle *intr_handle,
 
        /* check if the interrupt source for the fd is existent */
        TAILQ_FOREACH(src, &intr_sources, next) {
-               if (rte_intr_fd_get(src->intr_handle) == 
rte_intr_fd_get(intr_handle))
+               if (src->intr_handle == intr_handle)
                        break;
        }
 
@@ -635,7 +625,7 @@ rte_intr_callback_unregister(const struct rte_intr_handle 
*intr_handle,
 
        /* check if the interrupt source for the fd is existent */
        TAILQ_FOREACH(src, &intr_sources, next)
-               if (rte_intr_fd_get(src->intr_handle) == 
rte_intr_fd_get(intr_handle))
+               if (src->intr_handle == intr_handle)
                        break;
 
        /* No interrupt source registered for the fd */
@@ -666,7 +656,6 @@ rte_intr_callback_unregister(const struct rte_intr_handle 
*intr_handle,
                /* all callbacks for that source are removed. */
                if (TAILQ_EMPTY(&src->callbacks)) {
                        TAILQ_REMOVE(&intr_sources, src, next);
-                       rte_intr_instance_free(src->intr_handle);
                        free(src);
                }
        }
@@ -922,7 +911,6 @@ eal_intr_source_remove_and_free(struct rte_intr_source *src)
        }
 
        /* Free the interrupt source */
-       rte_intr_instance_free(src->intr_handle);
        free(src);
 }
 
@@ -1067,7 +1055,6 @@ eal_intr_process_interrupts(struct epoll_event *events, 
int nfds)
                /* all callbacks for that source are removed. */
                if (TAILQ_EMPTY(&src->callbacks)) {
                        TAILQ_REMOVE(&intr_sources, src, next);
-                       rte_intr_instance_free(src->intr_handle);
                        free(src);
                }
 
-- 
2.54.0

Reply via email to