* [PATCH net v6 0/4] net: stmmac: fix RX descriptors and XSK TX on resume
@ 2026-11-10 12:38 Ding Hui
2026-11-10 12:38 ` [PATCH net v6 1/4] net: stmmac: honor allocation flags when initializing RX buffers Ding Hui
` (3 more replies)
0 siblings, 4 replies; 5+ messages in thread
From: Ding Hui @ 2026-11-10 12:38 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
From: Ding Hui <dinghui@lixiang.com>
This series restores RX descriptor addresses overwritten by hardware
writeback between disabling NAPI and stopping RX DMA during suspend. It
also fixes an AF_XDP TX-only resume stall found while testing the series.
Patch 1 honors RX allocation flags. Patch 2 keeps unfilled XSK slots
CPU-owned and tracks pending refills explicitly, including an empty ring
and short refills. Patch 3 restores RX addresses from retained buffers,
rebuilds chain pointers and initializes descriptor control fields. It
publishes a contiguous XSK prefix and recomputes the tail and pending
region. Patch 4 restores the bound XSK TX pool after TX buffer cleanup;
otherwise zero-copy transmission can stop after resume.
The earlier commit 9c63faaa931e ("net: stmmac: re-init rx buffers when mac
resume back") was reverted by 00423969d806 after a Jetson TX2 suspend/resume
regression. This series retains existing buffers and DMA mappings instead
of recycling and reallocating every buffer. Jetson TX2 has not been tested.
Hardware validation used backports on our XGMAC platform with a vendor
kernel based on Linux 6.12.41, not these upstream commits as-is:
- Receiving iperf traffic from a PC while suspending and resuming caused a
fatal bus error after resume in almost every run without the RX fix.
The same test no longer showed that failure with the fix.
- Native XDP/AF_XDP zero-copy RX continued after two suspend/resume cycles
with the receive socket kept open.
- Zero-copy TX-only, with no RX fill buffers, is tested ok after one suspend/resume
cycle, with no fatal bus error.
- Pausing the receive application with SIGSTOP and resuming it with SIGCONT
recovered RX without recreating the socket.
- xdpsock is used for native XDP testing.
Additional testing would be welcome for RX sockets initially supplied with
0/1/8/15 fill buffers, replenishment in batches below 16, and those states
across suspend/resume. These differ from TX-only with no RX fill buffers.
Legacy descriptor cores, GMAC4, chain mode, timestamp-specific descriptor
paths, multiple XSK queues and long suspend/resume runs remain untested.
Missing page_pool buffers are still allocated during resume. Allocation
failure returns -ENOMEM without restarting DMA and leaves the device
detached. This adds a trigger for existing resume error-recovery problems;
full close/open recovery, including NAPI and platform power handling, is
separate work. The Medium-severity AI review on failure recovery is not
fully resolved by this revision.
Changes in v6:
- Add a separate fix to restore the bound XSK TX pool after resume cleanup,
addressing TX-only stalls found during XGMAC validation.
- Factor resume descriptor work into one TX queue loop and one RX loop,
following Thomas's review, and use common error cleanup.
- Restore addresses and chain pointers before initializing RX control fields,
preserving legacy fields and RIWT/IOC policy without a separate OWN loop.
- Add format-specific RX ownership clearing for unfilled XSK descriptors.
- Track pending XSK refills independently of circular index equality; retry
short refills and prevent consumption of unfilled descriptors.
- Restore a populated XSK prefix and retain later attached buffers for refill;
recompute the allocation count, initial tail and pending region.
- Select XSK buffer size independently of the initial allocation count.
- Discard any partially assembled RX frame before resetting the RX ring after resume.
Link to v5:
https://lore.kernel.org/netdev/20261025100636.613670-1-dinghui1111@163.com/
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 (4):
net: stmmac: honor allocation flags when initializing RX buffers
net: stmmac: track unfilled XSK RX descriptors explicitly
net: stmmac: restore RX descriptor buffer addresses on resume
net: stmmac: restore the XSK TX pool on resume
.../ethernet/stmicro/stmmac/dwmac4_descs.c | 6 +
.../ethernet/stmicro/stmmac/dwxgmac2_descs.c | 6 +
.../net/ethernet/stmicro/stmmac/enh_desc.c | 6 +
drivers/net/ethernet/stmicro/stmmac/hwif.h | 3 +
.../net/ethernet/stmicro/stmmac/norm_desc.c | 6 +
drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 +
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 277 +++++++++++++-----
7 files changed, 228 insertions(+), 78 deletions(-)
base-commit: 7076f684d5132e6a6145e1da2fa74771ecd052e5
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v6 1/4] net: stmmac: honor allocation flags when initializing RX buffers
2026-11-10 12:38 [PATCH net v6 0/4] net: stmmac: fix RX descriptors and XSK TX on resume Ding Hui
@ 2026-11-10 12:38 ` Ding Hui
2026-11-10 12:38 ` [PATCH net v6 2/4] net: stmmac: track unfilled XSK RX descriptors explicitly Ding Hui
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Ding Hui @ 2026-11-10 12:38 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
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>
---
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] 5+ messages in thread
* [PATCH net v6 2/4] net: stmmac: track unfilled XSK RX descriptors explicitly
2026-11-10 12:38 [PATCH net v6 0/4] net: stmmac: fix RX descriptors and XSK TX on resume Ding Hui
2026-11-10 12:38 ` [PATCH net v6 1/4] net: stmmac: honor allocation flags when initializing RX buffers Ding Hui
@ 2026-11-10 12:38 ` Ding Hui
2026-11-10 12:38 ` [PATCH net v6 3/4] net: stmmac: restore RX descriptor buffer addresses on resume Ding Hui
2026-11-10 12:38 ` [PATCH net v6 4/4] net: stmmac: restore the XSK TX pool " Ding Hui
3 siblings, 0 replies; 5+ messages in thread
From: Ding Hui @ 2026-11-10 12:38 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
From: Ding Hui <dinghui@lixiang.com>
An XSK fill ring may supply fewer buffers than the RX ring needs. Those
unfilled descriptors must remain CPU-owned, including on legacy cores
whose clear callback only clears the buffer address. Add an ownership
operation that preserves descriptor size and ring/chain control fields.
Track the number of descriptors awaiting refill independently of the
ring indices, so an entirely empty ring can be distinguished from one
with no pending refills. Initialize the pending region after the allocated
prefix and stop consumption before entering it. Retry pending refills
on each poll, including userspace wakeups with fewer than a batch of
buffers, and retain batching during packet processing.
Update the tail after successful refills and kick legacy RX DMA. Use the
end-of-ring tail address for a refill ending at index zero, including a
completely empty ring populated before DMA has advanced from the base.
Select the receive buffer size from the XSK pool even if no buffers were
available at initialization.
Fixes: bba2556efad6 ("net: stmmac: Enable RX via AF_XDP zero-copy")
Signed-off-by: Ding Hui <dinghui@lixiang.com>
---
.../ethernet/stmicro/stmmac/dwmac4_descs.c | 6 +++
.../ethernet/stmicro/stmmac/dwxgmac2_descs.c | 6 +++
.../net/ethernet/stmicro/stmmac/enh_desc.c | 6 +++
drivers/net/ethernet/stmicro/stmmac/hwif.h | 3 ++
.../net/ethernet/stmicro/stmmac/norm_desc.c | 6 +++
drivers/net/ethernet/stmicro/stmmac/stmmac.h | 2 +
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 49 +++++++++++++------
7 files changed, 63 insertions(+), 15 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
index c6a8f8d73501..307bbd63dbcb 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac4_descs.c
@@ -176,6 +176,11 @@ static void dwmac4_set_tx_owner(struct dma_desc *p)
p->des3 |= cpu_to_le32(TDES3_OWN);
}
+static void dwmac4_clear_rx_owner(struct dma_desc *p)
+{
+ p->des3 &= cpu_to_le32(~RDES3_OWN);
+}
+
static void dwmac4_set_rx_owner(struct dma_desc *p, int disable_rx_ic)
{
u32 flags = (RDES3_OWN | RDES3_BUFFER1_VALID_ADDR);
@@ -539,6 +544,7 @@ const struct stmmac_desc_ops dwmac4_desc_ops = {
.rx_status = dwmac4_wrback_get_rx_status,
.set_tx_owner = dwmac4_set_tx_owner,
.set_rx_owner = dwmac4_set_rx_owner,
+ .clear_rx_owner = dwmac4_clear_rx_owner,
.get_rx_vlan_tci = dwmac4_wrback_get_rx_vlan_tci,
.get_rx_vlan_valid = dwmac4_wrback_get_rx_vlan_valid,
.get_rx_frame_len = dwmac4_wrback_get_rx_frame_len,
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c
index 6719ac6e395b..ad377c76ed4e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwxgmac2_descs.c
@@ -51,6 +51,11 @@ static void dwxgmac2_set_tx_owner(struct dma_desc *p)
p->des3 |= cpu_to_le32(XGMAC_TDES3_OWN);
}
+static void dwxgmac2_clear_rx_owner(struct dma_desc *p)
+{
+ p->des3 &= cpu_to_le32(~XGMAC_RDES3_OWN);
+}
+
static void dwxgmac2_set_rx_owner(struct dma_desc *p, int disable_rx_ic)
{
u32 flags = XGMAC_RDES3_OWN;
@@ -350,6 +355,7 @@ const struct stmmac_desc_ops dwxgmac210_desc_ops = {
.rx_status = dwxgmac2_get_rx_status,
.set_tx_owner = dwxgmac2_set_tx_owner,
.set_rx_owner = dwxgmac2_set_rx_owner,
+ .clear_rx_owner = dwxgmac2_clear_rx_owner,
.get_rx_vlan_tci = dwxgmac2_wrback_get_rx_vlan_tci,
.get_rx_vlan_valid = dwxgmac2_wrback_get_rx_vlan_valid,
.get_rx_frame_len = dwxgmac2_get_rx_frame_len,
diff --git a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
index 051253601225..7c5eb4493875 100644
--- a/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/enh_desc.c
@@ -278,6 +278,11 @@ static void enh_desc_set_tx_owner(struct dma_desc *p)
p->des0 |= cpu_to_le32(ETDES0_OWN);
}
+static void enh_desc_clear_rx_owner(struct dma_desc *p)
+{
+ p->des0 &= cpu_to_le32(~RDES0_OWN);
+}
+
static void enh_desc_set_rx_owner(struct dma_desc *p, int disable_rx_ic)
{
p->des0 |= cpu_to_le32(RDES0_OWN);
@@ -439,6 +444,7 @@ const struct stmmac_desc_ops enh_desc_ops = {
.set_tx_ic = enh_desc_set_tx_ic,
.set_tx_owner = enh_desc_set_tx_owner,
.set_rx_owner = enh_desc_set_rx_owner,
+ .clear_rx_owner = enh_desc_clear_rx_owner,
.get_rx_frame_len = enh_desc_get_rx_frame_len,
.rx_extended_status = enh_desc_get_ext_status,
.enable_tx_timestamp = enh_desc_enable_tx_timestamp,
diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h
index 50b590d67d87..81328d5cd85d 100644
--- a/drivers/net/ethernet/stmicro/stmmac/hwif.h
+++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h
@@ -65,6 +65,7 @@ struct stmmac_desc_ops {
struct dma_desc *p, void __iomem *ioaddr);
/* Handle extra events on specific interrupts hw dependent */
void (*set_rx_owner)(struct dma_desc *p, int disable_rx_ic);
+ void (*clear_rx_owner)(struct dma_desc *p);
/* Get the receive frame size */
int (*get_rx_frame_len)(struct dma_desc *p, int rx_coe_type);
/* Return the reception status looking at the RDES1 */
@@ -121,6 +122,8 @@ struct stmmac_desc_ops {
stmmac_do_callback(__priv, desc, get_rx_vlan_valid, __args)
#define stmmac_tx_status(__priv, __args...) \
stmmac_do_callback(__priv, desc, tx_status, __args)
+#define stmmac_clear_rx_owner(__priv, __args...) \
+ stmmac_do_void_callback(__priv, desc, clear_rx_owner, __args)
#define stmmac_set_rx_owner(__priv, __args...) \
stmmac_do_void_callback(__priv, desc, set_rx_owner, __args)
#define stmmac_get_rx_frame_len(__priv, __args...) \
diff --git a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
index c4b613564f87..c7dd710580a2 100644
--- a/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
+++ b/drivers/net/ethernet/stmicro/stmmac/norm_desc.c
@@ -141,6 +141,11 @@ static void ndesc_set_tx_owner(struct dma_desc *p)
p->des0 |= cpu_to_le32(TDES0_OWN);
}
+static void ndesc_clear_rx_owner(struct dma_desc *p)
+{
+ p->des0 &= cpu_to_le32(~RDES0_OWN);
+}
+
static void ndesc_set_rx_owner(struct dma_desc *p, int disable_rx_ic)
{
p->des0 |= cpu_to_le32(RDES0_OWN);
@@ -284,6 +289,7 @@ const struct stmmac_desc_ops ndesc_ops = {
.set_tx_ic = ndesc_set_tx_ic,
.set_tx_owner = ndesc_set_tx_owner,
.set_rx_owner = ndesc_set_rx_owner,
+ .clear_rx_owner = ndesc_clear_rx_owner,
.get_rx_frame_len = ndesc_get_rx_frame_len,
.enable_tx_timestamp = ndesc_enable_tx_timestamp,
.get_tx_timestamp_status = ndesc_get_tx_timestamp_status,
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac.h b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
index 93292014c393..dc2cd46592fc 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac.h
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac.h
@@ -128,6 +128,8 @@ struct stmmac_rx_queue {
unsigned int cur_rx;
unsigned int dirty_rx;
unsigned int buf_alloc_num;
+ /* XSK descriptors waiting for refill, including a completely empty ring. */
+ unsigned int xsk_rx_pending;
unsigned int napi_skb_frag_size;
dma_addr_t dma_rx_phy;
unsigned int state_saved;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index f5ae12208e85..80789530e2ad 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -435,7 +435,7 @@ static void stmmac_set_queue_rx_buf_size(struct stmmac_priv *priv,
{
u32 buf_size;
- if (rx_q->xsk_pool && rx_q->buf_alloc_num)
+ if (rx_q->xsk_pool)
buf_size = xsk_pool_get_rx_frame_size(rx_q->xsk_pool);
else
buf_size = priv->dma_conf.dma_buf_sz;
@@ -452,6 +452,9 @@ static inline u32 stmmac_rx_dirty(struct stmmac_priv *priv, u32 queue)
{
struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
+ if (rx_q->xsk_pool)
+ return rx_q->xsk_rx_pending;
+
return CIRC_CNT(rx_q->cur_rx, rx_q->dirty_rx,
priv->dma_conf.dma_rx_size);
}
@@ -1609,6 +1612,9 @@ static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv,
priv->descriptor_mode,
(i == dma_conf->dma_rx_size - 1),
dma_conf->dma_buf_sz);
+ /* Only the allocated prefix may be handed to RX DMA. */
+ if (rx_q->xsk_pool && i >= rx_q->buf_alloc_num)
+ stmmac_clear_rx_owner(priv, desc);
}
}
@@ -5576,7 +5582,7 @@ static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
budget = min(budget, stmmac_rx_dirty(priv, queue));
- while (budget-- > 0 && entry != rx_q->cur_rx) {
+ while (budget-- > 0) {
struct stmmac_rx_buffer *buf = &rx_q->buf_pool[entry];
dma_addr_t dma_addr;
bool use_rx_wd;
@@ -5601,12 +5607,18 @@ static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
dma_wmb();
stmmac_set_rx_owner(priv, rx_desc, use_rx_wd);
+ rx_q->xsk_rx_pending--;
entry = STMMAC_NEXT_ENTRY(entry, priv->dma_conf.dma_rx_size);
}
if (rx_desc) {
rx_q->dirty_rx = entry;
- stmmac_set_queue_rx_tail_ptr(priv, rx_q, queue, rx_q->dirty_rx);
+ /* Use the end-of-ring address when filling an initially empty
+ * ring completely, so the tail differs from its initial base.
+ */
+ stmmac_set_queue_rx_tail_ptr(priv, rx_q, queue,
+ entry ?: priv->dma_conf.dma_rx_size);
+ stmmac_enable_dma_reception(priv, priv->ioaddr, queue);
}
return ret;
@@ -5627,7 +5639,6 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
struct stmmac_rxq_stats *rxq_stats = &priv->xstats.rxq_stats[queue];
struct stmmac_rx_queue *rx_q = &priv->dma_conf.rx_queue[queue];
unsigned int count = 0, error = 0, len = 0;
- int dirty = stmmac_rx_dirty(priv, queue);
unsigned int next_entry = rx_q->cur_rx;
u32 rx_errors = 0, rx_dropped = 0;
unsigned int desc_size;
@@ -5636,6 +5647,9 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
int xdp_status = 0;
int status = 0;
+ if (rx_q->xsk_rx_pending)
+ failure = !stmmac_rx_refill_zc(priv, queue, rx_q->xsk_rx_pending);
+
if (netif_msg_rx_status(priv)) {
void *rx_head = stmmac_get_rx_desc(priv, rx_q, 0);
@@ -5670,11 +5684,13 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
entry = next_entry;
buf = &rx_q->buf_pool[entry];
- if (dirty >= STMMAC_RX_FILL_BATCH) {
- failure = failure ||
- !stmmac_rx_refill_zc(priv, queue, dirty);
- dirty = 0;
- }
+ if (!failure && rx_q->xsk_rx_pending >= STMMAC_RX_FILL_BATCH)
+ failure = !stmmac_rx_refill_zc(priv, queue,
+ rx_q->xsk_rx_pending);
+
+ /* A CPU-owned slot in the refill region is not a packet. */
+ if (rx_q->xsk_rx_pending == priv->dma_conf.dma_rx_size)
+ break;
p = stmmac_get_rx_desc(priv, rx_q, entry);
@@ -5687,10 +5703,8 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
/* Prefetch the next RX descriptor */
next_entry = STMMAC_NEXT_ENTRY(rx_q->cur_rx,
priv->dma_conf.dma_rx_size);
- if (unlikely(next_entry == rx_q->dirty_rx))
- break;
-
rx_q->cur_rx = next_entry;
+ rx_q->xsk_rx_pending++;
np = stmmac_get_rx_desc(priv, rx_q, next_entry);
@@ -5706,7 +5720,6 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
if (unlikely(status == discard_frame)) {
xsk_buff_free(buf->xdp);
buf->xdp = NULL;
- dirty++;
error = 1;
if (!priv->hwts_rx_en)
rx_errors++;
@@ -5723,7 +5736,6 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
if (likely(status & rx_not_ls)) {
xsk_buff_free(buf->xdp);
buf->xdp = NULL;
- dirty++;
count++;
goto read_again;
}
@@ -5768,7 +5780,6 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
}
buf->xdp = NULL;
- dirty++;
count++;
}
@@ -5780,6 +5791,9 @@ static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue)
stmmac_finalize_xdp_rx(priv, xdp_status);
+ if (!failure && rx_q->xsk_rx_pending)
+ failure = !stmmac_rx_refill_zc(priv, queue, rx_q->xsk_rx_pending);
+
u64_stats_update_begin(&rxq_stats->napi_syncp);
u64_stats_add(&rxq_stats->napi.rx_pkt_n, count);
u64_stats_update_end(&rxq_stats->napi_syncp);
@@ -8414,6 +8428,11 @@ static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue)
rx_q->cur_rx = 0;
rx_q->dirty_rx = 0;
+ if (rx_q->xsk_pool) {
+ rx_q->dirty_rx = rx_q->buf_alloc_num % priv->dma_conf.dma_rx_size;
+ rx_q->xsk_rx_pending = priv->dma_conf.dma_rx_size -
+ rx_q->buf_alloc_num;
+ }
}
static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue)
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v6 3/4] net: stmmac: restore RX descriptor buffer addresses on resume
2026-11-10 12:38 [PATCH net v6 0/4] net: stmmac: fix RX descriptors and XSK TX on resume Ding Hui
2026-11-10 12:38 ` [PATCH net v6 1/4] net: stmmac: honor allocation flags when initializing RX buffers Ding Hui
2026-11-10 12:38 ` [PATCH net v6 2/4] net: stmmac: track unfilled XSK RX descriptors explicitly Ding Hui
@ 2026-11-10 12:38 ` Ding Hui
2026-11-10 12:38 ` [PATCH net v6 4/4] net: stmmac: restore the XSK TX pool " Ding Hui
3 siblings, 0 replies; 5+ messages in thread
From: Ding Hui @ 2026-11-10 12:38 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
From: Ding Hui <dinghui@lixiang.com>
Suspend disables NAPI before stopping RX DMA. Hardware can complete RX
descriptors in between, overwriting GMAC4/XGMAC buffer addresses with
writeback status. Resume currently rearms those descriptors without
restoring their addresses, leading to a fatal bus error.
Share buffer preparation with the refill paths and restore addresses
from attached buffers. Allocate missing primary and secondary pages
independently with GFP_KERNEL. If allocation fails, return the error
without restarting DMA and leave the device detached.
For XSK queues, restore a contiguous populated prefix and leave the rest
CPU-owned in the pending refill region. Preserve attached buffers beyond
the prefix for later refill. Recompute the initial tail and pending count
rather than retaining the allocation count from open.
Rebuild chain pointers and discard partial receive state from before the
DMA reset. Initialize RX descriptors after restoring their addresses,
using the existing per-queue initializer to preserve the watchdog policy
and legacy control fields. Keep the unpopulated XSK suffix CPU-owned when
DMA restarts. This replaces the separate final ownership-setting loop.
Collect the resume work in stmmac_resume_descriptors(), with one loop over
TX queues and one over RX queues. Free outstanding TX buffers before
initializing each TX ring, and remove the now-unused all-queue TX wrapper.
The RX rings no longer undergo initialization before address restoration.
Unlike the reverted commit 9c63faaa931e ("net: stmmac: re-init rx buffers
when mac resume back"), retain attached buffers and their DMA mappings.
Fixes: f748be531d70 ("stmmac: support new GMAC4")
Signed-off-by: Ding Hui <dinghui@lixiang.com>
---
.../net/ethernet/stmicro/stmmac/stmmac_main.c | 225 +++++++++++++-----
1 file changed, 162 insertions(+), 63 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 80789530e2ad..99e46174741e 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1676,6 +1676,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
@@ -1694,32 +1728,104 @@ 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 addresses overwritten by hardware writeback, retaining attached
+ * buffers and allocating missing page_pool buffers with GFP_KERNEL. Keep
+ * descriptors CPU-owned until addresses and chain pointers are restored.
+ *
+ * For XSK, publish only the contiguous populated prefix. The remainder is
+ * left in the refill region, including any buffers already attached there,
+ * and can be populated by NAPI when userspace supplies more fill buffers.
+ *
+ * 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 dma_desc *p;
+ int i, ret;
+
+ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ p = stmmac_get_rx_desc(priv, rx_q, i);
+ stmmac_clear_rx_owner(priv, p);
}
- buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset;
+ rx_q->buf_alloc_num = 0;
+ for (i = 0; i < dma_conf->dma_rx_size; i++) {
+ struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i];
- stmmac_set_desc_addr(priv, p, buf->addr);
- if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB)
- stmmac_init_desc3(priv, p);
+ 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)
+ break;
+ } else {
+ ret = stmmac_init_rx_buffers(priv, dma_conf, p, i,
+ GFP_KERNEL, queue);
+ if (ret)
+ return ret;
+ }
+ rx_q->buf_alloc_num++;
+ }
+
+ /* 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_mode_init(priv, des, rx_q->dma_rx_phy,
+ dma_conf->dma_rx_size, priv->extend_desc);
+ }
+
+ /* Discard a partially assembled frame from before the DMA reset. */
+ if (rx_q->state_saved && !rx_q->xsk_pool)
+ dev_kfree_skb_any(rx_q->state.skb);
+ rx_q->state_saved = false;
+ rx_q->rx_count_frames = 0;
+ stmmac_reset_rx_queue(priv, queue);
+
+ /* Initialize control fields and publish ownership only after restoring
+ * addresses and chain pointers. DMA is stopped; initialization also
+ * clears OWN again for the unpopulated XSK suffix before DMA restarts.
+ */
+ dma_wmb();
+ stmmac_clear_rx_descriptors(priv, dma_conf, queue);
return 0;
}
@@ -2156,16 +2262,37 @@ static void dma_free_tx_skbufs(struct stmmac_priv *priv,
}
/**
- * stmmac_free_tx_skbufs - free TX skb buffers
- * @priv: private structure
+ * stmmac_resume_descriptors - prepare TX and RX rings before restarting DMA
+ * @priv: driver private structure
+ *
+ * Release outstanding TX buffers before initializing each TX ring. Restore
+ * RX buffer addresses before initializing RX descriptors and their ownership.
+ * All DMA channels must be stopped throughout this operation.
+ *
+ * Return: 0 on success, or a negative errno if RX restoration fails.
*/
-static void stmmac_free_tx_skbufs(struct stmmac_priv *priv)
+static int stmmac_resume_descriptors(struct stmmac_priv *priv)
{
- u8 tx_queue_cnt = priv->plat->tx_queues_to_use;
- u8 queue;
+ struct stmmac_dma_conf *dma_conf = &priv->dma_conf;
+ u32 queue;
+ int ret;
- for (queue = 0; queue < tx_queue_cnt; queue++)
- dma_free_tx_skbufs(priv, &priv->dma_conf, queue);
+ for (queue = 0; queue < priv->plat->tx_queues_to_use; queue++) {
+ stmmac_reset_tx_queue(priv, queue);
+ dma_free_tx_skbufs(priv, dma_conf, queue);
+ stmmac_clear_tx_descriptors(priv, dma_conf, queue);
+ }
+
+ for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) {
+ ret = stmmac_reinit_rx_descriptors(priv, dma_conf, queue);
+ if (ret) {
+ netdev_err(priv->dev, "Failed to restore RX queue %u: %d\n",
+ queue, ret);
+ return ret;
+ }
+ }
+
+ return 0;
}
/**
@@ -5151,10 +5278,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];
@@ -5163,25 +5286,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);
@@ -5584,22 +5691,15 @@ static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget)
while (budget-- > 0) {
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);
@@ -8517,10 +8617,9 @@ int stmmac_resume(struct device *dev)
mutex_lock(&priv->lock);
- stmmac_reset_queues_param(priv);
-
- stmmac_free_tx_skbufs(priv);
- stmmac_clear_descriptors(priv, &priv->dma_conf);
+ ret = stmmac_resume_descriptors(priv);
+ if (ret)
+ goto error_unlock;
ret = stmmac_hw_setup(ndev);
if (ret < 0) {
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net v6 4/4] net: stmmac: restore the XSK TX pool on resume
2026-11-10 12:38 [PATCH net v6 0/4] net: stmmac: fix RX descriptors and XSK TX on resume Ding Hui
` (2 preceding siblings ...)
2026-11-10 12:38 ` [PATCH net v6 3/4] net: stmmac: restore RX descriptor buffer addresses on resume Ding Hui
@ 2026-11-10 12:38 ` Ding Hui
3 siblings, 0 replies; 5+ messages in thread
From: Ding Hui @ 2026-11-10 12:38 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
From: Ding Hui <dinghui@lixiang.com>
When suspend interrupts AF_XDP zero-copy transmission, resume releases
outstanding TX buffers through dma_free_tx_skbufs(). If it completes XSK
frames, that helper also clears tx_q->xsk_pool even though the socket
remains bound to the queue.
The resume path does not run the TX ring allocation routine that normally
sets this pointer. Consequently, stmmac_tx_clean() skips XSK transmission
and a running TX-only socket can stop sending after resume, despite the
link coming back up.
Restore the TX pool from the queue's existing XSK binding after releasing
TX buffers and before restarting DMA and NAPI. Keep the buffer cleanup
helper's behavior for teardown unchanged.
Fixes: 132c32ee5bc0 ("net: stmmac: Add TX via XDP zero-copy socket")
Signed-off-by: Ding Hui <dinghui@lixiang.com>
---
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index 99e46174741e..140b95f767fe 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -2280,6 +2280,9 @@ static int stmmac_resume_descriptors(struct stmmac_priv *priv)
for (queue = 0; queue < priv->plat->tx_queues_to_use; queue++) {
stmmac_reset_tx_queue(priv, queue);
dma_free_tx_skbufs(priv, dma_conf, queue);
+ /* Buffer cleanup may clear the pool; the socket remains bound. */
+ dma_conf->tx_queue[queue].xsk_pool =
+ stmmac_get_xsk_pool(priv, queue);
stmmac_clear_tx_descriptors(priv, dma_conf, queue);
}
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-10 12:40 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-10 12:38 [PATCH net v6 0/4] net: stmmac: fix RX descriptors and XSK TX on resume Ding Hui
2026-11-10 12:38 ` [PATCH net v6 1/4] net: stmmac: honor allocation flags when initializing RX buffers Ding Hui
2026-11-10 12:38 ` [PATCH net v6 2/4] net: stmmac: track unfilled XSK RX descriptors explicitly Ding Hui
2026-11-10 12:38 ` [PATCH net v6 3/4] net: stmmac: restore RX descriptor buffer addresses on resume Ding Hui
2026-11-10 12:38 ` [PATCH net v6 4/4] net: stmmac: restore the XSK TX pool " 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®