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

Commit 716fb91d authored by Weinn Jheng's avatar Weinn Jheng Committed by Felipe Balbi
Browse files

usb: gadget: u_ether: move hardware transmit to RX NAPI



In order to reduce the interrupt times in the embedded system,
a receiving workqueue is introduced.
This modification also enhanced the overall throughput as the
benefits of reducing interrupt occurrence.

This work was derived from previous work:
u_ether: move hardware transmit to RX workqueue.
Which should be base on codeaurora's work.

However, the benchmark on my platform shows the throughput
with workqueue is slightly better than NAPI.

Signed-off-by: default avatarWeinn Jheng <clanlab.proj@gmail.com>
Cc: David Brownell <dbrownell@users.sourceforge.net>
Cc: David S. Miller <davem@davemloft.net>
Cc: Stephen Hemminger <shemminger@vyatta.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Manu Gautam <mgautam@codeaurora.org>
Signed-off-by: default avatarFelipe Balbi <balbi@ti.com>
parent 3f89204b
Loading
Loading
Loading
Loading
+66 −35
Original line number Original line Diff line number Diff line
@@ -48,6 +48,8 @@


#define UETH__VERSION	"29-May-2008"
#define UETH__VERSION	"29-May-2008"


#define GETHER_NAPI_WEIGHT	32

struct eth_dev {
struct eth_dev {
	/* lock is held while accessing port_usb
	/* lock is held while accessing port_usb
	 */
	 */
@@ -72,6 +74,7 @@ struct eth_dev {
						struct sk_buff_head *list);
						struct sk_buff_head *list);


	struct work_struct	work;
	struct work_struct	work;
	struct napi_struct	rx_napi;


	unsigned long		todo;
	unsigned long		todo;
#define	WORK_RX_MEMORY		0
#define	WORK_RX_MEMORY		0
@@ -253,18 +256,16 @@ enomem:
		DBG(dev, "rx submit --> %d\n", retval);
		DBG(dev, "rx submit --> %d\n", retval);
		if (skb)
		if (skb)
			dev_kfree_skb_any(skb);
			dev_kfree_skb_any(skb);
		spin_lock_irqsave(&dev->req_lock, flags);
		list_add(&req->list, &dev->rx_reqs);
		spin_unlock_irqrestore(&dev->req_lock, flags);
	}
	}
	return retval;
	return retval;
}
}


static void rx_complete(struct usb_ep *ep, struct usb_request *req)
static void rx_complete(struct usb_ep *ep, struct usb_request *req)
{
{
	struct sk_buff	*skb = req->context, *skb2;
	struct sk_buff	*skb = req->context;
	struct eth_dev	*dev = ep->driver_data;
	struct eth_dev	*dev = ep->driver_data;
	int		status = req->status;
	int		status = req->status;
	bool		rx_queue = 0;


	switch (status) {
	switch (status) {


@@ -288,30 +289,8 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req)
		} else {
		} else {
			skb_queue_tail(&dev->rx_frames, skb);
			skb_queue_tail(&dev->rx_frames, skb);
		}
		}
		skb = NULL;
		if (!status)

			rx_queue = 1;
		skb2 = skb_dequeue(&dev->rx_frames);
		while (skb2) {
			if (status < 0
					|| ETH_HLEN > skb2->len
					|| skb2->len > VLAN_ETH_FRAME_LEN) {
				dev->net->stats.rx_errors++;
				dev->net->stats.rx_length_errors++;
				DBG(dev, "rx length %d\n", skb2->len);
				dev_kfree_skb_any(skb2);
				goto next_frame;
			}
			skb2->protocol = eth_type_trans(skb2, dev->net);
			dev->net->stats.rx_packets++;
			dev->net->stats.rx_bytes += skb2->len;

			/* no buffer copies needed, unless hardware can't
			 * use skb buffers.
			 */
			status = netif_rx(skb2);
next_frame:
			skb2 = skb_dequeue(&dev->rx_frames);
		}
		break;
		break;


	/* software-driven interface shutdown */
	/* software-driven interface shutdown */
@@ -334,22 +313,20 @@ quiesce:
		/* FALLTHROUGH */
		/* FALLTHROUGH */


	default:
	default:
		rx_queue = 1;
		dev_kfree_skb_any(skb);
		dev->net->stats.rx_errors++;
		dev->net->stats.rx_errors++;
		DBG(dev, "rx status %d\n", status);
		DBG(dev, "rx status %d\n", status);
		break;
		break;
	}
	}


	if (skb)
		dev_kfree_skb_any(skb);
	if (!netif_running(dev->net)) {
clean:
clean:
		spin_lock(&dev->req_lock);
		spin_lock(&dev->req_lock);
		list_add(&req->list, &dev->rx_reqs);
		list_add(&req->list, &dev->rx_reqs);
		spin_unlock(&dev->req_lock);
		spin_unlock(&dev->req_lock);
		req = NULL;

	}
	if (rx_queue && likely(napi_schedule_prep(&dev->rx_napi)))
	if (req)
		__napi_schedule(&dev->rx_napi);
		rx_submit(dev, req, GFP_ATOMIC);
}
}


static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
@@ -414,16 +391,24 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
{
{
	struct usb_request	*req;
	struct usb_request	*req;
	unsigned long		flags;
	unsigned long		flags;
	int			rx_counts = 0;


	/* fill unused rxq slots with some skb */
	/* fill unused rxq slots with some skb */
	spin_lock_irqsave(&dev->req_lock, flags);
	spin_lock_irqsave(&dev->req_lock, flags);
	while (!list_empty(&dev->rx_reqs)) {
	while (!list_empty(&dev->rx_reqs)) {

		if (++rx_counts > qlen(dev->gadget, dev->qmult))
			break;

		req = container_of(dev->rx_reqs.next,
		req = container_of(dev->rx_reqs.next,
				struct usb_request, list);
				struct usb_request, list);
		list_del_init(&req->list);
		list_del_init(&req->list);
		spin_unlock_irqrestore(&dev->req_lock, flags);
		spin_unlock_irqrestore(&dev->req_lock, flags);


		if (rx_submit(dev, req, gfp_flags) < 0) {
		if (rx_submit(dev, req, gfp_flags) < 0) {
			spin_lock_irqsave(&dev->req_lock, flags);
			list_add(&req->list, &dev->rx_reqs);
			spin_unlock_irqrestore(&dev->req_lock, flags);
			defer_kevent(dev, WORK_RX_MEMORY);
			defer_kevent(dev, WORK_RX_MEMORY);
			return;
			return;
		}
		}
@@ -433,6 +418,41 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
	spin_unlock_irqrestore(&dev->req_lock, flags);
	spin_unlock_irqrestore(&dev->req_lock, flags);
}
}


static int gether_poll(struct napi_struct *napi, int budget)
{
	struct eth_dev  *dev = container_of(napi, struct eth_dev, rx_napi);
	struct sk_buff	*skb;
	unsigned int	work_done = 0;
	int		status = 0;

	while ((skb = skb_dequeue(&dev->rx_frames))) {
		if (status < 0
				|| ETH_HLEN > skb->len
				|| skb->len > VLAN_ETH_FRAME_LEN) {
			dev->net->stats.rx_errors++;
			dev->net->stats.rx_length_errors++;
			DBG(dev, "rx length %d\n", skb->len);
			dev_kfree_skb_any(skb);
			continue;
		}
		skb->protocol = eth_type_trans(skb, dev->net);
		dev->net->stats.rx_packets++;
		dev->net->stats.rx_bytes += skb->len;

		status = netif_rx_ni(skb);
	}

	if (netif_running(dev->net)) {
		rx_fill(dev, GFP_KERNEL);
		work_done++;
	}

	if (work_done < budget)
		napi_complete(&dev->rx_napi);

	return work_done;
}

static void eth_work(struct work_struct *work)
static void eth_work(struct work_struct *work)
{
{
	struct eth_dev	*dev = container_of(work, struct eth_dev, work);
	struct eth_dev	*dev = container_of(work, struct eth_dev, work);
@@ -625,6 +645,7 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
	/* and open the tx floodgates */
	/* and open the tx floodgates */
	atomic_set(&dev->tx_qlen, 0);
	atomic_set(&dev->tx_qlen, 0);
	netif_wake_queue(dev->net);
	netif_wake_queue(dev->net);
	napi_enable(&dev->rx_napi);
}
}


static int eth_open(struct net_device *net)
static int eth_open(struct net_device *net)
@@ -651,6 +672,7 @@ static int eth_stop(struct net_device *net)
	unsigned long	flags;
	unsigned long	flags;


	VDBG(dev, "%s\n", __func__);
	VDBG(dev, "%s\n", __func__);
	napi_disable(&dev->rx_napi);
	netif_stop_queue(net);
	netif_stop_queue(net);


	DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
	DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
@@ -768,6 +790,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
		return ERR_PTR(-ENOMEM);
		return ERR_PTR(-ENOMEM);


	dev = netdev_priv(net);
	dev = netdev_priv(net);
	netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT);
	spin_lock_init(&dev->lock);
	spin_lock_init(&dev->lock);
	spin_lock_init(&dev->req_lock);
	spin_lock_init(&dev->req_lock);
	INIT_WORK(&dev->work, eth_work);
	INIT_WORK(&dev->work, eth_work);
@@ -830,6 +853,7 @@ struct net_device *gether_setup_name_default(const char *netname)
		return ERR_PTR(-ENOMEM);
		return ERR_PTR(-ENOMEM);


	dev = netdev_priv(net);
	dev = netdev_priv(net);
	netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT);
	spin_lock_init(&dev->lock);
	spin_lock_init(&dev->lock);
	spin_lock_init(&dev->req_lock);
	spin_lock_init(&dev->req_lock);
	INIT_WORK(&dev->work, eth_work);
	INIT_WORK(&dev->work, eth_work);
@@ -1113,6 +1137,7 @@ void gether_disconnect(struct gether *link)
{
{
	struct eth_dev		*dev = link->ioport;
	struct eth_dev		*dev = link->ioport;
	struct usb_request	*req;
	struct usb_request	*req;
	struct sk_buff		*skb;


	WARN_ON(!dev);
	WARN_ON(!dev);
	if (!dev)
	if (!dev)
@@ -1139,6 +1164,12 @@ void gether_disconnect(struct gether *link)
		spin_lock(&dev->req_lock);
		spin_lock(&dev->req_lock);
	}
	}
	spin_unlock(&dev->req_lock);
	spin_unlock(&dev->req_lock);

	spin_lock(&dev->rx_frames.lock);
	while ((skb = __skb_dequeue(&dev->rx_frames)))
		dev_kfree_skb_any(skb);
	spin_unlock(&dev->rx_frames.lock);

	link->in_ep->driver_data = NULL;
	link->in_ep->driver_data = NULL;
	link->in_ep->desc = NULL;
	link->in_ep->desc = NULL;