All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kuniyuki Iwashima <kuniyu@google.com>
To: "David S . Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	 Jakub Kicinski <kuba@kernel.org>,
	Paolo Abeni <pabeni@redhat.com>,
	Andrew Lunn <andrew+netdev@lunn.ch>
Cc: Simon Horman <horms@kernel.org>,
	Kuniyuki Iwashima <kuniyu@google.com>,
	 Kuniyuki Iwashima <kuni1840@gmail.com>,
	netdev@vger.kernel.org
Subject: [PATCH v1 net-next 07/14] net: Call unregister_netdevice_many() per netns.
Date: Wed,  1 Jul 2026 21:41:45 +0000	[thread overview]
Message-ID: <20260701214334.266991-8-kuniyu@google.com> (raw)
In-Reply-To: <20260701214334.266991-1-kuniyu@google.com>

For per-netns device unregistration, the list passed to
unregister_netdevice_many() must contain devices from a single
netns only (once all callers are converted).

Let's move collected devices in the following functions to
net->dev_unreg_head and let __rtnl_net_unlock() pass them to
unregister_netdevice_many().

  * default_device_exit_batch()
  * ops_exit_rtnl_list()
  * __rtnl_kill_links()

This allows incremental conversion of each driver to support
per-netns device unregistration without affecting the normal
kernel where CONFIG_DEBUG_NET_SMALL_RTNL is disabled.

Note that this change unbatches synchronize_rcu() etc in
unregister_netdevice_many(), but we can later split it into
multiple stages to batch them again.

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
 include/linux/netdevice.h |  6 ++++++
 net/core/dev.c            | 27 +++++++++++++++++++++++++++
 net/core/net_namespace.c  |  1 +
 net/core/rtnetlink.c      |  6 +++++-
 4 files changed, 39 insertions(+), 1 deletion(-)

diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 53454db3611a..0cd26fb59806 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -3479,6 +3479,7 @@ static inline void unregister_netdevice(struct net_device *dev)
 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,
@@ -3486,6 +3487,11 @@ static inline void unregister_netdevice_queue_net(struct net *net,
 {
 	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);
diff --git a/net/core/dev.c b/net/core/dev.c
index 0f0bf65f5bf9..57fb4741d0ac 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -12546,6 +12546,28 @@ void unregister_netdevice_queue_net(struct net *net, struct net_device *dev,
 }
 EXPORT_SYMBOL(unregister_netdevice_queue_net);
 
+void unregister_netdevice_queue_many_net(struct net *net, struct list_head *head)
+{
+	struct net_device *dev, *tmp;
+
+	spin_lock(&net->dev_unreg_lock);
+	list_for_each_entry_safe(dev, tmp, head, unreg_list) {
+		/* Once all cross-netns unregister_netdevice_queue() is
+		 * converted to _net() (or for debugging), remove this check.
+		 */
+		if (!net_eq(dev_net(dev), net))
+			continue;
+
+		DEBUG_NET_WARN_ONCE(!net_eq(dev_net(dev), net),
+				    "%s was unregistered from a different netns.\n",
+				    dev->name);
+
+		list_del_init(&dev->unreg_list);
+		list_move_tail(&dev->unreg_list_net, &net->dev_unreg_head);
+	}
+	spin_unlock(&net->dev_unreg_lock);
+}
+
 static void unregister_netdevice_move_net(struct net *net_old,
 					  struct net *net,
 					  struct net_device *dev)
@@ -13179,12 +13201,17 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list)
 	__rtnl_net_unlock(&init_net);
 
 	list_for_each_entry(net, net_list, exit_list) {
+		__rtnl_net_lock(net);
+
 		for_each_netdev_reverse(net, dev) {
 			if (dev->rtnl_link_ops && dev->rtnl_link_ops->dellink)
 				dev->rtnl_link_ops->dellink(dev, &dev_kill_list);
 			else
 				unregister_netdevice_queue(dev, &dev_kill_list);
 		}
+
+		unregister_netdevice_queue_many_net(net, &dev_kill_list);
+		__rtnl_net_unlock(net);
 	}
 	unregister_netdevice_many(&dev_kill_list);
 	rtnl_unlock();
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 578b48cf5318..a91d2b58aadd 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -181,6 +181,7 @@ static void ops_exit_rtnl_list(const struct list_head *ops_list,
 				ops->exit_rtnl(net, &dev_kill_list);
 		}
 
+		unregister_netdevice_queue_many_net(net, &dev_kill_list);
 		__rtnl_net_unlock(net);
 	}
 
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c
index 544498d3c325..b129f793d851 100644
--- a/net/core/rtnetlink.c
+++ b/net/core/rtnetlink.c
@@ -714,8 +714,12 @@ void rtnl_link_unregister(struct rtnl_link_ops *ops)
 	down_write(&pernet_ops_rwsem);
 	rtnl_lock_unregistering_all();
 
-	for_each_net(net)
+	for_each_net(net) {
+		__rtnl_net_lock(net);
 		__rtnl_kill_links(net, ops, &dev_kill_list);
+		unregister_netdevice_queue_many_net(net, &dev_kill_list);
+		__rtnl_net_unlock(net);
+	}
 
 	unregister_netdevice_many(&dev_kill_list);
 
-- 
2.55.0.rc0.799.gd6f94ed593-goog


  parent reply	other threads:[~2026-07-01 21:43 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-01 21:41 [PATCH v1 net-next 00/14] net: Support per-netns device unregistration Kuniyuki Iwashima
2026-07-01 21:41 ` [PATCH v1 net-next 01/14] rtnetlink: Lock sock_net(skb->sk) in rtnl_newlink() Kuniyuki Iwashima
2026-07-01 21:41 ` [PATCH v1 net-next 02/14] rtnetlink: Call unregister_netdevice_many() only once in rtnl_link_unregister() Kuniyuki Iwashima
2026-07-01 21:41 ` [PATCH v1 net-next 03/14] rtnetlink: Add per-netns rtnl_work Kuniyuki Iwashima
2026-07-01 21:41 ` [PATCH v1 net-next 04/14] net: Wrap default_device_exit_net() with __rtnl_net_lock() Kuniyuki Iwashima
2026-07-01 21:41 ` [PATCH v1 net-next 05/14] net: Hold __rtnl_net_lock() in netdev_wait_allrefs_any() Kuniyuki Iwashima
2026-07-01 21:41 ` [PATCH v1 net-next 06/14] net: Add per-netns netdev unregistration infra Kuniyuki Iwashima
2026-07-01 21:41 ` Kuniyuki Iwashima [this message]
2026-07-01 21:41 ` [PATCH v1 net-next 08/14] veth: Support per-netns device unregistration Kuniyuki Iwashima
2026-07-01 21:41 ` [PATCH v1 net-next 09/14] bareudp: Protect bareudp_list with mutex Kuniyuki Iwashima
2026-07-01 21:41 ` [PATCH v1 net-next 10/14] bareudp: Support per-netns netdev unregistration Kuniyuki Iwashima
2026-07-01 21:41 ` [PATCH v1 net-next 11/14] ipvlan: Convert ipvl_port.count to refcount_t Kuniyuki Iwashima
2026-07-01 21:41 ` [PATCH v1 net-next 12/14] ipvlan: Synchronise ipvlan_init() and ipvlan_uninit() for the same lower dev Kuniyuki Iwashima
2026-07-01 21:41 ` [PATCH v1 net-next 13/14] ipvlan: Protect ipvl_port.ipvlans with mutex Kuniyuki Iwashima
2026-07-01 21:41 ` [PATCH v1 net-next 14/14] ipvlan: Support per-netns netdev unregistration Kuniyuki Iwashima
2026-07-02  7:45 ` [syzbot ci] Re: net: Support per-netns device unregistration syzbot ci
2026-07-02 21:59   ` Kuniyuki Iwashima

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260701214334.266991-8-kuniyu@google.com \
    --to=kuniyu@google.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=horms@kernel.org \
    --cc=kuba@kernel.org \
    --cc=kuni1840@gmail.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.