diff options
author | Weinn Jheng <clanlab.proj@gmail.com> | 2014-02-27 04:49:00 -0500 |
---|---|---|
committer | Felipe Balbi <balbi@ti.com> | 2014-03-07 11:03:24 -0500 |
commit | 716fb91dfe1777bd6d5e598f3d3572214b3ed296 (patch) | |
tree | 75232c404790fbeb8816f244daeb4acf78d5cafa /drivers/usb | |
parent | 3f89204bae896e1d44468886a646b84acadc3c8f (diff) |
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 b7d4f82872b7..50d09c289137 100644 --- a/drivers/usb/gadget/u_ether.c +++ b/drivers/usb/gadget/u_ether.c | |||
@@ -48,6 +48,8 @@ | |||
48 | 48 | ||
49 | #define UETH__VERSION "29-May-2008" | 49 | #define UETH__VERSION "29-May-2008" |
50 | 50 | ||
51 | #define GETHER_NAPI_WEIGHT 32 | ||
52 | |||
51 | struct eth_dev { | 53 | struct eth_dev { |
52 | /* lock is held while accessing port_usb | 54 | /* lock is held while accessing port_usb |
53 | */ | 55 | */ |
@@ -72,6 +74,7 @@ struct eth_dev { | |||
72 | struct sk_buff_head *list); | 74 | struct sk_buff_head *list); |
73 | 75 | ||
74 | struct work_struct work; | 76 | struct work_struct work; |
77 | struct napi_struct rx_napi; | ||
75 | 78 | ||
76 | unsigned long todo; | 79 | unsigned long todo; |
77 | #define WORK_RX_MEMORY 0 | 80 | #define WORK_RX_MEMORY 0 |
@@ -253,18 +256,16 @@ enomem: | |||
253 | DBG(dev, "rx submit --> %d\n", retval); | 256 | DBG(dev, "rx submit --> %d\n", retval); |
254 | if (skb) | 257 | if (skb) |
255 | dev_kfree_skb_any(skb); | 258 | dev_kfree_skb_any(skb); |
256 | spin_lock_irqsave(&dev->req_lock, flags); | ||
257 | list_add(&req->list, &dev->rx_reqs); | ||
258 | spin_unlock_irqrestore(&dev->req_lock, flags); | ||
259 | } | 259 | } |
260 | return retval; | 260 | return retval; |
261 | } | 261 | } |
262 | 262 | ||
263 | static void rx_complete(struct usb_ep *ep, struct usb_request *req) | 263 | static void rx_complete(struct usb_ep *ep, struct usb_request *req) |
264 | { | 264 | { |
265 | struct sk_buff *skb = req->context, *skb2; | 265 | struct sk_buff *skb = req->context; |
266 | struct eth_dev *dev = ep->driver_data; | 266 | struct eth_dev *dev = ep->driver_data; |
267 | int status = req->status; | 267 | int status = req->status; |
268 | bool rx_queue = 0; | ||
268 | 269 | ||
269 | switch (status) { | 270 | switch (status) { |
270 | 271 | ||
@@ -288,30 +289,8 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) | |||
288 | } else { | 289 | } else { |
289 | skb_queue_tail(&dev->rx_frames, skb); | 290 | skb_queue_tail(&dev->rx_frames, skb); |
290 | } | 291 | } |
291 | skb = NULL; | 292 | if (!status) |
292 | 293 | rx_queue = 1; | |
293 | skb2 = skb_dequeue(&dev->rx_frames); | ||
294 | while (skb2) { | ||
295 | if (status < 0 | ||
296 | || ETH_HLEN > skb2->len | ||
297 | || skb2->len > VLAN_ETH_FRAME_LEN) { | ||
298 | dev->net->stats.rx_errors++; | ||
299 | dev->net->stats.rx_length_errors++; | ||
300 | DBG(dev, "rx length %d\n", skb2->len); | ||
301 | dev_kfree_skb_any(skb2); | ||
302 | goto next_frame; | ||
303 | } | ||
304 | skb2->protocol = eth_type_trans(skb2, dev->net); | ||
305 | dev->net->stats.rx_packets++; | ||
306 | dev->net->stats.rx_bytes += skb2->len; | ||
307 | |||
308 | /* no buffer copies needed, unless hardware can't | ||
309 | * use skb buffers. | ||
310 | */ | ||
311 | status = netif_rx(skb2); | ||
312 | next_frame: | ||
313 | skb2 = skb_dequeue(&dev->rx_frames); | ||
314 | } | ||
315 | break; | 294 | break; |
316 | 295 | ||
317 | /* software-driven interface shutdown */ | 296 | /* software-driven interface shutdown */ |
@@ -334,22 +313,20 @@ quiesce: | |||
334 | /* FALLTHROUGH */ | 313 | /* FALLTHROUGH */ |
335 | 314 | ||
336 | default: | 315 | default: |
316 | rx_queue = 1; | ||
317 | dev_kfree_skb_any(skb); | ||
337 | dev->net->stats.rx_errors++; | 318 | dev->net->stats.rx_errors++; |
338 | DBG(dev, "rx status %d\n", status); | 319 | DBG(dev, "rx status %d\n", status); |
339 | break; | 320 | break; |
340 | } | 321 | } |
341 | 322 | ||
342 | if (skb) | ||
343 | dev_kfree_skb_any(skb); | ||
344 | if (!netif_running(dev->net)) { | ||
345 | clean: | 323 | clean: |
346 | spin_lock(&dev->req_lock); | 324 | spin_lock(&dev->req_lock); |
347 | list_add(&req->list, &dev->rx_reqs); | 325 | list_add(&req->list, &dev->rx_reqs); |
348 | spin_unlock(&dev->req_lock); | 326 | spin_unlock(&dev->req_lock); |
349 | req = NULL; | 327 | |
350 | } | 328 | if (rx_queue && likely(napi_schedule_prep(&dev->rx_napi))) |
351 | if (req) | 329 | __napi_schedule(&dev->rx_napi); |
352 | rx_submit(dev, req, GFP_ATOMIC); | ||
353 | } | 330 | } |
354 | 331 | ||
355 | static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n) | 332 | 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) | |||
414 | { | 391 | { |
415 | struct usb_request *req; | 392 | struct usb_request *req; |
416 | unsigned long flags; | 393 | unsigned long flags; |
394 | int rx_counts = 0; | ||
417 | 395 | ||
418 | /* fill unused rxq slots with some skb */ | 396 | /* fill unused rxq slots with some skb */ |
419 | spin_lock_irqsave(&dev->req_lock, flags); | 397 | spin_lock_irqsave(&dev->req_lock, flags); |
420 | while (!list_empty(&dev->rx_reqs)) { | 398 | while (!list_empty(&dev->rx_reqs)) { |
399 | |||
400 | if (++rx_counts > qlen(dev->gadget, dev->qmult)) | ||
401 | break; | ||
402 | |||
421 | req = container_of(dev->rx_reqs.next, | 403 | req = container_of(dev->rx_reqs.next, |
422 | struct usb_request, list); | 404 | struct usb_request, list); |
423 | list_del_init(&req->list); | 405 | list_del_init(&req->list); |
424 | spin_unlock_irqrestore(&dev->req_lock, flags); | 406 | spin_unlock_irqrestore(&dev->req_lock, flags); |
425 | 407 | ||
426 | if (rx_submit(dev, req, gfp_flags) < 0) { | 408 | if (rx_submit(dev, req, gfp_flags) < 0) { |
409 | spin_lock_irqsave(&dev->req_lock, flags); | ||
410 | list_add(&req->list, &dev->rx_reqs); | ||
411 | spin_unlock_irqrestore(&dev->req_lock, flags); | ||
427 | defer_kevent(dev, WORK_RX_MEMORY); | 412 | defer_kevent(dev, WORK_RX_MEMORY); |
428 | return; | 413 | return; |
429 | } | 414 | } |
@@ -433,6 +418,41 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags) | |||
433 | spin_unlock_irqrestore(&dev->req_lock, flags); | 418 | spin_unlock_irqrestore(&dev->req_lock, flags); |
434 | } | 419 | } |
435 | 420 | ||
421 | static int gether_poll(struct napi_struct *napi, int budget) | ||
422 | { | ||
423 | struct eth_dev *dev = container_of(napi, struct eth_dev, rx_napi); | ||
424 | struct sk_buff *skb; | ||
425 | unsigned int work_done = 0; | ||
426 | int status = 0; | ||
427 | |||
428 | while ((skb = skb_dequeue(&dev->rx_frames))) { | ||
429 | if (status < 0 | ||
430 | || ETH_HLEN > skb->len | ||
431 | || skb->len > VLAN_ETH_FRAME_LEN) { | ||
432 | dev->net->stats.rx_errors++; | ||
433 | dev->net->stats.rx_length_errors++; | ||
434 | DBG(dev, "rx length %d\n", skb->len); | ||
435 | dev_kfree_skb_any(skb); | ||
436 | continue; | ||
437 | } | ||
438 | skb->protocol = eth_type_trans(skb, dev->net); | ||
439 | dev->net->stats.rx_packets++; | ||
440 | dev->net->stats.rx_bytes += skb->len; | ||
441 | |||
442 | status = netif_rx_ni(skb); | ||
443 | } | ||
444 | |||
445 | if (netif_running(dev->net)) { | ||
446 | rx_fill(dev, GFP_KERNEL); | ||
447 | work_done++; | ||
448 | } | ||
449 | |||
450 | if (work_done < budget) | ||
451 | napi_complete(&dev->rx_napi); | ||
452 | |||
453 | return work_done; | ||
454 | } | ||
455 | |||
436 | static void eth_work(struct work_struct *work) | 456 | static void eth_work(struct work_struct *work) |
437 | { | 457 | { |
438 | struct eth_dev *dev = container_of(work, struct eth_dev, work); | 458 | 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) | |||
625 | /* and open the tx floodgates */ | 645 | /* and open the tx floodgates */ |
626 | atomic_set(&dev->tx_qlen, 0); | 646 | atomic_set(&dev->tx_qlen, 0); |
627 | netif_wake_queue(dev->net); | 647 | netif_wake_queue(dev->net); |
648 | napi_enable(&dev->rx_napi); | ||
628 | } | 649 | } |
629 | 650 | ||
630 | static int eth_open(struct net_device *net) | 651 | static int eth_open(struct net_device *net) |
@@ -651,6 +672,7 @@ static int eth_stop(struct net_device *net) | |||
651 | unsigned long flags; | 672 | unsigned long flags; |
652 | 673 | ||
653 | VDBG(dev, "%s\n", __func__); | 674 | VDBG(dev, "%s\n", __func__); |
675 | napi_disable(&dev->rx_napi); | ||
654 | netif_stop_queue(net); | 676 | netif_stop_queue(net); |
655 | 677 | ||
656 | DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", | 678 | 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, | |||
768 | return ERR_PTR(-ENOMEM); | 790 | return ERR_PTR(-ENOMEM); |
769 | 791 | ||
770 | dev = netdev_priv(net); | 792 | dev = netdev_priv(net); |
793 | netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT); | ||
771 | spin_lock_init(&dev->lock); | 794 | spin_lock_init(&dev->lock); |
772 | spin_lock_init(&dev->req_lock); | 795 | spin_lock_init(&dev->req_lock); |
773 | INIT_WORK(&dev->work, eth_work); | 796 | INIT_WORK(&dev->work, eth_work); |
@@ -830,6 +853,7 @@ struct net_device *gether_setup_name_default(const char *netname) | |||
830 | return ERR_PTR(-ENOMEM); | 853 | return ERR_PTR(-ENOMEM); |
831 | 854 | ||
832 | dev = netdev_priv(net); | 855 | dev = netdev_priv(net); |
856 | netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT); | ||
833 | spin_lock_init(&dev->lock); | 857 | spin_lock_init(&dev->lock); |
834 | spin_lock_init(&dev->req_lock); | 858 | spin_lock_init(&dev->req_lock); |
835 | INIT_WORK(&dev->work, eth_work); | 859 | INIT_WORK(&dev->work, eth_work); |
@@ -1113,6 +1137,7 @@ void gether_disconnect(struct gether *link) | |||
1113 | { | 1137 | { |
1114 | struct eth_dev *dev = link->ioport; | 1138 | struct eth_dev *dev = link->ioport; |
1115 | struct usb_request *req; | 1139 | struct usb_request *req; |
1140 | struct sk_buff *skb; | ||
1116 | 1141 | ||
1117 | WARN_ON(!dev); | 1142 | WARN_ON(!dev); |
1118 | if (!dev) | 1143 | if (!dev) |
@@ -1139,6 +1164,12 @@ void gether_disconnect(struct gether *link) | |||
1139 | spin_lock(&dev->req_lock); | 1164 | spin_lock(&dev->req_lock); |
1140 | } | 1165 | } |
1141 | spin_unlock(&dev->req_lock); | 1166 | spin_unlock(&dev->req_lock); |
1167 | |||
1168 | spin_lock(&dev->rx_frames.lock); | ||
1169 | while ((skb = __skb_dequeue(&dev->rx_frames))) | ||
1170 | dev_kfree_skb_any(skb); | ||
1171 | spin_unlock(&dev->rx_frames.lock); | ||
1172 | |||
1142 | link->in_ep->driver_data = NULL; | 1173 | link->in_ep->driver_data = NULL; |
1143 | link->in_ep->desc = NULL; | 1174 | link->in_ep->desc = NULL; |
1144 | 1175 | ||