diff options
| -rw-r--r-- | include/linux/netdevice.h | 3 | ||||
| -rw-r--r-- | net/core/dev.c | 3 | ||||
| -rw-r--r-- | net/core/link_watch.c | 94 |
3 files changed, 62 insertions, 38 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c8fa4627de00..97873e31661c 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
| @@ -896,7 +896,7 @@ struct net_device { | |||
| 896 | /* device index hash chain */ | 896 | /* device index hash chain */ |
| 897 | struct hlist_node index_hlist; | 897 | struct hlist_node index_hlist; |
| 898 | 898 | ||
| 899 | struct net_device *link_watch_next; | 899 | struct list_head link_watch_list; |
| 900 | 900 | ||
| 901 | /* register/unregister state machine */ | 901 | /* register/unregister state machine */ |
| 902 | enum { NETREG_UNINITIALIZED=0, | 902 | enum { NETREG_UNINITIALIZED=0, |
| @@ -1600,6 +1600,7 @@ static inline void dev_hold(struct net_device *dev) | |||
| 1600 | */ | 1600 | */ |
| 1601 | 1601 | ||
| 1602 | extern void linkwatch_fire_event(struct net_device *dev); | 1602 | extern void linkwatch_fire_event(struct net_device *dev); |
| 1603 | extern void linkwatch_forget_dev(struct net_device *dev); | ||
| 1603 | 1604 | ||
| 1604 | /** | 1605 | /** |
| 1605 | * netif_carrier_ok - test if carrier present | 1606 | * netif_carrier_ok - test if carrier present |
diff --git a/net/core/dev.c b/net/core/dev.c index e25fe5d9343b..c128af708ebf 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
| @@ -5085,6 +5085,8 @@ static void netdev_wait_allrefs(struct net_device *dev) | |||
| 5085 | { | 5085 | { |
| 5086 | unsigned long rebroadcast_time, warning_time; | 5086 | unsigned long rebroadcast_time, warning_time; |
| 5087 | 5087 | ||
| 5088 | linkwatch_forget_dev(dev); | ||
| 5089 | |||
| 5088 | rebroadcast_time = warning_time = jiffies; | 5090 | rebroadcast_time = warning_time = jiffies; |
| 5089 | while (atomic_read(&dev->refcnt) != 0) { | 5091 | while (atomic_read(&dev->refcnt) != 0) { |
| 5090 | if (time_after(jiffies, rebroadcast_time + 1 * HZ)) { | 5092 | if (time_after(jiffies, rebroadcast_time + 1 * HZ)) { |
| @@ -5311,6 +5313,7 @@ struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, | |||
| 5311 | 5313 | ||
| 5312 | INIT_LIST_HEAD(&dev->napi_list); | 5314 | INIT_LIST_HEAD(&dev->napi_list); |
| 5313 | INIT_LIST_HEAD(&dev->unreg_list); | 5315 | INIT_LIST_HEAD(&dev->unreg_list); |
| 5316 | INIT_LIST_HEAD(&dev->link_watch_list); | ||
| 5314 | dev->priv_flags = IFF_XMIT_DST_RELEASE; | 5317 | dev->priv_flags = IFF_XMIT_DST_RELEASE; |
| 5315 | setup(dev); | 5318 | setup(dev); |
| 5316 | strcpy(dev->name, name); | 5319 | strcpy(dev->name, name); |
diff --git a/net/core/link_watch.c b/net/core/link_watch.c index bf8f7af699d7..5910b555a54a 100644 --- a/net/core/link_watch.c +++ b/net/core/link_watch.c | |||
| @@ -35,7 +35,7 @@ static unsigned long linkwatch_nextevent; | |||
| 35 | static void linkwatch_event(struct work_struct *dummy); | 35 | static void linkwatch_event(struct work_struct *dummy); |
| 36 | static DECLARE_DELAYED_WORK(linkwatch_work, linkwatch_event); | 36 | static DECLARE_DELAYED_WORK(linkwatch_work, linkwatch_event); |
| 37 | 37 | ||
| 38 | static struct net_device *lweventlist; | 38 | static LIST_HEAD(lweventlist); |
| 39 | static DEFINE_SPINLOCK(lweventlist_lock); | 39 | static DEFINE_SPINLOCK(lweventlist_lock); |
| 40 | 40 | ||
| 41 | static unsigned char default_operstate(const struct net_device *dev) | 41 | static unsigned char default_operstate(const struct net_device *dev) |
| @@ -89,8 +89,10 @@ static void linkwatch_add_event(struct net_device *dev) | |||
| 89 | unsigned long flags; | 89 | unsigned long flags; |
| 90 | 90 | ||
| 91 | spin_lock_irqsave(&lweventlist_lock, flags); | 91 | spin_lock_irqsave(&lweventlist_lock, flags); |
| 92 | dev->link_watch_next = lweventlist; | 92 | if (list_empty(&dev->link_watch_list)) { |
| 93 | lweventlist = dev; | 93 | list_add_tail(&dev->link_watch_list, &lweventlist); |
| 94 | dev_hold(dev); | ||
| 95 | } | ||
| 94 | spin_unlock_irqrestore(&lweventlist_lock, flags); | 96 | spin_unlock_irqrestore(&lweventlist_lock, flags); |
| 95 | } | 97 | } |
| 96 | 98 | ||
| @@ -133,9 +135,35 @@ static void linkwatch_schedule_work(int urgent) | |||
| 133 | } | 135 | } |
| 134 | 136 | ||
| 135 | 137 | ||
| 138 | static void linkwatch_do_dev(struct net_device *dev) | ||
| 139 | { | ||
| 140 | /* | ||
| 141 | * Make sure the above read is complete since it can be | ||
| 142 | * rewritten as soon as we clear the bit below. | ||
| 143 | */ | ||
| 144 | smp_mb__before_clear_bit(); | ||
| 145 | |||
| 146 | /* We are about to handle this device, | ||
| 147 | * so new events can be accepted | ||
| 148 | */ | ||
| 149 | clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state); | ||
| 150 | |||
| 151 | rfc2863_policy(dev); | ||
| 152 | if (dev->flags & IFF_UP) { | ||
| 153 | if (netif_carrier_ok(dev)) | ||
| 154 | dev_activate(dev); | ||
| 155 | else | ||
| 156 | dev_deactivate(dev); | ||
| 157 | |||
| 158 | netdev_state_change(dev); | ||
| 159 | } | ||
| 160 | dev_put(dev); | ||
| 161 | } | ||
| 162 | |||
| 136 | static void __linkwatch_run_queue(int urgent_only) | 163 | static void __linkwatch_run_queue(int urgent_only) |
| 137 | { | 164 | { |
| 138 | struct net_device *next; | 165 | struct net_device *dev; |
| 166 | LIST_HEAD(wrk); | ||
| 139 | 167 | ||
| 140 | /* | 168 | /* |
| 141 | * Limit the number of linkwatch events to one | 169 | * Limit the number of linkwatch events to one |
| @@ -153,46 +181,40 @@ static void __linkwatch_run_queue(int urgent_only) | |||
| 153 | clear_bit(LW_URGENT, &linkwatch_flags); | 181 | clear_bit(LW_URGENT, &linkwatch_flags); |
| 154 | 182 | ||
| 155 | spin_lock_irq(&lweventlist_lock); | 183 | spin_lock_irq(&lweventlist_lock); |
| 156 | next = lweventlist; | 184 | list_splice_init(&lweventlist, &wrk); |
| 157 | lweventlist = NULL; | ||
| 158 | spin_unlock_irq(&lweventlist_lock); | ||
| 159 | 185 | ||
| 160 | while (next) { | 186 | while (!list_empty(&wrk)) { |
| 161 | struct net_device *dev = next; | ||
| 162 | 187 | ||
| 163 | next = dev->link_watch_next; | 188 | dev = list_first_entry(&wrk, struct net_device, link_watch_list); |
| 189 | list_del_init(&dev->link_watch_list); | ||
| 164 | 190 | ||
| 165 | if (urgent_only && !linkwatch_urgent_event(dev)) { | 191 | if (urgent_only && !linkwatch_urgent_event(dev)) { |
| 166 | linkwatch_add_event(dev); | 192 | list_add_tail(&dev->link_watch_list, &lweventlist); |
| 167 | continue; | 193 | continue; |
| 168 | } | 194 | } |
| 169 | 195 | spin_unlock_irq(&lweventlist_lock); | |
| 170 | /* | 196 | linkwatch_do_dev(dev); |
| 171 | * Make sure the above read is complete since it can be | 197 | spin_lock_irq(&lweventlist_lock); |
| 172 | * rewritten as soon as we clear the bit below. | ||
| 173 | */ | ||
| 174 | smp_mb__before_clear_bit(); | ||
| 175 | |||
| 176 | /* We are about to handle this device, | ||
| 177 | * so new events can be accepted | ||
| 178 | */ | ||
| 179 | clear_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state); | ||
| 180 | |||
| 181 | rfc2863_policy(dev); | ||
| 182 | if (dev->flags & IFF_UP) { | ||
| 183 | if (netif_carrier_ok(dev)) | ||
| 184 | dev_activate(dev); | ||
| 185 | else | ||
| 186 | dev_deactivate(dev); | ||
| 187 | |||
| 188 | netdev_state_change(dev); | ||
| 189 | } | ||
| 190 | |||
| 191 | dev_put(dev); | ||
| 192 | } | 198 | } |
| 193 | 199 | ||
| 194 | if (lweventlist) | 200 | if (!list_empty(&lweventlist)) |
| 195 | linkwatch_schedule_work(0); | 201 | linkwatch_schedule_work(0); |
| 202 | spin_unlock_irq(&lweventlist_lock); | ||
| 203 | } | ||
| 204 | |||
| 205 | void linkwatch_forget_dev(struct net_device *dev) | ||
| 206 | { | ||
| 207 | unsigned long flags; | ||
| 208 | int clean = 0; | ||
| 209 | |||
| 210 | spin_lock_irqsave(&lweventlist_lock, flags); | ||
| 211 | if (!list_empty(&dev->link_watch_list)) { | ||
| 212 | list_del_init(&dev->link_watch_list); | ||
| 213 | clean = 1; | ||
| 214 | } | ||
| 215 | spin_unlock_irqrestore(&lweventlist_lock, flags); | ||
| 216 | if (clean) | ||
| 217 | linkwatch_do_dev(dev); | ||
| 196 | } | 218 | } |
| 197 | 219 | ||
| 198 | 220 | ||
| @@ -216,8 +238,6 @@ void linkwatch_fire_event(struct net_device *dev) | |||
| 216 | bool urgent = linkwatch_urgent_event(dev); | 238 | bool urgent = linkwatch_urgent_event(dev); |
| 217 | 239 | ||
| 218 | if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) { | 240 | if (!test_and_set_bit(__LINK_STATE_LINKWATCH_PENDING, &dev->state)) { |
| 219 | dev_hold(dev); | ||
| 220 | |||
| 221 | linkwatch_add_event(dev); | 241 | linkwatch_add_event(dev); |
| 222 | } else if (!urgent) | 242 | } else if (!urgent) |
| 223 | return; | 243 | return; |
