net: transition netdev reg state earlier in run_todo
authorJakub Kicinski <kuba@kernel.org>
Tue, 15 Feb 2022 22:53:09 +0000 (14:53 -0800)
committerJakub Kicinski <kuba@kernel.org>
Thu, 17 Feb 2022 16:22:17 +0000 (08:22 -0800)
In prep for unregistering netdevs out of order move the netdev
state validation and change outside of the loop.

While at it modernize this code and use WARN() instead of
pr_err() + dump_stack().

Reviewed-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Xin Long <lucien.xin@gmail.com>
Link: https://lore.kernel.org/r/20220215225310.3679266-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/core/dev.c

index 909fb381591084a91f7be2da981d813abb5f4b53..2749776e2dd2b82fc8383add7c61d8c5d66b8c04 100644 (file)
@@ -9906,6 +9906,7 @@ static void netdev_wait_allrefs(struct net_device *dev)
  */
 void netdev_run_todo(void)
 {
+       struct net_device *dev, *tmp;
        struct list_head list;
 #ifdef CONFIG_LOCKDEP
        struct list_head unlink_list;
@@ -9926,24 +9927,23 @@ void netdev_run_todo(void)
 
        __rtnl_unlock();
 
-
        /* Wait for rcu callbacks to finish before next phase */
        if (!list_empty(&list))
                rcu_barrier();
 
-       while (!list_empty(&list)) {
-               struct net_device *dev
-                       = list_first_entry(&list, struct net_device, todo_list);
-               list_del(&dev->todo_list);
-
+       list_for_each_entry_safe(dev, tmp, &list, todo_list) {
                if (unlikely(dev->reg_state != NETREG_UNREGISTERING)) {
-                       pr_err("network todo '%s' but state %d\n",
-                              dev->name, dev->reg_state);
-                       dump_stack();
+                       netdev_WARN(dev, "run_todo but not unregistering\n");
+                       list_del(&dev->todo_list);
                        continue;
                }
 
                dev->reg_state = NETREG_UNREGISTERED;
+       }
+
+       while (!list_empty(&list)) {
+               dev = list_first_entry(&list, struct net_device, todo_list);
+               list_del(&dev->todo_list);
 
                netdev_wait_allrefs(dev);