From: Jesper Dangaard Brouer With the introduction of BQL (Byte Queue Limits) for veth, there are now two independent mechanisms that can stop a transmit queue: - DRV_XOFF: set by netif_tx_stop_queue() when the ptr_ring is full - STACK_XOFF: set by BQL when the byte-in-flight limit is reached If either mechanism stalls without a corresponding wake/completion, the queue stops permanently. Enable the net device watchdog timer and implement ndo_tx_timeout as a failsafe recovery. The timeout handler resets BQL state (clearing STACK_XOFF) and wakes the queue (clearing DRV_XOFF), covering both stop mechanisms. The watchdog fires after 16 seconds, which accommodates worst-case NAPI processing (budget=64 packets x 250ms per-packet consumer delay) without false positives under normal backpressure. Signed-off-by: Jesper Dangaard Brouer Tested-by: Jonas Köppeler --- drivers/net/veth.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index a3505627f49e..2473f730734b 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -44,6 +44,13 @@ #define VETH_XDP_TX_BULK_SIZE 16 #define VETH_XDP_BATCH 16 +/* tx_timeout watchdog timeout. DRV_XOFF is only cleared at the end of a NAPI + * veth_poll() (netif_tx_wake_queue()), so the timeout must outlast a full + * worst-case poll: a 64-packet budget with a pessimistic 250 ms/pkt consumer + * delay => 64 * 250 ms = 16 s. + */ +#define VETH_WATCHDOG_TIMEOUT_MS (64 * 250) + struct veth_stats { u64 rx_drops; /* xdp */ @@ -1487,6 +1494,22 @@ static int veth_set_channels(struct net_device *dev, goto out; } +static void veth_tx_timeout(struct net_device *dev, unsigned int txqueue) +{ + struct netdev_queue *txq = netdev_get_tx_queue(dev, txqueue); + + netdev_err(dev, + "veth backpressure(0x%lX) stalled(n:%ld) TXQ(%u) re-enable\n", + txq->state, atomic_long_read(&txq->trans_timeout), txqueue); + + /* Cannot call netdev_tx_reset_queue(): dql_reset() races with + * peer NAPI calling dql_completed() concurrently. + * Just clear the stop bits; the qdisc will re-stop if still stuck. + */ + clear_bit(__QUEUE_STATE_STACK_XOFF, &txq->state); + netif_tx_wake_queue(txq); +} + static int veth_open(struct net_device *dev) { struct veth_priv *priv = netdev_priv(dev); @@ -1825,6 +1848,7 @@ static const struct net_device_ops veth_netdev_ops = { .ndo_bpf = veth_xdp, .ndo_xdp_xmit = veth_ndo_xdp_xmit, .ndo_get_peer_dev = veth_peer_dev, + .ndo_tx_timeout = veth_tx_timeout, }; static const struct xdp_metadata_ops veth_xdp_metadata_ops = { @@ -1864,6 +1888,7 @@ static void veth_setup(struct net_device *dev) dev->priv_destructor = veth_dev_free; dev->pcpu_stat_type = NETDEV_PCPU_STAT_TSTATS; dev->max_mtu = ETH_MAX_MTU; + dev->watchdog_timeo = msecs_to_jiffies(VETH_WATCHDOG_TIMEOUT_MS); dev->hw_features = VETH_FEATURES; dev->hw_enc_features = VETH_FEATURES; -- 2.43.0