[PATCH 6/6] dmaengine: plx_dma: don't invoke client callbacks with ring_lock held
From: Logan Gunthorpe
Date: Tue Nov 03 2026 - 11:59:25 EST
plx_dma_process_desc() and plx_dma_abort_desc() invoke the client's
completion callback with ring_lock held. A callback that submits a new
transfer, which is common, deadlocks taking the same lock in
plx_dma_prep_memcpy().
Copy and clear the callback and advance tail under the lock, then drop
the lock to invoke it.
Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Link: https://lore.kernel.org/dmaengine/20260717223217.B6FBF1F000E9@xxxxxxxxxxxxxxx
Signed-off-by: Logan Gunthorpe <logang@xxxxxxxxxxxx>
---
drivers/dma/plx_dma.c | 16 ++++++++++++----
1 file changed, 12 insertions(+), 4 deletions(-)
diff --git a/drivers/dma/plx_dma.c b/drivers/dma/plx_dma.c
index 8c4b19d681ad..17745f7a5658 100644
--- a/drivers/dma/plx_dma.c
+++ b/drivers/dma/plx_dma.c
@@ -134,6 +134,7 @@ static struct plx_dma_desc *plx_dma_get_desc(struct plx_dma_dev *plxdev, int i)
static void plx_dma_process_desc(struct plx_dma_dev *plxdev)
{
+ struct dmaengine_desc_callback cb;
struct dmaengine_result res;
struct plx_dma_desc *desc;
u32 flags;
@@ -165,11 +166,14 @@ static void plx_dma_process_desc(struct plx_dma_dev *plxdev)
dma_cookie_complete(&desc->txd);
dma_descriptor_unmap(&desc->txd);
- dmaengine_desc_get_callback_invoke(&desc->txd, &res);
+ dmaengine_desc_get_callback(&desc->txd, &cb);
desc->txd.callback = NULL;
desc->txd.callback_result = NULL;
-
plxdev->tail++;
+
+ spin_unlock_bh(&plxdev->ring_lock);
+ dmaengine_desc_callback_invoke(&cb, &res);
+ spin_lock_bh(&plxdev->ring_lock);
}
spin_unlock_bh(&plxdev->ring_lock);
@@ -177,6 +181,7 @@ static void plx_dma_process_desc(struct plx_dma_dev *plxdev)
static void plx_dma_abort_desc(struct plx_dma_dev *plxdev)
{
+ struct dmaengine_desc_callback cb;
struct dmaengine_result res;
struct plx_dma_desc *desc;
@@ -192,11 +197,14 @@ static void plx_dma_abort_desc(struct plx_dma_dev *plxdev)
dma_cookie_complete(&desc->txd);
dma_descriptor_unmap(&desc->txd);
- dmaengine_desc_get_callback_invoke(&desc->txd, &res);
+ dmaengine_desc_get_callback(&desc->txd, &cb);
desc->txd.callback = NULL;
desc->txd.callback_result = NULL;
-
plxdev->tail++;
+
+ spin_unlock_bh(&plxdev->ring_lock);
+ dmaengine_desc_callback_invoke(&cb, &res);
+ spin_lock_bh(&plxdev->ring_lock);
}
spin_unlock_bh(&plxdev->ring_lock);
--
2.47.3