Revert "net: rtnetlink: remove local list in __linkwatch_run_queue()"
authorJohannes Berg <johannes.berg@intel.com>
Fri, 8 Dec 2023 09:52:15 +0000 (10:52 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 11 Dec 2023 10:57:16 +0000 (10:57 +0000)
This reverts commit b8dbbbc535a9 ("net: rtnetlink: remove local list
in __linkwatch_run_queue()"). It's evidently broken when there's a
non-urgent work that gets added back, and then the loop can never
finish.

While reverting, add a note about that.

Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
Fixes: b8dbbbc535a9 ("net: rtnetlink: remove local list in __linkwatch_run_queue()")
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/core/link_watch.c

index 7be5b3ab32bd5c0755885d0aa3c62d4d29326689..429571c258da7720baf387fef81081a56a655ef5 100644 (file)
@@ -192,6 +192,11 @@ static void __linkwatch_run_queue(int urgent_only)
 #define MAX_DO_DEV_PER_LOOP    100
 
        int do_dev = MAX_DO_DEV_PER_LOOP;
+       /* Use a local list here since we add non-urgent
+        * events back to the global one when called with
+        * urgent_only=1.
+        */
+       LIST_HEAD(wrk);
 
        /* Give urgent case more budget */
        if (urgent_only)
@@ -213,11 +218,12 @@ static void __linkwatch_run_queue(int urgent_only)
        clear_bit(LW_URGENT, &linkwatch_flags);
 
        spin_lock_irq(&lweventlist_lock);
-       while (!list_empty(&lweventlist) && do_dev > 0) {
+       list_splice_init(&lweventlist, &wrk);
+
+       while (!list_empty(&wrk) && do_dev > 0) {
                struct net_device *dev;
 
-               dev = list_first_entry(&lweventlist, struct net_device,
-                                      link_watch_list);
+               dev = list_first_entry(&wrk, struct net_device, link_watch_list);
                list_del_init(&dev->link_watch_list);
 
                if (!netif_device_present(dev) ||
@@ -235,6 +241,9 @@ static void __linkwatch_run_queue(int urgent_only)
                spin_lock_irq(&lweventlist_lock);
        }
 
+       /* Add the remaining work back to lweventlist */
+       list_splice_init(&wrk, &lweventlist);
+
        if (!list_empty(&lweventlist))
                linkwatch_schedule_work(0);
        spin_unlock_irq(&lweventlist_lock);