The controller tasklet invokes callbacks after dropping the channel lock. Releasing a channel can therefore clear its configuration and disable the controller while that tasklet run is still using them. Disable the tasklet before clearing channel resources and re-enable it after updating the controller's in-use mask. This waits for an executing run while preserving pending schedules for the other channels. Have a pending run return before accessing registers when the last channel has already been released. Fixes: 3bfb1d20b547 ("dmaengine: Driver for the Synopsys DesignWare DMA controller") Cc: stable@vger.kernel.org Signed-off-by: James Hilliard --- drivers/dma/dw/core.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 1d6419e1cd3c..3bda85adbeee 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -398,6 +398,10 @@ static void dw_dma_tasklet(struct tasklet_struct *t) u32 status_err; unsigned int i; + /* Channel release may have drained a polled completion before this run. */ + if (!dw->in_use) + return; + status_xfer = dma_readl(dw, RAW.XFER); status_err = dma_readl(dw, RAW.ERROR); @@ -1067,6 +1071,8 @@ static void dwc_free_chan_resources(struct dma_chan *chan) struct dw_dma *dw = to_dw_dma(chan->device); unsigned long flags; + tasklet_disable(&dw->tasklet); + dev_dbg(chan2dev(chan), "%s: descs allocated=%u\n", __func__, dwc->descs_allocated); @@ -1092,6 +1098,8 @@ static void dwc_free_chan_resources(struct dma_chan *chan) if (!dw->in_use) do_dw_dma_off(dw); + tasklet_enable(&dw->tasklet); + dev_vdbg(chan2dev(chan), "%s: done\n", __func__); } -- 2.53.0