Blame view

net/core/link_watch.c 5.49 KB
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
1
2
3
4
5
6
7
8
9
10
11
12
  /*
   * Linux network device link state notification
   *
   * Author:
   *     Stefan Rompf <sux@loplof.de>
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License
   * as published by the Free Software Foundation; either version
   * 2 of the License, or (at your option) any later version.
   *
   */
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
13
14
15
16
  #include <linux/module.h>
  #include <linux/netdevice.h>
  #include <linux/if.h>
  #include <net/sock.h>
cacaddf57   Tommy S. Christensen   [NET]: Disable qu...
17
  #include <net/pkt_sched.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
18
19
20
  #include <linux/rtnetlink.h>
  #include <linux/jiffies.h>
  #include <linux/spinlock.h>
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
21
22
23
24
25
26
  #include <linux/workqueue.h>
  #include <linux/bitops.h>
  #include <asm/types.h>
  
  
  enum lw_bits {
d9568ba91   Herbert Xu   [NET] link_watch:...
27
  	LW_URGENT = 0,
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
28
29
30
31
  };
  
  static unsigned long linkwatch_flags;
  static unsigned long linkwatch_nextevent;
65f27f384   David Howells   WorkStruct: Pass ...
32
33
  static void linkwatch_event(struct work_struct *dummy);
  static DECLARE_DELAYED_WORK(linkwatch_work, linkwatch_event);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
34

e014debec   Eric Dumazet   linkwatch: linkwa...
35
  static LIST_HEAD(lweventlist);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
36
  static DEFINE_SPINLOCK(lweventlist_lock);
b00055aac   Stefan Rompf   [NET] core: add R...
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
  static unsigned char default_operstate(const struct net_device *dev)
  {
  	if (!netif_carrier_ok(dev))
  		return (dev->ifindex != dev->iflink ?
  			IF_OPER_LOWERLAYERDOWN : IF_OPER_DOWN);
  
  	if (netif_dormant(dev))
  		return IF_OPER_DORMANT;
  
  	return IF_OPER_UP;
  }
  
  
  static void rfc2863_policy(struct net_device *dev)
  {
  	unsigned char operstate = default_operstate(dev);
  
  	if (operstate == dev->operstate)
  		return;
  
  	write_lock_bh(&dev_base_lock);
  
  	switch(dev->link_mode) {
  	case IF_LINK_MODE_DORMANT:
  		if (operstate == IF_OPER_UP)
  			operstate = IF_OPER_DORMANT;
  		break;
  
  	case IF_LINK_MODE_DEFAULT:
  	default:
  		break;
3ff50b799   Stephen Hemminger   [NET]: cleanup ex...
68
  	}
b00055aac   Stefan Rompf   [NET] core: add R...
69
70
71
72
73
  
  	dev->operstate = operstate;
  
  	write_unlock_bh(&dev_base_lock);
  }
6fa9864b5   David S. Miller   net: Clean up exp...
74
  static bool linkwatch_urgent_event(struct net_device *dev)
294cc44b7   Herbert Xu   [NET]: Remove lin...
75
  {
c37e0c993   Eric Dumazet   net: linkwatch: a...
76
77
78
79
80
81
82
  	if (!netif_running(dev))
  		return false;
  
  	if (dev->ifindex != dev->iflink)
  		return true;
  
  	return netif_carrier_ok(dev) &&	qdisc_tx_changing(dev);
294cc44b7   Herbert Xu   [NET]: Remove lin...
83
84
85
86
87
88
89
90
  }
  
  
  static void linkwatch_add_event(struct net_device *dev)
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&lweventlist_lock, flags);
e014debec   Eric Dumazet   linkwatch: linkwa...
91
92
93
94
  	if (list_empty(&dev->link_watch_list)) {
  		list_add_tail(&dev->link_watch_list, &lweventlist);
  		dev_hold(dev);
  	}
294cc44b7   Herbert Xu   [NET]: Remove lin...
95
96
  	spin_unlock_irqrestore(&lweventlist_lock, flags);
  }
