diff options
author | David S. Miller <davem@davemloft.net> | 2012-12-01 20:45:24 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-12-01 20:45:24 -0500 |
commit | ddb303301bed80f700db6f36870642a08016f266 (patch) | |
tree | e332b17c20732b22192e2ddec7762c90c19f73b4 /net/atm/br2684.c | |
parent | 577b981714b0b3530817569bf705bd74881efc83 (diff) | |
parent | c48d49aab0b5b48b40e00fe43927efed5fc09d88 (diff) |
Merge git://git.infradead.org/users/dwmw2/atm
David Woodhouse says:
====================
This is the result of pulling on the thread started by Krzysztof Mazur's
original patch 'pppoatm: don't send frames to destroyed vcc'.
Various problems in the pppoatm and br2684 code are solved, some of which
were easily triggered and would panic the kernel.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/atm/br2684.c')
-rw-r--r-- | net/atm/br2684.c | 55 |
1 files changed, 49 insertions, 6 deletions
diff --git a/net/atm/br2684.c b/net/atm/br2684.c index 8eb6fbe8d8d..403e71fa88f 100644 --- a/net/atm/br2684.c +++ b/net/atm/br2684.c @@ -68,6 +68,8 @@ struct br2684_vcc { /* keep old push, pop functions for chaining */ void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb); void (*old_pop)(struct atm_vcc *vcc, struct sk_buff *skb); + void (*old_release_cb)(struct atm_vcc *vcc); + struct module *old_owner; enum br2684_encaps encaps; struct list_head brvccs; #ifdef CONFIG_ATM_BR2684_IPFILTER @@ -269,6 +271,17 @@ static int br2684_xmit_vcc(struct sk_buff *skb, struct net_device *dev, return !atmvcc->send(atmvcc, skb); } +static void br2684_release_cb(struct atm_vcc *atmvcc) +{ + struct br2684_vcc *brvcc = BR2684_VCC(atmvcc); + + if (atomic_read(&brvcc->qspace) > 0) + netif_wake_queue(brvcc->device); + + if (brvcc->old_release_cb) + brvcc->old_release_cb(atmvcc); +} + static inline struct br2684_vcc *pick_outgoing_vcc(const struct sk_buff *skb, const struct br2684_dev *brdev) { @@ -280,6 +293,8 @@ static netdev_tx_t br2684_start_xmit(struct sk_buff *skb, { struct br2684_dev *brdev = BRPRIV(dev); struct br2684_vcc *brvcc; + struct atm_vcc *atmvcc; + netdev_tx_t ret = NETDEV_TX_OK; pr_debug("skb_dst(skb)=%p\n", skb_dst(skb)); read_lock(&devs_lock); @@ -290,9 +305,26 @@ static netdev_tx_t br2684_start_xmit(struct sk_buff *skb, dev->stats.tx_carrier_errors++; /* netif_stop_queue(dev); */ dev_kfree_skb(skb); - read_unlock(&devs_lock); - return NETDEV_TX_OK; + goto out_devs; } + atmvcc = brvcc->atmvcc; + + bh_lock_sock(sk_atm(atmvcc)); + + if (test_bit(ATM_VF_RELEASED, &atmvcc->flags) || + test_bit(ATM_VF_CLOSE, &atmvcc->flags) || + !test_bit(ATM_VF_READY, &atmvcc->flags)) { + dev->stats.tx_dropped++; + dev_kfree_skb(skb); + goto out; + } + + if (sock_owned_by_user(sk_atm(atmvcc))) { + netif_stop_queue(brvcc->device); + ret = NETDEV_TX_BUSY; + goto out; + } + if (!br2684_xmit_vcc(skb, dev, brvcc)) { /* * We should probably use netif_*_queue() here, but that @@ -304,8 +336,11 @@ static netdev_tx_t br2684_start_xmit(struct sk_buff *skb, dev->stats.tx_errors++; dev->stats.tx_fifo_errors++; } + out: + bh_unlock_sock(sk_atm(atmvcc)); + out_devs: read_unlock(&devs_lock); - return NETDEV_TX_OK; + return ret; } /* @@ -378,9 +413,10 @@ static void br2684_close_vcc(struct br2684_vcc *brvcc) list_del(&brvcc->brvccs); write_unlock_irq(&devs_lock); brvcc->atmvcc->user_back = NULL; /* what about vcc->recvq ??? */ + brvcc->atmvcc->release_cb = brvcc->old_release_cb; brvcc->old_push(brvcc->atmvcc, NULL); /* pass on the bad news */ + module_put(brvcc->old_owner); kfree(brvcc); - module_put(THIS_MODULE); } /* when AAL5 PDU comes in: */ @@ -554,9 +590,13 @@ static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg) brvcc->encaps = (enum br2684_encaps)be.encaps; brvcc->old_push = atmvcc->push; brvcc->old_pop = atmvcc->pop; + brvcc->old_release_cb = atmvcc->release_cb; + brvcc->old_owner = atmvcc->owner; barrier(); atmvcc->push = br2684_push; atmvcc->pop = br2684_pop; + atmvcc->release_cb = br2684_release_cb; + atmvcc->owner = THIS_MODULE; /* initialize netdev carrier state */ if (atmvcc->dev->signal == ATM_PHY_SIG_LOST) @@ -695,10 +735,13 @@ static int br2684_ioctl(struct socket *sock, unsigned int cmd, return -ENOIOCTLCMD; if (!capable(CAP_NET_ADMIN)) return -EPERM; - if (cmd == ATM_SETBACKEND) + if (cmd == ATM_SETBACKEND) { + if (sock->state != SS_CONNECTED) + return -EINVAL; return br2684_regvcc(atmvcc, argp); - else + } else { return br2684_create(argp); + } #ifdef CONFIG_ATM_BR2684_IPFILTER case BR2684_SETFILT: if (atmvcc->push != br2684_push) |