]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/atm/br2684.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mason/linux...
[karo-tx-linux.git] / net / atm / br2684.c
index 4819d31533e0e1ffc17e501318b41a66d580d1a6..403e71fa88feb900a7d194f3f3c83f4f6e1cb0d4 100644 (file)
@@ -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 int copies_needed, copies_failed;
+       atomic_t qspace;
 };
 
 struct br2684_dev {
@@ -181,18 +184,15 @@ static struct notifier_block atm_dev_notifier = {
 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_buff *skb, struct net_device *dev,
        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(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);
@@ -289,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
@@ -303,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;
 }
 
 /*
@@ -377,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: */
@@ -504,6 +541,13 @@ static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg)
        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 *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)
@@ -687,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)