[PATCH] usb: gadget: f_ncm: restart the TX timer if the TX freelist is empty
Cosmin Tanislav <[email protected]>
| Newsgroups | org.kernel.vger.stable,org.kernel.vger.linux-kernel,org.kernel.vger.linux-usb |
|---|---|
| Message-ID | <[email protected]> |
ncm_wrap_ntb() aggregates datagrams into a pending NTB, which is sent either when it becomes full, or after 300us since the NTB was started, using ndo_start_xmit() (eth_start_xmit()). ndo_start_xmit() returns NETDEV_TX_BUSY when the TX freelist is empty. For regular packets this is fine since the networking core will requeue them, but for timer-flushed packets there is no logic to handle this situation, and HRTIMER_NORESTART is returned from ncm_tx_timeout() without checking eth_start_xmit()'s return value. Under sustained TX saturation the request freelist is empty most of the time. Since the timer is never restarted, small packets keep getting accumulated into the NTB, until either TX_MAX_NUM_DPE packets are stored or NTB_DEFAULT_IN_SIZE is reached, which is an unknown amount of time. Check the return value of ndo_start_xmit() and restart the timer after another 300us. Cc: [email protected] Fixes: 6d3865f9d41f ("usb: gadget: NCM: Add transmit multi-frame.") Signed-off-by: Cosmin Tanislav <[email protected]> --- drivers/usb/gadget/function/f_ncm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index bf02545b37a2..1f521c1898d0 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -1148,6 +1148,7 @@ static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data) { struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer); struct net_device *netdev = READ_ONCE(ncm->netdev); + netdev_tx_t ret; if (netdev) { /* XXX This allowance of a NULL skb argument to ndo_start_xmit @@ -1158,7 +1159,11 @@ static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data) * * This will call directly into u_ether's eth_start_xmit() */ - netdev->netdev_ops->ndo_start_xmit(NULL, netdev); + ret = netdev->netdev_ops->ndo_start_xmit(NULL, netdev); + if (ret == NETDEV_TX_BUSY) { + hrtimer_forward_now(data, TX_TIMEOUT_NSECS); + return HRTIMER_RESTART; + } } return HRTIMER_NORESTART; } -- 2.55.0