forked from mirrors/linux
		
	net: expedite synchronize_net() for cleanup_net()
cleanup_net() is the single thread responsible for netns dismantles, and a serious bottleneck. Before we can get per-netns RTNL, make sure all synchronize_net() called from this thread are using rcu_synchronize_expedited(). v3: deal with CONFIG_NET_NS=n Signed-off-by: Eric Dumazet <edumazet@google.com> Reviewed-by: Jesse Brandeburg <jbrandeburg@cloudflare.com> Link: https://patch.msgid.link/20250114205531.967841-2-edumazet@google.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
		
							parent
							
								
									0b6f6593aa
								
							
						
					
					
						commit
						0734d7c3d9
					
				
					 3 changed files with 17 additions and 1 deletions
				
			
		|  | @ -210,6 +210,8 @@ void net_ns_barrier(void); | ||||||
| 
 | 
 | ||||||
| struct ns_common *get_net_ns(struct ns_common *ns); | struct ns_common *get_net_ns(struct ns_common *ns); | ||||||
| struct net *get_net_ns_by_fd(int fd); | struct net *get_net_ns_by_fd(int fd); | ||||||
|  | extern struct task_struct *cleanup_net_task; | ||||||
|  | 
 | ||||||
| #else /* CONFIG_NET_NS */ | #else /* CONFIG_NET_NS */ | ||||||
| #include <linux/sched.h> | #include <linux/sched.h> | ||||||
| #include <linux/nsproxy.h> | #include <linux/nsproxy.h> | ||||||
|  |  | ||||||
|  | @ -10099,6 +10099,15 @@ static void dev_index_release(struct net *net, int ifindex) | ||||||
| 	WARN_ON(xa_erase(&net->dev_by_index, ifindex)); | 	WARN_ON(xa_erase(&net->dev_by_index, ifindex)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static bool from_cleanup_net(void) | ||||||
|  | { | ||||||
|  | #ifdef CONFIG_NET_NS | ||||||
|  | 	return current == cleanup_net_task; | ||||||
|  | #else | ||||||
|  | 	return false; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* Delayed registration/unregisteration */ | /* Delayed registration/unregisteration */ | ||||||
| LIST_HEAD(net_todo_list); | LIST_HEAD(net_todo_list); | ||||||
| DECLARE_WAIT_QUEUE_HEAD(netdev_unregistering_wq); | DECLARE_WAIT_QUEUE_HEAD(netdev_unregistering_wq); | ||||||
|  | @ -11474,7 +11483,7 @@ EXPORT_SYMBOL_GPL(alloc_netdev_dummy); | ||||||
| void synchronize_net(void) | void synchronize_net(void) | ||||||
| { | { | ||||||
| 	might_sleep(); | 	might_sleep(); | ||||||
| 	if (rtnl_is_locked()) | 	if (from_cleanup_net() || rtnl_is_locked()) | ||||||
| 		synchronize_rcu_expedited(); | 		synchronize_rcu_expedited(); | ||||||
| 	else | 	else | ||||||
| 		synchronize_rcu(); | 		synchronize_rcu(); | ||||||
|  |  | ||||||
|  | @ -588,6 +588,8 @@ static void unhash_nsid(struct net *net, struct net *last) | ||||||
| 
 | 
 | ||||||
| static LLIST_HEAD(cleanup_list); | static LLIST_HEAD(cleanup_list); | ||||||
| 
 | 
 | ||||||
|  | struct task_struct *cleanup_net_task; | ||||||
|  | 
 | ||||||
| static void cleanup_net(struct work_struct *work) | static void cleanup_net(struct work_struct *work) | ||||||
| { | { | ||||||
| 	const struct pernet_operations *ops; | 	const struct pernet_operations *ops; | ||||||
|  | @ -596,6 +598,8 @@ static void cleanup_net(struct work_struct *work) | ||||||
| 	LIST_HEAD(net_exit_list); | 	LIST_HEAD(net_exit_list); | ||||||
| 	LIST_HEAD(dev_kill_list); | 	LIST_HEAD(dev_kill_list); | ||||||
| 
 | 
 | ||||||
|  | 	cleanup_net_task = current; | ||||||
|  | 
 | ||||||
| 	/* Atomically snapshot the list of namespaces to cleanup */ | 	/* Atomically snapshot the list of namespaces to cleanup */ | ||||||
| 	net_kill_list = llist_del_all(&cleanup_list); | 	net_kill_list = llist_del_all(&cleanup_list); | ||||||
| 
 | 
 | ||||||
|  | @ -670,6 +674,7 @@ static void cleanup_net(struct work_struct *work) | ||||||
| 		put_user_ns(net->user_ns); | 		put_user_ns(net->user_ns); | ||||||
| 		net_free(net); | 		net_free(net); | ||||||
| 	} | 	} | ||||||
|  | 	cleanup_net_task = NULL; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Eric Dumazet
						Eric Dumazet