mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net v5 0/2] net: stmmac: restore RX descriptors on resume
@ 2026-10-25 10:06 Ding Hui
  2026-10-25 10:06 ` [PATCH net v5 1/2] net: stmmac: honor allocation flags when initializing RX buffers Ding Hui
  2026-10-25 10:06 ` [PATCH net v5 2/2] net: stmmac: restore RX descriptor buffer addresses on resume Ding Hui
  0 siblings, 2 replies; 3+ messages in thread
From: Ding Hui @ 2026-10-25 10:06 UTC (permalink / raw)
  To: Maxime Chevallier, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
	open list:STMMAC ETHERNET DRIVER,
	moderated list:ARM/STM32 ARCHITECTURE,
	moderated list:ARM/STM32 ARCHITECTURE, open list
  Cc: dinghui, xiasanbo, yangchen11, liuxuanjun, zhoujing3, fujiahui

From: Ding Hui <dinghui@lixiang.com>

This series fixes RX descriptor buffer addresses that hardware writeback
may overwrite between disabling NAPI and stopping RX DMA during suspend.
Restoring these addresses before restarting DMA on resume prevents a
fatal bus error.

Patch 1 fixes the ignored allocation flags independently. Patch 2 shares
buffer preparation with the normal and zero-copy refill paths and restores
RX descriptor addresses on resume. Missing primary and secondary pages
are allocated with GFP_KERNEL; failure aborts resume. Unavailable XSK slots
remain CPU-owned, and all address writes precede the final OWN update.

The earlier fix, commit 9c63faaa931e ("net: stmmac: re-init rx buffers when
mac resume back"), was reverted by commit 00423969d806 after a Jetson TX2
suspend/resume regression. This series retains attached buffers and their
DMA mappings and only allocates missing buffers. It does not repeat the
recycle-and-reallocate sequence of that fix. Jetson TX2 has not been tested
with this series.

Reproduction and testing:
On our hardware platform, run iperf traffic from a PC to the platform
(the platform is the receiver), and suspend/resume the platform while
traffic is running. Without the fix, a fatal bus error occurs after
resume in almost every run. With the fix applied, the fatal bus error
was no longer observed under the same test conditions.

This comparison was performed using a backport of this series to a kernel
based on Linux 6.12.41, running on our hardware platform that integrates
a Synopsys XGMAC.

Changes in v5:
- Target net and add Fixes tags to both patches.
- Split the allocation-flags fix into a separate patch, with a Fixes tag
  identifying the page_pool conversion that stopped honoring the flags.
- Share page_pool and XSK buffer preparation with the RX refill paths.
- Allocate missing secondary pages even when the primary page exists,
  using the current baseline's buffer2 capability check.
- Condense the reinitialization kernel-doc to two paragraphs.
- Restore descriptor addresses before the final dma_wmb()/OWN pass.
- Clarify that unavailable XSK slots remain CPU-owned and may stop DMA
  with RBU, rather than claiming that hardware skips them.
- Power down legacy SerDes if RX descriptor restoration fails.
- Link to v4:
  https://lore.kernel.org/netdev/20260627122533.1165324-1-dinghui1111@163.com/

Changes in v4:
- Document the return value of stmmac_reinit_rx_descriptors().
- Link to v3:
  https://lore.kernel.org/netdev/20260604144557.3175399-1-dinghui1111@163.com/

Changes in v3:
- Allocate missing page_pool buffers with GFP_KERNEL and abort resume
  on allocation failure.
- Try to allocate missing XSK buffers and clear descriptors when the
  fill ring cannot supply a buffer.
- Restore OWN after programming addresses and rebuilding chain pointers.
- Honor the flags argument of stmmac_init_rx_buffers().
- Restore RX descriptors after stmmac_clear_descriptors().
- Link to v2:
  https://lore.kernel.org/netdev/20260526022620.501229-1-dinghui1111@163.com/

Changes in v2:
- Introduce a helper to restore addresses from existing RX buffers.
- Update the commit message.
- Link to v1:
  https://lore.kernel.org/netdev/20260515053856.2310369-1-dinghui1111@163.com/

Ding Hui (2):
  net: stmmac: honor allocation flags when initializing RX buffers
  net: stmmac: restore RX descriptor buffer addresses on resume

 .../net/ethernet/stmicro/stmmac/stmmac_main.c | 190 +++++++++++++-----
 1 file changed, 138 insertions(+), 52 deletions(-)


base-commit: 7076f684d5132e6a6145e1da2fa74771ecd052e5
-- 
2.34.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH net v5 1/2] net: stmmac: honor allocation flags when initializing RX buffers
  2026-10-25 10:06 [PATCH net v5 0/2] net: stmmac: restore RX descriptors on resume Ding Hui
@ 2026-10-25 10:06 ` Ding Hui
  2026-10-25 10:06 ` [PATCH net v5 2/2] net: stmmac: restore RX descriptor buffer addresses on resume Ding Hui
  1 sibling, 0 replies; 3+ messages in thread
From: Ding Hui @ 2026-10-25 10:06 UTC (permalink / raw)
  To: Maxime Chevallier, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
	open list:STMMAC ETHERNET DRIVER,
	moderated list:ARM/STM32 ARCHITECTURE,
	moderated list:ARM/STM32 ARCHITECTURE, open list
  Cc: dinghui, xiasanbo, yangchen11, liuxuanjun, zhoujing3, fujiahui

From: Ding Hui <dinghui@lixiang.com>

stmmac_init_rx_buffers() accepts allocation flags but stopped using them
when RX buffers were converted to page_pool. As a result, callers passing
GFP_KERNEL still allocate with GFP_ATOMIC | __GFP_NOWARN and cannot use
reclaim to satisfy an allocation.

Use the supplied flags while retaining GFP_DMA32 for hardware without
64-bit DMA addressing. This also allows the resume path to allocate
missing RX pages with GFP_KERNEL.

Fixes: 2af6106ae949 ("net: stmmac: Introducing support for Page Pool")
Signed-off-by: Ding Hui <dinghui@lixiang.com>
---
Changes in v5:
- Split this fix from the resume fix as requested.
- Add a Fixes tag for the page_pool conversion.

 drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index b20e5038eead..f5ae12208e85 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1688,7 +1688,7 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
 {
 	struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
 	struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
-	gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
+	gfp_t gfp = flags;
 
 	if (priv->dma_cap.host_dma_width <= 32)
 		gfp |= GFP_DMA32;
-- 
2.34.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH net v5 2/2] net: stmmac: restore RX descriptor buffer addresses on resume
  2026-10-25 10:06 [PATCH net v5 0/2] net: stmmac: restore RX descriptors on resume Ding Hui
  2026-10-25 10:06 ` [PATCH net v5 1/2] net: stmmac: honor allocation flags when initializing RX buffers Ding Hui
@ 2026-10-25 10:06 ` Ding Hui
  1 sibling, 0 replies; 3+ messages in thread
From: Ding Hui @ 2026-10-25 10:06 UTC (permalink / raw)
  To: Maxime Chevallier, Andrew Lunn, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
	open list:STMMAC ETHERNET DRIVER,
	moderated list:ARM/STM32 ARCHITECTURE,
	moderated list:ARM/STM32 ARCHITECTURE, open list
  Cc: dinghui, xiasanbo, yangchen11, liuxuanjun, zhoujing3, fujiahui

From: Ding Hui <dinghui@lixiang.com>

stmmac_suspend() disables NAPI before stopping RX DMA. Frames received
in that interval can leave completed descriptors that have not been
refilled. On GMAC4 and XGMAC, hardware writeback overwrites the descriptor
buffer addresses with status and timestamp data.

stmmac_resume() calls stmmac_clear_descriptors(), which returns those
descriptors to DMA without restoring their buffer addresses. Restarting
RX DMA can therefore cause a fatal bus error.

Restore the addresses from the attached buffers before restarting DMA.
Factor buffer allocation and address programming into helpers shared
with the normal and zero-copy refill paths. Allocate missing primary
and secondary pages with GFP_KERNEL, including a missing secondary page
when the primary page was allocated before a refill failure. Abort
resume if a required page cannot be allocated.

