DMA Engine development
 help / color / mirror / Atom feed
* [PATCH v2 0/2] dmaengine: dw-axi-dmac: clean up DMAC enable and PM
@ 2026-05-25  7:10 tze.yee.ng
  2026-05-25  7:10 ` [PATCH v2 1/2] dmaengine: dw-axi-dmac: drop redundant DMAC enable in block start tze.yee.ng
  2026-05-25  7:10 ` [PATCH v2 2/2] dmaengine: dw-axi-dmac: fix PM for system sleep and channel alloc tze.yee.ng
  0 siblings, 2 replies; 5+ messages in thread
From: tze.yee.ng @ 2026-05-25  7:10 UTC (permalink / raw)
  To: Eugeniy Paltsev, Vinod Koul, Frank Li, dmaengine, linux-kernel
  Cc: Tze Yee Ng, Adrian Ng Ho Yin, Nazim Amirul

From: Tze Yee Ng <tze.yee.ng@altera.com>

The DesignWare AXI DMAC driver enables the controller in axi_dma_resume(),
which is invoked from the runtime PM resume path and from probe. Calling
axi_dma_enable() again at the start of every block transfer is redundant
on the normal path.

That extra call had also masked a gap in system-sleep power management:
with only runtime PM callbacks registered, a channel could remain allocated
across suspend/resume while the runtime usage count stayed non-zero and
axi_dma_runtime_resume() was not run, leaving DMAC_CFG and clocks out of
sync with software state. Removing the per-transfer enable without fixing
PM would make that scenario more visible.

This series drops the redundant enable and adds the missing system-sleep
and channel-allocation PM handling called out during review.

Patch 1 removes axi_dma_enable() from axi_chan_block_xfer_start().

Patch 2 (follow-up to review feedback from Sashiko Watanabe):

 - Add SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
   pm_runtime_force_resume) so system suspend/resume reuses the existing
   axi_dma_suspend() and axi_dma_resume() paths even when the runtime
   usage count is non-zero.

 - Replace pm_runtime_get() with pm_runtime_resume_and_get() in
   dma_chan_alloc_chan_resources(), with pm_runtime_put() on error paths,
   so clocks are enabled before a client can submit a transfer immediately
   after allocation.

Changes in v2:
- Add Patch 2 as a follow-up to review feedback from Sashiko Watanabe.
- No changes to Patch 1.

Niravkumar L Rabara (1):
  dmaengine: dw-axi-dmac: drop redundant DMAC enable in block start

Tze Yee Ng (1):
  dmaengine: dw-axi-dmac: fix PM for system sleep and channel alloc

 drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

-- 
2.43.7


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

* [PATCH v2 1/2] dmaengine: dw-axi-dmac: drop redundant DMAC enable in block start
  2026-05-25  7:10 [PATCH v2 0/2] dmaengine: dw-axi-dmac: clean up DMAC enable and PM tze.yee.ng
@ 2026-05-25  7:10 ` tze.yee.ng
  2026-05-25  7:52   ` sashiko-bot
  2026-05-25  7:10 ` [PATCH v2 2/2] dmaengine: dw-axi-dmac: fix PM for system sleep and channel alloc tze.yee.ng
  1 sibling, 1 reply; 5+ messages in thread
From: tze.yee.ng @ 2026-05-25  7:10 UTC (permalink / raw)
  To: Eugeniy Paltsev, Vinod Koul, Frank Li, dmaengine, linux-kernel
  Cc: Tze Yee Ng, Adrian Ng Ho Yin, Nazim Amirul

From: Niravkumar L Rabara <niravkumarlaxmidas.rabara@altera.com>

axi_chan_block_xfer_start() runs after the controller is already enabled,
so calling axi_dma_enable() again is unnecessary. Remove the redundant
enable call to keep the transfer start path clean and avoid repeated no-op
programming.

Signed-off-by: Niravkumar L Rabara <niravkumarlaxmidas.rabara@altera.com>
Signed-off-by: Tze Yee Ng <tze.yee.ng@altera.com>
---
 drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 2 --
 1 file changed, 2 deletions(-)

diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
index 4d53f077e9d2..f7a50f470461 100644
--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
@@ -437,8 +437,6 @@ static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
 		return;
 	}
 
-	axi_dma_enable(chan->chip);
-
 	config.dst_multblk_type = DWAXIDMAC_MBLK_TYPE_LL;
 	config.src_multblk_type = DWAXIDMAC_MBLK_TYPE_LL;
 	config.tt_fc = DWAXIDMAC_TT_FC_MEM_TO_MEM_DMAC;
-- 
2.43.7


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

* [PATCH v2 2/2] dmaengine: dw-axi-dmac: fix PM for system sleep and channel alloc
  2026-05-25  7:10 [PATCH v2 0/2] dmaengine: dw-axi-dmac: clean up DMAC enable and PM tze.yee.ng
  2026-05-25  7:10 ` [PATCH v2 1/2] dmaengine: dw-axi-dmac: drop redundant DMAC enable in block start tze.yee.ng
