Devicetree
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Yuanshen Cao" <alex.caoys@gmail.com>
Cc: dmaengine@vger.kernel.org, linux-sunxi@lists.linux.dev,
	conor+dt@kernel.org, devicetree@vger.kernel.org,
	Frank.Li@kernel.org, robh@kernel.org, vkoul@kernel.org
Subject: Re: [PATCH v4 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling
Date: Wed, 26 Aug 2026 01:45:22 +0000	[thread overview]
Message-ID: <20260826014522.8CAB71F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260826-sun60i-a733-dma-v4-1-dc2f745f40dc@gmail.com>

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

Pre-existing issues:
- [Critical] Preexisting logic errors in `sun6i_dma_interrupt` regarding outer loop integer division truncation and inner loop physical channel indexing lead to dropped interrupts, unhandled interrupt storms, and use-after-free conditions for upper channels (>= 8).
- [High] Deadlock risk due to acquiring `sdev->lock` without disabling softirqs in process context.
- [High] Lockless read of `vchan->phy` causes TOCTOU race that can pause or stop unrelated DMA channels.
- [High] Data race on IRQ_EN register during driver remove can cause an unhandled IRQ storm.
--

commit c54e18d9ab8b9baf10207d467edd0a2daf494d43
Author: Yuanshen Cao <alex.caoys@gmail.com>

    dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling
    
    The `sun6i-dma` driver currently uses hardcoded logic for interrupt
    handling (reading/writing interrupt enable and status registers) and
    register dumping.
    
    To support the Allwinner A733, which has a different register layout and
    interrupt handling logic, add `irq_stride`, `irq_en_offset`, and
    `irq_stat_offset` to `struct sun6i_dma_config`. Implement generic
    `sun6i_read/write_irq_*` functions using these new configuration fields
    to accommodate different hardware revisions.

> diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
> index f47a326dd7ffa..f305fbfb35453 100644
> --- a/drivers/dma/sun6i-dma.c
> +++ b/drivers/dma/sun6i-dma.c

[ ... ]

> @@ -549,14 +581,14 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
>  	u32 status;
>  
>  	for (i = 0; i < sdev->num_pchans / DMA_IRQ_CHAN_NR; i++) {
> -		status = readl(sdev->base + DMA_IRQ_STAT(i));
> +		status = sun6i_read_irq_stat(sdev, i);
>  		if (!status)
>  			continue;
>  
>  		dev_dbg(sdev->slave.dev, "DMA irq status %s: 0x%x\n",
>  			str_high_low(i), status);
>  
> -		writel(status, sdev->base + DMA_IRQ_STAT(i));
> +		sun6i_write_irq_stat(sdev, i, status);
>  
>  		for (j = 0; (j < DMA_IRQ_CHAN_NR) && status; j++) {
>  			pchan = sdev->pchans + j;

[Severity: Critical]
This is a pre-existing issue, but does this outer loop truncation and inner
loop physical channel indexing lead to dropped interrupts and unhandled
interrupt storms?

For the outer loop, integer division truncates the number of loops for SoCs
with non-multiple-of-8 channel counts (e.g., H3 with 12 channels). This
ignores interrupts for channels 8-11, causing unhandled hardware interrupts
and CPU hangs.

For the inner loop, does the array offset use only `j` without `i`? For
channels in higher registers (like channel 8, where `i=1`), this incorrectly
attributes the interrupt to channels 0-7 instead of
`i * num_channels_per_reg + j`.

This causes the DMA framework to prematurely complete active transfers on the
wrong channels, leading to use-after-free conditions, data corruption, and
dropped interrupts for the true channel.

Note that for the newly supported A733 SoC, which uses
`num_channels_per_reg = 1`, all interrupts for channels 1-15 will incorrectly
map to channel 0, rendering the DMA controller completely non-functional.

[ ... ]

> @@ -1072,9 +1104,11 @@ static struct dma_chan *sun6i_dma_of_xlate(struct of_phandle_args *dma_spec,
>  
>  static inline void sun6i_kill_tasklet(struct sun6i_dma_dev *sdev)
>  {
> +	int i;
> +
>  	/* Disable all interrupts from DMA */
> -	writel(0, sdev->base + DMA_IRQ_EN(0));
> -	writel(0, sdev->base + DMA_IRQ_EN(1));
> +	for (i = 0; i < 2; i++)
> +		sun6i_write_irq_en(sdev, i, 0);

[Severity: High]
This is a pre-existing issue, but can this unlocked write to the IRQ_EN
register race with the tasklet's read-modify-write and leave interrupts
enabled?

During driver unload, sun6i_kill_tasklet() writes 0 to the IRQ_EN register
to disable hardware interrupts without any lock. Concurrently, if a channel
is still completing, sun6i_dma_tasklet() can run on another CPU, calling
sun6i_dma_start_desc() which performs an unprotected read-modify-write on
the same IRQ_EN register.

The tasklet can overwrite the 0 written by sun6i_kill_tasklet() with a
non-zero interrupt enable mask. Because the hardware interrupt is left
enabled but the IRQ handler is immediately freed by devm_free_irq(), any
subsequent DMA interrupt assertion will trigger an unhandled IRQ storm,
permanently hanging the CPU.

[ ... ]

Additionally, there are a few other pre-existing issues in this file:

[Severity: High]
This is a pre-existing issue, but does acquiring sdev->lock without
disabling softirqs in process context create a deadlock risk?

Functions like sun6i_dma_terminate_all() and sun6i_dma_pause() can be
called from process context, acquiring sdev->lock using plain spin_lock():

drivers/dma/sun6i-dma.c:sun6i_dma_terminate_all() {
    ...
    spin_lock(&sdev->lock);
    list_del_init(&vchan->node);
    spin_unlock(&sdev->lock);
    ...
}

Concurrently, sun6i_dma_tasklet() executes in softirq context and acquires
the same lock using spin_lock_irq(&sdev->lock). If a hardware interrupt
occurs on the same CPU while the process context holds sdev->lock, the
returning interrupt will trigger do_softirq().

The sun6i_dma_tasklet() will then execute, attempting to acquire sdev->lock
and spinning forever, causing a CPU deadlock.

[Severity: High]
This is a pre-existing issue, but does a lockless read of vchan->phy cause
a TOCTOU race that can pause or stop unrelated DMA channels?

In sun6i_dma_pause(), sun6i_dma_terminate_all(), sun6i_dma_resume(), and
sun6i_dma_tx_status(), vchan->phy is read into a local pchan variable
before any locks are acquired:

drivers/dma/sun6i-dma.c:sun6i_dma_pause() {
    ...
    struct sun6i_pchan *pchan = vchan->phy;
    ...
}

Concurrently, sun6i_dma_tasklet() can finish a transfer, clear vchan->phy,
and immediately reallocate the physical channel to a completely different
virtual channel. The original functions then proceed to use the stale
pchan pointer without holding locks, inadvertently stopping, pausing, or
querying an unrelated active DMA transfer. Can this lead to data corruption
and hardware timeouts?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260826-sun60i-a733-dma-v4-0-dc2f745f40dc@gmail.com?part=1

  reply	other threads:[~2026-08-26  1:45 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-26  1:30 [PATCH v4 0/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller Yuanshen Cao
2026-08-26  1:30 ` [PATCH v4 1/5] dmaengine: sun6i-dma: Refactor to support A733 interrupt and register handling Yuanshen Cao
2026-08-26  1:45   ` sashiko-bot [this message]
2026-08-26  1:30 ` [PATCH v4 2/5] dmaengine: sun6i-dma: Support variable address widths using masks Yuanshen Cao
2026-08-26  1:40   ` sashiko-bot
2026-08-26  1:30 ` [PATCH v4 3/5] dmaengine: sun6i-dma: Add num_channels_per_reg for flexible interrupt mapping Yuanshen Cao
2026-08-26  1:43   ` sashiko-bot
2026-08-26  1:30 ` [PATCH v4 4/5] dt-bindings: dmaengine: sun50i-a64-dma: Add allwinner,sun60i-a733-dma compatible string Yuanshen Cao
2026-08-26  1:30 ` [PATCH v4 5/5] dmaengine: sun6i-dma: Add support for Allwinner A733 DMA controller Yuanshen Cao
2026-08-26  1:43   ` sashiko-bot
2026-08-26 16:26   ` Frank Li

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260826014522.8CAB71F000E9@smtp.kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=Frank.Li@kernel.org \
    --cc=alex.caoys@gmail.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dmaengine@vger.kernel.org \
    --cc=linux-sunxi@lists.linux.dev \
    --cc=robh@kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    --cc=vkoul@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox