]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ipmr: Remove RTNL in ipmr_rules_init() and ipmr_net_init().
authorKuniyuki Iwashima <kuniyu@google.com>
Sat, 28 Feb 2026 22:17:28 +0000 (22:17 +0000)
committerJakub Kicinski <kuba@kernel.org>
Tue, 3 Mar 2026 02:49:40 +0000 (18:49 -0800)
When ipmr_free_table() is called from ipmr_rules_init() or
ipmr_net_init(), the netns is not yet published.

Thus, no device should have been registered, and
mroute_clean_tables() will not call vif_delete(), so
unregister_netdevice_many() is unnecessary.

unregister_netdevice_many() does nothing if the list is empty,
but it requires RTNL due to the unconditional ASSERT_RTNL()
at the entry of unregister_netdevice_many_notify().

Let's remove unnecessary RTNL and ASSERT_RTNL() and instead
add WARN_ON_ONCE() in ipmr_free_table().

Note that we use a local list for the new WARN_ON_ONCE() because
dev_kill_list passed from ipmr_rules_exit_rtnl() may have some
devices when other ops->init() fails after ipmr durnig setup_net().

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20260228221800.1082070-11-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/ipv4/ipmr.c

index 72761c8b293059034eea32964834518af87ad90b..c22bcaead348c6d9be30ea14e67a5ca210eb8114 100644 (file)
@@ -276,10 +276,7 @@ static int __net_init ipmr_rules_init(struct net *net)
        return 0;
 
 err2:
-       rtnl_lock();
        ipmr_free_table(mrt, &dev_kill_list);
-       unregister_netdevice_many(&dev_kill_list);
-       rtnl_unlock();
 err1:
        fib_rules_unregister(ops);
        return err;
@@ -290,7 +287,6 @@ static void __net_exit ipmr_rules_exit_rtnl(struct net *net,
 {
        struct mr_table *mrt, *next;
 
-       ASSERT_RTNL();
        list_for_each_entry_safe(mrt, next, &net->ipv4.mr_tables, list) {
                list_del(&mrt->list);
                ipmr_free_table(mrt, dev_kill_list);
@@ -355,8 +351,6 @@ static int __net_init ipmr_rules_init(struct net *net)
 static void __net_exit ipmr_rules_exit_rtnl(struct net *net,
                                            struct list_head *dev_kill_list)
 {
-       ASSERT_RTNL();
-
        ipmr_free_table(net->ipv4.mrt, dev_kill_list);
 
        net->ipv4.mrt = NULL;
@@ -436,15 +430,19 @@ static struct mr_table *ipmr_new_table(struct net *net, u32 id)
 static void ipmr_free_table(struct mr_table *mrt, struct list_head *dev_kill_list)
 {
        struct net *net = read_pnet(&mrt->net);
+       LIST_HEAD(ipmr_dev_kill_list);
 
        WARN_ON_ONCE(!mr_can_free_table(net));
 
        timer_shutdown_sync(&mrt->ipmr_expire_timer);
        mroute_clean_tables(mrt, MRT_FLUSH_VIFS | MRT_FLUSH_VIFS_STATIC |
                            MRT_FLUSH_MFC | MRT_FLUSH_MFC_STATIC,
-                           dev_kill_list);
+                           &ipmr_dev_kill_list);
        rhltable_destroy(&mrt->mfc_hash);
        kfree(mrt);
+
+       WARN_ON_ONCE(!net_initialized(net) && !list_empty(&ipmr_dev_kill_list));
+       list_splice(&ipmr_dev_kill_list, dev_kill_list);
 }
 
 /* Service routines creating virtual interfaces: DVMRP tunnels and PIMREG */
@@ -3287,10 +3285,7 @@ static int __net_init ipmr_net_init(struct net *net)
 proc_cache_fail:
        remove_proc_entry("ip_mr_vif", net->proc_net);
 proc_vif_fail:
-       rtnl_lock();
        ipmr_rules_exit_rtnl(net, &dev_kill_list);
-       unregister_netdevice_many(&dev_kill_list);
-       rtnl_unlock();
 #endif
 ipmr_rules_fail:
        ipmr_notifier_exit(net);