* [PATCHv2 1/9] dmaengine: mv_xor: initialize chan state before requesting IRQ
2026-06-11 21:07 [PATCHv2 0/9] dma: mv_xor: convert to devm resource management Rosen Penev
@ 2026-06-11 21:07 ` Rosen Penev
2026-06-11 21:07 ` [PATCHv2 2/9] dmaengine: mv_xor: fix use-after-free in probe error path Rosen Penev
` (7 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Rosen Penev @ 2026-06-11 21:07 UTC (permalink / raw)
To: dmaengine
Cc: Vinod Koul, Frank Li, Thomas Petazzoni, Gregory CLEMENT,
Marcin Wojtas, Rob Herring, open list
In mv_xor_channel_add(), the IRQ is requested and unmasked before the
channel's spinlock, descriptor lists, and cookie state are initialized.
If an interrupt fires immediately (e.g. from a shared IRQ or previous
bind/unbind cycle), the handler schedules the tasklet, which then
accesses the uninitialized spinlock and lists in mv_chan_slot_cleanup(),
resulting in undefined behavior.
Fix by moving spin_lock_init(), INIT_LIST_HEAD(), dma_cookie_init(),
and tasklet_setup() to immediately follow the basic struct field
initialization, before any DMA mappings or IRQ registration.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/dma/mv_xor.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index 25ed61f1b089..93a8e9f7c529 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -1054,6 +1054,18 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
dma_dev->dev = &pdev->dev;
mv_chan->xordev = xordev;
+ spin_lock_init(&mv_chan->lock);
+ INIT_LIST_HEAD(&mv_chan->chain);
+ INIT_LIST_HEAD(&mv_chan->completed_slots);
+ INIT_LIST_HEAD(&mv_chan->free_slots);
+ INIT_LIST_HEAD(&mv_chan->allocated_slots);
+ mv_chan->dmachan.device = dma_dev;
+ dma_cookie_init(&mv_chan->dmachan);
+
+ mv_chan->mmr_base = xordev->xor_base;
+ mv_chan->mmr_high_base = xordev->xor_high_base;
+ tasklet_setup(&mv_chan->irq_tasklet, mv_xor_tasklet);
+
/*
* These source and destination dummy buffers are used to implement
* a DMA_INTERRUPT operation as a minimum-sized XOR operation.
@@ -1105,10 +1117,6 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
dma_dev->device_prep_dma_xor = mv_xor_prep_dma_xor;
}
- mv_chan->mmr_base = xordev->xor_base;
- mv_chan->mmr_high_base = xordev->xor_high_base;
- tasklet_setup(&mv_chan->irq_tasklet, mv_xor_tasklet);
-
/* clear errors before enabling interrupts */
mv_chan_clear_err_status(mv_chan);
@@ -1124,14 +1132,6 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
else
mv_chan_set_mode(mv_chan, XOR_OPERATION_MODE_XOR);
- spin_lock_init(&mv_chan->lock);
- INIT_LIST_HEAD(&mv_chan->chain);
- INIT_LIST_HEAD(&mv_chan->completed_slots);
- INIT_LIST_HEAD(&mv_chan->free_slots);
- INIT_LIST_HEAD(&mv_chan->allocated_slots);
- mv_chan->dmachan.device = dma_dev;
- dma_cookie_init(&mv_chan->dmachan);
-
list_add_tail(&mv_chan->dmachan.device_node, &dma_dev->channels);
if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) {
--
2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCHv2 2/9] dmaengine: mv_xor: fix use-after-free in probe error path
2026-06-11 21:07 [PATCHv2 0/9] dma: mv_xor: convert to devm resource management Rosen Penev
2026-06-11 21:07 ` [PATCHv2 1/9] dmaengine: mv_xor: initialize chan state before requesting IRQ Rosen Penev
@ 2026-06-11 21:07 ` Rosen Penev
2026-06-11 21:07 ` [PATCHv2 3/9] dmaengine: mv_xor: bound maximum channels for Armada 37xx Rosen Penev
` (6 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Rosen Penev @ 2026-06-11 21:07 UTC (permalink / raw)
To: dmaengine
Cc: Vinod Koul, Frank Li, Thomas Petazzoni, Gregory CLEMENT,
Marcin Wojtas, Rob Herring, open list
mv_xor_channel_remove() does not call tasklet_kill() to cancel
mv_chan->irq_tasklet. In the probe error path (err_channel_add) the
channel structure is devm-allocated, so it is freed automatically when
the probe function returns. If an interrupt fires and schedules the
tasklet during teardown, it can execute after devres has freed mv_chan,
resulting in a use-after-free.
Fix this by masking hardware interrupts on the channel and then calling
tasklet_kill() at the start of mv_xor_channel_remove(), ensuring no
new interrupts can schedule the tasklet and any already-running
instance has completed before the rest of the channel is torn down.
Assisted-by: opencode:big-pickle
Fixes: a6b4a9d2c106 ("dma: mv_xor: split initialization/cleanup of XOR channels")
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/dma/mv_xor.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index 93a8e9f7c529..ef29e8be1db6 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -106,6 +106,14 @@ static void mv_chan_set_next_descriptor(struct mv_xor_chan *chan,
writel_relaxed(next_desc_addr, XOR_NEXT_DESC(chan));
}
+static void mv_chan_mask_interrupts(struct mv_xor_chan *chan)
+{
+ u32 val = readl_relaxed(XOR_INTR_MASK(chan));
+
+ val &= ~(XOR_INTR_MASK_VALUE << (chan->idx * 16));
+ writel_relaxed(val, XOR_INTR_MASK(chan));
+}
+
static void mv_chan_unmask_interrupts(struct mv_xor_chan *chan)
{
u32 val = readl_relaxed(XOR_INTR_MASK(chan));
@@ -1011,6 +1019,9 @@ static int mv_xor_channel_remove(struct mv_xor_chan *mv_chan)
struct dma_chan *chan, *_chan;
struct device *dev = mv_chan->dmadev.dev;
+ mv_chan_mask_interrupts(mv_chan);
+ tasklet_kill(&mv_chan->irq_tasklet);
+
dma_async_device_unregister(&mv_chan->dmadev);
dma_free_wc(dev, MV_XOR_POOL_SIZE,
--
2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCHv2 3/9] dmaengine: mv_xor: bound maximum channels for Armada 37xx
2026-06-11 21:07 [PATCHv2 0/9] dma: mv_xor: convert to devm resource management Rosen Penev
2026-06-11 21:07 ` [PATCHv2 1/9] dmaengine: mv_xor: initialize chan state before requesting IRQ Rosen Penev
2026-06-11 21:07 ` [PATCHv2 2/9] dmaengine: mv_xor: fix use-after-free in probe error path Rosen Penev
@ 2026-06-11 21:07 ` Rosen Penev
2026-06-11 21:07 ` [PATCHv2 4/9] dmaengine: mv_xor: abort channel before freeing resources on timeout Rosen Penev
` (5 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Rosen Penev @ 2026-06-11 21:07 UTC (permalink / raw)
To: dmaengine
Cc: Vinod Koul, Frank Li, Thomas Petazzoni, Gregory CLEMENT,
Marcin Wojtas, Rob Herring, open list
For XOR_ARMADA_37XX the driver set max_channels = num_present_cpus()
without bounding it by MV_XOR_MAX_CHANNELS (2). On a system with
more than 2 CPUs this lets the probe loop write past the end of the
xordev->channels[] array when the DT describes enough child nodes.
Add the missing min_t() guard.
Assisted-by: opencode:big-pickle
Fixes: ac5f0f3f863e ("dmaengine: mv_xor: add support for Armada 3700 SoC")
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/dma/mv_xor.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index ef29e8be1db6..588af337afe3 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -1387,7 +1387,8 @@ static int mv_xor_probe(struct platform_device *pdev)
*/
max_engines = num_present_cpus();
if (xordev->xor_type == XOR_ARMADA_37XX)
- max_channels = num_present_cpus();
+ max_channels = min_t(unsigned int, MV_XOR_MAX_CHANNELS,
+ num_present_cpus());
else
max_channels = min_t(unsigned int,
MV_XOR_MAX_CHANNELS,
--
2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCHv2 4/9] dmaengine: mv_xor: abort channel before freeing resources on timeout
2026-06-11 21:07 [PATCHv2 0/9] dma: mv_xor: convert to devm resource management Rosen Penev
` (2 preceding siblings ...)
2026-06-11 21:07 ` [PATCHv2 3/9] dmaengine: mv_xor: bound maximum channels for Armada 37xx Rosen Penev
@ 2026-06-11 21:07 ` Rosen Penev
2026-06-11 21:07 ` [PATCHv2 5/9] dmaengine: mv_xor: use devm_clk_get_optional_enabled Rosen Penev
` (4 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Rosen Penev @ 2026-06-11 21:07 UTC (permalink / raw)
To: dmaengine
Cc: Vinod Koul, Frank Li, Thomas Petazzoni, Gregory CLEMENT,
Marcin Wojtas, Rob Herring, open list
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@gmail.com>
---
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
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCHv2 5/9] dmaengine: mv_xor: use devm_clk_get_optional_enabled
2026-06-11 21:07 [PATCHv2 0/9] dma: mv_xor: convert to devm resource management Rosen Penev
` (3 preceding siblings ...)
2026-06-11 21:07 ` [PATCHv2 4/9] dmaengine: mv_xor: abort channel before freeing resources on timeout Rosen Penev
@ 2026-06-11 21:07 ` Rosen Penev
2026-06-11 21:07 ` [PATCHv2 6/9] dmaengine: mv_xor: switch to of_irq_get() Rosen Penev
` (3 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Rosen Penev @ 2026-06-11 21:07 UTC (permalink / raw)
To: dmaengine
Cc: Vinod Koul, Frank Li, Thomas Petazzoni, Gregory CLEMENT,
Marcin Wojtas, Rob Herring, open list
Replace clk_get() + clk_prepare_enable() + clk_put() with
devm_clk_get_optional_enabled(). This eliminates the need for manual clock
cleanup in the probe error path.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/dma/mv_xor.c | 14 +++-----------
1 file changed, 3 insertions(+), 11 deletions(-)
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index 44a7d4f7fb0d..d9403172ef59 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -1383,12 +1383,9 @@ static int mv_xor_probe(struct platform_device *pdev)
mv_xor_conf_mbus_windows(xordev, dram);
}
- /* Not all platforms can gate the clock, so it is not
- * an error if the clock does not exists.
- */
- xordev->clk = clk_get(&pdev->dev, NULL);
- if (!IS_ERR(xordev->clk))
- clk_prepare_enable(xordev->clk);
+ xordev->clk = devm_clk_get_optional_enabled(&pdev->dev, NULL);
+ if (IS_ERR(xordev->clk))
+ return PTR_ERR(xordev->clk);
/*
* We don't want to have more than one channel per CPU in
@@ -1477,11 +1474,6 @@ static int mv_xor_probe(struct platform_device *pdev)
irq_dispose_mapping(xordev->channels[i]->irq);
}
- if (!IS_ERR(xordev->clk)) {
- clk_disable_unprepare(xordev->clk);
- clk_put(xordev->clk);
- }
-
return ret;
}
--
2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCHv2 6/9] dmaengine: mv_xor: switch to of_irq_get()
2026-06-11 21:07 [PATCHv2 0/9] dma: mv_xor: convert to devm resource management Rosen Penev
` (4 preceding siblings ...)
2026-06-11 21:07 ` [PATCHv2 5/9] dmaengine: mv_xor: use devm_clk_get_optional_enabled Rosen Penev
@ 2026-06-11 21:07 ` Rosen Penev
2026-06-11 21:07 ` [PATCHv2 7/9] dmaengine: mv_xor: use devm for dma pool and irq Rosen Penev
` (2 subsequent siblings)
8 siblings, 0 replies; 10+ messages in thread
From: Rosen Penev @ 2026-06-11 21:07 UTC (permalink / raw)
To: dmaengine
Cc: Vinod Koul, Frank Li, Thomas Petazzoni, Gregory CLEMENT,
Marcin Wojtas, Rob Herring, open list
Replace irq_of_parse_and_map() + irq_dispose_mapping() with of_irq_get(),
which maps the IRQ and registers a devres action to automatically dispose
the mapping on driver removal or probe failure. This allows dropping the
explicit irq_dispose_mapping() calls in the probe error path and remove
function.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/dma/mv_xor.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index d9403172ef59..eefc8f22bec6 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -1423,9 +1423,9 @@ static int mv_xor_probe(struct platform_device *pdev)
dma_cap_set(DMA_XOR, cap_mask);
dma_cap_set(DMA_INTERRUPT, cap_mask);
- irq = irq_of_parse_and_map(np, 0);
- if (!irq) {
- ret = -ENODEV;
+ irq = of_irq_get(np, 0);
+ if (irq < 0) {
+ ret = irq;
goto err_channel_add;
}
@@ -1433,7 +1433,6 @@ static int mv_xor_probe(struct platform_device *pdev)
cap_mask, irq);
if (IS_ERR(chan)) {
ret = PTR_ERR(chan);
- irq_dispose_mapping(irq);
goto err_channel_add;
}
@@ -1468,11 +1467,8 @@ static int mv_xor_probe(struct platform_device *pdev)
err_channel_add:
for (i = 0; i < MV_XOR_MAX_CHANNELS; i++)
- if (xordev->channels[i]) {
+ if (xordev->channels[i])
mv_xor_channel_remove(xordev->channels[i]);
- if (pdev->dev.of_node)
- irq_dispose_mapping(xordev->channels[i]->irq);
- }
return ret;
}
--
2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCHv2 7/9] dmaengine: mv_xor: use devm for dma pool and irq
2026-06-11 21:07 [PATCHv2 0/9] dma: mv_xor: convert to devm resource management Rosen Penev
` (5 preceding siblings ...)
2026-06-11 21:07 ` [PATCHv2 6/9] dmaengine: mv_xor: switch to of_irq_get() Rosen Penev
@ 2026-06-11 21:07 ` Rosen Penev
2026-06-11 21:07 ` [PATCHv2 8/9] dmaengine: mv_xor: allocate dummy buffers with dmam_alloc_coherent Rosen Penev
2026-06-11 21:07 ` [PATCHv2 9/9] dmaengine: mv_xor: add missing platform remove function Rosen Penev
8 siblings, 0 replies; 10+ messages in thread
From: Rosen Penev @ 2026-06-11 21:07 UTC (permalink / raw)
To: dmaengine
Cc: Vinod Koul, Frank Li, Thomas Petazzoni, Gregory CLEMENT,
Marcin Wojtas, Rob Herring, open list
Replace dma_alloc_wc() with dmam_alloc_attrs() and request_irq() with
devm_request_irq(). This eliminates the need for manual cleanup of the dma
pool and irq in both the channel remove function and the channel add
error labels, removing the err_free_irq and err_free_dma labels entirely.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/dma/mv_xor.c | 23 +++++++----------------
1 file changed, 7 insertions(+), 16 deletions(-)
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index eefc8f22bec6..0c159b9e9216 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -1037,8 +1037,6 @@ static int mv_xor_channel_remove(struct mv_xor_chan *mv_chan)
dma_async_device_unregister(&mv_chan->dmadev);
- dma_free_wc(dev, MV_XOR_POOL_SIZE,
- mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool);
dma_unmap_single(dev, mv_chan->dummy_src_addr,
MV_XOR_MIN_BYTE_COUNT, DMA_FROM_DEVICE);
dma_unmap_single(dev, mv_chan->dummy_dst_addr,
@@ -1049,8 +1047,6 @@ static int mv_xor_channel_remove(struct mv_xor_chan *mv_chan)
list_del(&chan->device_node);
}
- free_irq(mv_chan->irq, mv_chan);
-
return 0;
}
@@ -1113,8 +1109,8 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
* requires that we explicitly flush the writes
*/
mv_chan->dma_desc_pool_virt =
- dma_alloc_wc(&pdev->dev, MV_XOR_POOL_SIZE, &mv_chan->dma_desc_pool,
- GFP_KERNEL);
+ dmam_alloc_attrs(&pdev->dev, MV_XOR_POOL_SIZE, &mv_chan->dma_desc_pool,
+ GFP_KERNEL, DMA_ATTR_WRITE_COMBINE);
if (!mv_chan->dma_desc_pool_virt) {
ret = -ENOMEM;
goto err_unmap_dst;
@@ -1144,10 +1140,10 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
/* clear errors before enabling interrupts */
mv_chan_clear_err_status(mv_chan);
- ret = request_irq(mv_chan->irq, mv_xor_interrupt_handler,
+ ret = devm_request_irq(&pdev->dev, mv_chan->irq, mv_xor_interrupt_handler,
0, dev_name(&pdev->dev), mv_chan);
if (ret)
- goto err_free_dma;
+ goto err_unmap_dst;
mv_chan_unmask_interrupts(mv_chan);
@@ -1162,14 +1158,14 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
ret = mv_chan_memcpy_self_test(mv_chan);
dev_dbg(&pdev->dev, "memcpy self test returned %d\n", ret);
if (ret)
- goto err_free_irq;
+ goto err_unmap_dst;
}
if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) {
ret = mv_chan_xor_self_test(mv_chan);
dev_dbg(&pdev->dev, "xor self test returned %d\n", ret);
if (ret)
- goto err_free_irq;
+ goto err_unmap_dst;
}
dev_info(&pdev->dev, "Marvell XOR (%s): ( %s%s%s)\n",
@@ -1180,15 +1176,10 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
ret = dma_async_device_register(dma_dev);
if (ret)
- goto err_free_irq;
+ goto err_unmap_dst;
return mv_chan;
-err_free_irq:
- free_irq(mv_chan->irq, mv_chan);
-err_free_dma:
- dma_free_wc(&pdev->dev, MV_XOR_POOL_SIZE,
- mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool);
err_unmap_dst:
dma_unmap_single(dma_dev->dev, mv_chan->dummy_dst_addr,
MV_XOR_MIN_BYTE_COUNT, DMA_TO_DEVICE);
--
2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCHv2 8/9] dmaengine: mv_xor: allocate dummy buffers with dmam_alloc_coherent
2026-06-11 21:07 [PATCHv2 0/9] dma: mv_xor: convert to devm resource management Rosen Penev
` (6 preceding siblings ...)
2026-06-11 21:07 ` [PATCHv2 7/9] dmaengine: mv_xor: use devm for dma pool and irq Rosen Penev
@ 2026-06-11 21:07 ` Rosen Penev
2026-06-11 21:07 ` [PATCHv2 9/9] dmaengine: mv_xor: add missing platform remove function Rosen Penev
8 siblings, 0 replies; 10+ messages in thread
From: Rosen Penev @ 2026-06-11 21:07 UTC (permalink / raw)
To: dmaengine
Cc: Vinod Koul, Frank Li, Thomas Petazzoni, Gregory CLEMENT,
Marcin Wojtas, Rob Herring, open list
Replace the streaming DMA mappings for the dummy interrupt-operation
buffers with coherent allocations. The embedded char arrays in the
channel struct shared cachelines with other members, so dma_map_single
could corrupt adjacent data during cache maintenance. These buffers
are never touched by the CPU, so coherent memory is the correct choice.
The old DMA directions were also reversed: dummy_src is read by the
XOR engine (should be DMA_TO_DEVICE) and dummy_dst is written by it
(should be DMA_FROM_DEVICE). Coherent allocations are semantically
directionless, sidestepping the issue entirely.
With dmam_alloc_coherent managing the lifetime the old dma_unmap_single
calls and the error-path labels in mv_xor_channel_add are no longer
needed.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/dma/mv_xor.c | 50 ++++++++++++++------------------------------
drivers/dma/mv_xor.h | 4 ++--
2 files changed, 18 insertions(+), 36 deletions(-)
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index 0c159b9e9216..255df2dd9c71 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -1029,7 +1029,6 @@ mv_chan_xor_self_test(struct mv_xor_chan *mv_chan)
static int mv_xor_channel_remove(struct mv_xor_chan *mv_chan)
{
struct dma_chan *chan, *_chan;
- struct device *dev = mv_chan->dmadev.dev;
mv_chan_mask_interrupts(mv_chan);
mv_chan_disable(mv_chan);
@@ -1037,11 +1036,6 @@ static int mv_xor_channel_remove(struct mv_xor_chan *mv_chan)
dma_async_device_unregister(&mv_chan->dmadev);
- dma_unmap_single(dev, mv_chan->dummy_src_addr,
- MV_XOR_MIN_BYTE_COUNT, DMA_FROM_DEVICE);
- dma_unmap_single(dev, mv_chan->dummy_dst_addr,
- MV_XOR_MIN_BYTE_COUNT, DMA_TO_DEVICE);
-
list_for_each_entry_safe(chan, _chan, &mv_chan->dmadev.channels,
device_node) {
list_del(&chan->device_node);
@@ -1055,9 +1049,9 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
struct platform_device *pdev,
int idx, dma_cap_mask_t cap_mask, int irq)
{
- int ret = 0;
struct mv_xor_chan *mv_chan;
struct dma_device *dma_dev;
+ int ret;
mv_chan = devm_kzalloc(&pdev->dev, sizeof(*mv_chan), GFP_KERNEL);
if (!mv_chan)
@@ -1089,19 +1083,18 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
/*
* These source and destination dummy buffers are used to implement
* a DMA_INTERRUPT operation as a minimum-sized XOR operation.
- * Hence, we only need to map the buffers at initialization-time.
+ * Hence, we only need to allocate the buffers at initialization-time.
+ * The XOR engine reads from dummy_src and writes to dummy_dst.
*/
- mv_chan->dummy_src_addr = dma_map_single(dma_dev->dev,
- mv_chan->dummy_src, MV_XOR_MIN_BYTE_COUNT, DMA_FROM_DEVICE);
- if (dma_mapping_error(dma_dev->dev, mv_chan->dummy_src_addr))
+ mv_chan->dummy_src = dmam_alloc_coherent(&pdev->dev, MV_XOR_MIN_BYTE_COUNT,
+ &mv_chan->dummy_src_addr, GFP_KERNEL);
+ if (!mv_chan->dummy_src)
return ERR_PTR(-ENOMEM);
- mv_chan->dummy_dst_addr = dma_map_single(dma_dev->dev,
- mv_chan->dummy_dst, MV_XOR_MIN_BYTE_COUNT, DMA_TO_DEVICE);
- if (dma_mapping_error(dma_dev->dev, mv_chan->dummy_dst_addr)) {
- ret = -ENOMEM;
- goto err_unmap_src;
- }
+ mv_chan->dummy_dst = dmam_alloc_coherent(&pdev->dev, MV_XOR_MIN_BYTE_COUNT,
+ &mv_chan->dummy_dst_addr, GFP_KERNEL);
+ if (!mv_chan->dummy_dst)
+ return ERR_PTR(-ENOMEM);
/* allocate coherent memory for hardware descriptors
@@ -1111,10 +1104,8 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
mv_chan->dma_desc_pool_virt =
dmam_alloc_attrs(&pdev->dev, MV_XOR_POOL_SIZE, &mv_chan->dma_desc_pool,
GFP_KERNEL, DMA_ATTR_WRITE_COMBINE);
- if (!mv_chan->dma_desc_pool_virt) {
- ret = -ENOMEM;
- goto err_unmap_dst;
- }
+ if (!mv_chan->dma_desc_pool_virt)
+ return ERR_PTR(-ENOMEM);
/* discover transaction capabilities from the platform data */
dma_dev->cap_mask = cap_mask;
@@ -1143,7 +1134,7 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
ret = devm_request_irq(&pdev->dev, mv_chan->irq, mv_xor_interrupt_handler,
0, dev_name(&pdev->dev), mv_chan);
if (ret)
- goto err_unmap_dst;
+ return ERR_PTR(ret);
mv_chan_unmask_interrupts(mv_chan);
@@ -1158,14 +1149,14 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
ret = mv_chan_memcpy_self_test(mv_chan);
dev_dbg(&pdev->dev, "memcpy self test returned %d\n", ret);
if (ret)
- goto err_unmap_dst;
+ return ERR_PTR(ret);
}
if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) {
ret = mv_chan_xor_self_test(mv_chan);
dev_dbg(&pdev->dev, "xor self test returned %d\n", ret);
if (ret)
- goto err_unmap_dst;
+ return ERR_PTR(ret);
}
dev_info(&pdev->dev, "Marvell XOR (%s): ( %s%s%s)\n",
@@ -1176,18 +1167,9 @@ mv_xor_channel_add(struct mv_xor_device *xordev,
ret = dma_async_device_register(dma_dev);
if (ret)
- goto err_unmap_dst;
+ return ERR_PTR(ret);
return mv_chan;
-
-err_unmap_dst:
- dma_unmap_single(dma_dev->dev, mv_chan->dummy_dst_addr,
- MV_XOR_MIN_BYTE_COUNT, DMA_TO_DEVICE);
-err_unmap_src:
- dma_unmap_single(dma_dev->dev, mv_chan->dummy_src_addr,
- MV_XOR_MIN_BYTE_COUNT, DMA_FROM_DEVICE);
-
- return ERR_PTR(ret);
}
static void
diff --git a/drivers/dma/mv_xor.h b/drivers/dma/mv_xor.h
index c87cefd38a07..666c72e457d6 100644
--- a/drivers/dma/mv_xor.h
+++ b/drivers/dma/mv_xor.h
@@ -120,8 +120,8 @@ struct mv_xor_chan {
int slots_allocated;
struct tasklet_struct irq_tasklet;
int op_in_desc;
- char dummy_src[MV_XOR_MIN_BYTE_COUNT];
- char dummy_dst[MV_XOR_MIN_BYTE_COUNT];
+ void *dummy_src;
+ void *dummy_dst;
dma_addr_t dummy_src_addr, dummy_dst_addr;
u32 saved_config_reg, saved_int_mask_reg;
--
2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCHv2 9/9] dmaengine: mv_xor: add missing platform remove function
2026-06-11 21:07 [PATCHv2 0/9] dma: mv_xor: convert to devm resource management Rosen Penev
` (7 preceding siblings ...)
2026-06-11 21:07 ` [PATCHv2 8/9] dmaengine: mv_xor: allocate dummy buffers with dmam_alloc_coherent Rosen Penev
@ 2026-06-11 21:07 ` Rosen Penev
8 siblings, 0 replies; 10+ messages in thread
From: Rosen Penev @ 2026-06-11 21:07 UTC (permalink / raw)
To: dmaengine
Cc: Vinod Koul, Frank Li, Thomas Petazzoni, Gregory CLEMENT,
Marcin Wojtas, Rob Herring, open list
The driver was missing a remove callback, so channels, DMA
devices, and IRQs were never cleaned up on driver unbind.
Implement mv_xor_remove to undo probe, patterned after the
existing error path.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/dma/mv_xor.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c
index 255df2dd9c71..85cb77022144 100644
--- a/drivers/dma/mv_xor.c
+++ b/drivers/dma/mv_xor.c
@@ -1446,8 +1446,19 @@ static int mv_xor_probe(struct platform_device *pdev)
return ret;
}
+static void mv_xor_remove(struct platform_device *pdev)
+{
+ struct mv_xor_device *xordev = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < MV_XOR_MAX_CHANNELS; i++)
+ if (xordev->channels[i])
+ mv_xor_channel_remove(xordev->channels[i]);
+}
+
static struct platform_driver mv_xor_driver = {
.probe = mv_xor_probe,
+ .remove = mv_xor_remove,
.suspend = mv_xor_suspend,
.resume = mv_xor_resume,
.driver = {
--
2.54.0
^ permalink raw reply [flat|nested] 10+ messages in thread