[PATCH net-next v3 12/12] net: mctp: usb: Allow multiple urbs in flight
From: Jeremy Kerr <jk@codeconstruct.com.au>
Date: 2026-07-08 09:59:53
Also in:
linux-usb
Subsystem:
management component transport protocol (mctp), networking drivers, the rest · Maintainers:
Jeremy Kerr, Matt Johnston, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
Currently, we stop tx queues when we have one urb submitted. This means we will immediately hit dev_hard_start_xmit's tx-queues-off -> NETDEV_TX_BUSY case, and revert to the requeue -> gso_skb single-dequeue path, and no longer be able to pack skbs without an xmit_more indication. Instead, allow a few urbs to be in-flight, with a limit of 16kB of data outstanding (after which we will disable queues). With this, the tx path will cause fewer requeues (and therefore non-packed transfers) under normal loads. Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au> --- v2: - rework tx_qmem locking, prevent race between completion and submission for stopping/waking the tx queues; perform the wake while locked - only wake if netif_running() --- drivers/net/mctp/mctp-usb.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-)
diff --git a/drivers/net/mctp/mctp-usb.c b/drivers/net/mctp/mctp-usb.c
index 8e29be9ebd1d..2d31075374b4 100644
--- a/drivers/net/mctp/mctp-usb.c
+++ b/drivers/net/mctp/mctp-usb.c@@ -42,6 +42,9 @@ struct mctp_usb { struct mctp_usblib_tx tx; struct usb_anchor tx_anchor; + /* serialises tx_qmem updates to netdev queue states */ + spinlock_t tx_qmem_lock; + int tx_qmem; }; enum {
@@ -49,23 +52,41 @@ enum { MCTP_USB_SUBCLASS_SPAN = 0x02, }; +/* We use a total-size limit for outstanding URBs, as the transfer counts + * may vary a lot between spanning- and non-spanning modes. In spanning mode, + * this will allow for a couple of max-sized transfers to be in flight. In + * non-spanning mode, 32. + * + * We want to avoid disabling the tx queue if possible; doing so will end up + * requeueing to gso_skb, and we only dequeue from that one skb at a time, + * so can no longer perform transfer packing. + */ +static const unsigned int TX_QMEM_MAX = 16384; + static void mctp_usb_out_complete(struct urb *urb) { struct mctp_usblib_tx_ctx *tx_ctx = urb->context; struct mctp_usb *mctp_usb = mctp_usblib_tx_ctx_priv(tx_ctx); + unsigned int len = urb->transfer_buffer_length; struct net_device *netdev = mctp_usb->netdev; + unsigned long flags; mctp_usblib_tx_send_complete(tx_ctx, netdev, urb->status == 0); usb_free_urb(urb); - netif_wake_queue(netdev); + spin_lock_irqsave(&mctp_usb->tx_qmem_lock, flags); + mctp_usb->tx_qmem -= len; + if (mctp_usb->tx_qmem < TX_QMEM_MAX && netif_running(netdev)) + netif_wake_queue(netdev); + spin_unlock_irqrestore(&mctp_usb->tx_qmem_lock, flags); } static int mctp_usb_tx_send(struct mctp_usblib_tx_ctx *tx_ctx, void *data, size_t len) { struct mctp_usb *mctp_usb = mctp_usblib_tx_ctx_priv(tx_ctx); + unsigned long flags; struct urb *urb; int rc;
@@ -80,8 +101,6 @@ static int mctp_usb_tx_send(struct mctp_usblib_tx_ctx *tx_ctx, if (mctp_usb->span) urb->transfer_flags |= URB_ZERO_PACKET; - netif_stop_queue(mctp_usb->netdev); - usb_anchor_urb(urb, &mctp_usb->tx_anchor); rc = usb_submit_urb(urb, GFP_ATOMIC);
@@ -89,7 +108,12 @@ static int mctp_usb_tx_send(struct mctp_usblib_tx_ctx *tx_ctx, netdev_dbg(mctp_usb->netdev, "TX urb submit failed, %d\n", rc); usb_unanchor_urb(urb); usb_free_urb(urb); - netif_start_queue(mctp_usb->netdev); + } else { + spin_lock_irqsave(&mctp_usb->tx_qmem_lock, flags); + mctp_usb->tx_qmem += len; + if (mctp_usb->tx_qmem >= TX_QMEM_MAX) + netif_stop_queue(mctp_usb->netdev); + spin_unlock_irqrestore(&mctp_usb->tx_qmem_lock, flags); } return rc;
@@ -333,6 +357,7 @@ static int mctp_usb_probe(struct usb_interface *intf, spin_lock_init(&dev->rx_lock); if (dev->span) netdev->max_mtu = MCTP_USB_1_1_MTU_MAX; + spin_lock_init(&dev->tx_qmem_lock); usb_set_intfdata(intf, dev); mctp_usblib_rx_init(&dev->rx, le16_to_cpu(ep_in->wMaxPacketSize),
--
2.47.3