Subject: [PATCH net v2 3/4] net: mvpp2: do not return retired RX buffers to BM

From: Til Kaiser <[email-masked]>
Date: Tue, 2 Jun 2026 18:46:34 +0200

The RX refill failure path jumps to err_drop_frame, which returns the
descriptor buffer to the hardware BM pool. That is only valid while the
driver still owns the buffer.

After a non-PASS XDP verdict, mvpp2_run_xdp() may already have recycled,
redirected, or queued the page for XDP_TX. After build_skb(), freeing the
skb on refill failure also retires the data buffer. Returning either of
those buffers to BM lets hardware DMA into memory that is no longer owned
by the RX ring.

Split the error handling so buffers are returned to BM only while still
owned by the driver. Once XDP or an skb owns the buffer, only account the
RX error. Mark page-pool skbs for recycle before they can be freed on the
refill failure path, and unmap non-page-pool buffers before freeing them.

Fixes: 07dd0a7aae7f ("mvpp2: add basic XDP support")
Fixes: d6526926de73 ("net: mvpp2: fix memory leak in mvpp2_rx")
Signed-off-by: Til Kaiser <[email-masked]>
---
 .../net/ethernet/marvell/mvpp2/mvpp2_main.c   | 20 ++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
index 3372ed27cc8d..397aa5ca4992 100644
--- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
+++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c
@@ -3991,7 +3991,7 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi,
 				err = mvpp2_rx_refill(port, bm_pool, pp, pool);
 				if (err) {
 					netdev_err(port->dev, "failed to refill BM pools\n");
-					goto err_drop_frame;
+					goto err_drop_frame_retired;
 				}
 
 				ps.rx_packets++;
@@ -4010,6 +4010,8 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi,
 			netdev_warn(port->dev, "skb build failed\n");
 			goto err_drop_frame;
 		}
+		if (pp)
+			skb_mark_for_recycle(skb);
 
 		/* If we have RX hardware timestamping enabled, grab the
 		 * timestamp from the queue and convert.
@@ -4023,13 +4025,16 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi,
 		err = mvpp2_rx_refill(port, bm_pool, pp, pool);
 		if (err) {
 			netdev_err(port->dev, "failed to refill BM pools\n");
+			if (!pp)
+				dma_unmap_single_attrs(dev->dev.parent, dma_addr,
+						       bm_pool->buf_size,
+						       DMA_FROM_DEVICE,
+						       DMA_ATTR_SKIP_CPU_SYNC);
 			dev_kfree_skb_any(skb);
-			goto err_drop_frame;
+			goto err_drop_frame_retired;
 		}
 
-		if (pp)
-			skb_mark_for_recycle(skb);
-		else
+		if (!pp)
 			dma_unmap_single_attrs(dev->dev.parent, dma_addr,
 					       bm_pool->buf_size, DMA_FROM_DEVICE,
 					       DMA_ATTR_SKIP_CPU_SYNC);
@@ -4048,13 +4053,14 @@ static int mvpp2_rx(struct mvpp2_port *port, struct napi_struct *napi,
 		continue;
 
 err_drop_frame:
-		dev->stats.rx_errors++;
-		mvpp2_rx_error(port, rx_desc);
 		/* Return the buffer to the pool */
 		if (rx_status & MVPP2_RXD_BUF_HDR)
 			mvpp2_buff_hdr_pool_put(port, rx_desc, pool, rx_status);
 		else
 			mvpp2_bm_pool_put(port, pool, dma_addr, phys_addr);
+err_drop_frame_retired:
+		dev->stats.rx_errors++;
+		mvpp2_rx_error(port, rx_desc);
 	}
 
 	if (xdp_ret & MVPP2_XDP_REDIR)
-- 
2.54.0