Re: [PATCH v3 4/9] dmaengine: dw-edma: Complete descriptors before pausing

From: Frank Li

Date: Wed Jul 15 2026 - 14:57:16 EST


On Thu, Jul 16, 2026 at 02:57:35AM +0900, Koichiro Den wrote:
> If PAUSE is requested while the final chunk of a descriptor is in
> flight, the DONE interrupt takes the PAUSE path without checking whether
> the descriptor has been depleted. The depleted descriptor remains on the
> issued list and the channel enters EDMA_ST_PAUSE.
>
> On resume, dw_edma_start_transfer() finds the descriptor but no chunk to
> start and returns 0. The caller ignores that result and leaves the
> channel stuck in EDMA_ST_BUSY with no transfer running.
>
> Check for descriptor completion before acknowledging PAUSE. If there is
> no work to start on resume, leave the channel idle. Also ignore DONE
> interrupts while the channel is paused so a stale or repeated interrupt
> cannot change its state or start queued work.
>
> Fixes: e63d79d1ffcd ("dmaengine: Add Synopsys eDMA IP core driver")
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
> ---

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

> Changes in v3:
> - New patch for a pre-existing PAUSE completion issue. (Sashiko)
>
> drivers/dma/dw-edma/dw-edma-core.c | 20 ++++++++++++++------
> 1 file changed, 14 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index 272b03405746..b06b299661c0 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -327,7 +327,8 @@ static int dw_edma_device_resume(struct dma_chan *dchan)
> err = -EPERM;
> } else {
> chan->status = EDMA_ST_BUSY;
> - dw_edma_start_transfer(chan);
> + if (!dw_edma_start_transfer(chan))
> + chan->status = EDMA_ST_IDLE;
> }
>
> return err;
> @@ -691,10 +692,16 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> unsigned long flags;
>
> spin_lock_irqsave(&chan->vc.lock, flags);
> + if (chan->status == EDMA_ST_PAUSE) {
> + spin_unlock_irqrestore(&chan->vc.lock, flags);
> + return;
> + }
> +
> vd = vchan_next_desc(&chan->vc);
> if (vd) {
> switch (chan->request) {
> case EDMA_REQ_NONE:
> + case EDMA_REQ_PAUSE:
> desc = vd2dw_edma_desc(vd);
> if (!desc->chunks_alloc) {
> dw_hdma_set_callback_result(vd,
> @@ -703,6 +710,12 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> vchan_cookie_complete(vd);
> }
>
> + if (chan->request == EDMA_REQ_PAUSE) {
> + chan->request = EDMA_REQ_NONE;
> + chan->status = EDMA_ST_PAUSE;
> + break;
> + }
> +
> /* Continue transferring if there are remaining chunks or issued requests.
> */
> chan->status = dw_edma_start_transfer(chan) ? EDMA_ST_BUSY : EDMA_ST_IDLE;
> @@ -714,11 +727,6 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
> chan->status = EDMA_ST_IDLE;
> break;
>
> - case EDMA_REQ_PAUSE:
> - chan->request = EDMA_REQ_NONE;
> - chan->status = EDMA_ST_PAUSE;
> - break;
> -
> default:
> break;
> }
> --
> 2.51.0
>