net: usbnet: fix softirq storm on suspend

Suspending an open usbnet device results in constant
rescheduling of usbnet_bh.

commit 65841fd5 "usbnet: handle remote wakeup asap"
refactored the usbnet_bh code to allow sharing the
urb allocate and submit code with usbnet_resume. In
this process, a test for, and immediate return on,
ENOLINK from rx_submit was unintentionally dropped.

The rx queue will not grow if rx_submit fails,
making usbnet_bh reschedule itself.  This results
in a softirq storm if the error is persistent.
rx_submit translates the usb_submit_urb error
EHOSTUNREACH into ENOLINK, so this is an expected
and persistent error for a suspended device. The
old code tested for this condition and avoided
rescheduling.  Putting this test back.

Cc: <stable@vger.kernel.org> # v3.5
Cc: Ming Lei <ming.lei@canonical.com>
Cc: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Bjørn Mork <bjorn@mork.no>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Bjørn Mork 2012-09-02 22:26:18 +00:00 committed by David S. Miller
parent 4c3a5bdae2
commit 85e87870fa

View File

@ -1201,19 +1201,26 @@ deferred:
}
EXPORT_SYMBOL_GPL(usbnet_start_xmit);
static void rx_alloc_submit(struct usbnet *dev, gfp_t flags)
static int rx_alloc_submit(struct usbnet *dev, gfp_t flags)
{
struct urb *urb;
int i;
int ret = 0;
/* don't refill the queue all at once */
for (i = 0; i < 10 && dev->rxq.qlen < RX_QLEN(dev); i++) {
urb = usb_alloc_urb(0, flags);
if (urb != NULL) {
if (rx_submit(dev, urb, flags) == -ENOLINK)
return;
ret = rx_submit(dev, urb, flags);
if (ret)
goto err;
} else {
ret = -ENOMEM;
goto err;
}
}
err:
return ret;
}
/*-------------------------------------------------------------------------*/
@ -1257,7 +1264,8 @@ static void usbnet_bh (unsigned long param)
int temp = dev->rxq.qlen;
if (temp < RX_QLEN(dev)) {
rx_alloc_submit(dev, GFP_ATOMIC);
if (rx_alloc_submit(dev, GFP_ATOMIC) == -ENOLINK)
return;
if (temp != dev->rxq.qlen)
netif_dbg(dev, link, dev->net,
"rxqlen %d --> %d\n",