diff options
author | Jay Cheng <jacheng@nvidia.com> | 2012-09-29 22:07:22 -0400 |
---|---|---|
committer | Simone Willett <swillett@nvidia.com> | 2012-10-24 17:15:20 -0700 |
commit | 02005dda6af88d02f85709c4853d4618616aa35b (patch) | |
tree | dd9d12c3e1167f50c0455eb0523d74e78c789be1 /drivers | |
parent | c42dbef25ed8e4ee17348d50344c3a23eaa9c07d (diff) |
usb: gadget: ether: fix clog tx transmit
With g_len initial from 0 and buffer size is 10, it cause interrupt(IOC)
is triggered at entry 0 and 5, which is 1st and 6th entry in list. This deviate
double bufferring design that expect to trigger interrupt at half and full.
If interrupt is trggered at 0 and 5, when upper layer pass through data faster
then u_ether driver can free up, then the data flow start clogging from g_len=6
till g_len=9 and no interrupt is triggered to free up memory to consume data
from upper layer as illustrated below.
[0][1][2][3][4] [5][6][7][8][9]
IOC IOC
Initial g_len as 1, so interrupt will be triggered at 5th and 10th as illustrated
below. Especially, 10th entry, which is also last entry, must set interrupt to
free up memory.
[0][1][2][3][4] [5][6][7][8][9]
IOC IOC
Also enforce last entry in list to trigger interrupt in case user space
change qmult value at run time that will cause misalignment with buffer size.
For example:
echo 4 > /sys/module/g_android/parameters/qmult
[0][1][2][3][4] [5][6][7][8][9]
IOC IOC IOC
bug 1054552
Reviewed-on: http://git-master/r/139969
(cherry picked from commit 4ea9a0eff24d5f3c44402732e35bf12eaed7ed79)
Change-Id: I449a007036f6aea73626921bb24925b2ec57e167
Signed-off-by: Jay Cheng <jacheng@nvidia.com>
Reviewed-on: http://git-master/r/145243
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Venkat Moganty <vmoganty@nvidia.com>
Reviewed-by: Michael Hsu <mhsu@nvidia.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/gadget/u_ether.c | 17 |
1 files changed, 13 insertions, 4 deletions
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index dd2615a3f65e..4056b9be378d 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -496,6 +496,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, unsigned long flags; struct usb_ep *in; u16 cdc_filter; + int buffer_is_full = 0; spin_lock_irqsave(&dev->lock, flags); if (dev->port_usb) { @@ -549,8 +550,10 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, list_del(&req->list); /* temporarily stop TX queue when the freelist empties */ - if (list_empty(&dev->tx_reqs)) + if (list_empty(&dev->tx_reqs)) { + buffer_is_full = 1; netif_stop_queue(net); + } spin_unlock_irqrestore(&dev->req_lock, flags); /* no buffer copies needed, unless the network stack did it @@ -590,13 +593,19 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb, req->length = length; + /* In case user change qmult after f_rndis is enabled, qmult might be + * misaligned with buffer size. Always enable interrupt when buffer + * is full + */ + if (buffer_is_full) + req->no_interrupt = 0; + /* throttle high/super speed IRQ rate back slightly */ - if (gadget_is_dualspeed(dev->gadget)) + else 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; - retval = usb_ep_queue(in, req, GFP_ATOMIC); switch (retval) { default: @@ -630,7 +639,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); + atomic_set(&dev->tx_qlen, 1); netif_wake_queue(dev->net); } |