~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/net/core/link_watch.c

Version: ~ [ linux-6.4-rc3 ] ~ [ linux-6.3.4 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.30 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.113 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.180 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.243 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.283 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.315 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 // SPDX-License-Identifier: GPL-2.0-or-later
  2 /*
  3  * Linux network device link state notification
  4  *
  5  * Author:
  6  *     Stefan Rompf <sux@loplof.de>
  7  */
  8 
  9 #include <linux/module.h>
 10 #include <linux/netdevice.h>
 11 #include <linux/if.h>
 12 #include <net/sock.h>
 13 #include <net/pkt_sched.h>
 14 #include <linux/rtnetlink.h>
 15 #include <linux/jiffies.h>
 16 #include <linux/spinlock.h>
 17 #include <linux/workqueue.h>
 18 #include <linux/bitops.h>
 19 #include <linux/types.h>
 20 
 21 #include "dev.h"
 22 
 23 enum lw_bits {
 24         LW_URGENT = 0,
 25 };
 26 
 27 static unsigned long linkwatch_flags;
 28 static unsigned long linkwatch_nextevent;
 29 
 30 static void linkwatch_event(struct work_struct *dummy);
 31 static DECLARE_DELAYED_WORK(linkwatch_work, linkwatch_event);
 32 
 33 static LIST_HEAD(lweventlist);
 34 static DEFINE_SPINLOCK(lweventlist_lock);
 35 
 36 static unsigned char default_operstate(const struct net_device *dev)
 37 {
 38         if (netif_testing(dev))
 39                 return IF_OPER_TESTING;
 40 
 41         if (!netif_carrier_ok(dev))
 42                 return (dev->ifindex != dev_get_iflink(dev) ?
 43                         IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN);
 44 
 45         if (netif_dormant(dev))
 46                 return IF_OPER_DORMANT;
 47 
 48         return IF_OPER_UP;
 49 }
 50 
 51 
 52 static void rfc2863_policy(struct net_device *dev)
 53 {
 54         unsigned char operstate = default_operstate(dev);
 55 
 56         if (operstate == dev->operstate)
 57                 return;
 58 
 59         write_lock(&dev_base_lock);
 60 
 61         switch(dev->link_mode) {
 62         case IF_LINK_MODE_TESTING:
 63                 if (operstate == IF_OPER_UP)
 64                         operstate = IF_OPER_TESTING;
 65                 break;
 66 
 67         case IF_LINK_MODE_DORMANT:
 68                 if (operstate == IF_OPER_UP)
 69                         operstate = IF_OPER_DORMANT;
 70                 break;
 71         case IF_LINK_MODE_DEFAULT:
 72         default:
 73                 break;
 74         }
 75 
 76         dev->operstate = operstate;
 77 
 78         write_unlock(&dev_base_lock);
 79 }
 80 
 81 
 82 void linkwatch_init_dev(struct net_device *dev)
 83 {
 84         /* Handle pre-registration link state changes */
 85         if (!netif_carrier_ok(dev) || netif_dormant(dev) ||
 86             netif_testing(dev))
 87                 rfc2863_policy(dev);
 88 }
 89 
 90 
 91 static bool linkwatch_urgent_event(struct net_device *dev)
 92 {
 93         if (!netif_running(dev))
 94                 return false;
 95 
 96         if (dev->ifindex != dev_get_iflink(dev))
 97                 return true;
 98 
 99         if (netif_is_lag_port(dev) || netif_is_lag_master(dev))
100                 return true;
101 
102         return netif_carrier_ok(dev) && qdisc_tx_changing(dev);
103 }
104 
105 
106 static void linkwatch_add_event(struct net_device *dev)
107 {
108         unsigned long flags;
109 
110         spin_lock_irqsave(&lweventlist_lock, flags);
111         if (list_empty(&dev->link_watch_list)) {
112                 list_add_tail(&dev->link_watch_list, &lweventlist);
113                 netdev_hold(dev, &dev->linkwatch_dev_tracker, GFP_ATOMIC);
114         }
115         spin_unlock_irqrestore(&lweventlist_lock, flags);
116 }
117 
118 
119 static void linkwatch_schedule_work(int urgent)
120 {
121         unsigned long delay = linkwatch_nextevent - jiffies;
122 
123         if (test_bit(LW_URGENT, &linkwatch_flags))
124                 return;
125 
126         /* Minimise down-time: drop delay for up event. */
127         if (urgent) {
128                 if (test_and_set_bit(LW_URGENT, &linkwatch_flags))
129                         return;
130                 delay = 0;
131         }
132 
133         /* If we wrap around we'll delay it by at most HZ. */
134         if (delay > HZ)
135                 delay = 0;
136 
137         /*
138          * If urgent, schedule immediate execution; otherwise, don't
139          * override the existing timer.
140          */
141         if (test_bit(LW_URGENT, &linkwatch_flags))
142                 mod_delayed_work(system_wq, &linkwatch_work, 0);
143         else
144                 schedule_delayed_work(&linkwatch_work, delay);
145 }
146 
147 
148 static void linkwatch_do_dev(struct net_device *dev)
149 {
150         /*
151          * Make sure the above read is complete since it can be
152          * rewritten as soon as we clear the bit below.
153          */
154         smp_mb__before_atomic();
155 
156         /* We are about to handle this device,
157          * so new events can be accepted
158          */
159         clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);
160 
161         rfc2863_policy(dev);
162         if (dev->flags & IFF_UP) {
163                 if (netif_carrier_ok(dev))
164                         dev_activate(dev);
165                 else
166                         dev_deactivate(dev);
167 
168                 netdev_state_change(dev);
169         }
170         /* Note: our callers are responsible for calling netdev_tracker_free().
171          * This is the reason we use __dev_put() instead of dev_put().
172          */
173         __dev_put(dev);
174 }
175 
176 static void __linkwatch_run_queue(int urgent_only)
177 {
178 #define MAX_DO_DEV_PER_LOOP     100
179 
180         int do_dev = MAX_DO_DEV_PER_LOOP;
181         struct net_device *dev;
182         LIST_HEAD(wrk);
183 
184         /* Give urgent case more budget */
185         if (urgent_only)
186                 do_dev += MAX_DO_DEV_PER_LOOP;
187 
188         /*
189          * Limit the number of linkwatch events to one
190          * per second so that a runaway driver does not
191          * cause a storm of messages on the netlink
192          * socket.  This limit does not apply to up events
193          * while the device qdisc is down.
194          */
195         if (!urgent_only)
196                 linkwatch_nextevent = jiffies + HZ;
197         /* Limit wrap-around effect on delay. */
198         else if (time_after(linkwatch_nextevent, jiffies + HZ))
199                 linkwatch_nextevent = jiffies;
200 
201         clear_bit(LW_URGENT, &linkwatch_flags);
202 
203         spin_lock_irq(&lweventlist_lock);
204         list_splice_init(&lweventlist, &wrk);
205 
206         while (!list_empty(&wrk) && do_dev > 0) {
207 
208                 dev = list_first_entry(&wrk, struct net_device, link_watch_list);
209                 list_del_init(&dev->link_watch_list);
210 
211                 if (!netif_device_present(dev) ||
212                     (urgent_only && !linkwatch_urgent_event(dev))) {
213                         list_add_tail(&dev->link_watch_list, &lweventlist);
214                         continue;
215                 }
216                 /* We must free netdev tracker under
217                  * the spinlock protection.
218                  */
219                 netdev_tracker_free(dev, &dev->linkwatch_dev_tracker);
220                 spin_unlock_irq(&lweventlist_lock);
221                 linkwatch_do_dev(dev);
222                 do_dev--;
223                 spin_lock_irq(&lweventlist_lock);
224         }
225 
226         /* Add the remaining work back to lweventlist */
227         list_splice_init(&wrk, &lweventlist);
228 
229         if (!list_empty(&lweventlist))
230                 linkwatch_schedule_work(0);
231         spin_unlock_irq(&lweventlist_lock);
232 }
233 
234 void linkwatch_forget_dev(struct net_device *dev)
235 {
236         unsigned long flags;
237         int clean = 0;
238 
239         spin_lock_irqsave(&lweventlist_lock, flags);
240         if (!list_empty(&dev->link_watch_list)) {
241                 list_del_init(&dev->link_watch_list);
242                 clean = 1;
243                 /* We must release netdev tracker under
244                  * the spinlock protection.
245                  */
246                 netdev_tracker_free(dev, &dev->linkwatch_dev_tracker);
247         }
248         spin_unlock_irqrestore(&lweventlist_lock, flags);
249         if (clean)
250                 linkwatch_do_dev(dev);
251 }
252 
253 
254 /* Must be called with the rtnl semaphore held */
255 void linkwatch_run_queue(void)
256 {
257         __linkwatch_run_queue(0);
258 }
259 
260 
261 static void linkwatch_event(struct work_struct *dummy)
262 {
263         rtnl_lock();
264         __linkwatch_run_queue(time_after(linkwatch_nextevent, jiffies));
265         rtnl_unlock();
266 }
267 
268 
269 void linkwatch_fire_event(struct net_device *dev)
270 {
271         bool urgent = linkwatch_urgent_event(dev);
272 
273         if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) {
274                 linkwatch_add_event(dev);
275         } else if (!urgent)
276                 return;
277 
278         linkwatch_schedule_work(urgent);
279 }
280 EXPORT_SYMBOL(linkwatch_fire_event);
281 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp