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:
Родитель
4c3a5bdae2
Коммит
85e87870fa
|
@ -1201,19 +1201,26 @@ deferred:
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(usbnet_start_xmit);
|
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;
|
struct urb *urb;
|
||||||
int i;
|
int i;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
/* don't refill the queue all at once */
|
/* don't refill the queue all at once */
|
||||||
for (i = 0; i < 10 && dev->rxq.qlen < RX_QLEN(dev); i++) {
|
for (i = 0; i < 10 && dev->rxq.qlen < RX_QLEN(dev); i++) {
|
||||||
urb = usb_alloc_urb(0, flags);
|
urb = usb_alloc_urb(0, flags);
|
||||||
if (urb != NULL) {
|
if (urb != NULL) {
|
||||||
if (rx_submit(dev, urb, flags) == -ENOLINK)
|
ret = rx_submit(dev, urb, flags);
|
||||||
return;
|
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;
|
int temp = dev->rxq.qlen;
|
||||||
|
|
||||||
if (temp < RX_QLEN(dev)) {
|
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)
|
if (temp != dev->rxq.qlen)
|
||||||
netif_dbg(dev, link, dev->net,
|
netif_dbg(dev, link, dev->net,
|
||||||
"rxqlen %d --> %d\n",
|
"rxqlen %d --> %d\n",
|
||||||
|
|
Загрузка…
Ссылка в новой задаче