diff options
author | Weinn Jheng <clanlab.proj@gmail.com> | 2014-02-27 09:49:00 (GMT) |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2014-03-07 16:03:24 (GMT) |
commit | 716fb91dfe1777bd6d5e598f3d3572214b3ed296 (patch) | |
tree | 75232c404790fbeb8816f244daeb4acf78d5cafa /drivers/usb | |
parent | 3f89204bae896e1d44468886a646b84acadc3c8f (diff) | |
download | linux-716fb91dfe1777bd6d5e598f3d3572214b3ed296.tar.xz |
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: Weinn 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: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/gadget/u_ether.c | 101 |
1 files changed, 66 insertions, 35 deletions
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c index b7d4f82..50d09c2 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c @@ -48,6 +48,8 @@ #define UETH__VERSION "29-May-2008" +#define GETHER_NAPI_WEIGHT 32 + struct eth_dev { /* lock is held while accessing port_usb */ @@ -72,6 +74,7 @@ struct eth_dev { struct sk_buff_head *list); struct work_struct work; + struct napi_struct rx_napi; unsigned long todo; #define WORK_RX_MEMORY 0 @@ -253,18 +256,16 @@ enomem: DBG(dev, "rx submit --> %d\n", retval); if (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; } 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; int status = req->status; + bool rx_queue = 0; switch (status) { @@ -288,30 +289,8 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) } else { skb_queue_tail(&dev->rx_frames, skb); } - skb = NULL; - - 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); - } + if (!status) + rx_queue = 1; break; /* software-driven interface shutdown */ @@ -334,22 +313,20 @@ quiesce: /* FALLTHROUGH */ default: + rx_queue = 1; + dev_kfree_skb_any(skb); dev->net->stats.rx_errors++; DBG(dev, "rx status %d\n", status); break; } - if (skb) - dev_kfree_skb_any(skb); - if (!netif_running(dev->net)) { clean: spin_lock(&dev->req_lock); list_add(&req->list, &dev->rx_reqs); spin_unlock(&dev->req_lock); - req = NULL; - } - if (req) - rx_submit(dev, req, GFP_ATOMIC); + + if (rx_queue && likely(napi_schedule_prep(&dev->rx_napi))) + __napi_schedule(&dev->rx_napi); } 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; unsigned long flags; + int rx_counts = 0; /* fill unused rxq slots with some skb */ spin_lock_irqsave(&dev->req_lock, flags); while (!list_empty(&dev->rx_reqs)) { + + if (++rx_counts > qlen(dev->gadget, dev->qmult)) + break; + req = container_of(dev->rx_reqs.next, struct usb_request, list); list_del_init(&req->list); spin_unlock_irqrestore(&dev->req_lock, flags); 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); return; } @@ -433,6 +418,41 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_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) { 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 */ atomic_set(&dev->tx_qlen, 0); netif_wake_queue(dev->net); + napi_enable(&dev->rx_napi); } static int eth_open(struct net_device *net) @@ -651,6 +672,7 @@ static int eth_stop(struct net_device *net) unsigned long flags; VDBG(dev, "%s\n", __func__); + napi_disable(&dev->rx_napi); netif_stop_queue(net); 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); 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->req_lock); INIT_WORK(&dev->work, eth_work); @@ -830,6 +853,7 @@ struct net_device *gether_setup_name_default(const char *netname) return ERR_PTR(-ENOMEM); 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->req_lock); INIT_WORK(&dev->work, eth_work); @@ -1113,6 +1137,7 @@ void gether_disconnect(struct gether *link) { struct eth_dev *dev = link->ioport; struct usb_request *req; + struct sk_buff *skb; WARN_ON(!dev); if (!dev) @@ -1139,6 +1164,12 @@ void gether_disconnect(struct gether *link) spin_lock(&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->desc = NULL; |