DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] Interrupts API update
@ 2026-10-02  9:17 David Marchand
  2026-10-02  9:17 ` [PATCH 1/5] vdpa/mlx5: fix check on err interrupt FD David Marchand
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: David Marchand @ 2026-10-02  9:17 UTC (permalink / raw)
  To: dev, hkalra

This series is a simple cleanup to have a simpler helper that close all
FD reference in an interrupt handle.
It helped catch one small FD leak in FreeBSD EAL.

This should make life (a bit) easier on driver side and help limit FD
leaks.


-- 
David Marchand

Changes since RFC v4:
- rebased after internal API cleanup,
- sent as a non RFC,
- updated one comment in net/tap code,

Changes since RFC v3:
- fixed FreeBSD compilation,

Changes since RFC v2:
- added unit test for the new API,
- caught some FD leak by code review,
- split patch 3 from v2 in two, and added one debug check (see patch 5),

Changes since RFC v1:
- squashed FreeBSD PCI change,
- updated interrupt unit test,


David Marchand (5):
  vdpa/mlx5: fix check on err interrupt FD
  eal/freebsd: fix a FD leak in the alarm subsystem
  interrupts: mark file descriptors invalid on allocation
  interrupts: close interrupt FDs
  interrupts: warn on leaked file descriptors

 app/test/test_interrupts.c                    | 91 ++++++++++++++++++-
 doc/guides/rel_notes/release_26_11.rst        |  7 ++
 drivers/bus/cdx/cdx_vfio.c                    | 16 +---
 drivers/bus/pci/bsd/pci.c                     |  7 +-
 drivers/bus/pci/linux/pci_uio.c               | 15 +--
 drivers/bus/pci/linux/pci_vfio.c              | 29 +-----
 drivers/bus/pci/pci_common_uio.c              | 18 +---
 drivers/bus/vmbus/linux/vmbus_uio.c           | 13 +--
 drivers/bus/vmbus/vmbus_common_uio.c          | 17 +---
 drivers/common/cnxk/roc_platform.h            |  2 +
 drivers/common/mlx5/linux/mlx5_common_os.c    |  6 +-
 drivers/net/failsafe/failsafe.c               |  3 -
 drivers/net/mana/mana.c                       |  5 +-
 drivers/net/memif/memif_socket.c              | 13 +--
 drivers/net/memif/rte_eth_memif.c             |  6 --
 drivers/net/mlx4/mlx4.c                       |  3 -
 drivers/net/mlx4/mlx4_intr.c                  |  1 +
 drivers/net/sxe2/sxe2_irq.c                   |  5 +-
 drivers/net/tap/rte_eth_tap.c                 |  2 +-
 .../net/virtio/virtio_user/virtio_user_dev.c  |  2 +
 drivers/raw/cnxk_gpio/cnxk_gpio.c             |  3 +
 drivers/vdpa/mlx5/mlx5_vdpa_event.c           |  6 +-
 drivers/vdpa/mlx5/mlx5_vdpa_virtq.c           |  1 +
 lib/eal/common/eal_common_interrupts.c        | 35 +++++++
 lib/eal/freebsd/eal_alarm.c                   |  9 +-
 lib/eal/include/rte_interrupts.h              | 29 ++++++
 lib/eal/linux/eal_dev.c                       |  9 +-
 27 files changed, 214 insertions(+), 139 deletions(-)

-- 
2.54.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/5] vdpa/mlx5: fix check on err interrupt FD
  2026-10-02  9:17 [PATCH 0/5] Interrupts API update David Marchand
@ 2026-10-02  9:17 ` David Marchand
  2026-10-02  9:17 ` [PATCH 2/5] eal/freebsd: fix a FD leak in the alarm subsystem David Marchand
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: David Marchand @ 2026-10-02  9:17 UTC (permalink / raw)
  To: dev, hkalra
  Cc: Dariusz Sosnowski, Matan Azrad, Viacheslav Ovsiienko, Xueming Li,
	Maxime Coquelin

A 0 file descriptor is valid.
Prefer setting to -1 to indicate an invalid FD.

Fixes: 0474419bae7c ("vdpa/mlx5: handle hardware error")

Signed-off-by: David Marchand <david.marchand@redhat.com>
Acked-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
---
 drivers/vdpa/mlx5/mlx5_vdpa_event.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/vdpa/mlx5/mlx5_vdpa_event.c b/drivers/vdpa/mlx5/mlx5_vdpa_event.c
index 43585a94b2..dee2ccc23d 100644
--- a/drivers/vdpa/mlx5/mlx5_vdpa_event.c
+++ b/drivers/vdpa/mlx5/mlx5_vdpa_event.c
@@ -452,7 +452,7 @@ mlx5_vdpa_err_event_setup(struct mlx5_vdpa_priv *priv)
 					 mlx5_vdpa_err_interrupt_handler,
 					 priv);
 	if (ret != 0) {
-		rte_intr_fd_set(priv->err_intr_handle, 0);
+		rte_intr_fd_set(priv->err_intr_handle, -1);
 		DRV_LOG(ERR, "Failed to register error interrupt for device %d.",
 			priv->vid);
 		rte_errno = -ret;
@@ -473,7 +473,7 @@ mlx5_vdpa_err_event_unset(struct mlx5_vdpa_priv *priv)
 	int retries = MLX5_VDPA_INTR_RETRIES;
 	int ret = -EAGAIN;
 
-	if (!rte_intr_fd_get(priv->err_intr_handle))
+	if (rte_intr_fd_get(priv->err_intr_handle) < 0)
 		return;
 	while (retries-- && ret == -EAGAIN) {
 		ret = rte_intr_callback_unregister(priv->err_intr_handle,
@@ -487,6 +487,7 @@ mlx5_vdpa_err_event_unset(struct mlx5_vdpa_priv *priv)
 			rte_pause();
 		}
 	}
+	rte_intr_fd_set(priv->err_intr_handle, -1);
 	if (priv->err_chnl) {
 #ifdef HAVE_IBV_DEVX_EVENT
 		union {
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/5] eal/freebsd: fix a FD leak in the alarm subsystem
  2026-10-02  9:17 [PATCH 0/5] Interrupts API update David Marchand
  2026-10-02  9:17 ` [PATCH 1/5] vdpa/mlx5: fix check on err interrupt FD David Marchand
@ 2026-10-02  9:17 ` David Marchand
  2026-10-02  9:17 ` [PATCH 3/5] interrupts: mark file descriptors invalid on allocation David Marchand
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: David Marchand @ 2026-10-02  9:17 UTC (permalink / raw)
  To: dev, hkalra; +Cc: stable, Bruce Richardson, Anatoly Burakov

Caught by code review, while looking at rte_intr_instance_free() users.

Even if the FD is not used, it must still be closed when uninitialising
the alarm subsystem.

Fixes: 26021a715067 ("eal/bsd: support alarm API")
Cc: stable@dpdk.org

Signed-off-by: David Marchand <david.marchand@redhat.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
---
Changes since RFC v3:
- fixed compilation,

---
 lib/eal/freebsd/eal_alarm.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/lib/eal/freebsd/eal_alarm.c b/lib/eal/freebsd/eal_alarm.c
index c03e281e67..8544fe8d03 100644
--- a/lib/eal/freebsd/eal_alarm.c
+++ b/lib/eal/freebsd/eal_alarm.c
@@ -10,6 +10,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
+#include <unistd.h>
 #include <errno.h>
 
 #include <eal_export.h>
@@ -54,6 +55,8 @@ rte_eal_alarm_cleanup(void)
 	int ret = rte_intr_callback_unregister_sync(intr_handle,
 			eal_alarm_callback, (void *)-1);
 	if (ret >= 0) {
+		close(rte_intr_fd_get(intr_handle));
+		rte_intr_fd_set(intr_handle, -1);
 		rte_intr_instance_free(intr_handle);
 		intr_handle = NULL;
 	}
