USB: gadget: u_ether: Fix data stall issue in RNDIS tethering mode
authorBadhri Jagan Sridharan <Badhri@google.com>
Thu, 18 Sep 2014 17:42:41 +0000 (10:42 -0700)
committerBadhri Jagan Sridharan <Badhri@google.com>
Thu, 25 Sep 2014 02:00:05 +0000 (19:00 -0700)
For dual speed gadget, with current no. of request(10), there is
possibility of corner case occurence where all 10 reuqests are queued
to HW without setting IOC bit, which could lead to data stall in
RNDIS tethering and RNDIS local networking.

With this patch, counter will be incremented before queueing request to
HW and sets IOC bit for every nth request due to which the corner case
of all requests queued to HW without IOC bit set will be avoided.

Change-Id: I26515bfd9bbc8f7af38be7835692143f7093118a
Signed-off-by: Vijayavardhan Vennapusa <vvreddy@codeaurora.org>
drivers/usb/gadget/u_ether.c

index 6a5065966494a5d068e0a5d5a860dffae31c092a..63b590de24d64f76179a08f7b38c1decb0ad6d2d 100644 (file)
@@ -61,7 +61,7 @@ struct eth_dev {
 
        spinlock_t              req_lock;       /* guard {rx,tx}_reqs */
        struct list_head        tx_reqs, rx_reqs;
-       atomic_t                tx_qlen;
+       unsigned                tx_qlen;
 
        struct sk_buff_head     rx_frames;
 
@@ -491,7 +491,6 @@ static void tx_complete(struct usb_ep *ep, struct usb_request *req)
        spin_unlock(&dev->req_lock);
        dev_kfree_skb_any(skb);
 
-       atomic_dec(&dev->tx_qlen);
        if (netif_carrier_ok(dev->net))
                netif_wake_queue(dev->net);
 }
@@ -605,12 +604,19 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
 
        req->length = length;
 
-       /* throttle high/super speed IRQ rate back slightly */
-       if (gadget_is_dualspeed(dev->gadget))
-               req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH ||
-                                    dev->gadget->speed == USB_SPEED_SUPER)
-                       ? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
-                       : 0;
+       /* throttle highspeed IRQ rate back slightly */
+       if (gadget_is_dualspeed(dev->gadget) &&
+                        (dev->gadget->speed == USB_SPEED_HIGH)) {
+               dev->tx_qlen++;
+               if (dev->tx_qlen == qmult) {
+                       req->no_interrupt = 0;
+                       dev->tx_qlen = 0;
+               } else {
+                       req->no_interrupt = 1;
+               }
+       } else {
+               req->no_interrupt = 0;
+       }
 
        retval = usb_ep_queue(in, req, GFP_ATOMIC);
        switch (retval) {
@@ -619,7 +625,6 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
                break;
        case 0:
                net->trans_start = jiffies;
-               atomic_inc(&dev->tx_qlen);
        }
 
        if (retval) {
@@ -645,7 +650,7 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
        rx_fill(dev, gfp_flags);
 
        /* and open the tx floodgates */
-       atomic_set(&dev->tx_qlen, 0);
+       dev->tx_qlen = 0;
        netif_wake_queue(dev->net);
 }