For XSK queues, try to allocate missing buffers but leave unavailable
slots CPU-owned so an empty fill ring does not prevent TX-only use.
DMA may stop at such a slot with an RBU event; it must not receive an
invalid buffer address.

Rebuild chain pointers that timestamp writeback may have overwritten.
Publish OWN only after all address fields have been restored, since
programming the secondary address overwrites OWN on GMAC4 and XGMAC.
Keep existing buffers and their DMA mappings rather than recycling them
as the earlier, reverted resume fix did.

Fixes: f748be531d70 ("stmmac: support new GMAC4")
Signed-off-by: Ding Hui <dinghui@lixiang.com>
---
Changes in v5:
- Target net and add a Fixes tag for GMAC4 support.
- Share buffer preparation helpers with both RX refill paths.
- Allocate missing secondary pages independently of primary pages.
- Condense the kernel-doc and clarify CPU-owned XSK slots/RBU handling.
- Rebase onto net/main and retain SerDes cleanup on resume failure.

The allocation-flags fix is now patch 1/2 of this series.

 .../net/ethernet/stmicro/stmmac/stmmac_main.c | 190 +++++++++++++-----
 1 file changed, 138 insertions(+), 52 deletions(-)

diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index f5ae12208e85..e56f5d044fc8 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1670,6 +1670,40 @@ static bool stmmac_rx_check_buf2_cap(struct stmmac_priv *priv)
 	return priv->hw->desc && priv->hw->desc->set_sec_addr;
 }
 
