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 --- 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 83961b6899d0..c7211db43c36 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; @@ -352,6 +376,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); rc = mctp_usblib_rx_init(&dev->rx, le16_to_cpu(ep_in->wMaxPacketSize), -- 2.47.3