d9568ba91   Herbert Xu   [NET] link_watch:...
97
  static void linkwatch_schedule_work(int urgent)
294cc44b7   Herbert Xu   [NET]: Remove lin...
98
  {
d9568ba91   Herbert Xu   [NET] link_watch:...
99
100
101
  	unsigned long delay = linkwatch_nextevent - jiffies;
  
  	if (test_bit(LW_URGENT, &linkwatch_flags))
294cc44b7   Herbert Xu   [NET]: Remove lin...
102
  		return;
d9568ba91   Herbert Xu   [NET] link_watch:...
103
104
105
106
  	/* Minimise down-time: drop delay for up event. */
  	if (urgent) {
  		if (test_and_set_bit(LW_URGENT, &linkwatch_flags))
  			return;
294cc44b7   Herbert Xu   [NET]: Remove lin...
107
  		delay = 0;
db0ccffed   Herbert Xu   [NET] link_watch:...
108
  	}
294cc44b7   Herbert Xu   [NET]: Remove lin...
109

d9568ba91   Herbert Xu   [NET] link_watch:...
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  	/* If we wrap around we'll delay it by at most HZ. */
  	if (delay > HZ)
  		delay = 0;
  
  	/*
  	 * This is true if we've scheduled it immeditately or if we don't
  	 * need an immediate execution and it's already pending.
  	 */
  	if (schedule_delayed_work(&linkwatch_work, delay) == !delay)
  		return;
  
  	/* Don't bother if there is nothing urgent. */
  	if (!test_bit(LW_URGENT, &linkwatch_flags))
  		return;
  
  	/* It's already running which is good enough. */
1821f7cd6   stephen hemminger   net: allow netif_...
126
  	if (!__cancel_delayed_work(&linkwatch_work))
d9568ba91   Herbert Xu   [NET] link_watch:...
127
  		return;
25985edce   Lucas De Marchi   Fix common misspe...
128
  	/* Otherwise we reschedule it again for immediate execution. */
d9568ba91   Herbert Xu   [NET] link_watch:...
129
  	schedule_delayed_work(&linkwatch_work, 0);
294cc44b7   Herbert Xu   [NET]: Remove lin...
130
  }
e014debec   Eric Dumazet   linkwatch: linkwa...
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
  static void linkwatch_do_dev(struct net_device *dev)
  {
  	/*
  	 * Make sure the above read is complete since it can be
  	 * rewritten as soon as we clear the bit below.
  	 */
  	smp_mb__before_clear_bit();
  
  	/* We are about to handle this device,
  	 * so new events can be accepted
  	 */
  	clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state);
  
  	rfc2863_policy(dev);
  	if (dev->flags & IFF_UP) {
  		if (netif_carrier_ok(dev))
  			dev_activate(dev);
  		else
  			dev_deactivate(dev);
  
  		netdev_state_change(dev);
  	}
  	dev_put(dev);
  }
294cc44b7   Herbert Xu   [NET]: Remove lin...
155
  static void __linkwatch_run_queue(int urgent_only)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
