aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
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)) {