All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Allen Pais" <allen.lkml@gmail.com>
Cc: dmaengine@vger.kernel.org, vkoul@kernel.org, Frank.Li@kernel.org
Subject: Re: [PATCH v4 23/33] dmaengine: timb_dma: route callbacks via channel BH
Date: Tue, 10 Nov 2026 18:04:33 +0000	[thread overview]
Message-ID: <sashiko-outbox-207555@kernel.org> (raw)
In-Reply-To: <e9f90c61446942c160480ee81e220869de918105.1794315350.git.allen.lkml@gmail.com>

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [High] dmaengine: timb_dma: race in __td_ier_mask() and TIMBDMA_IER
  updates
- [High] dmaengine: timb_dma: race between timb_dma_chan_bh() and
  channel teardown
--

commit dc5312469115907bacab896cc42d4d204804abfc
Author: Allen Pais <allen.lkml@gmail.com>

dmaengine: timb_dma: route callbacks via channel BH

This commit moves per-channel DMA completion handling from the controller
tasklet to per-channel BH work items and drains the BH work during channel
teardown and driver removal.

> diff --git a/drivers/dma/timb_dma.c b/drivers/dma/timb_dma.c
> index 0acd7a36a3d0d..d24508e2a0bbf 100644
> --- a/drivers/dma/timb_dma.c
> +++ b/drivers/dma/timb_dma.c
> @@ -433,6 +433,8 @@ static void td_free_chan_resources(struct dma_chan *chan)
>  
>  	dev_dbg(dmaengine_chan_dev(chan), "%s: Entry\n", __func__);
>  
> +	dmaengine_kill_bh(&td_chan->chan);
> +
>  	/* check that all descriptors are free */
>  	BUG_ON(!list_empty(&td_chan->active_list));
>  	BUG_ON(!list_empty(&td_chan->queue));
> @@ -575,17 +577,42 @@ static void td_tasklet(struct tasklet_struct *t)
>  	for (i = 0; i < td->dma.chancnt; i++)
>  		if (ipr & (1 << i)) {
>  			struct timb_dma_chan *td_chan = td->channels + i;
> -			spin_lock(&td_chan->lock);
> -			__td_finish(td_chan);
> -			if (!list_empty(&td_chan->queue))
> -				__td_start_next(td_chan);
> -			spin_unlock(&td_chan->lock);
> +			dmaengine_schedule_bh(&td_chan->chan);
>  		}
>  
>  	ier = __td_ier_mask(td);
>  	iowrite32(ier, td->membase + TIMBDMA_IER);
>  }
>  
> +static void timb_dma_chan_bh(struct dma_chan *chan)
> +{
> +	struct timb_dma_chan *td_chan = container_of(chan, struct timb_dma_chan,
> +						     chan);
> +	struct timb_dma *td = tdchantotd(td_chan);
> +	u32 ier;
> +
> +	spin_lock(&td_chan->lock);
> +	__td_finish(td_chan);
> +	if (!list_empty(&td_chan->queue))
> +		__td_start_next(td_chan);
> +	spin_unlock(&td_chan->lock);

[Severity: High]
Can a pending timb_dma_chan_bh() work item prematurely complete a newly
submitted transfer after td_terminate_all() or td_issue_pending()?

Previously, td_tasklet() took td_chan->lock and finished the active
descriptor immediately.  Now, td_tasklet() only schedules the channel BH
work, and td_terminate_all() tears down the active descriptor without
canceling or flushing that work:

CPU0 (<softirq>)                  CPU1
td_tasklet()
  dmaengine_schedule_bh()
                                  td_terminate_all()
                                    spin_lock_bh(&td_chan->lock)
                                    __td_finish(td_chan)
                                    spin_unlock_bh(&td_chan->lock)
                                  td_tx_submit()
                                    spin_lock_bh(&td_chan->lock)
                                    __td_start_dma(td_chan)
                                    spin_unlock_bh(&td_chan->lock)
timb_dma_chan_bh()
  spin_lock(&td_chan->lock)
  __td_finish(td_chan)  <-- stops and unmaps the new in-flight transfer

When the stale BH work runs, __td_finish() unmaps and completes the newly
started descriptor at the head of td_chan->active_list while hardware DMA is
still in flight.

In addition, dmaengine_kill_bh() in td_free_chan_resources() only flushes
td_chan->chan.bh_work without waiting for td->tasklet.  If td_tasklet() is
pending or running on another CPU, it can schedule the channel BH after
dmaengine_kill_bh() returns, or leave td_chan->active_list non-empty when
td_free_chan_resources() reaches BUG_ON(!list_empty(&td_chan->active_list)).

> +
> +	/*
> +	 * The controller tasklet re-armed IER before this channel was
> +	 * finished. Recompute it now that the channel state has changed.
> +	 */
> +	ier = __td_ier_mask(td);
> +	iowrite32(ier, td->membase + TIMBDMA_IER);
> +}

[Severity: High]
Does calling __td_ier_mask(td) and writing TIMBDMA_IER without holding the
channel locks race with concurrent channel completions and starts?

Previously, __td_finish() and __td_start_next() ran sequentially inside the
single controller tasklet before calling __td_ier_mask(td).  Now, both
td_tasklet() and each channel's timb_dma_chan_bh() call __td_ier_mask(td)
without holding any td_chan->lock:

drivers/dma/timb_dma.c:__td_ier_mask() {
	for (i = 0; i < td->dma.chancnt; i++) {
		struct timb_dma_chan *td_chan = td->channels + i;
		if (td_chan->ongoing) {
			struct timb_dma_desc *td_desc =
				list_entry(td_chan->active_list.next,
				struct timb_dma_desc, desc_node);
			if (td_desc->interrupt)
				ret |= 1 << i;
		}
	}
}

If another CPU is inside __td_finish() (from another channel's BH,
td_terminate_all(), or td_issue_pending()), __td_ier_mask() can observe
td_chan->ongoing == true while list_move() empties td_chan->active_list:

CPU0                              CPU1
timb_dma_chan_bh()                __td_finish(td_chan)
  spin_unlock(&td_chan->lock)
  __td_ier_mask(td)
    if (td_chan->ongoing)
                                    td_chan->ongoing = false;
                                    list_move(&td_desc->desc_node,
                                              &td_chan->free_list);
      td_desc = list_entry(
        td_chan->active_list.next, ...)
      if (td_desc->interrupt)  <-- out-of-bounds read off list head

When active_list is empty, active_list.next points to &td_chan->active_list
itself, so reading td_desc->interrupt reads past struct timb_dma_chan (and
past the end of the struct timb_dma slab allocation on the last channel), or
reads a freed descriptor if td_free_chan_resources() frees free_list.

Second, writing ier to TIMBDMA_IER outside any lock races with
__td_enable_chan_irq() when another channel starts a transfer, overwriting
TIMBDMA_IER with a stale mask and losing that channel's completion
interrupt.

Third, td_irq() masks interrupts by writing 0 to TIMBDMA_IER and relies on
td_tasklet() to clear TIMBDMA_ISR before re-enabling TIMBDMA_IER.  Because
td_tasklet() now writes TIMBDMA_IER before timb_dma_chan_bh() runs
__td_finish() to stop RX DMA and clear ongoing, and because
timb_dma_chan_bh() can also write TIMBDMA_IER after td_irq() masks it for
another channel but before td_tasklet() acknowledges TIMBDMA_ISR, an
unacknowledged interrupt can be unmasked immediately and re-trigger
td_irq().

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/cover.1794315350.git.allen.lkml@gmail.com?part=23

  reply	other threads:[~2026-11-10 18:04 UTC|newest]

Thread overview: 44+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-10 13:13 [PATCH v4 00/33] dmaengine: migrate channel tasklets to WQ_BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 01/33] dmaengine: add tasklet-backed channel BH helpers Allen Pais
2026-11-10 13:13   ` Allen Pais
2026-11-10 13:13 ` [PATCH v4 02/33] dmaengine: back channel BH helpers with WQ_BH Allen Pais
2026-11-10 14:15   ` sashiko-bot
2026-11-10 13:13 ` [PATCH v4 03/33] dmaengine: apple-admac: use dmaengine BH callback Allen Pais
2026-11-10 13:13 ` [PATCH v4 04/33] dmaengine: at_xdmac: move irq bottom half to dmaengine BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 05/33] dmaengine: ep93xx: hook callbacks via " Allen Pais
2026-11-10 13:13 ` [PATCH v4 06/33] dmaengine: fsldma: migrate tasklet to " Allen Pais
2026-11-10 13:13 ` [PATCH v4 07/33] dmaengine: fsl_raid: run completions via " Allen Pais
2026-11-10 13:13 ` [PATCH v4 08/33] dmaengine: imx-dma: flip per-chan tasklet to " Allen Pais
2026-11-10 13:13 ` [PATCH v4 09/33] dmaengine: ioat: convert cleanup " Allen Pais
2026-11-10 13:13 ` [PATCH v4 10/33] dmaengine: mmp_pdma: replace per-chan tasklet with " Allen Pais
2026-11-10 13:13 ` [PATCH v4 11/33] dmaengine: mmp_tdma: hook completions to " Allen Pais
2026-11-10 13:13 ` [PATCH v4 12/33] dmaengine: mv_xor: convert irq tasklet " Allen Pais
2026-11-10 13:13 ` [PATCH v4 13/33] dmaengine: mxs-dma: use dmaengine BH scheduling Allen Pais
2026-11-10 13:13 ` [PATCH v4 14/33] dmaengine: nbpfaxi: switch callbacks to dmaengine BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 15/33] dmaengine: pch_dma: convert tasklet " Allen Pais
2026-11-10 13:13 ` [PATCH v4 16/33] dmaengine: ppc4xx: replace irq tasklet with " Allen Pais
2026-11-10 13:13 ` [PATCH v4 17/33] dmaengine: ste_dma40: convert per-channel tasklet to " Allen Pais
2026-11-10 17:08   ` sashiko-bot
2026-11-10 13:13 ` [PATCH v4 18/33] dmaengine: xgene-dma: wire descriptor cleanup " Allen Pais
2026-11-10 13:13 ` [PATCH v4 19/33] dmaengine: xilinx-dma: use dmaengine BH instead of tasklets Allen Pais
2026-11-10 17:35   ` sashiko-bot
2026-11-10 13:13 ` [PATCH v4 20/33] dmaengine: xilinx-dpdma: kill vchan BH on remove Allen Pais
2026-11-10 13:13 ` [PATCH v4 21/33] dmaengine: zynqmp-dma: switch completion tasklet to dmaengine BH Allen Pais
2026-11-10 13:13 ` [PATCH v4 22/33] dmaengine: tegra20-apb: use channel BH helpers Allen Pais
2026-11-10 13:13 ` [PATCH v4 23/33] dmaengine: timb_dma: route callbacks via channel BH Allen Pais
2026-11-10 18:04   ` sashiko-bot [this message]
2026-11-10 13:13 ` [PATCH v4 24/33] dmaengine: txx9dmac: " Allen Pais
2026-11-10 13:13 ` [PATCH v4 25/33] dmaengine: mv_xor_v2: use channel BH helpers Allen Pais
2026-11-10 13:13 ` [PATCH v4 26/33] dmaengine: mpc512x: route callbacks via channel BH Allen Pais
2026-11-10 18:44   ` sashiko-bot
2026-11-10 13:13 ` [PATCH v4 27/33] dmaengine: plx_dma: use channel BH helpers Allen Pais
2026-11-10 13:13 ` [PATCH v4 28/33] dmaengine: sf-pdma: route error callbacks through channel BH Allen Pais
2026-11-10 13:13   ` Allen Pais
2026-11-10 19:08   ` sashiko-bot
2026-11-10 13:13 ` [PATCH v4 29/33] dmaengine: pl330: route callbacks via " Allen Pais
2026-11-10 13:13 ` [PATCH v4 30/33] dmaengine: altera-msgdma: use channel BH helpers Allen Pais
2026-11-11  2:39   ` NG, ADRIAN HO YIN
2026-11-10 13:13 ` [PATCH v4 31/33] dmaengine: hidma: defer callbacks via channel BH Allen Pais
2026-11-10 19:51   ` sashiko-bot
2026-11-10 13:13 ` [PATCH v4 32/33] dmaengine: qcom-gpi: defer callbacks via vchan Allen Pais
2026-11-10 13:13 ` [PATCH v4 33/33] dmaengine: switchtec: use channel BH helpers Allen Pais

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=sashiko-outbox-207555@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=Frank.Li@kernel.org \
    --cc=allen.lkml@gmail.com \
    --cc=dmaengine@vger.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.