* [PATCH v5 1/9] dmaengine: Add API to combine configuration and preparation (sg and single)
From: Frank Li @ 2026-05-12 16:41 UTC (permalink / raw)
To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li
In-Reply-To: <20260512-dma_prep_config-v5-0-26865bf7d935@nxp.com>
Previously, configuration and preparation required two separate calls. This
works well when configuration is done only once during initialization.
However, in cases where the burst length or source/destination address must
be adjusted for each transfer, calling two functions is verbose and
requires additional locking to ensure both steps complete atomically.
Add a new API dmaengine_prep_config_single() and dmaengine_prep_config_sg()
and callback device_prep_config_sg() that combines configuration and
preparation into a single operation. If the configuration argument is
passed as NULL, fall back to the existing implementation.
Tested-by: Niklas Cassel <cassel@kernel.org>
Acked-by: Manivannan Sadhasivam <mani@kernel.org>
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
change in v4
- drop context in device_prep_config_sg()
change in v3
- remove Deprecated for callback device_prep_slave_sg().
- Move condition check before sg init.
- split function at return type.
- move safe version to next patch
change in v2
- add () for function
- use short name device_prep_sg(), remove "slave" and "config". the 'slave'
is reduntant. after remove slave, the function name is difference existed
one, so remove _config suffix.
---
Documentation/driver-api/dmaengine/client.rst | 9 ++++
include/linux/dmaengine.h | 63 +++++++++++++++++++++++----
2 files changed, 64 insertions(+), 8 deletions(-)
diff --git a/Documentation/driver-api/dmaengine/client.rst b/Documentation/driver-api/dmaengine/client.rst
index d491e385d61a98b8a804cd823caf254a2dc62cf4..5ee5d4a3596dd986b02f1bce3078ca6c4c1fb45a 100644
--- a/Documentation/driver-api/dmaengine/client.rst
+++ b/Documentation/driver-api/dmaengine/client.rst
@@ -80,6 +80,10 @@ The details of these operations are:
- slave_sg: DMA a list of scatter gather buffers from/to a peripheral
+ - config_sg: Similar with slave_sg, just pass down dma_slave_config
+ struct to avoid calling dmaengine_slave_config() every time adjusting the
+ burst length or the FIFO address is needed.
+
- peripheral_dma_vec: DMA an array of scatter gather buffers from/to a
peripheral. Similar to slave_sg, but uses an array of dma_vec
structures instead of a scatterlist.
@@ -106,6 +110,11 @@ The details of these operations are:
unsigned int sg_len, enum dma_data_direction direction,
unsigned long flags);
+ struct dma_async_tx_descriptor *dmaengine_prep_config_sg(
+ struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction dir,
+ unsigned long flags, struct dma_slave_config *config);
+
struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec(
struct dma_chan *chan, const struct dma_vec *vecs,
size_t nents, enum dma_data_direction direction,
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index b3d251c9734e95e1b75cf6763d4d2c3a1c6a9910..defa377d2ef54d94e6337cdfa7826a091295535e 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -835,6 +835,7 @@ struct dma_filter {
* where the address and size of each segment is located in one entry of
* the dma_vec array.
* @device_prep_slave_sg: prepares a slave dma operation
+ * @device_prep_config_sg: prepares a slave DMA operation with dma_slave_config
* @device_prep_dma_cyclic: prepare a cyclic dma operation suitable for audio.
* The function takes a buffer of size buf_len. The callback function will
* be called after period_len bytes have been transferred.
@@ -934,6 +935,10 @@ struct dma_device {
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
unsigned long flags, void *context);
+ struct dma_async_tx_descriptor *(*device_prep_config_sg)(
+ struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction direction,
+ unsigned long flags, struct dma_slave_config *config);
struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)(
struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
size_t period_len, enum dma_transfer_direction direction,
@@ -974,22 +979,44 @@ static inline bool is_slave_direction(enum dma_transfer_direction direction)
(direction == DMA_DEV_TO_DEV);
}
-static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single(
- struct dma_chan *chan, dma_addr_t buf, size_t len,
- enum dma_transfer_direction dir, unsigned long flags)
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_config_single(struct dma_chan *chan, dma_addr_t buf, size_t len,
+ enum dma_transfer_direction dir,
+ unsigned long flags,
+ struct dma_slave_config *config)
{
struct scatterlist sg;
+
+ if (!chan || !chan->device)
+ return NULL;
+
sg_init_table(&sg, 1);
sg_dma_address(&sg) = buf;
sg_dma_len(&sg) = len;
- if (!chan || !chan->device || !chan->device->device_prep_slave_sg)
+ if (chan->device->device_prep_config_sg)
+ return chan->device->device_prep_config_sg(chan, &sg, 1, dir,
+ flags, config);
+
+ if (config)
+ if (dmaengine_slave_config(chan, config))
+ return NULL;
+
+ if (!chan->device->device_prep_slave_sg)
return NULL;
return chan->device->device_prep_slave_sg(chan, &sg, 1,
dir, flags, NULL);
}
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_slave_single(struct dma_chan *chan, dma_addr_t buf, size_t len,
+ enum dma_transfer_direction dir,
+ unsigned long flags)
+{
+ return dmaengine_prep_config_single(chan, buf, len, dir, flags, NULL);
+}
+
/**
* dmaengine_prep_peripheral_dma_vec() - Prepare a DMA scatter-gather descriptor
* @chan: The channel to be used for this descriptor
@@ -1010,17 +1037,37 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec(
dir, flags);
}
-static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(
- struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
- enum dma_transfer_direction dir, unsigned long flags)
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_config_sg(struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction dir,
+ unsigned long flags, struct dma_slave_config *config)
{
- if (!chan || !chan->device || !chan->device->device_prep_slave_sg)
+ if (!chan || !chan->device)
+ return NULL;
+
+ if (chan->device->device_prep_config_sg)
+ return chan->device->device_prep_config_sg(chan, sgl, sg_len,
+ dir, flags, config);
+
+ if (config)
+ if (dmaengine_slave_config(chan, config))
+ return NULL;
+
+ if (!chan->device->device_prep_slave_sg)
return NULL;
return chan->device->device_prep_slave_sg(chan, sgl, sg_len,
dir, flags, NULL);
}
+static inline struct dma_async_tx_descriptor *
+dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sg_len, enum dma_transfer_direction dir,
+ unsigned long flags)
+{
+ return dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, NULL);
+}
+
#ifdef CONFIG_RAPIDIO_DMA_ENGINE
struct rio_dma_ext;
static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg(
--
2.43.0
^ permalink raw reply related
* [PATCH v5 0/9] dmaengine: Add new API to combine configuration and descriptor preparation
From: Frank Li @ 2026-05-12 16:41 UTC (permalink / raw)
To: Vinod Koul, Manivannan Sadhasivam, Krzysztof Wilczyński,
Kishon Vijay Abraham I, Bjorn Helgaas, Christoph Hellwig,
Sagi Grimberg, Chaitanya Kulkarni, Herbert Xu, David S. Miller,
Nicolas Ferre, Alexandre Belloni, Claudiu Beznea, Koichiro Den,
Niklas Cassel
Cc: dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx, Frank Li,
Damien Le Moal
Previously, configuration and preparation required two separate calls. This
works well when configuration is done only once during initialization.
However, in cases where the burst length or source/destination address must
be adjusted for each transfer, calling two functions is verbose.
if (dmaengine_slave_config(chan, &sconf)) {
dev_err(dev, "DMA slave config fail\n");
return -EIO;
}
tx = dmaengine_prep_slave_single(chan, dma_local, len, dir, flags);
After new API added
tx = dmaengine_prep_config_single(chan, dma_local, len, dir, flags, &sconf);
Additional, prevous two calls requires additional locking to ensure both
steps complete atomically.
mutex_lock()
dmaengine_slave_config()
dmaengine_prep_slave_single()
mutex_unlock()
after new API added, mutex lock can be moved. See patch
nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API
Signed-off-by: Frank Li <Frank.Li@nxp.com>
---
Changes in v5:
- collect Mani's reviewed-by tags
- use kernel doc for new APIs.
- Link to v4: https://lore.kernel.org/r/20260506-dma_prep_config-v4-0-85b3d22babff@nxp.com
Changes in v4:
- remove void* context in config_prep() callback
- use spin lock to protect config() and prep().
- Link to v3: https://lore.kernel.org/r/20260105-dma_prep_config-v3-0-a8480362fd42@nxp.com
Changes in v3:
- collect review tags
- create safe version in framework
- Link to v2: https://lore.kernel.org/r/20251218-dma_prep_config-v2-0-c07079836128@nxp.com
Changes in v2:
- Use name dmaengine_prep_config_single() and dmaengine_prep_config_sg()
- Add _safe version to avoid confuse, which needn't additional mutex.
- Update document/
- Update commit message. add () for function name. Use upcase for subject.
- Add more explain for remove lock.
- Link to v1: https://lore.kernel.org/r/20251208-dma_prep_config-v1-0-53490c5e1e2a@nxp.com
---
Frank Li (9):
dmaengine: Add API to combine configuration and preparation (sg and single)
dmaengine: Add safe API to combine configuration and preparation
PCI: endpoint: pci-epf-test: Use dmaenigne_prep_config_single() to simplify code
dmaengine: dw-edma: Use new .device_prep_config_sg() callback
dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer()
nvmet: pci-epf: Remove unnecessary dmaengine_terminate_sync() on each DMA transfer
nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API
PCI: epf-mhi: Use dmaengine_prep_config_single() to simplify code
crypto: atmel: Use dmaengine_prep_config_single() API
Documentation/driver-api/dmaengine/client.rst | 9 ++
drivers/crypto/atmel-aes.c | 10 +-
drivers/dma/dmaengine.c | 2 +
drivers/dma/dw-edma/dw-edma-core.c | 41 +++++--
drivers/nvme/target/pci-epf.c | 21 +---
drivers/pci/endpoint/functions/pci-epf-mhi.c | 52 +++------
drivers/pci/endpoint/functions/pci-epf-test.c | 8 +-
include/linux/dmaengine.h | 148 ++++++++++++++++++++++++--
8 files changed, 207 insertions(+), 84 deletions(-)
---
base-commit: b9303e6bff706758c167af686b5315ad00233bf8
change-id: 20251204-dma_prep_config-654170d245a2
Best regards,
--
Frank Li <Frank.Li@nxp.com>
^ permalink raw reply
* Re: [PATCH] crypto : ecc - Fix carry overflow in vli multiplication
From: David Laight @ 2026-05-12 15:27 UTC (permalink / raw)
To: Lukas Wunner
Cc: Anastasia Tishchenko, Ignat Korchagin, Stefan Berger, Herbert Xu,
David S . Miller, linux-crypto, linux-kernel
In-Reply-To: <agMvm_bA-OcDWhbc@wunner.de>
On Tue, 12 May 2026 15:48:11 +0200
Lukas Wunner <lukas@wunner.de> wrote:
> On Fri, May 08, 2026 at 02:48:44PM +0300, Anastasia Tishchenko wrote:
> > The carry flag calculation fails when r01.m_high is saturated
> > (0xFFFFFFFFFFFFFFFF) and addition of lower bits overflows.
> >
> > The condition (r01.m_high < product.m_high) doesn't handle the case
> > where r01.m_high == product.m_high and an additional carry exists
> > from lower-bit overflow.
> >
> > Add proper handling for this boundary by accounting for the carry
> > from the lower addition.
> [...]
> > +++ b/crypto/ecc.c
> > @@ -427,7 +427,10 @@ static void vli_mult(u64 *result, const u64 *left, const u64 *right,
> > product = mul_64_64(left[i], right[k - i]);
> >
> > r01 = add_128_128(r01, product);
> > - r2 += (r01.m_high < product.m_high);
> > + if (r01.m_high != product.m_high)
> > + r2 += (r01.m_high < product.m_high);
> > + else
> > + r2 += (r01.m_low < product.m_low);
> > }
> >
> > result[k] = r01.m_low;
>
> ICYMI, sashiko's AI-generated review alleges that the if-else condition
> may cause a timing side channel vis-à-vis binary arithmetic:
>
> https://sashiko.dev/#/patchset/20260508114844.29694-1-sv3iry%40gmail.com
>
> You may want to address this if/when respinning your patch. If you do,
> a code comment is probably merited to explain this subtlety.
Something like:
r2 += (r01.m_high < product.m_high);
r2 += (r01.m_high == product.m_high) & (r01.m_low < product.m_low);
would be constant time - but the compiler is very unlikely to generate
the object code you want on all (any?) architectures.
On x86 you want something like (pardon the pigeon assembler):
xor %rax,%rax
cmp r01.m_high, product.m_high
setc %al
lea r2, (r2, %rax)
sete %al
cmp r01.m_low, product.m_low
cmovnc %al, %ah
add r2, %rax
but I bet (two beers) you can't get it.
-- David
>
> Thanks,
>
> Lukas
>
^ permalink raw reply
* [PATCH] crypto: atmel - use min3 to simplify atmel_sha_append_sg
From: Thorsten Blum @ 2026-05-12 14:51 UTC (permalink / raw)
To: Herbert Xu, David S. Miller, Nicolas Ferre, Alexandre Belloni,
Claudiu Beznea
Cc: Thorsten Blum, linux-crypto, linux-arm-kernel, linux-kernel
Replace two consecutive min() calls with min3() to simplify the code.
And since count is unsigned and cannot be less than zero, adjust the if
check and update the comment accordingly.
Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
---
drivers/crypto/atmel-sha.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/crypto/atmel-sha.c b/drivers/crypto/atmel-sha.c
index 002b62902553..7e7c83a3d8cd 100644
--- a/drivers/crypto/atmel-sha.c
+++ b/drivers/crypto/atmel-sha.c
@@ -305,12 +305,12 @@ static size_t atmel_sha_append_sg(struct atmel_sha_reqctx *ctx)
size_t count;
while ((ctx->bufcnt < ctx->buflen) && ctx->total) {
- count = min(ctx->sg->length - ctx->offset, ctx->total);
- count = min(count, ctx->buflen - ctx->bufcnt);
+ count = min3(ctx->sg->length - ctx->offset, ctx->total,
+ ctx->buflen - ctx->bufcnt);
- if (count <= 0) {
+ if (count == 0) {
/*
- * Check if count <= 0 because the buffer is full or
+ * Check if count == 0 because the buffer is full or
* because the sg length is 0. In the latest case,
* check if there is another sg in the list, a 0 length
* sg doesn't necessarily mean the end of the sg list.
^ permalink raw reply related
* Re: [PATCH v4 7/9] nvmet: pci-epf: Use dmaengine_prep_config_single_safe() API
From: Manivannan Sadhasivam @ 2026-05-12 14:06 UTC (permalink / raw)
To: Frank Li
Cc: Vinod Koul, Krzysztof Wilczyński, Kishon Vijay Abraham I,
Bjorn Helgaas, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, Herbert Xu, David S. Miller, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Koichiro Den, Niklas Cassel,
dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx
In-Reply-To: <20260506-dma_prep_config-v4-7-85b3d22babff@nxp.com>
On Wed, May 06, 2026 at 04:44:19PM -0400, Frank Li wrote:
> Use the new dmaengine_prep_config_single_safe() API to combine the
> configuration and descriptor preparation into a single call.
>
> Since dmaengine_prep_config_single_safe() performs the configuration and
> preparation atomically and the mutex can be removed.
>
> Tested-by: Niklas Cassel <cassel@kernel.org>
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Manivannan Sadhasivam <mani@kernel.org>
- Mani
> ---
> drivers/nvme/target/pci-epf.c | 18 ++++--------------
> 1 file changed, 4 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c
> index 2afe8f4d0e46104a1b3c98db3905cf33e8c9e011..04d8f48d6950349ca97d2dbeae4e38e4714ad0d4 100644
> --- a/drivers/nvme/target/pci-epf.c
> +++ b/drivers/nvme/target/pci-epf.c
> @@ -388,22 +388,15 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
> return -EINVAL;
> }
>
> - mutex_lock(lock);
> -
> dma_dev = dmaengine_get_dma_device(chan);
> dma_addr = dma_map_single(dma_dev, seg->buf, seg->length, dir);
> ret = dma_mapping_error(dma_dev, dma_addr);
> if (ret)
> - goto unlock;
> -
> - ret = dmaengine_slave_config(chan, &sconf);
> - if (ret) {
> - dev_err(dev, "Failed to configure DMA channel\n");
> - goto unmap;
> - }
> + return ret;
>
> - desc = dmaengine_prep_slave_single(chan, dma_addr, seg->length,
> - sconf.direction, DMA_CTRL_ACK);
> + desc = dmaengine_prep_config_single_safe(chan, dma_addr, seg->length,
> + sconf.direction,
> + DMA_CTRL_ACK, &sconf);
> if (!desc) {
> dev_err(dev, "Failed to prepare DMA\n");
> ret = -EIO;
> @@ -426,9 +419,6 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
> unmap:
> dma_unmap_single(dma_dev, dma_addr, seg->length, dir);
>
> -unlock:
> - mutex_unlock(lock);
> -
> return ret;
> }
>
>
> --
> 2.43.0
>
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* Re: [PATCH v4 6/9] nvmet: pci-epf: Remove unnecessary dmaengine_terminate_sync() on each DMA transfer
From: Manivannan Sadhasivam @ 2026-05-12 14:05 UTC (permalink / raw)
To: Frank Li
Cc: Vinod Koul, Krzysztof Wilczyński, Kishon Vijay Abraham I,
Bjorn Helgaas, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, Herbert Xu, David S. Miller, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Koichiro Den, Niklas Cassel,
dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx,
Damien Le Moal
In-Reply-To: <20260506-dma_prep_config-v4-6-85b3d22babff@nxp.com>
On Wed, May 06, 2026 at 04:44:18PM -0400, Frank Li wrote:
> dmaengine_terminate_sync() cancels all pending requests. Calling it for
> every DMA transfer is unnecessary and counterproductive. This function is
> generally intended for cleanup paths such as module removal, device close,
> or unbind operations.
>
> Remove the redundant calls for success path and keep it only at error path.
>
> Tested-by: Niklas Cassel <cassel@kernel.org>
> Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Manivannan Sadhasivam <mani@kernel.org>
- Mani
> ---
> This one also fix stress test failure after remove mutex and use new API
> dmaengine_prep_slave_sg_config().
> ---
> drivers/nvme/target/pci-epf.c | 3 +--
> 1 file changed, 1 insertion(+), 2 deletions(-)
>
> diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c
> index 4e9db96ebfecd796244e5dc67c23e1abb1a14974..2afe8f4d0e46104a1b3c98db3905cf33e8c9e011 100644
> --- a/drivers/nvme/target/pci-epf.c
> +++ b/drivers/nvme/target/pci-epf.c
> @@ -420,10 +420,9 @@ static int nvmet_pci_epf_dma_transfer(struct nvmet_pci_epf *nvme_epf,
> if (dma_sync_wait(chan, cookie) != DMA_COMPLETE) {
> dev_err(dev, "DMA transfer failed\n");
> ret = -EIO;
> + dmaengine_terminate_sync(chan);
> }
>
> - dmaengine_terminate_sync(chan);
> -
> unmap:
> dma_unmap_single(dma_dev, dma_addr, seg->length, dir);
>
>
> --
> 2.43.0
>
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* Re: [PATCH v4 5/9] dmaengine: dw-edma: Pass dma_slave_config to dw_edma_device_transfer()
From: Manivannan Sadhasivam @ 2026-05-12 14:04 UTC (permalink / raw)
To: Frank Li
Cc: Vinod Koul, Krzysztof Wilczyński, Kishon Vijay Abraham I,
Bjorn Helgaas, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, Herbert Xu, David S. Miller, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Koichiro Den, Niklas Cassel,
dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx
In-Reply-To: <20260506-dma_prep_config-v4-5-85b3d22babff@nxp.com>
On Wed, May 06, 2026 at 04:44:17PM -0400, Frank Li wrote:
> Pass dma_slave_config to dw_edma_device_transfer() to support atomic
> configuration and descriptor preparation when a non-NULL config is
> provided to device_prep_config_sg().
>
> Tested-by: Niklas Cassel <cassel@kernel.org>
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
- Mani
> ---
> change in v3
> - rewrite dw_edma_device_slave_config() according to Damien's suggestion.
> ---
> drivers/dma/dw-edma/dw-edma-core.c | 27 +++++++++++++++++++++------
> 1 file changed, 21 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index f7f58b0010e26b529ffb7382d5b166a703587c71..ec6f6b1e482568a27ebe90852d5679672b24a1e9 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -267,6 +267,20 @@ static int dw_edma_device_config(struct dma_chan *dchan,
> return 0;
> }
>
> +static struct dma_slave_config *
> +dw_edma_device_get_config(struct dma_chan *dchan,
> + struct dma_slave_config *config)
> +{
> + struct dw_edma_chan *chan;
> +
> + if (config)
> + return config;
> +
> + chan = dchan2dw_edma_chan(dchan);
> +
> + return &chan->config;
> +}
> +
> static int dw_edma_device_pause(struct dma_chan *dchan)
> {
> struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
> @@ -385,7 +399,8 @@ dw_edma_device_tx_status(struct dma_chan *dchan, dma_cookie_t cookie,
> }
>
> static struct dma_async_tx_descriptor *
> -dw_edma_device_transfer(struct dw_edma_transfer *xfer)
> +dw_edma_device_transfer(struct dw_edma_transfer *xfer,
> + struct dma_slave_config *config)
> {
> struct dw_edma_chan *chan = dchan2dw_edma_chan(xfer->dchan);
> enum dma_transfer_direction dir = xfer->direction;
> @@ -472,8 +487,8 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
> src_addr = xfer->xfer.il->src_start;
> dst_addr = xfer->xfer.il->dst_start;
> } else {
> - src_addr = chan->config.src_addr;
> - dst_addr = chan->config.dst_addr;
> + src_addr = config->src_addr;
> + dst_addr = config->dst_addr;
> }
>
> if (dir == DMA_DEV_TO_MEM)
> @@ -595,7 +610,7 @@ dw_edma_device_prep_config_sg(struct dma_chan *dchan, struct scatterlist *sgl,
> if (config)
> dw_edma_device_config(dchan, config);
>
> - return dw_edma_device_transfer(&xfer);
> + return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, config));
> }
>
> static struct dma_async_tx_descriptor *
> @@ -614,7 +629,7 @@ dw_edma_device_prep_dma_cyclic(struct dma_chan *dchan, dma_addr_t paddr,
> xfer.flags = flags;
> xfer.type = EDMA_XFER_CYCLIC;
>
> - return dw_edma_device_transfer(&xfer);
> + return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, NULL));
> }
>
> static struct dma_async_tx_descriptor *
> @@ -630,7 +645,7 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan,
> xfer.flags = flags;
> xfer.type = EDMA_XFER_INTERLEAVED;
>
> - return dw_edma_device_transfer(&xfer);
> + return dw_edma_device_transfer(&xfer, dw_edma_device_get_config(dchan, NULL));
> }
>
> static void dw_hdma_set_callback_result(struct virt_dma_desc *vd,
>
> --
> 2.43.0
>
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* Re: [PATCH v4 4/9] dmaengine: dw-edma: Use new .device_prep_config_sg() callback
From: Manivannan Sadhasivam @ 2026-05-12 14:03 UTC (permalink / raw)
To: Frank Li
Cc: Vinod Koul, Krzysztof Wilczyński, Kishon Vijay Abraham I,
Bjorn Helgaas, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, Herbert Xu, David S. Miller, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Koichiro Den, Niklas Cassel,
dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx,
Damien Le Moal
In-Reply-To: <20260506-dma_prep_config-v4-4-85b3d22babff@nxp.com>
On Wed, May 06, 2026 at 04:44:16PM -0400, Frank Li wrote:
> Use the new .device_prep_config_sg() callback to combine configuration and
> descriptor preparation.
>
> No functional changes.
>
> Tested-by: Niklas Cassel <cassel@kernel.org>
> Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
- Mani
> ---
> change in v4
> - drop context in callback.
> change in v3
> - add Damien Le Moal review tag
> ---
> drivers/dma/dw-edma/dw-edma-core.c | 14 +++++++++-----
> 1 file changed, 9 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c
> index c2feb3adc79fa94b016913443305b9fae9deef12..f7f58b0010e26b529ffb7382d5b166a703587c71 100644
> --- a/drivers/dma/dw-edma/dw-edma-core.c
> +++ b/drivers/dma/dw-edma/dw-edma-core.c
> @@ -577,10 +577,11 @@ dw_edma_device_transfer(struct dw_edma_transfer *xfer)
> }
>
> static struct dma_async_tx_descriptor *
> -dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
> - unsigned int len,
> - enum dma_transfer_direction direction,
> - unsigned long flags, void *context)
> +dw_edma_device_prep_config_sg(struct dma_chan *dchan, struct scatterlist *sgl,
> + unsigned int len,
> + enum dma_transfer_direction direction,
> + unsigned long flags,
> + struct dma_slave_config *config)
> {
> struct dw_edma_transfer xfer;
>
> @@ -591,6 +592,9 @@ dw_edma_device_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl,
> xfer.flags = flags;
> xfer.type = EDMA_XFER_SCATTER_GATHER;
>
> + if (config)
> + dw_edma_device_config(dchan, config);
> +
> return dw_edma_device_transfer(&xfer);
> }
>
> @@ -970,7 +974,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc)
> dma->device_terminate_all = dw_edma_device_terminate_all;
> dma->device_issue_pending = dw_edma_device_issue_pending;
> dma->device_tx_status = dw_edma_device_tx_status;
> - dma->device_prep_slave_sg = dw_edma_device_prep_slave_sg;
> + dma->device_prep_config_sg = dw_edma_device_prep_config_sg;
> dma->device_prep_dma_cyclic = dw_edma_device_prep_dma_cyclic;
> dma->device_prep_interleaved_dma = dw_edma_device_prep_interleaved_dma;
>
>
> --
> 2.43.0
>
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* Re: [PATCH v4 2/9] dmaengine: Add safe API to combine configuration and preparation
From: Manivannan Sadhasivam @ 2026-05-12 14:03 UTC (permalink / raw)
To: Frank Li
Cc: Vinod Koul, Krzysztof Wilczyński, Kishon Vijay Abraham I,
Bjorn Helgaas, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, Herbert Xu, David S. Miller, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Koichiro Den, Niklas Cassel,
dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx
In-Reply-To: <20260506-dma_prep_config-v4-2-85b3d22babff@nxp.com>
On Wed, May 06, 2026 at 04:44:14PM -0400, Frank Li wrote:
> Introduce dmaengine_prep_config_single_safe() and
> dmaengine_prep_config_sg_safe() to provide a reentrant-safe way to
> combine slave configuration and transfer preparation.
>
> Drivers may implement the new device_prep_config_sg() callback to perform
> both steps atomically. If the callback is not provided, the helpers fall
> back to calling dmaengine_slave_config() followed by
> dmaengine_prep_slave_sg() under per-channel mutex protection.
>
> Tested-by: Niklas Cassel <cassel@kernel.org>
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
> ---
> chagne in v4
> - use spinlock() to protect config() and prep()
>
> change in v3
> - new patch
> ---
> drivers/dma/dmaengine.c | 2 ++
> include/linux/dmaengine.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 60 insertions(+)
>
> diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
> index 405bd2fbb4a3b94fd0bf44526f656f6a19feaad0..ba29e60160c1a0148793bb299849bccfebb6d32b 100644
> --- a/drivers/dma/dmaengine.c
> +++ b/drivers/dma/dmaengine.c
> @@ -1099,6 +1099,8 @@ static int __dma_async_device_channel_register(struct dma_device *device,
> chan->dev->device.parent = device->dev;
> chan->dev->chan = chan;
> chan->dev->dev_id = device->dev_id;
> + spin_lock_init(&chan->lock);
> +
> if (!name)
> dev_set_name(&chan->dev->device, "dma%dchan%d", device->dev_id, chan->chan_id);
> else
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index defa377d2ef54d94e6337cdfa7826a091295535e..23728f3d60804e49cd4cbbd3a513c4936eed5836 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -322,6 +322,8 @@ struct dma_router {
> * @slave: ptr to the device using this channel
> * @cookie: last cookie value returned to client
> * @completed_cookie: last completed cookie for this channel
> + * @lock: protect between config and prepare transfer when driver have not
> + * implemented callback device_prep_config_sg().
> * @chan_id: channel ID for sysfs
> * @dev: class device for sysfs
> * @name: backlink name for sysfs
> @@ -341,6 +343,12 @@ struct dma_chan {
> dma_cookie_t cookie;
> dma_cookie_t completed_cookie;
>
> + /*
> + * protect between config and prepare transfer because *_prep() may be
> + * called from complete callback, which is in GFP_NOSLEEP context.
> + */
> + spinlock_t lock; /* protect between config and prepare transfer since */
Why two comments?
> +
> /* sysfs */
> int chan_id;
> struct dma_chan_dev *dev;
> @@ -1068,6 +1076,56 @@ dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
> return dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, NULL);
> }
>
> +/*
> + * dmaengine_prep_config_single(sg)_safe() is re-entrant version.
> + *
> + * The unsafe variant (without the _safe suffix) falls back to calling
> + * dmaengine_slave_config() and dmaengine_prep_slave_sg() separately.
> + * In this case, additional locking may be required, depending on the
> + * DMA consumer's usage.
> + *
> + * If dmaengine driver have not implemented call back device_prep_config_sg()
> + * safe version use per-channel spinlock to protect call dmaengine_slave_config()
> + * and dmaengine_prep_slave_sg().
> + */
Use proper kernel-doc comments please...
> +static inline struct dma_async_tx_descriptor *
> +dmaengine_prep_config_sg_safe(struct dma_chan *chan, struct scatterlist *sgl,
> + unsigned int sg_len,
> + enum dma_transfer_direction dir,
> + unsigned long flags,
> + struct dma_slave_config *config)
> +{
> + struct dma_async_tx_descriptor *tx;
> +
> + if (!chan || !chan->device)
> + return NULL;
> +
> + if (!chan->device->device_prep_config_sg)
> + spin_lock(&chan->lock);
> +
> + tx = dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, config);
> +
> + if (!chan->device->device_prep_config_sg)
> + spin_unlock(&chan->lock);
> +
> + return tx;
> +}
> +
Missing kernel-doc.
- Mani
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* Re: [PATCH v4 1/9] dmaengine: Add API to combine configuration and preparation (sg and single)
From: Manivannan Sadhasivam @ 2026-05-12 14:00 UTC (permalink / raw)
To: Frank Li
Cc: Vinod Koul, Krzysztof Wilczyński, Kishon Vijay Abraham I,
Bjorn Helgaas, Christoph Hellwig, Sagi Grimberg,
Chaitanya Kulkarni, Herbert Xu, David S. Miller, Nicolas Ferre,
Alexandre Belloni, Claudiu Beznea, Koichiro Den, Niklas Cassel,
dmaengine, linux-kernel, linux-pci, linux-nvme, mhi,
linux-arm-msm, linux-crypto, linux-arm-kernel, imx
In-Reply-To: <20260506-dma_prep_config-v4-1-85b3d22babff@nxp.com>
On Wed, May 06, 2026 at 04:44:13PM -0400, Frank Li wrote:
> Previously, configuration and preparation required two separate calls. This
> works well when configuration is done only once during initialization.
>
> However, in cases where the burst length or source/destination address must
> be adjusted for each transfer, calling two functions is verbose and
> requires additional locking to ensure both steps complete atomically.
>
> Add a new API dmaengine_prep_config_single() and dmaengine_prep_config_sg()
> and callback device_prep_config_sg() that combines configuration and
> preparation into a single operation. If the configuration argument is
> passed as NULL, fall back to the existing implementation.
>
> Tested-by: Niklas Cassel <cassel@kernel.org>
> Signed-off-by: Frank Li <Frank.Li@nxp.com>
Acked-by: Manivannan Sadhasivam <mani@kernel.org>
My only concern is that since these APIs are defined as 'inline' functions,
adding more code will end up increasing the kernel Image size.
- Mani
> ---
> change in v4
> - drop context in device_prep_config_sg()
>
> change in v3
> - remove Deprecated for callback device_prep_slave_sg().
> - Move condition check before sg init.
> - split function at return type.
> - move safe version to next patch
>
> change in v2
> - add () for function
> - use short name device_prep_sg(), remove "slave" and "config". the 'slave'
> is reduntant. after remove slave, the function name is difference existed
> one, so remove _config suffix.
> ---
> Documentation/driver-api/dmaengine/client.rst | 9 ++++
> include/linux/dmaengine.h | 63 +++++++++++++++++++++++----
> 2 files changed, 64 insertions(+), 8 deletions(-)
>
> diff --git a/Documentation/driver-api/dmaengine/client.rst b/Documentation/driver-api/dmaengine/client.rst
> index d491e385d61a98b8a804cd823caf254a2dc62cf4..5ee5d4a3596dd986b02f1bce3078ca6c4c1fb45a 100644
> --- a/Documentation/driver-api/dmaengine/client.rst
> +++ b/Documentation/driver-api/dmaengine/client.rst
> @@ -80,6 +80,10 @@ The details of these operations are:
>
> - slave_sg: DMA a list of scatter gather buffers from/to a peripheral
>
> + - config_sg: Similar with slave_sg, just pass down dma_slave_config
> + struct to avoid calling dmaengine_slave_config() every time adjusting the
> + burst length or the FIFO address is needed.
> +
> - peripheral_dma_vec: DMA an array of scatter gather buffers from/to a
> peripheral. Similar to slave_sg, but uses an array of dma_vec
> structures instead of a scatterlist.
> @@ -106,6 +110,11 @@ The details of these operations are:
> unsigned int sg_len, enum dma_data_direction direction,
> unsigned long flags);
>
> + struct dma_async_tx_descriptor *dmaengine_prep_config_sg(
> + struct dma_chan *chan, struct scatterlist *sgl,
> + unsigned int sg_len, enum dma_transfer_direction dir,
> + unsigned long flags, struct dma_slave_config *config);
> +
> struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec(
> struct dma_chan *chan, const struct dma_vec *vecs,
> size_t nents, enum dma_data_direction direction,
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index b3d251c9734e95e1b75cf6763d4d2c3a1c6a9910..defa377d2ef54d94e6337cdfa7826a091295535e 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -835,6 +835,7 @@ struct dma_filter {
> * where the address and size of each segment is located in one entry of
> * the dma_vec array.
> * @device_prep_slave_sg: prepares a slave dma operation
> + * @device_prep_config_sg: prepares a slave DMA operation with dma_slave_config
> * @device_prep_dma_cyclic: prepare a cyclic dma operation suitable for audio.
> * The function takes a buffer of size buf_len. The callback function will
> * be called after period_len bytes have been transferred.
> @@ -934,6 +935,10 @@ struct dma_device {
> struct dma_chan *chan, struct scatterlist *sgl,
> unsigned int sg_len, enum dma_transfer_direction direction,
> unsigned long flags, void *context);
> + struct dma_async_tx_descriptor *(*device_prep_config_sg)(
> + struct dma_chan *chan, struct scatterlist *sgl,
> + unsigned int sg_len, enum dma_transfer_direction direction,
> + unsigned long flags, struct dma_slave_config *config);
> struct dma_async_tx_descriptor *(*device_prep_dma_cyclic)(
> struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
> size_t period_len, enum dma_transfer_direction direction,
> @@ -974,22 +979,44 @@ static inline bool is_slave_direction(enum dma_transfer_direction direction)
> (direction == DMA_DEV_TO_DEV);
> }
>
> -static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_single(
> - struct dma_chan *chan, dma_addr_t buf, size_t len,
> - enum dma_transfer_direction dir, unsigned long flags)
> +static inline struct dma_async_tx_descriptor *
> +dmaengine_prep_config_single(struct dma_chan *chan, dma_addr_t buf, size_t len,
> + enum dma_transfer_direction dir,
> + unsigned long flags,
> + struct dma_slave_config *config)
> {
> struct scatterlist sg;
> +
> + if (!chan || !chan->device)
> + return NULL;
> +
> sg_init_table(&sg, 1);
> sg_dma_address(&sg) = buf;
> sg_dma_len(&sg) = len;
>
> - if (!chan || !chan->device || !chan->device->device_prep_slave_sg)
> + if (chan->device->device_prep_config_sg)
> + return chan->device->device_prep_config_sg(chan, &sg, 1, dir,
> + flags, config);
> +
> + if (config)
> + if (dmaengine_slave_config(chan, config))
> + return NULL;
> +
> + if (!chan->device->device_prep_slave_sg)
> return NULL;
>
> return chan->device->device_prep_slave_sg(chan, &sg, 1,
> dir, flags, NULL);
> }
>
> +static inline struct dma_async_tx_descriptor *
> +dmaengine_prep_slave_single(struct dma_chan *chan, dma_addr_t buf, size_t len,
> + enum dma_transfer_direction dir,
> + unsigned long flags)
> +{
> + return dmaengine_prep_config_single(chan, buf, len, dir, flags, NULL);
> +}
> +
> /**
> * dmaengine_prep_peripheral_dma_vec() - Prepare a DMA scatter-gather descriptor
> * @chan: The channel to be used for this descriptor
> @@ -1010,17 +1037,37 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_peripheral_dma_vec(
> dir, flags);
> }
>
> -static inline struct dma_async_tx_descriptor *dmaengine_prep_slave_sg(
> - struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
> - enum dma_transfer_direction dir, unsigned long flags)
> +static inline struct dma_async_tx_descriptor *
> +dmaengine_prep_config_sg(struct dma_chan *chan, struct scatterlist *sgl,
> + unsigned int sg_len, enum dma_transfer_direction dir,
> + unsigned long flags, struct dma_slave_config *config)
> {
> - if (!chan || !chan->device || !chan->device->device_prep_slave_sg)
> + if (!chan || !chan->device)
> + return NULL;
> +
> + if (chan->device->device_prep_config_sg)
> + return chan->device->device_prep_config_sg(chan, sgl, sg_len,
> + dir, flags, config);
> +
> + if (config)
> + if (dmaengine_slave_config(chan, config))
> + return NULL;
> +
> + if (!chan->device->device_prep_slave_sg)
> return NULL;
>
> return chan->device->device_prep_slave_sg(chan, sgl, sg_len,
> dir, flags, NULL);
> }
>
> +static inline struct dma_async_tx_descriptor *
> +dmaengine_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
> + unsigned int sg_len, enum dma_transfer_direction dir,
> + unsigned long flags)
> +{
> + return dmaengine_prep_config_sg(chan, sgl, sg_len, dir, flags, NULL);
> +}
> +
> #ifdef CONFIG_RAPIDIO_DMA_ENGINE
> struct rio_dma_ext;
> static inline struct dma_async_tx_descriptor *dmaengine_prep_rio_sg(
>
> --
> 2.43.0
>
--
மணிவண்ணன் சதாசிவம்
^ permalink raw reply
* Re: [PATCH] crypto : ecc - Fix carry overflow in vli multiplication
From: Lukas Wunner @ 2026-05-12 13:48 UTC (permalink / raw)
To: Anastasia Tishchenko
Cc: Ignat Korchagin, Stefan Berger, Herbert Xu, David S . Miller,
linux-crypto, linux-kernel
In-Reply-To: <20260508114844.29694-1-sv3iry@gmail.com>
On Fri, May 08, 2026 at 02:48:44PM +0300, Anastasia Tishchenko wrote:
> The carry flag calculation fails when r01.m_high is saturated
> (0xFFFFFFFFFFFFFFFF) and addition of lower bits overflows.
>
> The condition (r01.m_high < product.m_high) doesn't handle the case
> where r01.m_high == product.m_high and an additional carry exists
> from lower-bit overflow.
>
> Add proper handling for this boundary by accounting for the carry
> from the lower addition.
[...]
> +++ b/crypto/ecc.c
> @@ -427,7 +427,10 @@ static void vli_mult(u64 *result, const u64 *left, const u64 *right,
> product = mul_64_64(left[i], right[k - i]);
>
> r01 = add_128_128(r01, product);
> - r2 += (r01.m_high < product.m_high);
> + if (r01.m_high != product.m_high)
> + r2 += (r01.m_high < product.m_high);
> + else
> + r2 += (r01.m_low < product.m_low);
> }
>
> result[k] = r01.m_low;
ICYMI, sashiko's AI-generated review alleges that the if-else condition
may cause a timing side channel vis-à-vis binary arithmetic:
https://sashiko.dev/#/patchset/20260508114844.29694-1-sv3iry%40gmail.com
You may want to address this if/when respinning your patch. If you do,
a code comment is probably merited to explain this subtlety.
Thanks,
Lukas
^ permalink raw reply
* [PATCH] crypto: cesa - use max to simplify mv_cesa_probe
From: Thorsten Blum @ 2026-05-12 13:34 UTC (permalink / raw)
To: Srujana Challa, Bharat Bhushan, Herbert Xu, David S. Miller,
Rosen Penev, Thorsten Blum, Krzysztof Kozlowski
Cc: linux-crypto, linux-kernel
Use max() to simplify mv_cesa_probe() and improve its readability.
Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
---
drivers/crypto/marvell/cesa/cesa.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/crypto/marvell/cesa/cesa.c b/drivers/crypto/marvell/cesa/cesa.c
index 687ed730174d..75d8ba23d9a2 100644
--- a/drivers/crypto/marvell/cesa/cesa.c
+++ b/drivers/crypto/marvell/cesa/cesa.c
@@ -18,6 +18,7 @@
#include <linux/io.h>
#include <linux/kthread.h>
#include <linux/mbus.h>
+#include <linux/minmax.h>
#include <linux/platform_device.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
@@ -442,10 +443,8 @@ static int mv_cesa_probe(struct platform_device *pdev)
sram_size = CESA_SA_DEFAULT_SRAM_SIZE;
of_property_read_u32(cesa->dev->of_node, "marvell,crypto-sram-size",
&sram_size);
- if (sram_size < CESA_SA_MIN_SRAM_SIZE)
- sram_size = CESA_SA_MIN_SRAM_SIZE;
- cesa->sram_size = sram_size;
+ cesa->sram_size = max(sram_size, CESA_SA_MIN_SRAM_SIZE);
spin_lock_init(&cesa->lock);
^ permalink raw reply related
* Re: [PATCH 01/19] btrfs: require at least 4 devices for RAID 6
From: David Sterba @ 2026-05-12 11:42 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Andrew Morton, Catalin Marinas, Will Deacon, Ard Biesheuvel,
Huacai Chen, WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260512052230.2947683-2-hch@lst.de>
On Tue, May 12, 2026 at 07:20:41AM +0200, Christoph Hellwig wrote:
> While the RAID6 algorithm could in theory support 3 devices by just
> copying the data disk to the two parity disks, this version is not only
> useless because it is a suboptimal version of 3-way mirroring, but also
> broken with various crashes and incorrect parity generation in various
> architecture-optimized implementations. Disallow it similar to mdraid
> which requires at least 4 devices for RAID 6.
>
> Fixes: 53b381b3abeb ("Btrfs: RAID5 and RAID6")
> Signed-off-by: Christoph Hellwig <hch@lst.de>
This patch should have been sent separately as it has user visible
impact and can potentially break some setups. The degenerate modes of
raid0, 5, or 6 are explicit as a possible middle step when converting
profiles. We can use a fallback implementation for this case if the
accelerated implementations cannot do it.
^ permalink raw reply
* Re: cleanup the RAID6 P/Q library v2
From: Ard Biesheuvel @ 2026-05-12 9:50 UTC (permalink / raw)
To: Christoph Hellwig, Andrew Morton
Cc: Catalin Marinas, Will Deacon, Huacai Chen, WANG Xuerui,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Heiko Carstens, Vasily Gorbik,
Alexander Gordeev, Christian Borntraeger, Sven Schnelle,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
H . Peter Anvin, Herbert Xu, Dan Williams, Chris Mason,
David Sterba, Arnd Bergmann, Song Liu, Yu Kuai, Li Nan,
linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260512052230.2947683-1-hch@lst.de>
On Tue, 12 May 2026, at 07:20, Christoph Hellwig wrote:
> Hi all,
>
> this series cleans up the RAID6 P/Q library to match the recent updates
> to the RAID 5 XOR library and other CRC/crypto libraries. This includes
> providing properly documented external interfaces, hiding the internals,
> using static_call instead of indirect calls and turning the user space
> test suite into an in-kernel kunit test which is also extended to
> improve coverage.
>
> Note that this changes registration so that non-priority algorithms are
> not registered, which greatly helps with the benchmark time at boot time.
> I'd like to encourage all architecture maintainers to see if they can
> further optimized this by registering as few as possible algorithms when
> there is a clear benefit in optimized or more unrolled implementations.
>
> This series sits on top of the "cleanup the RAID5 XOR library v3" series.
>
> A git tree is also available here:
>
> git://git.infradead.org/users/hch/misc.git lib-raid6
>
> Gitweb:
>
>
> https://git.infradead.org/?p=users/hch/misc.git;a=shortlog;h=refs/heads/lib-raid6
>
> Changes since v1:
> - fix arm64 objdir != srcdir builds
> - call the kunit module raid6_kunit.ko from the beginning
> - update MAINTAINERS
> - don't require preemptible context and apply the same restrictions as
> the merged version of the XOR API
> - fix the arm64 default in Kconfig
> - pick the last registered (and presumably most optimized) algorithm when
> benchmarking is disabled
> - port over the randomization fixes from the XOR series
> - misc other kunit cleanups
> - require at least 4 devices for RAID6 to skip broken special cases
>
Tested-by: Ard Biesheuvel <ardb@kernel.org> # kunit only on arm64
Acked-by: Ard Biesheuvel <ardb@kernel.org>
^ permalink raw reply
* Re: [PATCH v2 2/2] arm64: dts: qcom: glymur: Add crypto engine and BAM
From: Wenjia Zhang @ 2026-05-12 8:04 UTC (permalink / raw)
To: Harshal Dev, Thara Gopinath, Herbert Xu, David S. Miller,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson,
Konrad Dybcio, Dmitry Baryshkov
Cc: Neeraj Soni, Kuldeep Singh, Abel Vesa, linux-arm-msm,
linux-crypto, devicetree, linux-kernel
In-Reply-To: <20260505-glymur_crypto_enablement-v2-2-bf115aeb1459@oss.qualcomm.com>
On 5/5/2026 3:40 PM, Harshal Dev wrote:
> On almost all Qualcomm platforms, including Glymur, there is a Crypto
> engine IP block to which the CPU can off-load cryptographic computations
> for achieving acceleration.
> The engine is also DMA capable due to the presence of an associated Bus
> Access Manager (BAM) module.
>
> Describe the Crypto engine and its BAM.
>
> Signed-off-by: Harshal Dev <harshal.dev@oss.qualcomm.com>
> ---
> arch/arm64/boot/dts/qcom/glymur.dtsi | 26 ++++++++++++++++++++++++++
> 1 file changed, 26 insertions(+)
>
> diff --git a/arch/arm64/boot/dts/qcom/glymur.dtsi b/arch/arm64/boot/dts/qcom/glymur.dtsi
> index f23cf81ddb77..349da9966d52 100644
> --- a/arch/arm64/boot/dts/qcom/glymur.dtsi
> +++ b/arch/arm64/boot/dts/qcom/glymur.dtsi
> @@ -3675,6 +3675,32 @@ pcie3b_phy: phy@f10000 {
> status = "disabled";
> };
>
> + cryptobam: dma-controller@1dc4000 {
> + compatible = "qcom,bam-v1.7.4", "qcom,bam-v1.7.0";
> + reg = <0x0 0x01dc4000 0x0 0x28000>;
> + interrupts = <GIC_SPI 272 IRQ_TYPE_LEVEL_HIGH>;
> + #dma-cells = <1>;
> + iommus = <&apps_smmu 0x80 0x0>,
> + <&apps_smmu 0x81 0x0>;
> + qcom,ee = <0>;
> + qcom,controlled-remotely;
> + num-channels = <20>;
> + qcom,num-ees = <4>;
> + };
> +
> + crypto: crypto@1dfa000 {
> + compatible = "qcom,glymur-qce", "qcom,sm8150-qce", "qcom,qce";
> + reg = <0x0 0x01dfa000 0x0 0x6000>;
> + dmas = <&cryptobam 4>, <&cryptobam 5>;
> + dma-names = "rx",
> + "tx";
> + iommus = <&apps_smmu 0x80 0x0>,
> + <&apps_smmu 0x81 0x0>;
> + interconnects = <&aggre1_noc MASTER_CRYPTO QCOM_ICC_TAG_ALWAYS
> + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ALWAYS>;
> + interconnect-names = "memory";
> + };
> +
> tcsr_mutex: hwlock@1f40000 {
> compatible = "qcom,tcsr-mutex";
> reg = <0x0 0x01f40000 0x0 0x20000>;
Tested-by: Wenjia Zhang <wenjia.zhang@oss.qualcomm.com> # on Glymur-crd
device
root@qcom-armv8a:~# bash /usr/libexec/libkcapi/kcapi-convenience.sh
[PASSED: 64-bit - 7.0.0-next-20260415-00003-g5de0c764975a-dirty]
Convenience message digest operation
===================================================================
Number of failures: 0
root@qcom-armv8a:~#
Regards,
Wenjia
^ permalink raw reply
* [PATCH v1 2/2] hwrng: starfive: Update clk and reset sequence
From: lianfeng.ouyang @ 2026-05-12 6:24 UTC (permalink / raw)
To: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel
Cc: linux-crypto, devicetree, linux-kernel
In-Reply-To: <20260512062404.4540-1-lianfeng.ouyang@starfivetech.com>
From: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
for jhb100, While IP assert async reset, it may generate glitch
and propagate to downstream IP. In order to solve RDC issue,
conduct clock gating before asserting reset to prevent generating glitch.
Jia Jie Ho has resigned
Signed-off-by: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
---
MAINTAINERS | 2 +-
drivers/char/hw_random/jh7110-trng.c | 18 ++++++++++++++++--
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index d3a6b3f6b6a0..729b20ecc697 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -25280,7 +25280,7 @@ F: Documentation/devicetree/bindings/perf/starfive,jh8100-starlink-pmu.yaml
F: drivers/perf/starfive_starlink_pmu.c
STARFIVE TRNG DRIVER
-M: Jia Jie Ho <jiajie.ho@starfivetech.com>
+M: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
S: Supported
F: Documentation/devicetree/bindings/rng/starfive*
F: drivers/char/hw_random/jh7110-trng.c
diff --git a/drivers/char/hw_random/jh7110-trng.c b/drivers/char/hw_random/jh7110-trng.c
index 9776f4daa044..f5eb434c94f5 100644
--- a/drivers/char/hw_random/jh7110-trng.c
+++ b/drivers/char/hw_random/jh7110-trng.c
@@ -234,12 +234,18 @@ static irqreturn_t starfive_trng_irq(int irq, void *priv)
static void starfive_trng_cleanup(struct hwrng *rng)
{
struct starfive_trng *trng = to_trng(rng);
+ bool is_jhb100 = device_is_compatible(trng->dev, "starfive,jhb100-trng");
writel(0, trng->base + STARFIVE_CTRL);
- reset_control_assert(trng->rst);
+ if (!is_jhb100)
+ reset_control_assert(trng->rst);
+
clk_disable_unprepare(trng->hclk);
clk_disable_unprepare(trng->ahb);
+
+ if (is_jhb100)
+ reset_control_assert(trng->rst);
}
static int starfive_trng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
@@ -337,12 +343,19 @@ static int starfive_trng_probe(struct platform_device *pdev)
ret = devm_hwrng_register(&pdev->dev, &trng->rng);
if (ret) {
+ bool is_jhb100 = device_is_compatible(trng->dev, "starfive,jhb100-trng");
+
pm_runtime_disable(&pdev->dev);
- reset_control_assert(trng->rst);
+ if (!is_jhb100)
+ reset_control_assert(trng->rst);
+
clk_disable_unprepare(trng->ahb);
clk_disable_unprepare(trng->hclk);
+ if (is_jhb100)
+ reset_control_assert(trng->rst);
+
return dev_err_probe(&pdev->dev, ret, "Failed to register hwrng\n");
}
@@ -378,6 +391,7 @@ static const struct dev_pm_ops starfive_trng_pm_ops = {
static const struct of_device_id trng_dt_ids[] __maybe_unused = {
{ .compatible = "starfive,jh7110-trng" },
+ { .compatible = "starfive,jhb100-trng" },
{ }
};
MODULE_DEVICE_TABLE(of, trng_dt_ids);
--
2.43.0
^ permalink raw reply related
* [PATCH v1 1/2] dt-bindings: Add bindings for StarFive JHB100 SoC trng controller.
From: lianfeng.ouyang @ 2026-05-12 6:24 UTC (permalink / raw)
To: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel
Cc: linux-crypto, devicetree, linux-kernel
In-Reply-To: <20260512062404.4540-1-lianfeng.ouyang@starfivetech.com>
From: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
Signed-off-by: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
---
Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml b/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml
index 4639247e9e51..11346d77b2f6 100644
--- a/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml
+++ b/Documentation/devicetree/bindings/rng/starfive,jh7110-trng.yaml
@@ -13,8 +13,8 @@ properties:
compatible:
oneOf:
- items:
- - const: starfive,jh8100-trng
- const: starfive,jh7110-trng
+ - const: starfive,jhb100-trng
- const: starfive,jh7110-trng
reg:
--
2.43.0
^ permalink raw reply related
* [PATCH v1 0/2] Add trng driver to JHB100
From: lianfeng.ouyang @ 2026-05-12 6:24 UTC (permalink / raw)
To: Olivia Mackall, Herbert Xu, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Philipp Zabel
Cc: linux-crypto, devicetree, linux-kernel
From: Lianfeng Ouyang <lianfeng.ouyang@starfivetech.com>
for jhb100, While IP assert async reset, it may generate glitch
and propagate to downstream IP. In order to solve RDC issue,
conduct clock gating before asserting reset to prevent generating glitch.
Lianfeng Ouyang (2):
dt-bindings: Add bindings for StarFive JHB100 SoC trng controller.
hwrng: starfive: Update clk and reset sequence
.../bindings/rng/starfive,jh7110-trng.yaml | 2 +-
MAINTAINERS | 2 +-
drivers/char/hw_random/jh7110-trng.c | 18 ++++++++++++++++--
3 files changed, 18 insertions(+), 4 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH v3] rhashtable: Bounce deferred worker kick through irq_work
From: Hillf Danton @ 2026-05-12 6:07 UTC (permalink / raw)
To: Tejun Heo
Cc: Herbert Xu, Thomas Graf, Andrew Morton, linux-crypto,
linux-kernel
In-Reply-To: <20260421060326.2836354-1-tj@kernel.org>
On Mon, 20 Apr 2026 20:03:26 -1000 Tejun Heo wrote:
> --- a/lib/rhashtable.c
> +++ b/lib/rhashtable.c
> @@ -441,10 +441,33 @@ static void rht_deferred_worker(struct work_struct *work)
>
> mutex_unlock(&ht->mutex);
>
> + /*
> + * Re-arm via @run_work, not @run_irq_work.
> + * rhashtable_free_and_destroy() drains async work as irq_work_sync()
> + * followed by cancel_work_sync(). If this site queued irq_work while
> + * cancel_work_sync() was waiting for us, irq_work_sync() would already
> + * have returned and the stale irq_work could fire post-teardown.
> + * cancel_work_sync() natively handles self-requeue on @run_work.
> + */
> if (err)
> schedule_work(&ht->run_work);
> }
>
Two cents: add BUG to capture the failure of handling self-requeue.
--- x/kernel/workqueue.c
+++ y/kernel/workqueue.c
@@ -2369,6 +2369,10 @@ retry:
work_flags |= WORK_STRUCT_INACTIVE;
insert_work(pwq, work, &pwq->inactive_works, work_flags);
}
+ do {
+ unsigned long data = *work_data_bits(work);
+ BUG_ON(data & WORK_OFFQ_DISABLE_MASK);
+ } while (0);
out:
raw_spin_unlock(&pool->lock);
--
^ permalink raw reply
* [PATCH 19/19] raid6_kunit: randomize buffer alignment
From: Christoph Hellwig @ 2026-05-12 5:20 UTC (permalink / raw)
To: Andrew Morton
Cc: Catalin Marinas, Will Deacon, Ard Biesheuvel, Huacai Chen,
WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260512052230.2947683-1-hch@lst.de>
Add code to add random alignment to the buffers to test the case where
they are not page aligned, and to move the buffers to the end of the
allocation so that they are next to the vmalloc guard page.
This does not include the recovery buffers as the recovery requires
page alignment.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
lib/raid/raid6/tests/raid6_kunit.c | 41 +++++++++++++++++++++++++-----
1 file changed, 35 insertions(+), 6 deletions(-)
diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c
index d6ac777dcaee..7b45c7be36fc 100644
--- a/lib/raid/raid6/tests/raid6_kunit.c
+++ b/lib/raid/raid6/tests/raid6_kunit.c
@@ -21,6 +21,7 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
static struct rnd_state rng;
static void *test_buffers[RAID6_KUNIT_MAX_BUFFERS];
+static void *aligned_buffers[RAID6_KUNIT_MAX_BUFFERS];
static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES];
static size_t test_buflen;
@@ -50,6 +51,14 @@ static unsigned int random_nr_buffers(void)
RAID6_MIN_DISKS;
}
+/* Generate a random alignment that is a multiple of 64. */
+static unsigned int random_alignment(unsigned int max_alignment)
+{
+ if (max_alignment == 0)
+ return 0;
+ return (rand32() % (max_alignment + 1)) & ~63;
+}
+
static void makedata(int start, int stop)
{
int i;
@@ -80,7 +89,7 @@ static void test_recover_one(struct kunit *test, unsigned int nr_buffers,
for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
memset(test_recov_buffers[i], 0xf0, test_buflen);
- memcpy(dataptrs, test_buffers, sizeof(dataptrs));
+ memcpy(dataptrs, aligned_buffers, sizeof(dataptrs));
dataptrs[faila] = test_recov_buffers[0];
dataptrs[failb] = test_recov_buffers[1];
@@ -102,13 +111,13 @@ static void test_recover_one(struct kunit *test, unsigned int nr_buffers,
ta->recov->data2(nr_buffers, len, faila, failb, dataptrs);
}
- KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0],
+ KUNIT_EXPECT_MEMEQ_MSG(test, aligned_buffers[faila], dataptrs[faila],
len,
"faila miscompared: %3d[%c] buffers %u len %u (failb=%3d[%c])\n",
faila, member_type(nr_buffers, faila),
nr_buffers, len,
failb, member_type(nr_buffers, failb));
- KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1],
+ KUNIT_EXPECT_MEMEQ_MSG(test, aligned_buffers[failb], dataptrs[failb],
len,
"failb miscompared: %3d[%c] buffers %u len %u (faila=%3d[%c])\n",
failb, member_type(nr_buffers, failb),
@@ -152,9 +161,9 @@ static void test_rmw_one(struct kunit *test, unsigned int nr_buffers,
{
const struct test_args *ta = test->param_value;
- ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers);
+ ta->gen->xor_syndrome(nr_buffers, p1, p2, len, aligned_buffers);
makedata(p1, p2);
- ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers);
+ ta->gen->xor_syndrome(nr_buffers, p1, p2, len, aligned_buffers);
test_recover(test, nr_buffers, len);
}
@@ -178,13 +187,33 @@ static void raid6_test_one(struct kunit *test)
const struct test_args *ta = test->param_value;
unsigned int nr_buffers = random_nr_buffers();
unsigned int len = random_length(RAID6_KUNIT_MAX_BYTES);
+ unsigned int max_alignment;
+ int i;
/* Nuke syndromes */
memset(test_buffers[nr_buffers - 2], 0xee, test_buflen);
memset(test_buffers[nr_buffers - 1], 0xee, test_buflen);
+ /*
+ * If we're not using the entire buffer size, inject randomize alignment
+ * into the buffer.
+ */
+ max_alignment = RAID6_KUNIT_MAX_BYTES - len;
+ if (rand32() % 2 == 0) {
+ /* Use random alignments mod 64 */
+ for (i = 0; i < nr_buffers; i++)
+ aligned_buffers[i] = test_buffers[i] +
+ random_alignment(max_alignment);
+ } else {
+ /* Go up to the guard page, to catch buffer overreads */
+ unsigned int align = test_buflen - len;
+
+ for (i = 0; i < nr_buffers; i++)
+ aligned_buffers[i] = test_buffers[i] + align;
+ }
+
/* Generate assumed good syndrome */
- ta->gen->gen_syndrome(nr_buffers, len, test_buffers);
+ ta->gen->gen_syndrome(nr_buffers, len, aligned_buffers);
test_recover(test, nr_buffers, len);
--
2.53.0
^ permalink raw reply related
* [PATCH 18/19] raid6_kunit: randomize parameters and increase limits
From: Christoph Hellwig @ 2026-05-12 5:20 UTC (permalink / raw)
To: Andrew Morton
Cc: Catalin Marinas, Will Deacon, Ard Biesheuvel, Huacai Chen,
WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260512052230.2947683-1-hch@lst.de>
The current test has double-quadratic behavior in the selection for
the updated ("XORed") disks, and in the selection of updated pointers,
which makes scaling it to more tests difficult. At the same time it
only ever tests with the maximum number of disks, which leaves a
coverage hole for smaller ones.
Fix this by randomizing the total number, failed disks and regions
to update, and increasing the upper number of tests disks.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
lib/raid/raid6/tests/raid6_kunit.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c
index 775a0051f9a4..d6ac777dcaee 100644
--- a/lib/raid/raid6/tests/raid6_kunit.c
+++ b/lib/raid/raid6/tests/raid6_kunit.c
@@ -8,6 +8,7 @@
#include <kunit/test.h>
#include <linux/prandom.h>
#include <linux/vmalloc.h>
+#include <linux/raid/pq.h>
#include "../algos.h"
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
@@ -43,6 +44,12 @@ static unsigned int random_length(unsigned int max_length)
return round_up((rand32() % max_length) + 1, 512);
}
+static unsigned int random_nr_buffers(void)
+{
+ return (rand32() % (RAID6_KUNIT_MAX_BUFFERS - (RAID6_MIN_DISKS - 1))) +
+ RAID6_MIN_DISKS;
+}
+
static void makedata(int start, int stop)
{
int i;
@@ -169,9 +176,7 @@ static void test_rmw(struct kunit *test, unsigned int nr_buffers,
static void raid6_test_one(struct kunit *test)
{
const struct test_args *ta = test->param_value;
- /* including P/Q we need at least three buffers */
- unsigned int nr_buffers =
- (rand32() % (RAID6_KUNIT_MAX_BUFFERS - 2)) + 3;
+ unsigned int nr_buffers = random_nr_buffers();
unsigned int len = random_length(RAID6_KUNIT_MAX_BYTES);
/* Nuke syndromes */
--
2.53.0
^ permalink raw reply related
* [PATCH 17/19] raid6_kunit: randomize parameters and increase limits
From: Christoph Hellwig @ 2026-05-12 5:20 UTC (permalink / raw)
To: Andrew Morton
Cc: Catalin Marinas, Will Deacon, Ard Biesheuvel, Huacai Chen,
WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260512052230.2947683-1-hch@lst.de>
The current test has double-quadratic behavior in the selection for
the updated ("XORed") disks, and in the selection of updated pointers,
which makes scaling it to more tests difficult. At the same time it
only ever tests with the maximum number of disks, which leaves a
coverage hole for smaller ones.
Fix this by randomizing the total number, failed disks and regions
to update, and increasing the upper number of tests disks.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
lib/raid/raid6/tests/raid6_kunit.c | 184 ++++++++++++++++++++---------
1 file changed, 126 insertions(+), 58 deletions(-)
diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c
index 28b4467977c5..775a0051f9a4 100644
--- a/lib/raid/raid6/tests/raid6_kunit.c
+++ b/lib/raid/raid6/tests/raid6_kunit.c
@@ -13,13 +13,15 @@
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
#define RAID6_KUNIT_SEED 42
+#define RAID6_KUNIT_NUM_TEST_ITERS 10
+#define RAID6_KUNIT_MAX_BUFFERS 64 /* Including P and Q */
#define RAID6_KUNIT_MAX_FAILURES 2
-
-#define NDISKS 16 /* Including P and Q */
+#define RAID6_KUNIT_MAX_BYTES PAGE_SIZE
static struct rnd_state rng;
-static void *test_buffers[NDISKS];
+static void *test_buffers[RAID6_KUNIT_MAX_BUFFERS];
static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES];
+static size_t test_buflen;
struct test_args {
unsigned int recov_idx;
@@ -30,102 +32,167 @@ struct test_args {
static struct test_args args;
+static u32 rand32(void)
+{
+ return prandom_u32_state(&rng);
+}
+
+/* Generate a random length that is a multiple of 512. */
+static unsigned int random_length(unsigned int max_length)
+{
+ return round_up((rand32() % max_length) + 1, 512);
+}
+
static void makedata(int start, int stop)
{
int i;
for (i = start; i <= stop; i++)
- prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE);
+ prandom_bytes_state(&rng, test_buffers[i], test_buflen);
}
-static char member_type(int d)
+static char member_type(unsigned int nr_buffers, int d)
{
- switch (d) {
- case NDISKS-2:
+ if (d == nr_buffers - 2)
return 'P';
- case NDISKS-1:
+ if (d == nr_buffers - 1)
return 'Q';
- default:
- return 'D';
- }
+ return 'D';
}
-static void test_recover(struct kunit *test, int faila, int failb)
+static void test_recover_one(struct kunit *test, unsigned int nr_buffers,
+ unsigned int len, int faila, int failb)
{
const struct test_args *ta = test->param_value;
- void *dataptrs[NDISKS];
+ void *dataptrs[RAID6_KUNIT_MAX_BUFFERS];
int i;
+ if (faila > failb)
+ swap(faila, failb);
+
for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
- memset(test_recov_buffers[i], 0xf0, PAGE_SIZE);
+ memset(test_recov_buffers[i], 0xf0, test_buflen);
memcpy(dataptrs, test_buffers, sizeof(dataptrs));
dataptrs[faila] = test_recov_buffers[0];
dataptrs[failb] = test_recov_buffers[1];
- if (failb == NDISKS - 1) {
+ if (failb == nr_buffers - 1) {
/*
* We don't implement the data+Q failure scenario, since it
* is equivalent to a RAID-5 failure (XOR, then recompute Q).
*/
- if (faila != NDISKS - 2)
+ if (WARN_ON_ONCE(faila != nr_buffers - 2))
return;
/* P+Q failure. Just rebuild the syndrome. */
- ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs);
- } else if (failb == NDISKS - 2) {
+ ta->gen->gen_syndrome(nr_buffers, len, dataptrs);
+ } else if (failb == nr_buffers - 2) {
/* data+P failure. */
- ta->recov->datap(NDISKS, PAGE_SIZE, faila, dataptrs);
+ ta->recov->datap(nr_buffers, len, faila, dataptrs);
} else {
/* data+data failure. */
- ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs);
+ ta->recov->data2(nr_buffers, len, faila, failb, dataptrs);
}
KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0],
- PAGE_SIZE,
- "faila miscompared: %3d[%c] (failb=%3d[%c])\n",
- faila, member_type(faila),
- failb, member_type(failb));
+ len,
+ "faila miscompared: %3d[%c] buffers %u len %u (failb=%3d[%c])\n",
+ faila, member_type(nr_buffers, faila),
+ nr_buffers, len,
+ failb, member_type(nr_buffers, failb));
KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1],
- PAGE_SIZE,
- "failb miscompared: %3d[%c] (faila=%3d[%c])\n",
- failb, member_type(failb),
- faila, member_type(faila));
+ len,
+ "failb miscompared: %3d[%c] buffers %u len %u (faila=%3d[%c])\n",
+ failb, member_type(nr_buffers, failb),
+ nr_buffers, len,
+ faila, member_type(nr_buffers, faila));
}
-static void raid6_test(struct kunit *test)
+static void test_recover(struct kunit *test, unsigned int nr_buffers,
+ unsigned int len)
+{
+ unsigned int nr_data = nr_buffers - 2;
+ int iterations, i;
+
+ /* Test P+Q recovery */
+ test_recover_one(test, nr_buffers, len, nr_data, nr_buffers - 1);
+
+ /* Test data+P recovery */
+ for (i = 0; i < nr_buffers - 2; i++)
+ test_recover_one(test, nr_buffers, len, i, nr_data);
+
+ /* Double data failure is impossible with a single data disk */
+ if (nr_data == 1)
+ return;
+
+ /* Test data+data recovery using random sampling */
+ iterations = nr_buffers * 2; /* should provide good enough coverage */
+ for (i = 0; i < iterations; i++) {
+ int faila = rand32() % nr_data, failb;
+
+ do {
+ failb = rand32() % nr_data;
+ } while (failb == faila);
+
+ test_recover_one(test, nr_buffers, len, faila, failb);
+ }
+}
+
+/* Simulate rmw run */
+static void test_rmw_one(struct kunit *test, unsigned int nr_buffers,
+ unsigned int len, int p1, int p2)
{
const struct test_args *ta = test->param_value;
- int i, j, p1, p2;
+
+ ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers);
+ makedata(p1, p2);
+ ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers);
+ test_recover(test, nr_buffers, len);
+}
+
+static void test_rmw(struct kunit *test, unsigned int nr_buffers,
+ unsigned int len)
+{
+ int iterations = nr_buffers / 2, i;
+
+ for (i = 0; i < iterations; i++) {
+ int p1 = rand32() % (nr_buffers - 2);
+ int p2 = rand32() % (nr_buffers - 2);
+
+ if (p2 < p1)
+ swap(p1, p2);
+ test_rmw_one(test, nr_buffers, len, p1, p2);
+ }
+}
+
+static void raid6_test_one(struct kunit *test)
+{
+ const struct test_args *ta = test->param_value;
+ /* including P/Q we need at least three buffers */
+ unsigned int nr_buffers =
+ (rand32() % (RAID6_KUNIT_MAX_BUFFERS - 2)) + 3;
+ unsigned int len = random_length(RAID6_KUNIT_MAX_BYTES);
/* Nuke syndromes */
- memset(test_buffers[NDISKS - 2], 0xee, PAGE_SIZE);
- memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE);
+ memset(test_buffers[nr_buffers - 2], 0xee, test_buflen);
+ memset(test_buffers[nr_buffers - 1], 0xee, test_buflen);
/* Generate assumed good syndrome */
- ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, test_buffers);
+ ta->gen->gen_syndrome(nr_buffers, len, test_buffers);
- for (i = 0; i < NDISKS - 1; i++)
- for (j = i + 1; j < NDISKS; j++)
- test_recover(test, i, j);
+ test_recover(test, nr_buffers, len);
- if (!ta->gen->xor_syndrome)
- return;
+ if (ta->gen->xor_syndrome)
+ test_rmw(test, nr_buffers, len);
+}
- for (p1 = 0; p1 < NDISKS - 2; p1++) {
- for (p2 = p1; p2 < NDISKS - 2; p2++) {
- /* Simulate rmw run */
- ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
- test_buffers);
- makedata(p1, p2);
- ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
- test_buffers);
-
- for (i = 0; i < NDISKS - 1; i++)
- for (j = i + 1; j < NDISKS; j++)
- test_recover(test, i, j);
- }
- }
+static void raid6_test(struct kunit *test)
+{
+ int i;
+
+ for (i = 0; i < RAID6_KUNIT_NUM_TEST_ITERS; i++)
+ raid6_test_one(test);
}
static const void *raid6_gen_params(struct kunit *test, const void *prev,
@@ -169,23 +236,24 @@ static int raid6_suite_init(struct kunit_suite *suite)
* so that it is immediately followed by a guard page. This allows
* buffer overreads to be detected, even in assembly code.
*/
+ test_buflen = round_up(RAID6_KUNIT_MAX_BYTES, PAGE_SIZE);
for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) {
- test_recov_buffers[i] = vmalloc(PAGE_SIZE);
+ test_recov_buffers[i] = vmalloc(test_buflen);
if (!test_recov_buffers[i])
goto out_free_recov_buffers;
}
- for (i = 0; i < NDISKS; i++) {
- test_buffers[i] = vmalloc(PAGE_SIZE);
+ for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) {
+ test_buffers[i] = vmalloc(test_buflen);
if (!test_buffers[i])
goto out_free_buffers;
}
- makedata(0, NDISKS - 1);
+ makedata(0, RAID6_KUNIT_MAX_BUFFERS - 1);
return 0;
out_free_buffers:
- for (i = 0; i < NDISKS; i++)
+ for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++)
vfree(test_buffers[i]);
memset(test_buffers, 0, sizeof(test_buffers));
out_free_recov_buffers:
@@ -199,7 +267,7 @@ static void raid6_suite_exit(struct kunit_suite *suite)
{
int i;
- for (i = 0; i < NDISKS; i++)
+ for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++)
vfree(test_buffers[i]);
memset(test_buffers, 0, sizeof(test_buffers));
for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
--
2.53.0
^ permalink raw reply related
* [PATCH 16/19] raid6_kunit: cleanup dataptr handling
From: Christoph Hellwig @ 2026-05-12 5:20 UTC (permalink / raw)
To: Andrew Morton
Cc: Catalin Marinas, Will Deacon, Ard Biesheuvel, Huacai Chen,
WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260512052230.2947683-1-hch@lst.de>
Move the global dataptr array into test_recover() as all sites that fill
data or parity can use test_buffers directly, and this localized the
override for the failed slots to the recovery testing routine.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
lib/raid/raid6/tests/raid6_kunit.c | 19 +++++++------------
1 file changed, 7 insertions(+), 12 deletions(-)
diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c
index a4b65ccc9d20..28b4467977c5 100644
--- a/lib/raid/raid6/tests/raid6_kunit.c
+++ b/lib/raid/raid6/tests/raid6_kunit.c
@@ -18,7 +18,6 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
#define NDISKS 16 /* Including P and Q */
static struct rnd_state rng;
-static void *dataptrs[NDISKS];
static void *test_buffers[NDISKS];
static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES];
@@ -35,10 +34,8 @@ static void makedata(int start, int stop)
{
int i;
- for (i = start; i <= stop; i++) {
+ for (i = start; i <= stop; i++)
prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE);
- dataptrs[i] = test_buffers[i];
- }
}
static char member_type(int d)
@@ -56,11 +53,13 @@ static char member_type(int d)
static void test_recover(struct kunit *test, int faila, int failb)
{
const struct test_args *ta = test->param_value;
+ void *dataptrs[NDISKS];
int i;
for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
memset(test_recov_buffers[i], 0xf0, PAGE_SIZE);
+ memcpy(dataptrs, test_buffers, sizeof(dataptrs));
dataptrs[faila] = test_recov_buffers[0];
dataptrs[failb] = test_recov_buffers[1];
@@ -70,7 +69,7 @@ static void test_recover(struct kunit *test, int faila, int failb)
* is equivalent to a RAID-5 failure (XOR, then recompute Q).
*/
if (faila != NDISKS - 2)
- goto skip;
+ return;
/* P+Q failure. Just rebuild the syndrome. */
ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs);
@@ -92,10 +91,6 @@ static void test_recover(struct kunit *test, int faila, int failb)
"failb miscompared: %3d[%c] (faila=%3d[%c])\n",
failb, member_type(failb),
faila, member_type(faila));
-
-skip:
- dataptrs[faila] = test_buffers[faila];
- dataptrs[failb] = test_buffers[failb];
}
static void raid6_test(struct kunit *test)
@@ -108,7 +103,7 @@ static void raid6_test(struct kunit *test)
memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE);
/* Generate assumed good syndrome */
- ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs);
+ ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, test_buffers);
for (i = 0; i < NDISKS - 1; i++)
for (j = i + 1; j < NDISKS; j++)
@@ -121,10 +116,10 @@ static void raid6_test(struct kunit *test)
for (p2 = p1; p2 < NDISKS - 2; p2++) {
/* Simulate rmw run */
ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
- (void **)&dataptrs);
+ test_buffers);
makedata(p1, p2);
ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
- (void **)&dataptrs);
+ test_buffers);
for (i = 0; i < NDISKS - 1; i++)
for (j = i + 1; j < NDISKS; j++)
--
2.53.0
^ permalink raw reply related
* [PATCH 15/19] raid6_kunit: dynamically allocate data buffers using vmalloc
From: Christoph Hellwig @ 2026-05-12 5:20 UTC (permalink / raw)
To: Andrew Morton
Cc: Catalin Marinas, Will Deacon, Ard Biesheuvel, Huacai Chen,
WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260512052230.2947683-1-hch@lst.de>
Use vmalloc for the data buffers instead of using static .data allocations.
This provides for better out of bounds checking and avoids wasting kernel
memory after the test has run. vmalloc is used instead of kmalloc to
provide for better out of bounds access checking as in other kunit tests.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
lib/raid/raid6/tests/raid6_kunit.c | 77 ++++++++++++++++++++++++------
1 file changed, 62 insertions(+), 15 deletions(-)
diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c
index f55b081b6b13..a4b65ccc9d20 100644
--- a/lib/raid/raid6/tests/raid6_kunit.c
+++ b/lib/raid/raid6/tests/raid6_kunit.c
@@ -7,19 +7,20 @@
#include <kunit/test.h>
#include <linux/prandom.h>
+#include <linux/vmalloc.h>
#include "../algos.h"
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
#define RAID6_KUNIT_SEED 42
+#define RAID6_KUNIT_MAX_FAILURES 2
#define NDISKS 16 /* Including P and Q */
static struct rnd_state rng;
static void *dataptrs[NDISKS];
-static char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
-static char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
-static char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+static void *test_buffers[NDISKS];
+static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES];
struct test_args {
unsigned int recov_idx;
@@ -35,8 +36,8 @@ static void makedata(int start, int stop)
int i;
for (i = start; i <= stop; i++) {
- prandom_bytes_state(&rng, data[i], PAGE_SIZE);
- dataptrs[i] = data[i];
+ prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE);
+ dataptrs[i] = test_buffers[i];
}
}
@@ -55,12 +56,13 @@ static char member_type(int d)
static void test_recover(struct kunit *test, int faila, int failb)
{
const struct test_args *ta = test->param_value;
+ int i;
- memset(recovi, 0xf0, PAGE_SIZE);
- memset(recovj, 0xba, PAGE_SIZE);
+ for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
+ memset(test_recov_buffers[i], 0xf0, PAGE_SIZE);
- dataptrs[faila] = recovi;
- dataptrs[failb] = recovj;
+ dataptrs[faila] = test_recov_buffers[0];
+ dataptrs[failb] = test_recov_buffers[1];
if (failb == NDISKS - 1) {
/*
@@ -80,18 +82,20 @@ static void test_recover(struct kunit *test, int faila, int failb)
ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs);
}
- KUNIT_EXPECT_MEMEQ_MSG(test, data[faila], recovi, PAGE_SIZE,
+ KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0],
+ PAGE_SIZE,
"faila miscompared: %3d[%c] (failb=%3d[%c])\n",
faila, member_type(faila),
failb, member_type(failb));
- KUNIT_EXPECT_MEMEQ_MSG(test, data[failb], recovj, PAGE_SIZE,
+ KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1],
+ PAGE_SIZE,
"failb miscompared: %3d[%c] (faila=%3d[%c])\n",
failb, member_type(failb),
faila, member_type(faila));
skip:
- dataptrs[faila] = data[faila];
- dataptrs[failb] = data[failb];
+ dataptrs[faila] = test_buffers[faila];
+ dataptrs[failb] = test_buffers[failb];
}
static void raid6_test(struct kunit *test)
@@ -100,8 +104,8 @@ static void raid6_test(struct kunit *test)
int i, j, p1, p2;
/* Nuke syndromes */
- memset(data[NDISKS - 2], 0xee, PAGE_SIZE);
- memset(data[NDISKS - 1], 0xee, PAGE_SIZE);
+ memset(test_buffers[NDISKS - 2], 0xee, PAGE_SIZE);
+ memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE);
/* Generate assumed good syndrome */
ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs);
@@ -161,15 +165,58 @@ static struct kunit_case raid6_test_cases[] = {
static int raid6_suite_init(struct kunit_suite *suite)
{
+ int i;
+
prandom_seed_state(&rng, RAID6_KUNIT_SEED);
+
+ /*
+ * Allocate the test buffer using vmalloc() with a page-aligned length
+ * so that it is immediately followed by a guard page. This allows
+ * buffer overreads to be detected, even in assembly code.
+ */
+ for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) {
+ test_recov_buffers[i] = vmalloc(PAGE_SIZE);
+ if (!test_recov_buffers[i])
+ goto out_free_recov_buffers;
+ }
+ for (i = 0; i < NDISKS; i++) {
+ test_buffers[i] = vmalloc(PAGE_SIZE);
+ if (!test_buffers[i])
+ goto out_free_buffers;
+ }
+
makedata(0, NDISKS - 1);
+
return 0;
+
+out_free_buffers:
+ for (i = 0; i < NDISKS; i++)
+ vfree(test_buffers[i]);
+ memset(test_buffers, 0, sizeof(test_buffers));
+out_free_recov_buffers:
+ for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
+ vfree(test_recov_buffers[i]);
+ memset(test_recov_buffers, 0, sizeof(test_recov_buffers));
+ return -ENOMEM;
+}
+
+static void raid6_suite_exit(struct kunit_suite *suite)
+{
+ int i;
+
+ for (i = 0; i < NDISKS; i++)
+ vfree(test_buffers[i]);
+ memset(test_buffers, 0, sizeof(test_buffers));
+ for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
+ vfree(test_recov_buffers[i]);
+ memset(test_recov_buffers, 0, sizeof(test_recov_buffers));
}
static struct kunit_suite raid6_test_suite = {
.name = "raid6",
.test_cases = raid6_test_cases,
.suite_init = raid6_suite_init,
+ .suite_exit = raid6_suite_exit,
};
kunit_test_suite(raid6_test_suite);
--
2.53.0
^ permalink raw reply related
* [PATCH 14/19] raid6_kunit: use KUNIT_CASE_PARAM
From: Christoph Hellwig @ 2026-05-12 5:20 UTC (permalink / raw)
To: Andrew Morton
Cc: Catalin Marinas, Will Deacon, Ard Biesheuvel, Huacai Chen,
WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260512052230.2947683-1-hch@lst.de>
The raid6 test combines various generation and recovery algorithms. Use
KUNIT_CASE_PARAM and provide a generator that iterates over the possible
combinations instead of looping inside a single test instance.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
lib/raid/raid6/tests/raid6_kunit.c | 120 ++++++++++++++++-------------
1 file changed, 68 insertions(+), 52 deletions(-)
diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c
index daaa28e96ff1..f55b081b6b13 100644
--- a/lib/raid/raid6/tests/raid6_kunit.c
+++ b/lib/raid/raid6/tests/raid6_kunit.c
@@ -21,6 +21,15 @@ static char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
static char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
static char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+struct test_args {
+ unsigned int recov_idx;
+ const struct raid6_recov_calls *recov;
+ unsigned int gen_idx;
+ const struct raid6_calls *gen;
+};
+
+static struct test_args args;
+
static void makedata(int start, int stop)
{
int i;
@@ -43,9 +52,10 @@ static char member_type(int d)
}
}
-static void test_disks(struct kunit *test, const struct raid6_calls *calls,
- const struct raid6_recov_calls *ra, int faila, int failb)
+static void test_recover(struct kunit *test, int faila, int failb)
{
+ const struct test_args *ta = test->param_value;
+
memset(recovi, 0xf0, PAGE_SIZE);
memset(recovj, 0xba, PAGE_SIZE);
@@ -61,25 +71,23 @@ static void test_disks(struct kunit *test, const struct raid6_calls *calls,
goto skip;
/* P+Q failure. Just rebuild the syndrome. */
- calls->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs);
+ ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs);
} else if (failb == NDISKS - 2) {
/* data+P failure. */
- ra->datap(NDISKS, PAGE_SIZE, faila, dataptrs);
+ ta->recov->datap(NDISKS, PAGE_SIZE, faila, dataptrs);
} else {
/* data+data failure. */
- ra->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs);
+ ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs);
}
KUNIT_EXPECT_MEMEQ_MSG(test, data[faila], recovi, PAGE_SIZE,
- "algo=%-8s/%-8s faila miscompared: %3d[%c] (failb=%3d[%c])\n",
- calls->name, ra->name,
- faila, member_type(faila),
- failb, member_type(failb));
+ "faila miscompared: %3d[%c] (failb=%3d[%c])\n",
+ faila, member_type(faila),
+ failb, member_type(failb));
KUNIT_EXPECT_MEMEQ_MSG(test, data[failb], recovj, PAGE_SIZE,
- "algo=%-8s/%-8s failb miscompared: %3d[%c] (faila=%3d[%c])\n",
- calls->name, ra->name,
- failb, member_type(failb),
- faila, member_type(faila));
+ "failb miscompared: %3d[%c] (faila=%3d[%c])\n",
+ failb, member_type(failb),
+ faila, member_type(faila));
skip:
dataptrs[faila] = data[faila];
@@ -88,58 +96,66 @@ static void test_disks(struct kunit *test, const struct raid6_calls *calls,
static void raid6_test(struct kunit *test)
{
+ const struct test_args *ta = test->param_value;
int i, j, p1, p2;
- unsigned int r, g;
-
- for (r = 0; ; r++) {
- const struct raid6_recov_calls *ra = raid6_recov_algo_find(r);
-
- if (!ra)
- break;
-
- for (g = 0; ; g++) {
- const struct raid6_calls *calls = raid6_algo_find(g);
-
- if (!calls)
- break;
- /* Nuke syndromes */
- memset(data[NDISKS - 2], 0xee, PAGE_SIZE);
- memset(data[NDISKS - 1], 0xee, PAGE_SIZE);
+ /* Nuke syndromes */
+ memset(data[NDISKS - 2], 0xee, PAGE_SIZE);
+ memset(data[NDISKS - 1], 0xee, PAGE_SIZE);
- /* Generate assumed good syndrome */
- calls->gen_syndrome(NDISKS, PAGE_SIZE,
- (void **)&dataptrs);
+ /* Generate assumed good syndrome */
+ ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs);
- for (i = 0; i < NDISKS-1; i++)
- for (j = i+1; j < NDISKS; j++)
- test_disks(test, calls, ra, i, j);
+ for (i = 0; i < NDISKS - 1; i++)
+ for (j = i + 1; j < NDISKS; j++)
+ test_recover(test, i, j);
- if (!calls->xor_syndrome)
- continue;
+ if (!ta->gen->xor_syndrome)
+ return;
- for (p1 = 0; p1 < NDISKS-2; p1++)
- for (p2 = p1; p2 < NDISKS-2; p2++) {
+ for (p1 = 0; p1 < NDISKS - 2; p1++) {
+ for (p2 = p1; p2 < NDISKS - 2; p2++) {
+ /* Simulate rmw run */
+ ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
+ (void **)&dataptrs);
+ makedata(p1, p2);
+ ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
+ (void **)&dataptrs);
- /* Simulate rmw run */
- calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
- (void **)&dataptrs);
- makedata(p1, p2);
- calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
- (void **)&dataptrs);
+ for (i = 0; i < NDISKS - 1; i++)
+ for (j = i + 1; j < NDISKS; j++)
+ test_recover(test, i, j);
+ }
+ }
+}
- for (i = 0; i < NDISKS-1; i++)
- for (j = i+1; j < NDISKS; j++)
- test_disks(test, calls,
- ra, i, j);
- }
+static const void *raid6_gen_params(struct kunit *test, const void *prev,
+ char *desc)
+{
+ if (!prev) {
+ memset(&args, 0, sizeof(args));
+next_algo:
+ args.recov_idx = 0;
+ args.gen = raid6_algo_find(args.gen_idx);
+ if (!args.gen)
+ return NULL;
+ }
- }
+ if (args.recov)
+ args.recov_idx++;
+ args.recov = raid6_recov_algo_find(args.recov_idx);
+ if (!args.recov) {
+ args.gen_idx++;
+ goto next_algo;
}
+
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "gen=%s recov=%s",
+ args.gen->name, args.recov->name);
+ return &args;
}
static struct kunit_case raid6_test_cases[] = {
- KUNIT_CASE(raid6_test),
+ KUNIT_CASE_PARAM(raid6_test, raid6_gen_params),
{},
};
--
2.53.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox