Linux PCI subsystem development
 help / color / mirror / Atom feed
* [PATCH v5 0/9] dmaengine: Add new API to combine configuration and descriptor preparation
@ 2026-05-12 16:41 Frank Li
  2026-05-12 16:41 ` [PATCH v5 1/9] dmaengine: Add API to combine configuration and preparation (sg and single) Frank Li
                   ` (8 more replies)
  0 siblings, 9 replies; 15+ messages in thread
From: Frank Li @ 2026-05-12 16:41 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 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_single() 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                 |  21 +---
 drivers/pci/endpoint/functions/pci-epf-mhi.c  |  52 +++------
 drivers/pci/endpoint/functions/pci-epf-test.c |   8 +-
 include/linux/dmaengine.h                     | 148 ++++++++++++++++++++++++--
 8 files changed, 207 insertions(+), 84 deletions(-)
---
base-commit: b9303e6bff706758c167af686b5315ad00233bf8
change-id: 20251204-dma_prep_config-654170d245a2

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


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

* [PATCH v5 1/9] dmaengine: Add API to combine configuration and preparation (sg and single)
  2026-05-12 16:41 [PATCH v5 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank Li
@ 2026-05-12 16:41 ` Frank Li
  2026-05-12 16:42 ` [PATCH v5 2/9] dmaengine: Add safe API to combine configuration and preparation Frank Li
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Frank Li @ 2026-05-12 16:41 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

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 d491e385d61a98b8a804cd823caf254a2dc62cf4..5ee5d4a3596dd986b02f1bce3078ca6c4c1fb45a 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 b3d251c9734e95e1b75cf6763d4d2c3a1c6a9910..defa377d2ef54d94e6337cdfa7826a091295535e 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] 15+ messages in thread

* [PATCH v5 2/9] dmaengine: Add safe API to combine configuration and preparation
  2026-05-12 16:41 [PATCH v5 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank Li
  2026-05-12 16:41 ` [PATCH v5 1/9] dmaengine: Add API to combine configuration and preparation (sg and single) Frank Li
@ 2026-05-12 16:42 ` Frank Li
  2026-05-13 23:54   ` sashiko-bot
  2026-05-12 16:42 ` [PATCH v5 3/9] PCI: endpoint: pci-epf-test: Use dmaenigne_prep_config_single() to simplify code Frank Li
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 15+ messages in thread
From: Frank Li @ 2026-05-12 16:42 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

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 mutex protection.

Tested-by: Niklas Cassel <cassel@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
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 | 85 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 87 insertions(+)

diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 405bd2fbb4a3b94fd0bf44526f656f6a19feaad0..ba29e60160c1a0148793bb299849bccfebb6d32b 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 defa377d2ef54d94e6337cdfa7826a091295535e..83e8547de89bf56424f048c316bdc8d798791e25 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,83 @@ 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;
+
+	if (!chan || !chan->device)
+		return NULL;
+
+	if (!chan->device->device_prep_config_sg)
+		spin_lock(&chan->lock);
+
+	tx = dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, config);
+
+	if (!chan->device->device_prep_config_sg)
+		spin_unlock(&chan->lock);
+
+	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] 15+ messages in thread

* [PATCH v5 3/9] PCI: endpoint: pci-epf-test: Use dmaenigne_prep_config_single() to simplify code
  2026-05-12 16:41 [PATCH v5 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank Li
  2026-05-12 16:41 ` [PATCH v5 1/9] dmaengine: Add API to combine configuration and preparation (sg and single) Frank Li
  2026-05-12 16:42 ` [PATCH v5 2/9] dmaengine: Add safe API to combine configuration and preparation Frank Li
@ 2026-05-12 16:42 ` Frank Li
  2026-05-12 16:42 ` [PATCH v5 4/9] dmaengine: dw-edma: Use new .device_prep_config_sg() callback Frank Li
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Frank Li @ 2026-05-12 16:42 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

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 591d301fa89d89addf5df16e775e80460b689589..0f5cf2d7951088af3801ea1cc240b2ea8627eed5 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] 15+ messages in thread

* [PATCH v5 4/9] dmaengine: dw-edma: Use new .device_prep_config_sg() callback
  2026-05-12 16:41 [PATCH v5 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank Li
                   ` (2 preceding siblings ...)
  2026-05-12 16:42 ` [PATCH v5 3/9] PCI: endpoint: pci-epf-test: Use dmaenigne_prep_config_single() to simplify code Frank Li
@ 2026-05-12 16:42 ` Frank Li
  2026-05-14  0:10   ` sashiko-bot
  2026-05-12 16:42 ` [PATCH v5 5/9] dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer() Frank Li
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 15+ messages in thread
From: Frank Li @ 2026-05-12 16:42 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

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 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 c2feb3adc79fa94b016913443305b9fae9deef12..f7f58b0010e26b529ffb7382d5b166a703587c71 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 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] 15+ messages in thread

* [PATCH v5 5/9] dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer()
  2026-05-12 16:41 [PATCH v5 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank Li
                   ` (3 preceding siblings ...)
  2026-05-12 16:42 ` [PATCH v5 4/9] dmaengine: dw-edma: Use new .device_prep_config_sg() callback Frank Li
@ 2026-05-12 16:42 ` Frank Li
  2026-05-14  0:38   ` sashiko-bot
  2026-05-12 16:42 ` [PATCH v5 6/9] nvmet: pci-epf: Remove unnecessary dmaengine_terminate_sync() on each DMA transfer Frank Li
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 15+ messages in thread
From: Frank Li @ 2026-05-12 16:42 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

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 f7f58b0010e26b529ffb7382d5b166a703587c71..ec6f6b1e482568a27ebe90852d5679672b24a1e9 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 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] 15+ messages in thread

* [PATCH v5 6/9] nvmet: pci-epf: Remove unnecessary dmaengine_terminate_sync() on each DMA transfer
  2026-05-12 16:41 [PATCH v5 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank Li
                   ` (4 preceding siblings ...)
  2026-05-12 16:42 ` [PATCH v5 5/9] dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer() Frank Li
@ 2026-05-12 16:42 ` Frank Li
  2026-05-12 16:42 ` [PATCH v5 7/9] nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API Frank Li
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 15+ messages in thread
From: Frank Li @ 2026-05-12 16:42 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

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 4e9db96ebfecd796244e5dc67c23e1abb1a14974..2afe8f4d0e46104a1b3c98db3905cf33e8c9e011 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] 15+ messages in thread

* [PATCH v5 7/9] nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API
  2026-05-12 16:41 [PATCH v5 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank Li
                   ` (5 preceding siblings ...)
  2026-05-12 16:42 ` [PATCH v5 6/9] nvmet: pci-epf: Remove unnecessary dmaengine_terminate_sync() on each DMA transfer Frank Li
@ 2026-05-12 16:42 ` Frank Li
  2026-05-14  1:16   ` sashiko-bot
  2026-05-12 16:42 ` [PATCH v5 8/9] PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code Frank Li
  2026-05-12 16:42 ` [PATCH v5 9/9] crypto: atmel: Use dmaengine_prep_config_single() API Frank Li
  8 siblings, 1 reply; 15+ messages in thread
From: Frank Li @ 2026-05-12 16:42 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

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>
---
 drivers/nvme/target/pci-epf.c | 18 ++++--------------
 1 file changed, 4 insertions(+), 14 deletions(-)

diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c
index 2afe8f4d0e46104a1b3c98db3905cf33e8c9e011..04d8f48d6950349ca97d2dbeae4e38e4714ad0d4 100644
--- a/drivers/nvme/target/pci-epf.c
+++ b/drivers/nvme/target/pci-epf.c
@@ -388,22 +388,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 +419,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] 15+ messages in thread

* [PATCH v5 8/9] PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code
  2026-05-12 16:41 [PATCH v5 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank Li
                   ` (6 preceding siblings ...)
  2026-05-12 16:42 ` [PATCH v5 7/9] nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API Frank Li
@ 2026-05-12 16:42 ` Frank Li
  2026-05-12 16:42 ` [PATCH v5 9/9] crypto: atmel: Use dmaengine_prep_config_single() API Frank Li
  8 siblings, 0 replies; 15+ messages in thread
From: Frank Li @ 2026-05-12 16:42 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

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 7f5326925ed54abf4ae75c465dfe0a9bab37ce40..c3e3b58fb86cd75e175b69ca45530610c500b99e 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] 15+ messages in thread

