aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2007-05-08 21:34:17 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-05-11 02:45:07 -0400
commit572a103ded0ad880f75ce83e99f0512fbb80b5b0 (patch)
treeb469715be284a13c3f603903cc9158baa7baa992 /net
parentc33be3c362f1bc98f6e2d731a274ef138ae80741 (diff)
[NET] link_watch: Move link watch list into net_device
These days the link watch mechanism is an integral part of the network subsystem as it manages the carrier status. So it now makes sense to allocate some memory for it in net_device rather than allocating it on demand. In fact, this is necessary because we can't tolerate a memory allocation failure since that means we'd have to potentially throw a link up event away. It also simplifies the code greatly. In doing so I discovered a subtle race condition in the use of singleevent. This race condition still exists (and is somewhat magnified) without singleevent but it's now plugged thanks to an smp_mb__before_clear_bit. Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/core/link_watch.c50
1 files changed, 15 insertions, 35 deletions
diff --git a/net/core/link_watch.c b/net/core/link_watch.c
index e3c26a9ccad6..71a35da275d4 100644
--- a/net/core/link_watch.c
+++ b/net/core/link_watch.c
@@ -19,7 +19,6 @@
19#include <linux/rtnetlink.h> 19#include <linux/rtnetlink.h>
20#include <linux/jiffies.h> 20#include <linux/jiffies.h>
21#include <linux/spinlock.h> 21#include <linux/spinlock.h>
22#include <linux/list.h>
23#include <linux/slab.h> 22#include <linux/slab.h>
24#include <linux/workqueue.h> 23#include <linux/workqueue.h>
25#include <linux/bitops.h> 24#include <linux/bitops.h>
@@ -28,7 +27,6 @@
28 27
29enum lw_bits { 28enum lw_bits {
30 LW_RUNNING = 0, 29 LW_RUNNING = 0,
31 LW_SE_USED
32}; 30};
33 31
34static unsigned long linkwatch_flags; 32static unsigned long linkwatch_flags;
@@ -37,17 +35,9 @@ static unsigned long linkwatch_nextevent;
37static void linkwatch_event(struct work_struct *dummy); 35static void linkwatch_event(struct work_struct *dummy);
38static DECLARE_DELAYED_WORK(linkwatch_work, linkwatch_event); 36static DECLARE_DELAYED_WORK(linkwatch_work, linkwatch_event);
39 37
40static LIST_HEAD(lweventlist); 38static struct net_device *lweventlist;
41static DEFINE_SPINLOCK(lweventlist_lock); 39static DEFINE_SPINLOCK(lweventlist_lock);
42 40
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) 41static unsigned char default_operstate(const struct net_device *dev)
52{ 42{
53 if (!netif_carrier_ok(dev)) 43 if (!netif_carrier_ok(dev))
@@ -90,21 +80,23 @@ static void rfc2863_policy(struct net_device *dev)
90/* Must be called with the rtnl semaphore held */ 80/* Must be called with the rtnl semaphore held */
91void linkwatch_run_queue(void) 81void linkwatch_run_queue(void)
92{ 82{
93 struct list_head head, *n, *next; 83 struct net_device *next;
94 84
95 spin_lock_irq(&lweventlist_lock); 85 spin_lock_irq(&lweventlist_lock);
96 list_replace_init(&lweventlist, &head); 86 next = lweventlist;
87 lweventlist = NULL;
97 spin_unlock_irq(&lweventlist_lock); 88 spin_unlock_irq(&lweventlist_lock);
98 89
99 list_for_each_safe(n, next, &head) { 90 while (next) {
100 struct lw_event *event = list_entry(n, struct lw_event, list); 91 struct net_device *dev = next;
101 struct net_device *dev = event->dev;
102 92
103 if (event == &singleevent) { 93 next = dev->link_watch_next;
104 clear_bit(LW_SE_USED, &linkwatch_flags); 94
105 } else { 95 /*
106 kfree(event); 96 * Make sure the above read is complete since it can be
107 } 97 * rewritten as soon as we clear the bit below.
98 */
99 smp_mb__before_clear_bit();
108 100
109 /* We are about to handle this device, 101 /* We are about to handle this device,
110 * so new events can be accepted 102 * so new events can be accepted
@@ -147,24 +139,12 @@ void linkwatch_fire_event(struct net_device *dev)
147{ 139{
148 if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) { 140 if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) {
149 unsigned long flags; 141 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 142
163 dev_hold(dev); 143 dev_hold(dev);
164 event->dev = dev;
165 144
166 spin_lock_irqsave(&lweventlist_lock, flags); 145 spin_lock_irqsave(&lweventlist_lock, flags);
167 list_add_tail(&event->list, &lweventlist); 146 dev->link_watch_next = lweventlist;
147 lweventlist = dev;
168 spin_unlock_irqrestore(&lweventlist_lock, flags); 148 spin_unlock_irqrestore(&lweventlist_lock, flags);
169 149
170 if (!test_and_set_bit(LW_RUNNING, &linkwatch_flags)) { 150 if (!test_and_set_bit(LW_RUNNING, &linkwatch_flags)) {