summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorJay Cheng <jacheng@nvidia.com>2012-09-29 22:07:22 -0400
committerSimone Willett <swillett@nvidia.com>2012-10-24 17:15:20 -0700
commit02005dda6af88d02f85709c4853d4618616aa35b (patch)
treedd9d12c3e1167f50c0455eb0523d74e78c789be1 /drivers
parentc42dbef25ed8e4ee17348d50344c3a23eaa9c07d (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.c17
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);
}