[PATCH v4 18/24] dmaengine: dw-edma: Make the LL ring reset a full channel resync
Koichiro Den <[email protected]> Wed, 29 Jul 2026 23:30:30 +0900
| Newsgroups | org.kernel.vger.dmaengine,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- Changes in v4: - No changes. 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 b90b33460d22..b0e3f67d3942 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -214,10 +214,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. */ @@ -361,6 +380,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; @@ -379,12 +405,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