156
  {
e014debec   Eric Dumazet   linkwatch: linkwa...
157
158
  	struct net_device *dev;
  	LIST_HEAD(wrk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
159

294cc44b7   Herbert Xu   [NET]: Remove lin...
160
161
162
163
164
165
166
167
168
  	/*
  	 * Limit the number of linkwatch events to one
  	 * per second so that a runaway driver does not
  	 * cause a storm of messages on the netlink
  	 * socket.  This limit does not apply to up events
  	 * while the device qdisc is down.
  	 */
  	if (!urgent_only)
  		linkwatch_nextevent = jiffies + HZ;
d9568ba91   Herbert Xu   [NET] link_watch:...
169
170
171
172
173
  	/* Limit wrap-around effect on delay. */
  	else if (time_after(linkwatch_nextevent, jiffies + HZ))
  		linkwatch_nextevent = jiffies;
  
  	clear_bit(LW_URGENT, &linkwatch_flags);
294cc44b7   Herbert Xu   [NET]: Remove lin...
174

1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
175
  	spin_lock_irq(&lweventlist_lock);
e014debec   Eric Dumazet   linkwatch: linkwa...
176
  	list_splice_init(&lweventlist, &wrk);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
177

e014debec   Eric Dumazet   linkwatch: linkwa...
178
  	while (!list_empty(&wrk)) {
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
179

e014debec   Eric Dumazet   linkwatch: linkwa...
180
181
  		dev = list_first_entry(&wrk, struct net_device, link_watch_list);
  		list_del_init(&dev->link_watch_list);
572a103de   Herbert Xu   [NET] link_watch:...
182

294cc44b7   Herbert Xu   [NET]: Remove lin...
183
  		if (urgent_only && !linkwatch_urgent_event(dev)) {
e014debec   Eric Dumazet   linkwatch: linkwa...
184
  			list_add_tail(&dev->link_watch_list, &lweventlist);
294cc44b7   Herbert Xu   [NET]: Remove lin...
185
186
  			continue;
  		}
e014debec   Eric Dumazet   linkwatch: linkwa...
187
188
189
  		spin_unlock_irq(&lweventlist_lock);
  		linkwatch_do_dev(dev);
  		spin_lock_irq(&lweventlist_lock);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
190
  	}
294cc44b7   Herbert Xu   [NET]: Remove lin...
191

e014debec   Eric Dumazet   linkwatch: linkwa...
192
  	if (!list_empty(&lweventlist))
d9568ba91   Herbert Xu   [NET] link_watch:...
193
  		linkwatch_schedule_work(0);
e014debec   Eric Dumazet   linkwatch: linkwa...
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
  	spin_unlock_irq(&lweventlist_lock);
  }
  
  void linkwatch_forget_dev(struct net_device *dev)
  {
  	unsigned long flags;
  	int clean = 0;
  
  	spin_lock_irqsave(&lweventlist_lock, flags);
  	if (!list_empty(&dev->link_watch_list)) {
  		list_del_init(&dev->link_watch_list);
  		clean = 1;
  	}
  	spin_unlock_irqrestore(&lweventlist_lock, flags);
  	if (clean)
  		linkwatch_do_dev(dev);
4ec93edb1   YOSHIFUJI Hideaki   [NET] CORE: Fix w...
210
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
211

294cc44b7   Herbert Xu   [NET]: Remove lin...
212
213
  /* Must be called with the rtnl semaphore held */
  void linkwatch_run_queue(void)
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
214
  {
294cc44b7   Herbert Xu   [NET]: Remove lin...
215
216
  	__linkwatch_run_queue(0);
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
217

294cc44b7   Herbert Xu   [NET]: Remove lin...
218
219
  static void linkwatch_event(struct work_struct *dummy)
  {
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
220
  	rtnl_lock();
294cc44b7   Herbert Xu   [NET]: Remove lin...
221
  	__linkwatch_run_queue(time_after(linkwatch_nextevent, jiffies));
6756ae4b4   Stephen Hemminger   [NET]: Convert RT...
222
  	rtnl_unlock();
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
223
224
225
226
227
  }
  
  
  void linkwatch_fire_event(struct net_device *dev)
  {
6fa9864b5   David S. Miller   net: Clean up exp...
228
  	bool urgent = linkwatch_urgent_event(dev);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
229

d9568ba91   Herbert Xu   [NET] link_watch:...
230
  	if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) {
294cc44b7   Herbert Xu   [NET]: Remove lin...
231
  		linkwatch_add_event(dev);
d9568ba91   Herbert Xu   [NET] link_watch:...
232
233
  	} else if (!urgent)
  		return;
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
234

d9568ba91   Herbert Xu   [NET] link_watch:...
235
  	linkwatch_schedule_work(urgent);
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
236
  }
1da177e4c   Linus Torvalds   Linux-2.6.12-rc2
237
  EXPORT_SYMBOL(linkwatch_fire_event);