An interrupt source keeps a private copy of the interrupt handle it was
registered with. The copy inherits the handle FD, the device FD and the
event FDs but owns none of them: they are closed by whoever set up the
original handle.

Since the leaked FD warning was added, freeing a source after its last
callback is unregistered reports the handle FD and device FD as leaked,
for every device on close and again for every VFIO device at EAL cleanup.

Clear all FD references in the copy before freeing it.

Fixes: d770ee9caed0 ("interrupts: warn on leaked FDs")

Signed-off-by: Anatoly Burakov <[email protected]>
---
 lib/eal/linux/eal_interrupts.c | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)

diff --git a/lib/eal/linux/eal_interrupts.c b/lib/eal/linux/eal_interrupts.c
index 410b6d66c45..aadb7b84a22 100644
--- a/lib/eal/linux/eal_interrupts.c
+++ b/lib/eal/linux/eal_interrupts.c
@@ -95,6 +95,25 @@ static struct rte_intr_source_list intr_sources;
 /* interrupt handling thread */
 static rte_thread_t intr_thread;
 
+/*
+ * An interrupt source holds a copy of the handle it was registered with
+ * and owns none of the file descriptors. Clear the references before
+ * freeing the copy so they are not reported as leaks.
+ */
+static void
+eal_intr_source_free(struct rte_intr_source *src)
+{
+       struct rte_intr_handle *intr_handle = src->intr_handle;
+       int i;
+
+       rte_intr_fd_set(intr_handle, -1);
+       rte_intr_dev_fd_set(intr_handle, -1);
+       for (i = 0; i < rte_intr_nb_intr_get(intr_handle); i++)
+               rte_intr_efds_index_set(intr_handle, i, -1);
+       rte_intr_instance_free(intr_handle);
+       free(src);
+}
+
 /* VFIO interrupts */
 #define IRQ_SET_BUF_LEN  (sizeof(struct vfio_irq_set) + sizeof(int))
 /* irq set buffer length for queue interrupts and LSC interrupt */
@@ -666,8 +685,7 @@ 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);
+                       eal_intr_source_free(src);
                }
        }
 
@@ -922,8 +940,7 @@ eal_intr_source_remove_and_free(struct rte_intr_source *src)
        }
 
        /* Free the interrupt source */
-       rte_intr_instance_free(src->intr_handle);
-       free(src);
+       eal_intr_source_free(src);
 }
 
 static int
@@ -1067,8 +1084,7 @@ 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);
+                       eal_intr_source_free(src);
                }
 
                /* notify the pipe fd waited by epoll_wait to rebuild the wait 
list */
-- 
2.52.0

Reply via email to