From: Ding Hui On suspend, stmmac_suspend() calls stmmac_disable_all_queues() which stops the RX NAPI, but the RX DMA engine may still be running for a short window before stmmac_stop_all_dma() takes effect. During that window the hardware can write incoming frames into the buffers pointed to by the RX descriptors and write back the descriptors (clearing the OWN bit and overwriting RDES0/1/2 with status/timestamp data). Because NAPI is already disabled, the driver never refills these descriptors, so the RX ring is left in a "consumed but not refilled" state with stale content in the descriptor buffer-address fields. On resume, stmmac_clear_descriptors() only re-arms the OWN bit and does not repopulate the RX buffer address fields. When the DMA is restarted it dereferences these stale addresses and triggers a fatal bus error. Fix this without any allocation by introducing stmmac_reinit_rx_descriptors(), called from stmmac_resume() before stmmac_clear_descriptors(). The helper iterates every RX descriptor slot and re-programs its buffer address fields: - For normal (page_pool) queues: restore RDES0/1 from buf->addr and RDES2 from buf->sec_addr. The DMA mapping has remained valid across suspend/resume because no pages were freed. - For AF_XDP zero-copy queues: restore the DMA address from xsk_buff_xdp_get_dma(buf->xdp). Slots with buf->xdp == NULL (TX-only XSK socket) are skipped to avoid a NULL-pointer dereference. - For chain mode: call stmmac_mode_init() to rebuild the des3 next- descriptor pointer chain, which hardware may have overwritten with a PTP timestamp value (as noted in chain_mode.c:refill_desc3()). This approach keeps all RX buffers alive across the PM transition and avoids any allocation in the resume path, eliminating the OOM risk raised against the previous approach of freeing and re-allocating buffers. Signed-off-by: Ding Hui --- Changes in v2: - Introducing stmmac_reinit_rx_descriptors() to reinitializing rx buffers without any allocation. - Modify commit log. - Link to v1: https://lore.kernel.org/netdev/20260515053856.2310369-1-dinghui1111@163.com/ --- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 3591755ea30b..0dc27d8c66a0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -1642,6 +1642,79 @@ static void stmmac_clear_descriptors(struct stmmac_priv *priv, stmmac_clear_tx_descriptors(priv, dma_conf, queue); } +/** + * stmmac_reinit_rx_descriptors - re-program RX descriptors from existing + * buffers (allocation-free) + * @priv: driver private structure + * @dma_conf: structure holding the dma data + * @queue: RX queue index + * + * Description: walk rx_q->buf_pool[] and re-program every RX descriptor's + * buffer-address fields from the buffers that are already attached to the + * queue. This is intended for the resume path: between suspend and resume + * the descriptor buffer-address fields may have been overwritten by HW + * writeback (RDESx are reused for status/length on completion), but the + * underlying RX buffers (page_pool pages or XSK frames) are still alive + * in buf_pool[]. By re-using them we avoid any allocation on resume, + * which is unsafe under memory pressure. + * + * This helper is expected to be called only in stmmac_resume. + */ +static void 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]; + int i; + + for (i = 0; i < dma_conf->dma_rx_size; i++) { + struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; + struct dma_desc *p = stmmac_get_rx_desc(priv, rx_q, i); + + if (rx_q->xsk_pool) { + dma_addr_t dma_addr; + + /* The XSK pool may not be fully populated (e.g. + * xdpsock TX-only); skip empty slots. + */ + if (!buf->xdp) + continue; + + dma_addr = xsk_buff_xdp_get_dma(buf->xdp); + stmmac_set_desc_addr(priv, p, dma_addr); + stmmac_set_desc_sec_addr(priv, p, 0, false); + } else { + /* Theoretically unreachable: napi_disable() in + * stmmac_suspend() ensures all initialized slots + * have a valid page before we get here. + * Defensive check only. + */ + if (!buf->page) + continue; + + stmmac_set_desc_addr(priv, p, buf->addr); + stmmac_set_desc_sec_addr(priv, p, buf->sec_addr, + priv->sph_active && + buf->sec_page); + + if (dma_conf->dma_buf_sz == BUF_SIZE_16KiB) + stmmac_init_desc3(priv, p); + } + } + + /* Chain mode: re-link descriptor 'next' pointers. This is + * allocation-free; it just rewrites the per-descriptor next + * field which may have been clobbered by HW writeback. + */ + 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); + } +} + /** * stmmac_init_rx_buffers - init the RX descriptor buffer. * @priv: driver private structure @@ -8272,6 +8345,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) { @@ -8316,6 +8390,22 @@ int stmmac_resume(struct device *dev) mutex_lock(&priv->lock); + /* Re-program the RX descriptors from the buffers that are still + * attached to priv->dma_conf.rx_queue[].buf_pool[]. The buffer- + * address fields of the RX descriptors may have been overwritten + * by HW writeback while the DMA was being stopped on suspend + * (RDESx are reused for status/length on completion), so they + * must be repopulated before the DMA is restarted in + * stmmac_hw_setup() below; otherwise the controller would + * dereference stale addresses and trigger a fatal bus error. + * + * This path is allocation-free: it relies entirely on the RX + * buffers preserved across suspend, which makes the resume path + * safe under memory pressure. + */ + for (queue = 0; queue < priv->plat->rx_queues_to_use; queue++) + stmmac_reinit_rx_descriptors(priv, &priv->dma_conf, queue); + stmmac_reset_queues_param(priv); stmmac_free_tx_skbufs(priv); -- 2.34.1