summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorPaolo Abeni <pabeni@redhat.com>2026-07-11 12:57:54 +0200
committerPaolo Abeni <pabeni@redhat.com>2026-07-11 12:57:54 +0200
commit71ac90cca6d8e6fcdd674a50157181ef4ac441a0 (patch)
treef8d57c63e535c6946957daf9dc1897cd9e9ea64d /include/linux
parent23dad2d088dfc82cae1f5a936f8ff7ffebb38dd9 (diff)
parent00a40d809207a61f0762488aa5ce72e941b367ce (diff)
Merge branch 'net-support-per-netns-device-unregistration'
Kuniyuki Iwashima says: ==================== net: Support per-netns device unregistration The biggest blocker to per-netns RTNL is netdev unregistration. It starts within a single netns, but it can eventually involve multiple namespaces. There are three types of such cross-netns devices: 1. Paired devices (e.g., netkit, veth, vxcan) -> Unregistering one device also deletes its peer, which may reside in another netns. 2. Tunnel devices (e.g., bareudp, geneve, etc) -> Destroying a netns removes devices in another netns if their backend sockets reside in the dying netns 3. Stacked devices (e.g., ipvlan, macvlan, etc) -> Removing the lower device also removes multiple upper devices, each of which may reside in different namespaces. While the first two device types require at most two rtnl_net_lock()s, the stacked type has no upper limit. This makes it impossible to freeze all necessary namespaces in advance. This series introduces per-netns work, initially suggested at NetConf 2024, to delegate the unregistration of such cross-netns devices. https://netdev.bots.linux.dev/netconf/2024/kuniyu.pdf#page=62 The first half of the series wraps NETDEV_UNREGISTER (in core) with per-netns RTNL, adds a helper for per-netns device unregistration, and forces per-netns device unregistration in the core code when CONFIG_DEBUG_NET_SMALL_RTNL=y. The latter half picks out one from each type (veth, bareudp, ipvlan) and converts them to support per-netns device unregistration, although the operations are **still serialised under RTNL** for now. Please note that this series focuses only on the device unregistration paths. For example, there are ASSERT_RTNL() left in other paths, and Sashiko may point it out, but they are out of scope. This is just the first step, and we need more incremental changes to completely remove RTNL anyway. Now, we can see that unregistering a lower device (veth0 below) removes upper devices (ipvl2, ipvl3) in different namespaces using per-netns work with a different PID. The lower device (veth0) is freed only after all upper ipvlan devices have called netdev_put() in ipvlan_uninit(). # ip netns add ns1 # ip netns add ns2 # ip netns add ns3 # ip -n ns1 link add veth0 type veth peer veth1 # ip -n ns2 link add ipvl2 link veth0 link-netns ns1 type ipvlan mode l2 # ip -n ns3 link add ipvl3 link veth0 link-netns ns1 type ipvlan mode l2 # ip -n ns1 link del veth0 # bpftrace -e '#include <linux/netdevice.h> kprobe:ipvlan_uninit, kprobe:veth_dellink, kprobe:free_netdev { $dev = (struct net_device *)arg0; printf("PID: %d | DEV: %s%s\n", pid, $dev->name, kstack()); }' PID: 2010 | DEV: veth0 veth_dellink+5 rtnl_dellink+1213 rtnetlink_rcv_msg+1791 ... PID: 440 | DEV: ipvl2 ipvlan_uninit+5 unregister_netdevice_many_notify+7129 unregister_netdevice_many_net+1050 rtnl_net_work_func+136 ... PID: 440 | DEV: ipvl2 free_netdev+5 netdev_run_todo+4798 process_scheduled_works+2538 ... PID: 440 | DEV: ipvl3 ipvlan_uninit+5 unregister_netdevice_many_notify+7129 unregister_netdevice_many_net+1050 rtnl_net_work_func+136 process_scheduled_works+2538 ... PID: 2010 | DEV: veth0 free_netdev+5 netdev_run_todo+4798 rtnl_dellink+1507 rtnetlink_rcv_msg+1791 ... PID: 440 | DEV: ipvl3 free_netdev+5 netdev_run_todo+4798 process_scheduled_works+2538 ... v1: https://lore.kernel.org/netdev/20260701214334.266991-1-kuniyu@google.com/ ==================== Link: https://patch.msgid.link/20260703001009.1572444-1-kuniyu@google.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/netdevice.h24
-rw-r--r--include/linux/rtnetlink.h8
2 files changed, 32 insertions, 0 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 9981d637f8b5..8db25b79573e 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1845,6 +1845,8 @@ enum netdev_reg_state {
* @napi_list: List entry used for polling NAPI devices
* @unreg_list: List entry when we are unregistering the
* device; see the function unregister_netdev
+ * @unreg_list_net:List entry when we are unregistering the cross-netns
+ * device; see the function unregister_netdevice_queue_net()
* @close_list: List entry used when we are closing the device
* @ptype_all: Device-specific packet handlers for all protocols
* @ptype_specific: Device-specific, protocol-specific packet handlers
@@ -2241,6 +2243,9 @@ struct net_device {
struct list_head dev_list;
struct list_head napi_list;
struct list_head unreg_list;
+#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
+ struct list_head unreg_list_net;
+#endif
struct list_head close_list;
struct list_head ptype_all;
@@ -3472,6 +3477,25 @@ static inline void unregister_netdevice(struct net_device *dev)
unregister_netdevice_queue(dev, NULL);
}
+#ifdef CONFIG_DEBUG_NET_SMALL_RTNL
+void unregister_netdevice_queue_net(struct net *net, struct net_device *dev,
+ struct list_head *head);
+void unregister_netdevice_many_net(struct net *net);
+void unregister_netdevice_queue_many_net(struct net *net, struct list_head *head);
+#else
+static inline void unregister_netdevice_queue_net(struct net *net,
+ struct net_device *dev,
+ struct list_head *head)
+{
+ unregister_netdevice_queue(dev, head);
+}
+
+static inline void unregister_netdevice_queue_many_net(struct net *net,
+ struct list_head *head)
+{
+}
+#endif
+
int netdev_refcnt_read(const struct net_device *dev);
void free_netdev(struct net_device *dev);
diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h
index ea39dd23a197..95729339e7a5 100644
--- a/include/linux/rtnetlink.h
+++ b/include/linux/rtnetlink.h
@@ -115,6 +115,10 @@ bool rtnl_net_is_locked(struct net *net);
bool lockdep_rtnl_net_is_held(struct net *net);
+void rtnl_net_queue_work(struct net *net);
+void rtnl_net_flush_workqueue(void);
+void rtnl_net_work_func(struct work_struct *work);
+
#define rcu_dereference_rtnl_net(net, p) \
rcu_dereference_check(p, lockdep_rtnl_net_is_held(net))
#define rtnl_net_dereference(net, p) \
@@ -150,6 +154,10 @@ static inline void ASSERT_RTNL_NET(struct net *net)
ASSERT_RTNL();
}
+static inline void rtnl_net_flush_workqueue(void)
+{
+}
+
#define rcu_dereference_rtnl_net(net, p) \
rcu_dereference_rtnl(p)
#define rtnl_net_dereference(net, p) \