* [PATCH v2 0/3] dmaengine: dw-axi-dmac: fix termination and pause acknowledgment
@ 2026-11-10 3:05 James Hilliard
2026-11-10 3:06 ` [PATCH v2 1/3] dmaengine: dw-axi-dmac: poll the correct enable bit during termination James Hilliard
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: James Hilliard @ 2026-11-10 3:05 UTC (permalink / raw)
To: Eugeniy Paltsev, Vinod Koul, Frank Li, Sergey Khimich,
Andy Shevchenko, Sia Jee Heng
Cc: dmaengine, linux-kernel, James Hilliard, stable
Use the layout-aware channel enable check during termination and retain
transfer resources when the channel does not stop. Preserve suspension
status for the pause and resume paths, including acknowledgments that
arrive after a polling timeout or during another channel's interrupt.
Keep a pending pause across cyclic segment completion and resume the
segment only after the client releases the pause. Track the active
descriptor until completion so queued work cannot reissue it while its
interrupt is pending.
Assisted-by: Codex:gpt-6-astra
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
Changes in v2:
- Use readx_poll_timeout_atomic() for the single-argument status helpers
and express the termination polling interval and timeout in milliseconds,
following Andy's review.
- Track pending suspend requests separately from confirmed suspension so
resume can handle acknowledgments received after a pause timeout.
- Use the atomic polling helper, including its final status check, and
report -EAGAIN if resume cannot yet cancel a pending suspend request.
- Defer cyclic restart while paused and track the active descriptor until
completion, preventing resume or issue_pending from reissuing it.
- Reset pause state on successful termination and channel allocation;
clear stale status when starting a new descriptor.
- Link to v1: https://patch.msgid.link/20261108-submit-dw-axi-dma-stop-pause-v1-v1-0-c48ec19e9ee4@gmail.com
To: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
To: Vinod Koul <vkoul@kernel.org>
To: Frank Li <Frank.Li@kernel.org>
To: Sergey Khimich <serghox@gmail.com>
To: Sia Jee Heng <jee.heng.sia@intel.com>
To: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Cc: dmaengine@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
James Hilliard (3):
dmaengine: dw-axi-dmac: poll the correct enable bit during termination
dmaengine: dw-axi-dmac: retain descriptors when termination times out
dmaengine: dw-axi-dmac: preserve pending pause requests
drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 118 ++++++++++++++++++++-----
drivers/dma/dw-axi-dmac/dw-axi-dmac.h | 2 +
2 files changed, 96 insertions(+), 24 deletions(-)
---
base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
change-id: 20261108-submit-dw-axi-dma-stop-pause-v1-c93f1e767169
Best regards,
--
James Hilliard <james.hilliard1@gmail.com>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 1/3] dmaengine: dw-axi-dmac: poll the correct enable bit during termination
2026-11-10 3:05 [PATCH v2 0/3] dmaengine: dw-axi-dmac: fix termination and pause acknowledgment James Hilliard
@ 2026-11-10 3:06 ` James Hilliard
2026-11-10 7:38 ` Andy Shevchenko
2026-11-10 3:06 ` [PATCH v2 2/3] dmaengine: dw-axi-dmac: retain descriptors when termination times out James Hilliard
2026-11-10 3:06 ` [PATCH v2 3/3] dmaengine: dw-axi-dmac: preserve pending pause requests James Hilliard
2 siblings, 1 reply; 7+ messages in thread
From: James Hilliard @ 2026-11-10 3:06 UTC (permalink / raw)
To: Eugeniy Paltsev, Vinod Koul, Frank Li, Sergey Khimich,
Andy Shevchenko, Sia Jee Heng
Cc: dmaengine, linux-kernel, James Hilliard, stable
For channels 16 through 31, the channel enable bits occupy bits 32 through
47 of the extended DMAC_CHEN register. dma_chan_terminate_all() only reads
the low word and tests BIT(chan->id), so it can conclude that an upper-bank
channel has stopped while its actual enable bit remains set. Descriptors
can then be freed while hardware still owns them.
Poll axi_chan_is_hw_enable(), which already handles both register banks
and the smaller channel layouts, instead of constructing another mask.
Fixes: 495e18b16e3d ("dmaengine: dw-axi-dmac: Add support DMAX_NUM_CHANNELS > 16")
Cc: stable@vger.kernel.org
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 8 ++++----
1 file changed, 4 insertions(+), 4 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 7520c78f55cc..457ab9c47ceb 100644
--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
@@ -28,6 +28,7 @@
#include <linux/property.h>
#include <linux/reset.h>
#include <linux/slab.h>
+#include <linux/time64.h>
#include <linux/types.h>
#include "dw-axi-dmac.h"
@@ -1181,16 +1182,15 @@ static irqreturn_t dw_axi_dma_interrupt(int irq, void *dev_id)
static int dma_chan_terminate_all(struct dma_chan *dchan)
{
struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
- u32 chan_active = BIT(chan->id) << DMAC_CHAN_EN_SHIFT;
unsigned long flags;
- u32 val;
+ bool enabled;
int ret;
LIST_HEAD(head);
axi_chan_disable(chan);
- ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val,
- !(val & chan_active), 1000, 50000);
+ ret = readx_poll_timeout_atomic(axi_chan_is_hw_enable, chan, enabled, !enabled,
+ 1 * USEC_PER_MSEC, 50 * USEC_PER_MSEC);
if (ret == -ETIMEDOUT)
dev_warn(dmaengine_chan_dev(dchan),
"%s failed to stop\n", axi_chan_name(chan));
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 2/3] dmaengine: dw-axi-dmac: retain descriptors when termination times out
2026-11-10 3:05 [PATCH v2 0/3] dmaengine: dw-axi-dmac: fix termination and pause acknowledgment James Hilliard
2026-11-10 3:06 ` [PATCH v2 1/3] dmaengine: dw-axi-dmac: poll the correct enable bit during termination James Hilliard
@ 2026-11-10 3:06 ` James Hilliard
2026-11-10 7:40 ` Andy Shevchenko
2026-11-10 3:06 ` [PATCH v2 3/3] dmaengine: dw-axi-dmac: preserve pending pause requests James Hilliard
2 siblings, 1 reply; 7+ messages in thread
From: James Hilliard @ 2026-11-10 3:06 UTC (permalink / raw)
To: Eugeniy Paltsev, Vinod Koul, Frank Li, Sergey Khimich,
Andy Shevchenko, Sia Jee Heng
Cc: dmaengine, linux-kernel, James Hilliard, stable
dma_chan_terminate_all() warns when the channel enable bit does not clear,
but then frees the descriptors and returns success. Its synchronize
callback only drains virtual-channel callbacks, so terminate_sync() can
also report success while hardware still accesses the transfer.
Return the polling error before detaching descriptors or changing the
peripheral routing. This keeps the transfer resources available while
the channel remains enabled and lets the caller retry termination without
treating the timeout as a successful stop.
Hold the channel lock across disabling, polling and descriptor detachment
so an interrupt handler cannot restart the channel between the stop check
and cleanup.
Fixes: 1deb96c0fa58 ("dmaegine: dw-axi-dmac: Support device_prep_dma_cyclic()")
Cc: stable@vger.kernel.org
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 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 457ab9c47ceb..281d7c79246e 100644
--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
@@ -1187,21 +1187,24 @@ static int dma_chan_terminate_all(struct dma_chan *dchan)
int ret;
LIST_HEAD(head);
+ spin_lock_irqsave(&chan->vc.lock, flags);
+
axi_chan_disable(chan);
ret = readx_poll_timeout_atomic(axi_chan_is_hw_enable, chan, enabled, !enabled,
1 * USEC_PER_MSEC, 50 * USEC_PER_MSEC);
- if (ret == -ETIMEDOUT)
+ if (ret) {
+ spin_unlock_irqrestore(&chan->vc.lock, flags);
dev_warn(dmaengine_chan_dev(dchan),
"%s failed to stop\n", axi_chan_name(chan));
+ return ret;
+ }
if (chan->direction != DMA_MEM_TO_MEM)
dw_axi_dma_set_hw_channel(chan, false);
if (chan->direction == DMA_MEM_TO_DEV)
dw_axi_dma_set_byte_halfword(chan, false);
- spin_lock_irqsave(&chan->vc.lock, flags);
-
vchan_get_all_descriptors(&chan->vc, &head);
chan->cyclic = false;
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v2 3/3] dmaengine: dw-axi-dmac: preserve pending pause requests
2026-11-10 3:05 [PATCH v2 0/3] dmaengine: dw-axi-dmac: fix termination and pause acknowledgment James Hilliard
2026-11-10 3:06 ` [PATCH v2 1/3] dmaengine: dw-axi-dmac: poll the correct enable bit during termination James Hilliard
2026-11-10 3:06 ` [PATCH v2 2/3] dmaengine: dw-axi-dmac: retain descriptors when termination times out James Hilliard
@ 2026-11-10 3:06 ` James Hilliard
2026-11-10 7:46 ` Andy Shevchenko
2 siblings, 1 reply; 7+ messages in thread
From: James Hilliard @ 2026-11-10 3:06 UTC (permalink / raw)
To: Eugeniy Paltsev, Vinod Koul, Frank Li, Sergey Khimich,
Andy Shevchenko, Sia Jee Heng
Cc: dmaengine, linux-kernel, James Hilliard, stable
dma_chan_pause() clears SUSPENDED even when polling times out. Hardware
can set that status after the last poll, including during the final
delay, and the unconditional clear then discards an acknowledgment the
caller never observed. Another channel's interrupt can also consume the
status because the shared IRQ handler clears every channel's status
before taking its lock.
Leave SUSPENDED to the pause and resume paths. Track the suspend request
separately from its acknowledgment so a timeout can be followed by either
a pause retry or a resume. Wait for acknowledgment before clearing the
hardware request, since hardware ignores an earlier clear. If that wait
also times out, return -EAGAIN with the request still pending. Use the
atomic polling helper so an acknowledgment during the final delay is
observed.
A completed transfer is also quiescent, but its completion handler may
still need to advance a cyclic descriptor. Defer that restart while a
pause is pending, and resume it without reprogramming the first LLI.
Track the active descriptor until its completion handler runs so that
resume and issue_pending cannot restart a completed cyclic segment or
reissue a completed noncyclic descriptor while its IRQ is pending.
Start queued work after the active descriptor is retired, respecting the
pause request.
Clear stale suspension status at the start of a new descriptor rather
than at each cyclic restart. Reset the software state after successful
termination and when allocating a channel for a new client.
Fixes: 1fe20f1b8454 ("dmaengine: Introduce DW AXI DMAC driver")
Cc: stable@vger.kernel.org
Signed-off-by: James Hilliard <james.hilliard1@gmail.com>
---
drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c | 101 ++++++++++++++++++++-----
drivers/dma/dw-axi-dmac/dw-axi-dmac.h | 2 +
2 files changed, 86 insertions(+), 17 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 281d7c79246e..5c1eb120a28b 100644
--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c
@@ -481,6 +481,8 @@ static void axi_chan_block_xfer_start(struct axi_dma_chan *chan,
irq_mask |= DWAXIDMAC_IRQ_SUSPENDED;
axi_chan_irq_set(chan, irq_mask);
+ axi_chan_irq_clear(chan, DWAXIDMAC_IRQ_SUSPENDED);
+ chan->desc = first;
axi_chan_enable(chan);
}
@@ -489,6 +491,9 @@ static void axi_chan_start_first_queued(struct axi_dma_chan *chan)
struct axi_dma_desc *desc;
struct virt_dma_desc *vd;
+ if (chan->desc || chan->pause_requested)
+ return;
+
vd = vchan_next_desc(&chan->vc);
if (!vd)
return;
@@ -543,6 +548,10 @@ static int dma_chan_alloc_chan_resources(struct dma_chan *dchan)
pm_runtime_put(chan->chip->dev);
return -ENOMEM;
}
+ chan->desc = NULL;
+ chan->pause_requested = false;
+ chan->is_paused = false;
+ chan->cyclic_restart = false;
dev_vdbg(dmaengine_chan_dev(dchan), "%s: allocating\n", axi_chan_name(chan));
return 0;
@@ -1087,6 +1096,8 @@ static noinline void axi_chan_handle_err(struct axi_dma_chan *chan, u32 status)
vchan_cookie_complete(vd);
/* Try to restart the controller */
+ chan->desc = NULL;
+ chan->cyclic_restart = false;
axi_chan_start_first_queued(chan);
out:
@@ -1135,12 +1146,19 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan)
}
}
- axi_chan_enable(chan);
+ if (chan->pause_requested) {
+ chan->is_paused = true;
+ chan->cyclic_restart = true;
+ } else {
+ axi_chan_enable(chan);
+ }
}
} else {
/* Remove the completed descriptor from issued list before completing */
list_del(&vd->node);
vchan_cookie_complete(vd);
+ chan->desc = NULL;
+ axi_chan_start_first_queued(chan);
}
out:
@@ -1162,6 +1180,8 @@ static irqreturn_t dw_axi_dma_interrupt(int irq, void *dev_id)
for (i = 0; i < dw->hdata->nr_channels; i++) {
chan = &dw->chan[i];
status = axi_chan_irq_read(chan);
+ /* Leave suspension status for the pause and resume paths. */
+ status &= ~DWAXIDMAC_IRQ_SUSPENDED;
axi_chan_irq_clear(chan, status);
dev_vdbg(chip->dev, "%s %u IRQ status: 0x%08x\n",
@@ -1207,6 +1227,11 @@ static int dma_chan_terminate_all(struct dma_chan *dchan)
vchan_get_all_descriptors(&chan->vc, &head);
+ axi_chan_irq_clear(chan, DWAXIDMAC_IRQ_SUSPENDED);
+ chan->desc = NULL;
+ chan->pause_requested = false;
+ chan->is_paused = false;
+ chan->cyclic_restart = false;
chan->cyclic = false;
spin_unlock_irqrestore(&chan->vc.lock, flags);
@@ -1217,15 +1242,49 @@ static int dma_chan_terminate_all(struct dma_chan *dchan)
return 0;
}
+static bool axi_chan_is_suspended(struct axi_dma_chan *chan)
+{
+ if (!axi_chan_is_hw_enable(chan)) {
+ /* A completed transfer cannot acknowledge a new suspend request. */
+ axi_chan_disable(chan);
+ return true;
+ }
+
+ return axi_chan_irq_read(chan) & DWAXIDMAC_IRQ_SUSPENDED;
+}
+
+/* Called with vc.lock held. A timed-out request can still be acknowledged. */
+static int axi_chan_wait_for_suspend(struct axi_dma_chan *chan)
+{
+ bool suspended;
+ int ret;
+
+ if (chan->is_paused)
+ return 0;
+
+ ret = readx_poll_timeout_atomic(axi_chan_is_suspended, chan, suspended, suspended,
+ 2, 40);
+ if (ret)
+ return -EAGAIN;
+
+ axi_chan_irq_clear(chan, DWAXIDMAC_IRQ_SUSPENDED);
+ chan->is_paused = true;
+ return 0;
+}
+
static int dma_chan_pause(struct dma_chan *dchan)
{
struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
unsigned long flags;
- unsigned int timeout = 20; /* timeout iterations */
+ int ret;
u64 val;
spin_lock_irqsave(&chan->vc.lock, flags);
+ chan->pause_requested = true;
+ if (chan->is_paused || !axi_chan_is_hw_enable(chan))
+ goto wait;
+
if (chan->chip->dw->hdata->nr_channels >= DMAC_CHAN_16) {
val = axi_dma_ioread64(chan->chip, DMAC_CHSUSPREG);
if (chan->id >= DMAC_CHAN_16) {
@@ -1252,20 +1311,11 @@ static int dma_chan_pause(struct dma_chan *dchan)
}
}
- do {
- if (axi_chan_irq_read(chan) & DWAXIDMAC_IRQ_SUSPENDED)
- break;
-
- udelay(2);
- } while (--timeout);
-
- axi_chan_irq_clear(chan, DWAXIDMAC_IRQ_SUSPENDED);
-
- chan->is_paused = true;
-
+wait:
+ ret = axi_chan_wait_for_suspend(chan);
spin_unlock_irqrestore(&chan->vc.lock, flags);
- return timeout ? 0 : -EAGAIN;
+ return ret;
}
/* Called in chan locked context */
@@ -1299,6 +1349,8 @@ static inline void axi_chan_resume(struct axi_dma_chan *chan)
}
}
+ axi_chan_irq_clear(chan, DWAXIDMAC_IRQ_SUSPENDED);
+ chan->pause_requested = false;
chan->is_paused = false;
}
@@ -1306,15 +1358,30 @@ static int dma_chan_resume(struct dma_chan *dchan)
{
struct axi_dma_chan *chan = dchan_to_axi_dma_chan(dchan);
unsigned long flags;
+ int ret = 0;
spin_lock_irqsave(&chan->vc.lock, flags);
- if (chan->is_paused)
- axi_chan_resume(chan);
+ if (!chan->pause_requested)
+ goto out;
+
+ /* Hardware ignores resume until the suspend request is acknowledged. */
+ ret = axi_chan_wait_for_suspend(chan);
+ if (ret)
+ goto out;
+
+ axi_chan_resume(chan);
+ if (chan->cyclic_restart) {
+ chan->cyclic_restart = false;
+ axi_chan_enable(chan);
+ } else {
+ axi_chan_start_first_queued(chan);
+ }
+out:
spin_unlock_irqrestore(&chan->vc.lock, flags);
- return 0;
+ return ret;
}
static int axi_dma_suspend(struct axi_dma_chip *chip)
diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
index d8dd07efe60f..c61fcf070958 100644
--- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
+++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
@@ -52,7 +52,9 @@ struct axi_dma_chan {
enum dma_transfer_direction direction;
bool cyclic;
/* these other elements are all protected by vc.lock */
+ bool pause_requested;
bool is_paused;
+ bool cyclic_restart;
};
struct dw_axi_dma {
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/3] dmaengine: dw-axi-dmac: poll the correct enable bit during termination
2026-11-10 3:06 ` [PATCH v2 1/3] dmaengine: dw-axi-dmac: poll the correct enable bit during termination James Hilliard
@ 2026-11-10 7:38 ` Andy Shevchenko
0 siblings, 0 replies; 7+ messages in thread
From: Andy Shevchenko @ 2026-11-10 7:38 UTC (permalink / raw)
To: James Hilliard
Cc: Eugeniy Paltsev, Vinod Koul, Frank Li, Sergey Khimich,
Sia Jee Heng, dmaengine, linux-kernel, stable
On Mon, Nov 09, 2026 at 08:06:00PM -0700, James Hilliard wrote:
> For channels 16 through 31, the channel enable bits occupy bits 32 through
> 47 of the extended DMAC_CHEN register. dma_chan_terminate_all() only reads
> the low word and tests BIT(chan->id), so it can conclude that an upper-bank
> channel has stopped while its actual enable bit remains set. Descriptors
> can then be freed while hardware still owns them.
>
> Poll axi_chan_is_hw_enable(), which already handles both register banks
> and the smaller channel layouts, instead of constructing another mask.
...
> - ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val,
> - !(val & chan_active), 1000, 50000);
> + ret = readx_poll_timeout_atomic(axi_chan_is_hw_enable, chan, enabled, !enabled,
> + 1 * USEC_PER_MSEC, 50 * USEC_PER_MSEC);
Indentation of the second line is broken, and the first one is too long, check
how I recommended to put in my reply to v1.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/3] dmaengine: dw-axi-dmac: retain descriptors when termination times out
2026-11-10 3:06 ` [PATCH v2 2/3] dmaengine: dw-axi-dmac: retain descriptors when termination times out James Hilliard
@ 2026-11-10 7:40 ` Andy Shevchenko
0 siblings, 0 replies; 7+ messages in thread
From: Andy Shevchenko @ 2026-11-10 7:40 UTC (permalink / raw)
To: James Hilliard
Cc: Eugeniy Paltsev, Vinod Koul, Frank Li, Sergey Khimich,
Sia Jee Heng, dmaengine, linux-kernel, stable
On Mon, Nov 09, 2026 at 08:06:01PM -0700, James Hilliard wrote:
> dma_chan_terminate_all() warns when the channel enable bit does not clear,
> but then frees the descriptors and returns success. Its synchronize
> callback only drains virtual-channel callbacks, so terminate_sync() can
> also report success while hardware still accesses the transfer.
>
> Return the polling error before detaching descriptors or changing the
> peripheral routing. This keeps the transfer resources available while
> the channel remains enabled and lets the caller retry termination without
> treating the timeout as a successful stop.
>
> Hold the channel lock across disabling, polling and descriptor detachment
> so an interrupt handler cannot restart the channel between the stop check
> and cleanup.
...
> ret = readx_poll_timeout_atomic(axi_chan_is_hw_enable, chan, enabled, !enabled,
> 1 * USEC_PER_MSEC, 50 * USEC_PER_MSEC);
> - if (ret == -ETIMEDOUT)
> + if (ret) {
Just noticed, this should be part of the previous patch.
> + spin_unlock_irqrestore(&chan->vc.lock, flags);
> dev_warn(dmaengine_chan_dev(dchan),
> "%s failed to stop\n", axi_chan_name(chan));
> + return ret;
> + }
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 3/3] dmaengine: dw-axi-dmac: preserve pending pause requests
2026-11-10 3:06 ` [PATCH v2 3/3] dmaengine: dw-axi-dmac: preserve pending pause requests James Hilliard
@ 2026-11-10 7:46 ` Andy Shevchenko
0 siblings, 0 replies; 7+ messages in thread
From: Andy Shevchenko @ 2026-11-10 7:46 UTC (permalink / raw)
To: James Hilliard
Cc: Eugeniy Paltsev, Vinod Koul, Frank Li, Sergey Khimich,
Sia Jee Heng, dmaengine, linux-kernel, stable
On Mon, Nov 09, 2026 at 08:06:02PM -0700, James Hilliard wrote:
> dma_chan_pause() clears SUSPENDED even when polling times out. Hardware
> can set that status after the last poll, including during the final
> delay, and the unconditional clear then discards an acknowledgment the
> caller never observed. Another channel's interrupt can also consume the
> status because the shared IRQ handler clears every channel's status
> before taking its lock.
>
> Leave SUSPENDED to the pause and resume paths. Track the suspend request
> separately from its acknowledgment so a timeout can be followed by either
> a pause retry or a resume. Wait for acknowledgment before clearing the
> hardware request, since hardware ignores an earlier clear. If that wait
> also times out, return -EAGAIN with the request still pending. Use the
> atomic polling helper so an acknowledgment during the final delay is
> observed.
>
> A completed transfer is also quiescent, but its completion handler may
> still need to advance a cyclic descriptor. Defer that restart while a
> pause is pending, and resume it without reprogramming the first LLI.
> Track the active descriptor until its completion handler runs so that
> resume and issue_pending cannot restart a completed cyclic segment or
> reissue a completed noncyclic descriptor while its IRQ is pending.
> Start queued work after the active descriptor is retired, respecting the
> pause request.
>
> Clear stale suspension status at the start of a new descriptor rather
> than at each cyclic restart. Reset the software state after successful
> termination and when allocating a channel for a new client.
...
> +/* Called with vc.lock held. A timed-out request can still be acknowledged. */
We have context checkers, please use __must_hold() or alike annotations.
> +static int axi_chan_wait_for_suspend(struct axi_dma_chan *chan)
> +{
> + bool suspended;
> + int ret;
> +
> + if (chan->is_paused)
> + return 0;
> +
> + ret = readx_poll_timeout_atomic(axi_chan_is_suspended, chan, suspended, suspended,
> + 2, 40);
Again, fix indentation, reformat and split logically.
ret = readx_poll_timeout_atomic(axi_chan_is_suspended, chan,
suspended, suspended, 2, 40);
(note no leading spaces in the second line, only tabs fit).
> + if (ret)
> + return -EAGAIN;
> +
> + axi_chan_irq_clear(chan, DWAXIDMAC_IRQ_SUSPENDED);
> + chan->is_paused = true;
> + return 0;
> +}
...
> - do {
> - if (axi_chan_irq_read(chan) & DWAXIDMAC_IRQ_SUSPENDED)
> - break;
> -
> - udelay(2);
> - } while (--timeout);
> -
> - axi_chan_irq_clear(chan, DWAXIDMAC_IRQ_SUSPENDED);
> -
> - chan->is_paused = true;
> -
> +wait:
> + ret = axi_chan_wait_for_suspend(chan);
You may split the conversion to readx_poll_timeout() into a separate patch.
...
> --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
> +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac.h
> @@ -52,7 +52,9 @@ struct axi_dma_chan {
> enum dma_transfer_direction direction;
> bool cyclic;
> /* these other elements are all protected by vc.lock */
> + bool pause_requested;
> bool is_paused;
> + bool cyclic_restart;
Probably making them bitfields will be a good idea to save some memory:
bool pause_requested:1;
bool is_paused:1;
bool cyclic_restart:1;
> };
Just run `pahole` before and after (won't probably show you the gain,
but before might show better position for "cyclic" member, which can
be amended later).
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-11-10 7:46 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-10 3:05 [PATCH v2 0/3] dmaengine: dw-axi-dmac: fix termination and pause acknowledgment James Hilliard
2026-11-10 3:06 ` [PATCH v2 1/3] dmaengine: dw-axi-dmac: poll the correct enable bit during termination James Hilliard
2026-11-10 7:38 ` Andy Shevchenko
2026-11-10 3:06 ` [PATCH v2 2/3] dmaengine: dw-axi-dmac: retain descriptors when termination times out James Hilliard
2026-11-10 7:40 ` Andy Shevchenko
2026-11-10 3:06 ` [PATCH v2 3/3] dmaengine: dw-axi-dmac: preserve pending pause requests James Hilliard
2026-11-10 7:46 ` Andy Shevchenko
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®