@ 2026-05-25  7:10 ` tze.yee.ng
  2026-05-25  8:21   ` sashiko-bot
  1 sibling, 1 reply; 5+ messages in thread
From: tze.yee.ng @ 2026-05-25  7:10 UTC (permalink / raw)
  To: Eugeniy Paltsev, Vinod Koul, Frank Li, dmaengine, linux-kernel
  Cc: Tze Yee Ng, Adrian Ng Ho Yin, Nazim Amirul

From: Tze Yee Ng <tze.yee.ng@altera.com>

The driver only had runtime PM callbacks. If a channel stayed allocated
across system suspend/resume, the runtime usage count could remain
non-zero while hardware state (DMAC_CFG, clocks) was lost, and
axi_dma_runtime_resume() would not run to restore it.

Add system-sleep PM ops that use pm_runtime_force_suspend() and
pm_runtime_force_resume() so suspend/resume reuses the existing
axi_dma_suspend() and axi_dma_resume() paths.

Replace pm_runtime_get() with pm_runtime_resume_and_get() in
dma_chan_alloc_chan_resources() so clocks are enabled before a client
can immediately submit a transfer and touch MMIO.

Signed-off-by: Tze Yee Ng <tze.yee.ng@altera.com>
---
 drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
index f7a50f470461..bcefaff03b5c 100644
--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
@@ -516,11 +516,17 @@ static void dw_axi_dma_synchronize(struct dma_chan *dchan)
 static int dma_chan_alloc_chan_resources(struct dma_chan *dchan)
 {
 	struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
+	int ret;
+
+	ret = pm_runtime_resume_and_get(chan->chip->dev);
+	if (ret < 0)
+		return ret;
 
 	/* ASSERT: channel is idle */
 	if (axi_chan_is_hw_enable(chan)) {
 		dev_err(chan2dev(chan), "%s is non-idle!\n",
 			axi_chan_name(chan));
+		pm_runtime_put(chan->chip->dev);
 		return -EBUSY;
 	}
 
@@ -531,12 +537,11 @@ static int dma_chan_alloc_chan_resources(struct dma_chan *dchan)
 					  64, 0);
 	if (!chan->desc_pool) {
 		dev_err(chan2dev(chan), "No memory for descriptors\n");
+		pm_runtime_put(chan->chip->dev);
 		return -ENOMEM;
 	}
 	dev_vdbg(dchan2dev(dchan), "%s: allocating\n", axi_chan_name(chan));
 
-	pm_runtime_get(chan->chip->dev);
-
 	return 0;
 }
 
@@ -1663,6 +1668,8 @@ static void dw_remove(struct platform_device *pdev)
 }
 
 static const struct dev_pm_ops dw_axi_dma_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				pm_runtime_force_resume)
 	SET_RUNTIME_PM_OPS(axi_dma_runtime_suspend, axi_dma_runtime_resume, NULL)
 };
 
-- 
2.43.7


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

* Re: [PATCH v2 1/2] dmaengine: dw-axi-dmac: drop redundant DMAC enable in block start
  2026-05-25  7:10 ` [PATCH v2 1/2] dmaengine: dw-axi-dmac: drop redundant DMAC enable in block start tze.yee.ng
@ 2026-05-25  7:52   ` sashiko-bot
  0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-05-25  7:52 UTC (permalink / raw)
  To: tze.yee.ng; +Cc: vkoul, Frank.Li, dmaengine

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Concurrent transfers can race on global APB registers due to lack of a global lock in `dw_axi_dma_set_byte_halfword`.
--

commit b6ec2512e872c5f388866aa7fdbd27c4bf168a25
Author: Niravkumar L Rabara <niravkumarlaxmidas.rabara@altera.com>

dmaengine: dw-axi-dmac: drop redundant DMAC enable in block start

This commit removes an unnecessary call to axi_dma_enable() in
axi_chan_block_xfer_start(), as the controller is already enabled.
This avoids repeated no-op programming and keeps the transfer start
path clean.

> diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
> index 4d53f077e9d25..f7a50f470461c 100644
> --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
> +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
> @@ -437,8 +437,6 @@ static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
>  		return;
>  	}
>  
> -	axi_dma_enable(chan->chip);
> -
>  	config.dst_multblk_type = DWAXIDMAC_MBLK_TYPE_LL;
>  	config.src_multblk_type = DWAXIDMAC_MBLK_TYPE_LL;
>  	config.tt_fc = DWAXIDMAC_TT_FC_MEM_TO_MEM_DMAC;

[Severity: High]
This is a pre-existing issue, but can concurrent transfers race on the
global APB registers during this setup phase?

When this function executes for the DMA_MEM_TO_DEV direction, it calls
dw_axi_dma_set_byte_halfword(). That function performs read-modify-write
operations on the global DMAC_APB_BYTE_WR_CH_EN or 
DMAC_APB_HALFWORD_WR_CH_EN registers, which contain configuration bits
for all channels.

Since the locking in these paths typically relies on the per-channel
chan->vc.lock, concurrent calls on different channels could interleave.
Would this lead to one channel's configuration bit being silently
overwritten and lost, potentially resulting in incorrectly configured
transfers or memory corruption?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1779688569.git.tze.yee.ng@altera.com?part=1

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

* Re: [PATCH v2 2/2] dmaengine: dw-axi-dmac: fix PM for system sleep and channel alloc
  2026-05-25  7:10 ` [PATCH v2 2/2] dmaengine: dw-axi-dmac: fix PM for system sleep and channel alloc tze.yee.ng
@ 2026-05-25  8:21   ` sashiko-bot
  0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-05-25  8:21 UTC (permalink / raw)
  To: tze.yee.ng; +Cc: dmaengine, Frank.Li, vkoul

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:

Pre-existing issues:
- [High] Hardware is forcefully powered down in `dw_remove()` while the devm-managed DMA engine is still registered and actively exposing channels to clients.
- [High] `dw_remove()` leaks clock references if the device is in an active Runtime PM state.
- [High] `axi_dma_resume()` leaks the `cfgr_clk` reference if `core_clk` fails to enable.
- [High] The runtime suspend callback `axi_dma_suspend()` does not call `synchronize_irq()` before disabling hardware clocks.
- [High] The shared interrupt handler `dw_axi_dma_interrupt()` accesses hardware registers without ensuring the device is runtime active.
- [High] An asynchronous `pm_runtime_put()` is used before jumping to an error handling path that immediately calls `pm_runtime_disable()`, causing a race condition in `dw_probe()`.
- [High] The function `axi_req_irqs()` writes to the fixed-size `chip->irq` array without validating that `irq_count` does not exceed the array bounds.
- [Low] The `dw_axi_dma_pm_ops` structure is assigned unconditionally to the driver's `.pm` pointer without using the `pm_ptr()` wrapper macro.
--

commit 80df1f731d96db5786b01419f199171e1d6714a5
Author: Tze Yee Ng <tze.yee.ng@altera.com>

dmaengine: dw-axi-dmac: fix PM for system sleep and channel alloc

The driver only had runtime PM callbacks. If a channel stayed allocated
across system suspend/resume, the runtime usage count could remain
non-zero while hardware state (DMAC_CFG, clocks) was lost, and
axi_dma_runtime_resume() would not run to restore it. This commit adds
system-sleep PM ops to reuse suspend/resume paths and replaces
pm_runtime_get() with pm_runtime_resume_and_get().

> diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
> index f7a50f470461c..bcefaff03b5c6 100644
> --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
> +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c

[ ... ]

> @@ -1663,6 +1668,8 @@ static void dw_remove(struct platform_device *pdev)
>  }

[Severity: High]
This isn't a bug introduced by this patch, but does dw_remove() forcefully
power down hardware while the devm-managed DMA engine is still registered and
actively exposing channels to clients?

drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c:dw_remove() {
	...
	axi_dma_disable(chip);
	pm_runtime_disable(chip->dev);
	axi_dma_suspend(chip);
	...
}

The driver uses dmaenginem_async_device_register() to register the DMA engine,
relying on devres for unregistration, which executes strictly after the
remove() function returns.

During the window between this hardware shutdown and the delayed devres
unregistration, the DMA engine is still fully registered.  Could a client
that allocates a channel or submits a transfer during this time access
powered-off hardware, causing faults or synchronous aborts?


[Severity: High]
This is a pre-existing issue, but does dw_remove() leak clock references if
the device is in an active Runtime PM state?

drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c:dw_remove() {
	...
	clk_prepare_enable(chip->cfgr_clk);
	clk_prepare_enable(chip->core_clk);
	...
	pm_runtime_disable(chip->dev);
	axi_dma_suspend(chip);
	...
}

If the device is runtime-active when dw_remove() is invoked, its clocks are
already enabled.  dw_remove() unconditionally calls clk_prepare_enable() for
both clocks, then disables Runtime PM via pm_runtime_disable(), and finally
calls axi_dma_suspend() to disable the clocks.

Because pm_runtime_disable() prevents any further PM transitions, could the
runtime suspend callback be prevented from running and dropping the remaining
usage count, resulting in a permanent clock leak on unbind?

