When running XDP forwarding and interface gets shut down, kernel might panic or show SLUB "poison overwritten" errors due to a race condition between NAPI polling and resource freeing. Observed error is one of following: - Poison overwrriten [ 1889.547746] eth1: Link is Down [ 1889.549940] ============================================================================= [ 1889.549954] BUG kmalloc-4k (Tainted: G B ): Poison overwritten [ 1889.549959] ----------------------------------------------------------------------------- [ 1889.549963] 0xffffff882dcc4d80-0xffffff882dcc4da7 @offset=19840. First byte 0x0 instead of 0x6b [ 1889.549969] Allocated in __alloc_dma_tx_desc_resources+0x60/0x10c [stmmac] age=169 cpu=7 pid=27759 [ 1889.550020] __kmem_cache_alloc_node+0x100/0x2e8 [ 1889.550032] __kmalloc+0x58/0x1a0 [ 1889.550039] __alloc_dma_tx_desc_resources+0x60/0x10c [stmmac] [ 1889.550052] alloc_dma_desc_resources+0xec/0x164 [stmmac] [ 1889.550064] stmmac_setup_dma_desc+0xec/0x1e4 [stmmac] [ 1889.550076] stmmac_open+0x28/0x94 [stmmac] [...] - Wrong memory address [ 1901.546692] Unable to handle kernel paging request at virtual address dead000000000122 [...] [ 1902.964068] Call trace: [ 1902.967193] free_to_partial_list+0x560/0x600 [ 1902.972227] __slab_free+0x1a8/0x420 [ 1902.976480] __kmem_cache_free+0x204/0x218 [ 1902.981254] kfree+0x6c/0x128 [ 1902.984900] kvfree+0x3c/0x4c [ 1902.988545] page_pool_release+0x234/0x27c [ 1902.993320] page_pool_destroy+0xcc/0x190 [ 1902.998006] __free_dma_rx_desc_resources+0x100/0x360 [stmmac] [ 1903.004516] free_dma_desc_resources+0x8c/0xac [stmmac] [ 1903.010419] stmmac_release+0x1c0/0x2b4 [stmmac] [...] Root cause is stmmac_release() stops DMA and frees TX/RX ring buffers and page pools while XDP could still be accessing these resources in the background, because napi_synchronize() from napi_disable() does not ensure XDP is done. This makes small window where IRQ is still possible after NAPI has finished. Problem is that stmmac_release() handles closing XDP in different order than stmmac_xdp_disable_pool(), which is not affected by this issue, where synchronize_rcu() is executed after napi_disable() and disable_rx/tx_queue(). Fix this by following: - Set STMMAC_DOWN flag before stopping DMA to signal XDP to stop and discard - Call synchronize_rcu() after stopping DMA but before freeing resources to ensure all ongoing NAPI operations complete, in similar order to stmmac_xdp_disable_pool() - Clear STMMAC_DOWN flag in __stmmac_open() to restore normal operation. This was only done for stmmac_reset_subtask() during abnormal operation, which is not enough. This flag does not affect normal operation as it is used only for XDP apps. Usage of such flags is far from optimal and would be good to rewrite, but it would be quite an effort Also replace stmmac_disable_all_queues() with __stmmac_disable_all_queues(), as it is barren after this change. Fixes: bba2556efad66 ("net: stmmac: Enable RX via AF_XDP zero-copy") Co-developed-by: Chang-Sub Lee Signed-off-by: Chang-Sub Lee Signed-off-by: Jakub Raczynski --- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index b9ffff001baf..9d971ae35d48 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -262,7 +262,11 @@ static void stmmac_verify_args(void) pr_warn("stmmac: module parameter 'flow_ctrl' is obsolete - please remove from your module configuration\n"); } -static void __stmmac_disable_all_queues(struct stmmac_priv *priv) +/** + * stmmac_disable_all_queues - Disable all queues + * @priv: driver private structure + */ +static void stmmac_disable_all_queues(struct stmmac_priv *priv) { u8 rx_queues_cnt = priv->plat->rx_queues_to_use; u8 tx_queues_cnt = priv->plat->tx_queues_to_use; @@ -286,16 +290,15 @@ static void __stmmac_disable_all_queues(struct stmmac_priv *priv) } /** - * stmmac_disable_all_queues - Disable all queues + * stmmac_drain_xdp - Cleanup for XDP apps * @priv: driver private structure */ -static void stmmac_disable_all_queues(struct stmmac_priv *priv) +static void stmmac_drain_xdp(struct stmmac_priv *priv) { u8 rx_queues_cnt = priv->plat->rx_queues_to_use; struct stmmac_rx_queue *rx_q; u8 queue; - /* synchronize_rcu() needed for pending XDP buffers to drain */ for (queue = 0; queue < rx_queues_cnt; queue++) { rx_q = &priv->dma_conf.rx_queue[queue]; if (rx_q->xsk_pool) { @@ -303,8 +306,6 @@ static void stmmac_disable_all_queues(struct stmmac_priv *priv) break; } } - - __stmmac_disable_all_queues(priv); } /** @@ -4149,6 +4150,9 @@ static int __stmmac_open(struct net_device *dev, stmmac_reset_queues_param(priv); + /* Clear DOWN flag when opening the interface */ + clear_bit(STMMAC_DOWN, &priv->state); + ret = stmmac_hw_setup(dev); if (ret < 0) { netdev_err(priv->dev, "%s: Hw setup failed\n", __func__); @@ -4243,6 +4247,9 @@ static void __stmmac_release(struct net_device *dev) /* Stop and disconnect the PHY */ phylink_stop(priv->phylink); + /* Set DOWN flag to prevent XDP from processing new packets */ + set_bit(STMMAC_DOWN, &priv->state); + stmmac_disable_all_queues(priv); for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) @@ -4256,6 +4263,8 @@ static void __stmmac_release(struct net_device *dev) /* Stop TX/RX DMA and clear the descriptors */ stmmac_stop_all_dma(priv); + stmmac_drain_xdp(priv); + /* Release and free the Rx/Tx resources */ free_dma_desc_resources(priv, &priv->dma_conf); @@ -6412,7 +6421,7 @@ static int stmmac_setup_tc_block_cb(enum tc_setup_type type, void *type_data, if (!tc_cls_can_offload_and_chain0(priv->dev, type_data)) return ret; - __stmmac_disable_all_queues(priv); + stmmac_disable_all_queues(priv); switch (type) { case TC_SETUP_CLSU32: @@ -7122,6 +7131,9 @@ void stmmac_xdp_release(struct net_device *dev) /* Stop TX/RX DMA channels */ stmmac_stop_all_dma(priv); + /* Drain leftover XDP buffers */ + stmmac_drain_xdp(priv); + /* Release and free the Rx/Tx resources */ free_dma_desc_resources(priv, &priv->dma_conf); @@ -8208,6 +8220,8 @@ int stmmac_suspend(struct device *dev) /* Stop TX/RX DMA */ stmmac_stop_all_dma(priv); + stmmac_drain_xdp(priv); + stmmac_legacy_serdes_power_down(priv); /* Enable Power down mode by programming the PMT regs */ -- 2.34.1