+static int stmmac_prepare_rx_buffer(struct stmmac_priv *priv,
+				    struct stmmac_rx_queue *rx_q,
+				    struct stmmac_rx_buffer *buf,
+				    struct dma_desc *p, gfp_t flags)
+{
+	gfp_t gfp = flags;
+
+	if (priv->dma_cap.host_dma_width <= 32)
+		gfp |= GFP_DMA32;
+
+	if (!buf->page) {
+		buf->page = page_pool_alloc_pages(rx_q->page_pool, gfp);
+		if (!buf->page)
+			return -ENOMEM;
+		buf->page_offset = stmmac_rx_offset(priv);
+	}
+
+	if (stmmac_rx_check_buf2_cap(priv) && !buf->sec_page) {
+		buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp);
+		if (!buf->sec_page)
+			return -ENOMEM;
+
+		buf->sec_addr = page_pool_get_dma_addr(buf->sec_page);
+	}
+
+	buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset;
+
+	stmmac_set_desc_addr(priv, p, buf->addr);
+	stmmac_set_desc_sec_addr(priv, p, buf->sec_addr,
+				 stmmac_rx_check_buf2_cap(priv));
+
+	return 0;
+}
+
 /**
  * stmmac_init_rx_buffers - init the RX descriptor buffer.
  * @priv: driver private structure
@@ -1688,32 +1722,98 @@ static int stmmac_init_rx_buffers(struct stmmac_priv *priv,
 {
 	struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
 	struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
-	gfp_t gfp = flags;
+	int ret;
 
-	if (priv->dma_cap.host_dma_width <= 32)
-		gfp |= GFP_DMA32;
+	ret = stmmac_prepare_rx_buffer(priv, rx_q, buf, p, flags);
+	if (ret)
+		return ret;
 
-	if (!buf->page) {
-		buf->page = page_pool_alloc_pages(rx_q->page_pool, gfp);
-		if (!buf->page)
+	if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB)
+		stmmac_init_desc3(priv, p);
+
+	return 0;
+}
+
+static int stmmac_prepare_rx_buffer_zc(struct stmmac_priv *priv,
+				       struct stmmac_rx_queue *rx_q,
+				       struct stmmac_rx_buffer *buf,
+				       struct dma_desc *p)
+{
+	if (!buf->xdp) {
+		buf->xdp = xsk_buff_alloc(rx_q->xsk_pool);
+		if (!buf->xdp)
 			return -ENOMEM;
-		buf->page_offset = stmmac_rx_offset(priv);
 	}
 
-	if (stmmac_rx_check_buf2_cap(priv) && !buf->sec_page) {
-		buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp);
-		if (!buf->sec_page)
-			return -ENOMEM;
+	stmmac_set_desc_addr(priv, p, xsk_buff_xdp_get_dma(buf->xdp));
+	stmmac_set_desc_sec_addr(priv, p, 0, false);
 
-		buf->sec_addr = page_pool_get_dma_addr(buf->sec_page);
-		stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, true);
+	return 0;
+}
+
+/**
+ * stmmac_reinit_rx_descriptors - restore RX descriptors before restarting DMA
+ * @priv: driver private structure
+ * @dma_conf: DMA configuration
+ * @queue: RX queue index
+ *
+ * Restore buffer addresses overwritten by hardware writeback after
+ * stmmac_clear_descriptors(). Reuse attached buffers and allocate missing
+ * primary and secondary pages with GFP_KERNEL before restarting DMA.
+ *
+ * For XSK queues, try to allocate missing buffers and leave unavailable
+ * descriptors CPU-owned. Rebuild chain pointers when needed, then return
+ * valid descriptors to DMA after restoring all address fields.
+ *
+ * Return: 0 on success, or -ENOMEM if a required page cannot be allocated.
+ */
+static int stmmac_reinit_rx_descriptors(struct stmmac_priv *priv,
+					struct stmmac_dma_conf *dma_conf,
+					u32 queue)
+{
+	struct stmmac_rx_queue *rx_q = &dma_conf->rx_queue[queue];
+	struct stmmac_rx_buffer *buf;
+	struct dma_desc *p;
+	int i, ret;
+
+	for (i = 0; i < dma_conf->dma_rx_size; i++) {
+		buf = &rx_q->buf_pool[i];
+		p = stmmac_get_rx_desc(priv, rx_q, i);
+
+		if (rx_q->xsk_pool) {
+			ret = stmmac_prepare_rx_buffer_zc(priv, rx_q, buf, p);
+			if (ret) {
+				/* An empty fill ring must not prevent TX-only use. */
+				stmmac_clear_desc(priv, p);
+				continue;
+			}
+		} else {
+			ret = stmmac_init_rx_buffers(priv, dma_conf, p, i,
+						     GFP_KERNEL, queue);
+			if (ret)
+				return ret;
+		}
 	}
 
-	buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset;
+	/* Hardware timestamp writeback may have overwritten chain pointers. */
+	if (priv->descriptor_mode == STMMAC_CHAIN_MODE) {
+		void *des = priv->extend_desc ? (void *)rx_q->dma_erx :
+					      (void *)rx_q->dma_rx;
 
-	stmmac_set_desc_addr(priv, p, buf->addr);
-	if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB)
-		stmmac_init_desc3(priv, p);
+		stmmac_mode_init(priv, des, rx_q->dma_rx_phy,
+				 dma_conf->dma_rx_size, priv->extend_desc);
+	}
+
+	/* GMAC4/XGMAC secondary address writes overwrite the OWN bit. */
+	dma_wmb();
+	for (i = 0; i < dma_conf->dma_rx_size; i++) {
+		buf = &rx_q->buf_pool[i];
+		if (rx_q->xsk_pool && !buf->xdp)
+			continue;
+
+		p = stmmac_get_rx_desc(priv, rx_q, i);
+		stmmac_set_rx_owner(priv, p, false);
+	}
 
 	return 0;
 }
@@ -5145,10 +5245,6 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
 	struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
 	int dirty = stmmac_rx_dirty(priv, queue);
 	unsigned int entry = rx_q->dirty_rx;
-	gfp_t gfp = (GFP_ATOMIC | __GFP_NOWARN);
-
-	if (priv->dma_cap.host_dma_width <= 32)
-		gfp |= GFP_DMA32;
 
 	while (dirty-- > 0) {
 		struct stmmac_rx_buffer *buf = &rx_q->buf_pool[entry];
@@ -5157,25 +5253,9 @@ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue)
 
 		p = stmmac_get_rx_desc(priv, rx_q, entry);
 
