diff options
Diffstat (limited to 'target/linux/generic/patches-3.3/131-atm-fixes.patch')
-rw-r--r-- | target/linux/generic/patches-3.3/131-atm-fixes.patch | 900 |
1 files changed, 0 insertions, 900 deletions
diff --git a/target/linux/generic/patches-3.3/131-atm-fixes.patch b/target/linux/generic/patches-3.3/131-atm-fixes.patch deleted file mode 100644 index 4d3bd4f..0000000 --- a/target/linux/generic/patches-3.3/131-atm-fixes.patch +++ /dev/null @@ -1,900 +0,0 @@ -commit d7d3d8f1ee4435e32bc6c93187798b7e2e3170a9 -Author: David Woodhouse <David.Woodhouse@intel.com> -Date: Thu Nov 29 23:28:30 2012 +0000 - - solos-pci: remove list_vccs() debugging function - - No idea why we've gone so long dumping a list of VCCs with vci==0 on - every ->open() call... - - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - -commit c93967bfd3a3dffa759a3f28370167bf3cdbc3d0 -Author: David Woodhouse <David.Woodhouse@intel.com> -Date: Thu Nov 29 23:27:20 2012 +0000 - - solos-pci: use GFP_KERNEL where possible, not GFP_ATOMIC - - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - -commit ad6999e17ae4f7b99f6d28f425ae970acb115347 -Author: David Woodhouse <David.Woodhouse@intel.com> -Date: Thu Nov 29 23:15:30 2012 +0000 - - solos-pci: clean up pclose() function - - - Flush pending TX skbs from the queue rather than waiting for them all to - complete (suggested by Krzysztof Mazur <krzysiek@podlesie.net>). - - Clear ATM_VF_ADDR only when the PKT_PCLOSE packet has been submitted. - - Don't clear ATM_VF_READY at all — vcc_destroy_socket() does that for us. - - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - -commit 2547e97f29d6d69d567947d5ef90b6b4fbcaf565 -Author: David Woodhouse <David.Woodhouse@intel.com> -Date: Wed Nov 28 10:15:05 2012 +0000 - - pppoatm: optimise PPP channel wakeups after sock_owned_by_user() - - We don't need to schedule the wakeup tasklet on *every* unlock; only if we - actually blocked the channel in the first place. - - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - Acked-by: Krzysztof Mazur <krzysiek@podlesie.net> - -commit 990b0884a2e9668c08e9daa4d70a54d65329cb6f -Author: Krzysztof Mazur <krzysiek@podlesie.net> -Date: Wed Nov 28 09:08:04 2012 +0100 - - br2684: allow assign only on a connected socket - - The br2684 does not check if used vcc is in connected state, - causing potential Oops in pppoatm_send() when vcc->send() is called - on not fully connected socket. - - Now br2684 can be assigned only on connected sockets; otherwise - -EINVAL error is returned. - - Signed-off-by: Krzysztof Mazur <krzysiek@podlesie.net> - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - -commit f49b6da01f0abebb17f6241473d53018d923f6b0 -Author: Nathan Williams <nathan@traverse.com.au> -Date: Tue Nov 27 17:34:09 2012 +1100 - - solos-pci: Fix leak of skb received for unknown vcc - - ... and ensure that the next skb is set up for RX in the DMA case. - - Signed-off-by: Nathan Williams <nathan@traverse.com.au> - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - -commit a61d37ff4a555886c4ebe31d2c6d893afb6f4d3c -Author: David Woodhouse <David.Woodhouse@intel.com> -Date: Wed Nov 28 00:46:45 2012 +0000 - - br2684: fix module_put() race - - The br2684 code used module_put() during unassignment from vcc with - hope that we have BKL. This assumption is no longer true. - - Now owner field in atmvcc is used to move this module_put() - to vcc_destroy_socket(). - - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - Acked-by: Krzysztof Mazur <krzysiek@podlesie.net> - -commit c52f40629884ddc62c7af445fd5d620fdb466fb2 -Author: David Woodhouse <David.Woodhouse@intel.com> -Date: Wed Nov 28 00:05:52 2012 +0000 - - pppoatm: fix missing wakeup in pppoatm_send() - - Now that we can return zero from pppoatm_send() for reasons *other* than - the queue being full, that means we can't depend on a subsequent call to - pppoatm_pop() waking the queue, and we might leave it stalled - indefinitely. - - Use the ->release_cb() callback to wake the queue after the sock is - unlocked. - - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - Acked-by: Krzysztof Mazur <krzysiek@podlesie.net> - -commit 35826e7372fe39b7db7930eda0267c82d68d1a4c -Author: David Woodhouse <dwmw2@infradead.org> -Date: Tue Nov 27 23:28:36 2012 +0000 - - 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> - -commit 7f940dde65de4a707f3dd723bb6ce9de90ca1eab -Author: David Woodhouse <David.Woodhouse@intel.com> -Date: Wed Nov 28 00:03:11 2012 +0000 - - atm: add release_cb() callback to vcc - - The immediate use case for this is that it will allow us to ensure that a - pppoatm queue is woken after it has to drop a packet due to the sock being - locked. - - Note that 'release_cb' is called when the socket is *unlocked*. This is - not to be confused with vcc_release() — which probably ought to be called - vcc_close(). - - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - Acked-by: Krzysztof Mazur <krzysiek@podlesie.net> - -commit def1b2f9083f84d0a77730e537c76429914d17c1 -Author: David Woodhouse <David.Woodhouse@intel.com> -Date: Tue Nov 27 23:49:24 2012 +0000 - - solos-pci: wait for pending TX to complete when releasing vcc - - We should no longer be calling the old pop routine for the vcc, after - vcc_release() has completed. Make sure we wait for any pending TX skbs - to complete, by waiting for our own PKT_PCLOSE control skb to be sent. - - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - -commit 397ff16dce53888ec693b3718640be2560204751 -Author: Krzysztof Mazur <krzysiek@podlesie.net> -Date: Tue Nov 6 23:17:02 2012 +0100 - - pppoatm: do not inline pppoatm_may_send() - - The pppoatm_may_send() is quite heavy and it's called three times - in pppoatm_send() and inlining costs more than 200 bytes of code - (more than 10% of total pppoatm driver code size). - - add/remove: 1/0 grow/shrink: 0/1 up/down: 132/-367 (-235) - function old new delta - pppoatm_may_send - 132 +132 - pppoatm_send 900 533 -367 - - Signed-off-by: Krzysztof Mazur <krzysiek@podlesie.net> - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - -commit 071d93931a75dc1f82f0baa9959613af81c5a032 -Author: Krzysztof Mazur <krzysiek@podlesie.net> -Date: Sat Nov 10 23:33:19 2012 +0100 - - pppoatm: drop frames to not-ready vcc - - The vcc_destroy_socket() closes vcc before the protocol is detached - from vcc by calling vcc->push() with NULL skb. This leaves some time - window, where the protocol may call vcc->send() on closed vcc - and crash. - - Now pppoatm_send(), like vcc_sendmsg(), checks for vcc flags that - indicate that vcc is not ready. If the vcc is not ready we just - drop frame. Queueing frames is much more complicated because we - don't have callbacks that inform us about vcc flags changes. - - Signed-off-by: Krzysztof Mazur <krzysiek@podlesie.net> - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - -commit 3ac108006fd7f20cb8fc8ea2287f1497bcda00a1 -Author: Krzysztof Mazur <krzysiek@podlesie.net> -Date: Tue Nov 6 23:17:00 2012 +0100 - - pppoatm: take ATM socket lock in pppoatm_send() - - The pppoatm_send() does not take any lock that will prevent concurrent - vcc_sendmsg(). This causes two problems: - - - there is no locking between checking the send queue size - with atm_may_send() and incrementing sk_wmem_alloc, - and the real queue size can be a little higher than sk_sndbuf - - - the vcc->sendmsg() can be called concurrently. I'm not sure - if it's allowed. Some drivers (eni, nicstar, ...) seem - to assume it will never happen. - - Now pppoatm_send() takes ATM socket lock, the same that is used - in vcc_sendmsg() and other ATM socket functions. The pppoatm_send() - is called with BH disabled, so bh_lock_sock() is used instead - of lock_sock(). - - Signed-off-by: Krzysztof Mazur <krzysiek@podlesie.net> - Cc: Chas Williams - CONTRACTOR <chas@cmf.nrl.navy.mil> - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - -commit e41faed9cde1acce657f75a0b19a1787e9850d3f -Author: Krzysztof Mazur <krzysiek@podlesie.net> -Date: Tue Nov 6 23:16:59 2012 +0100 - - pppoatm: fix module_put() race - - The pppoatm used module_put() during unassignment from vcc with - hope that we have BKL. This assumption is no longer true. - - Now owner field in atmvcc is used to move this module_put() - to vcc_destroy_socket(). - - Signed-off-by: Krzysztof Mazur <krzysiek@podlesie.net> - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - -commit 3b1a914595f3f9beb9e38ff3ddc7bdafa092ba22 -Author: Krzysztof Mazur <krzysiek@podlesie.net> -Date: Tue Nov 6 23:16:58 2012 +0100 - - pppoatm: allow assign only on a connected socket - - The pppoatm does not check if used vcc is in connected state, - causing an Oops in pppoatm_send() when vcc->send() is called - on not fully connected socket. - - Now pppoatm can be assigned only on connected sockets; otherwise - -EINVAL error is returned. - - Signed-off-by: Krzysztof Mazur <krzysiek@podlesie.net> - Cc: Chas Williams - CONTRACTOR <chas@cmf.nrl.navy.mil> - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - -commit ec809bd817dfa1905283468e4c813684ed4efe78 -Author: Krzysztof Mazur <krzysiek@podlesie.net> -Date: Tue Nov 6 23:16:57 2012 +0100 - - atm: add owner of push() callback to atmvcc - - The atm is using atmvcc->push(vcc, NULL) callback to notify protocol - that vcc will be closed and protocol must detach from it. This callback - is usually used by protocol to decrement module usage count by module_put(), - but it leaves small window then module is still used after module_put(). - - Now the owner of push() callback is kept in atmvcc and - module_put(atmvcc->owner) is called after the protocol is detached from vcc. - - Signed-off-by: Krzysztof Mazur <krzysiek@podlesie.net> - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - Acked-by: Chas Williams <chas@cmf.nrl.navy.mil> - -commit ae088d663beebb3cad0e7abaac67ee61a7c578d5 -Author: David Woodhouse <dwmw2@infradead.org> -Date: Sun Nov 25 12:06:52 2012 +0000 - - atm: br2684: Fix excessive queue bloat - - There's really no excuse for an additional wmem_default of buffering - between the netdev queue and the ATM device. Two packets (one in-flight, - and one ready to send) ought to be fine. It's not as if it should take - long to get another from the netdev queue when we need it. - - If necessary we can make the queue space configurable later, but I don't - think it's likely to be necessary. - - cf. commit 9d02daf754238adac48fa075ee79e7edd3d79ed3 (pppoatm: Fix - excessive queue bloat) which did something very similar for PPPoATM. - - Note that there is a tremendously unlikely race condition which may - result in qspace temporarily going negative. If a CPU running the - br2684_pop() function goes off into the weeds for a long period of time - after incrementing qspace to 1, but before calling netdev_wake_queue()... - and another CPU ends up calling br2684_start_xmit() and *stopping* the - queue again before the first CPU comes back, the netdev queue could - end up being woken when qspace has already reached zero. - - An alternative approach to coping with this race would be to check in - br2684_start_xmit() for qspace==0 and return NETDEV_TX_BUSY, but just - using '> 0' and '< 1' for comparison instead of '== 0' and '!= 0' is - simpler. It just warranted a mention of *why* we do it that way... - - Move the call to atmvcc->send() to happen *after* the accounting and - potentially stopping the netdev queue, in br2684_xmit_vcc(). This matters - if the ->send() call suffers an immediate failure, because it'll call - br2684_pop() with the offending skb before returning. We want that to - happen *after* we've done the initial accounting for the packet in - question. Also make it return an appropriate success/failure indication - while we're at it. - - Tested by running 'ping -l 1000 bottomless.aaisp.net.uk' from within my - network, with only a single PPPoE-over-BR2684 link running. And after - setting txqueuelen on the nas0 interface to something low (5, in fact). - Before the patch, we'd see about 15 packets being queued and a resulting - latency of ~56ms being reached. After the patch, we see only about 8, - which is fairly much what we expect. And a max latency of ~36ms. On this - OpenWRT box, wmem_default is 163840. - - Signed-off-by: David Woodhouse <David.Woodhouse@intel.com> - Reviewed-by: Krzysztof Mazur <krzysiek@podlesie.net> - Signed-off-by: David S. Miller <davem@davemloft.net> - ---- a/drivers/atm/solos-pci.c -+++ b/drivers/atm/solos-pci.c -@@ -92,6 +92,7 @@ struct pkt_hdr { - }; - - struct solos_skb_cb { -+ struct completion c; - struct atm_vcc *vcc; - uint32_t dma_addr; - }; -@@ -164,7 +165,6 @@ static void fpga_queue(struct solos_card - static uint32_t fpga_tx(struct solos_card *); - static irqreturn_t solos_irq(int irq, void *dev_id); - static struct atm_vcc* find_vcc(struct atm_dev *dev, short vpi, int vci); --static int list_vccs(int vci); - static int atm_init(struct solos_card *, struct device *); - static void atm_remove(struct solos_card *); - static int send_command(struct solos_card *card, int dev, const char *buf, size_t size); -@@ -710,7 +710,8 @@ void solos_bh(unsigned long card_arg) - dev_warn(&card->dev->dev, "Received packet for unknown VPI.VCI %d.%d on port %d\n", - le16_to_cpu(header->vpi), le16_to_cpu(header->vci), - port); -- continue; -+ dev_kfree_skb_any(skb); -+ break; - } - atm_charge(vcc, skb->truesize); - vcc->push(vcc, skb); -@@ -790,44 +791,6 @@ static struct atm_vcc *find_vcc(struct a - return vcc; - } - --static int list_vccs(int vci) --{ -- struct hlist_head *head; -- struct atm_vcc *vcc; -- struct hlist_node *node; -- struct sock *s; -- int num_found = 0; -- int i; -- -- read_lock(&vcc_sklist_lock); -- if (vci != 0){ -- head = &vcc_hash[vci & (VCC_HTABLE_SIZE -1)]; -- sk_for_each(s, node, head) { -- num_found ++; -- vcc = atm_sk(s); -- printk(KERN_DEBUG "Device: %d Vpi: %d Vci: %d\n", -- vcc->dev->number, -- vcc->vpi, -- vcc->vci); -- } -- } else { -- for(i = 0; i < VCC_HTABLE_SIZE; i++){ -- head = &vcc_hash[i]; -- sk_for_each(s, node, head) { -- num_found ++; -- vcc = atm_sk(s); -- printk(KERN_DEBUG "Device: %d Vpi: %d Vci: %d\n", -- vcc->dev->number, -- vcc->vpi, -- vcc->vci); -- } -- } -- } -- read_unlock(&vcc_sklist_lock); -- return num_found; --} -- -- - static int popen(struct atm_vcc *vcc) - { - struct solos_card *card = vcc->dev->dev_data; -@@ -840,7 +803,7 @@ static int popen(struct atm_vcc *vcc) - return -EINVAL; - } - -- skb = alloc_skb(sizeof(*header), GFP_ATOMIC); -+ skb = alloc_skb(sizeof(*header), GFP_KERNEL); - if (!skb) { - if (net_ratelimit()) - dev_warn(&card->dev->dev, "Failed to allocate sk_buff in popen()\n"); -@@ -857,8 +820,6 @@ static int popen(struct atm_vcc *vcc) - - set_bit(ATM_VF_ADDR, &vcc->flags); - set_bit(ATM_VF_READY, &vcc->flags); -- list_vccs(0); -- - - return 0; - } -@@ -866,10 +827,21 @@ static int popen(struct atm_vcc *vcc) - static void pclose(struct atm_vcc *vcc) - { - struct solos_card *card = vcc->dev->dev_data; -- struct sk_buff *skb; -+ unsigned char port = SOLOS_CHAN(vcc->dev); -+ struct sk_buff *skb, *tmpskb; - struct pkt_hdr *header; - -- skb = alloc_skb(sizeof(*header), GFP_ATOMIC); -+ /* Remove any yet-to-be-transmitted packets from the pending queue */ -+ spin_lock(&card->tx_queue_lock); -+ skb_queue_walk_safe(&card->tx_queue[port], skb, tmpskb) { -+ if (SKB_CB(skb)->vcc == vcc) { -+ skb_unlink(skb, &card->tx_queue[port]); -+ solos_pop(vcc, skb); -+ } -+ } -+ spin_unlock(&card->tx_queue_lock); -+ -+ skb = alloc_skb(sizeof(*header), GFP_KERNEL); - if (!skb) { - dev_warn(&card->dev->dev, "Failed to allocate sk_buff in pclose()\n"); - return; -@@ -881,15 +853,21 @@ static void pclose(struct atm_vcc *vcc) - header->vci = cpu_to_le16(vcc->vci); - header->type = cpu_to_le16(PKT_PCLOSE); - -- fpga_queue(card, SOLOS_CHAN(vcc->dev), skb, NULL); -+ init_completion(&SKB_CB(skb)->c); - -- clear_bit(ATM_VF_ADDR, &vcc->flags); -- clear_bit(ATM_VF_READY, &vcc->flags); -+ fpga_queue(card, port, skb, NULL); -+ -+ if (!wait_for_completion_timeout(&SKB_CB(skb)->c, 5 * HZ)) -+ dev_warn(&card->dev->dev, "Timeout waiting for VCC close on port %d\n", -+ port); - - /* Hold up vcc_destroy_socket() (our caller) until solos_bh() in the - tasklet has finished processing any incoming packets (and, more to - the point, using the vcc pointer). */ - tasklet_unlock_wait(&card->tlet); -+ -+ clear_bit(ATM_VF_ADDR, &vcc->flags); -+ - return; - } - -@@ -1010,9 +988,12 @@ static uint32_t fpga_tx(struct solos_car - if (vcc) { - atomic_inc(&vcc->stats->tx); - solos_pop(vcc, oldskb); -- } else -+ } else { -+ struct pkt_hdr *header = (void *)oldskb->data; -+ if (le16_to_cpu(header->type) == PKT_PCLOSE) -+ complete(&SKB_CB(oldskb)->c); - dev_kfree_skb_irq(oldskb); -- -+ } - } - } - /* For non-DMA TX, write the 'TX start' bit for all four ports simultaneously */ -@@ -1246,7 +1227,7 @@ static int atm_init(struct solos_card *c - card->atmdev[i]->phy_data = (void *)(unsigned long)i; - atm_dev_signal_change(card->atmdev[i], ATM_PHY_SIG_FOUND); - -- skb = alloc_skb(sizeof(*header), GFP_ATOMIC); -+ skb = alloc_skb(sizeof(*header), GFP_KERNEL); - if (!skb) { - dev_warn(&card->dev->dev, "Failed to allocate sk_buff in atm_init()\n"); - continue; -@@ -1343,6 +1324,8 @@ static struct pci_driver fpga_driver = { - - static int __init solos_pci_init(void) - { -+ BUILD_BUG_ON(sizeof(struct solos_skb_cb) > sizeof(((struct sk_buff *)0)->cb)); -+ - printk(KERN_INFO "Solos PCI Driver Version %s\n", VERSION); - return pci_register_driver(&fpga_driver); - } ---- a/include/linux/atmdev.h -+++ b/include/linux/atmdev.h -@@ -307,6 +307,7 @@ struct atm_vcc { - struct atm_dev *dev; /* device back pointer */ - struct atm_qos qos; /* QOS */ - struct atm_sap sap; /* SAP */ -+ void (*release_cb)(struct atm_vcc *vcc); /* release_sock callback */ - void (*push)(struct atm_vcc *vcc,struct sk_buff *skb); - void (*pop)(struct atm_vcc *vcc,struct sk_buff *skb); /* optional */ - int (*push_oam)(struct atm_vcc *vcc,void *cell); -@@ -314,6 +315,7 @@ struct atm_vcc { - void *dev_data; /* per-device data */ - void *proto_data; /* per-protocol data */ - struct k_atm_aal_stats *stats; /* pointer to AAL stats group */ -+ struct module *owner; /* owner of ->push function */ - /* SVC part --- may move later ------------------------------------- */ - short itf; /* interface number */ - struct sockaddr_atmsvc local; ---- a/net/atm/br2684.c -+++ b/net/atm/br2684.c -@@ -68,12 +68,15 @@ 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 - struct br2684_filter filter; - #endif /* CONFIG_ATM_BR2684_IPFILTER */ - unsigned copies_needed, copies_failed; -+ atomic_t qspace; - }; - - struct br2684_dev { -@@ -181,18 +184,15 @@ static struct notifier_block atm_dev_not - static void br2684_pop(struct atm_vcc *vcc, struct sk_buff *skb) - { - struct br2684_vcc *brvcc = BR2684_VCC(vcc); -- struct net_device *net_dev = skb->dev; - -- pr_debug("(vcc %p ; net_dev %p )\n", vcc, net_dev); -+ pr_debug("(vcc %p ; net_dev %p )\n", vcc, brvcc->device); - brvcc->old_pop(vcc, skb); - -- if (!net_dev) -- return; -- -- if (atm_may_send(vcc, 0)) -- netif_wake_queue(net_dev); -- -+ /* If the queue space just went up from zero, wake */ -+ if (atomic_inc_return(&brvcc->qspace) == 1) -+ netif_wake_queue(brvcc->device); - } -+ - /* - * Send a packet out a particular vcc. Not to useful right now, but paves - * the way for multiple vcc's per itf. Returns true if we can send, -@@ -256,16 +256,30 @@ static int br2684_xmit_vcc(struct sk_buf - ATM_SKB(skb)->atm_options = atmvcc->atm_options; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; -- atmvcc->send(atmvcc, skb); - -- if (!atm_may_send(atmvcc, 0)) { -+ if (atomic_dec_return(&brvcc->qspace) < 1) { -+ /* No more please! */ - netif_stop_queue(brvcc->device); -- /*check for race with br2684_pop*/ -- if (atm_may_send(atmvcc, 0)) -- netif_start_queue(brvcc->device); -+ /* We might have raced with br2684_pop() */ -+ if (unlikely(atomic_read(&brvcc->qspace) > 0)) -+ netif_wake_queue(brvcc->device); - } - -- return 1; -+ /* If this fails immediately, the skb will be freed and br2684_pop() -+ will wake the queue if appropriate. Just return an error so that -+ the stats are updated correctly */ -+ 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, -@@ -279,6 +293,8 @@ static netdev_tx_t br2684_start_xmit(str - { - 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); -@@ -289,9 +305,26 @@ static netdev_tx_t br2684_start_xmit(str - 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 -@@ -303,8 +336,11 @@ static netdev_tx_t br2684_start_xmit(str - 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; - } - - /* -@@ -377,9 +413,10 @@ static void br2684_close_vcc(struct br26 - 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: */ -@@ -504,6 +541,13 @@ static int br2684_regvcc(struct atm_vcc - brvcc = kzalloc(sizeof(struct br2684_vcc), GFP_KERNEL); - if (!brvcc) - return -ENOMEM; -+ /* -+ * Allow two packets in the ATM queue. One actually being sent, and one -+ * for the ATM 'TX done' handler to send. It shouldn't take long to get -+ * the next one from the netdev queue, when we need it. More than that -+ * would be bufferbloat. -+ */ -+ atomic_set(&brvcc->qspace, 2); - write_lock_irq(&devs_lock); - net_dev = br2684_find_dev(&be.ifspec); - if (net_dev == NULL) { -@@ -546,9 +590,13 @@ static int br2684_regvcc(struct atm_vcc - 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) -@@ -687,10 +735,13 @@ static int br2684_ioctl(struct socket *s - 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) ---- a/net/atm/common.c -+++ b/net/atm/common.c -@@ -126,10 +126,19 @@ static void vcc_write_space(struct sock - rcu_read_unlock(); - } - -+static void vcc_release_cb(struct sock *sk) -+{ -+ struct atm_vcc *vcc = atm_sk(sk); -+ -+ if (vcc->release_cb) -+ vcc->release_cb(vcc); -+} -+ - static struct proto vcc_proto = { - .name = "VCC", - .owner = THIS_MODULE, - .obj_size = sizeof(struct atm_vcc), -+ .release_cb = vcc_release_cb, - }; - - int vcc_create(struct net *net, struct socket *sock, int protocol, int family) -@@ -156,7 +165,9 @@ int vcc_create(struct net *net, struct s - atomic_set(&sk->sk_rmem_alloc, 0); - vcc->push = NULL; - vcc->pop = NULL; -+ vcc->owner = NULL; - vcc->push_oam = NULL; -+ vcc->release_cb = NULL; - vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */ - vcc->atm_options = vcc->aal_options = 0; - sk->sk_destruct = vcc_sock_destruct; -@@ -175,6 +186,7 @@ static void vcc_destroy_socket(struct so - vcc->dev->ops->close(vcc); - if (vcc->push) - vcc->push(vcc, NULL); /* atmarpd has no push */ -+ module_put(vcc->owner); - - while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { - atm_return(vcc, skb->truesize); ---- a/net/atm/pppoatm.c -+++ b/net/atm/pppoatm.c -@@ -60,6 +60,8 @@ struct pppoatm_vcc { - struct atm_vcc *atmvcc; /* VCC descriptor */ - void (*old_push)(struct atm_vcc *, struct sk_buff *); - void (*old_pop)(struct atm_vcc *, struct sk_buff *); -+ void (*old_release_cb)(struct atm_vcc *); -+ struct module *old_owner; - /* keep old push/pop for detaching */ - enum pppoatm_encaps encaps; - atomic_t inflight; -@@ -107,6 +109,24 @@ static void pppoatm_wakeup_sender(unsign - ppp_output_wakeup((struct ppp_channel *) arg); - } - -+static void pppoatm_release_cb(struct atm_vcc *atmvcc) -+{ -+ struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc); -+ -+ /* -+ * As in pppoatm_pop(), it's safe to clear the BLOCKED bit here because -+ * the wakeup *can't* race with pppoatm_send(). They both hold the PPP -+ * channel's ->downl lock. And the potential race with *setting* it, -+ * which leads to the double-check dance in pppoatm_may_send(), doesn't -+ * exist here. In the sock_owned_by_user() case in pppoatm_send(), we -+ * set the BLOCKED bit while the socket is still locked. We know that -+ * ->release_cb() can't be called until that's done. -+ */ -+ if (test_and_clear_bit(BLOCKED, &pvcc->blocked)) -+ tasklet_schedule(&pvcc->wakeup_tasklet); -+ if (pvcc->old_release_cb) -+ pvcc->old_release_cb(atmvcc); -+} - /* - * This gets called every time the ATM card has finished sending our - * skb. The ->old_pop will take care up normal atm flow control, -@@ -151,12 +171,11 @@ static void pppoatm_unassign_vcc(struct - pvcc = atmvcc_to_pvcc(atmvcc); - atmvcc->push = pvcc->old_push; - atmvcc->pop = pvcc->old_pop; -+ atmvcc->release_cb = pvcc->old_release_cb; - tasklet_kill(&pvcc->wakeup_tasklet); - ppp_unregister_channel(&pvcc->chan); - atmvcc->user_back = NULL; - kfree(pvcc); -- /* Gee, I hope we have the big kernel lock here... */ -- module_put(THIS_MODULE); - } - - /* Called when an AAL5 PDU comes in */ -@@ -165,9 +184,13 @@ static void pppoatm_push(struct atm_vcc - struct pppoatm_vcc *pvcc = atmvcc_to_pvcc(atmvcc); - pr_debug("\n"); - if (skb == NULL) { /* VCC was closed */ -+ struct module *module; -+ - pr_debug("removing ATMPPP VCC %p\n", pvcc); -+ module = pvcc->old_owner; - pppoatm_unassign_vcc(atmvcc); - atmvcc->push(atmvcc, NULL); /* Pass along bad news */ -+ module_put(module); - return; - } - atm_return(atmvcc, skb->truesize); -@@ -211,7 +234,7 @@ error: - ppp_input_error(&pvcc->chan, 0); - } - --static inline int pppoatm_may_send(struct pppoatm_vcc *pvcc, int size) -+static int pppoatm_may_send(struct pppoatm_vcc *pvcc, int size) - { - /* - * It's not clear that we need to bother with using atm_may_send() -@@ -269,10 +292,33 @@ static inline int pppoatm_may_send(struc - static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb) - { - struct pppoatm_vcc *pvcc = chan_to_pvcc(chan); -+ struct atm_vcc *vcc; -+ int ret; -+ - ATM_SKB(skb)->vcc = pvcc->atmvcc; - pr_debug("(skb=0x%p, vcc=0x%p)\n", skb, pvcc->atmvcc); - if (skb->data[0] == '\0' && (pvcc->flags & SC_COMP_PROT)) - (void) skb_pull(skb, 1); -+ -+ vcc = ATM_SKB(skb)->vcc; -+ bh_lock_sock(sk_atm(vcc)); -+ if (sock_owned_by_user(sk_atm(vcc))) { -+ /* -+ * Needs to happen (and be flushed, hence test_and_) before we unlock -+ * the socket. It needs to be seen by the time our ->release_cb gets -+ * called. -+ */ -+ test_and_set_bit(BLOCKED, &pvcc->blocked); -+ goto nospace; -+ } -+ if (test_bit(ATM_VF_RELEASED, &vcc->flags) || -+ test_bit(ATM_VF_CLOSE, &vcc->flags) || -+ !test_bit(ATM_VF_READY, &vcc->flags)) { -+ bh_unlock_sock(sk_atm(vcc)); -+ kfree_skb(skb); -+ return DROP_PACKET; -+ } -+ - switch (pvcc->encaps) { /* LLC encapsulation needed */ - case e_llc: - if (skb_headroom(skb) < LLC_LEN) { -@@ -285,8 +331,10 @@ static int pppoatm_send(struct ppp_chann - } - kfree_skb(skb); - skb = n; -- if (skb == NULL) -+ if (skb == NULL) { -+ bh_unlock_sock(sk_atm(vcc)); - return DROP_PACKET; -+ } - } else if (!pppoatm_may_send(pvcc, skb->truesize)) - goto nospace; - memcpy(skb_push(skb, LLC_LEN), pppllc, LLC_LEN); -@@ -296,6 +344,7 @@ static int pppoatm_send(struct ppp_chann - goto nospace; - break; - case e_autodetect: -+ bh_unlock_sock(sk_atm(vcc)); - pr_debug("Trying to send without setting encaps!\n"); - kfree_skb(skb); - return 1; -@@ -305,9 +354,12 @@ static int pppoatm_send(struct ppp_chann - ATM_SKB(skb)->atm_options = ATM_SKB(skb)->vcc->atm_options; - pr_debug("atm_skb(%p)->vcc(%p)->dev(%p)\n", - skb, ATM_SKB(skb)->vcc, ATM_SKB(skb)->vcc->dev); -- return ATM_SKB(skb)->vcc->send(ATM_SKB(skb)->vcc, skb) -+ ret = ATM_SKB(skb)->vcc->send(ATM_SKB(skb)->vcc, skb) - ? DROP_PACKET : 1; -+ bh_unlock_sock(sk_atm(vcc)); -+ return ret; - nospace: -+ bh_unlock_sock(sk_atm(vcc)); - /* - * We don't have space to send this SKB now, but we might have - * already applied SC_COMP_PROT compression, so may need to undo -@@ -362,6 +414,8 @@ static int pppoatm_assign_vcc(struct atm - atomic_set(&pvcc->inflight, NONE_INFLIGHT); - pvcc->old_push = atmvcc->push; - pvcc->old_pop = atmvcc->pop; -+ pvcc->old_owner = atmvcc->owner; -+ pvcc->old_release_cb = atmvcc->release_cb; - pvcc->encaps = (enum pppoatm_encaps) be.encaps; - pvcc->chan.private = pvcc; - pvcc->chan.ops = &pppoatm_ops; -@@ -377,7 +431,9 @@ static int pppoatm_assign_vcc(struct atm - atmvcc->user_back = pvcc; - atmvcc->push = pppoatm_push; - atmvcc->pop = pppoatm_pop; -+ atmvcc->release_cb = pppoatm_release_cb; - __module_get(THIS_MODULE); -+ atmvcc->owner = THIS_MODULE; - - /* re-process everything received between connection setup and - backend setup */ -@@ -406,6 +462,8 @@ static int pppoatm_ioctl(struct socket * - return -ENOIOCTLCMD; - if (!capable(CAP_NET_ADMIN)) - return -EPERM; -+ if (sock->state != SS_CONNECTED) -+ return -EINVAL; - return pppoatm_assign_vcc(atmvcc, argp); - } - case PPPIOCGCHAN: |