[PATCH v3 18/24] dmaengine: dw-edma: Make the LL ring reset a full channel resync

From: Koichiro Den

Date: Mon Jul 27 2026 - 13:20:44 EST


Reset currently clears the ring but leaves issued descriptors unchanged.
A descriptor with published entries is then stranded because LL progress
can no longer account for those entries.

Cancel any pending stopped-tail recheck before replacing the ring state.
Keep descriptors with no outstanding published entries so their
unpublished work can use the fresh ring. Abort descriptors with
outstanding entries because their consumption is unknown and replay is
unsafe.

Since resync can complete and remove descriptors, reset the ring before
looking up the next issued descriptor.

Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v3:
- Reset before looking up the next issued descriptor. (Sashiko)
- Cancel any pending stopped-tail recheck before replacing the ring
state. (Sashiko)

drivers/dma/dw-edma/dw-edma-core.c | 32 ++++++++++++++++++++++++------
1 file changed, 26 insertions(+), 6 deletions(-)

diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index fc8bea495aac..0c30c0f51fea 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -213,10 +213,29 @@ static void dw_hdma_set_callback_result(struct virt_dma_desc *vd,

static void dw_edma_core_reset_ll(struct dw_edma_chan *chan)
{
+ struct virt_dma_desc *vd, *tmp;
u32 i;

dw_edma_ll_recheck_cancel(chan);

+ /*
+ * Software cannot tell which published entries completed before the
+ * reset. Replaying one could duplicate a transfer after its target
+ * changed ownership, while keeping descriptors with uncertain entries
+ * would block later in-order completions. Abort descriptors with published
+ * entries; untouched descriptors remain available for republishing.
+ */
+ list_for_each_entry_safe(vd, tmp, &chan->vc.desc_issued, node) {
+ struct dw_edma_desc *desc = vd2dw_edma_desc(vd);
+
+ if (desc->start_burst == desc->done_burst)
+ continue;
+
+ dw_hdma_set_callback_result(vd, DMA_TRANS_ABORTED);
+ list_del(&vd->node);
+ vchan_cookie_complete(vd);
+ }
+
chan->ll_head = 0;
chan->ll_done = 0;
/* Drop stale CB bits before reusing the circular LL ring. */
@@ -364,6 +383,13 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan)
struct dw_edma_desc *desc;
struct virt_dma_desc *vd;

+ if (!chan->non_ll) {
+ if (dw_edma_abort_is_pending(chan))
+ return 0;
+ if (!chan->ll_valid)
+ dw_edma_core_reset_ll(chan);
+ }
+
vd = vchan_next_desc(&chan->vc);
if (!vd)
return 0;
@@ -382,12 +408,6 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan)
return 1;
}

- if (dw_edma_abort_is_pending(chan))
- return 0;
-
- if (!chan->ll_valid)
- dw_edma_core_reset_ll(chan);
-
dw_edma_core_start(desc);

return 1;
--
2.51.0