net: make dev_unreg_count global
authorEric Dumazet <edumazet@google.com>
Fri, 2 Feb 2024 10:11:06 +0000 (10:11 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 4 Feb 2024 16:08:21 +0000 (16:08 +0000)
We can use a global dev_unreg_count counter instead
of a per netns one.

As a bonus we can factorize the changes done on it
for bulk device removals.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/rtnetlink.h
include/net/net_namespace.h
net/core/dev.c
net/core/rtnetlink.c

index 410529fca18b2f18ce2f94bba02ccebf1b544817..21780608cf47ca0687dbaaf0d07b561e8631412c 100644 (file)
@@ -47,6 +47,7 @@ extern int rtnl_lock_killable(void);
 extern bool refcount_dec_and_rtnl_lock(refcount_t *r);
 
 extern wait_queue_head_t netdev_unregistering_wq;
+extern atomic_t dev_unreg_count;
 extern struct rw_semaphore pernet_ops_rwsem;
 extern struct rw_semaphore net_rwsem;
 
index 13b3a4e29fdb3b1f37649072ea71181ec1bad256..cd0c2eedbb5e9ddcbd5e0a37e2eb7e0cf57495d5 100644 (file)
@@ -67,8 +67,6 @@ struct net {
                                                 */
        spinlock_t              rules_mod_lock;
 
-       atomic_t                dev_unreg_count;
-
        unsigned int            dev_base_seq;   /* protected by rtnl_mutex */
        u32                     ifindex;
 
index b53b9c94de4008aa7e808d58618675425aff0f4c..27ba057d06c490772320775f25fc2885f0a1ff3e 100644 (file)
@@ -9698,11 +9698,11 @@ static void dev_index_release(struct net *net, int ifindex)
 /* Delayed registration/unregisteration */
 LIST_HEAD(net_todo_list);
 DECLARE_WAIT_QUEUE_HEAD(netdev_unregistering_wq);
+atomic_t dev_unreg_count = ATOMIC_INIT(0);
 
 static void net_set_todo(struct net_device *dev)
 {
        list_add_tail(&dev->todo_list, &net_todo_list);
-       atomic_inc(&dev_net(dev)->dev_unreg_count);
 }
 
 static netdev_features_t netdev_sync_upper_features(struct net_device *lower,
@@ -10529,6 +10529,7 @@ void netdev_run_todo(void)
 {
        struct net_device *dev, *tmp;
        struct list_head list;
+       int cnt;
 #ifdef CONFIG_LOCKDEP
        struct list_head unlink_list;
 
@@ -10565,6 +10566,7 @@ void netdev_run_todo(void)
                linkwatch_sync_dev(dev);
        }
 
+       cnt = 0;
        while (!list_empty(&list)) {
                dev = netdev_wait_allrefs_any(&list);
                list_del(&dev->todo_list);
@@ -10582,12 +10584,13 @@ void netdev_run_todo(void)
                if (dev->needs_free_netdev)
                        free_netdev(dev);
 
-               if (atomic_dec_and_test(&dev_net(dev)->dev_unreg_count))
-                       wake_up(&netdev_unregistering_wq);
+               cnt++;
 
                /* Free network device */
                kobject_put(&dev->dev.kobj);
        }
+       if (cnt && atomic_sub_and_test(cnt, &dev_unreg_count))
+               wake_up(&netdev_unregistering_wq);
 }
 
 /* Convert net_device_stats to rtnl_link_stats64. rtnl_link_stats64 has
@@ -11034,6 +11037,7 @@ void unregister_netdevice_many_notify(struct list_head *head,
 {
        struct net_device *dev, *tmp;
        LIST_HEAD(close_head);
+       int cnt = 0;
 
        BUG_ON(dev_boot_phase);
        ASSERT_RTNL();
@@ -11130,7 +11134,9 @@ void unregister_netdevice_many_notify(struct list_head *head,
        list_for_each_entry(dev, head, unreg_list) {
                netdev_put(dev, &dev->dev_registered_tracker);
                net_set_todo(dev);
+               cnt++;
        }
+       atomic_add(cnt, &dev_unreg_count);
 
        list_del(head);
 }
index f6f29eb03ec277a1ea17ccc220fa7624bf6db092..31f433950c8dc953bcb65cc0469f7df962314b87 100644 (file)
@@ -483,24 +483,15 @@ EXPORT_SYMBOL_GPL(__rtnl_link_unregister);
  */
 static void rtnl_lock_unregistering_all(void)
 {
-       struct net *net;
-       bool unregistering;
        DEFINE_WAIT_FUNC(wait, woken_wake_function);
 
        add_wait_queue(&netdev_unregistering_wq, &wait);
        for (;;) {
-               unregistering = false;
                rtnl_lock();
                /* We held write locked pernet_ops_rwsem, and parallel
                 * setup_net() and cleanup_net() are not possible.
                 */
-               for_each_net(net) {
-                       if (atomic_read(&net->dev_unreg_count) > 0) {
-                               unregistering = true;
-                               break;
-                       }
-               }
-               if (!unregistering)
+               if (!atomic_read(&dev_unreg_count))
                        break;
                __rtnl_unlock();