* [PATCH v5 9/9] crypto: atmel: Use dmaengine_prep_config_single() API
  2026-05-12 16:41 [PATCH v5 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank Li
                   ` (7 preceding siblings ...)
  2026-05-12 16:42 ` [PATCH v5 8/9] PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code Frank Li
@ 2026-05-12 16:42 ` Frank Li
  2026-05-14  1:49   ` sashiko-bot
  8 siblings, 1 reply; 15+ messages in thread
From: Frank Li @ 2026-05-12 16:42 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

Using new API dmaengine_prep_config_single() to simple code.

No functional change.

Tested-by: Niklas Cassel <cassel@kernel.org>
Acked-by: Nicolas Ferre <nicolas.ferre@microchip.com>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
 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 b393689400b4c17294cba73fcd16567fdd6687f4..d890b5a277b9c1394d2c7912bd663ff86321099f 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] 15+ messages in thread

* Re: [PATCH v5 2/9] dmaengine: Add safe API to combine configuration and preparation
  2026-05-12 16:42 ` [PATCH v5 2/9] dmaengine: Add safe API to combine configuration and preparation Frank Li
@ 2026-05-13 23:54   ` sashiko-bot
  0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-05-13 23:54 UTC (permalink / raw)
  To: Frank Li; +Cc: vkoul, linux-pci, imx, dmaengine, Frank.Li

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Same-CPU deadlock due to `spin_lock()` usage in `dmaengine_prep_config_sg_safe()` without disabling interrupts or bottom halves.
- [Medium] Meaningful documentation and implementation mismatch regarding the synchronization primitive.
--

commit ed90e3a56790f8af46ffd2c002993a841a7927be
Author: Frank Li <Frank.Li@nxp.com>

dmaengine: Add safe API to combine configuration and preparation

This commit introduces dmaengine_prep_config_sg_safe() and
dmaengine_prep_config_single_safe() to provide a reentrant-safe way to
combine slave configuration and transfer preparation. It uses a new device
callback or falls back to a per-channel lock.

> 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 mutex protection.

This isn't a bug, but the commit message mentions mutex protection while
the code implements a spinlock. Could the commit message be updated to
reflect the actual synchronization primitive used?

> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index defa377d2ef54..83e8547de89bf 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h

[ ... ]

> +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;
> +
> +	if (!chan || !chan->device)
> +		return NULL;
> +
> +	if (!chan->device->device_prep_config_sg)
> +		spin_lock(&chan->lock);

Can this cause a same-CPU deadlock?

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?

> +
> +	tx = dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, config);
> +
> +	if (!chan->device->device_prep_config_sg)
> +		spin_unlock(&chan->lock);
> +
> +	return tx;
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com?part=2

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

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

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The return value of dw_edma_device_config() is ignored, leading to silent failures and state corruption.
- [High] The .device_prep_config_sg callback mutates shared channel state without locks, violating atomicity guarantees.
--

commit cc74cf96b36ed28ea1019078a61ff3eac8097031
Author: Frank Li <Frank.Li@nxp.com>

dmaengine: dw-edma: Use new .device_prep_config_sg() callback

This commit introduces the .device_prep_config_sg() callback to the dw-edma
driver 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..f7f58b0010e26 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
[ ... ]
> @@ -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);

Does this need to check the return value of dw_edma_device_config()?

If dw_edma_device_config() returns an error like -EINVAL, the driver will
silently proceed to dw_edma_device_transfer() with the old configuration.

Also, dw_edma_device_config() unconditionally clears chan->non_ll before
validating the new configuration:

static int dw_edma_device_config(...) {
	...
	chan->non_ll = false;
	if (chan->dw->chip->mf == EDMA_MF_HDMA_NATIVE) {
		if (config->peripheral_config &&
		    config->peripheral_size != sizeof(int)) {
			dev_err(dchan->device->dev, ...);
			return -EINVAL;
		}
	...
}

By ignoring the error, the transfer function might calculate bursts_max
incorrectly using a corrupted chan->non_ll flag. I see that subsequent
commits in this series do not appear to fix this missing error check.

Additionally, can this sequence result in a data race if called concurrently?

When a driver implements .device_prep_config_sg, the dmaengine framework
skips its internal spinlock in dmaengine_prep_config_sg_safe() and delegates
atomicity guarantees to the driver.

However, dw_edma_device_config() locklessly mutates shared channel state
such as chan->config and the chan->non_ll flag.

While a later commit in this series attempts to fix this by passing config
directly to the transfer function, dw_edma_device_config() still mutates
the shared chan->non_ll flag without locks, which is then read by
dw_edma_device_transfer() to determine bursts_max.

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

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com?part=4

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

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

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The `dw_edma_device_prep_config_sg` function ignores the return value of `dw_edma_device_config` and proceeds to use an explicitly rejected configuration for DMA transfers.
- [High] The implementation fails to achieve the claimed "atomic configuration" because it locklessly mutates shared channel state, corrupting concurrently active DMA transfers.
--

commit 8f2944930122fd18a6a04f03176bb9c6eddcd9ba
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
support atomic configuration and descriptor preparation when a non-NULL
configuration is provided to device_prep_config_sg().

> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index f7f58b0010e26..ec6f6b1e48256 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
[ ... ]
> @@ -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);

Should we check the return value of dw_edma_device_config() here?

If an invalid configuration is passed, dw_edma_device_config() returns
-EINVAL. By ignoring the return value, the code proceeds to extract the
source and destination addresses from the explicitly rejected configuration
via dw_edma_device_get_config(), potentially resulting in descriptors
prepared with unvalidated hardware parameters.

Additionally, the commit message states this change supports atomic
configuration, but dw_edma_device_config() mutates shared channel state:

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

Since the dmaengine API allows concurrent descriptor preparations when
device_prep_config_sg is provided, could this cause a data race on
chan->config and chan->non_ll?

If chan->non_ll is actively read by the interrupt handler to dictate
hardware execution mode, could modifying it while preparing a new descriptor
dynamically corrupt the execution mode of currently active transfers?

> -	return dw_edma_device_transfer(&xfer);
> +	return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, config));
>  }

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com?part=5

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

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

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] Removing the DMA serialization mutex causes severe CPU starvation, spurious timeouts, and cascading DMA transfer aborts.
- [Low] Unused local variable triggers a compiler warning and leaves dead mutexes in the device structure.
--

commit 4b68a703ef44d7ed4b6b20d869ad94bab23d5076
Author: Frank Li <Frank.Li@nxp.com>

nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API

This commit uses the new dmaengine_prep_config_single_safe() API to combine
DMA configuration and descriptor preparation into a single call. It also
removes the mutex that was previously used in the DMA transfer path.

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

Is this description entirely accurate?

While the configuration and preparation might now be atomic, the mutex also
strictly serialized the DMA transaction pipeline.

> diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c
> index 2afe8f4d0e461..04d8f48d69503 100644
> --- a/drivers/nvme/target/pci-epf.c
> +++ b/drivers/nvme/target/pci-epf.c
> @@ -388,22 +388,15 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
>  		return -EINVAL;
>  	}
>  
> -	mutex_lock(lock);

Does removing this mutex introduce concurrency regressions with the shared DMA
channel during the wait phase?

With the mutex removed, multiple concurrent NVMe worker threads can submit
transfers to the same channel. Since dma_sync_wait() busy-waits using
cpu_relax(), could multiple threads spinning simultaneously lead to severe CPU
starvation?

Additionally, dma_sync_wait() has a hardcoded 5-second timeout computed at
entry. If concurrent transfers queue up, could later transfers spuriously
time out while waiting for earlier ones to finish in hardware?

If a spurious timeout occurs, the error path further down calls
dmaengine_terminate_sync(chan). Does this unconditionally abort all active
and pending transfers on the shared channel, potentially causing cascading
data transfer failures?

Could unbounded concurrent submissions also exhaust the DMA channel's
fixed-size descriptor pool under load?

> -
>  	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;

[ ... ]

> @@ -426,9 +419,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;
>  }

This isn't a bug, but does leaving the local lock variable and its assignments
in the preceding switch statement trigger a -Wunused-but-set-variable compiler
warning?

Also, since dma_rx_lock and dma_tx_lock in struct nvmet_pci_epf are no
longer used anywhere else, should they be removed from the structure to avoid
leaving dead code?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com?part=7

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

* Re: [PATCH v5 9/9] crypto: atmel: Use dmaengine_prep_config_single() API
  2026-05-12 16:42 ` [PATCH v5 9/9] crypto: atmel: Use dmaengine_prep_config_single() API Frank Li
@ 2026-05-14  1:49   ` sashiko-bot
  0 siblings, 0 replies; 15+ messages in thread
From: sashiko-bot @ 2026-05-14  1:49 UTC (permalink / raw)
  To: Frank Li; +Cc: imx, linux-pci, dmaengine, Frank.Li, vkoul

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Mismatch between the commit message and the actual API used.
- [Medium] The patch masks specific DMA configuration error codes, unconditionally returning -ENOMEM on failure.
--

commit 05fb1a88b098522438016a4c4b3491605a64cf5f
Author: Frank Li <Frank.Li@nxp.com>

crypto: atmel: Use dmaengine_prep_config_single() API

This commit refactors atmel_aes_dma_transfer_start() to use a new combined
DMA engine API for configuration and scatterlist preparation. The commit
message notes that this is intended to simplify the code with no functional
change.

> crypto: atmel: Use dmaengine_prep_config_single() API
> 
> Using new API dmaengine_prep_config_single() to simple code.

This isn't a bug, but the commit message and subject mention
dmaengine_prep_config_single(), while the code actually introduces and uses
dmaengine_prep_config_sg().

> 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

[ ... ]

> @@ -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;

The commit message states there is no functional change, but does this alter
the error propagation?

In the original code, if dmaengine_slave_config() failed, its specific error
code was returned to the caller. Looking at the definition of
dmaengine_prep_config_sg(), it returns NULL if the underlying
dmaengine_slave_config() call fails:

include/linux/dmaengine.h:dmaengine_prep_config_sg() {
        ...
        if (config)
                if (dmaengine_slave_config(chan, config))
                        return NULL;
        ...
}

Since atmel_aes_dma_transfer_start() now handles a NULL return by returning
-ENOMEM, might this mask configuration errors by presenting them as
out-of-memory failures instead of their original error codes?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com?part=9

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

end of thread, other threads:[~2026-05-14  1:49 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-12 16:41 [PATCH v5 0/9] dmaengine: Add new API to combine configuration and descriptor preparation Frank Li
2026-05-12 16:41 ` [PATCH v5 1/9] dmaengine: Add API to combine configuration and preparation (sg and single) Frank Li
2026-05-12 16:42 ` [PATCH v5 2/9] dmaengine: Add safe API to combine configuration and preparation Frank Li
2026-05-13 23:54   ` sashiko-bot
2026-05-12 16:42 ` [PATCH v5 3/9] PCI: endpoint: pci-epf-test: Use dmaenigne_prep_config_single() to simplify code Frank Li
2026-05-12 16:42 ` [PATCH v5 4/9] dmaengine: dw-edma: Use new .device_prep_config_sg() callback Frank Li
2026-05-14  0:10   ` sashiko-bot
2026-05-12 16:42 ` [PATCH v5 5/9] dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer() Frank Li
2026-05-14  0:38   ` sashiko-bot
2026-05-12 16:42 ` [PATCH v5 6/9] nvmet: pci-epf: Remove unnecessary dmaengine_terminate_sync() on each DMA transfer Frank Li
2026-05-12 16:42 ` [PATCH v5 7/9] nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API Frank Li
2026-05-14  1:16   ` sashiko-bot
2026-05-12 16:42 ` [PATCH v5 8/9] PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code Frank Li
2026-05-12 16:42 ` [PATCH v5 9/9] crypto: atmel: Use dmaengine_prep_config_single() API Frank Li
2026-05-14  1:49   ` sashiko-bot

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