DMA Engine development
 help / color / mirror / Atom feed
* [PATCH v6 0/9] dmaengine: Add new API to combine configuration and descriptor preparation
@ 2026-05-20 22:00 Frank.Li
  2026-05-20 22:00 ` [PATCH v6 1/9] dmaengine: Add API to combine configuration and preparation (sg and single) Frank.Li
                   ` (8 more replies)
  0 siblings, 9 replies; 22+ messages in thread
From: Frank.Li @ 2026-05-20 22:00 UTC (permalink / raw)
  To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
	Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
	Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
	Niklas Cassel
  Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
	linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li,
	Damien Le Moal

Previously, configuration and preparation required two separate calls. This
works well when configuration is done only once during initialization.

However, in cases where the burst length or source/destination address must
be adjusted for each transfer, calling two functions is verbose.

	if (dmaengine_slave_config(chan, &sconf)) {
		dev_err(dev, "DMA slave config fail\n");
		return -EIO;
	}

	tx = dmaengine_prep_slave_single(chan, dma_local, len, dir, flags);

After new API added

	tx = dmaengine_prep_config_single(chan, dma_local, len, dir, flags, &sconf);

Additional, prevous two calls requires additional locking to ensure both
steps complete atomically.

    mutex_lock()
    dmaengine_slave_config()
    dmaengine_prep_slave_single()
    mutex_unlock()

after new API added, mutex lock can be moved. See patch
     nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API

Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
Changes in v6:
- Fix sashaki AI report problem, detail see each patch's change log
- Link to v5: https://lore.kernel.org/r/20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com

Changes in v5:
- collect Mani's reviewed-by tags
- use kernel doc for new APIs.
- Link to v4: https://lore.kernel.org/r/20260506-dma_prep_config-v4-0-85b3d22babff@nxp.com

Changes in v4:
- remove void* context in config_prep() callback
- use spin lock to protect config() and prep().
- Link to v3: https://lore.kernel.org/r/20260105-dma_prep_config-v3-0-a8480362fd42@nxp.com

Changes in v3:
- collect review tags
- create safe version in framework
- Link to v2: https://lore.kernel.org/r/20251218-dma_prep_config-v2-0-c07079836128@nxp.com

Changes in v2:
- Use name dmaengine_prep_config_single() and dmaengine_prep_config_sg()
- Add _safe version to avoid confuse, which needn't additional mutex.
- Update document/
- Update commit message. add () for function name. Use upcase for subject.
- Add more explain for remove lock.
- Link to v1: https://lore.kernel.org/r/20251208-dma_prep_config-v1-0-53490c5e1e2a@nxp.com

---
Frank Li (9):
      dmaengine: Add API to combine configuration and preparation (sg and single)
      dmaengine: Add safe API to combine configuration and preparation
      PCI: endpoint: pci-epf-test: Use dmaenigne_prep_config_single() to simplify code
      dmaengine: dw-edma: Use new .device_prep_config_sg() callback
      dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer()
      nvmet: pci-epf: Remove unnecessary dmaengine_terminate_sync() on each DMA transfer
      nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API
      PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code
      crypto: atmel: Use dmaengine_prep_config_sg() API

 Documentation/driver-api/dmaengine/client.rst |   9 ++
 drivers/crypto/atmel-aes.c                    |  10 +-
 drivers/dma/dmaengine.c                       |   2 +
 drivers/dma/dw-edma/dw-edma-core.c            |  41 +++++--
 drivers/nvme/target/pci-epf.c                 |  24 +----
 drivers/pci/endpoint/functions/pci-epf-mhi.c  |  52 +++------
 drivers/pci/endpoint/functions/pci-epf-test.c |   8 +-
 include/linux/dmaengine.h                     | 149 ++++++++++++++++++++++++--
 8 files changed, 208 insertions(+), 87 deletions(-)
---
base-commit: 254f49634ee16a731174d2ae34bc50bd5f45e731
change-id: 20251204-dma_prep_config-654170d245a2

Best regards,
--  
Frank Li <Frank.Li@nxp.com>


^ permalink raw reply	[flat|nested] 22+ messages in thread

* [PATCH v6 1/9] dmaengine: Add API to combine configuration and preparation (sg and single)
  2026-05-20 22:00 [PATCH v6 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank.Li
@ 2026-05-20 22:00 ` Frank.Li
  2026-05-20 22:00 ` [PATCH v6 2/9] dmaengine: Add safe API to combine configuration and preparation Frank.Li
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 22+ messages in thread
From: Frank.Li @ 2026-05-20 22:00 UTC (permalink / raw)
  To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
	Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
	Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
	Niklas Cassel
  Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
	linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li

From: Frank Li <Frank.Li@nxp.com>

Previously, configuration and preparation required two separate calls. This
works well when configuration is done only once during initialization.

However, in cases where the burst length or source/destination address must
be adjusted for each transfer, calling two functions is verbose and
requires additional locking to ensure both steps complete atomically.

Add a new API dmaengine_prep_config_single() and dmaengine_prep_config_sg()
and callback device_prep_config_sg() that combines configuration and
preparation into a single operation. If the configuration argument is
passed as NULL, fall back to the existing implementation.

Tested-by: Niklas Cassel <cassel@kernel.org>
Acked-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v4
- drop context in device_prep_config_sg()

change in v3
- remove Deprecated for callback device_prep_slave_sg().
- Move condition check before sg init.
- split function at return type.
- move safe version to next patch

change in v2
- add () for function
- use short name device_prep_sg(), remove "slave" and "config". the 'slave'
is reduntant. after remove slave, the function name is difference existed
one, so remove _config suffix.
---
 Documentation/driver-api/dmaengine/client.rst |  9 ++++
 include/linux/dmaengine.h                     | 63 +++++++++++++++++++++++----
 2 files changed, 64 insertions(+), 8 deletions(-)

diff --git a/Documentation/driver-api/dmaengine/client.rst b/Documentation/driver-api/dmaengine/client.rst
index d491e385d61a9..5ee5d4a3596dd 100644
--- a/Documentation/driver-api/dmaengine/client.rst
+++ b/Documentation/driver-api/dmaengine/client.rst
@@ -80,6 +80,10 @@ The details of these operations are:
 
   - slave_sg: DMA a list of scatter gather buffers from/to a peripheral
 
+  - config_sg: Similar with slave_sg, just pass down dma_slave_config
+    struct to avoid calling dmaengine_slave_config() every time adjusting the
+    burst length or the FIFO address is needed.
+
   - peripheral_dma_vec: DMA an array of scatter gather buffers from/to a
     peripheral. Similar to slave_sg, but uses an array of dma_vec
     structures instead of a scatterlist.
@@ -106,6 +110,11 @@ The details of these operations are:
 		unsigned int sg_len, enum dma_data_direction direction,
 		unsigned long flags);
 
+     struct dma_async_tx_descriptor *dmaengine_prep_config_sg(
+		struct dma_chan *chan, struct scatterlist *sgl,
+		unsigned int sg_len, enum dma_transfer_direction dir,
+		unsigned long flags, struct dma_slave_config *config);
+
      struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec(
 		struct dma_chan *chan, const struct dma_vec *vecs,
 		size_t nents, enum dma_data_direction direction,
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index b3d251c9734e9..defa377d2ef54 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -835,6 +835,7 @@ struct dma_filter {
  *	where the address and size of each segment is located in one entry of
  *	the dma_vec array.
  * @device_prep_slave_sg: prepares a slave dma operation
+ * @device_prep_config_sg: prepares a slave DMA operation with dma_slave_config
  * @device_prep_dma_cyclic: prepare a cyclic dma operation suitable for audio.
  *	The function takes a buffer of size buf_len. The callback function will
  *	be called after period_len bytes have been transferred.
@@ -934,6 +935,10 @@ struct dma_device {
 		struct dma_chan *chan, struct scatterlist *sgl,
 		unsigned int sg_len, enum dma_transfer_direction direction,
 		unsigned long flags, void *context);
+	struct dma_async_tx_descriptor *(*device_prep_config_sg)(
+		struct dma_chan *chan, struct scatterlist *sgl,
+		unsigned int sg_len, enum dma_transfer_direction direction,
+		unsigned long flags, struct dma_slave_config *config);
 	struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)(
 		struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
 		size_t period_len, enum dma_transfer_direction direction,
@@ -974,22 +979,44 @@ static inline bool is_slave_direction(enum dma_transfer_direction direction)
 	       (direction == DMA_DEV_TO_DEV);
 }
 
-static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single(
-	struct dma_chan *chan, dma_addr_t buf, size_t len,
-	enum dma_transfer_direction dir, unsigned long flags)
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_config_single(struct dma_chan *chan, dma_addr_t buf, size_t len,
+			     enum dma_transfer_direction dir,
+			     unsigned long flags,
+			     struct dma_slave_config *config)
 {
 	struct scatterlist sg;
+
+	if (!chan || !chan->device)
+		return NULL;
+
 	sg_init_table(&sg, 1);
 	sg_dma_address(&sg) = buf;
 	sg_dma_len(&sg) = len;
 
-	if (!chan || !chan->device || !chan->device->device_prep_slave_sg)
+	if (chan->device->device_prep_config_sg)
+		return chan->device->device_prep_config_sg(chan, &sg, 1, dir,
+							   flags, config);
+
+	if (config)
+		if (dmaengine_slave_config(chan, config))
+			return NULL;
+
+	if (!chan->device->device_prep_slave_sg)
 		return NULL;
 
 	return chan->device->device_prep_slave_sg(chan, &sg, 1,
 						  dir, flags, NULL);
 }
 
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_slave_single(struct dma_chan *chan, dma_addr_t buf, size_t len,
+			    enum dma_transfer_direction dir,
+			    unsigned long flags)
+{
+	return dmaengine_prep_config_single(chan, buf, len, dir, flags, NULL);
+}
+
 /**
  * dmaengine_prep_peripheral_dma_vec() - Prepare a DMA scatter-gather descriptor
  * @chan: The channel to be used for this descriptor
@@ -1010,17 +1037,37 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec(
 							    dir, flags);
 }
 
-static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(
-	struct dma_chan *chan, struct scatterlist *sgl,	unsigned int sg_len,
-	enum dma_transfer_direction dir, unsigned long flags)
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_config_sg(struct dma_chan *chan, struct scatterlist *sgl,
+			 unsigned int sg_len, enum dma_transfer_direction dir,
+			 unsigned long flags, struct dma_slave_config *config)
 {
-	if (!chan || !chan->device || !chan->device->device_prep_slave_sg)
+	if (!chan || !chan->device)
+		return NULL;
+
+	if (chan->device->device_prep_config_sg)
+		return chan->device->device_prep_config_sg(chan, sgl, sg_len,
+				dir, flags, config);
+
+	if (config)
+		if (dmaengine_slave_config(chan, config))
+			return NULL;
+
+	if (!chan->device->device_prep_slave_sg)
 		return NULL;
 
 	return chan->device->device_prep_slave_sg(chan, sgl, sg_len,
 						  dir, flags, NULL);
 }
 
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+			unsigned int sg_len, enum dma_transfer_direction dir,
+			unsigned long flags)
+{
+	return dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, NULL);
+}
+
 #ifdef CONFIG_RAPIDIO_DMA_ENGINE
 struct rio_dma_ext;
 static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg(

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v6 2/9] dmaengine: Add safe API to combine configuration and preparation
  2026-05-20 22:00 [PATCH v6 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank.Li
  2026-05-20 22:00 ` [PATCH v6 1/9] dmaengine: Add API to combine configuration and preparation (sg and single) Frank.Li
@ 2026-05-20 22:00 ` Frank.Li
  2026-05-21  0:01   ` sashiko-bot
  2026-05-20 22:00 ` [PATCH v6 3/9] PCI: endpoint: pci-epf-test: Use dmaenigne_prep_config_single() to simplify code Frank.Li
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 22+ messages in thread
From: Frank.Li @ 2026-05-20 22:00 UTC (permalink / raw)
  To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
	Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
	Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
	Niklas Cassel
  Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
	linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li

From: Frank Li <Frank.Li@nxp.com>

Introduce dmaengine_prep_config_single_safe() and
dmaengine_prep_config_sg_safe() to provide a reentrant-safe way to
combine slave configuration and transfer preparation.

Drivers may implement the new device_prep_config_sg() callback to perform
both steps atomically. If the callback is not provided, the helpers fall
back to calling dmaengine_slave_config() followed by
dmaengine_prep_slave_sg() under per-channel spinlock protection.

Tested-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v6
- replace mutex with spinlock in commit message
- use spinlock_saveirq according to AI review results

"The documentation in struct dma_chan notes that *_prep() may be called
from a completion callback. Since completion callbacks often execute in
softirq or hardirq contexts, if a thread calls this function from
process context, local interrupts remain enabled.

If a DMA interrupt fires on the same CPU while the lock is held, the
completion callback could attempt to call this function again to queue
the next transfer, leading it to wait on the already-held chan->lock.

Does this fallback path need to use spin_lock_irqsave() and
spin_unlock_irqrestore() to safely disable interrupts?
"

chagne in v5
- remove reduntant lock commments.
- use kernel doc to descritp API

chagne in v4
- use spinlock() to protect config() and prep()

change in v3
- new patch
---
 drivers/dma/dmaengine.c   |  2 ++
 include/linux/dmaengine.h | 86 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 88 insertions(+)

diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 405bd2fbb4a3b..ba29e60160c1a 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -1099,6 +1099,8 @@ static int __dma_async_device_channel_register(struct dma_device *device,
 	chan->dev->device.parent = device->dev;
 	chan->dev->chan = chan;
 	chan->dev->dev_id = device->dev_id;
+	spin_lock_init(&chan->lock);
+
 	if (!name)
 		dev_set_name(&chan->dev->device, "dma%dchan%d", device->dev_id, chan->chan_id);
 	else
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index defa377d2ef54..6fe46c0c94527 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -322,6 +322,8 @@ struct dma_router {
  * @slave: ptr to the device using this channel
  * @cookie: last cookie value returned to client
  * @completed_cookie: last completed cookie for this channel
+ * @lock: protect between config and prepare transfer when driver have not
+ *	  implemented callback device_prep_config_sg().
  * @chan_id: channel ID for sysfs
  * @dev: class device for sysfs
  * @name: backlink name for sysfs
@@ -341,6 +343,12 @@ struct dma_chan {
 	dma_cookie_t cookie;
 	dma_cookie_t completed_cookie;
 
+	/*
+	 * protect between config and prepare transfer because *_prep() may be
+	 * called from complete callback, which is in GFP_NOSLEEP context.
+	 */
+	spinlock_t lock;
+
 	/* sysfs */
 	int chan_id;
 	struct dma_chan_dev *dev;
@@ -1068,6 +1076,84 @@ dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
 	return dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, NULL);
 }
 
