Re: [PATCH v3 4/4] dmaengine: dw: synchronize completion callbacks

From: Frank Li

Date: Wed Oct 28 2026 - 12:32:45 EST


On Fri, Oct 23, 2026 at 10:27:25AM -0600, James Hilliard wrote:
> The controller tasklet can detach completed descriptors and invoke their
> callbacks outside the channel lock. Terminating the channel does not wait
> for those callbacks, and there is no device_synchronize hook. A client can
> therefore reuse callback state or release its buffers after
> dmaengine_synchronize() while an old callback still uses them.
>
> Disable and re-enable the controller tasklet from device_synchronize,
> outside the channel lock. Disabling waits for an executing tasklet,
> including descriptors it already removed from the channel lists. After
> termination, a later tasklet run cannot acquire another descriptor from
> the terminated channel. Clients must continue to exclude new submissions
> on that channel until synchronization finishes.
>
> The tasklet is shared, so other channels may schedule it while this wait
> is in progress. Preserve those pending runs: tasklet_kill() temporarily
> owns the scheduling bit and can consume a concurrent scheduling request,
> leaving controller interrupts masked. The disable/enable pair leaves the
> scheduling bit and pending tasklet queue intact. Other channels' hardware
> transfers continue, though their callbacks may be briefly deferred.
>
> Fixes: b36f09c3c441 ("dmaengine: Add transfer termination synchronization support")
> Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
> Closes: https://lore.kernel.org/r/sashiko-outbox-180707@xxxxxxxxxx
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: James Hilliard <james.hilliard1@xxxxxxxxx>
> ---

Reviewed-by: Frank Li <Frank.Li@xxxxxxx>

> drivers/dma/dw/core.c | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c
> index 6d27af3c0f2d..d80be93f0c49 100644
> --- a/drivers/dma/dw/core.c
> +++ b/drivers/dma/dw/core.c
> @@ -910,6 +910,19 @@ static int dwc_terminate_all(struct dma_chan *chan)
> return 0;
> }
>
> +static void dwc_synchronize(struct dma_chan *chan)
> +{
> + struct dw_dma *dw = to_dw_dma(chan->device);
> +
> + /*
> + * Termination removes the channel's descriptors from the lists, but a
> + * running tasklet may still own a completed descriptor and its callback.
> + * Wait for that run without losing schedules from the other channels.
> + */
> + tasklet_disable(&dw->tasklet);
> + tasklet_enable(&dw->tasklet);
> +}
> +
> static struct dw_desc *dwc_find_desc(struct dw_dma_chan *dwc, dma_cookie_t c)
> {
> struct dw_desc *desc;
> @@ -1318,6 +1331,7 @@ int do_dma_probe(struct dw_dma_chip *chip)
> dw->dma.device_pause = dwc_pause;
> dw->dma.device_resume = dwc_resume;
> dw->dma.device_terminate_all = dwc_terminate_all;
> + dw->dma.device_synchronize = dwc_synchronize;
>
> dw->dma.device_tx_status = dwc_tx_status;
> dw->dma.device_issue_pending = dwc_issue_pending;
>
> --
> 2.53.0
>