mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCHv2 0/9] dma: mv_xor: convert to devm resource management
@ 2026-06-11 21:07 Rosen Penev
  2026-06-11 21:07 ` [PATCHv2 1/9] dmaengine: mv_xor: initialize chan state before requesting IRQ Rosen Penev
                   ` (8 more replies)
  0 siblings, 9 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

Convert the mv_xor driver to use managed device resources (devm)
to simplify error handling and removal paths.

Patch 5 replaces the open-coded clock acquire/enable/disable/put
with devm_clk_get_optional_enabled, eliminating manual clock
cleanup in the probe error path.

Patch 6 adds the missing platform remove callback so that
channels, DMA devices, and IRQs are properly cleaned up on
driver unbind.

Patch 7 converts DMA pool allocation and IRQ requests to their
devm counterparts, allowing removal of the err_free_irq and
err_free_dma error labels.

v2: add more sashiko fixes.

Rosen Penev (9):
  dmaengine: mv_xor: initialize chan state before requesting IRQ
  dmaengine: mv_xor: fix use-after-free in probe error path
  dmaengine: mv_xor: bound maximum channels for Armada 37xx
  dmaengine: mv_xor: abort channel before freeing resources on timeout
  dmaengine: mv_xor: use devm_clk_get_optional_enabled
  dmaengine: mv_xor: switch to of_irq_get()
  dmaengine: mv_xor: use devm for dma pool and irq
  dmaengine: mv_xor: allocate dummy buffers with dmam_alloc_coherent
  dmaengine: mv_xor: add missing platform remove function

 drivers/dma/mv_xor.c | 161 +++++++++++++++++++++----------------------
 drivers/dma/mv_xor.h |   4 +-
 2 files changed, 81 insertions(+), 84 deletions(-)

--
2.54.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [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

end of thread, other threads:[~2026-06-11 21:07 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [PATCHv2 3/9] dmaengine: mv_xor: bound maximum channels for Armada 37xx Rosen Penev
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 ` [PATCHv2 5/9] dmaengine: mv_xor: use devm_clk_get_optional_enabled Rosen Penev
2026-06-11 21:07 ` [PATCHv2 6/9] dmaengine: mv_xor: switch to of_irq_get() Rosen Penev
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 ` [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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®