+/**
+ * dmaengine_prep_config_sg_safe - prepare a scatter-gather DMA transfer
+ *                                 with atomic slave configuration update
+ * @chan: DMA channel
+ * @sgl: scatterlist for the transfer
+ * @sg_len: number of entries in @sgl
+ * @dir: DMA transfer direction
+ * @flags: transfer preparation flags
+ * @config: DMA slave configuration for this transfer
+ *
+ * Prepare a DMA scatter-gather transfer together with a corresponding slave
+ * configuration update in a re-entrant and race-safe manner.
+ *
+ * DMA engine drivers may implement the optional
+ * device_prep_config_sg() callback to perform both the slave configuration
+ * and descriptor preparation atomically. In this case, the operation is
+ * fully handled by the DMA engine driver.
+ *
+ * If the DMA engine driver does not implement device_prep_config_sg(), falls
+ * back to calling dmaengine_slave_config() followed by dmaengine_prep_slave_sg().
+ * The fallback path is protected by a per-channel spinlock to ensure that
+ * concurrent callers cannot interleave configuration and descriptor preparation
+ * on the same DMA channel.
+ *
+ * Return: Pointer to a prepared DMA async transaction descriptor on success,
+ * or %NULL if the transfer could not be prepared.
+ */
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_config_sg_safe(struct dma_chan *chan, struct scatterlist *sgl,
+			      unsigned int sg_len,
+			      enum dma_transfer_direction dir,
+			      unsigned long flags,
+			      struct dma_slave_config *config)
+{
+	struct dma_async_tx_descriptor *tx;
+	unsigned long spinlock_flags;
+
+	if (!chan || !chan->device)
+		return NULL;
+
+	if (!chan->device->device_prep_config_sg)
+		spin_lock_irqsave(&chan->lock, spinlock_flags);
+
+	tx = dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, config);
+
+	if (!chan->device->device_prep_config_sg)
+		spin_unlock_irqrestore(&chan->lock, spinlock_flags);
+
+	return tx;
+}
+
+/**
+ * dmaengine_prep_config_single_safe - prepare a single-buffer DMA transfer
+ *                                     with atomic slave configuration update
+ * @chan: DMA channel
+ * @buf: DMA buffer address
+ * @len: length of the transfer in bytes
+ * @dir: DMA transfer direction
+ * @flags: transfer preparation flags
+ * @config: DMA slave configuration for this transfer
+ *
+ * Detail see dmaengine_prep_config_sg_safe().
+ */
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_config_single_safe(struct dma_chan *chan, dma_addr_t buf,
+				  size_t len, enum dma_transfer_direction dir,
+				  unsigned long flags,
+				  struct dma_slave_config *config)
+{
+	struct scatterlist sg;
+
+	sg_init_table(&sg, 1);
+	sg_dma_address(&sg) = buf;
+	sg_dma_len(&sg) = len;
+
+	return dmaengine_prep_config_sg_safe(chan, &sg, 1, dir, flags, config);
+}
+
 #ifdef CONFIG_RAPIDIO_DMA_ENGINE
 struct rio_dma_ext;
 static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg(

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v6 3/9] PCI: endpoint: pci-epf-test: Use dmaenigne_prep_config_single() to simplify code
  2026-05-20 22:00 [PATCH v6 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank.Li
  2026-05-20 22:00 ` [PATCH v6 1/9] dmaengine: Add API to combine configuration and preparation (sg and single) Frank.Li
  2026-05-20 22:00 ` [PATCH v6 2/9] dmaengine: Add safe API to combine configuration and preparation Frank.Li
@ 2026-05-20 22:00 ` Frank.Li
  2026-05-20 22:00 ` [PATCH v6 4/9] dmaengine: dw-edma: Use new .device_prep_config_sg() callback Frank.Li
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 22+ messages in thread
From: Frank.Li @ 2026-05-20 22:00 UTC (permalink / raw)
  To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
	Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
	Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
	Niklas Cassel
  Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
	linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li,
	Damien Le Moal

From: Frank Li <Frank.Li@nxp.com>

Use dmaenigne_prep_config_single() to simplify code.

No functional change.

Tested-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Acked-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v3
- add Damien Le Moal review tag
---
 drivers/pci/endpoint/functions/pci-epf-test.c | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c
index 591d301fa89d8..0f5cf2d795108 100644
--- a/drivers/pci/endpoint/functions/pci-epf-test.c
+++ b/drivers/pci/endpoint/functions/pci-epf-test.c
@@ -182,12 +182,8 @@ static int pci_epf_test_data_transfer(struct pci_epf_test *epf_test,
 		else
 			sconf.src_addr = dma_remote;
 
-		if (dmaengine_slave_config(chan, &sconf)) {
-			dev_err(dev, "DMA slave config fail\n");
-			return -EIO;
-		}
-		tx = dmaengine_prep_slave_single(chan, dma_local, len, dir,
-						 flags);
+		tx = dmaengine_prep_config_single(chan, dma_local, len,
+						  dir, flags, &sconf);
 	} else {
 		tx = dmaengine_prep_dma_memcpy(chan, dma_dst, dma_src, len,
 					       flags);

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v6 4/9] dmaengine: dw-edma: Use new .device_prep_config_sg() callback
  2026-05-20 22:00 [PATCH v6 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank.Li
                   ` (2 preceding siblings ...)
  2026-05-20 22:00 ` [PATCH v6 3/9] PCI: endpoint: pci-epf-test: Use dmaenigne_prep_config_single() to simplify code Frank.Li
@ 2026-05-20 22:00 ` Frank.Li
  2026-05-21  0:31   ` sashiko-bot
  2026-05-20 22:00 ` [PATCH v6 5/9] dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer() Frank.Li
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 22+ messages in thread
From: Frank.Li @ 2026-05-20 22:00 UTC (permalink / raw)
  To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
	Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
	Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
	Niklas Cassel
  Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
	linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li,
	Damien Le Moal

From: Frank Li <Frank.Li@nxp.com>

Use the new .device_prep_config_sg() callback to combine configuration and
descriptor preparation.

No functional changes.

Tested-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v6
- check dw_edma_device_config() return value; find by sashiko AI.
change in v4
- drop context in callback.
change in v3
- add Damien Le Moal review tag
---
 drivers/dma/dw-edma/dw-edma-core.c | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index c2feb3adc79fa..92572dd8131e6 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -577,10 +577,11 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
 }
 
 static struct dma_async_tx_descriptor *
-dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
-			     unsigned int len,
-			     enum dma_transfer_direction direction,
-			     unsigned long flags, void *context)
+dw_edma_device_prep_config_sg(struct dma_chan *dchan, struct scatterlist *sgl,
+			      unsigned int len,
+			      enum dma_transfer_direction direction,
+			      unsigned long flags,
+			      struct dma_slave_config *config)
 {
 	struct dw_edma_transfer xfer;
 
@@ -591,6 +592,9 @@ dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
 	xfer.flags = flags;
 	xfer.type = EDMA_XFER_SCATTER_GATHER;
 
+	if (config && dw_edma_device_config(dchan, config))
+		return NULL;
+
 	return dw_edma_device_transfer(&xfer);
 }
 
@@ -970,7 +974,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
 	dma->device_terminate_all = dw_edma_device_terminate_all;
 	dma->device_issue_pending = dw_edma_device_issue_pending;
 	dma->device_tx_status = dw_edma_device_tx_status;
-	dma->device_prep_slave_sg = dw_edma_device_prep_slave_sg;
+	dma->device_prep_config_sg = dw_edma_device_prep_config_sg;
 	dma->device_prep_dma_cyclic = dw_edma_device_prep_dma_cyclic;
 	dma->device_prep_interleaved_dma = dw_edma_device_prep_interleaved_dma;
 

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v6 5/9] dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer()
  2026-05-20 22:00 [PATCH v6 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank.Li
                   ` (3 preceding siblings ...)
  2026-05-20 22:00 ` [PATCH v6 4/9] dmaengine: dw-edma: Use new .device_prep_config_sg() callback Frank.Li
@ 2026-05-20 22:00 ` Frank.Li
  2026-05-21  0:51   ` sashiko-bot
  2026-05-20 22:00 ` [PATCH v6 6/9] nvmet: pci-epf: Remove unnecessary dmaengine_terminate_sync() on each DMA transfer Frank.Li
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 22+ messages in thread
From: Frank.Li @ 2026-05-20 22:00 UTC (permalink / raw)
  To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
	Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
	Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
	Niklas Cassel
  Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
	linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li

From: Frank Li <Frank.Li@nxp.com>

Pass dma_slave_config to dw_edma_device_transfer() to support atomic
configuration and descriptor preparation when a non-NULL config is
provided to device_prep_config_sg().

Tested-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v3
- rewrite dw_edma_device_slave_config() according to Damien's suggestion.
---
 drivers/dma/dw-edma/dw-edma-core.c | 27 +++++++++++++++++++++------
 1 file changed, 21 insertions(+), 6 deletions(-)

diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 92572dd8131e6..ba37bc983dcd2 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -267,6 +267,20 @@ static int dw_edma_device_config(struct dma_chan *dchan,
 	return 0;
 }
 
+static struct dma_slave_config *
+dw_edma_device_get_config(struct dma_chan *dchan,
+			  struct dma_slave_config *config)
+{
+	struct dw_edma_chan *chan;
+
+	if (config)
+		return config;
+
+	chan = dchan2dw_edma_chan(dchan);
+
+	return &chan->config;
+}
+
 static int dw_edma_device_pause(struct dma_chan *dchan)
 {
 	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
@@ -385,7 +399,8 @@ dw_edma_device_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
 }
 
 static struct dma_async_tx_descriptor *
-dw_edma_device_transfer(struct dw_edma_transfer *xfer)
+dw_edma_device_transfer(struct dw_edma_transfer *xfer,
+			struct dma_slave_config *config)
 {
 	struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
 	enum dma_transfer_direction dir = xfer->direction;
@@ -472,8 +487,8 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
 		src_addr = xfer->xfer.il->src_start;
 		dst_addr = xfer->xfer.il->dst_start;
 	} else {
-		src_addr = chan->config.src_addr;
-		dst_addr = chan->config.dst_addr;
+		src_addr = config->src_addr;
+		dst_addr = config->dst_addr;
 	}
 
 	if (dir == DMA_DEV_TO_MEM)
@@ -595,7 +610,7 @@ dw_edma_device_prep_config_sg(struct dma_chan *dchan, struct scatterlist *sgl,
 	if (config && dw_edma_device_config(dchan, config))
 		return NULL;
 
-	return dw_edma_device_transfer(&xfer);
+	return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, config));
 }
 
 static struct dma_async_tx_descriptor *
