net: remove default_device_exit()
authorEric Dumazet <edumazet@google.com>
Tue, 8 Feb 2022 04:50:38 +0000 (20:50 -0800)
committerJakub Kicinski <kuba@kernel.org>
Wed, 9 Feb 2022 04:41:35 +0000 (20:41 -0800)
For some reason default_device_ops kept two exit method:

1) default_device_exit() is called for each netns being dismantled in
a cleanup_net() round. This acquires rtnl for each invocation.

2) default_device_exit_batch() is called once with the list of all netns
int the batch, allowing for a single rtnl invocation.

Get rid of the .exit() method to handle the logic from
default_device_exit_batch(), to decrease the number of rtnl acquisition
to one.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/core/dev.c

index 66556a2..f5ef516 100644 (file)
@@ -10850,14 +10850,14 @@ static struct pernet_operations __net_initdata netdev_net_ops = {
        .exit = netdev_exit,
 };
 
-static void __net_exit default_device_exit(struct net *net)
+static void __net_exit default_device_exit_net(struct net *net)
 {
        struct net_device *dev, *aux;
        /*
         * Push all migratable network devices back to the
         * initial network namespace
         */
-       rtnl_lock();
+       ASSERT_RTNL();
        for_each_netdev_safe(net, dev, aux) {
                int err;
                char fb_name[IFNAMSIZ];
@@ -10881,22 +10881,22 @@ static void __net_exit default_device_exit(struct net *net)
                        BUG();
                }
        }
-       rtnl_unlock();
 }
 
 static void __net_exit rtnl_lock_unregistering(struct list_head *net_list)
 {
-       /* Return with the rtnl_lock held when there are no network
+       /* Return (with the rtnl_lock held) when there are no network
         * devices unregistering in any network namespace in net_list.
         */
-       struct net *net;
-       bool unregistering;
        DEFINE_WAIT_FUNC(wait, woken_wake_function);
+       bool unregistering;
+       struct net *net;
 
+       ASSERT_RTNL();
        add_wait_queue(&netdev_unregistering_wq, &wait);
        for (;;) {
                unregistering = false;
-               rtnl_lock();
+
                list_for_each_entry(net, net_list, exit_list) {
                        if (net->dev_unreg_count > 0) {
                                unregistering = true;
@@ -10908,6 +10908,7 @@ static void __net_exit rtnl_lock_unregistering(struct list_head *net_list)
                __rtnl_unlock();
 
                wait_woken(&wait, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
+               rtnl_lock();
        }
        remove_wait_queue(&netdev_unregistering_wq, &wait);
 }
@@ -10923,6 +10924,11 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list)
        struct net *net;
        LIST_HEAD(dev_kill_list);
 
+       rtnl_lock();
+       list_for_each_entry(net, net_list, exit_list) {
+               default_device_exit_net(net);
+               cond_resched();
+       }
        /* To prevent network device cleanup code from dereferencing
         * loopback devices or network devices that have been freed
         * wait here for all pending unregistrations to complete,
@@ -10935,6 +10941,7 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list)
         * default_device_exit_batch.
         */
        rtnl_lock_unregistering(net_list);
+
        list_for_each_entry(net, net_list, exit_list) {
                for_each_netdev_reverse(net, dev) {
                        if (dev->rtnl_link_ops && dev->rtnl_link_ops->dellink)
@@ -10948,7 +10955,6 @@ static void __net_exit default_device_exit_batch(struct list_head *net_list)
 }
 
 static struct pernet_operations __net_initdata default_device_ops = {
-       .exit = default_device_exit,
        .exit_batch = default_device_exit_batch,
 };