net: usb: Using workqueue as bottom half handler
usbnet driver uses tasklet as bottom half handler. Since tasklet
runs in interrupt context in case of high throughput driver is
spending more time in interrupt context to process rx buffers
this is causing watch dog reset. Hence replace the tasklet with
workqueue to avoid watchdog reset.
CRs-Fixed: 378526
Change-Id: I8eac339e37d734dbaaf7a2e874d3974f562e8680
Signed-off-by: Hemant Kumar <hemantk@codeaurora.org>
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index 2b73d99..d26c845 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -87,6 +87,8 @@
static const char driver_name [] = "usbnet";
+static struct workqueue_struct *usbnet_wq;
+
/* use ethtool to change the level for any given device */
static int msg_level = -1;
module_param (msg_level, int, 0);
@@ -246,7 +248,7 @@
if (skb_defer_rx_timestamp(skb))
return;
- status = netif_rx (skb);
+ status = netif_rx_ni(skb);
if (status != NET_RX_SUCCESS)
netif_dbg(dev, rx_err, dev->net,
"netif_rx status %d\n", status);
@@ -316,7 +318,7 @@
spin_lock(&dev->done.lock);
__skb_queue_tail(&dev->done, skb);
if (dev->done.qlen == 1)
- tasklet_schedule(&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
spin_unlock_irqrestore(&dev->done.lock, flags);
return old_state;
}
@@ -390,7 +392,7 @@
default:
netif_dbg(dev, rx_err, dev->net,
"rx submit, %d\n", retval);
- tasklet_schedule (&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
break;
case 0:
usb_mark_last_busy(dev->udev);
@@ -583,7 +585,7 @@
num++;
}
- tasklet_schedule(&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
netif_dbg(dev, rx_status, dev->net,
"paused rx queue disabled, %d skbs requeued\n", num);
@@ -652,7 +654,7 @@
{
if (netif_running(dev->net)) {
(void) unlink_urbs (dev, &dev->rxq);
- tasklet_schedule(&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
}
}
EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs);
@@ -726,7 +728,7 @@
*/
dev->flags = 0;
del_timer_sync (&dev->delay);
- tasklet_kill (&dev->bh);
+ cancel_work_sync(&dev->bh_w);
if (info->manage_power)
info->manage_power(dev, 0);
else
@@ -799,7 +801,7 @@
"simple");
// delay posting reads until we're fully open
- tasklet_schedule (&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
if (info->manage_power) {
retval = info->manage_power(dev, 1);
if (retval < 0)
@@ -969,7 +971,7 @@
status);
} else {
clear_bit (EVENT_RX_HALT, &dev->flags);
- tasklet_schedule (&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
}
}
@@ -994,7 +996,7 @@
usb_autopm_put_interface(dev->intf);
fail_lowmem:
if (resched)
- tasklet_schedule (&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
}
}
@@ -1080,7 +1082,7 @@
struct usbnet *dev = netdev_priv(net);
unlink_urbs (dev, &dev->txq);
- tasklet_schedule (&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
// FIXME: device recovery -- reset?
}
@@ -1267,13 +1269,21 @@
"rxqlen %d --> %d\n",
temp, dev->rxq.qlen);
if (dev->rxq.qlen < qlen)
- tasklet_schedule (&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
}
if (dev->txq.qlen < TX_QLEN (dev))
netif_wake_queue (dev->net);
}
}
+static void usbnet_bh_w(struct work_struct *work)
+{
+ struct usbnet *dev =
+ container_of(work, struct usbnet, bh_w);
+ unsigned long param = (unsigned long)dev;
+
+ usbnet_bh(param);
+}
/*-------------------------------------------------------------------------
*
@@ -1392,8 +1402,7 @@
skb_queue_head_init (&dev->txq);
skb_queue_head_init (&dev->done);
skb_queue_head_init(&dev->rxq_pause);
- dev->bh.func = usbnet_bh;
- dev->bh.data = (unsigned long) dev;
+ INIT_WORK(&dev->bh_w, usbnet_bh_w);
INIT_WORK (&dev->kevent, kevent);
init_usb_anchor(&dev->deferred);
dev->delay.function = usbnet_bh;
@@ -1577,7 +1586,7 @@
if (test_bit(EVENT_DEV_OPEN, &dev->flags)) {
if (!(dev->txq.qlen >= TX_QLEN(dev)))
netif_tx_wake_all_queues(dev->net);
- tasklet_schedule (&dev->bh);
+ queue_work(usbnet_wq, &dev->bh_w);
}
}
return 0;
@@ -1594,12 +1603,20 @@
FIELD_SIZEOF(struct sk_buff, cb) < sizeof(struct skb_data));
random_ether_addr(node_id);
+
+ usbnet_wq = create_singlethread_workqueue("usbnet");
+ if (!usbnet_wq) {
+ pr_err("%s: Unable to create workqueue:usbnet\n", __func__);
+ return -ENOMEM;
+ }
+
return 0;
}
module_init(usbnet_init);
static void __exit usbnet_exit(void)
{
+ destroy_workqueue(usbnet_wq);
}
module_exit(usbnet_exit);
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index 76f4396..72f5c96 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -56,7 +56,7 @@
struct sk_buff_head rxq_pause;
struct urb *interrupt;
struct usb_anchor deferred;
- struct tasklet_struct bh;
+ struct work_struct bh_w;
struct work_struct kevent;
unsigned long flags;