Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 32781950 authored by Azhar Shaikh's avatar Azhar Shaikh
Browse files

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.

Change-Id: I8eac339e37d734dbaaf7a2e874d3974f562e8680
Signed-off-by: default avatarAzhar Shaikh <azhars@codeaurora.org>
parent 8c949509
Loading
Loading
Loading
Loading
+33 −16
Original line number Diff line number Diff line
@@ -89,7 +89,8 @@ static u8 node_id [ETH_ALEN];

static const char driver_name [] = "usbnet";

struct workqueue_struct		*usbnet_wq;
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);
@@ -403,7 +404,7 @@ unlock_and_schedule:
	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);
@@ -500,7 +501,7 @@ static enum skb_state defer_bh(struct usbnet *dev, struct sk_buff *skb,
	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;
}
@@ -579,7 +580,7 @@ static int rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags)
		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:
			__usbnet_queue_skb(&dev->rxq, skb, rx_start);
@@ -745,7 +746,7 @@ void usbnet_resume_rx(struct usbnet *dev)
		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);
@@ -814,7 +815,7 @@ void usbnet_unlink_rx_urbs(struct usbnet *dev)
{
	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);
@@ -887,7 +888,7 @@ int usbnet_stop (struct net_device *net)
	 */
	dev->flags = 0;
	del_timer_sync (&dev->delay);
	tasklet_kill (&dev->bh);
	cancel_work_sync(&dev->bh_w);
	if (!pm)
		usb_autopm_put_interface(dev->intf);

@@ -972,7 +973,7 @@ int usbnet_open (struct net_device *net)
	clear_bit(EVENT_RX_KILL, &dev->flags);

	// 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) {
@@ -1114,7 +1115,7 @@ static void __handle_link_change(struct usbnet *dev)
		 */
	} else {
		/* submitting URBs for reading packets */
		tasklet_schedule(&dev->bh);
		queue_work(usbnet_wq, &dev->bh_w);
	}

	/* hard_mtu or rx_urb_size may change during link change */
@@ -1187,7 +1188,7 @@ fail_halt:
					   status);
		} else {
			clear_bit (EVENT_RX_HALT, &dev->flags);
			tasklet_schedule (&dev->bh);
			queue_work(usbnet_wq, &dev->bh_w);
		}
	}

@@ -1212,7 +1213,7 @@ fail_halt:
			usb_autopm_put_interface(dev->intf);
fail_lowmem:
			if (resched)
				tasklet_schedule (&dev->bh);
				queue_work(usbnet_wq, &dev->bh_w);
		}
	}

@@ -1308,13 +1309,14 @@ void usbnet_tx_timeout (struct net_device *net)
	struct usbnet		*dev = netdev_priv(net);

	unlink_urbs (dev, &dev->txq);
	tasklet_schedule (&dev->bh);
	queue_work(usbnet_wq, &dev->bh_w);
	/* this needs to be handled individually because the generic layer
	 * doesn't know what is sufficient and could not restore private
	 * information if a remedy of an unconditional reset were used.
	 */
	if (dev->driver_info->recover)
		(dev->driver_info->recover)(dev);

}
EXPORT_SYMBOL_GPL(usbnet_tx_timeout);

@@ -1572,13 +1574,21 @@ static void usbnet_bh (unsigned long param)
					  "rxqlen %d --> %d\n",
					  temp, dev->rxq.qlen);
			if (dev->rxq.qlen < RX_QLEN(dev))
				tasklet_schedule (&dev->bh);
				queue_work(usbnet_wq, &dev->bh_w);
		}
		if (!enable_ipa_bridge && 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);
}

/*-------------------------------------------------------------------------
 *
@@ -2065,8 +2075,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
	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;
@@ -2347,7 +2356,7 @@ int usbnet_resume (struct usb_interface *intf)

			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);
		}
	}

@@ -2599,12 +2608,20 @@ static int __init usbnet_init(void)
		FIELD_SIZEOF(struct sk_buff, cb) < sizeof(struct skb_data));

	eth_random_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);

+1 −1
Original line number Diff line number Diff line
@@ -91,7 +91,7 @@ struct usbnet {
	unsigned		interrupt_count;
	struct mutex		interrupt_mutex;
	struct usb_anchor	deferred;
	struct tasklet_struct	bh;
	struct work_struct	bh_w;

	struct work_struct	kevent;
	unsigned long		flags;