@@ -62,7 +65,7 @@ rte_eal_alarm_cleanup(void)
 int
 rte_eal_alarm_init(void)
 {
-	int fd;
+	int fd = -1;
 
 	intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
 	if (intr_handle == NULL) {
@@ -88,6 +91,8 @@ rte_eal_alarm_init(void)
 
 	return 0;
 error:
+	if (fd >= 0)
+		close(fd);
 	rte_intr_instance_free(intr_handle);
 	return -1;
 }
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/5] interrupts: mark file descriptors invalid on allocation
  2026-10-02  9:17 [PATCH 0/5] Interrupts API update David Marchand
  2026-10-02  9:17 ` [PATCH 1/5] vdpa/mlx5: fix check on err interrupt FD David Marchand
  2026-10-02  9:17 ` [PATCH 2/5] eal/freebsd: fix a FD leak in the alarm subsystem David Marchand
@ 2026-10-02  9:17 ` David Marchand
  2026-10-02  9:17 ` [PATCH 4/5] interrupts: close interrupt FDs David Marchand
  2026-10-02  9:17 ` [PATCH 5/5] interrupts: warn on leaked file descriptors David Marchand
  4 siblings, 0 replies; 6+ messages in thread
From: David Marchand @ 2026-10-02  9:17 UTC (permalink / raw)
  To: dev, hkalra
  Cc: Nipun Gupta, Nikhil Agarwal, Chenbo Xia, Anatoly Burakov, Wei Hu,
	Gaetan Rivet, Jakub Grajciar, Matan Azrad, Viacheslav Ovsiienko,
	Stephen Hemminger, Bruce Richardson

Initialize fd, dev_fd, and all efds array elements as invalid in
rte_intr_instance_alloc().
This follows Unix convention, avoiding confusion with fd 0
which could result from zero-initialization.

Update the API documentation to reflect this behavior and add a
unit test check to verify the fd is invalid after allocation.

Update the FreeBSD PCI implementation that checked against a 0 FD.

Add a hack in the unit test to let the UIO simulated tests pass.

Remove now-redundant initialization calls that were setting the fd
invalid right after allocation.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since RFC v4:
- allowed use of internal API in unit test,

Changes since RFC v1:
- squashed FreeBSD PCI update,
- updated unit test,

---
 app/test/test_interrupts.c             | 14 ++++++++++++--
 doc/guides/rel_notes/release_26_11.rst |  5 +++++
 drivers/bus/cdx/cdx_vfio.c             |  6 ------
 drivers/bus/pci/bsd/pci.c              |  2 +-
 drivers/bus/pci/linux/pci_vfio.c       | 11 -----------
 drivers/bus/pci/pci_common_uio.c       |  6 ------
 drivers/bus/vmbus/vmbus_common_uio.c   |  6 ------
 drivers/net/failsafe/failsafe.c        |  3 ---
 drivers/net/mana/mana.c                |  4 ----
 drivers/net/memif/rte_eth_memif.c      |  6 ------
 drivers/net/mlx4/mlx4.c                |  3 ---
 drivers/net/tap/rte_eth_tap.c          |  1 -
 lib/eal/common/eal_common_interrupts.c |  6 ++++++
 lib/eal/freebsd/eal_alarm.c            |  3 ---
 lib/eal/include/rte_interrupts.h       |  2 ++
 lib/eal/linux/eal_dev.c                |  4 ----
 16 files changed, 26 insertions(+), 56 deletions(-)

diff --git a/app/test/test_interrupts.c b/app/test/test_interrupts.c
index 3a5be92cd7..74c725d916 100644
--- a/app/test/test_interrupts.c
+++ b/app/test/test_interrupts.c
@@ -2,6 +2,8 @@
  * Copyright(c) 2010-2014 Intel Corporation
  */
 
+#define ALLOW_INTERNAL_API
+
 #include <stdio.h>
 #include <stdint.h>
 #include <unistd.h>
@@ -73,11 +75,13 @@ test_interrupt_init(void)
 			rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
 		if (!intr_handles[i])
 			return -1;
+		if (rte_intr_fd_get(intr_handles[i]) != -1)
+			return -1;
+		if (rte_intr_dev_fd_get(intr_handles[i]) != -1)
+			return -1;
 	}
 
 	test_intr_handle = intr_handles[TEST_INTERRUPT_HANDLE_INVALID];
-	if (rte_intr_fd_set(test_intr_handle, -1))
-		return -1;
 	if (rte_intr_type_set(test_intr_handle, RTE_INTR_HANDLE_UNKNOWN))
 		return -1;
 
@@ -90,6 +94,9 @@ test_interrupt_init(void)
 	test_intr_handle = intr_handles[TEST_INTERRUPT_HANDLE_VALID_UIO];
 	if (rte_intr_fd_set(test_intr_handle, pfds.readfd))
 		return -1;
+	/* HACK: UIO type does not require a device FD, but a valid handle should contain one */
+	if (rte_intr_dev_fd_set(test_intr_handle, INT_MAX))
+		return -1;
 	if (rte_intr_type_set(test_intr_handle, RTE_INTR_HANDLE_UIO))
 		return -1;
 
@@ -108,6 +115,9 @@ test_interrupt_init(void)
 	test_intr_handle = intr_handles[TEST_INTERRUPT_HANDLE_CASE1];
 	if (rte_intr_fd_set(test_intr_handle, pfds.writefd))
 		return -1;
+	/* HACK: UIO type does not require a device FD, but a valid handle should contain one */
+	if (rte_intr_dev_fd_set(test_intr_handle, INT_MAX))
+		return -1;
 	if (rte_intr_type_set(test_intr_handle, RTE_INTR_HANDLE_UIO))
 		return -1;
 
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index 030bd84cea..780899edc3 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -213,6 +213,11 @@ API Changes
    Also, make sure to start the actual text at the margin.
    =======================================================
 
+* interrupts: Updated interrupt handle file descriptor management.
+
+  * ``rte_intr_instance_alloc()`` now initializes file descriptors
+    to invalid values instead of zero.
+
 * ethdev: Updated VMDq related API.
 
   * At port configuration time, the number of VMDq pools advertised by a driver
diff --git a/drivers/bus/cdx/cdx_vfio.c b/drivers/bus/cdx/cdx_vfio.c
index 11fe3265d2..0e4af4e940 100644
--- a/drivers/bus/cdx/cdx_vfio.c
+++ b/drivers/bus/cdx/cdx_vfio.c
@@ -397,9 +397,6 @@ cdx_vfio_map_resource_primary(struct rte_cdx_device *dev)
 	struct cdx_map *maps;
 	int vfio_dev_fd, i, ret;
 
-	if (rte_intr_fd_set(dev->intr_handle, -1))
-		return -1;
-
 	ret = rte_vfio_setup_device(RTE_CDX_BUS_DEVICES_PATH, dev_name,
 				    &vfio_dev_fd, &device_info);
 	if (ret)
@@ -493,9 +490,6 @@ cdx_vfio_map_resource_secondary(struct rte_cdx_device *dev)
 	const char *dev_name = dev->device.name;
 	struct cdx_map *maps;
 
-	if (rte_intr_fd_set(dev->intr_handle, -1))
-		return -1;
-
 	/* if we're in a secondary process, just find our tailq entry */
 	TAILQ_FOREACH(vfio_res, vfio_res_list, next) {
 		if (strcmp(vfio_res->name, dev_name))
diff --git a/drivers/bus/pci/bsd/pci.c b/drivers/bus/pci/bsd/pci.c
index c6df31d486..bbe08605af 100644
--- a/drivers/bus/pci/bsd/pci.c
+++ b/drivers/bus/pci/bsd/pci.c
@@ -93,7 +93,7 @@ pci_uio_free_resource(struct rte_pci_device *dev,
 {
 	rte_free(uio_res);
 
-	if (rte_intr_fd_get(dev->intr_handle)) {
+	if (rte_intr_fd_get(dev->intr_handle) >= 0) {
 		close(rte_intr_fd_get(dev->intr_handle));
 		rte_intr_fd_set(dev->intr_handle, -1);
 		rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
diff --git a/drivers/bus/pci/linux/pci_vfio.c b/drivers/bus/pci/linux/pci_vfio.c
index bc5c5c2499..a92a0d86ec 100644
--- a/drivers/bus/pci/linux/pci_vfio.c
+++ b/drivers/bus/pci/linux/pci_vfio.c
@@ -742,12 +742,6 @@ pci_vfio_map_resource_primary(struct rte_pci_device *dev)
 
 	struct pci_map *maps;
 
-	if (rte_intr_fd_set(dev->intr_handle, -1))
-		return -1;
-
-	if (rte_intr_fd_set(dev->vfio_req_intr_handle, -1))
-		return -1;
-
 	/* store PCI address string */
 	snprintf(pci_addr, sizeof(pci_addr), PCI_PRI_FMT,
 			loc->domain, loc->bus, loc->devid, loc->function);
@@ -939,11 +933,6 @@ pci_vfio_map_resource_secondary(struct rte_pci_device *dev)
 
 	struct pci_map *maps;
 
-	if (rte_intr_fd_set(dev->intr_handle, -1))
-		return -1;
-	if (rte_intr_fd_set(dev->vfio_req_intr_handle, -1))
-		return -1;
-
 	/* store PCI address string */
 	snprintf(pci_addr, sizeof(pci_addr), PCI_PRI_FMT,
 			loc->domain, loc->bus, loc->devid, loc->function);
diff --git a/drivers/bus/pci/pci_common_uio.c b/drivers/bus/pci/pci_common_uio.c
index 71974e9f56..47d50721dc 100644
--- a/drivers/bus/pci/pci_common_uio.c
+++ b/drivers/bus/pci/pci_common_uio.c
@@ -101,12 +101,6 @@ pci_uio_map_resource(struct rte_pci_device *dev)
 	struct mapped_pci_res_list *uio_res_list =
 		RTE_TAILQ_CAST(rte_uio_tailq.head, mapped_pci_res_list);
 
-	if (rte_intr_fd_set(dev->intr_handle, -1))
-		return -1;
-
-	if (rte_intr_dev_fd_set(dev->intr_handle, -1))
-		return -1;
-
 	/* allocate uio resource */
 	ret = pci_uio_alloc_resource(dev, &uio_res);
 	if (ret)
diff --git a/drivers/bus/vmbus/vmbus_common_uio.c b/drivers/bus/vmbus/vmbus_common_uio.c
index c6e7e07302..7459f4ea7a 100644
--- a/drivers/bus/vmbus/vmbus_common_uio.c
+++ b/drivers/bus/vmbus/vmbus_common_uio.c
@@ -173,12 +173,6 @@ vmbus_uio_map_resource(struct rte_vmbus_device *dev)
 	int ret;
 
 	/* TODO: handle rescind */
-	if (rte_intr_fd_set(dev->intr_handle, -1))
-		return -1;
-
-	if (rte_intr_dev_fd_set(dev->intr_handle, -1))
-		return -1;
-
 	if (rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN))
 		return -1;
 
diff --git a/drivers/net/failsafe/failsafe.c b/drivers/net/failsafe/failsafe.c
index aa3fe53b22..3ba143924d 100644
--- a/drivers/net/failsafe/failsafe.c
+++ b/drivers/net/failsafe/failsafe.c
@@ -279,9 +279,6 @@ fs_eth_dev_create(struct rte_vdev_device *vdev)
 		goto cancel_alarm;
 	}
 
-	if (rte_intr_fd_set(PRIV(dev)->intr_handle, -1))
-		goto cancel_alarm;
-
 	if (rte_intr_type_set(PRIV(dev)->intr_handle, RTE_INTR_HANDLE_EXT))
 		goto cancel_alarm;
 
diff --git a/drivers/net/mana/mana.c b/drivers/net/mana/mana.c
index 72c8501d3c..ff1745edff 100644
--- a/drivers/net/mana/mana.c
+++ b/drivers/net/mana/mana.c
@@ -1946,10 +1946,6 @@ mana_intr_install(struct rte_eth_dev *eth_dev, struct mana_priv *priv)
 		return -ENOMEM;
 	}
 
-	ret = rte_intr_fd_set(priv->intr_handle, -1);
-	if (ret)
-		goto free_intr;
-
 	ret = mana_fd_set_non_blocking(ctx->async_fd);
 	if (ret) {
 		DRV_LOG(ERR, "Failed to change async_fd to NONBLOCK");
diff --git a/drivers/net/memif/rte_eth_memif.c b/drivers/net/memif/rte_eth_memif.c
index 1c8dc1d5ec..b41edbe8ff 100644
--- a/drivers/net/memif/rte_eth_memif.c
+++ b/drivers/net/memif/rte_eth_memif.c
@@ -1496,9 +1496,6 @@ memif_tx_queue_setup(struct rte_eth_dev *dev,
 	mq->n_pkts = 0;
 	mq->n_bytes = 0;
 
-	if (rte_intr_fd_set(mq->intr_handle, -1))
-		return -rte_errno;
-
 	if (rte_intr_type_set(mq->intr_handle, RTE_INTR_HANDLE_EXT))
 		return -rte_errno;
 
@@ -1536,9 +1533,6 @@ memif_rx_queue_setup(struct rte_eth_dev *dev,
 	mq->n_pkts = 0;
 	mq->n_bytes = 0;
 
-	if (rte_intr_fd_set(mq->intr_handle, -1))
-		return -rte_errno;
-
 	if (rte_intr_type_set(mq->intr_handle, RTE_INTR_HANDLE_EXT))
 		return -rte_errno;
 
diff --git a/drivers/net/mlx4/mlx4.c b/drivers/net/mlx4/mlx4.c
index 28f1116891..dbcd98d49f 100644
--- a/drivers/net/mlx4/mlx4.c
+++ b/drivers/net/mlx4/mlx4.c
@@ -1062,9 +1062,6 @@ mlx4_pci_probe(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev)
 			goto port_error;
 		}
 
-		if (rte_intr_fd_set(priv->intr_handle, -1))
-			goto port_error;
-
 		if (rte_intr_type_set(priv->intr_handle, RTE_INTR_HANDLE_EXT))
 			goto port_error;
 
diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index f23c23b465..aaa7f87af6 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -2214,7 +2214,6 @@ eth_dev_tap_create(struct rte_vdev_device *vdev, const char *tap_name,
 	dev->tx_pkt_burst = pmd_tx_burst;
 
 	rte_intr_type_set(pmd->intr_handle, RTE_INTR_HANDLE_EXT);
-	rte_intr_fd_set(pmd->intr_handle, -1);
 	dev->intr_handle = pmd->intr_handle;
 
 	/* Presetup the fds to -1 as being not valid */
diff --git a/lib/eal/common/eal_common_interrupts.c b/lib/eal/common/eal_common_interrupts.c
index a2a310750a..4e31a71319 100644
--- a/lib/eal/common/eal_common_interrupts.c
+++ b/lib/eal/common/eal_common_interrupts.c
@@ -68,6 +68,8 @@ struct rte_intr_handle *rte_intr_instance_alloc(uint32_t flags)
 		rte_errno = ENOMEM;
 		goto fail;
 	}
+	for (int i = 0; i < RTE_MAX_RXTX_INTR_VEC_ID; i++)
+		intr_handle->efds[i] = -1;
 
 	if (uses_rte_memory) {
 		intr_handle->elist = rte_zmalloc(NULL,
@@ -83,6 +85,8 @@ struct rte_intr_handle *rte_intr_instance_alloc(uint32_t flags)
 		goto fail;
 	}
 
+	intr_handle->fd = -1;
+	intr_handle->dev_fd = -1;
 	intr_handle->alloc_flags = flags;
 	intr_handle->nb_intr = RTE_MAX_RXTX_INTR_VEC_ID;
 
@@ -153,6 +157,8 @@ int rte_intr_event_list_update(struct rte_intr_handle *intr_handle, int size)
 		goto fail;
 	}
 	intr_handle->efds = tmp_efds;
+	for (int i = intr_handle->nb_intr; i < size; i++)
+		intr_handle->efds[i] = -1;
 
 	if (uses_rte_memory) {
 		tmp_elist = rte_realloc(intr_handle->elist,
diff --git a/lib/eal/freebsd/eal_alarm.c b/lib/eal/freebsd/eal_alarm.c
index 8544fe8d03..65d0af9aaa 100644
--- a/lib/eal/freebsd/eal_alarm.c
+++ b/lib/eal/freebsd/eal_alarm.c
@@ -76,9 +76,6 @@ rte_eal_alarm_init(void)
 	if (rte_intr_type_set(intr_handle, RTE_INTR_HANDLE_ALARM))
 		goto error;
 
-	if (rte_intr_fd_set(intr_handle, -1))
-		goto error;
-
 	/* on FreeBSD, timers don't use fd's, and their identifiers are stored
 	 * in separate namespace from fd's, so using any value is OK. however,
 	 * EAL interrupts handler expects fd's to be unique, so use an actual fd
diff --git a/lib/eal/include/rte_interrupts.h b/lib/eal/include/rte_interrupts.h
index 0ae16bbe19..1cfb9d3f8e 100644
--- a/lib/eal/include/rte_interrupts.h
+++ b/lib/eal/include/rte_interrupts.h
@@ -225,6 +225,8 @@ uint32_t rte_intr_active_events_flags(void);
  * can be realloced later based on size of MSIX interrupts supported by a PCI
  * device.
  *
+ * All file descriptors (fd, dev_fd, efds) are initialized to -1.
+ *
  * This function should be called from application or driver, before calling
  * any of the interrupt APIs.
  *
diff --git a/lib/eal/linux/eal_dev.c b/lib/eal/linux/eal_dev.c
index 92fbd04a6a..267b5a9493 100644
--- a/lib/eal/linux/eal_dev.c
+++ b/lib/eal/linux/eal_dev.c
@@ -360,10 +360,6 @@ rte_dev_event_monitor_start(void)
 	if (ret)
 		goto exit;
 
-	ret = rte_intr_fd_set(intr_handle, -1);
-	if (ret)
-		goto exit;
-
 	ret = dev_uev_socket_fd_create();
 	if (ret) {
 		EAL_LOG(ERR, "error create device event fd.");
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/5] interrupts: close interrupt FDs
  2026-10-02  9:17 [PATCH 0/5] Interrupts API update David Marchand
                   ` (2 preceding siblings ...)
  2026-10-02  9:17 ` [PATCH 3/5] interrupts: mark file descriptors invalid on allocation David Marchand
@ 2026-10-02  9:17 ` David Marchand
  2026-10-02  9:17 ` [PATCH 5/5] interrupts: warn on leaked file descriptors David Marchand
  4 siblings, 0 replies; 6+ messages in thread
From: David Marchand @ 2026-10-02  9:17 UTC (permalink / raw)
  To: dev, hkalra
  Cc: Nipun Gupta, Nikhil Agarwal, Chenbo Xia, Anatoly Burakov, Wei Hu,
	Nithin Dabilpuram, Kiran Kumar K, Sunil Kumar Kori, Satha Rao,
	Jakub Grajciar, Bruce Richardson

Add rte_intr_fd_close() and rte_intr_dev_fd_close() helpers to
encapsulate the common pattern of closing an interrupt handle's
file descriptor and resetting it to -1.

This simplifies code in multiple drivers that previously had to:
  if (rte_intr_fd_get(handle) >= 0) {
      close(rte_intr_fd_get(handle));
      rte_intr_fd_set(handle, -1);
  }

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since RFC v2:
- added unit test,
- converted bus/cdx and FreeBSD EAL interrupt code,
- added wrappers for common/cnxk,

---
 app/test/test_interrupts.c             | 72 ++++++++++++++++++++++++++
 doc/guides/rel_notes/release_26_11.rst |  2 +
 drivers/bus/cdx/cdx_vfio.c             |  6 +--
 drivers/bus/pci/bsd/pci.c              |  7 +--
 drivers/bus/pci/linux/pci_uio.c        | 15 ++----
 drivers/bus/pci/linux/pci_vfio.c       | 16 ++----
 drivers/bus/pci/pci_common_uio.c       | 12 +----
 drivers/bus/vmbus/linux/vmbus_uio.c    | 13 ++---
 drivers/bus/vmbus/vmbus_common_uio.c   | 11 +---
 drivers/common/cnxk/roc_platform.h     |  2 +
 drivers/net/memif/memif_socket.c       | 13 ++---
 lib/eal/common/eal_common_interrupts.c | 23 ++++++++
 lib/eal/freebsd/eal_alarm.c            |  3 +-
 lib/eal/include/rte_interrupts.h       | 27 ++++++++++
 lib/eal/linux/eal_dev.c                |  5 +-
 15 files changed, 147 insertions(+), 80 deletions(-)

diff --git a/app/test/test_interrupts.c b/app/test/test_interrupts.c
index 74c725d916..5ec2a725c5 100644
--- a/app/test/test_interrupts.c
+++ b/app/test/test_interrupts.c
@@ -171,6 +171,65 @@ test_interrupt_handle_compare(struct rte_intr_handle *intr_handle_l,
 	return 0;
 }
 
+/**
+ * Tests for rte_intr_fd_close() and rte_intr_dev_fd_close().
+ */
+static int
+test_interrupt_close(void)
+{
+	struct rte_intr_handle *intr_handle;
+	int pipefd[2];
+
+	/* check with null intr_handle */
+	rte_intr_fd_close(NULL);
+	rte_intr_dev_fd_close(NULL);
+
+	intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
+	if (!intr_handle)
+		return -1;
+
+	if (pipe(pipefd) < 0) {
+		rte_intr_instance_free(intr_handle);
+		return -1;
+	}
+
+	if (rte_intr_fd_set(intr_handle, pipefd[0]) < 0 ||
+			rte_intr_dev_fd_set(intr_handle, pipefd[1]) < 0) {
+		close(pipefd[0]);
+		close(pipefd[1]);
+		rte_intr_instance_free(intr_handle);
+		return -1;
+	}
+
+	/* check rte_intr_fd_close */
+	rte_intr_fd_close(intr_handle);
+
+	if (rte_intr_fd_get(intr_handle) != -1) {
+		printf("fd not set to -1 after close\n");
+		close(pipefd[1]);
+		rte_intr_instance_free(intr_handle);
+		return -1;
+	}
+
+	/* calling again should be a noop */
+	rte_intr_fd_close(intr_handle);
+
+	/* check rte_intr_dev_fd_close */
+	rte_intr_dev_fd_close(intr_handle);
+
+	if (rte_intr_dev_fd_get(intr_handle) != -1) {
+		printf("dev_fd not set to -1 after close\n");
+		rte_intr_instance_free(intr_handle);
+		return -1;
+	}
+
+	/* calling again should be a noop */
+	rte_intr_dev_fd_close(intr_handle);
+
+	rte_intr_instance_free(intr_handle);
+	return 0;
+}
+
 #else
 /* to be implemented for bsd later */
 static inline int
@@ -208,6 +267,12 @@ test_interrupt_handle_compare(struct rte_intr_handle *intr_handle_l,
 
 	return 0;
 }
+
+static int
+test_interrupt_close(void)
+{
+	return 0;
+}
 #endif /* RTE_EXEC_ENV_LINUX */
 
 /**
@@ -568,6 +633,13 @@ test_interrupt(void)
 	}
 	rte_delay_ms(TEST_INTERRUPT_CHECK_INTERVAL);
 
+	printf("start interrupt close test\n");
+	if (test_interrupt_close() < 0) {
+		printf("fail to check interrupt close\n");
+		goto out;
+	}
+	rte_delay_ms(TEST_INTERRUPT_CHECK_INTERVAL);
+
 	ret = 0;
 
 out:
diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index 780899edc3..bbe14e3a43 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -217,6 +217,8 @@ API Changes
 
   * ``rte_intr_instance_alloc()`` now initializes file descriptors
     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.
 
 * ethdev: Updated VMDq related API.
 
diff --git a/drivers/bus/cdx/cdx_vfio.c b/drivers/bus/cdx/cdx_vfio.c
index 0e4af4e940..8d67058bfe 100644
--- a/drivers/bus/cdx/cdx_vfio.c
+++ b/drivers/bus/cdx/cdx_vfio.c
@@ -110,11 +110,7 @@ cdx_vfio_unmap_resource_primary(struct rte_cdx_device *dev)
 			CDX_BUS_ERR("Error when disabling bus master for %s",
 				    dev->device.name);
 
-		if (close(rte_intr_fd_get(dev->intr_handle)) < 0) {
-			CDX_BUS_ERR("Error when closing eventfd file descriptor for %s",
-				dev->device.name);
-			return -1;
-		}
+		rte_intr_fd_close(dev->intr_handle);
 	}
 
 	vfio_dev_fd = rte_intr_dev_fd_get(dev->intr_handle);
diff --git a/drivers/bus/pci/bsd/pci.c b/drivers/bus/pci/bsd/pci.c
index bbe08605af..f1012bb91b 100644
--- a/drivers/bus/pci/bsd/pci.c
+++ b/drivers/bus/pci/bsd/pci.c
@@ -93,11 +93,8 @@ pci_uio_free_resource(struct rte_pci_device *dev,
 {
 	rte_free(uio_res);
 
-	if (rte_intr_fd_get(dev->intr_handle) >= 0) {
-		close(rte_intr_fd_get(dev->intr_handle));
-		rte_intr_fd_set(dev->intr_handle, -1);
-		rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
-	}
+	rte_intr_fd_close(dev->intr_handle);
+	rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
 }
 
 int
diff --git a/drivers/bus/pci/linux/pci_uio.c b/drivers/bus/pci/linux/pci_uio.c
index 40e96b1c67..0983474994 100644
--- a/drivers/bus/pci/linux/pci_uio.c
+++ b/drivers/bus/pci/linux/pci_uio.c
@@ -197,20 +197,11 @@ void
 pci_uio_free_resource(struct rte_pci_device *dev,
 		struct mapped_pci_resource *uio_res)
 {
-	int uio_cfg_fd = rte_intr_dev_fd_get(dev->intr_handle);
-
 	rte_free(uio_res);
 
-	if (uio_cfg_fd >= 0) {
-		close(uio_cfg_fd);
-		rte_intr_dev_fd_set(dev->intr_handle, -1);
-	}
-
-	if (rte_intr_fd_get(dev->intr_handle) >= 0) {
-		close(rte_intr_fd_get(dev->intr_handle));
-		rte_intr_fd_set(dev->intr_handle, -1);
-		rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
-	}
+	rte_intr_dev_fd_close(dev->intr_handle);
+	rte_intr_fd_close(dev->intr_handle);
+	rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
 }
 
 int
diff --git a/drivers/bus/pci/linux/pci_vfio.c b/drivers/bus/pci/linux/pci_vfio.c
index a92a0d86ec..84c338c68e 100644
--- a/drivers/bus/pci/linux/pci_vfio.c
+++ b/drivers/bus/pci/linux/pci_vfio.c
@@ -354,9 +354,7 @@ pci_vfio_enable_notifier(struct rte_pci_device *dev, int vfio_dev_fd)
 
 	return 0;
 error:
-	close(fd);
-
-	rte_intr_fd_set(dev->vfio_req_intr_handle, -1);
+	rte_intr_fd_close(dev->vfio_req_intr_handle);
 	rte_intr_type_set(dev->vfio_req_intr_handle, RTE_INTR_HANDLE_UNKNOWN);
 	rte_intr_dev_fd_set(dev->vfio_req_intr_handle, -1);
 
@@ -383,9 +381,7 @@ pci_vfio_disable_notifier(struct rte_pci_device *dev)
 		return -1;
 	}
 
-	close(rte_intr_fd_get(dev->vfio_req_intr_handle));
-
-	rte_intr_fd_set(dev->vfio_req_intr_handle, -1);
+	rte_intr_fd_close(dev->vfio_req_intr_handle);
 	rte_intr_type_set(dev->vfio_req_intr_handle, RTE_INTR_HANDLE_UNKNOWN);
 	rte_intr_dev_fd_set(dev->vfio_req_intr_handle, -1);
 
@@ -1073,13 +1069,7 @@ pci_vfio_unmap_resource_primary(struct rte_pci_device *dev)
 		return -1;
 	}
 
-	if (rte_intr_fd_get(dev->intr_handle) < 0)
-		return -1;
-
-	if (close(rte_intr_fd_get(dev->intr_handle)) < 0) {
-		PCI_LOG(INFO, "Error when closing eventfd file descriptor for %s", pci_addr);
-		return -1;
-	}
+	rte_intr_fd_close(dev->intr_handle);
 
 	vfio_dev_fd = rte_intr_dev_fd_get(dev->intr_handle);
 	if (vfio_dev_fd < 0)
diff --git a/drivers/bus/pci/pci_common_uio.c b/drivers/bus/pci/pci_common_uio.c
index 47d50721dc..52a10f806a 100644
--- a/drivers/bus/pci/pci_common_uio.c
+++ b/drivers/bus/pci/pci_common_uio.c
@@ -211,7 +211,6 @@ pci_uio_unmap_resource(struct rte_pci_device *dev)
 	struct mapped_pci_resource *uio_res;
 	struct mapped_pci_res_list *uio_res_list =
 			RTE_TAILQ_CAST(rte_uio_tailq.head, mapped_pci_res_list);
-	int uio_cfg_fd;
 
 	if (dev == NULL)
 		return;
@@ -222,15 +221,8 @@ pci_uio_unmap_resource(struct rte_pci_device *dev)
 		return;
 
 	/* close fd */
-	if (rte_intr_fd_get(dev->intr_handle) >= 0)
-		close(rte_intr_fd_get(dev->intr_handle));
-	uio_cfg_fd = rte_intr_dev_fd_get(dev->intr_handle);
-	if (uio_cfg_fd >= 0) {
-		close(uio_cfg_fd);
-		rte_intr_dev_fd_set(dev->intr_handle, -1);
-	}
-
-	rte_intr_fd_set(dev->intr_handle, -1);
+	rte_intr_fd_close(dev->intr_handle);
+	rte_intr_dev_fd_close(dev->intr_handle);
 	rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
 
 	/* secondary processes - just free maps */
diff --git a/drivers/bus/vmbus/linux/vmbus_uio.c b/drivers/bus/vmbus/linux/vmbus_uio.c
index fbafc5027d..c52d6738a5 100644
--- a/drivers/bus/vmbus/linux/vmbus_uio.c
+++ b/drivers/bus/vmbus/linux/vmbus_uio.c
@@ -67,16 +67,9 @@ vmbus_uio_free_resource(struct rte_vmbus_device *dev,
 {
 	rte_free(uio_res);
 
-	if (rte_intr_dev_fd_get(dev->intr_handle) >= 0) {
-		close(rte_intr_dev_fd_get(dev->intr_handle));
-		rte_intr_dev_fd_set(dev->intr_handle, -1);
-	}
-
-	if (rte_intr_fd_get(dev->intr_handle) >= 0) {
-		close(rte_intr_fd_get(dev->intr_handle));
-		rte_intr_fd_set(dev->intr_handle, -1);
-		rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
-	}
+	rte_intr_dev_fd_close(dev->intr_handle);
+	rte_intr_fd_close(dev->intr_handle);
+	rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
 }
 
 int
diff --git a/drivers/bus/vmbus/vmbus_common_uio.c b/drivers/bus/vmbus/vmbus_common_uio.c
index 7459f4ea7a..9765f5f609 100644
--- a/drivers/bus/vmbus/vmbus_common_uio.c
+++ b/drivers/bus/vmbus/vmbus_common_uio.c
@@ -254,14 +254,7 @@ vmbus_uio_unmap_resource(struct rte_vmbus_device *dev)
 	rte_free(uio_res);
 
 	/* close fd */
-	if (rte_intr_fd_get(dev->intr_handle) >= 0)
-		close(rte_intr_fd_get(dev->intr_handle));
-
-	if (rte_intr_dev_fd_get(dev->intr_handle) >= 0) {
-		close(rte_intr_dev_fd_get(dev->intr_handle));
-		rte_intr_dev_fd_set(dev->intr_handle, -1);
-	}
-
-	rte_intr_fd_set(dev->intr_handle, -1);
+	rte_intr_fd_close(dev->intr_handle);
+	rte_intr_dev_fd_close(dev->intr_handle);
 	rte_intr_type_set(dev->intr_handle, RTE_INTR_HANDLE_UNKNOWN);
 }
diff --git a/drivers/common/cnxk/roc_platform.h b/drivers/common/cnxk/roc_platform.h
index ac4f76473f..aba9782023 100644
--- a/drivers/common/cnxk/roc_platform.h
+++ b/drivers/common/cnxk/roc_platform.h
@@ -171,8 +171,10 @@ plt_thread_is_valid(plt_thread_t thr)
 #define plt_intr_vec_list_free		rte_intr_vec_list_free
 #define plt_intr_fd_set			rte_intr_fd_set
 #define plt_intr_fd_get			rte_intr_fd_get
+#define plt_intr_fd_close		rte_intr_fd_close
 #define plt_intr_dev_fd_get		rte_intr_dev_fd_get
 #define plt_intr_dev_fd_set		rte_intr_dev_fd_set
+#define plt_intr_dev_fd_close		rte_intr_dev_fd_close
 #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
diff --git a/drivers/net/memif/memif_socket.c b/drivers/net/memif/memif_socket.c
index 898ad75fa6..71139f2313 100644
--- a/drivers/net/memif/memif_socket.c
+++ b/drivers/net/memif/memif_socket.c
@@ -510,8 +510,7 @@ memif_intr_unregister_handler(struct rte_intr_handle *intr_handle, void *arg)
 	struct memif_control_channel *cc = arg;
 
 	/* close control channel fd */
-	if (rte_intr_fd_get(intr_handle) >= 0)
-		close(rte_intr_fd_get(intr_handle));
+	rte_intr_fd_close(intr_handle);
 	/* clear message queue */
 	while ((elt = TAILQ_FIRST(&cc->msg_queue)) != NULL) {
 		TAILQ_REMOVE(&cc->msg_queue, elt, next);
@@ -596,10 +595,7 @@ memif_disconnect(struct rte_eth_dev *dev)
 				continue;
 		}
 
-		if (rte_intr_fd_get(mq->intr_handle) > 0) {
-			close(rte_intr_fd_get(mq->intr_handle));
-			rte_intr_fd_set(mq->intr_handle, -1);
-		}
+		rte_intr_fd_close(mq->intr_handle);
 	}
 	for (i = 0; i < pmd->cfg.num_s2c_rings; i++) {
 		if (pmd->role == MEMIF_ROLE_SERVER) {
@@ -614,10 +610,7 @@ memif_disconnect(struct rte_eth_dev *dev)
 				continue;
 		}
 
-		if (rte_intr_fd_get(mq->intr_handle) > 0) {
-			close(rte_intr_fd_get(mq->intr_handle));
-			rte_intr_fd_set(mq->intr_handle, -1);
-		}
+		rte_intr_fd_close(mq->intr_handle);
 	}
 
 	memif_free_regions(dev);
diff --git a/lib/eal/common/eal_common_interrupts.c b/lib/eal/common/eal_common_interrupts.c
index 4e31a71319..8176eb089a 100644
--- a/lib/eal/common/eal_common_interrupts.c
+++ b/lib/eal/common/eal_common_interrupts.c
@@ -4,6 +4,7 @@
 
 #include <stdlib.h>
 #include <string.h>
+#include <unistd.h>
 
 #include <rte_errno.h>
 #include <rte_interrupts.h>
@@ -219,6 +220,17 @@ int rte_intr_fd_get(const struct rte_intr_handle *intr_handle)
 	return -1;
 }
 
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_intr_fd_close, 26.11)
+void rte_intr_fd_close(struct rte_intr_handle *intr_handle)
+{
+	int fd = rte_intr_fd_get(intr_handle);
+
+	if (fd >= 0) {
+		close(fd);
+		rte_intr_fd_set(intr_handle, -1);
+	}
+}
+
 RTE_EXPORT_SYMBOL(rte_intr_type_set)
 int rte_intr_type_set(struct rte_intr_handle *intr_handle,
 	enum rte_intr_handle_type type)
@@ -265,6 +277,17 @@ int rte_intr_dev_fd_get(const struct rte_intr_handle *intr_handle)
 	return -1;
 }
 
+RTE_EXPORT_INTERNAL_SYMBOL(rte_intr_dev_fd_close)
+void rte_intr_dev_fd_close(struct rte_intr_handle *intr_handle)
+{
+	int fd = rte_intr_dev_fd_get(intr_handle);
+
+	if (fd >= 0) {
+		close(fd);
+		rte_intr_dev_fd_set(intr_handle, -1);
+	}
+}
+
 RTE_EXPORT_INTERNAL_SYMBOL(rte_intr_max_intr_set)
 int rte_intr_max_intr_set(struct rte_intr_handle *intr_handle,
 				 int max_intr)
diff --git a/lib/eal/freebsd/eal_alarm.c b/lib/eal/freebsd/eal_alarm.c
index 65d0af9aaa..ee4a961f9f 100644
--- a/lib/eal/freebsd/eal_alarm.c
+++ b/lib/eal/freebsd/eal_alarm.c
@@ -55,8 +55,7 @@ rte_eal_alarm_cleanup(void)
 	int ret = rte_intr_callback_unregister_sync(intr_handle,
 			eal_alarm_callback, (void *)-1);
 	if (ret >= 0) {
-		close(rte_intr_fd_get(intr_handle));
-		rte_intr_fd_set(intr_handle, -1);
+		rte_intr_fd_close(intr_handle);
 		rte_intr_instance_free(intr_handle);
 		intr_handle = NULL;
 	}
diff --git a/lib/eal/include/rte_interrupts.h b/lib/eal/include/rte_interrupts.h
index 1cfb9d3f8e..d04c0c7b15 100644
--- a/lib/eal/include/rte_interrupts.h
+++ b/lib/eal/include/rte_interrupts.h
@@ -279,6 +279,19 @@ rte_intr_fd_set(struct rte_intr_handle *intr_handle, int fd);
 int
 rte_intr_fd_get(const struct rte_intr_handle *intr_handle);
 
+/**
+ * Close the FD of the given interrupt handle instance,
+ * and set FD to -1.
+ *
+ * If FD is not valid (< 0), nothing is done.
+ *
+ * @param intr_handle
+ *  pointer to the interrupt handle.
+ */
+__rte_experimental
+void
+rte_intr_fd_close(struct rte_intr_handle *intr_handle);
+
 /**
  * Set the type field of interrupt handle with user provided
  * interrupt type.
@@ -467,6 +480,20 @@ __rte_internal
 int
 rte_intr_dev_fd_get(const struct rte_intr_handle *intr_handle);
 
+/**
+ * @internal
+ * Close the device FD of the given interrupt handle instance,
+ * and set device FD to -1.
+ *
+ * If device FD is not valid (< 0), nothing is done.
+ *
+ * @param intr_handle
+ *  pointer to the interrupt handle.
+ */
+__rte_internal
+void
+rte_intr_dev_fd_close(struct rte_intr_handle *intr_handle);
+
 /**
  * @internal
  * Set the max intr field of interrupt handle with user
diff --git a/lib/eal/linux/eal_dev.c b/lib/eal/linux/eal_dev.c
index 267b5a9493..35223b6b26 100644
--- a/lib/eal/linux/eal_dev.c
+++ b/lib/eal/linux/eal_dev.c
@@ -242,10 +242,7 @@ static void
 dev_delayed_unregister(void *param)
 {
 	rte_intr_callback_unregister(intr_handle, dev_uev_handler, param);
-	if (rte_intr_fd_get(intr_handle) >= 0) {
-		close(rte_intr_fd_get(intr_handle));
-		rte_intr_fd_set(intr_handle, -1);
-	}
+	rte_intr_fd_close(intr_handle);
 }
 
 static void
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 5/5] interrupts: warn on leaked file descriptors
  2026-10-02  9:17 [PATCH 0/5] Interrupts API update David Marchand
                   ` (3 preceding siblings ...)
  2026-10-02  9:17 ` [PATCH 4/5] interrupts: close interrupt FDs David Marchand
@ 2026-10-02  9:17 ` David Marchand
  4 siblings, 0 replies; 6+ messages in thread
From: David Marchand @ 2026-10-02  9:17 UTC (permalink / raw)
  To: dev, hkalra
  Cc: Nipun Gupta, Nikhil Agarwal, Anatoly Burakov, Chenbo Xia,
	Dariusz Sosnowski, Viacheslav Ovsiienko, Bing Zhao, Ori Kam,
	Suanming Mou, Matan Azrad, Wei Hu, Jie Liu, Stephen Hemminger,
	Maxime Coquelin, Jakub Palider, Tomasz Duszynski

Let's warn on interrupt handle that still reference what looks like a
valid file descriptor.

This will likely raise false positives, but it should help limiting FD
leaks on the long term.

Add missing reset to -1 in drivers, and document code.

Signed-off-by: David Marchand <david.marchand@redhat.com>
---
Changes since RFC v4:
- updated comment in net/tap,

Changes since RFC v2:
- split code comments added by patch 3 in the RFC v2,
- added a check on leftover FD in a interrupt handle,

---
 app/test/test_interrupts.c                       | 5 ++++-
 drivers/bus/cdx/cdx_vfio.c                       | 4 ++++
 drivers/bus/pci/linux/pci_vfio.c                 | 2 ++
 drivers/common/mlx5/linux/mlx5_common_os.c       | 6 +++++-
 drivers/net/mana/mana.c                          | 1 +
 drivers/net/mlx4/mlx4_intr.c                     | 1 +
 drivers/net/sxe2/sxe2_irq.c                      | 5 ++++-
 drivers/net/tap/rte_eth_tap.c                    | 1 +
 drivers/net/virtio/virtio_user/virtio_user_dev.c | 2 ++
 drivers/raw/cnxk_gpio/cnxk_gpio.c                | 3 +++
 drivers/vdpa/mlx5/mlx5_vdpa_event.c              | 1 +
 drivers/vdpa/mlx5/mlx5_vdpa_virtq.c              | 1 +
 lib/eal/common/eal_common_interrupts.c           | 6 ++++++
 13 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/app/test/test_interrupts.c b/app/test/test_interrupts.c
index 5ec2a725c5..2d63a2fd98 100644
--- a/app/test/test_interrupts.c
+++ b/app/test/test_interrupts.c
@@ -132,8 +132,11 @@ test_interrupt_deinit(void)
 {
 	int i;
 
-	for (i = 0; i < TEST_INTERRUPT_HANDLE_MAX; i++)
+	for (i = 0; i < TEST_INTERRUPT_HANDLE_MAX; i++) {
+		rte_intr_fd_set(intr_handles[i], -1);
+		rte_intr_dev_fd_set(intr_handles[i], -1);
 		rte_intr_instance_free(intr_handles[i]);
+	}
 	close(pfds.pipefd[0]);
 	close(pfds.pipefd[1]);
 
diff --git a/drivers/bus/cdx/cdx_vfio.c b/drivers/bus/cdx/cdx_vfio.c
index 8d67058bfe..0c1b263a85 100644
--- a/drivers/bus/cdx/cdx_vfio.c
+++ b/drivers/bus/cdx/cdx_vfio.c
@@ -116,6 +116,8 @@ cdx_vfio_unmap_resource_primary(struct rte_cdx_device *dev)
 	vfio_dev_fd = rte_intr_dev_fd_get(dev->intr_handle);
 	if (vfio_dev_fd < 0)
 		return -1;
+	/* vfio_dev_fd is owned by VFIO, only clear reference here. */
+	rte_intr_dev_fd_set(dev->intr_handle, -1);
 
 	ret = rte_vfio_release_device(RTE_CDX_BUS_DEVICES_PATH, dev->device.name,
 				      vfio_dev_fd);
@@ -150,6 +152,8 @@ cdx_vfio_unmap_resource_secondary(struct rte_cdx_device *dev)
 	vfio_dev_fd = rte_intr_dev_fd_get(dev->intr_handle);
 	if (vfio_dev_fd < 0)
 		return -1;
+	/* vfio_dev_fd is owned by VFIO, only clear reference here. */
+	rte_intr_dev_fd_set(dev->intr_handle, -1);
 
 	ret = rte_vfio_release_device(RTE_CDX_BUS_DEVICES_PATH, dev->device.name,
 				      vfio_dev_fd);
diff --git a/drivers/bus/pci/linux/pci_vfio.c b/drivers/bus/pci/linux/pci_vfio.c
index 84c338c68e..b5f05e9f9f 100644
--- a/drivers/bus/pci/linux/pci_vfio.c
+++ b/drivers/bus/pci/linux/pci_vfio.c
@@ -356,6 +356,7 @@ pci_vfio_enable_notifier(struct rte_pci_device *dev, int vfio_dev_fd)
 error:
 	rte_intr_fd_close(dev->vfio_req_intr_handle);
 	rte_intr_type_set(dev->vfio_req_intr_handle, RTE_INTR_HANDLE_UNKNOWN);
+	/* vfio_dev_fd is managed by VFIO layer, only clear reference here. */
 	rte_intr_dev_fd_set(dev->vfio_req_intr_handle, -1);
 
 	return -1;
@@ -383,6 +384,7 @@ pci_vfio_disable_notifier(struct rte_pci_device *dev)
 
 	rte_intr_fd_close(dev->vfio_req_intr_handle);
 	rte_intr_type_set(dev->vfio_req_intr_handle, RTE_INTR_HANDLE_UNKNOWN);
+	/* vfio_dev_fd is managed by VFIO layer, only clear reference here. */
 	rte_intr_dev_fd_set(dev->vfio_req_intr_handle, -1);
 
 	return 0;
diff --git a/drivers/common/mlx5/linux/mlx5_common_os.c b/drivers/common/mlx5/linux/mlx5_common_os.c
index 11a4eb8eee..903963670d 100644
--- a/drivers/common/mlx5/linux/mlx5_common_os.c
+++ b/drivers/common/mlx5/linux/mlx5_common_os.c
@@ -1108,6 +1108,7 @@ mlx5_os_interrupt_handler_create(int mode, bool set_fd_nonblock, int fd,
 	}
 	return tmp_intr_handle;
 err:
+	rte_intr_fd_set(tmp_intr_handle, -1);
 	rte_intr_instance_free(tmp_intr_handle);
 	return NULL;
 }
@@ -1181,8 +1182,11 @@ void
 mlx5_os_interrupt_handler_destroy(struct rte_intr_handle *intr_handle,
 				  rte_intr_callback_fn cb, void *cb_arg)
 {
-	if (rte_intr_fd_get(intr_handle) >= 0)
+	if (rte_intr_fd_get(intr_handle) >= 0) {
 		mlx5_intr_callback_unregister(intr_handle, cb, cb_arg);
+		/* fd is not owned by the driver, only clear reference here. */
+		rte_intr_fd_set(intr_handle, -1);
+	}
 	rte_intr_instance_free(intr_handle);
 }
 
diff --git a/drivers/net/mana/mana.c b/drivers/net/mana/mana.c
index ff1745edff..30b0ce90d6 100644
--- a/drivers/net/mana/mana.c
+++ b/drivers/net/mana/mana.c
@@ -1964,6 +1964,7 @@ mana_intr_install(struct rte_eth_dev *eth_dev, struct mana_priv *priv)
 					 mana_intr_handler, priv);
 	if (ret) {
 		DRV_LOG(ERR, "Failed to register intr callback");
+		/* fd is owned by ibverbs, only clear reference here. */
 		rte_intr_fd_set(priv->intr_handle, -1);
 		goto free_intr;
 	}
