The TX and RX interrupt handlers can schedule dma_err_tasklet. Killing it before freeing the IRQs leaves a window for an interrupt handler to schedule it again, so the tasklet can access descriptors and TX skb state after nixge_stop() releases them. Keep the initial DMA channel stop while the completion IRQ handlers are still installed. Then free both IRQs to stop and synchronize the tasklet producers before draining error recovery. A tasklet queued before the IRQs are freed can restart both channels. Stop them again after tasklet_kill() returns so error recovery cannot undo the final stop before the descriptors are released. This issue was identified during our ongoing static-analysis research while reviewing kernel code. Fixes: 492caffa8a1a ("net: ethernet: nixge: Add support for National Instruments XGE netdev") Cc: stable@vger.kernel.org Assisted-by: LLM Co-developed-by: Ijae Kim Signed-off-by: Ijae Kim Signed-off-by: Myeonghun Pak --- Compile-tested nixge.o on net commit 6dc989ea46b96ce170840174b4a38c4a387fb005 with x86_64 allyesconfig and allmodconfig, both with W=1. No hardware testing was performed. drivers/net/ethernet/ni/nixge.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/ni/nixge.c b/drivers/net/ethernet/ni/nixge.c index 230d5ff99dd7..7bba6f8e64fe 100644 --- a/drivers/net/ethernet/ni/nixge.c +++ b/drivers/net/ethernet/ni/nixge.c @@ -920,6 +920,7 @@ static int nixge_stop(struct net_device *ndev) phy_disconnect(ndev->phydev); } + /* Stop DMA while the completion IRQ handlers are still installed. */ cr = nixge_dma_read_reg(priv, XAXIDMA_RX_CR_OFFSET); nixge_dma_write_reg(priv, XAXIDMA_RX_CR_OFFSET, cr & (~XAXIDMA_CR_RUNSTOP_MASK)); @@ -927,11 +928,20 @@ static int nixge_stop(struct net_device *ndev) nixge_dma_write_reg(priv, XAXIDMA_TX_CR_OFFSET, cr & (~XAXIDMA_CR_RUNSTOP_MASK)); - tasklet_kill(&priv->dma_err_tasklet); - + /* Remove both producers before draining the error tasklet. */ free_irq(priv->tx_irq, ndev); free_irq(priv->rx_irq, ndev); + tasklet_kill(&priv->dma_err_tasklet); + + /* Error recovery may have restarted DMA, so stop both channels again. */ + cr = nixge_dma_read_reg(priv, XAXIDMA_RX_CR_OFFSET); + nixge_dma_write_reg(priv, XAXIDMA_RX_CR_OFFSET, + cr & (~XAXIDMA_CR_RUNSTOP_MASK)); + cr = nixge_dma_read_reg(priv, XAXIDMA_TX_CR_OFFSET); + nixge_dma_write_reg(priv, XAXIDMA_TX_CR_OFFSET, + cr & (~XAXIDMA_CR_RUNSTOP_MASK)); + nixge_hw_dma_bd_release(ndev); return 0; -- 2.53.0