From ced418ca2cdb5f8c0dc2f1a36590892034468ab6 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Wed, 28 Jan 2026 16:28:13 +0800 Subject: [PATCH] net: macb: Fix RX ring refill issue after link up When the link goes down, the receive pipeline is cleared but the RX queue pointer register remains unchanged. This causes a critical issue where some RX ring entries may contain invalid addresses after link up. The problem manifests when: 1. An entry with an invalid address prevents rx_tail from advancing (gem_rx() checks rxused as 0) 2. No gap exists between rx_prepared_head and rx_tail from previous refill operations 3. gem_rx_refill() cannot refill entries because CIRC_SPACE() always returns 0 4. RSR register reports exception with value 0x7 5. The hardware detected that the buffer was unavailable. The DMA will reread the pointer each time it receives an end-of-frame signal until a valid pointer is found, but the software cannot fill it. This regression was introduced by commit 99537d5c476c which moved mog_init_rings() from macb_mac_link_up() to macb_open(), leaving the RX ring in an inconsistent state after link transitions. Fix this by: - Reinitializing RX rings on link up via mog_init_rings() - Adding full_refill atomic counter to force complete ring refill - Ensuring all entries are traversed and refilled after link up - Resetting rx_tail and rx_prepared_head to 0 Fixes: 99537d5c476c ("net: macb: Relocate mog_init_rings() callback from macb_mac_link_up() to macb_open()") Closes: https://github.com/raspberrypi/linux/issues/7200#issuecomment-3770114859 Signed-off-by: Xiaolei Wang --- drivers/net/ethernet/cadence/macb.h | 1 + drivers/net/ethernet/cadence/macb_main.c | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb.h b/drivers/net/ethernet/cadence/macb.h index f797244f576416..90c0803c65c655 100644 --- a/drivers/net/ethernet/cadence/macb.h +++ b/drivers/net/ethernet/cadence/macb.h @@ -1259,6 +1259,7 @@ struct macb_queue { void *rx_buffers; struct napi_struct napi_rx; struct queue_stats stats; + atomic_t full_refill; }; struct ethtool_rx_fs_item { diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 30f0ea8c435686..913a58c969376e 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -775,11 +775,18 @@ static void macb_mac_link_up(struct phylink_config *config, /* Initialize rings & buffers as clearing MACB_BIT(TE) in link down * cleared the pipeline and control registers. */ - macb_init_buffers(bp); for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) + atomic_set(&queue->full_refill, -1); + + bp->macbgem_ops.mog_init_rings(bp); + macb_init_buffers(bp); + + for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) { + atomic_set(&queue->full_refill, bp->rx_ring_size); queue_writel(queue, IER, bp->rx_intr_mask | MACB_TX_INT_FLAGS | MACB_BIT(HRESP)); + } } macb_or_gem_writel(bp, NCFGR, ctrl); @@ -1327,8 +1334,9 @@ static void gem_rx_refill(struct macb_queue *queue) struct macb *bp = queue->bp; struct macb_dma_desc *desc; - while (CIRC_SPACE(queue->rx_prepared_head, queue->rx_tail, - bp->rx_ring_size) > 0) { + while (atomic_dec_if_positive(&queue->full_refill) >= 0 || + ((CIRC_SPACE(queue->rx_prepared_head, queue->rx_tail, bp->rx_ring_size) > 0) && + (atomic_read(&queue->full_refill) != -1))) { entry = macb_rx_ring_wrap(bp, queue->rx_prepared_head); /* Make hw descriptor updates visible to CPU */ @@ -1453,6 +1461,7 @@ static int gem_rx(struct macb_queue *queue, struct napi_struct *napi, queue->stats.rx_dropped++; break; } + /* now everything is ready for receiving packet */ queue->rx_skbuff[entry] = NULL; len = ctrl & bp->rx_frm_len_mask;