diff options
author | David Woodhouse <dwmw2@infradead.org> | 2012-11-27 18:28:36 -0500 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2012-12-01 19:05:14 -0500 |
commit | b89588531f1db93a218c108eee713ff6002a67bf (patch) | |
tree | 074d0b433a381f95325082c8d9f0ba10a835b517 /net/atm | |
parent | c971f08cba56ed17fe22040ca5ff97fe5c3f0bd7 (diff) |
br2684: don't send frames on not-ready vcc
Avoid submitting packets to a vcc which is being closed. Things go badly
wrong when the ->pop method gets later called after everything's been
torn down.
Use the ATM socket lock for synchronisation with vcc_destroy_socket(),
which clears the ATM_VF_READY bit under the same lock. Otherwise, we
could end up submitting a packet to the device driver even after its
->ops->close method has been called. And it could call the vcc's ->pop
method after the protocol has been shut down. Which leads to a panic.
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Acked-by: Krzysztof Mazur <krzysiek@podlesie.net>
Diffstat (limited to 'net/atm')
-rw-r--r-- | net/atm/br2684.c | 43 |
1 files changed, 40 insertions, 3 deletions
diff --git a/net/atm/br2684.c b/net/atm/br2684.c index 8eb6fbe8d8dd..4de3ae7bc3e2 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c | |||
@@ -68,6 +68,7 @@ struct br2684_vcc { | |||
68 | /* keep old push, pop functions for chaining */ | 68 | /* keep old push, pop functions for chaining */ |
69 | void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb); | 69 | void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb); |
70 | void (*old_pop)(struct atm_vcc *vcc, struct sk_buff *skb); | 70 | void (*old_pop)(struct atm_vcc *vcc, struct sk_buff *skb); |
71 | void (*old_release_cb)(struct atm_vcc *vcc); | ||
71 | enum br2684_encaps encaps; | 72 | enum br2684_encaps encaps; |
72 | struct list_head brvccs; | 73 | struct list_head brvccs; |
73 | #ifdef CONFIG_ATM_BR2684_IPFILTER | 74 | #ifdef CONFIG_ATM_BR2684_IPFILTER |
@@ -269,6 +270,17 @@ static int br2684_xmit_vcc(struct sk_buff *skb, struct net_device *dev, | |||
269 | return !atmvcc->send(atmvcc, skb); | 270 | return !atmvcc->send(atmvcc, skb); |
270 | } | 271 | } |
271 | 272 | ||
273 | static void br2684_release_cb(struct atm_vcc *atmvcc) | ||
274 | { | ||
275 | struct br2684_vcc *brvcc = BR2684_VCC(atmvcc); | ||
276 | |||
277 | if (atomic_read(&brvcc->qspace) > 0) | ||
278 | netif_wake_queue(brvcc->device); | ||
279 | |||
280 | if (brvcc->old_release_cb) | ||
281 | brvcc->old_release_cb(atmvcc); | ||
282 | } | ||
283 | |||
272 | static inline struct br2684_vcc *pick_outgoing_vcc(const struct sk_buff *skb, | 284 | static inline struct br2684_vcc *pick_outgoing_vcc(const struct sk_buff *skb, |
273 | const struct br2684_dev *brdev) | 285 | const struct br2684_dev *brdev) |
274 | { | 286 | { |
@@ -280,6 +292,8 @@ static netdev_tx_t br2684_start_xmit(struct sk_buff *skb, | |||
280 | { | 292 | { |
281 | struct br2684_dev *brdev = BRPRIV(dev); | 293 | struct br2684_dev *brdev = BRPRIV(dev); |
282 | struct br2684_vcc *brvcc; | 294 | struct br2684_vcc *brvcc; |
295 | struct atm_vcc *atmvcc; | ||
296 | netdev_tx_t ret = NETDEV_TX_OK; | ||
283 | 297 | ||
284 | pr_debug("skb_dst(skb)=%p\n", skb_dst(skb)); | 298 | pr_debug("skb_dst(skb)=%p\n", skb_dst(skb)); |
285 | read_lock(&devs_lock); | 299 | read_lock(&devs_lock); |
@@ -290,9 +304,26 @@ static netdev_tx_t br2684_start_xmit(struct sk_buff *skb, | |||
290 | dev->stats.tx_carrier_errors++; | 304 | dev->stats.tx_carrier_errors++; |
291 | /* netif_stop_queue(dev); */ | 305 | /* netif_stop_queue(dev); */ |
292 | dev_kfree_skb(skb); | 306 | dev_kfree_skb(skb); |
293 | read_unlock(&devs_lock); | 307 | goto out_devs; |
294 | return NETDEV_TX_OK; | ||
295 | } | 308 | } |
309 | atmvcc = brvcc->atmvcc; | ||
310 | |||
311 | bh_lock_sock(sk_atm(atmvcc)); | ||
312 | |||
313 | if (test_bit(ATM_VF_RELEASED, &atmvcc->flags) || | ||
314 | test_bit(ATM_VF_CLOSE, &atmvcc->flags) || | ||
315 | !test_bit(ATM_VF_READY, &atmvcc->flags)) { | ||
316 | dev->stats.tx_dropped++; | ||
317 | dev_kfree_skb(skb); | ||
318 | goto out; | ||
319 | } | ||
320 | |||
321 | if (sock_owned_by_user(sk_atm(atmvcc))) { | ||
322 | netif_stop_queue(brvcc->device); | ||
323 | ret = NETDEV_TX_BUSY; | ||
324 | goto out; | ||
325 | } | ||
326 | |||
296 | if (!br2684_xmit_vcc(skb, dev, brvcc)) { | 327 | if (!br2684_xmit_vcc(skb, dev, brvcc)) { |
297 | /* | 328 | /* |
298 | * We should probably use netif_*_queue() here, but that | 329 | * We should probably use netif_*_queue() here, but that |
@@ -304,8 +335,11 @@ static netdev_tx_t br2684_start_xmit(struct sk_buff *skb, | |||
304 | dev->stats.tx_errors++; | 335 | dev->stats.tx_errors++; |
305 | dev->stats.tx_fifo_errors++; | 336 | dev->stats.tx_fifo_errors++; |
306 | } | 337 | } |
338 | out: | ||
339 | bh_unlock_sock(sk_atm(atmvcc)); | ||
340 | out_devs: | ||
307 | read_unlock(&devs_lock); | 341 | read_unlock(&devs_lock); |
308 | return NETDEV_TX_OK; | 342 | return ret; |
309 | } | 343 | } |
310 | 344 | ||
311 | /* | 345 | /* |
@@ -378,6 +412,7 @@ static void br2684_close_vcc(struct br2684_vcc *brvcc) | |||
378 | list_del(&brvcc->brvccs); | 412 | list_del(&brvcc->brvccs); |
379 | write_unlock_irq(&devs_lock); | 413 | write_unlock_irq(&devs_lock); |
380 | brvcc->atmvcc->user_back = NULL; /* what about vcc->recvq ??? */ | 414 | brvcc->atmvcc->user_back = NULL; /* what about vcc->recvq ??? */ |
415 | brvcc->atmvcc->release_cb = brvcc->old_release_cb; | ||
381 | brvcc->old_push(brvcc->atmvcc, NULL); /* pass on the bad news */ | 416 | brvcc->old_push(brvcc->atmvcc, NULL); /* pass on the bad news */ |
382 | kfree(brvcc); | 417 | kfree(brvcc); |
383 | module_put(THIS_MODULE); | 418 | module_put(THIS_MODULE); |
@@ -554,9 +589,11 @@ static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg) | |||
554 | brvcc->encaps = (enum br2684_encaps)be.encaps; | 589 | brvcc->encaps = (enum br2684_encaps)be.encaps; |
555 | brvcc->old_push = atmvcc->push; | 590 | brvcc->old_push = atmvcc->push; |
556 | brvcc->old_pop = atmvcc->pop; | 591 | brvcc->old_pop = atmvcc->pop; |
592 | brvcc->old_release_cb = atmvcc->release_cb; | ||
557 | barrier(); | 593 | barrier(); |
558 | atmvcc->push = br2684_push; | 594 | atmvcc->push = br2684_push; |
559 | atmvcc->pop = br2684_pop; | 595 | atmvcc->pop = br2684_pop; |
596 | atmvcc->release_cb = br2684_release_cb; | ||
560 | 597 | ||
561 | /* initialize netdev carrier state */ | 598 | /* initialize netdev carrier state */ |
562 | if (atmvcc->dev->signal == ATM_PHY_SIG_LOST) | 599 | if (atmvcc->dev->signal == ATM_PHY_SIG_LOST) |