[PATCH 1/2] net/tap: fix FD leak on netns change
David Marchand
david.marchand at redhat.com
Tue Nov 3 18:17:29 CET 2026
When a tap interface moves to another namespace, tap_netns_change()
calls tap_lsc_intr_handle_set(dev, 1) to recreate the LSC interrupt
without first tearing down the old one. The previous LSC netlink
socket fd is never closed.
Defer the full LSC teardown and re-registration to an alarm callback
which runs in the interrupt thread but outside any active source
context. The alarm switches to the device's network namespace, tears
down the old LSC handle, sets up a new one (opening a fresh netlink
socket in the correct namespace), restores the original namespace,
and forces carrier state back up.
Extract tap_dev_ns_switch/tap_dev_ns_restore helpers to share the
namespace switch logic between tap_netns_change and the alarm.
Fixes: aae3a38e9e03 ("net/tap: detect namespace change")
Cc: stable at dpdk.org
Signed-off-by: David Marchand <david.marchand at redhat.com>
---
drivers/net/tap/rte_eth_tap.c | 100 ++++++++++++++++++++++------------
1 file changed, 65 insertions(+), 35 deletions(-)
diff --git a/drivers/net/tap/rte_eth_tap.c b/drivers/net/tap/rte_eth_tap.c
index 1d2b71675a..15320c4b7e 100644
--- a/drivers/net/tap/rte_eth_tap.c
+++ b/drivers/net/tap/rte_eth_tap.c
@@ -17,6 +17,7 @@
#include <rte_ip.h>
#include <rte_ethdev.h>
#include <rte_errno.h>
+#include <rte_alarm.h>
#include <rte_cycles.h>
#include <assert.h>
@@ -1796,41 +1797,57 @@ tap_mac_addr_remove(struct rte_eth_dev *dev __rte_unused,
static void tap_dev_intr_handler(void *cb_arg);
static int tap_lsc_intr_handle_set(struct rte_eth_dev *dev, int set);
+static void tap_lsc_register_alarm(void *arg);
+#ifdef TUNGETDEVNETNS
static int
-tap_netns_change(struct rte_eth_dev *dev)
+tap_dev_ns_switch(struct pmd_internals *pmd, int *orig_netns_fd)
{
- struct pmd_internals *pmd = dev->data->dev_private;
-#ifdef TUNGETDEVNETNS
- int netns_fd, orig_netns_fd, new_nlsk_fd;
+ int netns_fd;
netns_fd = ioctl(pmd->ka_fd, TUNGETDEVNETNS);
- if (netns_fd < 0) {
- TAP_LOG(INFO, "%s: interface deleted", pmd->name);
- return 0;
- }
-
- /* Interface was moved to another namespace */
- pmd->if_index = 0;
+ if (netns_fd < 0)
+ return -1;
- /* Save current namespace */
- orig_netns_fd = open("/proc/self/ns/net", O_RDONLY);
- if (orig_netns_fd < 0) {
- TAP_LOG(ERR, "%s: failed to open original netns: %s",
- pmd->name, strerror(errno));
+ *orig_netns_fd = open("/proc/self/ns/net", O_RDONLY);
+ if (*orig_netns_fd < 0) {
close(netns_fd);
return -1;
}
- /* Switch to new namespace */
if (setns(netns_fd, CLONE_NEWNET) < 0) {
- TAP_LOG(ERR, "%s: failed to enter new netns: %s",
- pmd->name, strerror(errno));
close(netns_fd);
- close(orig_netns_fd);
+ close(*orig_netns_fd);
return -1;
}
+ close(netns_fd);
+ return 0;
+}
+
+static void
+tap_dev_ns_restore(int orig_netns_fd)
+{
+ setns(orig_netns_fd, CLONE_NEWNET);
+ close(orig_netns_fd);
+}
+#endif
+
+static int
+tap_netns_change(struct rte_eth_dev *dev)
+{
+ struct pmd_internals *pmd = dev->data->dev_private;
+#ifdef TUNGETDEVNETNS
+ int orig_netns_fd, new_nlsk_fd;
+
+ if (tap_dev_ns_switch(pmd, &orig_netns_fd) < 0) {
+ TAP_LOG(INFO, "%s: interface deleted", pmd->name);
+ return 0;
+ }
+
+ /* Interface was moved to another namespace */
+ pmd->if_index = 0;
+
/*
* Update ifindex by querying interface name.
* The interface now has a new ifindex in the new namespace.
@@ -1840,22 +1857,10 @@ tap_netns_change(struct rte_eth_dev *dev)
/* Recreate netlink socket in new namespace */
new_nlsk_fd = tap_nl_init(0);
- /* Recreate LSC interrupt netlink socket in new namespace */
- rte_intr_callback_unregister_pending(pmd->intr_handle, tap_dev_intr_handler, dev, NULL);
- if (tap_lsc_intr_handle_set(dev, 1) < 0)
- TAP_LOG(WARNING, "%s: failed to recreate LSC interrupt socket",
- pmd->name);
+ /* Defer LSC re-registration outside callback context */
+ rte_eal_alarm_set(1, tap_lsc_register_alarm, dev);
- /* Force carrier back after switching netns */
- tap_carrier_set(pmd, 1);
-
- /* Switch back to original namespace */
- if (setns(orig_netns_fd, CLONE_NEWNET) < 0)
- TAP_LOG(ERR, "%s: failed to return to original netns: %s",
- pmd->name, strerror(errno));
-
- close(orig_netns_fd);
- close(netns_fd);
+ tap_dev_ns_restore(orig_netns_fd);
if (pmd->if_index == 0) {
TAP_LOG(WARNING, "%s: interface moved to another namespace, "
@@ -1952,6 +1957,7 @@ tap_lsc_intr_handle_set(struct rte_eth_dev *dev, int set)
}
clean:
+ rte_eal_alarm_cancel(tap_lsc_register_alarm, dev);
do {
ret = rte_intr_callback_unregister(pmd->intr_handle,
tap_dev_intr_handler, dev);
@@ -1975,6 +1981,30 @@ tap_lsc_intr_handle_set(struct rte_eth_dev *dev, int set)
return 0;
}
+static void
+tap_lsc_register_alarm(void *arg)
+{
+#ifdef TUNGETDEVNETNS
+ struct rte_eth_dev *dev = arg;
+ struct pmd_internals *pmd = dev->data->dev_private;
+ int orig_netns_fd;
+
+ if (tap_dev_ns_switch(pmd, &orig_netns_fd) < 0)
+ return;
+
+ tap_lsc_intr_handle_set(dev, 0);
+ if (tap_lsc_intr_handle_set(dev, 1) < 0)
+ TAP_LOG(ERR, "%s: failed to register LSC interrupt", pmd->name);
+
+ tap_dev_ns_restore(orig_netns_fd);
+
+ tap_carrier_set(pmd, 1);
+ tap_link_update(dev, 0);
+#else
+ RTE_SET_USED(arg);
+#endif
+}
+
static int
tap_intr_handle_set(struct rte_eth_dev *dev, int set)
{
--
2.56.0
More information about the stable
mailing list