diff --git a/drivers/net/mlx4/mlx4_intr.c b/drivers/net/mlx4/mlx4_intr.c
index 01057482ec..c278dd5ecc 100644
--- a/drivers/net/mlx4/mlx4_intr.c
+++ b/drivers/net/mlx4/mlx4_intr.c
@@ -265,6 +265,7 @@ mlx4_intr_uninstall(struct mlx4_priv *priv)
 					     (void (*)(void *))
 					     mlx4_interrupt_handler,
 					     priv);
+		/* fd is owned by ibverbs, only clear reference here. */
 		if (rte_intr_fd_set(priv->intr_handle, -1))
 			return -rte_errno;
 	}
diff --git a/drivers/net/sxe2/sxe2_irq.c b/drivers/net/sxe2/sxe2_irq.c
index 4c3a29900d..256949e74e 100644
--- a/drivers/net/sxe2/sxe2_irq.c
+++ b/drivers/net/sxe2/sxe2_irq.c
@@ -397,8 +397,11 @@ static void sxe2_intr_handler_destroy(struct rte_intr_handle *intr_handle,
 	if (!intr_handle)
 		return;
 
-	if (rte_intr_fd_get(intr_handle) >= 0)
+	if (rte_intr_fd_get(intr_handle) >= 0) {
 		(void)rte_intr_callback_unregister(intr_handle, cb, cb_arg);
+		/* fd is not owned by the driver, only clear reference here. */
+		rte_intr_fd_set(intr_handle, -1);
+	}
 	rte_intr_instance_free(intr_handle);
 }
 
diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index aaa7f87af6..de1716e423 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -1968,6 +1968,7 @@ tap_lsc_intr_handle_set(struct rte_eth_dev *dev, int set)
 
 	if (rte_intr_fd_get(pmd->intr_handle) >= 0) {
 		tap_nl_final(rte_intr_fd_get(pmd->intr_handle));
+		/* fd has been closed already, only clear reference here. */
 		rte_intr_fd_set(pmd->intr_handle, -1);
 	}
 
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index f3df73c1f0..f89c871beb 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -858,6 +858,8 @@ virtio_user_dev_uninit(struct virtio_user_dev *dev)
 {
 	struct rte_eth_dev *eth_dev = &rte_eth_devices[dev->hw.port_id];
 
+	/* fd is not owned by the driver, only clear reference here. */
+	rte_intr_fd_set(eth_dev->intr_handle, -1);
 	rte_intr_instance_free(eth_dev->intr_handle);
 	eth_dev->intr_handle = NULL;
 
diff --git a/drivers/raw/cnxk_gpio/cnxk_gpio.c b/drivers/raw/cnxk_gpio/cnxk_gpio.c
index e22fb7cf7b..8a5dda68dc 100644
--- a/drivers/raw/cnxk_gpio/cnxk_gpio.c
+++ b/drivers/raw/cnxk_gpio/cnxk_gpio.c
@@ -442,6 +442,8 @@ cnxk_gpio_unregister_irq(struct cnxk_gpio *gpio)
 	if (ret)
 		return ret;
 
+	/* fd is owned by gpio, only clear reference here. */
+	rte_intr_fd_set(gpio->intr.intr_handle, -1);
 	rte_intr_instance_free(gpio->intr.intr_handle);
 	gpio->intr.intr_handle = NULL;
 
@@ -635,6 +637,7 @@ cnxk_gpio_register_irq_compat(struct cnxk_gpio *gpio, struct cnxk_gpio_irq *irq,
 
 	return 0;
 out:
+	rte_intr_fd_set(intr_handle, -1);
 	rte_intr_instance_free(intr_handle);
 
 	return ret;
diff --git a/drivers/vdpa/mlx5/mlx5_vdpa_event.c b/drivers/vdpa/mlx5/mlx5_vdpa_event.c
index dee2ccc23d..d18cf7d7e5 100644
--- a/drivers/vdpa/mlx5/mlx5_vdpa_event.c
+++ b/drivers/vdpa/mlx5/mlx5_vdpa_event.c
@@ -487,6 +487,7 @@ mlx5_vdpa_err_event_unset(struct mlx5_vdpa_priv *priv)
 			rte_pause();
 		}
 	}
+	/* fd is owned by err_chnl, only clear reference here. */
 	rte_intr_fd_set(priv->err_intr_handle, -1);
 	if (priv->err_chnl) {
 #ifdef HAVE_IBV_DEVX_EVENT
diff --git a/drivers/vdpa/mlx5/mlx5_vdpa_virtq.c b/drivers/vdpa/mlx5/mlx5_vdpa_virtq.c
index 093cdd08d2..9cad7e1df4 100644
--- a/drivers/vdpa/mlx5/mlx5_vdpa_virtq.c
+++ b/drivers/vdpa/mlx5/mlx5_vdpa_virtq.c
@@ -96,6 +96,7 @@ mlx5_vdpa_virtq_unregister_intr_handle(struct mlx5_vdpa_virtq *virtq)
 				pthread_mutex_lock(&virtq->virtq_lock);
 			}
 		}
+		/* fd is owned by vhost, only clear reference here. */
 		(void)rte_intr_fd_set(virtq->intr_handle, -1);
 	}
 	rte_intr_instance_free(virtq->intr_handle);
diff --git a/lib/eal/common/eal_common_interrupts.c b/lib/eal/common/eal_common_interrupts.c
index 8176eb089a..6368ef1771 100644
--- a/lib/eal/common/eal_common_interrupts.c
+++ b/lib/eal/common/eal_common_interrupts.c
@@ -187,6 +187,12 @@ void rte_intr_instance_free(struct rte_intr_handle *intr_handle)
 {
 	if (intr_handle == NULL)
 		return;
+	if (rte_intr_fd_get(intr_handle) >= 0)
+		EAL_LOG(NOTICE, "Some interrupt handle is leaking a FD: %d",
+			rte_intr_fd_get(intr_handle));
+	if (rte_intr_dev_fd_get(intr_handle) >= 0)
+		EAL_LOG(NOTICE, "Some interrupt handle is leaking a device FD: %d",
+			rte_intr_dev_fd_get(intr_handle));
 	if (RTE_INTR_INSTANCE_USES_RTE_MEMORY(intr_handle->alloc_flags)) {
 		rte_free(intr_handle->efds);
 		rte_free(intr_handle->elist);
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-10-02  9:18 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-02  9:17 [PATCH 0/5] Interrupts API update David Marchand
2026-10-02  9:17 ` [PATCH 1/5] vdpa/mlx5: fix check on err interrupt FD David Marchand
2026-10-02  9:17 ` [PATCH 2/5] eal/freebsd: fix a FD leak in the alarm subsystem David Marchand
2026-10-02  9:17 ` [PATCH 3/5] interrupts: mark file descriptors invalid on allocation David Marchand
2026-10-02  9:17 ` [PATCH 4/5] interrupts: close interrupt FDs David Marchand
2026-10-02  9:17 ` [PATCH 5/5] interrupts: warn on leaked file descriptors David Marchand

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox