Linux PCI subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Nathan Lynch" <nathan.lynch@amd.com>
Cc: dmaengine@vger.kernel.org, linux-pci@vger.kernel.org,
	vkoul@kernel.org, Frank.Li@kernel.org
Subject: Re: [PATCH v4 25/25] dmaengine: sdxi: Add DMA engine provider
Date: Wed, 04 Nov 2026 03:24:17 +0000	[thread overview]
Message-ID: <sashiko-outbox-196598@kernel.org> (raw)
In-Reply-To: <20261103-sdxi-base-v4-25-3668ccc7aab8@amd.com>

Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] dmaengine: sdxi: devm DMA unregister runs after sdxi_unregister() frees cxts
- [High] dmaengine: sdxi: sdxi_tx_status() loses error state and skips dma_tx_state
- [Medium] dmaengine: sdxi: sdxi_device_init() ignores sdxi_dma_register() error
- [Medium] dmaengine: sdxi: context stop-on-error prevents IRQ reaping and stalls sync
- [Medium] dmaengine: sdxi: premature AKey and vector release on context teardown
- [Medium] dmaengine: sdxi: missing max_buffer check in sdxi_dma_prep_memcpy()
- [Low] dmaengine: sdxi: undocumented dma_channels parameter in kernel-parameters.txt
--

commit cdf3a67512f9931ad28471e39e5814ce577c3d81
Author: Nathan Lynch <nathan.lynch@amd.com>

dmaengine: sdxi: Add DMA engine provider

This commit registers a virt-dma memcpy provider for SDXI functions with a
configurable number of channels per function. It batches copy descriptors
behind fenced completion interrupts and implements channel termination and
synchronization callbacks.

> diff --git a/drivers/dma/sdxi/device.c b/drivers/dma/sdxi/device.c
> index 30c4e687cedf1..265ebea1e350e 100644
> --- a/drivers/dma/sdxi/device.c
> +++ b/drivers/dma/sdxi/device.c
> @@ -23,6 +23,7 @@
>  #include <linux/xarray.h>
>  
>  #include "context.h"
> +#include "dma.h"
>  #include "hw.h"
>  #include "mmio.h"
>  #include "sdxi.h"
> @@ -424,6 +425,7 @@ static int sdxi_device_init(struct sdxi_dev *sdxi)
>  	if (err)
>  		return err;
>  
> +	sdxi_dma_register(sdxi);
>  	return 0;
>  }

[Severity: Medium]
As reported by Sashiko on v3 [1], sdxi_dma_register() returns -ENOMEM when
devm_kzalloc() fails and returns the negative error code from
dmaenginem_async_device_register() when registration fails, but
sdxi_device_init() ignores the return value and unconditionally returns 0.

Should sdxi_device_init() check the return value of sdxi_dma_register() and
clean up via sdxi_dev_stop() on failure, or should sdxi_dma_register() return
void if DMA engine registration is intentionally best-effort?

> diff --git a/drivers/dma/sdxi/dma.c b/drivers/dma/sdxi/dma.c
> new file mode 100644
> index 0000000000000..512855c83da27
> --- /dev/null
> +++ b/drivers/dma/sdxi/dma.c
> @@ -0,0 +1,509 @@

[ ... ]

> +static unsigned short dma_channels = 1;
> +module_param(dma_channels, ushort, 0644);
> +MODULE_PARM_DESC(dma_channels, "DMA channels per function (default: 1)");

[Severity: Low]
This adds the sdxi_core.dma_channels module parameter, which can also be set
on the kernel command line at boot. Should this parameter be documented in
Documentation/admin-guide/kernel-parameters.txt?

[ ... ]

> +static struct dma_async_tx_descriptor *
> +sdxi_dma_prep_memcpy(struct dma_chan *dma_chan, dma_addr_t dst,
> +		     dma_addr_t src, size_t len, unsigned long flags)
> +{
> +	struct sdxi_dma_chan *sdchan = to_sdxi_dma_chan(dma_chan);
> +	struct sdxi_cxt *cxt = sdchan->cxt;
> +	u16 akey_index = sdxi_akey_index(cxt, sdchan->akey);
> +	struct sdxi_dma_txd *txd;
> +	struct sdxi_copy copy = {
> +		.src = src,
> +		.dst = dst,
> +		.src_akey = akey_index,
> +		.dst_akey = akey_index,
> +		.len = len,
> +	};
> +
> +	/*
> +	 * Validate now, with a throwaway encode onto the stack. The real
> +	 * encode into the ring happens at issue.
> +	 */
> +	if (sdxi_encode_copy(&(struct sdxi_desc){}, &copy))
> +		return NULL;

[Severity: Medium]
In sdxi_fn_activate() and sdxi_publish_cxt(), the function and client context
are configured with sdxi->max_buffer, which restricts each copy descriptor to
at most (u64)SZ_2M << sdxi->max_buffer bytes:

drivers/dma/sdxi/device.c:sdxi_fn_activate()
	sdxi->max_buffer = FIELD_GET(SDXI_MMIO_CAP1_MAX_BUFFER, cap1);
	dma_set_max_seg_size(sdxi->dev,
			     min((u64)SZ_2M << sdxi->max_buffer, U32_MAX));

drivers/dma/sdxi/context.c:sdxi_publish_cxt()
	*ent = (typeof(*ent)) {
		...
		.max_buffer  = cxt->sdxi->max_buffer,
		...
	};

However, sdxi_dma_prep_memcpy() only validates copy.len through
sdxi_encode_copy() -> sdxi_encode_size32(), which accepts any length up to
SZ_4G. Because DMA engine memcpy callers such as dmatest and async_memcpy do
not check dma_get_max_seg_size() before calling device_prep_dma_memcpy(), can
a transfer with len > ((u64)SZ_2M << cxt->sdxi->max_buffer) succeed in
sdxi_dma_prep_memcpy() and then fault the hardware context when issued?

> +
> +	txd = kmalloc_obj(*txd, GFP_NOWAIT);
> +	if (!txd)
> +		return NULL;
> +
> +	*txd = (typeof(*txd)) {
> +		.copy = copy,
> +	};
> +
> +	return vchan_tx_prep(&sdchan->vchan, &txd->vdesc, flags);
> +}
> +
> +static enum dma_status sdxi_tx_status(struct dma_chan *chan,
> +				      dma_cookie_t cookie,
> +				      struct dma_tx_state *state)
> +{
> +	struct sdxi_dma_chan *sdchan = to_sdxi_dma_chan(dma_chan);
> +	struct virt_dma_desc *vdesc;
> +
> +	/* Under the lock for a consistent cookie snapshot; the IRQ reaps. */
> +	guard(spinlock_irqsave)(&sdchan->vchan.lock);
> +
> +	/* vchan_find_desc() only returns issued txds, which own a completion. */
> +	vdesc = vchan_find_desc(&sdchan->vchan, cookie);
> +	if (vdesc) {
> +		struct sdxi_dma_txd *txd = to_sdxi_dma_txd(vdesc);
> +
> +		if (sdxi_cst_blk_check(txd->completion.cst_blk) == SDXI_COMPLETION_ERROR)
> +			return DMA_ERROR;
> +	}
> +
> +	return dma_cookie_status(chan, cookie, state);
> +}

[Severity: High]
As reported by Sashiko on v3 [2], when a descriptor finishes with
SDXI_COMPLETION_ERROR, sdxi_dma_reap_locked() removes it from
vchan->desc_issued and calls vchan_cookie_complete():

	if (state == SDXI_COMPLETION_ERROR)
		txd->vdesc.tx_result.result = DMA_TRANS_ABORTED;

	list_del(&vdesc->node);
	sdxi_cst_pool_put(sdchan->cxt->cst_pool, txd->completion.cst_blk);
	txd->completion = (struct sdxi_completion) {};
	vchan_cookie_complete(&txd->vdesc);

Because vchan_cookie_complete() advances chan->completed_cookie to that
descriptor's cookie and vchan_find_desc() only searches vchan->desc_issued,
won't any call to sdxi_tx_status() after the descriptor is reaped fall through
to dma_cookie_status() and report DMA_COMPLETE instead of DMA_ERROR?

Also, when vchan_find_desc() does find an errored descriptor still on
vchan->desc_issued, returning DMA_ERROR directly skips dma_cookie_status()
and leaves *state uninitialized when the caller passes a non-NULL state
pointer (such as in dma_async_is_tx_complete()).

[ ... ]

> +static bool sdxi_dma_idle(struct sdxi_dma_chan *sdchan)
> +{
> +	struct virt_dma_chan *vchan = &sdchan->vchan;
> +
> +	guard(spinlock_irqsave)(&vchan->lock);
> +
> +	sdxi_dma_reap_locked(sdchan);
> +	if (sdxi_dma_drain_locked(sdchan))
> +		sdxi_cxt_kick(sdchan->cxt);
> +
> +	return list_empty(&vchan->desc_terminated) &&
> +	       list_empty(&vchan->desc_issued) &&
> +	       list_empty(&sdchan->pending);
> +}

[Severity: Medium]
Client contexts are configured with stop-on-error enabled in
sdxi_publish_cxt():

drivers/dma/sdxi/context.c:sdxi_publish_cxt()
	ctl_cfg = (typeof(ctl_cfg)) {
		.se              = 1,
		.csa             = 1,
		...
	};

In sdxi_dma_drain_locked(), completion interrupts are generated by a fenced
DSC_INTR descriptor placed at the end of each copy batch. If a copy descriptor
encounters an error, the context stops on that descriptor before executing the
trailing fenced DSC_INTR descriptor or any subsequent copies in the ring.

When that happens, won't the completion interrupt never fire, and won't
sdxi_dma_idle() in sdxi_dma_synchronize() continue waiting on the remaining
issued and pending descriptors until the 10-second SDXI_DMA_SYNC_TIMEOUT_US
expires rather than detecting that cxt->sq->cxt_sts.state has entered
CXTV_STOP_FN or CXTV_ERR_FN?

[ ... ]

> +static int sdxi_dma_alloc_chan_resources(struct dma_chan *dma_chan)
> +{

[ ... ]

> +	err = sdxi_start_cxt(sdchan->cxt);
> +	if (err)
> +		goto free_irq;
> +
> +	return 0;
> +free_irq:
> +	free_irq(sdchan->irq, sdchan);
> +free_akey:
> +	sdxi_free_akey(sdchan->cxt, sdchan->akey);
> +free_vector:
> +	sdxi_free_vector(sdxi, vector);
> +exit_cxt:
> +	sdxi_cxt_exit(sdchan->cxt);
> +	return err;
> +}
> +
> +static void sdxi_dma_free_chan_resources(struct dma_chan *dma_chan)
> +{
> +	struct sdxi_dma_chan *sdchan = to_sdxi_dma_chan(dma_chan);
> +	LIST_HEAD(head);
> +
> +	sdxi_stop_cxt(sdchan->cxt);
> +	free_irq(sdchan->irq, sdchan);
> +	sdxi_free_vector(sdchan->cxt->sdxi, sdchan->vector);
> +	sdxi_free_akey(sdchan->cxt, sdchan->akey);
> +	/* virt-dma does not know about the driver's pending list. */
> +	scoped_guard(spinlock_irqsave, &sdchan->vchan.lock)
> +		list_splice_tail_init(&sdchan->pending, &head);
> +	vchan_dma_desc_free_list(&sdchan->vchan, &head);
> +	vchan_free_chan_resources(to_virt_chan(dma_chan));
> +	sdxi_cxt_exit(sdchan->cxt);
> +}

[Severity: Medium]
In sdxi_dma_free_chan_resources(), if sdxi_stop_cxt() fails or times out (or
is interrupted in sdxi_cxt_reserve()), or if sdxi_cxt_update() fails inside
sdxi_cxt_exit(), sdxi_cxt_exit() retains the context until function shutdown
because the hardware may still reference it:

drivers/dma/sdxi/context.c:sdxi_cxt_exit()
	state = FIELD_GET(SDXI_CXT_STS_STATE, READ_ONCE(cxt->sq->cxt_sts.state));
	if (state != CXTV_STOP_SW && state != CXTV_STOP_FN && state != CXTV_ERR_FN)
		return -EBUSY;
	...
	err = sdxi_cxt_update(cxt);
	if (err) {
		dev_err(sdxi->dev,
			"context %u release failed (%d), retaining resources until function shutdown\n",
			cxt->id, err);
		return err;
	}

	sdxi_free_cxt(cxt);

Because sdxi_dma_free_chan_resources() calls sdxi_free_vector() and
sdxi_free_akey() unconditionally before sdxi_cxt_exit(), can this zero the
DMA-visible AKey entry and release sdchan->vector for reuse by another
channel while the unstopped context is still active in hardware?

Similarly, in sdxi_dma_alloc_chan_resources(), if sdxi_start_cxt() times out
after publishing DSC_CXT_START to the admin ring, jumping to free_irq frees
the AKey and vector and calls sdxi_cxt_exit() without stopping the context,
while DSC_CXT_START may still be pending or may have just transitioned the
context to CXTV_RUN.

> +
> +int sdxi_dma_register(struct sdxi_dev *sdxi)
> +{

[ ... ]

> +	err = dmaenginem_async_device_register(dma_dev);
> +	if (err)
> +		return dev_warn_probe(dev, err, "failed to register dma device\n");
> +
> +	return 0;
> +}

[Severity: High]
Does using dmaenginem_async_device_register() here cause a use-after-free and
double-free during driver unbind or PCI removal?

During probe, sdxi_pci_probe() -> sdxi_register() -> sdxi_device_init() ->
sdxi_dma_register() registers dma_dev with dmaenginem_async_device_register(),
which defers dma_async_device_unregister() to devres_release_all() after the
bus remove callback returns.

When the device is removed or unbound, sdxi_pci_remove() calls
sdxi_unregister() first:

drivers/dma/sdxi/device.c:sdxi_unregister()
	sdxi_dev_stop(sdxi);
	sdxi_client_cxts_exit(sdxi);
	ida_destroy(&sdxi->vectors);

sdxi_client_cxts_exit() frees every client context in sdxi->client_cxts via
sdxi_free_cxt() and destroys the xarray, while dma_dev and any allocated
channels are still registered. When devres_release_all() subsequently runs
dmaenginem_async_device_unregister() -> dma_async_device_unregister() ->
sdxi_dma_free_chan_resources() (or if an interrupt or channel callback runs
before devres teardown), sdxi_dma_free_chan_resources() dereferences the
already-freed sdchan->cxt, accesses the destroyed sdxi->vectors IDA and
sdxi->client_cxts xarray, and frees sdchan->cxt a second time in
sdxi_cxt_exit().

[1] https://lore.kernel.org/r/20260605-sdxi-base-v3-23-4d38ca2bdffe@amd.com
[2] https://lore.kernel.org/r/20260605-sdxi-base-v3-23-4d38ca2bdffe@amd.com

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261103-sdxi-base-v4-0-3668ccc7aab8@amd.com?part=25

      reply	other threads:[~2026-11-04  3:24 UTC|newest]

Thread overview: 50+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-04  2:49 [PATCH v4 00/25] dmaengine: Smart Data Accelerator Interface (SDXI) basic support Nathan Lynch via B4 Relay
2026-11-04  2:49 ` [PATCH v4 01/25] PCI: Add SNIA SDXI accelerator sub-class Nathan Lynch via B4 Relay
2026-11-04  2:55   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 02/25] MAINTAINERS: Add entry for SDXI driver Nathan Lynch via B4 Relay
2026-11-04  2:49 ` [PATCH v4 03/25] dmaengine: sdxi: Add PCI initialization Nathan Lynch via B4 Relay
2026-11-04  2:56   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 04/25] dmaengine: sdxi: Prepare function and sanitize MMIO state Nathan Lynch via B4 Relay
2026-11-04  3:06   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 05/25] dmaengine: sdxi: Feature discovery and initial configuration Nathan Lynch via B4 Relay
2026-11-04  2:54   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 06/25] dmaengine: sdxi: Configure context tables Nathan Lynch via B4 Relay
2026-11-04  2:55   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 07/25] dmaengine: sdxi: Allocate context control DMA pool Nathan Lynch via B4 Relay
2026-11-04  2:54   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 08/25] dmaengine: sdxi: Introduce submission queue Nathan Lynch via B4 Relay
2026-11-04  2:56   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 09/25] dmaengine: sdxi: Allocate administrative context Nathan Lynch via B4 Relay
2026-11-04  2:56   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 10/25] dmaengine: sdxi: Install " Nathan Lynch via B4 Relay
2026-11-04  2:58   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 11/25] dmaengine: sdxi: Start functions on probe, stop on remove Nathan Lynch via B4 Relay
2026-11-04  3:04   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 12/25] dmaengine: sdxi: Add client context allocation API Nathan Lynch via B4 Relay
2026-11-04  3:02   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 13/25] dmaengine: sdxi: Add descriptor ring management Nathan Lynch via B4 Relay
2026-11-04  3:08   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 14/25] dmaengine: sdxi: Add unit tests for descriptor ring reservations Nathan Lynch via B4 Relay
2026-11-04  3:02   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 15/25] dmaengine: sdxi: Per-context access key (AKey) table entry allocator Nathan Lynch via B4 Relay
2026-11-04  2:59   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 16/25] dmaengine: sdxi: Add completion status block API Nathan Lynch via B4 Relay
2026-11-04  2:59   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 17/25] dmaengine: sdxi: Add unit tests for completion pool ownership Nathan Lynch via B4 Relay
2026-11-04  3:00   ` sashiko-bot
2026-11-04  2:49 ` [PATCH v4 18/25] dmaengine: sdxi: Add context submission helper Nathan Lynch via B4 Relay
2026-11-04  3:01   ` sashiko-bot
2026-11-04  2:50 ` [PATCH v4 19/25] dmaengine: sdxi: Encode context start, stop, update, and sync descriptors Nathan Lynch via B4 Relay
2026-11-04  3:01   ` sashiko-bot
2026-11-04  2:50 ` [PATCH v4 20/25] dmaengine: sdxi: Add client context release with update and sync Nathan Lynch via B4 Relay
2026-11-04  3:12   ` sashiko-bot
2026-11-04  2:50 ` [PATCH v4 21/25] dmaengine: sdxi: Provide context start and stop APIs Nathan Lynch via B4 Relay
2026-11-04  3:04   ` sashiko-bot
2026-11-04  2:50 ` [PATCH v4 22/25] dmaengine: sdxi: Encode copy and interrupt descriptors Nathan Lynch via B4 Relay
2026-11-04  3:07   ` sashiko-bot
2026-11-04  2:50 ` [PATCH v4 23/25] dmaengine: sdxi: Add unit tests for descriptor encoding Nathan Lynch via B4 Relay
2026-11-04  3:05   ` sashiko-bot
2026-11-04  2:50 ` [PATCH v4 24/25] dmaengine: sdxi: MSI/MSI-X vector allocation and mapping Nathan Lynch via B4 Relay
2026-11-04  3:15   ` sashiko-bot
2026-11-04  2:50 ` [PATCH v4 25/25] dmaengine: sdxi: Add DMA engine provider Nathan Lynch via B4 Relay
2026-11-04  3:24   ` sashiko-bot [this message]

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-196598@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=Frank.Li@kernel.org \
    --cc=dmaengine@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=nathan.lynch@amd.com \
    --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