>  
>  static const struct dev_pm_ops dw_axi_dma_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> +				pm_runtime_force_resume)
>  	SET_RUNTIME_PM_OPS(axi_dma_runtime_suspend, axi_dma_runtime_resume, NULL)
>  };

[Severity: Low]
This is a pre-existing issue, but is the dw_axi_dma_pm_ops structure assigned
unconditionally to the driver's .pm pointer without using the pm_ptr()
wrapper macro?

drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c:
static struct platform_driver dw_driver = {
	...
	.driver = {
		...
		.pm = &dw_axi_dma_pm_ops,
	},
};

Could this produce dead code or compiler warnings when PM support is disabled?


[Severity: High]
This isn't a bug introduced by this patch, but does axi_dma_resume() leak
the cfgr_clk reference if core_clk fails to enable?

drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c:axi_dma_resume() {
	ret = clk_prepare_enable(chip->cfgr_clk);
	if (ret < 0)
		return ret;

	ret = clk_prepare_enable(chip->core_clk);
	if (ret < 0)
		return ret;
	...
}

If clk_prepare_enable(chip->core_clk) fails, the function immediately returns
the error code.  Should it call clk_disable_unprepare(chip->cfgr_clk) to
clean up the successfully enabled clock?


[Severity: High]
This is a pre-existing issue, but does the runtime suspend callback
axi_dma_suspend() fail to call synchronize_irq() before disabling hardware
clocks?

drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c:axi_dma_suspend() {
	axi_dma_irq_disable(chip);
	axi_dma_disable(chip);

	clk_disable_unprepare(chip->core_clk);
	clk_disable_unprepare(chip->cfgr_clk);
	...
}

The driver requests its interrupt with the IRQF_SHARED flag.  Could failing to
call synchronize_irq() prior to powering down hardware introduce a race
condition where the interrupt handler might execute mid-flight on another CPU,
leading to invalid register accesses and bus faults?


[Severity: High]
This isn't a bug introduced by this patch, but does the shared interrupt
handler dw_axi_dma_interrupt() access hardware registers without ensuring
the device is runtime active?

drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c:dw_axi_dma_interrupt() {
	...
	axi_dma_irq_disable(chip);
	...
}

Because the driver uses IRQF_SHARED, its interrupt handler can be triggered
when another device on the same shared line asserts an interrupt.

Should the handler check pm_runtime_get_if_active() before accessing MMIO
registers to avoid a precise external abort or system crash if the DMA
controller is runtime-suspended and its clocks are disabled?


[Severity: High]
This is a pre-existing issue, but is an asynchronous pm_runtime_put() used
before jumping to an error handling path that immediately calls
pm_runtime_disable(), causing a race condition in dw_probe()?

drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c:dw_probe() {
	...
	pm_runtime_put(chip->dev);

	ret = dmaenginem_async_device_register(&dw->dma);
	if (ret)
		goto err_pm_disable;
	...
err_pm_disable:
	pm_runtime_disable(chip->dev);
	return ret;
}

If dmaenginem_async_device_register() fails, pm_runtime_disable() cancels
the pending async idle/suspend work.  Does this permanently leave the hardware
in an incorrect active power state and leak the clock resources?

Should a synchronous pm_runtime_put_sync() be used instead?


[Severity: High]
This isn't a bug introduced by this patch, but does the function
axi_req_irqs() write to the fixed-size chip->irq array without validating
that irq_count does not exceed the array bounds?

drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c:axi_req_irqs() {
	int irq_count = platform_irq_count(pdev);
	int ret;

	for (int i = 0; i < irq_count; i++) {
		chip->irq[i] = platform_get_irq(pdev, i);
		...
	}
	...
}

The chip->irq array is statically allocated with a size of DMAC_MAX_CHANNELS
(32).  If a misconfigured device tree specifies more than 32 interrupts, could
this loop write beyond the array boundaries, corrupting adjacent members in
struct axi_dma_chip and leading to arbitrary memory corruption?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1779688569.git.tze.yee.ng@altera.com?part=2

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

end of thread, other threads:[~2026-05-25  8:21 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-25  7:10 [PATCH v2 0/2] dmaengine: dw-axi-dmac: clean up DMAC enable and PM tze.yee.ng
2026-05-25  7:10 ` [PATCH v2 1/2] dmaengine: dw-axi-dmac: drop redundant DMAC enable in block start tze.yee.ng
2026-05-25  7:52   ` sashiko-bot
2026-05-25  7:10 ` [PATCH v2 2/2] dmaengine: dw-axi-dmac: fix PM for system sleep and channel alloc tze.yee.ng
2026-05-25  8:21   ` sashiko-bot

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