@@ -614,7 +629,7 @@ dw_edma_device_prep_dma_cyclic(struct dma_chan *dchan, dma_addr_t paddr,
 	xfer.flags = flags;
 	xfer.type = EDMA_XFER_CYCLIC;
 
-	return dw_edma_device_transfer(&xfer);
+	return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, NULL));
 }
 
 static struct dma_async_tx_descriptor *
@@ -630,7 +645,7 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan,
 	xfer.flags = flags;
 	xfer.type = EDMA_XFER_INTERLEAVED;
 
-	return dw_edma_device_transfer(&xfer);
+	return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, NULL));
 }
 
 static void dw_hdma_set_callback_result(struct virt_dma_desc *vd,

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v6 6/9] nvmet: pci-epf: Remove unnecessary dmaengine_terminate_sync() on each DMA transfer
  2026-05-20 22:00 [PATCH v6 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank.Li
                   ` (4 preceding siblings ...)
  2026-05-20 22:00 ` [PATCH v6 5/9] dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer() Frank.Li
@ 2026-05-20 22:00 ` Frank.Li
  2026-05-20 22:00 ` [PATCH v6 7/9] nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API Frank.Li
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 22+ messages in thread
From: Frank.Li @ 2026-05-20 22:00 UTC (permalink / raw)
  To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
	Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
	Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
	Niklas Cassel
  Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
	linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li,
	Damien Le Moal

From: Frank Li <Frank.Li@nxp.com>

dmaengine_terminate_sync() cancels all pending requests. Calling it for
every DMA transfer is unnecessary and counterproductive. This function is
generally intended for cleanup paths such as module removal, device close,
or unbind operations.

Remove the redundant calls for success path and keep it only at error path.

Tested-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Acked-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
This one also fix stress test failure after remove mutex and use new API
dmaengine_prep_slave_sg_config().
---
 drivers/nvme/target/pci-epf.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c
index 4e9db96ebfecd..2afe8f4d0e461 100644
--- a/drivers/nvme/target/pci-epf.c
+++ b/drivers/nvme/target/pci-epf.c
@@ -420,10 +420,9 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
 	if (dma_sync_wait(chan, cookie) != DMA_COMPLETE) {
 		dev_err(dev, "DMA transfer failed\n");
 		ret = -EIO;
+		dmaengine_terminate_sync(chan);
 	}
 
-	dmaengine_terminate_sync(chan);
-
 unmap:
 	dma_unmap_single(dma_dev, dma_addr, seg->length, dir);
 

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v6 7/9] nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API
  2026-05-20 22:00 [PATCH v6 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank.Li
                   ` (5 preceding siblings ...)
  2026-05-20 22:00 ` [PATCH v6 6/9] nvmet: pci-epf: Remove unnecessary dmaengine_terminate_sync() on each DMA transfer Frank.Li
@ 2026-05-20 22:00 ` Frank.Li
  2026-05-21  1:39   ` sashiko-bot
  2026-05-20 22:00 ` [PATCH v6 8/9] PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code Frank.Li
  2026-05-20 22:00 ` [PATCH v6 9/9] crypto: atmel: Use dmaengine_prep_config_sg() API Frank.Li
  8 siblings, 1 reply; 22+ messages in thread
From: Frank.Li @ 2026-05-20 22:00 UTC (permalink / raw)
  To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
	Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
	Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
	Niklas Cassel
  Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
	linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li

From: Frank Li <Frank.Li@nxp.com>

Use the new dmaengine_prep_config_single_safe() API to combine the
configuration and descriptor preparation into a single call.

Since dmaengine_prep_config_single_safe() performs the configuration and
preparation atomically and the mutex can be removed.

Tested-by: Niklas Cassel <cassel@kernel.org>
Acked-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v6
- remove local unused variable lock (sashika AI)
---
 drivers/nvme/target/pci-epf.c | 21 ++++-----------------
 1 file changed, 4 insertions(+), 17 deletions(-)

diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c
index 2afe8f4d0e461..f917d6ec278b7 100644
--- a/drivers/nvme/target/pci-epf.c
+++ b/drivers/nvme/target/pci-epf.c
@@ -368,18 +368,15 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
 	struct dma_chan *chan;
 	dma_cookie_t cookie;
 	dma_addr_t dma_addr;
-	struct mutex *lock;
 	int ret;
 
 	switch (dir) {
 	case DMA_FROM_DEVICE:
-		lock = &nvme_epf->dma_rx_lock;
 		chan = nvme_epf->dma_rx_chan;
 		sconf.direction = DMA_DEV_TO_MEM;
 		sconf.src_addr = seg->pci_addr;
 		break;
 	case DMA_TO_DEVICE:
-		lock = &nvme_epf->dma_tx_lock;
 		chan = nvme_epf->dma_tx_chan;
 		sconf.direction = DMA_MEM_TO_DEV;
 		sconf.dst_addr = seg->pci_addr;
@@ -388,22 +385,15 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
 		return -EINVAL;
 	}
 
-	mutex_lock(lock);
-
 	dma_dev = dmaengine_get_dma_device(chan);
 	dma_addr = dma_map_single(dma_dev, seg->buf, seg->length, dir);
 	ret = dma_mapping_error(dma_dev, dma_addr);
 	if (ret)
-		goto unlock;
-
-	ret = dmaengine_slave_config(chan, &sconf);
-	if (ret) {
-		dev_err(dev, "Failed to configure DMA channel\n");
-		goto unmap;
-	}
+		return ret;
 
-	desc = dmaengine_prep_slave_single(chan, dma_addr, seg->length,
-					   sconf.direction, DMA_CTRL_ACK);
+	desc = dmaengine_prep_config_single_safe(chan, dma_addr, seg->length,
+						 sconf.direction,
+						 DMA_CTRL_ACK, &sconf);
 	if (!desc) {
 		dev_err(dev, "Failed to prepare DMA\n");
 		ret = -EIO;
@@ -426,9 +416,6 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
 unmap:
 	dma_unmap_single(dma_dev, dma_addr, seg->length, dir);
 
-unlock:
-	mutex_unlock(lock);
-
 	return ret;
 }
 

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v6 8/9] PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code
  2026-05-20 22:00 [PATCH v6 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank.Li
                   ` (6 preceding siblings ...)
  2026-05-20 22:00 ` [PATCH v6 7/9] nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API Frank.Li
@ 2026-05-20 22:00 ` Frank.Li
  2026-05-21  2:08   ` sashiko-bot
  2026-05-20 22:00 ` [PATCH v6 9/9] crypto: atmel: Use dmaengine_prep_config_sg() API Frank.Li
  8 siblings, 1 reply; 22+ messages in thread
From: Frank.Li @ 2026-05-20 22:00 UTC (permalink / raw)
  To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
	Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
	Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
	Niklas Cassel
  Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
	linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li

From: Frank Li <Frank.Li@nxp.com>

Use dmaengine_prep_config_single() to simplify
pci_epf_mhi_edma_read[_sync]() and pci_epf_mhi_edma_write[_sync]().

No functional change.

Tested-by: Niklas Cassel <cassel@kernel.org>
Acked-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
Keep mutex lock because sync with other function.
---
 drivers/pci/endpoint/functions/pci-epf-mhi.c | 52 +++++++++-------------------
 1 file changed, 16 insertions(+), 36 deletions(-)

diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c
index 7f5326925ed54..c3e3b58fb86cd 100644
--- a/drivers/pci/endpoint/functions/pci-epf-mhi.c
+++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c
@@ -328,12 +328,6 @@ static int pci_epf_mhi_edma_read(struct mhi_ep_cntrl *mhi_cntrl,
 	config.direction = DMA_DEV_TO_MEM;
 	config.src_addr = buf_info->host_addr;
 
-	ret = dmaengine_slave_config(chan, &config);
-	if (ret) {
-		dev_err(dev, "Failed to configure DMA channel\n");
-		goto err_unlock;
-	}
-
 	dst_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size,
 				  DMA_FROM_DEVICE);
 	ret = dma_mapping_error(dma_dev, dst_addr);
@@ -342,9 +336,10 @@ static int pci_epf_mhi_edma_read(struct mhi_ep_cntrl *mhi_cntrl,
 		goto err_unlock;
 	}
 
-	desc = dmaengine_prep_slave_single(chan, dst_addr, buf_info->size,
-					   DMA_DEV_TO_MEM,
-					   DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+	desc = dmaengine_prep_config_single(chan, dst_addr, buf_info->size,
+					    DMA_DEV_TO_MEM,
+					    DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
+					    &config);
 	if (!desc) {
 		dev_err(dev, "Failed to prepare DMA\n");
 		ret = -EIO;
@@ -401,12 +396,6 @@ static int pci_epf_mhi_edma_write(struct mhi_ep_cntrl *mhi_cntrl,
 	config.direction = DMA_MEM_TO_DEV;
 	config.dst_addr = buf_info->host_addr;
 
-	ret = dmaengine_slave_config(chan, &config);
-	if (ret) {
-		dev_err(dev, "Failed to configure DMA channel\n");
-		goto err_unlock;
-	}
-
 	src_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size,
 				  DMA_TO_DEVICE);
 	ret = dma_mapping_error(dma_dev, src_addr);
@@ -415,9 +404,10 @@ static int pci_epf_mhi_edma_write(struct mhi_ep_cntrl *mhi_cntrl,
 		goto err_unlock;
 	}
 
-	desc = dmaengine_prep_slave_single(chan, src_addr, buf_info->size,
-					   DMA_MEM_TO_DEV,
-					   DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+	desc = dmaengine_prep_config_single(chan, src_addr, buf_info->size,
+					    DMA_MEM_TO_DEV,
+					    DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
+					    &config);
 	if (!desc) {
 		dev_err(dev, "Failed to prepare DMA\n");
 		ret = -EIO;
@@ -506,12 +496,6 @@ static int pci_epf_mhi_edma_read_async(struct mhi_ep_cntrl *mhi_cntrl,
 	config.direction = DMA_DEV_TO_MEM;
 	config.src_addr = buf_info->host_addr;
 
-	ret = dmaengine_slave_config(chan, &config);
-	if (ret) {
-		dev_err(dev, "Failed to configure DMA channel\n");
-		goto err_unlock;
-	}
-
 	dst_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size,
 				  DMA_FROM_DEVICE);
 	ret = dma_mapping_error(dma_dev, dst_addr);
@@ -520,9 +504,10 @@ static int pci_epf_mhi_edma_read_async(struct mhi_ep_cntrl *mhi_cntrl,
 		goto err_unlock;
 	}
 
-	desc = dmaengine_prep_slave_single(chan, dst_addr, buf_info->size,
-					   DMA_DEV_TO_MEM,
-					   DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+	desc = dmaengine_prep_config_single(chan, dst_addr, buf_info->size,
+					    DMA_DEV_TO_MEM,
+					    DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
+					    &config);
 	if (!desc) {
 		dev_err(dev, "Failed to prepare DMA\n");
 		ret = -EIO;
@@ -585,12 +570,6 @@ static int pci_epf_mhi_edma_write_async(struct mhi_ep_cntrl *mhi_cntrl,
 	config.direction = DMA_MEM_TO_DEV;
 	config.dst_addr = buf_info->host_addr;
 
-	ret = dmaengine_slave_config(chan, &config);
-	if (ret) {
-		dev_err(dev, "Failed to configure DMA channel\n");
-		goto err_unlock;
-	}
-
 	src_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size,
 				  DMA_TO_DEVICE);
 	ret = dma_mapping_error(dma_dev, src_addr);
@@ -599,9 +578,10 @@ static int pci_epf_mhi_edma_write_async(struct mhi_ep_cntrl *mhi_cntrl,
 		goto err_unlock;
 	}
 
-	desc = dmaengine_prep_slave_single(chan, src_addr, buf_info->size,
-					   DMA_MEM_TO_DEV,
-					   DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
+	desc = dmaengine_prep_config_single(chan, src_addr, buf_info->size,
+					    DMA_MEM_TO_DEV,
+					    DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
+					    &config);
 	if (!desc) {
 		dev_err(dev, "Failed to prepare DMA\n");
 		ret = -EIO;

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* [PATCH v6 9/9] crypto: atmel: Use dmaengine_prep_config_sg() API
  2026-05-20 22:00 [PATCH v6 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank.Li
                   ` (7 preceding siblings ...)
  2026-05-20 22:00 ` [PATCH v6 8/9] PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code Frank.Li
@ 2026-05-20 22:00 ` Frank.Li
  2026-05-21  2:33   ` sashiko-bot
  8 siblings, 1 reply; 22+ messages in thread
From: Frank.Li @ 2026-05-20 22:00 UTC (permalink / raw)
  To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
	Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
	Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
	Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
	Niklas Cassel
  Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
	linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li

From: Frank Li <Frank.Li@nxp.com>

Using new API dmaengine_prep_config_sg() to simple code.

dmaengine_prep_config_sg() does not distinguish between configuration
failures and descriptor preparation failures, as both are reported through
a NULL return value. Converting both cases to -ENOMEM is therefore
acceptable and consistent with the helper's abstraction.

In practice, most users only care whether the operation succeeds or fails,
and do not depend on the exact errno value returned from this path.

Tested-by: Niklas Cassel <cassel@kernel.org>
Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v6
- add commit message about error propagation (sashaki AI)
---
 drivers/crypto/atmel-aes.c | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c
index b393689400b4c..d890b5a277b9c 100644
--- a/drivers/crypto/atmel-aes.c
+++ b/drivers/crypto/atmel-aes.c
@@ -795,7 +795,6 @@ static int atmel_aes_dma_transfer_start(struct atmel_aes_dev *dd,
 	struct dma_slave_config config;
 	dma_async_tx_callback callback;
 	struct atmel_aes_dma *dma;
-	int err;
 
 	memset(&config, 0, sizeof(config));
 	config.src_addr_width = addr_width;
@@ -820,12 +819,9 @@ static int atmel_aes_dma_transfer_start(struct atmel_aes_dev *dd,
 		return -EINVAL;
 	}
 
-	err = dmaengine_slave_config(dma->chan, &config);
-	if (err)
-		return err;
-
-	desc = dmaengine_prep_slave_sg(dma->chan, dma->sg, dma->sg_len, dir,
-				       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	desc = dmaengine_prep_config_sg(dma->chan, dma->sg, dma->sg_len, dir,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK,
+					&config);
 	if (!desc)
 		return -ENOMEM;
 

-- 
2.43.0


^ permalink raw reply related	[flat|nested] 22+ messages in thread

* Re: [PATCH v6 2/9] dmaengine: Add safe API to combine configuration and preparation
  2026-05-20 22:00 ` [PATCH v6 2/9] dmaengine: Add safe API to combine configuration and preparation Frank.Li
@ 2026-05-21  0:01   ` sashiko-bot
  2026-05-21 14:56     ` Frank Li
  0 siblings, 1 reply; 22+ messages in thread
From: sashiko-bot @ 2026-05-21  0:01 UTC (permalink / raw)
  To: Frank.Li; +Cc: dmaengine, linux-pci, Frank.Li, vkoul, imx

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] Stack-allocated scatterlist in dmaengine_prep_config_single_safe() leads to use-after-free when consumed asynchronously by DMA drivers.
--

commit 7f280444e5ae16961e6f77cb674cd3ac061779a0
Author: Frank Li <Frank.Li@nxp.com>

dmaengine: Add safe API to combine configuration and preparation

This commit introduces two helpers to combine DMA slave configuration and
transfer preparation atomically. Drivers can either provide a new callback
or the helpers will serialize the operations using a per-channel spinlock.

> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index defa377d2ef54..6fe46c0c94527 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
[ ... ]
> +static inline struct dma_async_tx_descriptor *
> +dmaengine_prep_config_single_safe(struct dma_chan *chan, dma_addr_t buf,
> +				  size_t len, enum dma_transfer_direction dir,
> +				  unsigned long flags,
> +				  struct dma_slave_config *config)
> +{
> +	struct scatterlist sg;
> +
> +	sg_init_table(&sg, 1);
> +	sg_dma_address(&sg) = buf;
> +	sg_dma_len(&sg) = len;
> +
> +	return dmaengine_prep_config_sg_safe(chan, &sg, 1, dir, flags, config);

[Severity: Critical]
Can this lead to a use-after-free if the DMA driver consumes the scatterlist
asynchronously?

The scatterlist sg is allocated on the stack here. While some drivers might
process the scatterlist synchronously during the device_prep_slave_sg or
device_prep_config_sg callbacks, others (such as qdma) may save the sgl
pointer internally to process later during tx_submit or hardware enqueue.

By the time the driver accesses the saved sgl pointer, this function will
have returned and the stack frame will be destroyed.

> +}
> +
>  #ifdef CONFIG_RAPIDIO_DMA_ENGINE

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260520-dma_prep_config-v6-0-06e49b7acb38@nxp.com?part=2

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v6 4/9] dmaengine: dw-edma: Use new .device_prep_config_sg() callback
  2026-05-20 22:00 ` [PATCH v6 4/9] dmaengine: dw-edma: Use new .device_prep_config_sg() callback Frank.Li
@ 2026-05-21  0:31   ` sashiko-bot
  2026-05-21 14:59     ` Frank Li
  0 siblings, 1 reply; 22+ messages in thread
From: sashiko-bot @ 2026-05-21  0:31 UTC (permalink / raw)
  To: Frank.Li; +Cc: vkoul, Frank.Li, linux-pci, imx, dmaengine

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] The newly added `dw_edma_device_prep_config_sg` callback modifies shared channel state locklessly, violating dmaengine API atomicity guarantees and potentially corrupting active hardware transfers.
--

commit 3f734236aec2497e34a2baf98f60bcd0cd1c6525
Author: Frank Li <Frank.Li@nxp.com>
dmaengine: dw-edma: Use new .device_prep_config_sg() callback

This commit replaces the device_prep_slave_sg callback with the new
device_prep_config_sg callback to combine configuration and descriptor
preparation.

> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index c2feb3adc79fa..92572dd8131e6 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
[ ... ]
> @@ -577,10 +577,11 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
>  }
>  
>  static struct dma_async_tx_descriptor *
> -dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
> -			     unsigned int len,
> -			     enum dma_transfer_direction direction,
> -			     unsigned long flags, void *context)
> +dw_edma_device_prep_config_sg(struct dma_chan *dchan, struct scatterlist *sgl,
> +			      unsigned int len,
> +			      enum dma_transfer_direction direction,
> +			      unsigned long flags,
> +			      struct dma_slave_config *config)
>  {
>  	struct dw_edma_transfer xfer;
>  
> @@ -591,6 +592,9 @@ dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
>  	xfer.flags = flags;
>  	xfer.type = EDMA_XFER_SCATTER_GATHER;
>  
> +	if (config && dw_edma_device_config(dchan, config))
> +		return NULL;

[Severity: High]
Does this introduce a data race on the shared channel state?

By implementing the device_prep_config_sg callback, the driver bypasses
the dmaengine core's per-channel spinlock, which normally protects
configuration and preparation.

However, dw_edma_device_config() modifies global channel state locklessly:

static int dw_edma_device_config(...)
{
        ...
        chan->non_ll = false;
        ...
        if (cfg_non_ll || non_ll)
                chan->non_ll = true;
        ...
        memcpy(&chan->config, config, sizeof(*config));
        ...
}

If multiple threads queue transfers concurrently, could this corrupt the
descriptor configuration?

More importantly, if a new transfer is prepared while the channel is actively
executing, the interrupt handler might read the momentarily cleared or
overwritten chan->non_ll.

Could this cause the interrupt handler to invoke the wrong hardware routine
and desynchronize the hardware state?

> +
>  	return dw_edma_device_transfer(&xfer);
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260520-dma_prep_config-v6-0-06e49b7acb38@nxp.com?part=4

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v6 5/9] dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer()
  2026-05-20 22:00 ` [PATCH v6 5/9] dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer() Frank.Li
@ 2026-05-21  0:51   ` sashiko-bot
  2026-05-21 15:02     ` Frank Li
  0 siblings, 1 reply; 22+ messages in thread
From: sashiko-bot @ 2026-05-21  0:51 UTC (permalink / raw)
  To: Frank.Li; +Cc: vkoul, Frank.Li, imx, linux-pci, dmaengine

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

New issues:
- [High] The patch fails to provide atomic configuration because dw_edma_device_prep_config_sg() calls dw_edma_device_config(), mutating global channel state without synchronization.

Pre-existing issues:
- [High] Channel control functions like dw_edma_device_pause() modify state without vc.lock, causing a TOCTOU race that permanently hangs the channel.
--

commit 248bbd394732f32660b56f83243ae2005c3e1952
Author: Frank Li <Frank.Li@nxp.com>

dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer()

This commit passes dma_slave_config to dw_edma_device_transfer() to enable
atomic configuration and descriptor preparation when a non-NULL config is
provided to device_prep_config_sg().

Tested-by: Niklas Cassel <cassel@kernel.org>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>

> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index 92572dd8131e6..ba37bc983dcd2 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
[ ... ]
> @@ -267,6 +267,20 @@ static int dw_edma_device_config(struct dma_chan *dchan,
>  	return 0;
>  }
>  
> +static struct dma_slave_config *
> +dw_edma_device_get_config(struct dma_chan *dchan,
> +			  struct dma_slave_config *config)
> +{
> +	struct dw_edma_chan *chan;
> +
> +	if (config)
> +		return config;
> +
> +	chan = dchan2dw_edma_chan(dchan);
> +
> +	return &chan->config;
> +}
> +
>  static int dw_edma_device_pause(struct dma_chan *dchan)
>  {
>  	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);

[Severity: High]
This is a pre-existing issue, but does this function have a TOCTOU race
condition?

Because chan->status and chan->request are checked and modified without
acquiring chan->vc.lock:

drivers/dma/dw-edma/dw-edma-core.c:dw_edma_device_pause() {
    ...
    if (!chan->configured)
        err = -EPERM;
    else if (chan->status != EDMA_ST_BUSY)
        err = -EPERM;
    else if (chan->request != EDMA_REQ_NONE)
        err = -EPERM;
    else
        chan->request = EDMA_REQ_PAUSE;
    ...
}

Could the interrupt handler dw_edma_done_interrupt() run concurrently
(with vc.lock held) and transition chan->status to EDMA_ST_IDLE before
chan->request is set to EDMA_REQ_PAUSE?

If that happens, the channel would be left in an invalid state (IDLE with a
PAUSE request), which would cause subsequent calls to dw_edma_device_resume()
and dw_edma_device_issue_pending() to fail permanently.

[ ... ]
> @@ -595,7 +610,7 @@ dw_edma_device_prep_config_sg(struct dma_chan *dchan, struct scatterlist *sgl,
>  	if (config && dw_edma_device_config(dchan, config))
>  		return NULL;
>  
> -	return dw_edma_device_transfer(&xfer);
> +	return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, config));
>  }

[Severity: High]
Is a lockless mutation of global channel state still occurring here?

The commit message mentions supporting atomic configuration, but
dw_edma_device_config() is still called here before dw_edma_device_transfer().

Looking at dw_edma_device_config(), it modifies chan->non_ll and performs
a lockless memcpy over chan->config:

drivers/dma/dw-edma/dw-edma-core.c:dw_edma_device_config() {
    ...
    if (cfg_non_ll || non_ll)
        chan->non_ll = true;
    ...
    memcpy(&chan->config, config, sizeof(*config));
    chan->configured = true;
    ...
}

Will concurrent calls to prepare descriptors data race on chan->config and
chan->non_ll, corrupting the transfer configuration?

Additionally, dw_edma_device_transfer() still incorrectly relies on the
mutated chan->non_ll to calculate bursts_max.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260520-dma_prep_config-v6-0-06e49b7acb38@nxp.com?part=5

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v6 7/9] nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API
  2026-05-20 22:00 ` [PATCH v6 7/9] nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API Frank.Li
@ 2026-05-21  1:39   ` sashiko-bot
  2026-05-21 15:08     ` Frank Li
  0 siblings, 1 reply; 22+ messages in thread
From: sashiko-bot @ 2026-05-21  1:39 UTC (permalink / raw)
  To: Frank.Li; +Cc: dmaengine, Frank.Li, imx, linux-pci, vkoul

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] Removing the mutex introduces a cascading teardown failure and use-after-free where a single DMA timeout forcefully terminates all concurrent transfers on the shared channel.
- [High] Massive CPU wastage and starvation due to un-serialized concurrent busy-waiting on the DMA channel.
- [Low] Unused mutex fields and their initialization/teardown code were left behind in the structure and lifecycle functions.
--

commit 9bda24c8bcaef3ff0459f6035b72408052dec87b
Author: Frank Li <Frank.Li@nxp.com>

nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API

This commit replaces dmaengine_slave_config() and dmaengine_prep_slave_single()
with dmaengine_prep_config_single_safe(), combining the configuration and
preparation into a single call. It also removes the mutex locks under the
assumption that atomic configuration makes them unnecessary.

> diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c
> index 2afe8f4d0e461..f917d6ec278b7 100644
> --- a/drivers/nvme/target/pci-epf.c
> +++ b/drivers/nvme/target/pci-epf.c
> @@ -368,18 +368,15 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
>  	struct dma_chan *chan;
>  	dma_cookie_t cookie;
>  	dma_addr_t dma_addr;
> -	struct mutex *lock;
>  	int ret;
>  
>  	switch (dir) {
>  	case DMA_FROM_DEVICE:
> -		lock = &nvme_epf->dma_rx_lock;
>  		chan = nvme_epf->dma_rx_chan;
>  		sconf.direction = DMA_DEV_TO_MEM;
>  		sconf.src_addr = seg->pci_addr;
>  		break;
>  	case DMA_TO_DEVICE:
> -		lock = &nvme_epf->dma_tx_lock;

[Severity: Low]
Are the dma_tx_lock and dma_rx_lock fields still needed in struct nvmet_pci_epf?

Since the locks are no longer used here, it seems the field declarations
and their initialization/teardown code might have been left behind.

>  		chan = nvme_epf->dma_tx_chan;
>  		sconf.direction = DMA_MEM_TO_DEV;
>  		sconf.dst_addr = seg->pci_addr;

[ ... ]

> @@ -388,22 +385,15 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
>  		return -EINVAL;
>  	}
>  
> -	mutex_lock(lock);
> -

[Severity: High]
Does removing the mutex introduce concurrent busy-waiting on the shared DMA
channel?

dma_sync_wait() internally uses a cpu_relax() loop to busy-wait for hardware
completion. Before this patch, the mutex ensured only one thread actively
polled the hardware at a time, while other concurrent workers slept efficiently
on mutex_lock().

By removing the mutex, it appears multiple worker threads could concurrently
enter dma_sync_wait() on the same channel, unnecessarily consuming 100% of
multiple CPU cores and potentially causing CPU exhaustion.

[Severity: High]
Can removing the mutex also lead to a cascading teardown failure and
use-after-free?

Because the mutex was removed, multiple worker threads can concurrently submit
and wait on the shared dma_tx_chan or dma_rx_chan. If one transfer times out
in dma_sync_wait(), the error path calls dmaengine_terminate_sync(chan).

It looks like this would forcefully abort the shared channel, destroying all
other active and pending descriptors belonging to concurrent NVMe commands.

Also, if Thread A calls dmaengine_terminate_sync(chan) while Thread B is
between dmaengine_prep_config_single_safe() and dmaengine_submit():

Thread B:
	desc = dmaengine_prep_config_single_safe(...)
	// Thread A calls dmaengine_terminate_sync() here
	cookie = dmaengine_submit(desc);

Could Thread B's descriptor be freed by the termination, resulting in a
use-after-free when Thread B attempts to submit it?

>  	dma_dev = dmaengine_get_dma_device(chan);
>  	dma_addr = dma_map_single(dma_dev, seg->buf, seg->length, dir);
>  	ret = dma_mapping_error(dma_dev, dma_addr);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260520-dma_prep_config-v6-0-06e49b7acb38@nxp.com?part=7

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v6 8/9] PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code
  2026-05-20 22:00 ` [PATCH v6 8/9] PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code Frank.Li
@ 2026-05-21  2:08   ` sashiko-bot
  2026-05-21 15:09     ` Frank Li
  0 siblings, 1 reply; 22+ messages in thread
From: sashiko-bot @ 2026-05-21  2:08 UTC (permalink / raw)
  To: Frank.Li; +Cc: dmaengine, linux-pci, imx, Frank.Li, vkoul

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

Pre-existing issues:
- [High] The DMA workqueue is destroyed before pending DMA transfers are explicitly terminated, which can lead to a Use-After-Free if a DMA callback executes late.
--

commit 01fd36b3138418658a89651874d85c30619199e8
Author: Frank Li <Frank.Li@nxp.com>

PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code

This commit replaces dmaengine_slave_config() and dmaengine_prep_slave_single()
with a single call to dmaengine_prep_config_single() in the EDMA read and write
paths. The change aims to simplify the code without introducing any functional
differences.

> diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c
> index 7f5326925ed54..c3e3b58fb86cd 100644
> --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c
> +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c
[ ... ]
> @@ -506,12 +496,6 @@ static int pci_epf_mhi_edma_read_async(struct mhi_ep_cntrl *mhi_cntrl,
>  	config.direction = DMA_DEV_TO_MEM;
>  	config.src_addr = buf_info->host_addr;
>  
> -	ret = dmaengine_slave_config(chan, &config);
> -	if (ret) {
> -		dev_err(dev, "Failed to configure DMA channel\n");
> -		goto err_unlock;
> -	}
> -
>  	dst_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size,
>  				  DMA_FROM_DEVICE);
>  	ret = dma_mapping_error(dma_dev, dst_addr);
> @@ -520,9 +504,10 @@ static int pci_epf_mhi_edma_read_async(struct mhi_ep_cntrl *mhi_cntrl,
>  		goto err_unlock;
>  	}
>  
> -	desc = dmaengine_prep_slave_single(chan, dst_addr, buf_info->size,
> -					   DMA_DEV_TO_MEM,
> -					   DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
> +	desc = dmaengine_prep_config_single(chan, dst_addr, buf_info->size,
> +					    DMA_DEV_TO_MEM,
> +					    DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
> +					    &config);

[Severity: High]
This is a pre-existing issue, but I noticed a potential problem with how these
asynchronous DMA transfers are cleaned up.

In pci_epf_mhi_dma_deinit(), the DMA workqueue is destroyed before pending DMA
transfers are explicitly terminated:

static void pci_epf_mhi_dma_deinit(struct pci_epf_mhi *epf_mhi)
{
	destroy_workqueue(epf_mhi->dma_wq);
	dma_release_channel(epf_mhi->dma_chan_tx);
	dma_release_channel(epf_mhi->dma_chan_rx);
	...
}

Does this sequence lead to a use-after-free if a DMA callback executes late?

If a transfer is physically still running in the DMA engine hardware when the
deinitialization begins, destroy_workqueue() will flush existing queued work
but cannot stop the hardware itself.

If the hardware completes the transfer a moment later, the DMA engine
interrupt will fire and invoke pci_epf_mhi_dma_async_callback(), which
blindly calls queue_work(epf_mhi->dma_wq, ...).

Should dmaengine_terminate_sync() be called to abort pending asynchronous
DMA transfers on all active channels before destroying the workqueue?

>  	if (!desc) {
>  		dev_err(dev, "Failed to prepare DMA\n");
>  		ret = -EIO;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260520-dma_prep_config-v6-0-06e49b7acb38@nxp.com?part=8

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v6 9/9] crypto: atmel: Use dmaengine_prep_config_sg() API
  2026-05-20 22:00 ` [PATCH v6 9/9] crypto: atmel: Use dmaengine_prep_config_sg() API Frank.Li
@ 2026-05-21  2:33   ` sashiko-bot
  2026-05-21 15:11     ` Frank Li
  0 siblings, 1 reply; 22+ messages in thread
From: sashiko-bot @ 2026-05-21  2:33 UTC (permalink / raw)
  To: Frank.Li; +Cc: dmaengine, vkoul, Frank.Li, linux-pci, imx

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

Pre-existing issues:
- [High] Tasklets can be rescheduled after they are killed during device removal, leading to a Use-After-Free.
- [High] The device unbind path does not wait for active crypto transforms (TFMs) to be freed, leading to a Use-After-Free.
- [High] Pending requests in the crypto queue are not completed or flushed during device removal.
- [High] Calling `dmaengine_terminate_sync()` from atomic context (tasklet/softirq) in the error path.
--

commit 8fb292764f2c30a42b59a7ee1bce9dcae3cc201f
Author: Frank Li <Frank.Li@nxp.com>

crypto: atmel: Use dmaengine_prep_config_sg() API

This commit simplifies the driver by replacing dmaengine_slave_config()
and dmaengine_prep_slave_sg() with the combined dmaengine_prep_config_sg()
API. It converts both configuration and descriptor preparation failures to
return -ENOMEM.

> diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c
> index b393689400b4c..d890b5a277b9c 100644
> --- a/drivers/crypto/atmel-aes.c
> +++ b/drivers/crypto/atmel-aes.c
> @@ -795,7 +795,6 @@ static int atmel_aes_dma_transfer_start(struct atmel_aes_dev *dd,
[ ... ]
> -	err = dmaengine_slave_config(dma->chan, &config);
> -	if (err)
> -		return err;
> -
> -	desc = dmaengine_prep_slave_sg(dma->chan, dma->sg, dma->sg_len, dir,
> -				       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> +	desc = dmaengine_prep_config_sg(dma->chan, dma->sg, dma->sg_len, dir,
> +					DMA_PREP_INTERRUPT | DMA_CTRL_ACK,
> +					&config);
>  	if (!desc)
>  		return -ENOMEM;

[Severity: High]
This is a pre-existing issue, but can this error path lead to a kernel BUG
by sleeping in an atomic context?

If dmaengine_prep_config_sg() fails and returns NULL, it seems the failure
propagates up to atmel_aes_dma_start() which might jump to an error label
and call dmaengine_terminate_sync().

Since the crypto request queue is processed via atmel_aes_queue_task()
(a tasklet running in softirq context), wouldn't calling
dmaengine_terminate_sync() (which uses might_sleep()) from this atomic
context cause issues?

[Severity: High]
This is a pre-existing issue, but does device removal have a use-after-free
vulnerability if a DMA transfer completes concurrently?

In atmel_aes_remove(), tasklet_kill() is called before
atmel_aes_dma_cleanup(). If a DMA transfer completes immediately after
tasklet_kill(), the interrupt callback (atmel_aes_dma_callback()) could
execute and schedule the queue_task tasklet again via tasklet_schedule().

Since the aes_dd driver data structure is managed via devm_kzalloc() and
freed upon return from atmel_aes_remove(), would the tasklet subsequently
access freed memory when it runs?

[Severity: High]
This is a pre-existing issue, but does the unbind path properly wait for
active crypto transforms (TFMs) to be freed?

In atmel_aes_remove(), the driver unregisters its algorithms using
crypto_unregister_algs(). This prevents new TFMs from being instantiated,
but it doesn't appear to revoke or wait for active TFMs to be freed.

Because aes_dd is allocated using devm_kzalloc() and freed once
atmel_aes_remove() completes, could subsequent crypto operations triggered
by userspace via remaining TFMs dereference the freed aes_dd structure?

[Severity: High]
This is a pre-existing issue, but are pending requests in the crypto queue
completed or flushed during device removal?

During device unbind and atmel_aes_remove(), it appears the driver never
purges, aborts, or flushes the crypto requests currently waiting in
aes_dd->queue.

When the device structure is freed by devres, wouldn't all queued
asynchronous requests be lost without invoking their completion callbacks,
causing callers waiting on wait_for_completion() to hang indefinitely?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260520-dma_prep_config-v6-0-06e49b7acb38@nxp.com?part=9

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v6 2/9] dmaengine: Add safe API to combine configuration and preparation
  2026-05-21  0:01   ` sashiko-bot
@ 2026-05-21 14:56     ` Frank Li
  0 siblings, 0 replies; 22+ messages in thread
From: Frank Li @ 2026-05-21 14:56 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: Frank.Li, dmaengine, linux-pci, Frank.Li, vkoul, imx

On Thu, May 21, 2026 at 12:01:04AM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [Critical] Stack-allocated scatterlist in dmaengine_prep_config_single_safe() leads to use-after-free when consumed asynchronously by DMA drivers.
> --
>
> commit 7f280444e5ae16961e6f77cb674cd3ac061779a0
> Author: Frank Li <Frank.Li@nxp.com>
>
> dmaengine: Add safe API to combine configuration and preparation
>
> This commit introduces two helpers to combine DMA slave configuration and
> transfer preparation atomically. Drivers can either provide a new callback
> or the helpers will serialize the operations using a per-channel spinlock.
>
> > diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> > index defa377d2ef54..6fe46c0c94527 100644
> > --- a/include/linux/dmaengine.h
> > +++ b/include/linux/dmaengine.h
> [ ... ]
> > +static inline struct dma_async_tx_descriptor *
> > +dmaengine_prep_config_single_safe(struct dma_chan *chan, dma_addr_t buf,
> > +				  size_t len, enum dma_transfer_direction dir,
> > +				  unsigned long flags,
> > +				  struct dma_slave_config *config)
> > +{
> > +	struct scatterlist sg;
> > +
> > +	sg_init_table(&sg, 1);
> > +	sg_dma_address(&sg) = buf;
> > +	sg_dma_len(&sg) = len;
> > +
> > +	return dmaengine_prep_config_sg_safe(chan, &sg, 1, dir, flags, config);
>
> [Severity: Critical]
> Can this lead to a use-after-free if the DMA driver consumes the scatterlist
> asynchronously?
>
> The scatterlist sg is allocated on the stack here. While some drivers might
> process the scatterlist synchronously during the device_prep_slave_sg or
> device_prep_config_sg callbacks, others (such as qdma) may save the sgl
> pointer internally to process later during tx_submit or hardware enqueue.
>
> By the time the driver accesses the saved sgl pointer, this function will
> have returned and the stack frame will be destroyed.

This is NOT this patch's problem. exiting prep_single() use the same method.
dmaengine callback implement should save these informaiton into their
dma_async_tx_descriptor, can't direct use caller's memory.

But this point is also important and easy to be missed. suggest add it to
dmaegine documentation.

Frank

>
> > +}
> > +
> >  #ifdef CONFIG_RAPIDIO_DMA_ENGINE
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260520-dma_prep_config-v6-0-06e49b7acb38@nxp.com?part=2

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v6 4/9] dmaengine: dw-edma: Use new .device_prep_config_sg() callback
  2026-05-21  0:31   ` sashiko-bot
@ 2026-05-21 14:59     ` Frank Li
  0 siblings, 0 replies; 22+ messages in thread
From: Frank Li @ 2026-05-21 14:59 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: Frank.Li, vkoul, Frank.Li, linux-pci, imx, dmaengine

On Thu, May 21, 2026 at 12:31:59AM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
> - [High] The newly added `dw_edma_device_prep_config_sg` callback modifies shared channel state locklessly, violating dmaengine API atomicity guarantees and potentially corrupting active hardware transfers.
> --
>
> commit 3f734236aec2497e34a2baf98f60bcd0cd1c6525
> Author: Frank Li <Frank.Li@nxp.com>
> dmaengine: dw-edma: Use new .device_prep_config_sg() callback
>
> This commit replaces the device_prep_slave_sg callback with the new
> device_prep_config_sg callback to combine configuration and descriptor
> preparation.
>
> > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > index c2feb3adc79fa..92572dd8131e6 100644
> > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> [ ... ]
> > @@ -577,10 +577,11 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
> >  }
> >
> >  static struct dma_async_tx_descriptor *
> > -dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
> > -			     unsigned int len,
> > -			     enum dma_transfer_direction direction,
> > -			     unsigned long flags, void *context)
> > +dw_edma_device_prep_config_sg(struct dma_chan *dchan, struct scatterlist *sgl,
> > +			      unsigned int len,
> > +			      enum dma_transfer_direction direction,
> > +			      unsigned long flags,
> > +			      struct dma_slave_config *config)
> >  {
> >  	struct dw_edma_transfer xfer;
> >
> > @@ -591,6 +592,9 @@ dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
> >  	xfer.flags = flags;
> >  	xfer.type = EDMA_XFER_SCATTER_GATHER;
> >
> > +	if (config && dw_edma_device_config(dchan, config))
> > +		return NULL;
>
> [Severity: High]
> Does this introduce a data race on the shared channel state?
>
> By implementing the device_prep_config_sg callback, the driver bypasses
> the dmaengine core's per-channel spinlock, which normally protects
> configuration and preparation.
>
> However, dw_edma_device_config() modifies global channel state locklessly:
>
> static int dw_edma_device_config(...)
> {
>         ...
>         chan->non_ll = false;
>         ...
>         if (cfg_non_ll || non_ll)
>                 chan->non_ll = true;
>         ...
>         memcpy(&chan->config, config, sizeof(*config));
>         ...
> }
>
> If multiple threads queue transfers concurrently, could this corrupt the
> descriptor configuration?
>
> More importantly, if a new transfer is prepared while the channel is actively
> executing, the interrupt handler might read the momentarily cleared or
> overwritten chan->non_ll.
>
> Could this cause the interrupt handler to invoke the wrong hardware routine
> and desynchronize the hardware state?

This case never happen. change->non_ll only happen once after request
channel.

Frank

>
> > +
> >  	return dw_edma_device_transfer(&xfer);
> >  }
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260520-dma_prep_config-v6-0-06e49b7acb38@nxp.com?part=4

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v6 5/9] dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer()
  2026-05-21  0:51   ` sashiko-bot
@ 2026-05-21 15:02     ` Frank Li
  0 siblings, 0 replies; 22+ messages in thread
From: Frank Li @ 2026-05-21 15:02 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: Frank.Li, vkoul, Frank.Li, imx, linux-pci, dmaengine

On Thu, May 21, 2026 at 12:51:26AM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> New issues:
> - [High] The patch fails to provide atomic configuration because dw_edma_device_prep_config_sg() calls dw_edma_device_config(), mutating global channel state without synchronization.
>
> Pre-existing issues:
> - [High] Channel control functions like dw_edma_device_pause() modify state without vc.lock, causing a TOCTOU race that permanently hangs the channel.
> --
>
> commit 248bbd394732f32660b56f83243ae2005c3e1952
> Author: Frank Li <Frank.Li@nxp.com>
>
> dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer()
>
> This commit passes dma_slave_config to dw_edma_device_transfer() to enable
> atomic configuration and descriptor preparation when a non-NULL config is
> provided to device_prep_config_sg().
>
> Tested-by: Niklas Cassel <cassel@kernel.org>
> Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
>
> > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > index 92572dd8131e6..ba37bc983dcd2 100644
> > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> [ ... ]
> > @@ -267,6 +267,20 @@ static int dw_edma_device_config(struct dma_chan *dchan,
> >  	return 0;
> >  }
> >
> > +static struct dma_slave_config *
> > +dw_edma_device_get_config(struct dma_chan *dchan,
> > +			  struct dma_slave_config *config)
> > +{
> > +	struct dw_edma_chan *chan;
> > +
> > +	if (config)
> > +		return config;
> > +
> > +	chan = dchan2dw_edma_chan(dchan);
> > +
> > +	return &chan->config;
> > +}
> > +
> >  static int dw_edma_device_pause(struct dma_chan *dchan)
> >  {
> >  	struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
>
> [Severity: High]
> This is a pre-existing issue, but does this function have a TOCTOU race
> condition?


Need fix later.

>
> Because chan->status and chan->request are checked and modified without
> acquiring chan->vc.lock:
>
> drivers/dma/dw-edma/dw-edma-core.c:dw_edma_device_pause() {
>     ...
>     if (!chan->configured)
>         err = -EPERM;
>     else if (chan->status != EDMA_ST_BUSY)
>         err = -EPERM;
>     else if (chan->request != EDMA_REQ_NONE)
>         err = -EPERM;
>     else
>         chan->request = EDMA_REQ_PAUSE;
>     ...
> }
>
> Could the interrupt handler dw_edma_done_interrupt() run concurrently
> (with vc.lock held) and transition chan->status to EDMA_ST_IDLE before
> chan->request is set to EDMA_REQ_PAUSE?
>
> If that happens, the channel would be left in an invalid state (IDLE with a
> PAUSE request), which would cause subsequent calls to dw_edma_device_resume()
> and dw_edma_device_issue_pending() to fail permanently.
>
> [ ... ]
> > @@ -595,7 +610,7 @@ dw_edma_device_prep_config_sg(struct dma_chan *dchan, struct scatterlist *sgl,
> >  	if (config && dw_edma_device_config(dchan, config))
> >  		return NULL;
> >
> > -	return dw_edma_device_transfer(&xfer);
> > +	return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, config));
> >  }
>
> [Severity: High]
> Is a lockless mutation of global channel state still occurring here?
>
> The commit message mentions supporting atomic configuration, but
> dw_edma_device_config() is still called here before dw_edma_device_transfer().
>
> Looking at dw_edma_device_config(), it modifies chan->non_ll and performs
> a lockless memcpy over chan->config:
>
> drivers/dma/dw-edma/dw-edma-core.c:dw_edma_device_config() {
>     ...
>     if (cfg_non_ll || non_ll)
>         chan->non_ll = true;
>     ...
>     memcpy(&chan->config, config, sizeof(*config));
>     chan->configured = true;
>     ...
> }
>
> Will concurrent calls to prepare descriptors data race on chan->config and
> chan->non_ll, corrupting the transfer configuration?

Never happen. Change non_ll only happen once after request channel.

Frank

>
> Additionally, dw_edma_device_transfer() still incorrectly relies on the
> mutated chan->non_ll to calculate bursts_max.
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260520-dma_prep_config-v6-0-06e49b7acb38@nxp.com?part=5

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v6 7/9] nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API
  2026-05-21  1:39   ` sashiko-bot
@ 2026-05-21 15:08     ` Frank Li
  0 siblings, 0 replies; 22+ messages in thread
From: Frank Li @ 2026-05-21 15:08 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: Frank.Li, dmaengine, Frank.Li, imx, linux-pci, vkoul

On Thu, May 21, 2026 at 01:39:56AM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> - [High] Removing the mutex introduces a cascading teardown failure and use-after-free where a single DMA timeout forcefully terminates all concurrent transfers on the shared channel.
> - [High] Massive CPU wastage and starvation due to un-serialized concurrent busy-waiting on the DMA channel.
> - [Low] Unused mutex fields and their initialization/teardown code were left behind in the structure and lifecycle functions.
> --
>
> commit 9bda24c8bcaef3ff0459f6035b72408052dec87b
> Author: Frank Li <Frank.Li@nxp.com>
>
> nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API
>
> This commit replaces dmaengine_slave_config() and dmaengine_prep_slave_single()
> with dmaengine_prep_config_single_safe(), combining the configuration and
> preparation into a single call. It also removes the mutex locks under the
> assumption that atomic configuration makes them unnecessary.
>
> > diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c
> > index 2afe8f4d0e461..f917d6ec278b7 100644
> > --- a/drivers/nvme/target/pci-epf.c
> > +++ b/drivers/nvme/target/pci-epf.c
> > @@ -368,18 +368,15 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
> >  	struct dma_chan *chan;
> >  	dma_cookie_t cookie;
> >  	dma_addr_t dma_addr;
> > -	struct mutex *lock;
> >  	int ret;
> >
> >  	switch (dir) {
> >  	case DMA_FROM_DEVICE:
> > -		lock = &nvme_epf->dma_rx_lock;
> >  		chan = nvme_epf->dma_rx_chan;
> >  		sconf.direction = DMA_DEV_TO_MEM;
> >  		sconf.src_addr = seg->pci_addr;
> >  		break;
> >  	case DMA_TO_DEVICE:
> > -		lock = &nvme_epf->dma_tx_lock;
>
> [Severity: Low]
> Are the dma_tx_lock and dma_rx_lock fields still needed in struct nvmet_pci_epf?
>
> Since the locks are no longer used here, it seems the field declarations
> and their initialization/teardown code might have been left behind.

yes, let me remove at next version.

>
> >  		chan = nvme_epf->dma_tx_chan;
> >  		sconf.direction = DMA_MEM_TO_DEV;
> >  		sconf.dst_addr = seg->pci_addr;
>
> [ ... ]
>
> > @@ -388,22 +385,15 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
> >  		return -EINVAL;
> >  	}
> >
> > -	mutex_lock(lock);
> > -
>
> [Severity: High]
> Does removing the mutex introduce concurrent busy-waiting on the shared DMA
> channel?
>
> dma_sync_wait() internally uses a cpu_relax() loop to busy-wait for hardware
> completion. Before this patch, the mutex ensured only one thread actively
> polled the hardware at a time, while other concurrent workers slept efficiently
> on mutex_lock().
>
> By removing the mutex, it appears multiple worker threads could concurrently
> enter dma_sync_wait() on the same channel, unnecessarily consuming 100% of
> multiple CPU cores and potentially causing CPU exhaustion.

exitting problem. some edma improvement patch is on going.

>
> [Severity: High]
> Can removing the mutex also lead to a cascading teardown failure and
> use-after-free?
>
> Because the mutex was removed, multiple worker threads can concurrently submit
> and wait on the shared dma_tx_chan or dma_rx_chan. If one transfer times out
> in dma_sync_wait(), the error path calls dmaengine_terminate_sync(chan).
>
> It looks like this would forcefully abort the shared channel, destroying all
> other active and pending descriptors belonging to concurrent NVMe commands.
>
> Also, if Thread A calls dmaengine_terminate_sync(chan) while Thread B is
> between dmaengine_prep_config_single_safe() and dmaengine_submit():

This is another problem.

Frank

>
> Thread B:
> 	desc = dmaengine_prep_config_single_safe(...)
> 	// Thread A calls dmaengine_terminate_sync() here
> 	cookie = dmaengine_submit(desc);
>
> Could Thread B's descriptor be freed by the termination, resulting in a
> use-after-free when Thread B attempts to submit it?
>
> >  	dma_dev = dmaengine_get_dma_device(chan);
> >  	dma_addr = dma_map_single(dma_dev, seg->buf, seg->length, dir);
> >  	ret = dma_mapping_error(dma_dev, dma_addr);
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260520-dma_prep_config-v6-0-06e49b7acb38@nxp.com?part=7

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v6 8/9] PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code
  2026-05-21  2:08   ` sashiko-bot
@ 2026-05-21 15:09     ` Frank Li
  0 siblings, 0 replies; 22+ messages in thread
From: Frank Li @ 2026-05-21 15:09 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: Frank.Li, dmaengine, linux-pci, imx, Frank.Li, vkoul

On Thu, May 21, 2026 at 02:08:13AM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] The DMA workqueue is destroyed before pending DMA transfers are explicitly terminated, which can lead to a Use-After-Free if a DMA callback executes late.
> --
>
> commit 01fd36b3138418658a89651874d85c30619199e8
> Author: Frank Li <Frank.Li@nxp.com>
>
> PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code
>
> This commit replaces dmaengine_slave_config() and dmaengine_prep_slave_single()
> with a single call to dmaengine_prep_config_single() in the EDMA read and write
> paths. The change aims to simplify the code without introducing any functional
> differences.
>
> > diff --git a/drivers/pci/endpoint/functions/pci-epf-mhi.c b/drivers/pci/endpoint/functions/pci-epf-mhi.c
> > index 7f5326925ed54..c3e3b58fb86cd 100644
> > --- a/drivers/pci/endpoint/functions/pci-epf-mhi.c
> > +++ b/drivers/pci/endpoint/functions/pci-epf-mhi.c
> [ ... ]
> > @@ -506,12 +496,6 @@ static int pci_epf_mhi_edma_read_async(struct mhi_ep_cntrl *mhi_cntrl,
> >  	config.direction = DMA_DEV_TO_MEM;
> >  	config.src_addr = buf_info->host_addr;
> >
> > -	ret = dmaengine_slave_config(chan, &config);
> > -	if (ret) {
> > -		dev_err(dev, "Failed to configure DMA channel\n");
> > -		goto err_unlock;
> > -	}
> > -
> >  	dst_addr = dma_map_single(dma_dev, buf_info->dev_addr, buf_info->size,
> >  				  DMA_FROM_DEVICE);
> >  	ret = dma_mapping_error(dma_dev, dst_addr);
> > @@ -520,9 +504,10 @@ static int pci_epf_mhi_edma_read_async(struct mhi_ep_cntrl *mhi_cntrl,
> >  		goto err_unlock;
> >  	}
> >
> > -	desc = dmaengine_prep_slave_single(chan, dst_addr, buf_info->size,
> > -					   DMA_DEV_TO_MEM,
> > -					   DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
> > +	desc = dmaengine_prep_config_single(chan, dst_addr, buf_info->size,
> > +					    DMA_DEV_TO_MEM,
> > +					    DMA_CTRL_ACK | DMA_PREP_INTERRUPT,
> > +					    &config);
>
> [Severity: High]
> This is a pre-existing issue, but I noticed a potential problem with how these
> asynchronous DMA transfers are cleaned up.

This is not this patch problem.

Frank

>
> In pci_epf_mhi_dma_deinit(), the DMA workqueue is destroyed before pending DMA
> transfers are explicitly terminated:
>
> static void pci_epf_mhi_dma_deinit(struct pci_epf_mhi *epf_mhi)
> {
> 	destroy_workqueue(epf_mhi->dma_wq);
> 	dma_release_channel(epf_mhi->dma_chan_tx);
> 	dma_release_channel(epf_mhi->dma_chan_rx);
> 	...
> }
>
> Does this sequence lead to a use-after-free if a DMA callback executes late?
>
> If a transfer is physically still running in the DMA engine hardware when the
> deinitialization begins, destroy_workqueue() will flush existing queued work
> but cannot stop the hardware itself.
>
> If the hardware completes the transfer a moment later, the DMA engine
> interrupt will fire and invoke pci_epf_mhi_dma_async_callback(), which
> blindly calls queue_work(epf_mhi->dma_wq, ...).
>
> Should dmaengine_terminate_sync() be called to abort pending asynchronous
> DMA transfers on all active channels before destroying the workqueue?
>
> >  	if (!desc) {
> >  		dev_err(dev, "Failed to prepare DMA\n");
> >  		ret = -EIO;
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260520-dma_prep_config-v6-0-06e49b7acb38@nxp.com?part=8

^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: [PATCH v6 9/9] crypto: atmel: Use dmaengine_prep_config_sg() API
  2026-05-21  2:33   ` sashiko-bot
@ 2026-05-21 15:11     ` Frank Li
  0 siblings, 0 replies; 22+ messages in thread
From: Frank Li @ 2026-05-21 15:11 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: Frank.Li, dmaengine, vkoul, Frank.Li, linux-pci, imx

On Thu, May 21, 2026 at 02:33:02AM +0000, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
>
> Pre-existing issues:
> - [High] Tasklets can be rescheduled after they are killed during device removal, leading to a Use-After-Free.
> - [High] The device unbind path does not wait for active crypto transforms (TFMs) to be freed, leading to a Use-After-Free.
> - [High] Pending requests in the crypto queue are not completed or flushed during device removal.
> - [High] Calling `dmaengine_terminate_sync()` from atomic context (tasklet/softirq) in the error path.
> --
>
> commit 8fb292764f2c30a42b59a7ee1bce9dcae3cc201f
> Author: Frank Li <Frank.Li@nxp.com>
>
> crypto: atmel: Use dmaengine_prep_config_sg() API
>
> This commit simplifies the driver by replacing dmaengine_slave_config()
> and dmaengine_prep_slave_sg() with the combined dmaengine_prep_config_sg()
> API. It converts both configuration and descriptor preparation failures to
> return -ENOMEM.
>
> > diff --git a/drivers/crypto/atmel-aes.c b/drivers/crypto/atmel-aes.c
> > index b393689400b4c..d890b5a277b9c 100644
> > --- a/drivers/crypto/atmel-aes.c
> > +++ b/drivers/crypto/atmel-aes.c
> > @@ -795,7 +795,6 @@ static int atmel_aes_dma_transfer_start(struct atmel_aes_dev *dd,
> [ ... ]
> > -	err = dmaengine_slave_config(dma->chan, &config);
> > -	if (err)
> > -		return err;
> > -
> > -	desc = dmaengine_prep_slave_sg(dma->chan, dma->sg, dma->sg_len, dir,
> > -				       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> > +	desc = dmaengine_prep_config_sg(dma->chan, dma->sg, dma->sg_len, dir,
> > +					DMA_PREP_INTERRUPT | DMA_CTRL_ACK,
> > +					&config);
> >  	if (!desc)
> >  		return -ENOMEM;
>
> [Severity: High]
> This is a pre-existing issue, but can this error path lead to a kernel BUG
> by sleeping in an atomic context?
>
> If dmaengine_prep_config_sg() fails and returns NULL, it seems the failure
> propagates up to atmel_aes_dma_start() which might jump to an error label
> and call dmaengine_terminate_sync().
>
> Since the crypto request queue is processed via atmel_aes_queue_task()
> (a tasklet running in softirq context), wouldn't calling
> dmaengine_terminate_sync() (which uses might_sleep()) from this atomic
> context cause issues?
>
> [Severity: High]
> This is a pre-existing issue, but does device removal have a use-after-free
> vulnerability if a DMA transfer completes concurrently?
>
> In atmel_aes_remove(), tasklet_kill() is called before
> atmel_aes_dma_cleanup(). If a DMA transfer completes immediately after
> tasklet_kill(), the interrupt callback (atmel_aes_dma_callback()) could
> execute and schedule the queue_task tasklet again via tasklet_schedule().
>
> Since the aes_dd driver data structure is managed via devm_kzalloc() and
> freed upon return from atmel_aes_remove(), would the tasklet subsequently
> access freed memory when it runs?
>
> [Severity: High]
> This is a pre-existing issue, but does the unbind path properly wait for
> active crypto transforms (TFMs) to be freed?
>
> In atmel_aes_remove(), the driver unregisters its algorithms using
> crypto_unregister_algs(). This prevents new TFMs from being instantiated,
> but it doesn't appear to revoke or wait for active TFMs to be freed.
>
> Because aes_dd is allocated using devm_kzalloc() and freed once
> atmel_aes_remove() completes, could subsequent crypto operations triggered
> by userspace via remaining TFMs dereference the freed aes_dd structure?
>
> [Severity: High]
> This is a pre-existing issue, but are pending requests in the crypto queue
> completed or flushed during device removal?
>
> During device unbind and atmel_aes_remove(), it appears the driver never
> purges, aborts, or flushes the crypto requests currently waiting in
> aes_dd->queue.
>
> When the device structure is freed by devres, wouldn't all queued
> asynchronous requests be lost without invoking their completion callbacks,
> causing callers waiting on wait_for_completion() to hang indefinitely?

All is exiting problem.

Frank

>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20260520-dma_prep_config-v6-0-06e49b7acb38@nxp.com?part=9

^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2026-05-21 15:11 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-20 22:00 [PATCH v6 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank.Li
2026-05-20 22:00 ` [PATCH v6 1/9] dmaengine: Add API to combine configuration and preparation (sg and single) Frank.Li
2026-05-20 22:00 ` [PATCH v6 2/9] dmaengine: Add safe API to combine configuration and preparation Frank.Li
2026-05-21  0:01   ` sashiko-bot
2026-05-21 14:56     ` Frank Li
2026-05-20 22:00 ` [PATCH v6 3/9] PCI: endpoint: pci-epf-test: Use dmaenigne_prep_config_single() to simplify code Frank.Li
2026-05-20 22:00 ` [PATCH v6 4/9] dmaengine: dw-edma: Use new .device_prep_config_sg() callback Frank.Li
2026-05-21  0:31   ` sashiko-bot
2026-05-21 14:59     ` Frank Li
2026-05-20 22:00 ` [PATCH v6 5/9] dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer() Frank.Li
2026-05-21  0:51   ` sashiko-bot
2026-05-21 15:02     ` Frank Li
2026-05-20 22:00 ` [PATCH v6 6/9] nvmet: pci-epf: Remove unnecessary dmaengine_terminate_sync() on each DMA transfer Frank.Li
2026-05-20 22:00 ` [PATCH v6 7/9] nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API Frank.Li
2026-05-21  1:39   ` sashiko-bot
2026-05-21 15:08     ` Frank Li
2026-05-20 22:00 ` [PATCH v6 8/9] PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code Frank.Li
2026-05-21  2:08   ` sashiko-bot
2026-05-21 15:09     ` Frank Li
2026-05-20 22:00 ` [PATCH v6 9/9] crypto: atmel: Use dmaengine_prep_config_sg() API Frank.Li
2026-05-21  2:33   ` sashiko-bot
2026-05-21 15:11     ` Frank Li

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox