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

usbnet: Add support for Byte Queue Limits (BQL)

In the current implementation, usbnet uses a fixed tx_qlen of:

USB2: 60 * 1518 bytes = 91.08 KB
USB3: 60 * 5 * 1518 bytes = 454.80 KB

Such large transmit queues can be problematic, especially for cellular
modems. For example, with a typical celluar link speed of 10 Mbit/s, a
fully occupied USB3 transmit queue results in:

454.80 KB / (10 Mbit/s / 8 bit/byte) = 363.84 ms

of additional latency.

This patch adds support for Byte Queue Limits (BQL) [1] to dynamically
manage the transmit queue size and reduce latency without sacrificing
throughput.

Testing was performed on various devices using the usbnet driver for
packet transmission:

- DELOCK 66045: USB3 to 2.5 GbE adapter (ax88179_178a)
- DELOCK 61969: USB2 to 1 GbE adapter (asix)
- Quectel RM520: 5G modem (qmi_wwan)
- USB2 Android tethering (cdc_ncm)

No performance degradation was observed for iperf3 TCP or UDP traffic,
while latency for a prioritized ping application was significantly
reduced. For example, using the USB3 to 2.5 GbE adapter, which was fully
utilized by iperf3 UDP traffic, the prioritized ping was improved from
1.6 ms to 0.6 ms. With the same setup but with a 100 Mbit/s Ethernet
connection, the prioritized ping was improved from 35 ms to 5 ms.

[1] https://lwn.net/Articles/469652/

Signed-off-by: Simon Schippers <simon.schippers@tu-dortmund.de>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20251106175615.26948-1-simon.schippers@tu-dortmund.de
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Simon Schippers and committed by
Jakub Kicinski
7ff14c52 23c52b58

+13
+11
drivers/net/usb/usbnet.c
··· 831 831 832 832 clear_bit(EVENT_DEV_OPEN, &dev->flags); 833 833 netif_stop_queue (net); 834 + netdev_reset_queue(net); 834 835 835 836 netif_info(dev, ifdown, dev->net, 836 837 "stop stats: rx/tx %lu/%lu, errs %lu/%lu\n", ··· 940 939 } 941 940 942 941 set_bit(EVENT_DEV_OPEN, &dev->flags); 942 + netdev_reset_queue(net); 943 943 netif_start_queue (net); 944 944 netif_info(dev, ifup, dev->net, 945 945 "open: enable queueing (rx %d, tx %d) mtu %d %s framing\n", ··· 1502 1500 case 0: 1503 1501 netif_trans_update(net); 1504 1502 __usbnet_queue_skb(&dev->txq, skb, tx_start); 1503 + netdev_sent_queue(net, skb->len); 1505 1504 if (dev->txq.qlen >= TX_QLEN (dev)) 1506 1505 netif_stop_queue (net); 1507 1506 } ··· 1566 1563 static void usbnet_bh(struct timer_list *t) 1567 1564 { 1568 1565 struct usbnet *dev = timer_container_of(dev, t, delay); 1566 + unsigned int bytes_compl = 0, pkts_compl = 0; 1569 1567 struct sk_buff *skb; 1570 1568 struct skb_data *entry; 1571 1569 ··· 1578 1574 usb_free_skb(skb); 1579 1575 continue; 1580 1576 case tx_done: 1577 + bytes_compl += skb->len; 1578 + pkts_compl++; 1581 1579 kfree(entry->urb->sg); 1582 1580 fallthrough; 1583 1581 case rx_cleanup: ··· 1589 1583 netdev_dbg(dev->net, "bogus skb state %d\n", entry->state); 1590 1584 } 1591 1585 } 1586 + 1587 + spin_lock_bh(&dev->bql_spinlock); 1588 + netdev_completed_queue(dev->net, pkts_compl, bytes_compl); 1589 + spin_unlock_bh(&dev->bql_spinlock); 1592 1590 1593 1591 /* restart RX again after disabling due to high error rate */ 1594 1592 clear_bit(EVENT_RX_KILL, &dev->flags); ··· 1765 1755 skb_queue_head_init (&dev->txq); 1766 1756 skb_queue_head_init (&dev->done); 1767 1757 skb_queue_head_init(&dev->rxq_pause); 1758 + spin_lock_init(&dev->bql_spinlock); 1768 1759 INIT_WORK(&dev->bh_work, usbnet_bh_work); 1769 1760 INIT_WORK (&dev->kevent, usbnet_deferred_kevent); 1770 1761 init_usb_anchor(&dev->deferred);
+2
include/linux/usb/usbnet.h
··· 14 14 #include <linux/skbuff.h> 15 15 #include <linux/types.h> 16 16 #include <linux/usb.h> 17 + #include <linux/spinlock.h> 17 18 18 19 /* interface from usbnet core to each USB networking link we handle */ 19 20 struct usbnet { ··· 60 59 struct mutex interrupt_mutex; 61 60 struct usb_anchor deferred; 62 61 struct work_struct bh_work; 62 + spinlock_t bql_spinlock; 63 63 64 64 struct work_struct kevent; 65 65 unsigned long flags;