* [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code
@ 2026-01-09 15:28 Frank Li
2026-01-09 15:28 ` [PATCH v2 01/11] dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK Frank Li
` (11 more replies)
0 siblings, 12 replies; 23+ messages in thread
From: Frank Li @ 2026-01-09 15:28 UTC (permalink / raw)
To: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-hardening, linux-pci, linux-nvme,
imx, Frank Li
This patch week depend on the below serise.
https://lore.kernel.org/imx/20251208-dma_prep_config-v1-0-53490c5e1e2a@nxp.com/
Basic change
struct dw_edma_desc *desc
└─ chunk list
└─ burst list
To
struct dw_edma_desc *desc
└─ burst[n]
And reduce at least 2 times kzalloc() for each dma descriptor create.
I only test eDMA part, not hardware test hdma part.
The finial goal is dymatic add DMA request when DMA running. So needn't
wait for irq for fetch next round DMA request.
This work is neccesary to for dymatic DMA request appending.
The post this part first to review and test firstly during working dymatic
DMA part.
performance is little bit better. Use NVME as EP function
Before
Rnd read, 4KB, QD=1, 1 job : IOPS=6660, BW=26.0MiB/s (27.3MB/s)
Rnd read, 4KB, QD=32, 1 job : IOPS=28.6k, BW=112MiB/s (117MB/s)
Rnd read, 4KB, QD=32, 4 jobs: IOPS=33.4k, BW=130MiB/s (137MB/s)
Rnd read, 128KB, QD=1, 1 job : IOPS=914, BW=114MiB/s (120MB/s)
Rnd read, 128KB, QD=32, 1 job : IOPS=1204, BW=151MiB/s (158MB/s)
Rnd read, 128KB, QD=32, 4 jobs: IOPS=1255, BW=157MiB/s (165MB/s)
Rnd read, 512KB, QD=1, 1 job : IOPS=248, BW=124MiB/s (131MB/s)
Rnd read, 512KB, QD=32, 1 job : IOPS=353, BW=177MiB/s (185MB/s)
Rnd read, 512KB, QD=32, 4 jobs: IOPS=388, BW=194MiB/s (204MB/s)
Rnd write, 4KB, QD=1, 1 job : IOPS=6241, BW=24.4MiB/s (25.6MB/s)
Rnd write, 4KB, QD=32, 1 job : IOPS=24.7k, BW=96.5MiB/s (101MB/s)
Rnd write, 4KB, QD=32, 4 jobs: IOPS=26.9k, BW=105MiB/s (110MB/s)
Rnd write, 128KB, QD=1, 1 job : IOPS=780, BW=97.5MiB/s (102MB/s)
Rnd write, 128KB, QD=32, 1 job : IOPS=987, BW=123MiB/s (129MB/s)
Rnd write, 128KB, QD=32, 4 jobs: IOPS=1021, BW=128MiB/s (134MB/s)
Seq read, 128KB, QD=1, 1 job : IOPS=1190, BW=149MiB/s (156MB/s)
Seq read, 128KB, QD=32, 1 job : IOPS=1400, BW=175MiB/s (184MB/s)
Seq read, 512KB, QD=1, 1 job : IOPS=243, BW=122MiB/s (128MB/s)
Seq read, 512KB, QD=32, 1 job : IOPS=355, BW=178MiB/s (186MB/s)
Seq read, 1MB, QD=32, 1 job : IOPS=191, BW=192MiB/s (201MB/s)
Seq write, 128KB, QD=1, 1 job : IOPS=784, BW=98.1MiB/s (103MB/s)
Seq write, 128KB, QD=32, 1 job : IOPS=1030, BW=129MiB/s (135MB/s)
Seq write, 512KB, QD=1, 1 job : IOPS=216, BW=108MiB/s (114MB/s)
Seq write, 512KB, QD=32, 1 job : IOPS=295, BW=148MiB/s (155MB/s)
Seq write, 1MB, QD=32, 1 job : IOPS=164, BW=165MiB/s (173MB/s)
Rnd rdwr, 4K..1MB, QD=8, 4 jobs: IOPS=250, BW=126MiB/s (132MB/s)
IOPS=261, BW=132MiB/s (138MB/s
After
Rnd read, 4KB, QD=1, 1 job : IOPS=6780, BW=26.5MiB/s (27.8MB/s)
Rnd read, 4KB, QD=32, 1 job : IOPS=28.6k, BW=112MiB/s (117MB/s)
Rnd read, 4KB, QD=32, 4 jobs: IOPS=33.4k, BW=130MiB/s (137MB/s)
Rnd read, 128KB, QD=1, 1 job : IOPS=1188, BW=149MiB/s (156MB/s)
Rnd read, 128KB, QD=32, 1 job : IOPS=1440, BW=180MiB/s (189MB/s)
Rnd read, 128KB, QD=32, 4 jobs: IOPS=1282, BW=160MiB/s (168MB/s)
Rnd read, 512KB, QD=1, 1 job : IOPS=254, BW=127MiB/s (134MB/s)
Rnd read, 512KB, QD=32, 1 job : IOPS=354, BW=177MiB/s (186MB/s)
Rnd read, 512KB, QD=32, 4 jobs: IOPS=388, BW=194MiB/s (204MB/s)
Rnd write, 4KB, QD=1, 1 job : IOPS=6282, BW=24.5MiB/s (25.7MB/s)
Rnd write, 4KB, QD=32, 1 job : IOPS=24.9k, BW=97.5MiB/s (102MB/s)
Rnd write, 4KB, QD=32, 4 jobs: IOPS=27.4k, BW=107MiB/s (112MB/s)
Rnd write, 128KB, QD=1, 1 job : IOPS=1098, BW=137MiB/s (144MB/s)
Rnd write, 128KB, QD=32, 1 job : IOPS=1195, BW=149MiB/s (157MB/s)
Rnd write, 128KB, QD=32, 4 jobs: IOPS=1120, BW=140MiB/s (147MB/s)
Seq read, 128KB, QD=1, 1 job : IOPS=936, BW=117MiB/s (123MB/s)
Seq read, 128KB, QD=32, 1 job : IOPS=1218, BW=152MiB/s (160MB/s)
Seq read, 512KB, QD=1, 1 job : IOPS=301, BW=151MiB/s (158MB/s)
Seq read, 512KB, QD=32, 1 job : IOPS=360, BW=180MiB/s (189MB/s)
Seq read, 1MB, QD=32, 1 job : IOPS=193, BW=194MiB/s (203MB/s)
Seq write, 128KB, QD=1, 1 job : IOPS=796, BW=99.5MiB/s (104MB/s)
Seq write, 128KB, QD=32, 1 job : IOPS=1019, BW=127MiB/s (134MB/s)
Seq write, 512KB, QD=1, 1 job : IOPS=213, BW=107MiB/s (112MB/s)
Seq write, 512KB, QD=32, 1 job : IOPS=273, BW=137MiB/s (143MB/s)
Seq write, 1MB, QD=32, 1 job : IOPS=168, BW=168MiB/s (177MB/s)
Rnd rdwr, 4K..1MB, QD=8, 4 jobs: IOPS=255, BW=128MiB/s (134MB/s)
IOPS=266, BW=135MiB/s (141MB/s)
To: Manivannan Sadhasivam <mani@kernel.org>
To: Vinod Koul <vkoul@kernel.org>
To: Gustavo Pimentel <Gustavo.Pimentel@synopsys.com>
To: Kees Cook <kees@kernel.org>
To: Gustavo A. R. Silva <gustavoars@kernel.org>
Cc: dmaengine@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-hardening@vger.kernel.org
To: Manivannan Sadhasivam <mani@kernel.org>
To: Krzysztof Wilczyński <kwilczynski@kernel.org>
To: Kishon Vijay Abraham I <kishon@kernel.org>
To: Bjorn Helgaas <bhelgaas@google.com>
To: Christoph Hellwig <hch@lst.de>
To: Niklas Cassel <cassel@kernel.org>
Cc: linux-pci@vger.kernel.org
Cc: linux-nvme@lists.infradead.org
Cc: imx@lists.linux.dev
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
Changes in v2:
- use 'eDMA' and 'HDMA' at commit message
- remove debug code.
- keep 'inline' to avoid build warning
- Link to v1: https://lore.kernel.org/r/20251212-edma_ll-v1-0-fc863d9f5ca3@nxp.com
---
Frank Li (11):
dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK
dmaengine: dw-edma: Move control field update of DMA link to the last step
dmaengine: dw-edma: Add xfer_sz field to struct dw_edma_chunk
dmaengine: dw-edma: Remove ll_max = -1 in dw_edma_channel_setup()
dmaengine: dw-edma: Move ll_region from struct dw_edma_chunk to struct dw_edma_chan
dmaengine: dw-edma: Pass down dw_edma_chan to reduce one level of indirection
dmaengine: dw-edma: Add helper dw_(edma|hdma)_v0_core_ch_enable()
dmaengine: dw-edma: Add callbacks to fill link list entries
dmaengine: dw-edma: Use common dw_edma_core_start() for both eDMA and HDMA
dmaengine: dw-edma: Use burst array instead of linked list
dmaengine: dw-edma: Remove struct dw_edma_chunk
drivers/dma/dw-edma/dw-edma-core.c | 203 +++++++----------------------
drivers/dma/dw-edma/dw-edma-core.h | 64 +++++++---
drivers/dma/dw-edma/dw-edma-v0-core.c | 234 +++++++++++++++++-----------------
drivers/dma/dw-edma/dw-hdma-v0-core.c | 147 +++++++++++----------
4 files changed, 292 insertions(+), 356 deletions(-)
---
base-commit: 5498240f25c3ccbd33af3197bec1578d678dc34d
change-id: 20251211-edma_ll-0904ba089f01
Best regards,
--
Frank Li <Frank.Li@nxp.com>
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 01/11] dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK
2026-01-09 15:28 [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Frank Li
@ 2026-01-09 15:28 ` Frank Li
2026-02-25 8:26 ` Koichiro Den
2026-01-09 15:28 ` [PATCH v2 02/11] dmaengine: dw-edma: Move control field update of DMA link to the last step Frank Li
` (10 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Frank Li @ 2026-01-09 15:28 UTC (permalink / raw)
To: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-hardening, linux-pci, linux-nvme,
imx, Frank Li
The DONE_INT_MASK and ABORT_INT_MASK registers are shared by all DMA
channels, and modifying them requires a read-modify-write sequence.
Because this operation is not atomic, concurrent calls to
dw_edma_v0_core_start() can introduce race conditions if two channels
update these registers simultaneously.
Add a spinlock to serialize access to these registers and prevent race
conditions.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
vc.lock protect should be another problem. This one just fix register
access for difference DMA channel.
Other improve defer to dynamtic append descriptor works later.
---
drivers/dma/dw-edma/dw-edma-v0-core.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index b75fdaffad9a4ea6cd8d15e8f43bea550848b46c..2850a9df80f54d00789144415ed2dfe31dea3965 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -364,6 +364,7 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
{
struct dw_edma_chan *chan = chunk->chan;
struct dw_edma *dw = chan->dw;
+ unsigned long flags;
u32 tmp;
dw_edma_v0_core_write_chunk(chunk);
@@ -408,6 +409,8 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
}
}
/* Interrupt unmask - done, abort */
+ raw_spin_lock_irqsave(&dw->lock, flags);
+
tmp = GET_RW_32(dw, chan->dir, int_mask);
tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
@@ -416,6 +419,9 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id));
SET_RW_32(dw, chan->dir, linked_list_err_en, tmp);
+
+ raw_spin_unlock_irqrestore(&dw->lock, flags);
+
/* Channel control */
SET_CH_32(dw, chan->dir, chan->id, ch_control1,
(DW_EDMA_V0_CCS | DW_EDMA_V0_LLE));
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 02/11] dmaengine: dw-edma: Move control field update of DMA link to the last step
2026-01-09 15:28 [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Frank Li
2026-01-09 15:28 ` [PATCH v2 01/11] dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK Frank Li
@ 2026-01-09 15:28 ` Frank Li
2026-01-09 15:28 ` [PATCH v2 03/11] dmaengine: dw-edma: Add xfer_sz field to struct dw_edma_chunk Frank Li
` (9 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Frank Li @ 2026-01-09 15:28 UTC (permalink / raw)
To: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-hardening, linux-pci, linux-nvme,
imx, Frank Li
The control field in a DMA link list entry must be updated as the final
step because it includes the CB bit, which indicates whether the entry is
ready. Add dma_wmb() to ensure the correct memory write ordering.
Currently the driver does not update DMA link entries while the DMA is
running, so no visible failure occurs. However, fixing the ordering now
prepares the driver for supporting link entry updates during DMA operation.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/dw-edma/dw-edma-v0-core.c | 10 ++++++----
drivers/dma/dw-edma/dw-hdma-v0-core.c | 10 ++++++----
2 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index 2850a9df80f54d00789144415ed2dfe31dea3965..1b0add95ed655d8d16d381c8294acb252b7bcd2d 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -284,17 +284,18 @@ static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
struct dw_edma_v0_lli *lli = chunk->ll_region.vaddr.mem + ofs;
- lli->control = control;
lli->transfer_size = size;
lli->sar.reg = sar;
lli->dar.reg = dar;
+ dma_wmb();
+ lli->control = control;
} else {
struct dw_edma_v0_lli __iomem *lli = chunk->ll_region.vaddr.io + ofs;
- writel(control, &lli->control);
writel(size, &lli->transfer_size);
writeq(sar, &lli->sar.reg);
writeq(dar, &lli->dar.reg);
+ writel(control, &lli->control);
}
}
@@ -306,13 +307,14 @@ static void dw_edma_v0_write_ll_link(struct dw_edma_chunk *chunk,
if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
struct dw_edma_v0_llp *llp = chunk->ll_region.vaddr.mem + ofs;
- llp->control = control;
llp->llp.reg = pointer;
+ dma_wmb();
+ llp->control = control;
} else {
struct dw_edma_v0_llp __iomem *llp = chunk->ll_region.vaddr.io + ofs;
- writel(control, &llp->control);
writeq(pointer, &llp->llp.reg);
+ writel(control, &llp->control);
}
}
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
index e3f8db4fe909a1776bb5899e0b4c9c7a9d178c05..f1fc1060d3f77e3b12dea48efa72c5b3a0a58c8b 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -160,17 +160,18 @@ static void dw_hdma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
struct dw_hdma_v0_lli *lli = chunk->ll_region.vaddr.mem + ofs;
- lli->control = control;
lli->transfer_size = size;
lli->sar.reg = sar;
lli->dar.reg = dar;
+ dma_wmb();
+ lli->control = control;
} else {
struct dw_hdma_v0_lli __iomem *lli = chunk->ll_region.vaddr.io + ofs;
- writel(control, &lli->control);
writel(size, &lli->transfer_size);
writeq(sar, &lli->sar.reg);
writeq(dar, &lli->dar.reg);
+ writel(control, &lli->control);
}
}
@@ -182,13 +183,14 @@ static void dw_hdma_v0_write_ll_link(struct dw_edma_chunk *chunk,
if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
struct dw_hdma_v0_llp *llp = chunk->ll_region.vaddr.mem + ofs;
- llp->control = control;
llp->llp.reg = pointer;
+ dma_wmb();
+ llp->control = control;
} else {
struct dw_hdma_v0_llp __iomem *llp = chunk->ll_region.vaddr.io + ofs;
- writel(control, &llp->control);
writeq(pointer, &llp->llp.reg);
+ writel(control, &llp->control);
}
}
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 03/11] dmaengine: dw-edma: Add xfer_sz field to struct dw_edma_chunk
2026-01-09 15:28 [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Frank Li
2026-01-09 15:28 ` [PATCH v2 01/11] dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK Frank Li
2026-01-09 15:28 ` [PATCH v2 02/11] dmaengine: dw-edma: Move control field update of DMA link to the last step Frank Li
@ 2026-01-09 15:28 ` Frank Li
2026-01-09 15:28 ` [PATCH v2 04/11] dmaengine: dw-edma: Remove ll_max = -1 in dw_edma_channel_setup() Frank Li
` (8 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Frank Li @ 2026-01-09 15:28 UTC (permalink / raw)
To: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-hardening, linux-pci, linux-nvme,
imx, Frank Li
Reusing ll_region.sz as the transfer size is misleading because
ll_region.sz represents the memory size of the EDMA link list, not the
amount of data to be transferred.
Add a new xfer_sz field to explicitly indicate the total transfer size
of a chunk.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/dw-edma/dw-edma-core.c | 4 ++--
drivers/dma/dw-edma/dw-edma-core.h | 1 +
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 744c60ec964102287bd32b9e55d0f3024d1d39d9..c6b014949afe82f10362711fc8a956fe60a72835 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -192,7 +192,7 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan)
return 0;
dw_edma_core_start(dw, child, !desc->xfer_sz);
- desc->xfer_sz += child->ll_region.sz;
+ desc->xfer_sz += child->xfer_sz;
dw_edma_free_burst(child);
list_del(&child->list);
kfree(child);
@@ -477,7 +477,7 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer,
else if (xfer->type == EDMA_XFER_INTERLEAVED)
burst->sz = xfer->xfer.il->sgl[i % fsz].size;
- chunk->ll_region.sz += burst->sz;
+ chunk->xfer_sz += burst->sz;
desc->alloc_sz += burst->sz;
if (dir == DMA_DEV_TO_MEM) {
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index 71894b9e0b1539c636171738963e80a0a5ef43a4..722f3e0011208f503f426b65645ef26fbae3804b 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -57,6 +57,7 @@ struct dw_edma_chunk {
u32 bursts_alloc;
u8 cb;
+ u32 xfer_sz;
struct dw_edma_region ll_region; /* Linked list */
};
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 04/11] dmaengine: dw-edma: Remove ll_max = -1 in dw_edma_channel_setup()
2026-01-09 15:28 [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Frank Li
` (2 preceding siblings ...)
2026-01-09 15:28 ` [PATCH v2 03/11] dmaengine: dw-edma: Add xfer_sz field to struct dw_edma_chunk Frank Li
@ 2026-01-09 15:28 ` Frank Li
2026-02-25 8:30 ` Koichiro Den
2026-01-09 15:28 ` [PATCH v2 05/11] dmaengine: dw-edma: Move ll_region from struct dw_edma_chunk to struct dw_edma_chan Frank Li
` (7 subsequent siblings)
11 siblings, 1 reply; 23+ messages in thread
From: Frank Li @ 2026-01-09 15:28 UTC (permalink / raw)
To: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-hardening, linux-pci, linux-nvme,
imx, Frank Li
dw_edma_channel_setup() calculates ll_max based on the size of the
ll_region, but the value is later overwritten with -1, preventing the
code from ever reaching the calculated ll_max.
Typically ll_max is around 170 for a 4 KB page and four DMA R/W channels.
It is uncommon for a single DMA request to reach this limit, so the issue
has not been observed in practice. However, if it occurs, the driver may
overwrite adjacent memory before reporting an error.
Remove the incorrect assignment so the calculated ll_max is honored
Fixes: 31fb8c1ff962d ("dmaengine: dw-edma: Improve the linked list and data blocks definition")
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/dw-edma/dw-edma-core.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index c6b014949afe82f10362711fc8a956fe60a72835..b154bdd7f2897d9a28df698a425afc1b1c93698b 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -770,7 +770,6 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ);
else
chan->ll_max = (chip->ll_region_rd[chan->id].sz / EDMA_LL_SZ);
- chan->ll_max -= 1;
dev_vdbg(dev, "L. List:\tChannel %s[%u] max_cnt=%u\n",
str_write_read(chan->dir == EDMA_DIR_WRITE),
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 05/11] dmaengine: dw-edma: Move ll_region from struct dw_edma_chunk to struct dw_edma_chan
2026-01-09 15:28 [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Frank Li
` (3 preceding siblings ...)
2026-01-09 15:28 ` [PATCH v2 04/11] dmaengine: dw-edma: Remove ll_max = -1 in dw_edma_channel_setup() Frank Li
@ 2026-01-09 15:28 ` Frank Li
2026-01-09 15:28 ` [PATCH v2 06/11] dmaengine: dw-edma: Pass down dw_edma_chan to reduce one level of indirection Frank Li
` (6 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Frank Li @ 2026-01-09 15:28 UTC (permalink / raw)
To: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-hardening, linux-pci, linux-nvme,
imx, Frank Li
ll_region is identical for all chunks belonging to the same DMA channel,
so there is no need to copy it into each chunk. Move ll_region to
struct dw_edma_chan to avoid redundant copies.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/dw-edma/dw-edma-core.c | 14 ++++----------
drivers/dma/dw-edma/dw-edma-core.h | 2 +-
drivers/dma/dw-edma/dw-edma-v0-core.c | 18 ++++++++++--------
drivers/dma/dw-edma/dw-hdma-v0-core.c | 18 ++++++++++--------
4 files changed, 25 insertions(+), 27 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index b154bdd7f2897d9a28df698a425afc1b1c93698b..5b12af20cb37156a8dec440401d956652b890d53 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -64,7 +64,6 @@ static struct dw_edma_burst *dw_edma_alloc_burst(struct dw_edma_chunk *chunk)
static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc)
{
- struct dw_edma_chip *chip = desc->chan->dw->chip;
struct dw_edma_chan *chan = desc->chan;
struct dw_edma_chunk *chunk;
@@ -81,13 +80,6 @@ static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc)
* - Even chunks originate CB equal to 1
*/
chunk->cb = !(desc->chunks_alloc % 2);
- if (chan->dir == EDMA_DIR_WRITE) {
- chunk->ll_region.paddr = chip->ll_region_wr[chan->id].paddr;
- chunk->ll_region.vaddr = chip->ll_region_wr[chan->id].vaddr;
- } else {
- chunk->ll_region.paddr = chip->ll_region_rd[chan->id].paddr;
- chunk->ll_region.vaddr = chip->ll_region_rd[chan->id].vaddr;
- }
if (desc->chunk) {
/* Create and add new element into the linked list */
@@ -767,9 +759,11 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
chan->status = EDMA_ST_IDLE;
if (chan->dir == EDMA_DIR_WRITE)
- chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ);
+ chan->ll_region = chip->ll_region_wr[chan->id];
else
- chan->ll_max = (chip->ll_region_rd[chan->id].sz / EDMA_LL_SZ);
+ chan->ll_region = chip->ll_region_rd[chan->id];
+
+ chan->ll_max = chan->ll_region.sz / EDMA_LL_SZ;
dev_vdbg(dev, "L. List:\tChannel %s[%u] max_cnt=%u\n",
str_write_read(chan->dir == EDMA_DIR_WRITE),
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index 722f3e0011208f503f426b65645ef26fbae3804b..e074a6375f8a6853c212e65d2d54cb3e614b1483 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -58,7 +58,6 @@ struct dw_edma_chunk {
u8 cb;
u32 xfer_sz;
- struct dw_edma_region ll_region; /* Linked list */
};
struct dw_edma_desc {
@@ -79,6 +78,7 @@ struct dw_edma_chan {
enum dw_edma_dir dir;
u32 ll_max;
+ struct dw_edma_region ll_region; /* Linked list */
struct msi_msg msi;
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index 1b0add95ed655d8d16d381c8294acb252b7bcd2d..a1656b3c6cf9e389b6349dd13f9a4ac3d71b4689 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -280,9 +280,10 @@ static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
u32 control, u32 size, u64 sar, u64 dar)
{
ptrdiff_t ofs = i * sizeof(struct dw_edma_v0_lli);
+ struct dw_edma_chan *chan = chunk->chan;
if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
- struct dw_edma_v0_lli *lli = chunk->ll_region.vaddr.mem + ofs;
+ struct dw_edma_v0_lli *lli = chan->ll_region.vaddr.mem + ofs;
lli->transfer_size = size;
lli->sar.reg = sar;
@@ -290,7 +291,7 @@ static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
dma_wmb();
lli->control = control;
} else {
- struct dw_edma_v0_lli __iomem *lli = chunk->ll_region.vaddr.io + ofs;
+ struct dw_edma_v0_lli __iomem *lli = chan->ll_region.vaddr.io + ofs;
writel(size, &lli->transfer_size);
writeq(sar, &lli->sar.reg);
@@ -303,15 +304,16 @@ static void dw_edma_v0_write_ll_link(struct dw_edma_chunk *chunk,
int i, u32 control, u64 pointer)
{
ptrdiff_t ofs = i * sizeof(struct dw_edma_v0_lli);
+ struct dw_edma_chan *chan = chunk->chan;
if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
- struct dw_edma_v0_llp *llp = chunk->ll_region.vaddr.mem + ofs;
+ struct dw_edma_v0_llp *llp = chan->ll_region.vaddr.mem + ofs;
llp->llp.reg = pointer;
dma_wmb();
llp->control = control;
} else {
- struct dw_edma_v0_llp __iomem *llp = chunk->ll_region.vaddr.io + ofs;
+ struct dw_edma_v0_llp __iomem *llp = chan->ll_region.vaddr.io + ofs;
writeq(pointer, &llp->llp.reg);
writel(control, &llp->control);
@@ -345,7 +347,7 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
if (!chunk->cb)
control |= DW_EDMA_V0_CB;
- dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr);
+ dw_edma_v0_write_ll_link(chunk, i, control, chan->ll_region.paddr);
}
static void dw_edma_v0_sync_ll_data(struct dw_edma_chunk *chunk)
@@ -359,7 +361,7 @@ static void dw_edma_v0_sync_ll_data(struct dw_edma_chunk *chunk)
* last MWr TLP is completed
*/
if (!(chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
- readl(chunk->ll_region.vaddr.io);
+ readl(chunk->chan->ll_region.vaddr.io);
}
static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
@@ -430,9 +432,9 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
/* Linked list */
/* llp is not aligned on 64bit -> keep 32bit accesses */
SET_CH_32(dw, chan->dir, chan->id, llp.lsb,
- lower_32_bits(chunk->ll_region.paddr));
+ lower_32_bits(chan->ll_region.paddr));
SET_CH_32(dw, chan->dir, chan->id, llp.msb,
- upper_32_bits(chunk->ll_region.paddr));
+ upper_32_bits(chan->ll_region.paddr));
}
dw_edma_v0_sync_ll_data(chunk);
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
index f1fc1060d3f77e3b12dea48efa72c5b3a0a58c8b..c12cc80c6c99697b50cf65a9720dab5a379dbe54 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -156,9 +156,10 @@ static void dw_hdma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
u32 control, u32 size, u64 sar, u64 dar)
{
ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli);
+ struct dw_edma_chan *chan = chunk->chan;
if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
- struct dw_hdma_v0_lli *lli = chunk->ll_region.vaddr.mem + ofs;
+ struct dw_hdma_v0_lli *lli = chan->ll_region.vaddr.mem + ofs;
lli->transfer_size = size;
lli->sar.reg = sar;
@@ -166,7 +167,7 @@ static void dw_hdma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
dma_wmb();
lli->control = control;
} else {
- struct dw_hdma_v0_lli __iomem *lli = chunk->ll_region.vaddr.io + ofs;
+ struct dw_hdma_v0_lli __iomem *lli = chan->ll_region.vaddr.io + ofs;
writel(size, &lli->transfer_size);
writeq(sar, &lli->sar.reg);
@@ -179,15 +180,16 @@ static void dw_hdma_v0_write_ll_link(struct dw_edma_chunk *chunk,
int i, u32 control, u64 pointer)
{
ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli);
+ struct dw_edma_chan *chan = chunk->chan;
if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
- struct dw_hdma_v0_llp *llp = chunk->ll_region.vaddr.mem + ofs;
+ struct dw_hdma_v0_llp *llp = chan->ll_region.vaddr.mem + ofs;
llp->llp.reg = pointer;
dma_wmb();
llp->control = control;
} else {
- struct dw_hdma_v0_llp __iomem *llp = chunk->ll_region.vaddr.io + ofs;
+ struct dw_hdma_v0_llp __iomem *llp = chan->ll_region.vaddr.io + ofs;
writeq(pointer, &llp->llp.reg);
writel(control, &llp->control);
@@ -210,7 +212,7 @@ static void dw_hdma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
if (!chunk->cb)
control |= DW_HDMA_V0_CB;
- dw_hdma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr);
+ dw_hdma_v0_write_ll_link(chunk, i, control, chunk->chan->ll_region.paddr);
}
static void dw_hdma_v0_sync_ll_data(struct dw_edma_chunk *chunk)
@@ -224,7 +226,7 @@ static void dw_hdma_v0_sync_ll_data(struct dw_edma_chunk *chunk)
* last MWr TLP is completed
*/
if (!(chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
- readl(chunk->ll_region.vaddr.io);
+ readl(chunk->chan->ll_region.vaddr.io);
}
static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
@@ -251,9 +253,9 @@ static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
/* Linked list */
/* llp is not aligned on 64bit -> keep 32bit accesses */
SET_CH_32(dw, chan->dir, chan->id, llp.lsb,
- lower_32_bits(chunk->ll_region.paddr));
+ lower_32_bits(chan->ll_region.paddr));
SET_CH_32(dw, chan->dir, chan->id, llp.msb,
- upper_32_bits(chunk->ll_region.paddr));
+ upper_32_bits(chan->ll_region.paddr));
}
/* Set consumer cycle */
SET_CH_32(dw, chan->dir, chan->id, cycle_sync,
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 06/11] dmaengine: dw-edma: Pass down dw_edma_chan to reduce one level of indirection
2026-01-09 15:28 [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Frank Li
` (4 preceding siblings ...)
2026-01-09 15:28 ` [PATCH v2 05/11] dmaengine: dw-edma: Move ll_region from struct dw_edma_chunk to struct dw_edma_chan Frank Li
@ 2026-01-09 15:28 ` Frank Li
2026-01-09 15:28 ` [PATCH v2 07/11] dmaengine: dw-edma: Add helper dw_(edma|hdma)_v0_core_ch_enable() Frank Li
` (5 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Frank Li @ 2026-01-09 15:28 UTC (permalink / raw)
To: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-hardening, linux-pci, linux-nvme,
imx, Frank Li
Some helper functions do not use any information from dw_edma_chunk, so
passing a dw_edma_chan pointer directly avoids an unnecessary level of
pointer dereferencing and simplifies data access.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/dw-edma/dw-edma-v0-core.c | 22 ++++++++++------------
drivers/dma/dw-edma/dw-hdma-v0-core.c | 23 +++++++++++------------
2 files changed, 21 insertions(+), 24 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index a1656b3c6cf9e389b6349dd13f9a4ac3d71b4689..79265684613df4f4a30d6108d696b95a2934dffe 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -276,13 +276,12 @@ dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
return ret;
}
-static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
+static void dw_edma_v0_write_ll_data(struct dw_edma_chan *chan, int i,
u32 control, u32 size, u64 sar, u64 dar)
{
ptrdiff_t ofs = i * sizeof(struct dw_edma_v0_lli);
- struct dw_edma_chan *chan = chunk->chan;
- if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
+ if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
struct dw_edma_v0_lli *lli = chan->ll_region.vaddr.mem + ofs;
lli->transfer_size = size;
@@ -300,13 +299,12 @@ static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
}
}
-static void dw_edma_v0_write_ll_link(struct dw_edma_chunk *chunk,
+static void dw_edma_v0_write_ll_link(struct dw_edma_chan *chan,
int i, u32 control, u64 pointer)
{
ptrdiff_t ofs = i * sizeof(struct dw_edma_v0_lli);
- struct dw_edma_chan *chan = chunk->chan;
- if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
+ if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
struct dw_edma_v0_llp *llp = chan->ll_region.vaddr.mem + ofs;
llp->llp.reg = pointer;
@@ -339,7 +337,7 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
control |= DW_EDMA_V0_RIE;
}
- dw_edma_v0_write_ll_data(chunk, i++, control, child->sz,
+ dw_edma_v0_write_ll_data(chan, i++, control, child->sz,
child->sar, child->dar);
}
@@ -347,10 +345,10 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
if (!chunk->cb)
control |= DW_EDMA_V0_CB;
- dw_edma_v0_write_ll_link(chunk, i, control, chan->ll_region.paddr);
+ dw_edma_v0_write_ll_link(chan, i, control, chan->ll_region.paddr);
}
-static void dw_edma_v0_sync_ll_data(struct dw_edma_chunk *chunk)
+static void dw_edma_v0_sync_ll_data(struct dw_edma_chan *chan)
{
/*
* In case of remote eDMA engine setup, the DW PCIe RP/EP internal
@@ -360,8 +358,8 @@ static void dw_edma_v0_sync_ll_data(struct dw_edma_chunk *chunk)
* LL memory in a hope that the MRd TLP will return only after the
* last MWr TLP is completed
*/
- if (!(chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
- readl(chunk->chan->ll_region.vaddr.io);
+ if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
+ readl(chan->ll_region.vaddr.io);
}
static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
@@ -437,7 +435,7 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
upper_32_bits(chan->ll_region.paddr));
}
- dw_edma_v0_sync_ll_data(chunk);
+ dw_edma_v0_sync_ll_data(chan);
/* Doorbell */
SET_RW_32(dw, chan->dir, doorbell,
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
index c12cc80c6c99697b50cf65a9720dab5a379dbe54..27f79d9b97d91fdbafc4f1e1e4d099bbbddf60e2 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -152,13 +152,12 @@ dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
return ret;
}
-static void dw_hdma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
+static void dw_hdma_v0_write_ll_data(struct dw_edma_chan *chan, int i,
u32 control, u32 size, u64 sar, u64 dar)
{
ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli);
- struct dw_edma_chan *chan = chunk->chan;
- if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
+ if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
struct dw_hdma_v0_lli *lli = chan->ll_region.vaddr.mem + ofs;
lli->transfer_size = size;
@@ -176,13 +175,12 @@ static void dw_hdma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i,
}
}
-static void dw_hdma_v0_write_ll_link(struct dw_edma_chunk *chunk,
+static void dw_hdma_v0_write_ll_link(struct dw_edma_chan *chan,
int i, u32 control, u64 pointer)
{
ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli);
- struct dw_edma_chan *chan = chunk->chan;
- if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
+ if (chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) {
struct dw_hdma_v0_llp *llp = chan->ll_region.vaddr.mem + ofs;
llp->llp.reg = pointer;
@@ -198,6 +196,7 @@ static void dw_hdma_v0_write_ll_link(struct dw_edma_chunk *chunk,
static void dw_hdma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
{
+ struct dw_edma_chan *chan = chunk->chan;
struct dw_edma_burst *child;
u32 control = 0, i = 0;
@@ -205,17 +204,17 @@ static void dw_hdma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
control = DW_HDMA_V0_CB;
list_for_each_entry(child, &chunk->burst->list, list)
- dw_hdma_v0_write_ll_data(chunk, i++, control, child->sz,
+ dw_hdma_v0_write_ll_data(chan, i++, control, child->sz,
child->sar, child->dar);
control = DW_HDMA_V0_LLP | DW_HDMA_V0_TCB;
if (!chunk->cb)
control |= DW_HDMA_V0_CB;
- dw_hdma_v0_write_ll_link(chunk, i, control, chunk->chan->ll_region.paddr);
+ dw_hdma_v0_write_ll_link(chan, i, control, chunk->chan->ll_region.paddr);
}
-static void dw_hdma_v0_sync_ll_data(struct dw_edma_chunk *chunk)
+static void dw_hdma_v0_sync_ll_data(struct dw_edma_chan *chan)
{
/*
* In case of remote HDMA engine setup, the DW PCIe RP/EP internal
@@ -225,8 +224,8 @@ static void dw_hdma_v0_sync_ll_data(struct dw_edma_chunk *chunk)
* LL memory in a hope that the MRd TLP will return only after the
* last MWr TLP is completed
*/
- if (!(chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
- readl(chunk->chan->ll_region.vaddr.io);
+ if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
+ readl(chan->ll_region.vaddr.io);
}
static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
@@ -261,7 +260,7 @@ static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
SET_CH_32(dw, chan->dir, chan->id, cycle_sync,
HDMA_V0_CONSUMER_CYCLE_STAT | HDMA_V0_CONSUMER_CYCLE_BIT);
- dw_hdma_v0_sync_ll_data(chunk);
+ dw_hdma_v0_sync_ll_data(chan);
/* Doorbell */
SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START);
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 07/11] dmaengine: dw-edma: Add helper dw_(edma|hdma)_v0_core_ch_enable()
2026-01-09 15:28 [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Frank Li
` (5 preceding siblings ...)
2026-01-09 15:28 ` [PATCH v2 06/11] dmaengine: dw-edma: Pass down dw_edma_chan to reduce one level of indirection Frank Li
@ 2026-01-09 15:28 ` Frank Li
2026-01-09 15:28 ` [PATCH v2 08/11] dmaengine: dw-edma: Add callbacks to fill link list entries Frank Li
` (4 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Frank Li @ 2026-01-09 15:28 UTC (permalink / raw)
To: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-hardening, linux-pci, linux-nvme,
imx, Frank Li
Move the channel-enable logic into a new helper function,
dw_(edma|hdma)_v0_core_ch_enable(), in preparation for supporting dynamic
link entry additions.
No functional changes.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/dw-edma/dw-edma-v0-core.c | 128 +++++++++++++++++-----------------
drivers/dma/dw-edma/dw-hdma-v0-core.c | 49 +++++++------
2 files changed, 91 insertions(+), 86 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index 79265684613df4f4a30d6108d696b95a2934dffe..1f1bc8b8d04aff85ddaf0b077fd01a87ac54046f 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -318,6 +318,67 @@ static void dw_edma_v0_write_ll_link(struct dw_edma_chan *chan,
}
}
+static void dw_edma_v0_core_ch_enable(struct dw_edma_chan *chan)
+{
+ struct dw_edma *dw = chan->dw;
+ unsigned long flags;
+ u32 tmp;
+
+ /* Enable engine */
+ SET_RW_32(dw, chan->dir, engine_en, BIT(0));
+ if (dw->chip->mf == EDMA_MF_HDMA_COMPAT) {
+ switch (chan->id) {
+ case 0:
+ SET_RW_COMPAT(dw, chan->dir, ch0_pwr_en, BIT(0));
+ break;
+ case 1:
+ SET_RW_COMPAT(dw, chan->dir, ch1_pwr_en, BIT(0));
+ break;
+ case 2:
+ SET_RW_COMPAT(dw, chan->dir, ch2_pwr_en, BIT(0));
+ break;
+ case 3:
+ SET_RW_COMPAT(dw, chan->dir, ch3_pwr_en, BIT(0));
+ break;
+ case 4:
+ SET_RW_COMPAT(dw, chan->dir, ch4_pwr_en, BIT(0));
+ break;
+ case 5:
+ SET_RW_COMPAT(dw, chan->dir, ch5_pwr_en, BIT(0));
+ break;
+ case 6:
+ SET_RW_COMPAT(dw, chan->dir, ch6_pwr_en, BIT(0));
+ break;
+ case 7:
+ SET_RW_COMPAT(dw, chan->dir, ch7_pwr_en, BIT(0));
+ break;
+ }
+ }
+ /* Interrupt unmask - done, abort */
+ raw_spin_lock_irqsave(&dw->lock, flags);
+
+ tmp = GET_RW_32(dw, chan->dir, int_mask);
+ tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
+ tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
+ SET_RW_32(dw, chan->dir, int_mask, tmp);
+ /* Linked list error */
+ tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
+ tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id));
+ SET_RW_32(dw, chan->dir, linked_list_err_en, tmp);
+
+ raw_spin_unlock_irqrestore(&dw->lock, flags);
+
+ /* Channel control */
+ SET_CH_32(dw, chan->dir, chan->id, ch_control1,
+ (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE));
+ /* Linked list */
+ /* llp is not aligned on 64bit -> keep 32bit accesses */
+ SET_CH_32(dw, chan->dir, chan->id, llp.lsb,
+ lower_32_bits(chan->ll_region.paddr));
+ SET_CH_32(dw, chan->dir, chan->id, llp.msb,
+ upper_32_bits(chan->ll_region.paddr));
+}
+
static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
{
struct dw_edma_burst *child;
@@ -366,74 +427,11 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
{
struct dw_edma_chan *chan = chunk->chan;
struct dw_edma *dw = chan->dw;
- unsigned long flags;
- u32 tmp;
dw_edma_v0_core_write_chunk(chunk);
- if (first) {
- /* Enable engine */
- SET_RW_32(dw, chan->dir, engine_en, BIT(0));
- if (dw->chip->mf == EDMA_MF_HDMA_COMPAT) {
- switch (chan->id) {
- case 0:
- SET_RW_COMPAT(dw, chan->dir, ch0_pwr_en,
- BIT(0));
- break;
- case 1:
- SET_RW_COMPAT(dw, chan->dir, ch1_pwr_en,
- BIT(0));
- break;
- case 2:
- SET_RW_COMPAT(dw, chan->dir, ch2_pwr_en,
- BIT(0));
- break;
- case 3:
- SET_RW_COMPAT(dw, chan->dir, ch3_pwr_en,
- BIT(0));
- break;
- case 4:
- SET_RW_COMPAT(dw, chan->dir, ch4_pwr_en,
- BIT(0));
- break;
- case 5:
- SET_RW_COMPAT(dw, chan->dir, ch5_pwr_en,
- BIT(0));
- break;
- case 6:
- SET_RW_COMPAT(dw, chan->dir, ch6_pwr_en,
- BIT(0));
- break;
- case 7:
- SET_RW_COMPAT(dw, chan->dir, ch7_pwr_en,
- BIT(0));
- break;
- }
- }
- /* Interrupt unmask - done, abort */
- raw_spin_lock_irqsave(&dw->lock, flags);
-
- tmp = GET_RW_32(dw, chan->dir, int_mask);
- tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
- tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
- SET_RW_32(dw, chan->dir, int_mask, tmp);
- /* Linked list error */
- tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
- tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id));
- SET_RW_32(dw, chan->dir, linked_list_err_en, tmp);
-
- raw_spin_unlock_irqrestore(&dw->lock, flags);
-
- /* Channel control */
- SET_CH_32(dw, chan->dir, chan->id, ch_control1,
- (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE));
- /* Linked list */
- /* llp is not aligned on 64bit -> keep 32bit accesses */
- SET_CH_32(dw, chan->dir, chan->id, llp.lsb,
- lower_32_bits(chan->ll_region.paddr));
- SET_CH_32(dw, chan->dir, chan->id, llp.msb,
- upper_32_bits(chan->ll_region.paddr));
- }
+ if (first)
+ dw_edma_v0_core_ch_enable(chan);
dw_edma_v0_sync_ll_data(chan);
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
index 27f79d9b97d91fdbafc4f1e1e4d099bbbddf60e2..953868ef424250c1b696b9e61b72ba9a9c7c38c9 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -194,6 +194,31 @@ static void dw_hdma_v0_write_ll_link(struct dw_edma_chan *chan,
}
}
+static void dw_hdma_v0_core_ch_enable(struct dw_edma_chan *chan)
+{
+ struct dw_edma *dw = chan->dw;
+ u32 tmp;
+
+ /* Enable engine */
+ SET_CH_32(dw, chan->dir, chan->id, ch_en, BIT(0));
+ /* Interrupt unmask - stop, abort */
+ tmp = GET_CH_32(dw, chan->dir, chan->id, int_setup);
+ tmp &= ~(HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK);
+ /* Interrupt enable - stop, abort */
+ tmp |= HDMA_V0_LOCAL_STOP_INT_EN | HDMA_V0_LOCAL_ABORT_INT_EN;
+ if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL))
+ tmp |= HDMA_V0_REMOTE_STOP_INT_EN | HDMA_V0_REMOTE_ABORT_INT_EN;
+ SET_CH_32(dw, chan->dir, chan->id, int_setup, tmp);
+ /* Channel control */
+ SET_CH_32(dw, chan->dir, chan->id, control1, HDMA_V0_LINKLIST_EN);
+ /* Linked list */
+ /* llp is not aligned on 64bit -> keep 32bit accesses */
+ SET_CH_32(dw, chan->dir, chan->id, llp.lsb,
+ lower_32_bits(chan->ll_region.paddr));
+ SET_CH_32(dw, chan->dir, chan->id, llp.msb,
+ upper_32_bits(chan->ll_region.paddr));
+}
+
static void dw_hdma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
{
struct dw_edma_chan *chan = chunk->chan;
@@ -232,30 +257,12 @@ static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
{
struct dw_edma_chan *chan = chunk->chan;
struct dw_edma *dw = chan->dw;
- u32 tmp;
dw_hdma_v0_core_write_chunk(chunk);
- if (first) {
- /* Enable engine */
- SET_CH_32(dw, chan->dir, chan->id, ch_en, BIT(0));
- /* Interrupt unmask - stop, abort */
- tmp = GET_CH_32(dw, chan->dir, chan->id, int_setup);
- tmp &= ~(HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK);
- /* Interrupt enable - stop, abort */
- tmp |= HDMA_V0_LOCAL_STOP_INT_EN | HDMA_V0_LOCAL_ABORT_INT_EN;
- if (!(dw->chip->flags & DW_EDMA_CHIP_LOCAL))
- tmp |= HDMA_V0_REMOTE_STOP_INT_EN | HDMA_V0_REMOTE_ABORT_INT_EN;
- SET_CH_32(dw, chan->dir, chan->id, int_setup, tmp);
- /* Channel control */
- SET_CH_32(dw, chan->dir, chan->id, control1, HDMA_V0_LINKLIST_EN);
- /* Linked list */
- /* llp is not aligned on 64bit -> keep 32bit accesses */
- SET_CH_32(dw, chan->dir, chan->id, llp.lsb,
- lower_32_bits(chan->ll_region.paddr));
- SET_CH_32(dw, chan->dir, chan->id, llp.msb,
- upper_32_bits(chan->ll_region.paddr));
- }
+ if (first)
+ dw_hdma_v0_core_ch_enable(chan);
+
/* Set consumer cycle */
SET_CH_32(dw, chan->dir, chan->id, cycle_sync,
HDMA_V0_CONSUMER_CYCLE_STAT | HDMA_V0_CONSUMER_CYCLE_BIT);
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 08/11] dmaengine: dw-edma: Add callbacks to fill link list entries
2026-01-09 15:28 [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Frank Li
` (6 preceding siblings ...)
2026-01-09 15:28 ` [PATCH v2 07/11] dmaengine: dw-edma: Add helper dw_(edma|hdma)_v0_core_ch_enable() Frank Li
@ 2026-01-09 15:28 ` Frank Li
2026-01-09 15:28 ` [PATCH v2 09/11] dmaengine: dw-edma: Use common dw_edma_core_start() for both eDMA and HDMA Frank Li
` (3 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Frank Li @ 2026-01-09 15:28 UTC (permalink / raw)
To: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-hardening, linux-pci, linux-nvme,
imx, Frank Li
Introduce four new callbacks to fill link list entries in preparation for
replacing dw_(edma|hdma)_v0_core_start().
Filling link list entries is expected to become more complex, and without
this abstraction both eDMA and HDMA paths would need to duplicate the same
logic. Add fill-entry callbacks so the code can be shared cleanly between
eDMA and HDMA implementations.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v2
- update commit message
- use eDMA and HDMI
- keep inline to avoid build warnings. dw-edma-v0-core.c also include
dw-edma-core.h
---
drivers/dma/dw-edma/dw-edma-core.h | 29 ++++++++++++++++++++++
drivers/dma/dw-edma/dw-edma-v0-core.c | 46 +++++++++++++++++++++++++++++++++++
drivers/dma/dw-edma/dw-hdma-v0-core.c | 38 +++++++++++++++++++++++++++++
3 files changed, 113 insertions(+)
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index e074a6375f8a6853c212e65d2d54cb3e614b1483..09e6b25225407f5bacb2699cbba26d1bab90b0c7 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -125,6 +125,12 @@ struct dw_edma_core_ops {
irqreturn_t (*handle_int)(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
dw_edma_handler_t done, dw_edma_handler_t abort);
void (*start)(struct dw_edma_chunk *chunk, bool first);
+ void (*ll_data)(struct dw_edma_chan *chan, struct dw_edma_burst *burst,
+ u32 idx, bool cb, bool irq);
+ void (*ll_link)(struct dw_edma_chan *chan, u32 idx, bool cb, u64 addr);
+ void (*ch_doorbell)(struct dw_edma_chan *chan);
+ void (*ch_enable)(struct dw_edma_chan *chan);
+
void (*ch_config)(struct dw_edma_chan *chan);
void (*debugfs_on)(struct dw_edma *dw);
};
@@ -201,6 +207,29 @@ void dw_edma_core_ch_config(struct dw_edma_chan *chan)
chan->dw->core->ch_config(chan);
}
+static inline void
+dw_edma_core_ll_data(struct dw_edma_chan *chan, struct dw_edma_burst *burst,
+ u32 idx, bool cb, bool irq)
+{
+ chan->dw->core->ll_data(chan, burst, idx, cb, irq);
+}
+
+static inline void
+dw_edma_core_ll_link(struct dw_edma_chan *chan, u32 idx, bool cb, u64 addr)
+{
+ chan->dw->core->ll_link(chan, idx, cb, addr);
+}
+
+static inline void dw_edma_core_ch_doorbell(struct dw_edma_chan *chan)
+{
+ chan->dw->core->ch_doorbell(chan);
+}
+
+static inline void dw_edma_core_ch_enable(struct dw_edma_chan *chan)
+{
+ chan->dw->core->ch_enable(chan);
+}
+
static inline
void dw_edma_core_debugfs_on(struct dw_edma *dw)
{
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index 1f1bc8b8d04aff85ddaf0b077fd01a87ac54046f..7c954af7a3e377f1fb2a250279383af4fa0d8d3e 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -509,6 +509,48 @@ static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan)
}
}
+static void
+dw_edma_v0_core_ll_data(struct dw_edma_chan *chan, struct dw_edma_burst *burst,
+ u32 idx, bool cb, bool irq)
+{
+ u32 control = 0;
+
+ if (cb)
+ control |= DW_EDMA_V0_CB;
+
+ if (irq) {
+ control |= DW_EDMA_V0_LIE;
+
+ if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
+ control |= DW_EDMA_V0_RIE;
+ }
+
+ dw_edma_v0_write_ll_data(chan, idx, control, burst->sz, burst->sar,
+ burst->dar);
+}
+
+static void
+dw_edma_v0_core_ll_link(struct dw_edma_chan *chan, u32 idx, bool cb, u64 addr)
+{
+ u32 control = DW_EDMA_V0_LLP | DW_EDMA_V0_TCB;
+
+ if (!cb)
+ control |= DW_EDMA_V0_CB;
+
+ dw_edma_v0_write_ll_link(chan, idx, control, chan->ll_region.paddr);
+}
+
+static void dw_edma_v0_core_ch_doorbell(struct dw_edma_chan *chan)
+{
+ struct dw_edma *dw = chan->dw;
+
+ dw_edma_v0_sync_ll_data(chan);
+
+ /* Doorbell */
+ SET_RW_32(dw, chan->dir, doorbell,
+ FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id));
+}
+
/* eDMA debugfs callbacks */
static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw)
{
@@ -521,6 +563,10 @@ static const struct dw_edma_core_ops dw_edma_v0_core = {
.ch_status = dw_edma_v0_core_ch_status,
.handle_int = dw_edma_v0_core_handle_int,
.start = dw_edma_v0_core_start,
+ .ll_data = dw_edma_v0_core_ll_data,
+ .ll_link = dw_edma_v0_core_ll_link,
+ .ch_doorbell = dw_edma_v0_core_ch_doorbell,
+ .ch_enable = dw_edma_v0_core_ch_enable,
.ch_config = dw_edma_v0_core_ch_config,
.debugfs_on = dw_edma_v0_core_debugfs_on,
};
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
index 953868ef424250c1b696b9e61b72ba9a9c7c38c9..94350bb2bdcd6e29d8a42380160a5bd77caf4680 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -287,6 +287,40 @@ static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)
SET_CH_32(dw, chan->dir, chan->id, msi_msgdata, chan->msi.data);
}
+static void
+dw_hdma_v0_core_ll_data(struct dw_edma_chan *chan, struct dw_edma_burst *burst,
+ u32 idx, bool cb, bool irq)
+{
+ u32 control = 0;
+
+ if (cb)
+ control |= DW_HDMA_V0_CB;
+
+ dw_hdma_v0_write_ll_data(chan, idx, control, burst->sz, burst->sar,
+ burst->dar);
+}
+
+static void
+dw_hdma_v0_core_ll_link(struct dw_edma_chan *chan, u32 idx, bool cb, u64 addr)
+{
+ u32 control = DW_HDMA_V0_LLP | DW_HDMA_V0_TCB;
+
+ if (!cb)
+ control |= DW_HDMA_V0_CB;
+
+ dw_hdma_v0_write_ll_link(chan, idx, control, chan->ll_region.paddr);
+}
+
+static void dw_hdma_v0_core_ch_doorbell(struct dw_edma_chan *chan)
+{
+ struct dw_edma *dw = chan->dw;
+
+ dw_hdma_v0_sync_ll_data(chan);
+
+ /* Doorbell */
+ SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START);
+}
+
/* HDMA debugfs callbacks */
static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw)
{
@@ -299,6 +333,10 @@ static const struct dw_edma_core_ops dw_hdma_v0_core = {
.ch_status = dw_hdma_v0_core_ch_status,
.handle_int = dw_hdma_v0_core_handle_int,
.start = dw_hdma_v0_core_start,
+ .ll_data = dw_hdma_v0_core_ll_data,
+ .ll_link = dw_hdma_v0_core_ll_link,
+ .ch_doorbell = dw_hdma_v0_core_ch_doorbell,
+ .ch_enable = dw_hdma_v0_core_ch_enable,
.ch_config = dw_hdma_v0_core_ch_config,
.debugfs_on = dw_hdma_v0_core_debugfs_on,
};
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 09/11] dmaengine: dw-edma: Use common dw_edma_core_start() for both eDMA and HDMA
2026-01-09 15:28 [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Frank Li
` (7 preceding siblings ...)
2026-01-09 15:28 ` [PATCH v2 08/11] dmaengine: dw-edma: Add callbacks to fill link list entries Frank Li
@ 2026-01-09 15:28 ` Frank Li
2026-01-09 15:28 ` [PATCH v2 10/11] dmaengine: dw-edma: Use burst array instead of linked list Frank Li
` (2 subsequent siblings)
11 siblings, 0 replies; 23+ messages in thread
From: Frank Li @ 2026-01-09 15:28 UTC (permalink / raw)
To: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-hardening, linux-pci, linux-nvme,
imx, Frank Li
Use common dw_edma_core_start() for both eDMA and HDMA. Remove .start()
callback functions at eDMA and HDMA.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v2
- use eDMA and HDMA
---
drivers/dma/dw-edma/dw-edma-core.c | 24 ++++++++++++++++--
drivers/dma/dw-edma/dw-edma-core.h | 7 -----
drivers/dma/dw-edma/dw-edma-v0-core.c | 48 -----------------------------------
drivers/dma/dw-edma/dw-hdma-v0-core.c | 43 +++----------------------------
4 files changed, 25 insertions(+), 97 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 5b12af20cb37156a8dec440401d956652b890d53..37918f733eb4d36c7ced6418b85a885affadc8f7 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -163,9 +163,29 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc)
dw_edma_free_desc(vd2dw_edma_desc(vdesc));
}
+static void dw_edma_core_start(struct dw_edma_chunk *chunk, bool first)
+{
+ struct dw_edma_chan *chan = chunk->chan;
+ struct dw_edma_burst *child;
+ u32 i = 0;
+ int j;
+
+ j = chunk->bursts_alloc;
+ list_for_each_entry(child, &chunk->burst->list, list) {
+ j--;
+ dw_edma_core_ll_data(chan, child, i++, chunk->cb, !j);
+ }
+
+ dw_edma_core_ll_link(chan, i, chunk->cb, chan->ll_region.paddr);
+
+ if (first)
+ dw_edma_core_ch_enable(chan);
+
+ dw_edma_core_ch_doorbell(chan);
+}
+
static int dw_edma_start_transfer(struct dw_edma_chan *chan)
{
- struct dw_edma *dw = chan->dw;
struct dw_edma_chunk *child;
struct dw_edma_desc *desc;
struct virt_dma_desc *vd;
@@ -183,7 +203,7 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan)
if (!child)
return 0;
- dw_edma_core_start(dw, child, !desc->xfer_sz);
+ dw_edma_core_start(child, !desc->xfer_sz);
desc->xfer_sz += child->xfer_sz;
dw_edma_free_burst(child);
list_del(&child->list);
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index 09e6b25225407f5bacb2699cbba26d1bab90b0c7..0ad460e1942221c678a76e9ab2aee35f1af4eb25 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -124,7 +124,6 @@ struct dw_edma_core_ops {
enum dma_status (*ch_status)(struct dw_edma_chan *chan);
irqreturn_t (*handle_int)(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
dw_edma_handler_t done, dw_edma_handler_t abort);
- void (*start)(struct dw_edma_chunk *chunk, bool first);
void (*ll_data)(struct dw_edma_chan *chan, struct dw_edma_burst *burst,
u32 idx, bool cb, bool irq);
void (*ll_link)(struct dw_edma_chan *chan, u32 idx, bool cb, u64 addr);
@@ -195,12 +194,6 @@ dw_edma_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir,
return dw_irq->dw->core->handle_int(dw_irq, dir, done, abort);
}
-static inline
-void dw_edma_core_start(struct dw_edma *dw, struct dw_edma_chunk *chunk, bool first)
-{
- dw->core->start(chunk, first);
-}
-
static inline
void dw_edma_core_ch_config(struct dw_edma_chan *chan)
{
diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
index 7c954af7a3e377f1fb2a250279383af4fa0d8d3e..7b4591f984ad8b6f9909db16775368ff471db2b8 100644
--- a/drivers/dma/dw-edma/dw-edma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
@@ -379,36 +379,6 @@ static void dw_edma_v0_core_ch_enable(struct dw_edma_chan *chan)
upper_32_bits(chan->ll_region.paddr));
}
-static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
-{
- struct dw_edma_burst *child;
- struct dw_edma_chan *chan = chunk->chan;
- u32 control = 0, i = 0;
- int j;
-
- if (chunk->cb)
- control = DW_EDMA_V0_CB;
-
- j = chunk->bursts_alloc;
- list_for_each_entry(child, &chunk->burst->list, list) {
- j--;
- if (!j) {
- control |= DW_EDMA_V0_LIE;
- if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL))
- control |= DW_EDMA_V0_RIE;
- }
-
- dw_edma_v0_write_ll_data(chan, i++, control, child->sz,
- child->sar, child->dar);
- }
-
- control = DW_EDMA_V0_LLP | DW_EDMA_V0_TCB;
- if (!chunk->cb)
- control |= DW_EDMA_V0_CB;
-
- dw_edma_v0_write_ll_link(chan, i, control, chan->ll_region.paddr);
-}
-
static void dw_edma_v0_sync_ll_data(struct dw_edma_chan *chan)
{
/*
@@ -423,23 +393,6 @@ static void dw_edma_v0_sync_ll_data(struct dw_edma_chan *chan)
readl(chan->ll_region.vaddr.io);
}
-static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
-{
- struct dw_edma_chan *chan = chunk->chan;
- struct dw_edma *dw = chan->dw;
-
- dw_edma_v0_core_write_chunk(chunk);
-
- if (first)
- dw_edma_v0_core_ch_enable(chan);
-
- dw_edma_v0_sync_ll_data(chan);
-
- /* Doorbell */
- SET_RW_32(dw, chan->dir, doorbell,
- FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id));
-}
-
static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan)
{
struct dw_edma *dw = chan->dw;
@@ -562,7 +515,6 @@ static const struct dw_edma_core_ops dw_edma_v0_core = {
.ch_count = dw_edma_v0_core_ch_count,
.ch_status = dw_edma_v0_core_ch_status,
.handle_int = dw_edma_v0_core_handle_int,
- .start = dw_edma_v0_core_start,
.ll_data = dw_edma_v0_core_ll_data,
.ll_link = dw_edma_v0_core_ll_link,
.ch_doorbell = dw_edma_v0_core_ch_doorbell,
diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c
index 94350bb2bdcd6e29d8a42380160a5bd77caf4680..7f9fe3a6edd94583fd09c80a8d79527ed6383a8c 100644
--- a/drivers/dma/dw-edma/dw-hdma-v0-core.c
+++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c
@@ -217,26 +217,10 @@ static void dw_hdma_v0_core_ch_enable(struct dw_edma_chan *chan)
lower_32_bits(chan->ll_region.paddr));
SET_CH_32(dw, chan->dir, chan->id, llp.msb,
upper_32_bits(chan->ll_region.paddr));
-}
-
-static void dw_hdma_v0_core_write_chunk(struct dw_edma_chunk *chunk)
-{
- struct dw_edma_chan *chan = chunk->chan;
- struct dw_edma_burst *child;
- u32 control = 0, i = 0;
-
- if (chunk->cb)
- control = DW_HDMA_V0_CB;
-
- list_for_each_entry(child, &chunk->burst->list, list)
- dw_hdma_v0_write_ll_data(chan, i++, control, child->sz,
- child->sar, child->dar);
-
- control = DW_HDMA_V0_LLP | DW_HDMA_V0_TCB;
- if (!chunk->cb)
- control |= DW_HDMA_V0_CB;
- dw_hdma_v0_write_ll_link(chan, i, control, chunk->chan->ll_region.paddr);
+ /* Set consumer cycle */
+ SET_CH_32(dw, chan->dir, chan->id, cycle_sync,
+ HDMA_V0_CONSUMER_CYCLE_STAT | HDMA_V0_CONSUMER_CYCLE_BIT);
}
static void dw_hdma_v0_sync_ll_data(struct dw_edma_chan *chan)
@@ -253,26 +237,6 @@ static void dw_hdma_v0_sync_ll_data(struct dw_edma_chan *chan)
readl(chan->ll_region.vaddr.io);
}
-static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
-{
- struct dw_edma_chan *chan = chunk->chan;
- struct dw_edma *dw = chan->dw;
-
- dw_hdma_v0_core_write_chunk(chunk);
-
- if (first)
- dw_hdma_v0_core_ch_enable(chan);
-
- /* Set consumer cycle */
- SET_CH_32(dw, chan->dir, chan->id, cycle_sync,
- HDMA_V0_CONSUMER_CYCLE_STAT | HDMA_V0_CONSUMER_CYCLE_BIT);
-
- dw_hdma_v0_sync_ll_data(chan);
-
- /* Doorbell */
- SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START);
-}
-
static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan)
{
struct dw_edma *dw = chan->dw;
@@ -332,7 +296,6 @@ static const struct dw_edma_core_ops dw_hdma_v0_core = {
.ch_count = dw_hdma_v0_core_ch_count,
.ch_status = dw_hdma_v0_core_ch_status,
.handle_int = dw_hdma_v0_core_handle_int,
- .start = dw_hdma_v0_core_start,
.ll_data = dw_hdma_v0_core_ll_data,
.ll_link = dw_hdma_v0_core_ll_link,
.ch_doorbell = dw_hdma_v0_core_ch_doorbell,
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 10/11] dmaengine: dw-edma: Use burst array instead of linked list
2026-01-09 15:28 [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Frank Li
` (8 preceding siblings ...)
2026-01-09 15:28 ` [PATCH v2 09/11] dmaengine: dw-edma: Use common dw_edma_core_start() for both eDMA and HDMA Frank Li
@ 2026-01-09 15:28 ` Frank Li
2026-01-09 15:28 ` [PATCH v2 11/11] dmaengine: dw-edma: Remove struct dw_edma_chunk Frank Li
2026-01-12 13:07 ` [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Niklas Cassel
11 siblings, 0 replies; 23+ messages in thread
From: Frank Li @ 2026-01-09 15:28 UTC (permalink / raw)
To: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-hardening, linux-pci, linux-nvme,
imx, Frank Li
The current descriptor layout is:
struct dw_edma_desc *desc
└─ chunk list
└─ burst list
Creating a DMA descriptor requires at least three kzalloc() calls because
each burst is allocated as a linked-list node. Since the number of bursts
is already known when the descriptor is created, a linked list is not
necessary.
Allocate a burst array when creating each chunk to simplify the code and
eliminate one kzalloc() call.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/dw-edma/dw-edma-core.c | 114 +++++++------------------------------
drivers/dma/dw-edma/dw-edma-core.h | 9 +--
2 files changed, 24 insertions(+), 99 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 37918f733eb4d36c7ced6418b85a885affadc8f7..9e65155fd93d69ddbc8235fad671fad4dc120979 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -40,38 +40,15 @@ u64 dw_edma_get_pci_address(struct dw_edma_chan *chan, phys_addr_t cpu_addr)
return cpu_addr;
}
-static struct dw_edma_burst *dw_edma_alloc_burst(struct dw_edma_chunk *chunk)
-{
- struct dw_edma_burst *burst;
-
- burst = kzalloc(sizeof(*burst), GFP_NOWAIT);
- if (unlikely(!burst))
- return NULL;
-
- INIT_LIST_HEAD(&burst->list);
- if (chunk->burst) {
- /* Create and add new element into the linked list */
- chunk->bursts_alloc++;
- list_add_tail(&burst->list, &chunk->burst->list);
- } else {
- /* List head */
- chunk->bursts_alloc = 0;
- chunk->burst = burst;
- }
-
- return burst;
-}
-
-static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc)
+static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc, u32 nburst)
{
struct dw_edma_chan *chan = desc->chan;
struct dw_edma_chunk *chunk;
- chunk = kzalloc(sizeof(*chunk), GFP_NOWAIT);
+ chunk = kzalloc(struct_size(chunk, burst, nburst), GFP_NOWAIT);
if (unlikely(!chunk))
return NULL;
- INIT_LIST_HEAD(&chunk->list);
chunk->chan = chan;
/* Toggling change bit (CB) in each chunk, this is a mechanism to
* inform the eDMA HW block that this is a new linked list ready
@@ -81,20 +58,10 @@ static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc)
*/
chunk->cb = !(desc->chunks_alloc % 2);
- if (desc->chunk) {
- /* Create and add new element into the linked list */
- if (!dw_edma_alloc_burst(chunk)) {
- kfree(chunk);
- return NULL;
- }
- desc->chunks_alloc++;
- list_add_tail(&chunk->list, &desc->chunk->list);
- } else {
- /* List head */
- chunk->burst = NULL;
- desc->chunks_alloc = 0;
- desc->chunk = chunk;
- }
+ chunk->nburst = nburst;
+
+ list_add_tail(&chunk->list, &desc->chunk_list);
+ desc->chunks_alloc++;
return chunk;
}
@@ -108,53 +75,23 @@ static struct dw_edma_desc *dw_edma_alloc_desc(struct dw_edma_chan *chan)
return NULL;
desc->chan = chan;
- if (!dw_edma_alloc_chunk(desc)) {
- kfree(desc);
- return NULL;
- }
- return desc;
-}
+ INIT_LIST_HEAD(&desc->chunk_list);
-static void dw_edma_free_burst(struct dw_edma_chunk *chunk)
-{
- struct dw_edma_burst *child, *_next;
-
- /* Remove all the list elements */
- list_for_each_entry_safe(child, _next, &chunk->burst->list, list) {
- list_del(&child->list);
- kfree(child);
- chunk->bursts_alloc--;
- }
-
- /* Remove the list head */
- kfree(child);
- chunk->burst = NULL;
+ return desc;
}
-static void dw_edma_free_chunk(struct dw_edma_desc *desc)
+static void dw_edma_free_desc(struct dw_edma_desc *desc)
{
struct dw_edma_chunk *child, *_next;
- if (!desc->chunk)
- return;
-
/* Remove all the list elements */
- list_for_each_entry_safe(child, _next, &desc->chunk->list, list) {
- dw_edma_free_burst(child);
+ list_for_each_entry_safe(child, _next, &desc->chunk_list, list) {
list_del(&child->list);
kfree(child);
desc->chunks_alloc--;
}
- /* Remove the list head */
- kfree(child);
- desc->chunk = NULL;
-}
-
-static void dw_edma_free_desc(struct dw_edma_desc *desc)
-{
- dw_edma_free_chunk(desc);
kfree(desc);
}
@@ -166,15 +103,11 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc)
static void dw_edma_core_start(struct dw_edma_chunk *chunk, bool first)
{
struct dw_edma_chan *chan = chunk->chan;
- struct dw_edma_burst *child;
u32 i = 0;
- int j;
- j = chunk->bursts_alloc;
- list_for_each_entry(child, &chunk->burst->list, list) {
- j--;
- dw_edma_core_ll_data(chan, child, i++, chunk->cb, !j);
- }
+ for (i = 0; i < chunk->nburst; i++)
+ dw_edma_core_ll_data(chan, &chunk->burst[i], i, chunk->cb,
+ i == chunk->nburst - 1);
dw_edma_core_ll_link(chan, i, chunk->cb, chan->ll_region.paddr);
@@ -198,14 +131,13 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan)
if (!desc)
return 0;
- child = list_first_entry_or_null(&desc->chunk->list,
+ child = list_first_entry_or_null(&desc->chunk_list,
struct dw_edma_chunk, list);
if (!child)
return 0;
dw_edma_core_start(child, !desc->xfer_sz);
desc->xfer_sz += child->xfer_sz;
- dw_edma_free_burst(child);
list_del(&child->list);
kfree(child);
desc->chunks_alloc--;
@@ -375,13 +307,13 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer,
struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
enum dma_transfer_direction dir = xfer->direction;
struct scatterlist *sg = NULL;
- struct dw_edma_chunk *chunk;
+ struct dw_edma_chunk *chunk = NULL;
struct dw_edma_burst *burst;
struct dw_edma_desc *desc;
u64 src_addr, dst_addr;
size_t fsz = 0;
u32 cnt = 0;
- int i;
+ u32 i;
if (!chan->configured)
return NULL;
@@ -441,10 +373,6 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer,
if (unlikely(!desc))
goto err_alloc;
- chunk = dw_edma_alloc_chunk(desc);
- if (unlikely(!chunk))
- goto err_alloc;
-
if (xfer->type == EDMA_XFER_INTERLEAVED) {
src_addr = xfer->xfer.il->src_start;
dst_addr = xfer->xfer.il->dst_start;
@@ -472,15 +400,15 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer,
if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg)
break;
- if (chunk->bursts_alloc == chan->ll_max) {
- chunk = dw_edma_alloc_chunk(desc);
+ if (!(i % chan->ll_max)) {
+ u32 n = min(cnt - i, chan->ll_max);
+
+ chunk = dw_edma_alloc_chunk(desc, n);
if (unlikely(!chunk))
goto err_alloc;
}
- burst = dw_edma_alloc_burst(chunk);
- if (unlikely(!burst))
- goto err_alloc;
+ burst = chunk->burst + (i % chan->ll_max);
if (xfer->type == EDMA_XFER_CYCLIC)
burst->sz = xfer->xfer.cyclic.len;
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index 0ad460e1942221c678a76e9ab2aee35f1af4eb25..e9e0346c35b1917a5ef2c545fdf1754d18684154 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -43,7 +43,6 @@ struct dw_edma_chan;
struct dw_edma_chunk;
struct dw_edma_burst {
- struct list_head list;
u64 sar;
u64 dar;
u32 sz;
@@ -52,18 +51,16 @@ struct dw_edma_burst {
struct dw_edma_chunk {
struct list_head list;
struct dw_edma_chan *chan;
- struct dw_edma_burst *burst;
-
- u32 bursts_alloc;
-
u8 cb;
u32 xfer_sz;
+ u32 nburst;
+ struct dw_edma_burst burst[] __counted_by(nburst);
};
struct dw_edma_desc {
struct virt_dma_desc vd;
struct dw_edma_chan *chan;
- struct dw_edma_chunk *chunk;
+ struct list_head chunk_list;
u32 chunks_alloc;
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 11/11] dmaengine: dw-edma: Remove struct dw_edma_chunk
2026-01-09 15:28 [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Frank Li
` (9 preceding siblings ...)
2026-01-09 15:28 ` [PATCH v2 10/11] dmaengine: dw-edma: Use burst array instead of linked list Frank Li
@ 2026-01-09 15:28 ` Frank Li
2026-01-12 13:07 ` [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Niklas Cassel
11 siblings, 0 replies; 23+ messages in thread
From: Frank Li @ 2026-01-09 15:28 UTC (permalink / raw)
To: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-hardening, linux-pci, linux-nvme,
imx, Frank Li
The current descriptor layout is:
struct dw_edma_desc *desc
└─ chunk list
└─ burst[]
Creating a DMA descriptor requires at least two kzalloc() calls because
each chunk is allocated as a linked-list node. Since the number of bursts
is already known when the descriptor is created, this linked-list layer is
unnecessary.
Move the burst array directly into struct dw_edma_desc and remove the
struct dw_edma_chunk layer entirely.
Use start_burst and done_burst to track the current bursts, which current
are in the DMA link list.
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v2
- remove debug code
- move "residue = desc->alloc_sz;" in if(desc) check
- keep inline to avoid build warning
---
drivers/dma/dw-edma/dw-edma-core.c | 128 ++++++++++++-------------------------
drivers/dma/dw-edma/dw-edma-core.h | 24 ++++---
2 files changed, 56 insertions(+), 96 deletions(-)
diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
index 9e65155fd93d69ddbc8235fad671fad4dc120979..1c8aef5e03b0e2c93aec9f1cb0588b4e8b1508d9 100644
--- a/drivers/dma/dw-edma/dw-edma-core.c
+++ b/drivers/dma/dw-edma/dw-edma-core.c
@@ -40,76 +40,45 @@ u64 dw_edma_get_pci_address(struct dw_edma_chan *chan, phys_addr_t cpu_addr)
return cpu_addr;
}
-static struct dw_edma_chunk *dw_edma_alloc_chunk(struct dw_edma_desc *desc, u32 nburst)
-{
- struct dw_edma_chan *chan = desc->chan;
- struct dw_edma_chunk *chunk;
-
- chunk = kzalloc(struct_size(chunk, burst, nburst), GFP_NOWAIT);
- if (unlikely(!chunk))
- return NULL;
-
- chunk->chan = chan;
- /* Toggling change bit (CB) in each chunk, this is a mechanism to
- * inform the eDMA HW block that this is a new linked list ready
- * to be consumed.
- * - Odd chunks originate CB equal to 0
- * - Even chunks originate CB equal to 1
- */
- chunk->cb = !(desc->chunks_alloc % 2);
-
- chunk->nburst = nburst;
-
- list_add_tail(&chunk->list, &desc->chunk_list);
- desc->chunks_alloc++;
-
- return chunk;
-}
-
-static struct dw_edma_desc *dw_edma_alloc_desc(struct dw_edma_chan *chan)
+static struct dw_edma_desc *
+dw_edma_alloc_desc(struct dw_edma_chan *chan, u32 nburst)
{
struct dw_edma_desc *desc;
- desc = kzalloc(sizeof(*desc), GFP_NOWAIT);
+ desc = kzalloc(struct_size(desc, burst, nburst), GFP_NOWAIT);
if (unlikely(!desc))
return NULL;
desc->chan = chan;
-
- INIT_LIST_HEAD(&desc->chunk_list);
+ desc->nburst = nburst;
+ desc->cb = true;
return desc;
}
-static void dw_edma_free_desc(struct dw_edma_desc *desc)
-{
- struct dw_edma_chunk *child, *_next;
-
- /* Remove all the list elements */
- list_for_each_entry_safe(child, _next, &desc->chunk_list, list) {
- list_del(&child->list);
- kfree(child);
- desc->chunks_alloc--;
- }
-
- kfree(desc);
-}
-
static void vchan_free_desc(struct virt_dma_desc *vdesc)
{
- dw_edma_free_desc(vd2dw_edma_desc(vdesc));
+ kfree(vd2dw_edma_desc(vdesc));
}
-static void dw_edma_core_start(struct dw_edma_chunk *chunk, bool first)
+static void dw_edma_core_start(struct dw_edma_desc *desc, bool first)
{
- struct dw_edma_chan *chan = chunk->chan;
+ struct dw_edma_chan *chan = desc->chan;
u32 i = 0;
- for (i = 0; i < chunk->nburst; i++)
- dw_edma_core_ll_data(chan, &chunk->burst[i], i, chunk->cb,
- i == chunk->nburst - 1);
+ for (i = 0; i < desc->nburst; i++) {
+ if (i == chan->ll_max - 1)
+ break;
+
+ dw_edma_core_ll_data(chan, &desc->burst[i + desc->start_burst],
+ i, desc->cb,
+ i == desc->nburst - 1 || i == chan->ll_max - 2);
+ }
- dw_edma_core_ll_link(chan, i, chunk->cb, chan->ll_region.paddr);
+ desc->done_burst = desc->start_burst;
+ desc->start_burst += i;
+
+ dw_edma_core_ll_link(chan, i, desc->cb, chan->ll_region.paddr);
if (first)
dw_edma_core_ch_enable(chan);
@@ -119,7 +88,6 @@ static void dw_edma_core_start(struct dw_edma_chunk *chunk, bool first)
static int dw_edma_start_transfer(struct dw_edma_chan *chan)
{
- struct dw_edma_chunk *child;
struct dw_edma_desc *desc;
struct virt_dma_desc *vd;
@@ -131,16 +99,9 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan)
if (!desc)
return 0;
- child = list_first_entry_or_null(&desc->chunk_list,
- struct dw_edma_chunk, list);
- if (!child)
- return 0;
+ dw_edma_core_start(desc, !desc->start_burst);
- dw_edma_core_start(child, !desc->xfer_sz);
- desc->xfer_sz += child->xfer_sz;
- list_del(&child->list);
- kfree(child);
- desc->chunks_alloc--;
+ desc->cb = !desc->cb;
return 1;
}
@@ -289,8 +250,10 @@ dw_edma_device_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
vd = vchan_find_desc(&chan->vc, cookie);
if (vd) {
desc = vd2dw_edma_desc(vd);
- if (desc)
- residue = desc->alloc_sz - desc->xfer_sz;
+
+ residue = desc->alloc_sz;
+ if (desc && desc->done_burst)
+ residue -= desc->burst[desc->done_burst].xfer_sz;
}
spin_unlock_irqrestore(&chan->vc.lock, flags);
@@ -307,7 +270,6 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer,
struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
enum dma_transfer_direction dir = xfer->direction;
struct scatterlist *sg = NULL;
- struct dw_edma_chunk *chunk = NULL;
struct dw_edma_burst *burst;
struct dw_edma_desc *desc;
u64 src_addr, dst_addr;
@@ -369,10 +331,6 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer,
return NULL;
}
- desc = dw_edma_alloc_desc(chan);
- if (unlikely(!desc))
- goto err_alloc;
-
if (xfer->type == EDMA_XFER_INTERLEAVED) {
src_addr = xfer->xfer.il->src_start;
dst_addr = xfer->xfer.il->dst_start;
@@ -396,19 +354,15 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer,
fsz = xfer->xfer.il->frame_size;
}
+ desc = dw_edma_alloc_desc(chan, cnt);
+ if (unlikely(!desc))
+ return NULL;
+
for (i = 0; i < cnt; i++) {
if (xfer->type == EDMA_XFER_SCATTER_GATHER && !sg)
break;
- if (!(i % chan->ll_max)) {
- u32 n = min(cnt - i, chan->ll_max);
-
- chunk = dw_edma_alloc_chunk(desc, n);
- if (unlikely(!chunk))
- goto err_alloc;
- }
-
- burst = chunk->burst + (i % chan->ll_max);
+ burst = desc->burst + i;
if (xfer->type == EDMA_XFER_CYCLIC)
burst->sz = xfer->xfer.cyclic.len;
@@ -417,8 +371,8 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer,
else if (xfer->type == EDMA_XFER_INTERLEAVED)
burst->sz = xfer->xfer.il->sgl[i % fsz].size;
- chunk->xfer_sz += burst->sz;
desc->alloc_sz += burst->sz;
+ burst->xfer_sz = desc->alloc_sz;
if (dir == DMA_DEV_TO_MEM) {
burst->sar = src_addr;
@@ -473,12 +427,6 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer,
}
return vchan_tx_prep(&chan->vc, &desc->vd, xfer->flags);
-
-err_alloc:
- if (desc)
- dw_edma_free_desc(desc);
-
- return NULL;
}
static struct dma_async_tx_descriptor *
@@ -551,8 +499,14 @@ static void dw_hdma_set_callback_result(struct virt_dma_desc *vd,
return;
desc = vd2dw_edma_desc(vd);
- if (desc)
- residue = desc->alloc_sz - desc->xfer_sz;
+ if (desc) {
+ residue = desc->alloc_sz;
+
+ if (result == DMA_TRANS_NOERROR)
+ residue -= desc->burst[desc->start_burst - 1].xfer_sz;
+ else if (desc->done_burst)
+ residue -= desc->burst[desc->done_burst - 1].xfer_sz;
+ }
res = &vd->tx_result;
res->result = result;
@@ -571,7 +525,7 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
switch (chan->request) {
case EDMA_REQ_NONE:
desc = vd2dw_edma_desc(vd);
- if (!desc->chunks_alloc) {
+ if (desc->start_burst >= desc->nburst) {
dw_hdma_set_callback_result(vd,
DMA_TRANS_NOERROR);
list_del(&vd->node);
diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h
index e9e0346c35b1917a5ef2c545fdf1754d18684154..31039eb85079cbbd38a90d249091113ad646c6f9 100644
--- a/drivers/dma/dw-edma/dw-edma-core.h
+++ b/drivers/dma/dw-edma/dw-edma-core.h
@@ -46,15 +46,8 @@ struct dw_edma_burst {
u64 sar;
u64 dar;
u32 sz;
-};
-
-struct dw_edma_chunk {
- struct list_head list;
- struct dw_edma_chan *chan;
- u8 cb;
+ /* precalulate summary of previous burst total size */
u32 xfer_sz;
- u32 nburst;
- struct dw_edma_burst burst[] __counted_by(nburst);
};
struct dw_edma_desc {
@@ -66,6 +59,12 @@ struct dw_edma_desc {
u32 alloc_sz;
u32 xfer_sz;
+
+ u32 done_burst;
+ u32 start_burst;
+ u8 cb;
+ u32 nburst;
+ struct dw_edma_burst burst[] __counted_by(nburst);
};
struct dw_edma_chan {
@@ -126,7 +125,6 @@ struct dw_edma_core_ops {
void (*ll_link)(struct dw_edma_chan *chan, u32 idx, bool cb, u64 addr);
void (*ch_doorbell)(struct dw_edma_chan *chan);
void (*ch_enable)(struct dw_edma_chan *chan);
-
void (*ch_config)(struct dw_edma_chan *chan);
void (*debugfs_on)(struct dw_edma *dw);
};
@@ -166,6 +164,14 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan)
return vc2dw_edma_chan(to_virt_chan(dchan));
}
+static inline u64 dw_edma_core_get_ll_paddr(struct dw_edma_chan *chan)
+{
+ if (chan->dir == EDMA_DIR_WRITE)
+ return chan->dw->chip->ll_region_wr[chan->id].paddr;
+
+ return chan->dw->chip->ll_region_rd[chan->id].paddr;
+}
+
static inline
void dw_edma_core_off(struct dw_edma *dw)
{
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code
2026-01-09 15:28 [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Frank Li
` (10 preceding siblings ...)
2026-01-09 15:28 ` [PATCH v2 11/11] dmaengine: dw-edma: Remove struct dw_edma_chunk Frank Li
@ 2026-01-12 13:07 ` Niklas Cassel
11 siblings, 0 replies; 23+ messages in thread
From: Niklas Cassel @ 2026-01-12 13:07 UTC (permalink / raw)
To: Frank Li
Cc: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
dmaengine, linux-kernel, linux-hardening, linux-pci, linux-nvme,
imx
Hello Frank,
Thanks for doing this work!
Sorry for pointing out a lot of typos here.
However, I do think it gives a better impression if there are fewer typos.
On Fri, Jan 09, 2026 at 10:28:20AM -0500, Frank Li wrote:
Subject: dmaengine: dw-edma: flatten desc structions and simple code
s/structions/structures/
s/simple/simplify/
> This patch week depend on the below serise.
s/serise/series/
> https://lore.kernel.org/imx/20251208-dma_prep_config-v1-0-53490c5e1e2a@nxp.com/
Should this not be based on:
https://lore.kernel.org/dmaengine/20260105-dma_prep_config-v3-0-a8480362fd42@nxp.com/
instead?
>
> Basic change
>
> struct dw_edma_desc *desc
> └─ chunk list
> └─ burst list
>
> To
>
> struct dw_edma_desc *desc
> └─ burst[n]
>
> And reduce at least 2 times kzalloc() for each dma descriptor create.
s/create/creation/
>
> I only test eDMA part, not hardware test hdma part.
>
> The finial goal is dymatic add DMA request when DMA running. So needn't
s/finial/final/
Here and many other places:
s/dymatic/dynamic/
Also in patch 1/11:
s/dynamtic/dynamic/
> wait for irq for fetch next round DMA request.
>
> This work is neccesary to for dymatic DMA request appending.
s/to/too/
s/neccessary/necessary/
>
> The post this part first to review and test firstly during working dymatic
> DMA part.
>
> performance is little bit better. Use NVME as EP function
s/NVME/NVMe/
>
> Before
>
> Rnd read, 4KB, QD=1, 1 job : IOPS=6660, BW=26.0MiB/s (27.3MB/s)
> Rnd read, 4KB, QD=32, 1 job : IOPS=28.6k, BW=112MiB/s (117MB/s)
> Rnd read, 4KB, QD=32, 4 jobs: IOPS=33.4k, BW=130MiB/s (137MB/s)
> Rnd read, 128KB, QD=1, 1 job : IOPS=914, BW=114MiB/s (120MB/s)
> Rnd read, 128KB, QD=32, 1 job : IOPS=1204, BW=151MiB/s (158MB/s)
> Rnd read, 128KB, QD=32, 4 jobs: IOPS=1255, BW=157MiB/s (165MB/s)
> Rnd read, 512KB, QD=1, 1 job : IOPS=248, BW=124MiB/s (131MB/s)
> Rnd read, 512KB, QD=32, 1 job : IOPS=353, BW=177MiB/s (185MB/s)
> Rnd read, 512KB, QD=32, 4 jobs: IOPS=388, BW=194MiB/s (204MB/s)
> Rnd write, 4KB, QD=1, 1 job : IOPS=6241, BW=24.4MiB/s (25.6MB/s)
> Rnd write, 4KB, QD=32, 1 job : IOPS=24.7k, BW=96.5MiB/s (101MB/s)
> Rnd write, 4KB, QD=32, 4 jobs: IOPS=26.9k, BW=105MiB/s (110MB/s)
> Rnd write, 128KB, QD=1, 1 job : IOPS=780, BW=97.5MiB/s (102MB/s)
> Rnd write, 128KB, QD=32, 1 job : IOPS=987, BW=123MiB/s (129MB/s)
> Rnd write, 128KB, QD=32, 4 jobs: IOPS=1021, BW=128MiB/s (134MB/s)
> Seq read, 128KB, QD=1, 1 job : IOPS=1190, BW=149MiB/s (156MB/s)
> Seq read, 128KB, QD=32, 1 job : IOPS=1400, BW=175MiB/s (184MB/s)
> Seq read, 512KB, QD=1, 1 job : IOPS=243, BW=122MiB/s (128MB/s)
> Seq read, 512KB, QD=32, 1 job : IOPS=355, BW=178MiB/s (186MB/s)
> Seq read, 1MB, QD=32, 1 job : IOPS=191, BW=192MiB/s (201MB/s)
> Seq write, 128KB, QD=1, 1 job : IOPS=784, BW=98.1MiB/s (103MB/s)
> Seq write, 128KB, QD=32, 1 job : IOPS=1030, BW=129MiB/s (135MB/s)
> Seq write, 512KB, QD=1, 1 job : IOPS=216, BW=108MiB/s (114MB/s)
> Seq write, 512KB, QD=32, 1 job : IOPS=295, BW=148MiB/s (155MB/s)
> Seq write, 1MB, QD=32, 1 job : IOPS=164, BW=165MiB/s (173MB/s)
> Rnd rdwr, 4K..1MB, QD=8, 4 jobs: IOPS=250, BW=126MiB/s (132MB/s)
> IOPS=261, BW=132MiB/s (138MB/s
>
> After
> Rnd read, 4KB, QD=1, 1 job : IOPS=6780, BW=26.5MiB/s (27.8MB/s)
> Rnd read, 4KB, QD=32, 1 job : IOPS=28.6k, BW=112MiB/s (117MB/s)
> Rnd read, 4KB, QD=32, 4 jobs: IOPS=33.4k, BW=130MiB/s (137MB/s)
> Rnd read, 128KB, QD=1, 1 job : IOPS=1188, BW=149MiB/s (156MB/s)
> Rnd read, 128KB, QD=32, 1 job : IOPS=1440, BW=180MiB/s (189MB/s)
> Rnd read, 128KB, QD=32, 4 jobs: IOPS=1282, BW=160MiB/s (168MB/s)
> Rnd read, 512KB, QD=1, 1 job : IOPS=254, BW=127MiB/s (134MB/s)
> Rnd read, 512KB, QD=32, 1 job : IOPS=354, BW=177MiB/s (186MB/s)
> Rnd read, 512KB, QD=32, 4 jobs: IOPS=388, BW=194MiB/s (204MB/s)
> Rnd write, 4KB, QD=1, 1 job : IOPS=6282, BW=24.5MiB/s (25.7MB/s)
> Rnd write, 4KB, QD=32, 1 job : IOPS=24.9k, BW=97.5MiB/s (102MB/s)
> Rnd write, 4KB, QD=32, 4 jobs: IOPS=27.4k, BW=107MiB/s (112MB/s)
> Rnd write, 128KB, QD=1, 1 job : IOPS=1098, BW=137MiB/s (144MB/s)
> Rnd write, 128KB, QD=32, 1 job : IOPS=1195, BW=149MiB/s (157MB/s)
> Rnd write, 128KB, QD=32, 4 jobs: IOPS=1120, BW=140MiB/s (147MB/s)
> Seq read, 128KB, QD=1, 1 job : IOPS=936, BW=117MiB/s (123MB/s)
> Seq read, 128KB, QD=32, 1 job : IOPS=1218, BW=152MiB/s (160MB/s)
> Seq read, 512KB, QD=1, 1 job : IOPS=301, BW=151MiB/s (158MB/s)
> Seq read, 512KB, QD=32, 1 job : IOPS=360, BW=180MiB/s (189MB/s)
> Seq read, 1MB, QD=32, 1 job : IOPS=193, BW=194MiB/s (203MB/s)
> Seq write, 128KB, QD=1, 1 job : IOPS=796, BW=99.5MiB/s (104MB/s)
> Seq write, 128KB, QD=32, 1 job : IOPS=1019, BW=127MiB/s (134MB/s)
> Seq write, 512KB, QD=1, 1 job : IOPS=213, BW=107MiB/s (112MB/s)
> Seq write, 512KB, QD=32, 1 job : IOPS=273, BW=137MiB/s (143MB/s)
> Seq write, 1MB, QD=32, 1 job : IOPS=168, BW=168MiB/s (177MB/s)
> Rnd rdwr, 4K..1MB, QD=8, 4 jobs: IOPS=255, BW=128MiB/s (134MB/s)
> IOPS=266, BW=135MiB/s (141MB/s)
>
> To: Manivannan Sadhasivam <mani@kernel.org>
> To: Vinod Koul <vkoul@kernel.org>
> To: Gustavo Pimentel <Gustavo.Pimentel@synopsys.com>
> To: Kees Cook <kees@kernel.org>
> To: Gustavo A. R. Silva <gustavoars@kernel.org>
> Cc: dmaengine@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> Cc: linux-hardening@vger.kernel.org
> To: Manivannan Sadhasivam <mani@kernel.org>
> To: Krzysztof Wilczyński <kwilczynski@kernel.org>
> To: Kishon Vijay Abraham I <kishon@kernel.org>
> To: Bjorn Helgaas <bhelgaas@google.com>
> To: Christoph Hellwig <hch@lst.de>
> To: Niklas Cassel <cassel@kernel.org>
> Cc: linux-pci@vger.kernel.org
> Cc: linux-nvme@lists.infradead.org
> Cc: imx@lists.linux.dev
>
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
> ---
> Changes in v2:
> - use 'eDMA' and 'HDMA' at commit message
> - remove debug code.
> - keep 'inline' to avoid build warning
> - Link to v1: https://lore.kernel.org/r/20251212-edma_ll-v1-0-fc863d9f5ca3@nxp.com
>
> ---
> Frank Li (11):
> dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK
> dmaengine: dw-edma: Move control field update of DMA link to the last step
> dmaengine: dw-edma: Add xfer_sz field to struct dw_edma_chunk
> dmaengine: dw-edma: Remove ll_max = -1 in dw_edma_channel_setup()
> dmaengine: dw-edma: Move ll_region from struct dw_edma_chunk to struct dw_edma_chan
> dmaengine: dw-edma: Pass down dw_edma_chan to reduce one level of indirection
> dmaengine: dw-edma: Add helper dw_(edma|hdma)_v0_core_ch_enable()
> dmaengine: dw-edma: Add callbacks to fill link list entries
> dmaengine: dw-edma: Use common dw_edma_core_start() for both eDMA and HDMA
> dmaengine: dw-edma: Use burst array instead of linked list
> dmaengine: dw-edma: Remove struct dw_edma_chunk
>
> drivers/dma/dw-edma/dw-edma-core.c | 203 +++++++----------------------
> drivers/dma/dw-edma/dw-edma-core.h | 64 +++++++---
> drivers/dma/dw-edma/dw-edma-v0-core.c | 234 +++++++++++++++++-----------------
> drivers/dma/dw-edma/dw-hdma-v0-core.c | 147 +++++++++++----------
> 4 files changed, 292 insertions(+), 356 deletions(-)
> ---
> base-commit: 5498240f25c3ccbd33af3197bec1578d678dc34d
> change-id: 20251211-edma_ll-0904ba089f01
>
> Best regards,
> --
> Frank Li <Frank.Li@nxp.com>
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 01/11] dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK
2026-01-09 15:28 ` [PATCH v2 01/11] dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK Frank Li
@ 2026-02-25 8:26 ` Koichiro Den
2026-02-25 15:50 ` Frank Li
0 siblings, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-02-25 8:26 UTC (permalink / raw)
To: Frank Li
Cc: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel, dmaengine, linux-kernel, linux-hardening,
linux-pci, linux-nvme, imx
On Fri, Jan 09, 2026 at 10:28:21AM -0500, Frank Li wrote:
> The DONE_INT_MASK and ABORT_INT_MASK registers are shared by all DMA
> channels, and modifying them requires a read-modify-write sequence.
> Because this operation is not atomic, concurrent calls to
> dw_edma_v0_core_start() can introduce race conditions if two channels
> update these registers simultaneously.
>
> Add a spinlock to serialize access to these registers and prevent race
> conditions.
>
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
> ---
> vc.lock protect should be another problem. This one just fix register
> access for difference DMA channel.
>
> Other improve defer to dynamtic append descriptor works later.
> ---
> drivers/dma/dw-edma/dw-edma-v0-core.c | 6 ++++++
> 1 file changed, 6 insertions(+)
Hi Frank,
I'm very interested in seeing the work toward the "dynamic append" series land,
but in my opinion this one can be submitted independently.
Even in the current mainline, under concurrent multi-channel load, this race can
already be triggered.
Also, with this patch, dw->lock is no longer "Only for legacy", so we should
probably update the comment in dw-edma-core.h.
Best regards,
Koichiro
>
> diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> index b75fdaffad9a4ea6cd8d15e8f43bea550848b46c..2850a9df80f54d00789144415ed2dfe31dea3965 100644
> --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> @@ -364,6 +364,7 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> {
> struct dw_edma_chan *chan = chunk->chan;
> struct dw_edma *dw = chan->dw;
> + unsigned long flags;
> u32 tmp;
>
> dw_edma_v0_core_write_chunk(chunk);
> @@ -408,6 +409,8 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> }
> }
> /* Interrupt unmask - done, abort */
> + raw_spin_lock_irqsave(&dw->lock, flags);
> +
> tmp = GET_RW_32(dw, chan->dir, int_mask);
> tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
> tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
> @@ -416,6 +419,9 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
> tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id));
> SET_RW_32(dw, chan->dir, linked_list_err_en, tmp);
> +
> + raw_spin_unlock_irqrestore(&dw->lock, flags);
> +
> /* Channel control */
> SET_CH_32(dw, chan->dir, chan->id, ch_control1,
> (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE));
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 04/11] dmaengine: dw-edma: Remove ll_max = -1 in dw_edma_channel_setup()
2026-01-09 15:28 ` [PATCH v2 04/11] dmaengine: dw-edma: Remove ll_max = -1 in dw_edma_channel_setup() Frank Li
@ 2026-02-25 8:30 ` Koichiro Den
2026-02-25 15:43 ` Frank Li
0 siblings, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-02-25 8:30 UTC (permalink / raw)
To: Frank Li
Cc: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel, dmaengine, linux-kernel, linux-hardening,
linux-pci, linux-nvme, imx
On Fri, Jan 09, 2026 at 10:28:24AM -0500, Frank Li wrote:
> dw_edma_channel_setup() calculates ll_max based on the size of the
> ll_region, but the value is later overwritten with -1, preventing the
> code from ever reaching the calculated ll_max.
>
> Typically ll_max is around 170 for a 4 KB page and four DMA R/W channels.
> It is uncommon for a single DMA request to reach this limit, so the issue
> has not been observed in practice. However, if it occurs, the driver may
> overwrite adjacent memory before reporting an error.
>
> Remove the incorrect assignment so the calculated ll_max is honored
>
> Fixes: 31fb8c1ff962d ("dmaengine: dw-edma: Improve the linked list and data blocks definition")
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
> ---
> drivers/dma/dw-edma/dw-edma-core.c | 1 -
> 1 file changed, 1 deletion(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index c6b014949afe82f10362711fc8a956fe60a72835..b154bdd7f2897d9a28df698a425afc1b1c93698b 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -770,7 +770,6 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ);
> else
> chan->ll_max = (chip->ll_region_rd[chan->id].sz / EDMA_LL_SZ);
> - chan->ll_max -= 1;
Just curious: wasn't this to reserve one slot for the final link element?
Best regards,
Koichiro
>
> dev_vdbg(dev, "L. List:\tChannel %s[%u] max_cnt=%u\n",
> str_write_read(chan->dir == EDMA_DIR_WRITE),
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 04/11] dmaengine: dw-edma: Remove ll_max = -1 in dw_edma_channel_setup()
2026-02-25 8:30 ` Koichiro Den
@ 2026-02-25 15:43 ` Frank Li
2026-02-26 2:16 ` Koichiro Den
0 siblings, 1 reply; 23+ messages in thread
From: Frank Li @ 2026-02-25 15:43 UTC (permalink / raw)
To: Koichiro Den
Cc: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel, dmaengine, linux-kernel, linux-hardening,
linux-pci, linux-nvme, imx
On Wed, Feb 25, 2026 at 05:30:33PM +0900, Koichiro Den wrote:
> On Fri, Jan 09, 2026 at 10:28:24AM -0500, Frank Li wrote:
> > dw_edma_channel_setup() calculates ll_max based on the size of the
> > ll_region, but the value is later overwritten with -1, preventing the
> > code from ever reaching the calculated ll_max.
> >
> > Typically ll_max is around 170 for a 4 KB page and four DMA R/W channels.
> > It is uncommon for a single DMA request to reach this limit, so the issue
> > has not been observed in practice. However, if it occurs, the driver may
> > overwrite adjacent memory before reporting an error.
> >
> > Remove the incorrect assignment so the calculated ll_max is honored
> >
> > Fixes: 31fb8c1ff962d ("dmaengine: dw-edma: Improve the linked list and data blocks definition")
> > Signed-off-by: Frank Li <Frank.Li@nxp.com>
> > ---
> > drivers/dma/dw-edma/dw-edma-core.c | 1 -
> > 1 file changed, 1 deletion(-)
> >
> > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > index c6b014949afe82f10362711fc8a956fe60a72835..b154bdd7f2897d9a28df698a425afc1b1c93698b 100644
> > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > @@ -770,7 +770,6 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> > chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ);
> > else
> > chan->ll_max = (chip->ll_region_rd[chan->id].sz / EDMA_LL_SZ);
> > - chan->ll_max -= 1;
>
> Just curious: wasn't this to reserve one slot for the final link element?
when calculate avaible entry, always use chan-ll_max -1. ll_max indicate
memory size, final link element actually occupted a space.
Frank
>
> Best regards,
> Koichiro
>
> >
> > dev_vdbg(dev, "L. List:\tChannel %s[%u] max_cnt=%u\n",
> > str_write_read(chan->dir == EDMA_DIR_WRITE),
> >
> > --
> > 2.34.1
> >
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 01/11] dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK
2026-02-25 8:26 ` Koichiro Den
@ 2026-02-25 15:50 ` Frank Li
2026-02-26 2:22 ` Koichiro Den
0 siblings, 1 reply; 23+ messages in thread
From: Frank Li @ 2026-02-25 15:50 UTC (permalink / raw)
To: Koichiro Den
Cc: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel, dmaengine, linux-kernel, linux-hardening,
linux-pci, linux-nvme, imx
On Wed, Feb 25, 2026 at 05:26:02PM +0900, Koichiro Den wrote:
> On Fri, Jan 09, 2026 at 10:28:21AM -0500, Frank Li wrote:
> > The DONE_INT_MASK and ABORT_INT_MASK registers are shared by all DMA
> > channels, and modifying them requires a read-modify-write sequence.
> > Because this operation is not atomic, concurrent calls to
> > dw_edma_v0_core_start() can introduce race conditions if two channels
> > update these registers simultaneously.
> >
> > Add a spinlock to serialize access to these registers and prevent race
> > conditions.
> >
> > Signed-off-by: Frank Li <Frank.Li@nxp.com>
> > ---
> > vc.lock protect should be another problem. This one just fix register
> > access for difference DMA channel.
> >
> > Other improve defer to dynamtic append descriptor works later.
> > ---
> > drivers/dma/dw-edma/dw-edma-v0-core.c | 6 ++++++
> > 1 file changed, 6 insertions(+)
>
> Hi Frank,
>
> I'm very interested in seeing the work toward the "dynamic append" series land,
> but in my opinion this one can be submitted independently.
This patch serial is actually straight forwards. we can ask vnod pick first
one in case have other problems. put together to reduce patch's dependency.
Frank
>
> Even in the current mainline, under concurrent multi-channel load, this race can
> already be triggered.
>
> Also, with this patch, dw->lock is no longer "Only for legacy", so we should
> probably update the comment in dw-edma-core.h.
>
> Best regards,
> Koichiro
>
> >
> > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > index b75fdaffad9a4ea6cd8d15e8f43bea550848b46c..2850a9df80f54d00789144415ed2dfe31dea3965 100644
> > --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > @@ -364,6 +364,7 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > {
> > struct dw_edma_chan *chan = chunk->chan;
> > struct dw_edma *dw = chan->dw;
> > + unsigned long flags;
> > u32 tmp;
> >
> > dw_edma_v0_core_write_chunk(chunk);
> > @@ -408,6 +409,8 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > }
> > }
> > /* Interrupt unmask - done, abort */
> > + raw_spin_lock_irqsave(&dw->lock, flags);
> > +
> > tmp = GET_RW_32(dw, chan->dir, int_mask);
> > tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
> > tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
> > @@ -416,6 +419,9 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
> > tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id));
> > SET_RW_32(dw, chan->dir, linked_list_err_en, tmp);
> > +
> > + raw_spin_unlock_irqrestore(&dw->lock, flags);
> > +
> > /* Channel control */
> > SET_CH_32(dw, chan->dir, chan->id, ch_control1,
> > (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE));
> >
> > --
> > 2.34.1
> >
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 04/11] dmaengine: dw-edma: Remove ll_max = -1 in dw_edma_channel_setup()
2026-02-25 15:43 ` Frank Li
@ 2026-02-26 2:16 ` Koichiro Den
2026-02-26 15:21 ` Frank Li
0 siblings, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-02-26 2:16 UTC (permalink / raw)
To: Frank Li
Cc: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel, dmaengine, linux-kernel, linux-hardening,
linux-pci, linux-nvme, imx
On Wed, Feb 25, 2026 at 10:43:32AM -0500, Frank Li wrote:
> On Wed, Feb 25, 2026 at 05:30:33PM +0900, Koichiro Den wrote:
> > On Fri, Jan 09, 2026 at 10:28:24AM -0500, Frank Li wrote:
> > > dw_edma_channel_setup() calculates ll_max based on the size of the
> > > ll_region, but the value is later overwritten with -1, preventing the
> > > code from ever reaching the calculated ll_max.
> > >
> > > Typically ll_max is around 170 for a 4 KB page and four DMA R/W channels.
> > > It is uncommon for a single DMA request to reach this limit, so the issue
> > > has not been observed in practice. However, if it occurs, the driver may
> > > overwrite adjacent memory before reporting an error.
> > >
> > > Remove the incorrect assignment so the calculated ll_max is honored
> > >
> > > Fixes: 31fb8c1ff962d ("dmaengine: dw-edma: Improve the linked list and data blocks definition")
> > > Signed-off-by: Frank Li <Frank.Li@nxp.com>
> > > ---
> > > drivers/dma/dw-edma/dw-edma-core.c | 1 -
> > > 1 file changed, 1 deletion(-)
> > >
> > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > > index c6b014949afe82f10362711fc8a956fe60a72835..b154bdd7f2897d9a28df698a425afc1b1c93698b 100644
> > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > @@ -770,7 +770,6 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> > > chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ);
> > > else
> > > chan->ll_max = (chip->ll_region_rd[chan->id].sz / EDMA_LL_SZ);
> > > - chan->ll_max -= 1;
> >
> > Just curious: wasn't this to reserve one slot for the final link element?
>
> when calculate avaible entry, always use chan-ll_max -1. ll_max indicate
> memory size, final link element actually occupted a space.
After the entire series is applied (esp. [PATCH v2 10/11] + [PATCH v2 11/11]),
yes, that makes sense to me. My concern was that before the semantics of
"ll_max" changes, this "-1" was required. In other words, this seemed to me not
a fix but a preparatory patch. Please correct me if I'm misunderstainding.
Thanks,
Koichiro
>
> Frank
> >
> > Best regards,
> > Koichiro
> >
> > >
> > > dev_vdbg(dev, "L. List:\tChannel %s[%u] max_cnt=%u\n",
> > > str_write_read(chan->dir == EDMA_DIR_WRITE),
> > >
> > > --
> > > 2.34.1
> > >
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 01/11] dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK
2026-02-25 15:50 ` Frank Li
@ 2026-02-26 2:22 ` Koichiro Den
2026-02-26 15:23 ` Frank Li
0 siblings, 1 reply; 23+ messages in thread
From: Koichiro Den @ 2026-02-26 2:22 UTC (permalink / raw)
To: Frank Li
Cc: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel, dmaengine, linux-kernel, linux-hardening,
linux-pci, linux-nvme, imx
On Wed, Feb 25, 2026 at 10:50:41AM -0500, Frank Li wrote:
> On Wed, Feb 25, 2026 at 05:26:02PM +0900, Koichiro Den wrote:
> > On Fri, Jan 09, 2026 at 10:28:21AM -0500, Frank Li wrote:
> > > The DONE_INT_MASK and ABORT_INT_MASK registers are shared by all DMA
> > > channels, and modifying them requires a read-modify-write sequence.
> > > Because this operation is not atomic, concurrent calls to
> > > dw_edma_v0_core_start() can introduce race conditions if two channels
> > > update these registers simultaneously.
> > >
> > > Add a spinlock to serialize access to these registers and prevent race
> > > conditions.
> > >
> > > Signed-off-by: Frank Li <Frank.Li@nxp.com>
> > > ---
> > > vc.lock protect should be another problem. This one just fix register
> > > access for difference DMA channel.
> > >
> > > Other improve defer to dynamtic append descriptor works later.
> > > ---
> > > drivers/dma/dw-edma/dw-edma-v0-core.c | 6 ++++++
> > > 1 file changed, 6 insertions(+)
> >
> > Hi Frank,
> >
> > I'm very interested in seeing the work toward the "dynamic append" series land,
> > but in my opinion this one can be submitted independently.
>
> This patch serial is actually straight forwards. we can ask vnod pick first
> one in case have other problems. put together to reduce patch's dependency.
Yes, I see.
My understanding is that the originally planned dependency chain was:
#1->#2->#3
#1 [PATCH v2 0/8] dmaengine: Add new API to combine onfiguration and descriptor preparation
https://lore.kernel.org/dmaengine/20251218-dma_prep_config-v2-0-c07079836128@nxp.com/
#2 (this series)
#3 [PATCH RFT 0/5] dmaengine: dw-edma: support dynamtic add link entry during dma engine running
https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-0-9a98c9c98536@nxp.com/
I'm not sure whether #1 will proceed, as the thread appears to have stalled. I
might be missing something, though. In any case, #1 is semantically orthogonal
to #2, so I believe #2 can be considered independently.
Thanks,
Koichiro
>
> Frank
> >
> > Even in the current mainline, under concurrent multi-channel load, this race can
> > already be triggered.
> >
> > Also, with this patch, dw->lock is no longer "Only for legacy", so we should
> > probably update the comment in dw-edma-core.h.
> >
> > Best regards,
> > Koichiro
> >
> > >
> > > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > > index b75fdaffad9a4ea6cd8d15e8f43bea550848b46c..2850a9df80f54d00789144415ed2dfe31dea3965 100644
> > > --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> > > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > > @@ -364,6 +364,7 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > > {
> > > struct dw_edma_chan *chan = chunk->chan;
> > > struct dw_edma *dw = chan->dw;
> > > + unsigned long flags;
> > > u32 tmp;
> > >
> > > dw_edma_v0_core_write_chunk(chunk);
> > > @@ -408,6 +409,8 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > > }
> > > }
> > > /* Interrupt unmask - done, abort */
> > > + raw_spin_lock_irqsave(&dw->lock, flags);
> > > +
> > > tmp = GET_RW_32(dw, chan->dir, int_mask);
> > > tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
> > > tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
> > > @@ -416,6 +419,9 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > > tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
> > > tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id));
> > > SET_RW_32(dw, chan->dir, linked_list_err_en, tmp);
> > > +
> > > + raw_spin_unlock_irqrestore(&dw->lock, flags);
> > > +
> > > /* Channel control */
> > > SET_CH_32(dw, chan->dir, chan->id, ch_control1,
> > > (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE));
> > >
> > > --
> > > 2.34.1
> > >
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 04/11] dmaengine: dw-edma: Remove ll_max = -1 in dw_edma_channel_setup()
2026-02-26 2:16 ` Koichiro Den
@ 2026-02-26 15:21 ` Frank Li
2026-02-27 6:30 ` Koichiro Den
0 siblings, 1 reply; 23+ messages in thread
From: Frank Li @ 2026-02-26 15:21 UTC (permalink / raw)
To: Koichiro Den
Cc: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel, dmaengine, linux-kernel, linux-hardening,
linux-pci, linux-nvme, imx
On Thu, Feb 26, 2026 at 11:16:26AM +0900, Koichiro Den wrote:
> On Wed, Feb 25, 2026 at 10:43:32AM -0500, Frank Li wrote:
> > On Wed, Feb 25, 2026 at 05:30:33PM +0900, Koichiro Den wrote:
> > > On Fri, Jan 09, 2026 at 10:28:24AM -0500, Frank Li wrote:
> > > > dw_edma_channel_setup() calculates ll_max based on the size of the
> > > > ll_region, but the value is later overwritten with -1, preventing the
> > > > code from ever reaching the calculated ll_max.
> > > >
> > > > Typically ll_max is around 170 for a 4 KB page and four DMA R/W channels.
> > > > It is uncommon for a single DMA request to reach this limit, so the issue
> > > > has not been observed in practice. However, if it occurs, the driver may
> > > > overwrite adjacent memory before reporting an error.
> > > >
> > > > Remove the incorrect assignment so the calculated ll_max is honored
> > > >
> > > > Fixes: 31fb8c1ff962d ("dmaengine: dw-edma: Improve the linked list and data blocks definition")
> > > > Signed-off-by: Frank Li <Frank.Li@nxp.com>
> > > > ---
> > > > drivers/dma/dw-edma/dw-edma-core.c | 1 -
> > > > 1 file changed, 1 deletion(-)
> > > >
> > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > > > index c6b014949afe82f10362711fc8a956fe60a72835..b154bdd7f2897d9a28df698a425afc1b1c93698b 100644
> > > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > > @@ -770,7 +770,6 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> > > > chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ);
> > > > else
> > > > chan->ll_max = (chip->ll_region_rd[chan->id].sz / EDMA_LL_SZ);
> > > > - chan->ll_max -= 1;
> > >
> > > Just curious: wasn't this to reserve one slot for the final link element?
> >
> > when calculate avaible entry, always use chan-ll_max -1. ll_max indicate
> > memory size, final link element actually occupted a space.
>
> After the entire series is applied (esp. [PATCH v2 10/11] + [PATCH v2 11/11]),
> yes, that makes sense to me. My concern was that before the semantics of
> "ll_max" changes, this "-1" was required. In other words, this seemed to me not
> a fix but a preparatory patch. Please correct me if I'm misunderstainding.
Thanks, I found I make mistake, wrong think it as "chan->ll_max = -1".
I will squash this change to patch 10.
Frank
>
> Thanks,
> Koichiro
>
> >
> > Frank
> > >
> > > Best regards,
> > > Koichiro
> > >
> > > >
> > > > dev_vdbg(dev, "L. List:\tChannel %s[%u] max_cnt=%u\n",
> > > > str_write_read(chan->dir == EDMA_DIR_WRITE),
> > > >
> > > > --
> > > > 2.34.1
> > > >
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 01/11] dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK
2026-02-26 2:22 ` Koichiro Den
@ 2026-02-26 15:23 ` Frank Li
2026-02-27 6:29 ` Koichiro Den
0 siblings, 1 reply; 23+ messages in thread
From: Frank Li @ 2026-02-26 15:23 UTC (permalink / raw)
To: Koichiro Den
Cc: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel, dmaengine, linux-kernel, linux-hardening,
linux-pci, linux-nvme, imx
On Thu, Feb 26, 2026 at 11:22:41AM +0900, Koichiro Den wrote:
> On Wed, Feb 25, 2026 at 10:50:41AM -0500, Frank Li wrote:
> > On Wed, Feb 25, 2026 at 05:26:02PM +0900, Koichiro Den wrote:
> > > On Fri, Jan 09, 2026 at 10:28:21AM -0500, Frank Li wrote:
> > > > The DONE_INT_MASK and ABORT_INT_MASK registers are shared by all DMA
> > > > channels, and modifying them requires a read-modify-write sequence.
> > > > Because this operation is not atomic, concurrent calls to
> > > > dw_edma_v0_core_start() can introduce race conditions if two channels
> > > > update these registers simultaneously.
> > > >
> > > > Add a spinlock to serialize access to these registers and prevent race
> > > > conditions.
> > > >
> > > > Signed-off-by: Frank Li <Frank.Li@nxp.com>
> > > > ---
> > > > vc.lock protect should be another problem. This one just fix register
> > > > access for difference DMA channel.
> > > >
> > > > Other improve defer to dynamtic append descriptor works later.
> > > > ---
> > > > drivers/dma/dw-edma/dw-edma-v0-core.c | 6 ++++++
> > > > 1 file changed, 6 insertions(+)
> > >
> > > Hi Frank,
> > >
> > > I'm very interested in seeing the work toward the "dynamic append" series land,
> > > but in my opinion this one can be submitted independently.
> >
> > This patch serial is actually straight forwards. we can ask vnod pick first
> > one in case have other problems. put together to reduce patch's dependency.
>
> Yes, I see.
>
> My understanding is that the originally planned dependency chain was:
>
> #1->#2->#3
>
> #1 [PATCH v2 0/8] dmaengine: Add new API to combine onfiguration and descriptor preparation
> https://lore.kernel.org/dmaengine/20251218-dma_prep_config-v2-0-c07079836128@nxp.com/
> #2 (this series)
> #3 [PATCH RFT 0/5] dmaengine: dw-edma: support dynamtic add link entry during dma engine running
> https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-0-9a98c9c98536@nxp.com/
>
> I'm not sure whether #1 will proceed, as the thread appears to have stalled. I
Vnod said he will review #1 in this week. If not progress, I will create
new one without dependent #1.
Frank
> might be missing something, though. In any case, #1 is semantically orthogonal
> to #2, so I believe #2 can be considered independently.
>
> Thanks,
> Koichiro
>
> >
> > Frank
> > >
> > > Even in the current mainline, under concurrent multi-channel load, this race can
> > > already be triggered.
> > >
> > > Also, with this patch, dw->lock is no longer "Only for legacy", so we should
> > > probably update the comment in dw-edma-core.h.
> > >
> > > Best regards,
> > > Koichiro
> > >
> > > >
> > > > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > > > index b75fdaffad9a4ea6cd8d15e8f43bea550848b46c..2850a9df80f54d00789144415ed2dfe31dea3965 100644
> > > > --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> > > > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > > > @@ -364,6 +364,7 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > > > {
> > > > struct dw_edma_chan *chan = chunk->chan;
> > > > struct dw_edma *dw = chan->dw;
> > > > + unsigned long flags;
> > > > u32 tmp;
> > > >
> > > > dw_edma_v0_core_write_chunk(chunk);
> > > > @@ -408,6 +409,8 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > > > }
> > > > }
> > > > /* Interrupt unmask - done, abort */
> > > > + raw_spin_lock_irqsave(&dw->lock, flags);
> > > > +
> > > > tmp = GET_RW_32(dw, chan->dir, int_mask);
> > > > tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
> > > > tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
> > > > @@ -416,6 +419,9 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > > > tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
> > > > tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id));
> > > > SET_RW_32(dw, chan->dir, linked_list_err_en, tmp);
> > > > +
> > > > + raw_spin_unlock_irqrestore(&dw->lock, flags);
> > > > +
> > > > /* Channel control */
> > > > SET_CH_32(dw, chan->dir, chan->id, ch_control1,
> > > > (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE));
> > > >
> > > > --
> > > > 2.34.1
> > > >
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 01/11] dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK
2026-02-26 15:23 ` Frank Li
@ 2026-02-27 6:29 ` Koichiro Den
0 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-02-27 6:29 UTC (permalink / raw)
To: Frank Li
Cc: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel, dmaengine, linux-kernel, linux-hardening,
linux-pci, linux-nvme, imx
On Thu, Feb 26, 2026 at 10:23:27AM -0500, Frank Li wrote:
> On Thu, Feb 26, 2026 at 11:22:41AM +0900, Koichiro Den wrote:
> > On Wed, Feb 25, 2026 at 10:50:41AM -0500, Frank Li wrote:
> > > On Wed, Feb 25, 2026 at 05:26:02PM +0900, Koichiro Den wrote:
> > > > On Fri, Jan 09, 2026 at 10:28:21AM -0500, Frank Li wrote:
> > > > > The DONE_INT_MASK and ABORT_INT_MASK registers are shared by all DMA
> > > > > channels, and modifying them requires a read-modify-write sequence.
> > > > > Because this operation is not atomic, concurrent calls to
> > > > > dw_edma_v0_core_start() can introduce race conditions if two channels
> > > > > update these registers simultaneously.
> > > > >
> > > > > Add a spinlock to serialize access to these registers and prevent race
> > > > > conditions.
> > > > >
> > > > > Signed-off-by: Frank Li <Frank.Li@nxp.com>
> > > > > ---
> > > > > vc.lock protect should be another problem. This one just fix register
> > > > > access for difference DMA channel.
> > > > >
> > > > > Other improve defer to dynamtic append descriptor works later.
> > > > > ---
> > > > > drivers/dma/dw-edma/dw-edma-v0-core.c | 6 ++++++
> > > > > 1 file changed, 6 insertions(+)
> > > >
> > > > Hi Frank,
> > > >
> > > > I'm very interested in seeing the work toward the "dynamic append" series land,
> > > > but in my opinion this one can be submitted independently.
> > >
> > > This patch serial is actually straight forwards. we can ask vnod pick first
> > > one in case have other problems. put together to reduce patch's dependency.
> >
> > Yes, I see.
> >
> > My understanding is that the originally planned dependency chain was:
> >
> > #1->#2->#3
> >
> > #1 [PATCH v2 0/8] dmaengine: Add new API to combine onfiguration and descriptor preparation
> > https://lore.kernel.org/dmaengine/20251218-dma_prep_config-v2-0-c07079836128@nxp.com/
> > #2 (this series)
> > #3 [PATCH RFT 0/5] dmaengine: dw-edma: support dynamtic add link entry during dma engine running
> > https://lore.kernel.org/dmaengine/20260109-edma_dymatic-v1-0-9a98c9c98536@nxp.com/
> >
> > I'm not sure whether #1 will proceed, as the thread appears to have stalled. I
>
> Vnod said he will review #1 in this week. If not progress, I will create
> new one without dependent #1.
Thanks for letting me know that.
Best regards,
Koichiro
>
> Frank
>
> > might be missing something, though. In any case, #1 is semantically orthogonal
> > to #2, so I believe #2 can be considered independently.
> >
> > Thanks,
> > Koichiro
> >
> > >
> > > Frank
> > > >
> > > > Even in the current mainline, under concurrent multi-channel load, this race can
> > > > already be triggered.
> > > >
> > > > Also, with this patch, dw->lock is no longer "Only for legacy", so we should
> > > > probably update the comment in dw-edma-core.h.
> > > >
> > > > Best regards,
> > > > Koichiro
> > > >
> > > > >
> > > > > diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > > > > index b75fdaffad9a4ea6cd8d15e8f43bea550848b46c..2850a9df80f54d00789144415ed2dfe31dea3965 100644
> > > > > --- a/drivers/dma/dw-edma/dw-edma-v0-core.c
> > > > > +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c
> > > > > @@ -364,6 +364,7 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > > > > {
> > > > > struct dw_edma_chan *chan = chunk->chan;
> > > > > struct dw_edma *dw = chan->dw;
> > > > > + unsigned long flags;
> > > > > u32 tmp;
> > > > >
> > > > > dw_edma_v0_core_write_chunk(chunk);
> > > > > @@ -408,6 +409,8 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > > > > }
> > > > > }
> > > > > /* Interrupt unmask - done, abort */
> > > > > + raw_spin_lock_irqsave(&dw->lock, flags);
> > > > > +
> > > > > tmp = GET_RW_32(dw, chan->dir, int_mask);
> > > > > tmp &= ~FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id));
> > > > > tmp &= ~FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id));
> > > > > @@ -416,6 +419,9 @@ static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first)
> > > > > tmp = GET_RW_32(dw, chan->dir, linked_list_err_en);
> > > > > tmp |= FIELD_PREP(EDMA_V0_LINKED_LIST_ERR_MASK, BIT(chan->id));
> > > > > SET_RW_32(dw, chan->dir, linked_list_err_en, tmp);
> > > > > +
> > > > > + raw_spin_unlock_irqrestore(&dw->lock, flags);
> > > > > +
> > > > > /* Channel control */
> > > > > SET_CH_32(dw, chan->dir, chan->id, ch_control1,
> > > > > (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE));
> > > > >
> > > > > --
> > > > > 2.34.1
> > > > >
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v2 04/11] dmaengine: dw-edma: Remove ll_max = -1 in dw_edma_channel_setup()
2026-02-26 15:21 ` Frank Li
@ 2026-02-27 6:30 ` Koichiro Den
0 siblings, 0 replies; 23+ messages in thread
From: Koichiro Den @ 2026-02-27 6:30 UTC (permalink / raw)
To: Frank Li
Cc: Manivannan Sadhasivam, Vinod Koul, Gustavo Pimentel, Kees Cook,
Gustavo A. R. Silva, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Niklas Cassel, dmaengine, linux-kernel, linux-hardening,
linux-pci, linux-nvme, imx
On Thu, Feb 26, 2026 at 10:21:28AM -0500, Frank Li wrote:
> On Thu, Feb 26, 2026 at 11:16:26AM +0900, Koichiro Den wrote:
> > On Wed, Feb 25, 2026 at 10:43:32AM -0500, Frank Li wrote:
> > > On Wed, Feb 25, 2026 at 05:30:33PM +0900, Koichiro Den wrote:
> > > > On Fri, Jan 09, 2026 at 10:28:24AM -0500, Frank Li wrote:
> > > > > dw_edma_channel_setup() calculates ll_max based on the size of the
> > > > > ll_region, but the value is later overwritten with -1, preventing the
> > > > > code from ever reaching the calculated ll_max.
> > > > >
> > > > > Typically ll_max is around 170 for a 4 KB page and four DMA R/W channels.
> > > > > It is uncommon for a single DMA request to reach this limit, so the issue
> > > > > has not been observed in practice. However, if it occurs, the driver may
> > > > > overwrite adjacent memory before reporting an error.
> > > > >
> > > > > Remove the incorrect assignment so the calculated ll_max is honored
> > > > >
> > > > > Fixes: 31fb8c1ff962d ("dmaengine: dw-edma: Improve the linked list and data blocks definition")
> > > > > Signed-off-by: Frank Li <Frank.Li@nxp.com>
> > > > > ---
> > > > > drivers/dma/dw-edma/dw-edma-core.c | 1 -
> > > > > 1 file changed, 1 deletion(-)
> > > > >
> > > > > diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > index c6b014949afe82f10362711fc8a956fe60a72835..b154bdd7f2897d9a28df698a425afc1b1c93698b 100644
> > > > > --- a/drivers/dma/dw-edma/dw-edma-core.c
> > > > > +++ b/drivers/dma/dw-edma/dw-edma-core.c
> > > > > @@ -770,7 +770,6 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> > > > > chan->ll_max = (chip->ll_region_wr[chan->id].sz / EDMA_LL_SZ);
> > > > > else
> > > > > chan->ll_max = (chip->ll_region_rd[chan->id].sz / EDMA_LL_SZ);
> > > > > - chan->ll_max -= 1;
> > > >
> > > > Just curious: wasn't this to reserve one slot for the final link element?
> > >
> > > when calculate avaible entry, always use chan-ll_max -1. ll_max indicate
> > > memory size, final link element actually occupted a space.
> >
> > After the entire series is applied (esp. [PATCH v2 10/11] + [PATCH v2 11/11]),
> > yes, that makes sense to me. My concern was that before the semantics of
> > "ll_max" changes, this "-1" was required. In other words, this seemed to me not
> > a fix but a preparatory patch. Please correct me if I'm misunderstainding.
>
> Thanks, I found I make mistake, wrong think it as "chan->ll_max = -1".
> I will squash this change to patch 10.
Thanks for the confirmation. I agree with the idea of squashing.
Best regards,
Koichiro
>
> Frank
>
> >
> > Thanks,
> > Koichiro
> >
> > >
> > > Frank
> > > >
> > > > Best regards,
> > > > Koichiro
> > > >
> > > > >
> > > > > dev_vdbg(dev, "L. List:\tChannel %s[%u] max_cnt=%u\n",
> > > > > str_write_read(chan->dir == EDMA_DIR_WRITE),
> > > > >
> > > > > --
> > > > > 2.34.1
> > > > >
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-02-27 6:30 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-09 15:28 [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Frank Li
2026-01-09 15:28 ` [PATCH v2 01/11] dmaengine: dw-edma: Add spinlock to protect DONE_INT_MASK and ABORT_INT_MASK Frank Li
2026-02-25 8:26 ` Koichiro Den
2026-02-25 15:50 ` Frank Li
2026-02-26 2:22 ` Koichiro Den
2026-02-26 15:23 ` Frank Li
2026-02-27 6:29 ` Koichiro Den
2026-01-09 15:28 ` [PATCH v2 02/11] dmaengine: dw-edma: Move control field update of DMA link to the last step Frank Li
2026-01-09 15:28 ` [PATCH v2 03/11] dmaengine: dw-edma: Add xfer_sz field to struct dw_edma_chunk Frank Li
2026-01-09 15:28 ` [PATCH v2 04/11] dmaengine: dw-edma: Remove ll_max = -1 in dw_edma_channel_setup() Frank Li
2026-02-25 8:30 ` Koichiro Den
2026-02-25 15:43 ` Frank Li
2026-02-26 2:16 ` Koichiro Den
2026-02-26 15:21 ` Frank Li
2026-02-27 6:30 ` Koichiro Den
2026-01-09 15:28 ` [PATCH v2 05/11] dmaengine: dw-edma: Move ll_region from struct dw_edma_chunk to struct dw_edma_chan Frank Li
2026-01-09 15:28 ` [PATCH v2 06/11] dmaengine: dw-edma: Pass down dw_edma_chan to reduce one level of indirection Frank Li
2026-01-09 15:28 ` [PATCH v2 07/11] dmaengine: dw-edma: Add helper dw_(edma|hdma)_v0_core_ch_enable() Frank Li
2026-01-09 15:28 ` [PATCH v2 08/11] dmaengine: dw-edma: Add callbacks to fill link list entries Frank Li
2026-01-09 15:28 ` [PATCH v2 09/11] dmaengine: dw-edma: Use common dw_edma_core_start() for both eDMA and HDMA Frank Li
2026-01-09 15:28 ` [PATCH v2 10/11] dmaengine: dw-edma: Use burst array instead of linked list Frank Li
2026-01-09 15:28 ` [PATCH v2 11/11] dmaengine: dw-edma: Remove struct dw_edma_chunk Frank Li
2026-01-12 13:07 ` [PATCH v2 00/11] dmaengine: dw-edma: flatten desc structions and simple code Niklas Cassel
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox