Re: [PATCH v7 2/7] dmaengine: dw-edma: Avoid resetting an LL on stale stopped status
From: Frank Li
Date: Wed Nov 04 2026 - 12:22:08 EST
On Thu, Nov 05, 2026 at 01:12:22AM +0900, Koichiro Den wrote:
> CS can still report STOPPED briefly after a doorbell write, before the
> DMA engine starts consuming the published LL entries. Finishing
> termination relying solely on that value can reset the ring while a
> transfer is about to start, leaving hardware and software ring positions
> out of sync and stalling subsequent transfers.
>
> Require LLP to reach the published tail, on top of the standard criteria
> (ie. CS=STOPPED and zero transfer size) before
> dw_edma_finish_termination(). Non-LL mode behavior is unchanged.
>
> Cc: stable@xxxxxxxxxxxxxxx
> Fixes: db79c4e264ef ("dmaengine: dw-edma: Make DMA link list work as a circular buffer")
> Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
> ---
Reviewed-by: Frank Li <Frank.Li@xxxxxxx>
> Changes from v6:
> - New patch. Resolve issues found during terminate/pause test under
> dma load.
>
> drivers/dma/dw-edma/dw-edma-core.c | 24 ++++++++++++++++++++++--
> 1 file changed, 22 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index b08bb31657c9..fb38d51037c5 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -146,6 +146,23 @@ static bool dw_edma_ll_pending(struct dw_edma_chan *chan)
> return chan->ll_head != chan->ll_done;
> }
>
> +static bool dw_edma_ll_stopped(struct dw_edma_chan *chan)
> +{
> + return dw_edma_core_ch_status(chan) == DMA_COMPLETE &&
> + !dw_edma_core_ch_transfer_size(chan);
> +}
> +
> +static bool dw_edma_ll_drained(struct dw_edma_chan *chan)
> +{
> + int idx = dw_edma_core_ll_cur_idx(chan);
> +
> + if (idx == chan->ll_max)
> + idx = 0;
> +
> + return idx >= 0 && (u32)idx == chan->ll_head &&
> + dw_edma_ll_stopped(chan);
> +}
> +
> static void dw_edma_core_ll_start(struct dw_edma_desc *desc)
> {
> struct dw_edma_chan *chan = desc->chan;
> @@ -455,7 +472,9 @@ static int dw_edma_device_terminate_all(struct dma_chan *dchan)
> dw_edma_finish_termination(chan);
> } else if (chan->status == EDMA_ST_IDLE) {
> dw_edma_finish_termination(chan);
> - } else if (dw_edma_core_ch_status(chan) == DMA_COMPLETE) {
> + } else if (chan->non_ll ?
> + dw_edma_core_ch_status(chan) == DMA_COMPLETE :
> + dw_edma_ll_drained(chan)) {
> /*
> * The channel is in a false BUSY state, probably didn't
> * receive or lost an interrupt
> @@ -790,7 +809,8 @@ static void dw_edma_done_interrupt_locked(struct dw_edma_chan *chan)
> if (!vd)
> break;
>
> - dw_edma_finish_termination(chan);
> + if (chan->non_ll || dw_edma_ll_drained(chan))
> + dw_edma_finish_termination(chan);
> break;
>
> default:
> --
> 2.54.0
>