forked from mirrors/linux
		
	net: Add ops_undo_single for module load/unload.
If ops_init() fails while loading a module or we unload the module, free_exit_list() rolls back the changes. The rollback sequence is the same as ops_undo_list(). The ops is already removed from pernet_list before calling free_exit_list(). If we link the ops to a temporary list, we can reuse ops_undo_list(). Let's add a wrapper of ops_undo_list() and use it instead of free_exit_list(). Now, we have the central place to roll back ops_init(). Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com> Reviewed-by: Sabrina Dubroca <sd@queasysnail.net> Link: https://patch.msgid.link/20250411205258.63164-3-kuniyu@amazon.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
		
							parent
							
								
									e333b1c3cf
								
							
						
					
					
						commit
						fed176bf31
					
				
					 1 changed files with 24 additions and 30 deletions
				
			
		|  | @ -235,6 +235,17 @@ static void ops_undo_list(const struct list_head *ops_list, | ||||||
| 		ops_free_list(ops, net_exit_list); | 		ops_free_list(ops, net_exit_list); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void ops_undo_single(struct pernet_operations *ops, | ||||||
|  | 			    struct list_head *net_exit_list) | ||||||
|  | { | ||||||
|  | 	bool hold_rtnl = !!ops->exit_batch_rtnl; | ||||||
|  | 	LIST_HEAD(ops_list); | ||||||
|  | 
 | ||||||
|  | 	list_add(&ops->list, &ops_list); | ||||||
|  | 	ops_undo_list(&ops_list, NULL, net_exit_list, false, hold_rtnl); | ||||||
|  | 	list_del(&ops->list); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* should be called with nsid_lock held */ | /* should be called with nsid_lock held */ | ||||||
| static int alloc_netid(struct net *net, struct net *peer, int reqid) | static int alloc_netid(struct net *net, struct net *peer, int reqid) | ||||||
| { | { | ||||||
|  | @ -1235,31 +1246,13 @@ void __init net_ns_init(void) | ||||||
| 	rtnl_register_many(net_ns_rtnl_msg_handlers); | 	rtnl_register_many(net_ns_rtnl_msg_handlers); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void free_exit_list(struct pernet_operations *ops, struct list_head *net_exit_list) |  | ||||||
| { |  | ||||||
| 	ops_pre_exit_list(ops, net_exit_list); |  | ||||||
| 	synchronize_rcu(); |  | ||||||
| 
 |  | ||||||
| 	if (ops->exit_batch_rtnl) { |  | ||||||
| 		LIST_HEAD(dev_kill_list); |  | ||||||
| 
 |  | ||||||
| 		rtnl_lock(); |  | ||||||
| 		ops->exit_batch_rtnl(net_exit_list, &dev_kill_list); |  | ||||||
| 		unregister_netdevice_many(&dev_kill_list); |  | ||||||
| 		rtnl_unlock(); |  | ||||||
| 	} |  | ||||||
| 	ops_exit_list(ops, net_exit_list); |  | ||||||
| 
 |  | ||||||
| 	ops_free_list(ops, net_exit_list); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #ifdef CONFIG_NET_NS | #ifdef CONFIG_NET_NS | ||||||
| static int __register_pernet_operations(struct list_head *list, | static int __register_pernet_operations(struct list_head *list, | ||||||
| 					struct pernet_operations *ops) | 					struct pernet_operations *ops) | ||||||
| { | { | ||||||
|  | 	LIST_HEAD(net_exit_list); | ||||||
| 	struct net *net; | 	struct net *net; | ||||||
| 	int error; | 	int error; | ||||||
| 	LIST_HEAD(net_exit_list); |  | ||||||
| 
 | 
 | ||||||
| 	list_add_tail(&ops->list, list); | 	list_add_tail(&ops->list, list); | ||||||
| 	if (ops->init || ops->id) { | 	if (ops->init || ops->id) { | ||||||
|  | @ -1278,21 +1271,21 @@ static int __register_pernet_operations(struct list_head *list, | ||||||
| out_undo: | out_undo: | ||||||
| 	/* If I have an error cleanup all namespaces I initialized */ | 	/* If I have an error cleanup all namespaces I initialized */ | ||||||
| 	list_del(&ops->list); | 	list_del(&ops->list); | ||||||
| 	free_exit_list(ops, &net_exit_list); | 	ops_undo_single(ops, &net_exit_list); | ||||||
| 	return error; | 	return error; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void __unregister_pernet_operations(struct pernet_operations *ops) | static void __unregister_pernet_operations(struct pernet_operations *ops) | ||||||
| { | { | ||||||
| 	struct net *net; |  | ||||||
| 	LIST_HEAD(net_exit_list); | 	LIST_HEAD(net_exit_list); | ||||||
|  | 	struct net *net; | ||||||
| 
 | 
 | ||||||
| 	list_del(&ops->list); |  | ||||||
| 	/* See comment in __register_pernet_operations() */ | 	/* See comment in __register_pernet_operations() */ | ||||||
| 	for_each_net(net) | 	for_each_net(net) | ||||||
| 		list_add_tail(&net->exit_list, &net_exit_list); | 		list_add_tail(&net->exit_list, &net_exit_list); | ||||||
| 
 | 
 | ||||||
| 	free_exit_list(ops, &net_exit_list); | 	list_del(&ops->list); | ||||||
|  | 	ops_undo_single(ops, &net_exit_list); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #else | #else | ||||||
|  | @ -1300,22 +1293,23 @@ static void __unregister_pernet_operations(struct pernet_operations *ops) | ||||||
| static int __register_pernet_operations(struct list_head *list, | static int __register_pernet_operations(struct list_head *list, | ||||||
| 					struct pernet_operations *ops) | 					struct pernet_operations *ops) | ||||||
| { | { | ||||||
| 	if (!init_net_initialized) { |  | ||||||
| 	list_add_tail(&ops->list, list); | 	list_add_tail(&ops->list, list); | ||||||
|  | 
 | ||||||
|  | 	if (!init_net_initialized) | ||||||
| 		return 0; | 		return 0; | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	return ops_init(ops, &init_net); | 	return ops_init(ops, &init_net); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void __unregister_pernet_operations(struct pernet_operations *ops) | static void __unregister_pernet_operations(struct pernet_operations *ops) | ||||||
| { | { | ||||||
| 	if (!init_net_initialized) { |  | ||||||
| 	list_del(&ops->list); | 	list_del(&ops->list); | ||||||
| 	} else { | 
 | ||||||
|  | 	if (init_net_initialized) { | ||||||
| 		LIST_HEAD(net_exit_list); | 		LIST_HEAD(net_exit_list); | ||||||
|  | 
 | ||||||
| 		list_add(&init_net.exit_list, &net_exit_list); | 		list_add(&init_net.exit_list, &net_exit_list); | ||||||
| 		free_exit_list(ops, &net_exit_list); | 		ops_undo_single(ops, &net_exit_list); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Kuniyuki Iwashima
						Kuniyuki Iwashima