mirror of
https://github.com/torvalds/linux.git
synced 2025-08-16 22:51:39 +02:00
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…
Add table
Add a link
Reference in a new issue