blob: 71a35da275d4fd24c2d696b00031ca5836495b46 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/module.h>
15#include <linux/netdevice.h>
16#include <linux/if.h>
17#include <net/sock.h>
Tommy S. Christensencacaddf2005-05-03 16:18:52 -070018#include <net/pkt_sched.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070019#include <linux/rtnetlink.h>
20#include <linux/jiffies.h>
21#include <linux/spinlock.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#include <linux/slab.h>
23#include <linux/workqueue.h>
24#include <linux/bitops.h>
25#include <asm/types.h>
26
27
28enum lw_bits {
29 LW_RUNNING = 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -070030};
31
32static unsigned long linkwatch_flags;
33static unsigned long linkwatch_nextevent;
34
David Howells65f27f32006-11-22 14:55:48 +000035static void linkwatch_event(struct work_struct *dummy);
36static DECLARE_DELAYED_WORK(linkwatch_work, linkwatch_event);
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
Herbert Xu572a1032007-05-08 18:34:17 -070038static struct net_device *lweventlist;
Linus Torvalds1da177e2005-04-16 15:20:36 -070039static DEFINE_SPINLOCK(lweventlist_lock);
40
Stefan Rompfb00055a2006-03-20 17:09:11 -080041static unsigned char default_operstate(const struct net_device *dev)
42{
43 if (!netif_carrier_ok(dev))
44 return (dev->ifindex != dev->iflink ?
45 IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN);
46
47 if (netif_dormant(dev))
48 return IF_OPER_DORMANT;
49
50 return IF_OPER_UP;
51}
52
53
54static void rfc2863_policy(struct net_device *dev)
55{
56 unsigned char operstate = default_operstate(dev);
57
58 if (operstate == dev->operstate)
59 return;
60
61 write_lock_bh(&dev_base_lock);
62
63 switch(dev->link_mode) {
64 case IF_LINK_MODE_DORMANT:
65 if (operstate == IF_OPER_UP)
66 operstate = IF_OPER_DORMANT;
67 break;
68
69 case IF_LINK_MODE_DEFAULT:
70 default:
71 break;
Stephen Hemminger3ff50b72007-04-20 17:09:22 -070072 }
Stefan Rompfb00055a2006-03-20 17:09:11 -080073
74 dev->operstate = operstate;
75
76 write_unlock_bh(&dev_base_lock);
77}
78
79
Linus Torvalds1da177e2005-04-16 15:20:36 -070080/* Must be called with the rtnl semaphore held */
81void linkwatch_run_queue(void)
82{
Herbert Xu572a1032007-05-08 18:34:17 -070083 struct net_device *next;
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
85 spin_lock_irq(&lweventlist_lock);
Herbert Xu572a1032007-05-08 18:34:17 -070086 next = lweventlist;
87 lweventlist = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070088 spin_unlock_irq(&lweventlist_lock);
89
Herbert Xu572a1032007-05-08 18:34:17 -070090 while (next) {
91 struct net_device *dev = next;
Linus Torvalds1da177e2005-04-16 15:20:36 -070092
Herbert Xu572a1032007-05-08 18:34:17 -070093 next = dev->link_watch_next;
94
95 /*
96 * Make sure the above read is complete since it can be
97 * rewritten as soon as we clear the bit below.
98 */
99 smp_mb__before_clear_bit();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100
101 /* We are about to handle this device,
102 * so new events can be accepted
103 */
104 clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);
105
Stefan Rompfb00055a2006-03-20 17:09:11 -0800106 rfc2863_policy(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107 if (dev->flags & IFF_UP) {
Tommy S. Christensencacaddf2005-05-03 16:18:52 -0700108 if (netif_carrier_ok(dev)) {
109 WARN_ON(dev->qdisc_sleeping == &noop_qdisc);
110 dev_activate(dev);
111 } else
112 dev_deactivate(dev);
113
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 netdev_state_change(dev);
115 }
116
117 dev_put(dev);
118 }
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +0900119}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
121
David Howells65f27f32006-11-22 14:55:48 +0000122static void linkwatch_event(struct work_struct *dummy)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123{
124 /* Limit the number of linkwatch events to one
125 * per second so that a runaway driver does not
126 * cause a storm of messages on the netlink
127 * socket
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +0900128 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 linkwatch_nextevent = jiffies + HZ;
130 clear_bit(LW_RUNNING, &linkwatch_flags);
131
Stephen Hemminger6756ae42006-03-20 22:23:58 -0800132 rtnl_lock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133 linkwatch_run_queue();
Stephen Hemminger6756ae42006-03-20 22:23:58 -0800134 rtnl_unlock();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135}
136
137
138void linkwatch_fire_event(struct net_device *dev)
139{
140 if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) {
141 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142
143 dev_hold(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144
145 spin_lock_irqsave(&lweventlist_lock, flags);
Herbert Xu572a1032007-05-08 18:34:17 -0700146 dev->link_watch_next = lweventlist;
147 lweventlist = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148 spin_unlock_irqrestore(&lweventlist_lock, flags);
149
150 if (!test_and_set_bit(LW_RUNNING, &linkwatch_flags)) {
Herbert Xu8c105682006-05-09 15:27:54 -0700151 unsigned long delay = linkwatch_nextevent - jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152
Herbert Xu8c105682006-05-09 15:27:54 -0700153 /* If we wrap around we'll delay it by at most HZ. */
David Howells52bad642006-11-22 14:54:01 +0000154 if (delay > HZ)
155 delay = 0;
156 schedule_delayed_work(&linkwatch_work, delay);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 }
158 }
159}
160
161EXPORT_SYMBOL(linkwatch_fire_event);