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