diff mbox series

[v1,net-next,03/14] net: Add ->exit_rtnl() hook to struct pernet_operations.

Message ID 20250410022004.8668-4-kuniyu@amazon.com (mailing list archive)
State Superseded
Delegated to: Netdev Maintainers
Headers show
Series net: Convert ->exit_batch_rtnl() to ->exit_rtnl(). | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 50 this patch: 50
netdev/build_tools success Errors and warnings before: 26 (+2) this patch: 26 (+2)
netdev/cc_maintainers success CCed 6 of 6 maintainers
netdev/build_clang success Errors and warnings before: 78 this patch: 78
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 4251 this patch: 4251
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 88 lines checked
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 3 this patch: 3
netdev/source_inline success Was 0 now: 0
netdev/contest success net-next-2025-04-11--03-00 (tests: 900)

Commit Message

Kuniyuki Iwashima April 10, 2025, 2:19 a.m. UTC
struct pernet_operations provides two batching hooks; ->exit_batch()
and ->exit_batch_rtnl().

The batching variant is beneficial if ->exit() meets any of the
following conditions:

  1) ->exit() repeatedly acquires a global lock for each netns

  2) ->exit() has a time-consuming operation that can be factored
     out (e.g. synchronize_rcu(), smp_mb(), etc)

  3) ->exit() does not need to repeat the same iterations for each
     netns (e.g. inet_twsk_purge())

Currently, none of the ->exit_batch_rtnl() functions satisfy any of
the above conditions because RTNL is factored out and held by the
caller and all of these functions iterate over the dying netns list.

Also, we want to hold per-netns RTNL there but avoid spreading
__rtnl_net_lock() across multiple locations.

Let's add ->exit_rtnl() hook and run it under __rtnl_net_lock().

The following patches will convert all ->exit_batch_rtnl() users
to ->exit_rtnl().

Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
---
 include/net/net_namespace.h |  2 ++
 net/core/net_namespace.c    | 53 +++++++++++++++++++++++++++----------
 2 files changed, 41 insertions(+), 14 deletions(-)
diff mbox series

Patch

diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index bd57d8fb54f1..b071e6eed9d5 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -475,6 +475,8 @@  struct pernet_operations {
 	void (*exit)(struct net *net);
 	void (*exit_batch)(struct list_head *net_exit_list);
 	/* Following method is called with RTNL held. */
+	void (*exit_rtnl)(struct net *net,
+			  struct list_head *dev_kill_list);
 	void (*exit_batch_rtnl)(struct list_head *net_exit_list,
 				struct list_head *dev_kill_list);
 	unsigned int * const id;
diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c
index 5e0ee117cf25..7ea818342721 100644
--- a/net/core/net_namespace.c
+++ b/net/core/net_namespace.c
@@ -163,16 +163,51 @@  static void ops_pre_exit_list(const struct pernet_operations *ops,
 	}
 }
 
+static void ops_exit_rtnl_list(const struct list_head *ops_list,
+			       const struct pernet_operations *ops,
+			       struct list_head *net_exit_list)
+{
+	const struct pernet_operations *saved_ops = ops;
+	LIST_HEAD(dev_kill_list);
+	struct net *net;
+
+	rtnl_lock();
+
+	list_for_each_entry(net, net_exit_list, exit_list) {
+		__rtnl_net_lock(net);
+
+		ops = saved_ops;
+		list_for_each_entry_continue_reverse(ops, ops_list, list) {
+			if (ops->exit_rtnl)
+				ops->exit_rtnl(net, &dev_kill_list);
+		}
+
+		__rtnl_net_unlock(net);
+	}
+
+	ops = saved_ops;
+	list_for_each_entry_continue_reverse(ops, ops_list, list) {
+		if (ops->exit_batch_rtnl)
+			ops->exit_batch_rtnl(net_exit_list, &dev_kill_list);
+	}
+
+	unregister_netdevice_many(&dev_kill_list);
+
+	rtnl_unlock();
+}
+
 static void ops_exit_list(const struct pernet_operations *ops,
 			  struct list_head *net_exit_list)
 {
-	struct net *net;
 	if (ops->exit) {
+		struct net *net;
+
 		list_for_each_entry(net, net_exit_list, exit_list) {
 			ops->exit(net);
 			cond_resched();
 		}
 	}
+
 	if (ops->exit_batch)
 		ops->exit_batch(net_exit_list);
 }
@@ -213,18 +248,8 @@  static void ops_undo_list(const struct list_head *ops_list,
 	else
 		synchronize_rcu();
 
-	if (hold_rtnl) {
-		LIST_HEAD(dev_kill_list);
-
-		ops = saved_ops;
-		rtnl_lock();
-		list_for_each_entry_continue_reverse(ops, ops_list, list) {
-			if (ops->exit_batch_rtnl)
-				ops->exit_batch_rtnl(net_exit_list, &dev_kill_list);
-		}
-		unregister_netdevice_many(&dev_kill_list);
-		rtnl_unlock();
-	}
+	if (hold_rtnl)
+		ops_exit_rtnl_list(ops_list, saved_ops, net_exit_list);
 
 	ops = saved_ops;
 	list_for_each_entry_continue_reverse(ops, ops_list, list)
@@ -238,7 +263,7 @@  static void ops_undo_list(const struct list_head *ops_list,
 static void ops_undo_single(struct pernet_operations *ops,
 			    struct list_head *net_exit_list)
 {
-	bool hold_rtnl = !!ops->exit_batch_rtnl;
+	bool hold_rtnl = ops->exit_rtnl || ops->exit_batch_rtnl;
 	LIST_HEAD(ops_list);
 
 	list_add(&ops->list, &ops_list);