Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 03f981cf2ec95dd8bc43d2ecccaec4e83c8375e2 181 lines 4.0 kB view raw
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/list.h> 23#include <linux/slab.h> 24#include <linux/workqueue.h> 25#include <linux/bitops.h> 26#include <asm/types.h> 27 28 29enum lw_bits { 30 LW_RUNNING = 0, 31 LW_SE_USED 32}; 33 34static unsigned long linkwatch_flags; 35static unsigned long linkwatch_nextevent; 36 37static void linkwatch_event(struct work_struct *dummy); 38static DECLARE_DELAYED_WORK(linkwatch_work, linkwatch_event); 39 40static LIST_HEAD(lweventlist); 41static DEFINE_SPINLOCK(lweventlist_lock); 42 43struct lw_event { 44 struct list_head list; 45 struct net_device *dev; 46}; 47 48/* Avoid kmalloc() for most systems */ 49static struct lw_event singleevent; 50 51static unsigned char default_operstate(const struct net_device *dev) 52{ 53 if (!netif_carrier_ok(dev)) 54 return (dev->ifindex != dev->iflink ? 55 IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN); 56 57 if (netif_dormant(dev)) 58 return IF_OPER_DORMANT; 59 60 return IF_OPER_UP; 61} 62 63 64static void rfc2863_policy(struct net_device *dev) 65{ 66 unsigned char operstate = default_operstate(dev); 67 68 if (operstate == dev->operstate) 69 return; 70 71 write_lock_bh(&dev_base_lock); 72 73 switch(dev->link_mode) { 74 case IF_LINK_MODE_DORMANT: 75 if (operstate == IF_OPER_UP) 76 operstate = IF_OPER_DORMANT; 77 break; 78 79 case IF_LINK_MODE_DEFAULT: 80 default: 81 break; 82 }; 83 84 dev->operstate = operstate; 85 86 write_unlock_bh(&dev_base_lock); 87} 88 89 90/* Must be called with the rtnl semaphore held */ 91void linkwatch_run_queue(void) 92{ 93 struct list_head head, *n, *next; 94 95 spin_lock_irq(&lweventlist_lock); 96 list_replace_init(&lweventlist, &head); 97 spin_unlock_irq(&lweventlist_lock); 98 99 list_for_each_safe(n, next, &head) { 100 struct lw_event *event = list_entry(n, struct lw_event, list); 101 struct net_device *dev = event->dev; 102 103 if (event == &singleevent) { 104 clear_bit(LW_SE_USED, &linkwatch_flags); 105 } else { 106 kfree(event); 107 } 108 109 /* We are about to handle this device, 110 * so new events can be accepted 111 */ 112 clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state); 113 114 rfc2863_policy(dev); 115 if (dev->flags & IFF_UP) { 116 if (netif_carrier_ok(dev)) { 117 WARN_ON(dev->qdisc_sleeping == &noop_qdisc); 118 dev_activate(dev); 119 } else 120 dev_deactivate(dev); 121 122 netdev_state_change(dev); 123 } 124 125 dev_put(dev); 126 } 127} 128 129 130static void linkwatch_event(struct work_struct *dummy) 131{ 132 /* Limit the number of linkwatch events to one 133 * per second so that a runaway driver does not 134 * cause a storm of messages on the netlink 135 * socket 136 */ 137 linkwatch_nextevent = jiffies + HZ; 138 clear_bit(LW_RUNNING, &linkwatch_flags); 139 140 rtnl_lock(); 141 linkwatch_run_queue(); 142 rtnl_unlock(); 143} 144 145 146void linkwatch_fire_event(struct net_device *dev) 147{ 148 if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) { 149 unsigned long flags; 150 struct lw_event *event; 151 152 if (test_and_set_bit(LW_SE_USED, &linkwatch_flags)) { 153 event = kmalloc(sizeof(struct lw_event), GFP_ATOMIC); 154 155 if (unlikely(event == NULL)) { 156 clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state); 157 return; 158 } 159 } else { 160 event = &singleevent; 161 } 162 163 dev_hold(dev); 164 event->dev = dev; 165 166 spin_lock_irqsave(&lweventlist_lock, flags); 167 list_add_tail(&event->list, &lweventlist); 168 spin_unlock_irqrestore(&lweventlist_lock, flags); 169 170 if (!test_and_set_bit(LW_RUNNING, &linkwatch_flags)) { 171 unsigned long delay = linkwatch_nextevent - jiffies; 172 173 /* If we wrap around we'll delay it by at most HZ. */ 174 if (delay > HZ) 175 delay = 0; 176 schedule_delayed_work(&linkwatch_work, delay); 177 } 178 } 179} 180 181EXPORT_SYMBOL(linkwatch_fire_event);