* [PATCHv2 1/2] dmaengine: idma64: use kzalloc_flex
2026-07-12 22:00 [PATCHv2 0/2] dmaengine: idma64: descriptor allocation and length limit fixes Rosen Penev
@ 2026-07-12 22:00 ` Rosen Penev
2026-07-13 11:57 ` Andy Shevchenko
2026-07-12 22:00 ` [PATCHv2 2/2] dmaengine: idma64: use sg_nents_for_dma to respect hardware descriptor length limit Rosen Penev
1 sibling, 1 reply; 5+ messages in thread
From: Rosen Penev @ 2026-07-12 22:00 UTC (permalink / raw)
To: dmaengine
Cc: Andy Shevchenko, Vinod Koul, Frank Li, Kees Cook,
Gustavo A. R. Silva, open list,
open list:KERNEL HARDENING (not covered by other
areas):Keyword:b__counted_by(_le|_be|_ptr)?b
Simplifies allocations by using a flexible array member in this struct.
Remove idma64_alloc_desc. It now offers no readability advantages in
this single usage.
Add __counted_by to get extra runtime analysis.
Apply the exact same treatment to struct idma64_dma and devm_kzalloc.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
---
drivers/dma/idma64.c | 30 ++++--------------------------
drivers/dma/idma64.h | 4 ++--
2 files changed, 6 insertions(+), 28 deletions(-)
diff --git a/drivers/dma/idma64.c b/drivers/dma/idma64.c
index 5fcd1befc92d..d914f50ec309 100644
--- a/drivers/dma/idma64.c
+++ b/drivers/dma/idma64.c
@@ -192,23 +192,6 @@ static irqreturn_t idma64_irq(int irq, void *dev)
/* ---------------------------------------------------------------------- */
-static struct idma64_desc *idma64_alloc_desc(unsigned int ndesc)
-{
- struct idma64_desc *desc;
-
- desc = kzalloc_obj(*desc, GFP_NOWAIT);
- if (!desc)
- return NULL;
-
- desc->hw = kzalloc_objs(*desc->hw, ndesc, GFP_NOWAIT);
- if (!desc->hw) {
- kfree(desc);
- return NULL;
- }
-
- return desc;
-}
-
static void idma64_desc_free(struct idma64_chan *idma64c,
struct idma64_desc *desc)
{
@@ -223,7 +206,6 @@ static void idma64_desc_free(struct idma64_chan *idma64c,
} while (i);
}
- kfree(desc->hw);
kfree(desc);
}
@@ -307,10 +289,12 @@ static struct dma_async_tx_descriptor *idma64_prep_slave_sg(
struct scatterlist *sg;
unsigned int i;
- desc = idma64_alloc_desc(sg_len);
+ desc = kzalloc_flex(*desc, hw, sg_len, GFP_NOWAIT);
if (!desc)
return NULL;
+ desc->ndesc = sg_len;
+
for_each_sg(sgl, sg, sg_len, i) {
struct idma64_hw_desc *hw = &desc->hw[i];
@@ -326,7 +310,6 @@ static struct dma_async_tx_descriptor *idma64_prep_slave_sg(
hw->len = sg_dma_len(sg);
}
- desc->ndesc = sg_len;
desc->direction = direction;
desc->status = DMA_IN_PROGRESS;
@@ -541,18 +524,13 @@ static int idma64_probe(struct idma64_chip *chip)
unsigned short i;
int ret;
- idma64 = devm_kzalloc(chip->dev, sizeof(*idma64), GFP_KERNEL);
+ idma64 = devm_kzalloc(chip->dev, struct_size(idma64, chan, nr_chan), GFP_KERNEL);
if (!idma64)
return -ENOMEM;
idma64->regs = chip->regs;
chip->idma64 = idma64;
- idma64->chan = devm_kcalloc(chip->dev, nr_chan, sizeof(*idma64->chan),
- GFP_KERNEL);
- if (!idma64->chan)
- return -ENOMEM;
-
idma64->all_chan_mask = (1 << nr_chan) - 1;
/* Turn off iDMA controller */
diff --git a/drivers/dma/idma64.h b/drivers/dma/idma64.h
index d013b54356aa..1a67dbb24db5 100644
--- a/drivers/dma/idma64.h
+++ b/drivers/dma/idma64.h
@@ -113,10 +113,10 @@ struct idma64_hw_desc {
struct idma64_desc {
struct virt_dma_desc vdesc;
enum dma_transfer_direction direction;
- struct idma64_hw_desc *hw;
unsigned int ndesc;
size_t length;
enum dma_status status;
+ struct idma64_hw_desc hw[] __counted_by(ndesc);
};
static inline struct idma64_desc *to_idma64_desc(struct virt_dma_desc *vdesc)
@@ -187,7 +187,7 @@ struct idma64 {
/* channels */
unsigned short all_chan_mask;
- struct idma64_chan *chan;
+ struct idma64_chan chan[];
};
static inline struct idma64 *to_idma64(struct dma_device *ddev)
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCHv2 2/2] dmaengine: idma64: use sg_nents_for_dma to respect hardware descriptor length limit
2026-07-12 22:00 [PATCHv2 0/2] dmaengine: idma64: descriptor allocation and length limit fixes Rosen Penev
2026-07-12 22:00 ` [PATCHv2 1/2] dmaengine: idma64: use kzalloc_flex Rosen Penev
@ 2026-07-12 22:00 ` Rosen Penev
2026-07-13 12:13 ` Andy Shevchenko
1 sibling, 1 reply; 5+ messages in thread
From: Rosen Penev @ 2026-07-12 22:00 UTC (permalink / raw)
To: dmaengine
Cc: Andy Shevchenko, Vinod Koul, Frank Li, Kees Cook,
Gustavo A. R. Silva, open list,
open list:KERNEL HARDENING (not covered by other
areas):Keyword:b__counted_by(_le|_be|_ptr)?b
The iDMA 64-bit hardware has a 17-bit block transfer size field in the
CTL_HI register (IDMA64C_CTLH_BLOCK_TS_MASK = 0x1ffff). When a
scatterlist entry exceeds this limit, the driver would silently
truncate the length, transferring fewer bytes than intended.
Use sg_nents_for_dma() to compute the number of hardware descriptors
needed after splitting large SG entries into chunks that fit within
the hardware limit. Split the loop to iterate over each chunk.
Assisted-by: opencode:big-pickle
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/dma/idma64.c | 44 ++++++++++++++++++++++++++++++--------------
drivers/dma/idma64.h | 3 ++-
2 files changed, 32 insertions(+), 15 deletions(-)
diff --git a/drivers/dma/idma64.c b/drivers/dma/idma64.c
index d914f50ec309..6954ec2cdeae 100644
--- a/drivers/dma/idma64.c
+++ b/drivers/dma/idma64.c
@@ -287,27 +287,43 @@ static struct dma_async_tx_descriptor *idma64_prep_slave_sg(
struct idma64_chan *idma64c = to_idma64_chan(chan);
struct idma64_desc *desc;
struct scatterlist *sg;
- unsigned int i;
+ unsigned int i, nents;
+ int ndesc;
- desc = kzalloc_flex(*desc, hw, sg_len, GFP_NOWAIT);
+ ndesc = sg_nents_for_dma(sgl, sg_len, IDMA64C_CTLH_BLOCK_TS_MASK);
+ if (ndesc <= 0)
+ return NULL;
+
+ desc = kzalloc_flex(*desc, hw, ndesc, GFP_NOWAIT);
if (!desc)
return NULL;
- desc->ndesc = sg_len;
+ desc->ndesc = ndesc;
+ nents = 0;
for_each_sg(sgl, sg, sg_len, i) {
- struct idma64_hw_desc *hw = &desc->hw[i];
-
- /* Allocate DMA capable memory for hardware descriptor */
- hw->lli = dma_pool_alloc(idma64c->pool, GFP_NOWAIT, &hw->llp);
- if (!hw->lli) {
- desc->ndesc = i;
- idma64_desc_free(idma64c, desc);
- return NULL;
+ dma_addr_t addr = sg_dma_address(sg);
+ unsigned int len = sg_dma_len(sg);
+
+ while (len) {
+ struct idma64_hw_desc *hwdesc = &desc->hw[nents++];
+ unsigned int chunk = min(len, IDMA64C_CTLH_BLOCK_TS_MASK);
+
+ hwdesc->lli = dma_pool_alloc(idma64c->pool, GFP_NOWAIT,
+ &hwdesc->llp);
+ if (!hwdesc->lli) {
+ /* nents was already incremented by ++ above */
+ desc->ndesc = nents - 1;
+ idma64_desc_free(idma64c, desc);
+ return NULL;
+ }
+
+ hwdesc->phys = addr;
+ hwdesc->len = chunk;
+
+ addr += chunk;
+ len -= chunk;
}
-
- hw->phys = sg_dma_address(sg);
- hw->len = sg_dma_len(sg);
}
desc->direction = direction;
diff --git a/drivers/dma/idma64.h b/drivers/dma/idma64.h
index 1a67dbb24db5..297a91594b31 100644
--- a/drivers/dma/idma64.h
+++ b/drivers/dma/idma64.h
@@ -8,6 +8,7 @@
#ifndef __DMA_IDMA64_H__
#define __DMA_IDMA64_H__
+#include <linux/bits.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/spinlock.h>
@@ -51,7 +52,7 @@
#define IDMA64C_CTLL_LLP_S_EN (1 << 28) /* src block chain */
/* Bitfields in CTL_HI */
-#define IDMA64C_CTLH_BLOCK_TS_MASK ((1 << 17) - 1)
+#define IDMA64C_CTLH_BLOCK_TS_MASK GENMASK_U32(16, 0)
#define IDMA64C_CTLH_BLOCK_TS(x) ((x) & IDMA64C_CTLH_BLOCK_TS_MASK)
#define IDMA64C_CTLH_DONE (1 << 17)
--
2.55.0
^ permalink raw reply [flat|nested] 5+ messages in thread