Re: [PATCH v2 18/64] dmaengine: xgene-dma: wire descriptor cleanup to dma_chan BH

[email protected] Mon, 27 Jul 2026 20:59:49 +0000
Newsgroups org.kernel.vger.dmaengine
Message-ID <[email protected]>
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

Pre-existing issues:
- [High] During device teardown, the driver frees the IRQ before flushing the bottom half workqueue. This results in the bottom half handler calling `enable_irq()` on a freed IRQ descriptor, which violates the API and can cause warnings or oopses in the IRQ subsystem.
- [High] Potential spinlock recursion deadlock in `xgene_dma_cleanup_descriptors()` when called from process context via `xgene_dma_free_chan_resources()`.
- [High] Hardware interrupts are unmasked before the bottom halves are initialized in the probe sequence, allowing an early interrupt to permanently hang the channel.
- [High] In the probe error path, bottom halves are flushed before hardware interrupts are masked, leading to a race condition that can result in a Use-After-Free of driver structures when the workqueue later executes.
- [High] The driver returns ring slots to the hardware before clearing the descriptor completion signature, risking permanently lost completions.
--

--- Patch [18]: [PATCH v2 18/64] dmaengine: xgene-dma: wire descriptor cleanup to dma_chan BH ---
commit 3558adb2da1479ded074f1f3135c3903cdea9f57
Author: Allen Pais <[email protected]>

dmaengine: xgene-dma: wire descriptor cleanup to dma_chan BH

Replace the per-channel tasklet with the shared dma_chan BH helper.
The handler continues to run in softirq context while dmaengine owns
the common scheduling and teardown mechanism.

> diff --git a/drivers/dma/xgene-dma.c b/drivers/dma/xgene-dma.c
> index fa1173e499009..8d0428c2944ef 100644
> --- a/drivers/dma/xgene-dma.c
> +++ b/drivers/dma/xgene-dma.c
> @@ -975,9 +973,10 @@ static enum dma_status xgene_dma_tx_status(struct dma_chan *dchan,
>  	return dma_cookie_status(dchan, cookie, txstate);
>  }
>  
> -static void xgene_dma_tasklet_cb(struct tasklet_struct *t)
> +static void xgene_dma_tasklet_cb(struct dma_chan *c)
>  {
> -	struct xgene_dma_chan *chan = from_tasklet(chan, t, tasklet);
> +	struct xgene_dma_chan *chan = container_of(c, struct xgene_dma_chan,
> +						   dma_chan);
>  
>  	/* Run all cleanup for descriptors which have been completed */
>  	xgene_dma_cleanup_descriptors(chan);

[Severity: High]
This is a pre-existing issue, but can this cause a spinlock recursion
deadlock?

Looking at xgene_dma_free_chan_resources(), it runs in process context and
calls xgene_dma_cleanup_descriptors(), which acquires chan->lock using
spin_lock() without disabling softirqs.

If a hardware interrupt occurs on the same CPU, it schedules the bottom half.
On interrupt exit, the softirq executes xgene_dma_tasklet_cb(), which also
calls xgene_dma_cleanup_descriptors() and attempts to acquire the
already-held chan->lock.

[Severity: High]
This is a pre-existing issue, but does this out-of-order execution risk
permanently losing DMA completions?

In xgene_dma_cleanup_descriptors(), the driver writes to ring->cmd to notify
the hardware that a descriptor slot is free, and then clears the descriptor's
completion signature in memory:

xgene_dma_cleanup_descriptors() {
    ...
    iowrite32(-1, ring->cmd);
    desc_hw->m0 = cpu_to_le64(XGENE_DMA_DESC_EMPTY_SIGNATURE);
    ...
}

Since the hardware engine is active, could it immediately write a new
completion signature into that slot before the CPU executes the assignment,
causing the CPU to unconditionally overwrite the valid completion signature
with the empty signature?

[ ... ]

> @@ -1539,7 +1538,7 @@ static int xgene_dma_async_register(struct xgene_dma *pdma, int id)
>  	INIT_LIST_HEAD(&chan->ld_pending);
>  	INIT_LIST_HEAD(&chan->ld_running);
>  	INIT_LIST_HEAD(&chan->ld_completed);
> -	tasklet_setup(&chan->tasklet, xgene_dma_tasklet_cb);
> +	dma_chan_init_bh(&chan->dma_chan, xgene_dma_tasklet_cb);

[Severity: High]
This is a pre-existing issue, but can an early interrupt permanently hang
the channel during probe?

In xgene_dma_probe(), xgene_dma_init_hw() enables the DMA engine and unmasks
interrupts before xgene_dma_init_async() initializes the bottom halves.

If a pending or spurious interrupt fires, the ISR attempts to schedule the
uninitialized BH, which fails. Because the BH never runs, enable_irq() is
never called, leaving the channel's IRQ permanently disabled.

[ ... ]

> @@ -1579,7 +1578,7 @@ static int xgene_dma_init_async(struct xgene_dma *pdma)
>  		if (ret) {
>  			for (j = 0; j < i; j++) {
>  				dma_async_device_unregister(&pdma->dma_dev[j]);
> -				tasklet_kill(&pdma->chan[j].tasklet);
> +				dma_chan_kill_bh(&pdma->chan[j].dma_chan);
>  			}

[Severity: High]
This is a pre-existing issue, but could this error path lead to a
use-after-free of the driver structures?

If xgene_dma_init_async() fails, it calls dma_chan_kill_bh() to clean up
initialized channels, but does not mask hardware interrupts.

An interrupt could fire immediately after, re-queuing the BH. The probe
function then calls xgene_dma_free_irqs(), returns an error, and devm
frees the pdma memory.

Could the global workqueue subsequently execute the re-scheduled BH,
dereferencing the freed chan structure?

[ ... ]

> @@ -1790,7 +1789,7 @@ static void xgene_dma_remove(struct platform_device *pdev)
>  
>  	for (i = 0; i < XGENE_DMA_MAX_CHANNEL; i++) {
>  		chan = &pdma->chan[i];
> -		tasklet_kill(&chan->tasklet);
> +		dma_chan_kill_bh(&chan->dma_chan);
>  		xgene_dma_delete_chan_rings(chan);
>  	}

[Severity: High]
This is a pre-existing issue, but is there a risk of accessing a freed IRQ
descriptor during this teardown sequence?

xgene_dma_remove() calls xgene_dma_free_irqs() which frees chan->rx_irq, and
then calls dma_chan_kill_bh().

If the bottom half was already queued, it executes after the IRQ is freed
and unconditionally calls enable_irq(chan->rx_irq), which violates the API
and could cause an oops in the IRQ subsystem.

-- 
Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=18