| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Linux network device link state notification |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Author: |
|---|
| 5 | 6 | * Stefan Rompf <sux@loplof.de> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or |
|---|
| 8 | | - * modify it under the terms of the GNU General Public License |
|---|
| 9 | | - * as published by the Free Software Foundation; either version |
|---|
| 10 | | - * 2 of the License, or (at your option) any later version. |
|---|
| 11 | | - * |
|---|
| 12 | 7 | */ |
|---|
| 13 | 8 | |
|---|
| 14 | 9 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 39 | 34 | |
|---|
| 40 | 35 | static unsigned char default_operstate(const struct net_device *dev) |
|---|
| 41 | 36 | { |
|---|
| 37 | + if (netif_testing(dev)) |
|---|
| 38 | + return IF_OPER_TESTING; |
|---|
| 39 | + |
|---|
| 42 | 40 | if (!netif_carrier_ok(dev)) |
|---|
| 43 | 41 | return (dev->ifindex != dev_get_iflink(dev) ? |
|---|
| 44 | 42 | IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN); |
|---|
| .. | .. |
|---|
| 60 | 58 | write_lock_bh(&dev_base_lock); |
|---|
| 61 | 59 | |
|---|
| 62 | 60 | switch(dev->link_mode) { |
|---|
| 61 | + case IF_LINK_MODE_TESTING: |
|---|
| 62 | + if (operstate == IF_OPER_UP) |
|---|
| 63 | + operstate = IF_OPER_TESTING; |
|---|
| 64 | + break; |
|---|
| 65 | + |
|---|
| 63 | 66 | case IF_LINK_MODE_DORMANT: |
|---|
| 64 | 67 | if (operstate == IF_OPER_UP) |
|---|
| 65 | 68 | operstate = IF_OPER_DORMANT; |
|---|
| 66 | 69 | break; |
|---|
| 67 | | - |
|---|
| 68 | 70 | case IF_LINK_MODE_DEFAULT: |
|---|
| 69 | 71 | default: |
|---|
| 70 | 72 | break; |
|---|
| .. | .. |
|---|
| 79 | 81 | void linkwatch_init_dev(struct net_device *dev) |
|---|
| 80 | 82 | { |
|---|
| 81 | 83 | /* Handle pre-registration link state changes */ |
|---|
| 82 | | - if (!netif_carrier_ok(dev) || netif_dormant(dev)) |
|---|
| 84 | + if (!netif_carrier_ok(dev) || netif_dormant(dev) || |
|---|
| 85 | + netif_testing(dev)) |
|---|
| 83 | 86 | rfc2863_policy(dev); |
|---|
| 84 | 87 | } |
|---|
| 85 | 88 | |
|---|
| .. | .. |
|---|
| 168 | 171 | |
|---|
| 169 | 172 | static void __linkwatch_run_queue(int urgent_only) |
|---|
| 170 | 173 | { |
|---|
| 174 | +#define MAX_DO_DEV_PER_LOOP 100 |
|---|
| 175 | + |
|---|
| 176 | + int do_dev = MAX_DO_DEV_PER_LOOP; |
|---|
| 171 | 177 | struct net_device *dev; |
|---|
| 172 | 178 | LIST_HEAD(wrk); |
|---|
| 179 | + |
|---|
| 180 | + /* Give urgent case more budget */ |
|---|
| 181 | + if (urgent_only) |
|---|
| 182 | + do_dev += MAX_DO_DEV_PER_LOOP; |
|---|
| 173 | 183 | |
|---|
| 174 | 184 | /* |
|---|
| 175 | 185 | * Limit the number of linkwatch events to one |
|---|
| .. | .. |
|---|
| 189 | 199 | spin_lock_irq(&lweventlist_lock); |
|---|
| 190 | 200 | list_splice_init(&lweventlist, &wrk); |
|---|
| 191 | 201 | |
|---|
| 192 | | - while (!list_empty(&wrk)) { |
|---|
| 202 | + while (!list_empty(&wrk) && do_dev > 0) { |
|---|
| 193 | 203 | |
|---|
| 194 | 204 | dev = list_first_entry(&wrk, struct net_device, link_watch_list); |
|---|
| 195 | 205 | list_del_init(&dev->link_watch_list); |
|---|
| 196 | 206 | |
|---|
| 197 | | - if (urgent_only && !linkwatch_urgent_event(dev)) { |
|---|
| 207 | + if (!netif_device_present(dev) || |
|---|
| 208 | + (urgent_only && !linkwatch_urgent_event(dev))) { |
|---|
| 198 | 209 | list_add_tail(&dev->link_watch_list, &lweventlist); |
|---|
| 199 | 210 | continue; |
|---|
| 200 | 211 | } |
|---|
| 201 | 212 | spin_unlock_irq(&lweventlist_lock); |
|---|
| 202 | 213 | linkwatch_do_dev(dev); |
|---|
| 214 | + do_dev--; |
|---|
| 203 | 215 | spin_lock_irq(&lweventlist_lock); |
|---|
| 204 | 216 | } |
|---|
| 205 | 217 | |
|---|
| 218 | + /* Add the remaining work back to lweventlist */ |
|---|
| 219 | + list_splice_init(&wrk, &lweventlist); |
|---|
| 220 | + |
|---|
| 206 | 221 | if (!list_empty(&lweventlist)) |
|---|
| 207 | 222 | linkwatch_schedule_work(0); |
|---|
| 208 | 223 | spin_unlock_irq(&lweventlist_lock); |
|---|