From 78281f6ed79413e5a16eac6edc4a72c6836ae5a9 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Thu, 18 Sep 2014 10:42:41 -0700 Subject: [PATCH] USB: gadget: u_ether: Fix data stall issue in RNDIS tethering mode 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 --- drivers/usb/gadget/function/u_ether.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index a06bd0e21392..998e67b03864 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -66,7 +66,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; @@ -494,7 +494,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); } @@ -614,12 +613,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) % dev->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) { @@ -628,7 +634,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) { @@ -655,7 +660,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); } -- 2.34.1