[PATCH 2/7] dmaengine: dw-edma: Terminate STOP requests without callbacks
From: Koichiro Den
Date: Fri Jul 10 2026 - 04:09:30 EST
The STOP request path handles device_terminate_all(). The DMA Engine
client documentation says in the "Terminate APIs" section of
Documentation/driver-api/dmaengine/client.rst:
"No callback functions will be called for any incomplete transfers."
dw-edma used vchan_cookie_complete() for a stopped descriptor. This
queues the descriptor on the completed list and schedules its callback.
A late callback after dmaengine_terminate_sync() can dereference
callback state, such as a request object, that the client has already
freed.
Move the stopped descriptor to the terminated list. Complete the cookie
before doing so, so cookie polling observes that the transfer is no
longer in flight, but do not schedule the completion callback. Add a
synchronize callback so virt-dma can release terminated descriptors.
Fixes: e63d79d1ffcd ("dmaengine: Add Synopsys eDMA IP core driver")
Signed-off-by: Koichiro Den <den@xxxxxxxxxxxxx>
---
Changes in v2:
- Split out into this preparation series (was patch 03/17 of
the dynamic LL appends v1).
- No changes.
drivers/dma/dw-edma/dw-edma-core.c | 18 ++++++++++++++++--
1 file changed, 16 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 89a4c498a17b..4e0dc52397e2 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -201,6 +201,13 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan)
return 1;
}
+static void dw_edma_terminate_vdesc(struct virt_dma_desc *vd)
+{
+ list_del(&vd->node);
+ dma_cookie_complete(&vd->tx);
+ vchan_terminate_vdesc(vd);
+}
+
static void dw_edma_device_caps(struct dma_chan *dchan,
struct dma_slave_caps *caps)
{
@@ -673,8 +680,7 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
break;
case EDMA_REQ_STOP:
- list_del(&vd->node);
- vchan_cookie_complete(vd);
+ dw_edma_terminate_vdesc(vd);
chan->request = EDMA_REQ_NONE;
chan->status = EDMA_ST_IDLE;
break;
@@ -856,6 +862,13 @@ static int dw_edma_alloc_chan_resources(struct dma_chan *dchan)
return 0;
}
+static void dw_edma_device_synchronize(struct dma_chan *dchan)
+{
+ struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
+
+ vchan_synchronize(&chan->vc);
+}
+
static void dw_edma_free_chan_resources(struct dma_chan *dchan)
{
unsigned long timeout = jiffies + msecs_to_jiffies(5000);
@@ -968,6 +981,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
dma->device_pause = dw_edma_device_pause;
dma->device_resume = dw_edma_device_resume;
dma->device_terminate_all = dw_edma_device_terminate_all;
+ dma->device_synchronize = dw_edma_device_synchronize;
dma->device_issue_pending = dw_edma_device_issue_pending;
dma->device_tx_status = dw_edma_device_tx_status;
dma->device_prep_slave_sg = dw_edma_device_prep_slave_sg;
--
2.51.0