[PATCHv2 4/9] dmaengine: mv_xor: abort channel before freeing resources on timeout
From: Rosen Penev
Date: Thu Jun 11 2026 - 17:08:14 EST
In mv_chan_memcpy_self_test() and mv_chan_xor_self_test(), if the DMA
operation times out (status check returns !DMA_COMPLETE), the code jumps
to free_resources which unmaps DMA buffers, frees the descriptor pool,
and releases the source/destination pages without ever stopping the
hardware channel. The DMA engine may still be executing and could DMA
into the freed destination pages, corrupting kernel memory.
Add mv_chan_disable() which masks interrupts and clears the activation
register to abort any in-flight operation. Call it in the self-test
error paths before freeing resources, and also at the top of
mv_xor_free_chan_resources() to protect clients that release the
channel while DMA is active.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@xxxxxxxxx>
---
drivers/dma/mv_xor.c | 21 +++++++++++++++++----
1 file changed, 17 insertions(+), 4 deletions(-)
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index 588af337afe3..44a7d4f7fb0d 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -169,6 +169,12 @@ static void mv_chan_activate(struct mv_xor_chan *chan)
writel(BIT(0), XOR_ACTIVATION(chan));
}
+static void mv_chan_disable(struct mv_xor_chan *chan)
+{
+ mv_chan_mask_interrupts(chan);
+ writel_relaxed(0, XOR_ACTIVATION(chan));
+}
+
static char mv_chan_is_busy(struct mv_xor_chan *chan)
{
u32 state = readl_relaxed(XOR_ACTIVATION(chan));
@@ -638,6 +644,8 @@ static void mv_xor_free_chan_resources(struct dma_chan *chan)
struct mv_xor_desc_slot *iter, *_iter;
int in_use_descs = 0;
+ mv_chan_disable(mv_chan);
+
spin_lock_bh(&mv_chan->lock);
mv_chan_slot_cleanup(mv_chan);
@@ -854,7 +862,7 @@ static int mv_chan_memcpy_self_test(struct mv_xor_chan *mv_chan)
dev_err(dma_chan->device->dev,
"Self-test copy timed out, disabling\n");
err = -ENODEV;
- goto free_resources;
+ goto free_chan;
}
dma_sync_single_for_cpu(dma_chan->device->dev, dest_dma,
@@ -863,9 +871,11 @@ static int mv_chan_memcpy_self_test(struct mv_xor_chan *mv_chan)
dev_err(dma_chan->device->dev,
"Self-test copy failed compare, disabling\n");
err = -ENODEV;
- goto free_resources;
+ goto free_chan;
}
+free_chan:
+ mv_chan_disable(mv_chan);
free_resources:
dmaengine_unmap_put(unmap);
mv_xor_free_chan_resources(dma_chan);
@@ -987,7 +997,7 @@ mv_chan_xor_self_test(struct mv_xor_chan *mv_chan)
dev_err(dma_chan->device->dev,
"Self-test xor timed out, disabling\n");
err = -ENODEV;
- goto free_resources;
+ goto free_chan;
}
dma_sync_single_for_cpu(dma_chan->device->dev, dest_dma,
@@ -999,10 +1009,12 @@ mv_chan_xor_self_test(struct mv_xor_chan *mv_chan)
"Self-test xor failed compare, disabling. index %d, data %x, expected %x\n",
i, ptr[i], cmp_word);
err = -ENODEV;
- goto free_resources;
+ goto free_chan;
}
}
+free_chan:
+ mv_chan_disable(mv_chan);
free_resources:
dmaengine_unmap_put(unmap);
mv_xor_free_chan_resources(dma_chan);
@@ -1020,6 +1032,7 @@ static int mv_xor_channel_remove(struct mv_xor_chan *mv_chan)
struct device *dev = mv_chan->dmadev.dev;
mv_chan_mask_interrupts(mv_chan);
+ mv_chan_disable(mv_chan);
tasklet_kill(&mv_chan->irq_tasklet);
dma_async_device_unregister(&mv_chan->dmadev);
--
2.54.0