Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usbnet: optimize usbnet_bh() to reduce CPU load

The current source pushes skb into dev-done queue by calling
skb_dequeue_tail() and then pop it by skb_dequeue() to branch to
rx_cleanup state for freeing urb/skb in usbnet_bh(). It takes extra CPU
load, 2.21% (skb_queue_tail) as follows,

- 11.58% 0.26% swapper [k] usbnet_bh
- 11.32% usbnet_bh
- 6.43% skb_dequeue
6.34% _raw_spin_unlock_irqrestore
- 2.21% skb_queue_tail
2.19% _raw_spin_unlock_irqrestore
- 1.68% consume_skb
- 0.97% kfree_skbmem
0.80% kmem_cache_free
0.53% skb_release_data

To reduce the extra CPU load use return values to call helper function
usb_free_skb() to free the resources instead of calling skb_queue_tail()
and skb_dequeue() for push and pop respectively.

- 7.87% 0.25% swapper [k] usbnet_bh
- 7.62% usbnet_bh
- 4.81% skb_dequeue
4.74% _raw_spin_unlock_irqrestore
- 1.75% consume_skb
- 0.98% kfree_skbmem
0.78% kmem_cache_free
0.58% skb_release_data
0.53% smsc95xx_rx_fixup

Signed-off-by: Leesoo Ahn <lsahn@ooseel.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Leesoo Ahn and committed by
David S. Miller
fb59bf28 9cb8bae3

+17 -12
+17 -12
drivers/net/usb/usbnet.c
··· 555 555 556 556 /*-------------------------------------------------------------------------*/ 557 557 558 - static inline void rx_process (struct usbnet *dev, struct sk_buff *skb) 558 + static inline int rx_process(struct usbnet *dev, struct sk_buff *skb) 559 559 { 560 560 if (dev->driver_info->rx_fixup && 561 561 !dev->driver_info->rx_fixup (dev, skb)) { 562 562 /* With RX_ASSEMBLE, rx_fixup() must update counters */ 563 563 if (!(dev->driver_info->flags & FLAG_RX_ASSEMBLE)) 564 564 dev->net->stats.rx_errors++; 565 - goto done; 565 + return -EPROTO; 566 566 } 567 567 // else network stack removes extra byte if we forced a short packet 568 568 569 569 /* all data was already cloned from skb inside the driver */ 570 570 if (dev->driver_info->flags & FLAG_MULTI_PACKET) 571 - goto done; 571 + return -EALREADY; 572 572 573 573 if (skb->len < ETH_HLEN) { 574 574 dev->net->stats.rx_errors++; 575 575 dev->net->stats.rx_length_errors++; 576 576 netif_dbg(dev, rx_err, dev->net, "rx length %d\n", skb->len); 577 - } else { 578 - usbnet_skb_return(dev, skb); 579 - return; 577 + return -EPROTO; 580 578 } 581 579 582 - done: 583 - skb_queue_tail(&dev->done, skb); 580 + usbnet_skb_return(dev, skb); 581 + return 0; 584 582 } 585 583 586 584 /*-------------------------------------------------------------------------*/ ··· 1512 1514 return ret; 1513 1515 } 1514 1516 1517 + static inline void usb_free_skb(struct sk_buff *skb) 1518 + { 1519 + struct skb_data *entry = (struct skb_data *)skb->cb; 1520 + 1521 + usb_free_urb(entry->urb); 1522 + dev_kfree_skb(skb); 1523 + } 1524 + 1515 1525 /*-------------------------------------------------------------------------*/ 1516 1526 1517 1527 // tasklet (work deferred from completions, in_irq) or timer ··· 1534 1528 entry = (struct skb_data *) skb->cb; 1535 1529 switch (entry->state) { 1536 1530 case rx_done: 1537 - entry->state = rx_cleanup; 1538 - rx_process (dev, skb); 1531 + if (rx_process(dev, skb)) 1532 + usb_free_skb(skb); 1539 1533 continue; 1540 1534 case tx_done: 1541 1535 kfree(entry->urb->sg); 1542 1536 fallthrough; 1543 1537 case rx_cleanup: 1544 - usb_free_urb (entry->urb); 1545 - dev_kfree_skb (skb); 1538 + usb_free_skb(skb); 1546 1539 continue; 1547 1540 default: 1548 1541 netdev_dbg(dev->net, "bogus skb state %d\n", entry->state);