From f4d3c0039dafa292f2482d5951c368e6c3f7bfc3 Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Wed, 30 Sep 2026 11:52:55 +0200 Subject: [PATCH 1/2] net: macb: reap lost TX completions on TX timeout When the frames went out but their TCOMP interrupt got lost, TBQP already matches tx_head and macb_tx_restart() does nothing. TX NAPI never runs again and the queue stays stopped for good. Fixes: e438ec3e9e95 ("net: macb: add TX stall timeout callback to recover from lost TSTART write") Link: https://github.com/raspberrypi/linux/issues/7661 [nb: open-code macb_queue_isr_clear(), not available in 6.18] Signed-off-by: Nicolai Buchwitz --- drivers/net/ethernet/cadence/macb_main.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 95bc3839173c15..40d9540351104c 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -4699,8 +4699,20 @@ static int macb_setup_tc(struct net_device *dev, enum tc_setup_type type, static void macb_tx_timeout(struct net_device *dev, unsigned int q) { struct macb *bp = netdev_priv(dev); + struct macb_queue *queue = &bp->queues[q]; - macb_tx_restart(&bp->queues[q]); + macb_tx_restart(queue); + + /* The frames may have gone out with their TCOMP interrupt lost. + * macb_tx_restart() does nothing then as TBQP already matches + * tx_head, so reap them from NAPI or the queue stays stopped. + */ + if (macb_tx_complete_pending(queue)) { + queue_writel(queue, IDR, MACB_BIT(TCOMP)); + if (bp->caps & MACB_CAPS_ISR_CLEAR_ON_WRITE) + queue_writel(queue, ISR, MACB_BIT(TCOMP)); + napi_schedule(&queue->napi_tx); + } } static const struct net_device_ops macb_netdev_ops = { From e8ebfe75804d0c0257eb145bd1505f71cbac46e9 Mon Sep 17 00:00:00 2001 From: Nicolai Buchwitz Date: Wed, 30 Sep 2026 11:52:55 +0200 Subject: [PATCH 2/2] net: macb: credit unsent frames to BQL in macb_tx_error_task() The frames dropped on a TX error are never returned to BQL. Each error shrinks the BQL budget for good until the queue stops with an empty ring. netdev_tx_reset_queue() is not safe here as the task runs without tx_ptr_lock, so macb_start_xmit() may still be queueing. Fixes: e6a532185daa ("net: cadence: macb: Implement BQL") Signed-off-by: Nicolai Buchwitz --- drivers/net/ethernet/cadence/macb_main.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 40d9540351104c..6434129dedf060 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -1357,10 +1357,8 @@ static void macb_tx_error_task(struct work_struct *work) skb->data); bp->dev->stats.tx_packets++; queue->stats.tx_packets++; - packets++; bp->dev->stats.tx_bytes += skb->len; queue->stats.tx_bytes += skb->len; - bytes += skb->len; } } else { /* "Buffers exhausted mid-frame" errors may only happen @@ -1374,6 +1372,12 @@ static void macb_tx_error_task(struct work_struct *work) desc->ctrl = ctrl | MACB_BIT(TX_USED); } + /* BQL has to see every frame leaving the ring, sent or not */ + if (skb) { + packets++; + bytes += skb->len; + } + macb_tx_unmap(bp, tx_skb, 0); }