-		if (!buf->page) {
-			buf->page = page_pool_alloc_pages(rx_q->page_pool, gfp);
-			if (!buf->page)
-				break;
-		}
-
-		if (stmmac_rx_check_buf2_cap(priv) && !buf->sec_page) {
-			buf->sec_page = page_pool_alloc_pages(rx_q->page_pool, gfp);
-			if (!buf->sec_page)
-				break;
-
-			buf->sec_addr = page_pool_get_dma_addr(buf->sec_page);
-		}
-
-		buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset;
-
-		stmmac_set_desc_addr(priv, p, buf->addr);
-		stmmac_set_desc_sec_addr(priv, p, buf->sec_addr,
-					 stmmac_rx_check_buf2_cap(priv));
+		if (stmmac_prepare_rx_buffer(priv, rx_q, buf, p,
+					     GFP_ATOMIC | __GFP_NOWARN))
+			break;
 		stmmac_refill_desc3(priv, rx_q, p);
 
 		use_rx_wd = !stmmac_rx_need_ic(priv, rx_q, queue);
@@ -5578,22 +5658,15 @@ static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
 
 	while (budget-- > 0 && entry != rx_q->cur_rx) {
 		struct stmmac_rx_buffer *buf = &rx_q->buf_pool[entry];
-		dma_addr_t dma_addr;
+		struct dma_desc *p = stmmac_get_rx_desc(priv, rx_q, entry);
 		bool use_rx_wd;
 
-		if (!buf->xdp) {
-			buf->xdp = xsk_buff_alloc(rx_q->xsk_pool);
-			if (!buf->xdp) {
-				ret = false;
-				break;
-			}
+		if (stmmac_prepare_rx_buffer_zc(priv, rx_q, buf, p)) {
+			ret = false;
+			break;
 		}
 
-		rx_desc = stmmac_get_rx_desc(priv, rx_q, entry);
-
-		dma_addr = xsk_buff_xdp_get_dma(buf->xdp);
-		stmmac_set_desc_addr(priv, rx_desc, dma_addr);
-		stmmac_set_desc_sec_addr(priv, rx_desc, 0, false);
+		rx_desc = p;
 		stmmac_refill_desc3(priv, rx_q, rx_desc);
 
 		use_rx_wd = !stmmac_rx_need_ic(priv, rx_q, queue);
@@ -8454,6 +8527,7 @@ int stmmac_resume(struct device *dev)
 {
 	struct net_device *ndev = dev_get_drvdata(dev);
 	struct stmmac_priv *priv = netdev_priv(ndev);
+	u32 queue;
 	int ret;
 
 	if (priv->plat->resume) {
@@ -8503,6 +8577,18 @@ int stmmac_resume(struct device *dev)
 	stmmac_free_tx_skbufs(priv);
 	stmmac_clear_descriptors(priv, &priv->dma_conf);
 
+	for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) {
+		ret = stmmac_reinit_rx_descriptors(priv, &priv->dma_conf, queue);
+		if (ret) {
+			netdev_err(ndev, "Failed to restore RX queue %u: %d\n",
+				   queue, ret);
+			stmmac_legacy_serdes_power_down(priv);
+			mutex_unlock(&priv->lock);
+			rtnl_unlock();
+			return ret;
+		}
+	}
+
 	ret = stmmac_hw_setup(ndev);
 	if (ret < 0) {
 		netdev_err(priv->dev, "%s: Hw setup failed\n", __func__);
-- 
2.34.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-10-25 10:08 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-25 10:06 [PATCH net v5 0/2] net: stmmac: restore RX descriptors on resume Ding Hui
2026-10-25 10:06 ` [PATCH net v5 1/2] net: stmmac: honor allocation flags when initializing RX buffers Ding Hui
2026-10-25 10:06 ` [PATCH net v5 2/2] net: stmmac: restore RX descriptor buffer addresses on resume Ding Hui

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®