* [PATCH 25/27] xor: use static_call for xor_gen
From: Christoph Hellwig @ 2026-03-11 7:03 UTC (permalink / raw)
To: Andrew Morton
Cc: Richard Henderson, Matt Turner, Magnus Lindholm, Russell King,
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,
David S. Miller, Andreas Larsson, Richard Weinberger,
Anton Ivanov, Johannes Berg, 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, Theodore Ts'o, Jason A. Donenfeld,
linux-alpha, linux-kernel, linux-arm-kernel, loongarch,
linuxppc-dev, linux-riscv, linux-s390, sparclinux, linux-um,
linux-crypto, linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260311070416.972667-1-hch@lst.de>
Avoid the indirect call for xor_generation by using a static_call.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
lib/raid/xor/xor-32regs.c | 2 +-
lib/raid/xor/xor-core.c | 29 ++++++++++++++---------------
lib/raid/xor/xor_impl.h | 4 ++++
3 files changed, 19 insertions(+), 16 deletions(-)
diff --git a/lib/raid/xor/xor-32regs.c b/lib/raid/xor/xor-32regs.c
index acb4a10d1e95..759a31f76414 100644
--- a/lib/raid/xor/xor-32regs.c
+++ b/lib/raid/xor/xor-32regs.c
@@ -209,7 +209,7 @@ xor_32regs_5(unsigned long bytes, unsigned long * __restrict p1,
} while (--lines > 0);
}
-DO_XOR_BLOCKS(32regs, xor_32regs_2, xor_32regs_3, xor_32regs_4, xor_32regs_5);
+__DO_XOR_BLOCKS(32regs, xor_32regs_2, xor_32regs_3, xor_32regs_4, xor_32regs_5);
struct xor_block_template xor_block_32regs = {
.name = "32regs",
diff --git a/lib/raid/xor/xor-core.c b/lib/raid/xor/xor-core.c
index c3523f92ab83..331da1afc0e4 100644
--- a/lib/raid/xor/xor-core.c
+++ b/lib/raid/xor/xor-core.c
@@ -11,10 +11,14 @@
#include <linux/raid/xor.h>
#include <linux/jiffies.h>
#include <linux/preempt.h>
+#include <linux/static_call.h>
#include "xor_impl.h"
-/* The xor routine to use. */
-static struct xor_block_template *active_template;
+/*
+ * Provide a temporary default until the fastest or forced implementation is
+ * picked.
+ */
+DEFINE_STATIC_CALL(xor_gen_impl, xor_gen_32regs);
/**
* xor_gen - generate RAID-style XOR information
@@ -36,13 +40,13 @@ void xor_gen(void *dest, void **srcs, unsigned int src_cnt, unsigned int bytes)
lockdep_assert_preemption_enabled();
WARN_ON_ONCE(bytes & 511);
- active_template->xor_gen(dest, srcs, src_cnt, bytes);
+ static_call(xor_gen_impl)(dest, srcs, src_cnt, bytes);
}
EXPORT_SYMBOL(xor_gen);
/* Set of all registered templates. */
static struct xor_block_template *__initdata template_list;
-static bool __initdata xor_forced = false;
+static struct xor_block_template *forced_template;
/**
* xor_register - register a XOR template
@@ -68,7 +72,7 @@ void __init xor_register(struct xor_block_template *tmpl)
*/
void __init xor_force(struct xor_block_template *tmpl)
{
- active_template = tmpl;
+ forced_template = tmpl;
}
#define BENCH_SIZE 4096
@@ -110,7 +114,7 @@ static int __init calibrate_xor_blocks(void)
void *b1, *b2;
struct xor_block_template *f, *fastest;
- if (xor_forced)
+ if (forced_template)
return 0;
b1 = (void *) __get_free_pages(GFP_KERNEL, 2);
@@ -127,7 +131,7 @@ static int __init calibrate_xor_blocks(void)
if (f->speed > fastest->speed)
fastest = f;
}
- active_template = fastest;
+ static_call_update(xor_gen_impl, fastest->xor_gen);
pr_info("xor: using function: %s (%d MB/sec)\n",
fastest->name, fastest->speed);
@@ -155,21 +159,16 @@ static int __init xor_init(void)
* If this arch/cpu has a short-circuited selection, don't loop through
* all the possible functions, just use the best one.
*/
- if (active_template) {
+ if (forced_template) {
pr_info("xor: automatically using best checksumming function %-10s\n",
- active_template->name);
- xor_forced = true;
+ forced_template->name);
+ static_call_update(xor_gen_impl, forced_template->xor_gen);
return 0;
}
#ifdef MODULE
return calibrate_xor_blocks();
#else
- /*
- * Pick the first template as the temporary default until calibration
- * happens.
- */
- active_template = template_list;
return 0;
#endif
}
diff --git a/lib/raid/xor/xor_impl.h b/lib/raid/xor/xor_impl.h
index 09ae2916f71e..59c801777219 100644
--- a/lib/raid/xor/xor_impl.h
+++ b/lib/raid/xor/xor_impl.h
@@ -50,6 +50,10 @@ extern struct xor_block_template xor_block_32regs;
extern struct xor_block_template xor_block_8regs_p;
extern struct xor_block_template xor_block_32regs_p;
+/* default call until updated */
+void xor_gen_32regs(void *dest, void **srcs, unsigned int src_cnt,
+ unsigned int bytes);
+
void __init xor_register(struct xor_block_template *tmpl);
void __init xor_force(struct xor_block_template *tmpl);
--
2.47.3
^ permalink raw reply related
* [PATCH 26/27] random: factor out a __limit_random_u32_below helper
From: Christoph Hellwig @ 2026-03-11 7:03 UTC (permalink / raw)
To: Andrew Morton
Cc: Richard Henderson, Matt Turner, Magnus Lindholm, Russell King,
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,
David S. Miller, Andreas Larsson, Richard Weinberger,
Anton Ivanov, Johannes Berg, 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, Theodore Ts'o, Jason A. Donenfeld,
linux-alpha, linux-kernel, linux-arm-kernel, loongarch,
linuxppc-dev, linux-riscv, linux-s390, sparclinux, linux-um,
linux-crypto, linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260311070416.972667-1-hch@lst.de>
Factor out the guts of __get_random_u32_below into a new helper,
so that callers with their own prng state can reuse this code.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
drivers/char/random.c | 26 +++++++++++++++-----------
include/linux/random.h | 1 +
2 files changed, 16 insertions(+), 11 deletions(-)
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 7ff4d29911fd..23b5addf02fb 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -544,18 +544,16 @@ DEFINE_BATCHED_ENTROPY(u16)
DEFINE_BATCHED_ENTROPY(u32)
DEFINE_BATCHED_ENTROPY(u64)
-u32 __get_random_u32_below(u32 ceil)
+/*
+ * This is the slow path for variable ceil. It is still fast, most of the time,
+ * by doing traditional reciprocal multiplication and opportunistically
+ * comparing the lower half to ceil itself, before falling back to computing a
+ * larger bound, and then rejecting samples whose lower half would indicate a
+ * range indivisible by ceil. The use of `-ceil % ceil` is analogous to `2^32 %
+ * ceil`, but is computable in 32-bits.
+ */
+u32 __limit_random_u32_below(u32 ceil, u32 rand)
{
- /*
- * This is the slow path for variable ceil. It is still fast, most of
- * the time, by doing traditional reciprocal multiplication and
- * opportunistically comparing the lower half to ceil itself, before
- * falling back to computing a larger bound, and then rejecting samples
- * whose lower half would indicate a range indivisible by ceil. The use
- * of `-ceil % ceil` is analogous to `2^32 % ceil`, but is computable
- * in 32-bits.
- */
- u32 rand = get_random_u32();
u64 mult;
/*
@@ -577,6 +575,12 @@ u32 __get_random_u32_below(u32 ceil)
}
return mult >> 32;
}
+EXPORT_SYMBOL_GPL(__limit_random_u32_below);
+
+u32 __get_random_u32_below(u32 ceil)
+{
+ return __limit_random_u32_below(ceil, get_random_u32());
+}
EXPORT_SYMBOL(__get_random_u32_below);
#ifdef CONFIG_SMP
diff --git a/include/linux/random.h b/include/linux/random.h
index 8a8064dc3970..54401dd53f68 100644
--- a/include/linux/random.h
+++ b/include/linux/random.h
@@ -50,6 +50,7 @@ static inline unsigned long get_random_long(void)
#endif
}
+u32 __limit_random_u32_below(u32 ceil, u32 rand);
u32 __get_random_u32_below(u32 ceil);
/*
--
2.47.3
^ permalink raw reply related
* [PATCH 27/27] xor: add a kunit test case
From: Christoph Hellwig @ 2026-03-11 7:03 UTC (permalink / raw)
To: Andrew Morton
Cc: Richard Henderson, Matt Turner, Magnus Lindholm, Russell King,
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,
David S. Miller, Andreas Larsson, Richard Weinberger,
Anton Ivanov, Johannes Berg, 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, Theodore Ts'o, Jason A. Donenfeld,
linux-alpha, linux-kernel, linux-arm-kernel, loongarch,
linuxppc-dev, linux-riscv, linux-s390, sparclinux, linux-um,
linux-crypto, linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260311070416.972667-1-hch@lst.de>
Add a test case for the XOR routines loosely based on the CRC kunit
test.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
lib/raid/Kconfig | 11 ++
lib/raid/xor/Makefile | 2 +-
lib/raid/xor/tests/Makefile | 3 +
lib/raid/xor/tests/xor_kunit.c | 180 +++++++++++++++++++++++++++++++++
4 files changed, 195 insertions(+), 1 deletion(-)
create mode 100644 lib/raid/xor/tests/Makefile
create mode 100644 lib/raid/xor/tests/xor_kunit.c
diff --git a/lib/raid/Kconfig b/lib/raid/Kconfig
index 4359971ebd04..97c123806466 100644
--- a/lib/raid/Kconfig
+++ b/lib/raid/Kconfig
@@ -6,3 +6,14 @@ config XOR_BLOCKS
# selected by architectures that provide an optimized XOR implementation
config XOR_BLOCKS_ARCH
bool
+
+config XOR_KUNIT_TEST
+ tristate "KUnit tests for xor_gen" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ default KUNIT_ALL_TESTS
+ select XOR_BLOCKS
+ help
+ Unit tests for the XOR library functions.
+
+ This is intended to help people writing architecture-specific
+ optimized versions. If unsure, say N.
diff --git a/lib/raid/xor/Makefile b/lib/raid/xor/Makefile
index 7b748ddda9d4..74185bdc3dd8 100644
--- a/lib/raid/xor/Makefile
+++ b/lib/raid/xor/Makefile
@@ -30,7 +30,7 @@ xor-$(CONFIG_SPARC64) += sparc/xor-sparc64.o sparc/xor-sparc64-glue.o
xor-$(CONFIG_S390) += s390/xor.o
xor-$(CONFIG_X86_32) += x86/xor-avx.o x86/xor-sse.o x86/xor-mmx.o
xor-$(CONFIG_X86_64) += x86/xor-avx.o x86/xor-sse.o
-
+obj-y += tests/
CFLAGS_arm/xor-neon.o += $(CC_FLAGS_FPU)
CFLAGS_REMOVE_arm/xor-neon.o += $(CC_FLAGS_NO_FPU)
diff --git a/lib/raid/xor/tests/Makefile b/lib/raid/xor/tests/Makefile
new file mode 100644
index 000000000000..661e8f6ffd1f
--- /dev/null
+++ b/lib/raid/xor/tests/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-$(CONFIG_XOR_KUNIT_TEST) += xor_kunit.o
diff --git a/lib/raid/xor/tests/xor_kunit.c b/lib/raid/xor/tests/xor_kunit.c
new file mode 100644
index 000000000000..23ee415e914c
--- /dev/null
+++ b/lib/raid/xor/tests/xor_kunit.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Unit test the XOR library functions.
+ *
+ * Copyright 2024 Google LLC
+ * Copyright 2026 Christoph Hellwig
+ *
+ * Based on the CRC tests by Eric Biggers <ebiggers@google.com>.
+ */
+#include <kunit/test.h>
+#include <linux/prandom.h>
+#include <linux/string_choices.h>
+#include <linux/vmalloc.h>
+#include <linux/raid/xor.h>
+
+#define XOR_KUNIT_SEED 42
+#define XOR_KUNIT_MAX_BYTES 16384
+#define XOR_KUNIT_MAX_BUFFERS 64
+#define XOR_KUNIT_NUM_TEST_ITERS 1000
+
+static struct rnd_state rng;
+static void *test_buffers[XOR_KUNIT_MAX_BUFFERS];
+static void *test_dest;
+static void *test_ref;
+static size_t test_buflen;
+
+static u32 rand32(void)
+{
+ return prandom_u32_state(&rng);
+}
+
+static u32 rand32_below(u32 ceil)
+{
+ return __limit_random_u32_below(ceil, prandom_u32_state(&rng));
+}
+
+/* Reference implementation using dumb byte-wise XOR */
+static void xor_ref(void *dest, void **srcs, unsigned int src_cnt,
+ unsigned int bytes)
+{
+ unsigned int off, idx;
+ u8 *d = dest;
+
+ for (off = 0; off < bytes; off++) {
+ for (idx = 0; idx < src_cnt; idx++) {
+ u8 *src = srcs[idx];
+
+ d[off] ^= src[off];
+ }
+ }
+}
+
+/* Generate a random length that is a multiple of 512. */
+static unsigned int generate_random_length(unsigned int max_length)
+{
+ return (rand32_below(max_length / 512) + 1) * 512;
+}
+
+/* Generate a random alignment that is a multiple of 32. */
+static unsigned int generate_random_alignment(unsigned int max_alignment)
+{
+ return (rand32_below((max_alignment + 1) / 32)) * 32;
+}
+
+static void xor_generate_random_data(void)
+{
+ int i;
+
+ prandom_bytes_state(&rng, test_dest, test_buflen);
+ memcpy(test_ref, test_dest, test_buflen);
+ for (i = 0; i < XOR_KUNIT_MAX_BUFFERS; i++)
+ prandom_bytes_state(&rng, test_buffers[i], test_buflen);
+}
+
+/* Test that xor_gen gives the same result as a reference implementation. */
+static void xor_test(struct kunit *test)
+{
+ void *aligned_buffers[XOR_KUNIT_MAX_BUFFERS];
+ size_t i;
+
+ for (i = 0; i < XOR_KUNIT_NUM_TEST_ITERS; i++) {
+ unsigned int nr_buffers =
+ rand32_below(XOR_KUNIT_MAX_BUFFERS) + 1;
+ unsigned int len = generate_random_length(XOR_KUNIT_MAX_BYTES);
+ unsigned int max_alignment, align = 0;
+ void *buffers;
+
+ if (rand32() % 8 == 0)
+ /* Refresh the data occasionally. */
+ xor_generate_random_data();
+
+ /*
+ * If we're not using the entire buffer size, inject randomize
+ * alignment into the buffer.
+ */
+ max_alignment = XOR_KUNIT_MAX_BYTES - len;
+ if (max_alignment) {
+ int j;
+
+ align = generate_random_alignment(max_alignment);
+ for (j = 0; j < nr_buffers; j++)
+ aligned_buffers[j] = test_buffers[j] +
+ generate_random_alignment(max_alignment);
+ buffers = aligned_buffers;
+ } else {
+ buffers = test_buffers;
+ }
+
+ /*
+ * Compute the XOR, and verify that it equals the XOR computed
+ * by a simple byte-at-a-time reference implementation.
+ */
+ xor_ref(test_ref + align, buffers, nr_buffers, len);
+ xor_gen(test_dest + align, buffers, nr_buffers, len);
+ KUNIT_EXPECT_MEMEQ_MSG(test, test_ref, test_dest, len,
+ "Wrong result with buffers=%u, len=%u, align=%s",
+ nr_buffers, len, str_yes_no(max_alignment));
+ }
+}
+
+static struct kunit_case xor_test_cases[] = {
+ KUNIT_CASE(xor_test),
+ {},
+};
+
+static int xor_suite_init(struct kunit_suite *suite)
+{
+ int i;
+
+ /*
+ * 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.
+ */
+ test_buflen = round_up(XOR_KUNIT_MAX_BYTES, PAGE_SIZE);
+ test_ref = vmalloc(test_buflen);
+ if (!test_ref)
+ return -ENOMEM;
+ test_dest = vmalloc(test_buflen);
+ if (!test_dest)
+ goto out_free_ref;
+ for (i = 0; i < XOR_KUNIT_MAX_BUFFERS; i++) {
+ test_buffers[i] = vmalloc(test_buflen);
+ if (!test_buffers[i])
+ goto out_free_buffers;
+ }
+
+ prandom_seed_state(&rng, XOR_KUNIT_SEED);
+ xor_generate_random_data();
+ return 0;
+
+out_free_buffers:
+ while (--i >= 0)
+ vfree(test_buffers[i]);
+ vfree(test_dest);
+out_free_ref:
+ vfree(test_ref);
+ return -ENOMEM;
+}
+
+static void xor_suite_exit(struct kunit_suite *suite)
+{
+ int i;
+
+ vfree(test_ref);
+ vfree(test_dest);
+ for (i = 0; i < XOR_KUNIT_MAX_BUFFERS; i++)
+ vfree(test_buffers[i]);
+}
+
+static struct kunit_suite xor_test_suite = {
+ .name = "xor",
+ .test_cases = xor_test_cases,
+ .suite_init = xor_suite_init,
+ .suite_exit = xor_suite_exit,
+};
+kunit_test_suite(xor_test_suite);
+
+MODULE_DESCRIPTION("Unit tests and benchmarks for the XOR library functions");
+MODULE_LICENSE("GPL");
--
2.47.3
^ permalink raw reply related
* Re: [PATCH 03/27] um/xor: cleanup xor.h
From: Richard Weinberger @ 2026-03-11 8:45 UTC (permalink / raw)
To: hch
Cc: Andrew Morton, Richard Henderson, Matt Turner, Magnus Lindholm,
Russell King, Catalin Marinas, will, 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, davem,
Andreas Larsson, anton ivanov, Johannes Berg, Thomas Gleixner,
mingo, bp, dave hansen, x86, hpa, Herbert Xu, dan j williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, tytso, Jason A. Donenfeld, linux-alpha, linux-kernel,
linux-arm-kernel, loongarch, linuxppc-dev, linux-riscv,
linux-s390, sparclinux, linux-um, Linux Crypto Mailing List,
linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260311070416.972667-4-hch@lst.de>
----- Ursprüngliche Mail -----
> Since commit c055e3eae0f1 ("crypto: xor - use ktime for template
> benchmarking") the benchmarking works just fine even for TT_MODE_INFCPU,
> so drop the workarounds. Note that for CPUs supporting AVX2, which
> includes almost everything built in the last 10 years, the AVX2
> implementation is forced anyway.
>
> CONFIG_X86_32 is always correctly set for UM in arch/x86/um/Kconfig,
> so don't override it either.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> arch/um/include/asm/xor.h | 16 ----------------
> 1 file changed, 16 deletions(-)
Acked-by: Richard Weinberger <richard@nod.at>
Thanks,
//richard
^ permalink raw reply
* Re: cleanup the RAID5 XOR library v2
From: Andrew Morton @ 2026-03-11 18:57 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Richard Henderson, Matt Turner, Magnus Lindholm, Russell King,
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,
David S. Miller, Andreas Larsson, Richard Weinberger,
Anton Ivanov, Johannes Berg, 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, Theodore Ts'o, Jason A. Donenfeld,
linux-alpha, linux-kernel, linux-arm-kernel, loongarch,
linuxppc-dev, linux-riscv, linux-s390, sparclinux, linux-um,
linux-crypto, linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260311070416.972667-1-hch@lst.de>
On Wed, 11 Mar 2026 08:03:32 +0100 Christoph Hellwig <hch@lst.de> wrote:
> the XOR library used for the RAID5 parity is a bit of a mess right now.
> The main file sits in crypto/ despite not being cryptography and not
> using the crypto API, with the generic implementations sitting in
> include/asm-generic and the arch implementations sitting in an asm/
> header in theory. The latter doesn't work for many cases, so
> architectures often build the code directly into the core kernel, or
> create another module for the architecture code.
>
> Changes this to a single module in lib/ that also contains the
> architecture optimizations, similar to the library work Eric Biggers
> has done for the CRC and crypto libraries later. After that it changes
> to better calling conventions that allow for smarter architecture
> implementations (although none is contained here yet), and uses
> static_call to avoid indirection function call overhead.
Cool, thanks, I updated mm.git's mm-nonmm-unstable branch (and hence
linux-next) to this version.
I retained Heiko's ack on [17/27].
^ permalink raw reply
* Re: [PATCH 26/27] random: factor out a __limit_random_u32_below helper
From: Eric Biggers @ 2026-03-11 22:29 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Andrew Morton, Richard Henderson, Matt Turner, Magnus Lindholm,
Russell King, 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, David S. Miller, Andreas Larsson,
Richard Weinberger, Anton Ivanov, Johannes Berg, 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, Theodore Ts'o,
Jason A. Donenfeld, linux-alpha, linux-kernel, linux-arm-kernel,
loongarch, linuxppc-dev, linux-riscv, linux-s390, sparclinux,
linux-um, linux-crypto, linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260311070416.972667-27-hch@lst.de>
On Wed, Mar 11, 2026 at 08:03:58AM +0100, Christoph Hellwig wrote:
> Factor out the guts of __get_random_u32_below into a new helper,
> so that callers with their own prng state can reuse this code.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
I think I'd prefer that the test just uses the mod operation instead,
like many of the existing tests do:
prandom_u32_state(&rng) % ceil
Yes, when ceil isn't a power of 2 the result isn't uniformly
distributed. But that's perfectly fine for these tests, especially with
the values of ceil being used being far smaller than U32_MAX.
There's been an effort to keep the cryptographic random number generator
(drivers/char/random.c and include/linux/random.h) separate from the
non-cryptographic random number generator (lib/random32.c and
include/linux/prandom.h). This patch feels like it's going in a
slightly wrong direction, where random.c gains a function that's used
with both cryptographic and non-cryptographic random numbers.
And if someone actually needs a fully unform distribution, then they'd
probably want cryptographic random numbers as well.
So I'm not sure the proposed combination of "fully uniform
non-cryptographic random numbers" makes much sense.
Plus the '% ceil' implementation is much easier to understand.
- Eric
^ permalink raw reply
* Re: [PATCH 27/27] xor: add a kunit test case
From: Eric Biggers @ 2026-03-12 0:54 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Andrew Morton, Richard Henderson, Matt Turner, Magnus Lindholm,
Russell King, 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, David S. Miller, Andreas Larsson,
Richard Weinberger, Anton Ivanov, Johannes Berg, 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, Theodore Ts'o,
Jason A. Donenfeld, linux-alpha, linux-kernel, linux-arm-kernel,
loongarch, linuxppc-dev, linux-riscv, linux-s390, sparclinux,
linux-um, linux-crypto, linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260311070416.972667-28-hch@lst.de>
On Wed, Mar 11, 2026 at 08:03:59AM +0100, Christoph Hellwig wrote:
> diff --git a/lib/raid/Kconfig b/lib/raid/Kconfig
> index 4359971ebd04..97c123806466 100644
> --- a/lib/raid/Kconfig
> +++ b/lib/raid/Kconfig
> @@ -6,3 +6,14 @@ config XOR_BLOCKS
> # selected by architectures that provide an optimized XOR implementation
> config XOR_BLOCKS_ARCH
> bool
> +
> +config XOR_KUNIT_TEST
> + tristate "KUnit tests for xor_gen" if !KUNIT_ALL_TESTS
> + depends on KUNIT
> + default KUNIT_ALL_TESTS
> + select XOR_BLOCKS
> + help
> + Unit tests for the XOR library functions.
> +
> + This is intended to help people writing architecture-specific
> + optimized versions. If unsure, say N.
The convention for KUnit tests is actually to depend on the code they
test, not select it, so that it's easy to enable only the tests that are
relevant to a particular kernel build. So instead of
"select XOR_BLOCKS", this should use "depends on KUNIT && XOR_BLOCKS".
(Yes, I got this wrong in the crypto and CRC tests. I recently fixed it
in the crypto tests, and I have pending patches that fix the CRC test.)
There should also be a lib/raid/.kunitconfig file containing something
like:
CONFIG_KUNIT=y
CONFIG_BTRFS_FS=y
CONFIG_XOR_KUNIT_TEST=y
(CONFIG_BTRFS_FS is there because it's one of the visible symbols that
select the hidden symbol XOR_BLOCKS.)
> +static u32 rand32(void)
> +{
> + return prandom_u32_state(&rng);
> +}
> +
> +static u32 rand32_below(u32 ceil)
> +{
> + return __limit_random_u32_below(ceil, prandom_u32_state(&rng));
> +}
> +
[...]
> +
> +/* Generate a random length that is a multiple of 512. */
> +static unsigned int generate_random_length(unsigned int max_length)
> +{
> + return (rand32_below(max_length / 512) + 1) * 512;
> +}
> +
> +/* Generate a random alignment that is a multiple of 32. */
> +static unsigned int generate_random_alignment(unsigned int max_alignment)
> +{
> + return (rand32_below((max_alignment + 1) / 32)) * 32;
> +}
As per my comment on patch 26, these should just use a simple mod
operations so that the new random.c helper function (which conflates
cryptographic and non-cryptographic random numbers) isn't needed.
Maybe:
return (rand32() % (max_length + 1)) & ~511;
and
return (rand32() % (max_alignment + 1)) & ~63;
> +/* Test that xor_gen gives the same result as a reference implementation. */
> +static void xor_test(struct kunit *test)
> +{
> + void *aligned_buffers[XOR_KUNIT_MAX_BUFFERS];
> + size_t i;
> +
> + for (i = 0; i < XOR_KUNIT_NUM_TEST_ITERS; i++) {
> + unsigned int nr_buffers =
> + rand32_below(XOR_KUNIT_MAX_BUFFERS) + 1;
> + unsigned int len = generate_random_length(XOR_KUNIT_MAX_BYTES);
> + unsigned int max_alignment, align = 0;
> + void *buffers;
> +
> + if (rand32() % 8 == 0)
> + /* Refresh the data occasionally. */
> + xor_generate_random_data();
> +
> + /*
> + * If we're not using the entire buffer size, inject randomize
> + * alignment into the buffer.
> + */
> + max_alignment = XOR_KUNIT_MAX_BYTES - len;
> + if (max_alignment) {
> + int j;
> +
> + align = generate_random_alignment(max_alignment);
> + for (j = 0; j < nr_buffers; j++)
> + aligned_buffers[j] = test_buffers[j] +
> + generate_random_alignment(max_alignment);
> + buffers = aligned_buffers;
> + } else {
> + buffers = test_buffers;
> + }
This isn't taking advantage of the guard pages properly, since it rarely
selects buffers that go all the way up to the guard page.
If the guard page testing is going to be included (which is a good idea;
the crypto and CRC tests have it and they already caught a bug using
it), then the data should be placed at the very end of the buffers more
often, like what the CRC test does.
> + /*
> + * Compute the XOR, and verify that it equals the XOR computed
> + * by a simple byte-at-a-time reference implementation.
> + */
> + xor_ref(test_ref + align, buffers, nr_buffers, len);
> + xor_gen(test_dest + align, buffers, nr_buffers, len);
> + KUNIT_EXPECT_MEMEQ_MSG(test, test_ref, test_dest, len,
> + "Wrong result with buffers=%u, len=%u, align=%s",
> + nr_buffers, len, str_yes_no(max_alignment));
When align != 0, this does the comparison at the wrong offset.
The message also shows "align=no" if fully aligned buffers were used and
"align=yes" if they were not, which is a bit confusing. Maybe replace
align=%s with randalign=%s.
> +MODULE_DESCRIPTION("Unit tests and benchmarks for the XOR library functions");
There's no benchmark included (yet), so that should be left out of the
description.
Also, I tried running this test on different architectures, and in
qemu-system-sparc64 it crashes with an alignment fault in xor_vis_5().
It goes away if the minimum tested alignment is increased from 32 bytes
to 64 bytes. lib/raid/xor/sparc/xor-sparc64.S has a comment that
documents a requirement of "!(((long)dest | (long)sourceN) & (64 - 1))",
i.e. 64-byte alignment.
So, it seems the assumption that 32 bytes is the maximum required
alignment over all architectures is not correct. The tested alignment
will need to be increased to 64 bytes, and the kerneldoc for xor_gen()
will need to be updated as well.
- Eric
^ permalink raw reply
* Re: [PATCH 23/27] btrfs: use xor_gen
From: David Sterba @ 2026-03-12 6:14 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Andrew Morton, Richard Henderson, Matt Turner, Magnus Lindholm,
Russell King, 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, David S. Miller, Andreas Larsson,
Richard Weinberger, Anton Ivanov, Johannes Berg, 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, Theodore Ts'o,
Jason A. Donenfeld, linux-alpha, linux-kernel, linux-arm-kernel,
loongarch, linuxppc-dev, linux-riscv, linux-s390, sparclinux,
linux-um, linux-crypto, linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260311070416.972667-24-hch@lst.de>
On Wed, Mar 11, 2026 at 08:03:55AM +0100, Christoph Hellwig wrote:
> Use the new xor_gen helper instead of open coding the loop around
> xor_blocks. This helper is very similar to the existing run_xor helper
> in btrfs, except that the destination buffer is passed explicitly.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
Acked-by: David Sterba <dsterba@suse.com>
^ permalink raw reply
* Re: [PATCH 26/27] random: factor out a __limit_random_u32_below helper
From: David Laight @ 2026-03-12 8:38 UTC (permalink / raw)
To: Eric Biggers
Cc: Christoph Hellwig, Andrew Morton, Richard Henderson, Matt Turner,
Magnus Lindholm, Russell King, 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, David S. Miller, Andreas Larsson,
Richard Weinberger, Anton Ivanov, Johannes Berg, 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, Theodore Ts'o,
Jason A. Donenfeld, linux-alpha, linux-kernel, linux-arm-kernel,
loongarch, linuxppc-dev, linux-riscv, linux-s390, sparclinux,
linux-um, linux-crypto, linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260311222935.GA3161@quark>
On Wed, 11 Mar 2026 15:29:35 -0700
Eric Biggers <ebiggers@kernel.org> wrote:
> On Wed, Mar 11, 2026 at 08:03:58AM +0100, Christoph Hellwig wrote:
> > Factor out the guts of __get_random_u32_below into a new helper,
> > so that callers with their own prng state can reuse this code.
> >
> > Signed-off-by: Christoph Hellwig <hch@lst.de>
>
> I think I'd prefer that the test just uses the mod operation instead,
> like many of the existing tests do:
>
> prandom_u32_state(&rng) % ceil
Or possibly what the old code used:
(prandom_u32_state(&rnd) * (u64)ceil) >> 32
Which distributes the values evenly across the range although
some values happen 1 more time than others.
I suspect that is good enough for a lot of the users of the cryptographic
random number generator as well.
David
>
> Yes, when ceil isn't a power of 2 the result isn't uniformly
> distributed. But that's perfectly fine for these tests, especially with
> the values of ceil being used being far smaller than U32_MAX.
>
> There's been an effort to keep the cryptographic random number generator
> (drivers/char/random.c and include/linux/random.h) separate from the
> non-cryptographic random number generator (lib/random32.c and
> include/linux/prandom.h). This patch feels like it's going in a
> slightly wrong direction, where random.c gains a function that's used
> with both cryptographic and non-cryptographic random numbers.
>
> And if someone actually needs a fully unform distribution, then they'd
> probably want cryptographic random numbers as well.
>
> So I'm not sure the proposed combination of "fully uniform
> non-cryptographic random numbers" makes much sense.
>
> Plus the '% ceil' implementation is much easier to understand.
>
> - Eric
>
^ permalink raw reply
* Re: [PATCH 26/27] random: factor out a __limit_random_u32_below helper
From: Jason A. Donenfeld @ 2026-03-12 13:46 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Andrew Morton, Richard Henderson, Matt Turner, Magnus Lindholm,
Russell King, 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, David S. Miller, Andreas Larsson,
Richard Weinberger, Anton Ivanov, Johannes Berg, 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, Theodore Ts'o,
linux-alpha, linux-kernel, linux-arm-kernel, loongarch,
linuxppc-dev, linux-riscv, linux-s390, sparclinux, linux-um,
linux-crypto, linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260311070416.972667-27-hch@lst.de>
On Wed, Mar 11, 2026 at 08:03:58AM +0100, Christoph Hellwig wrote:
> Factor out the guts of __get_random_u32_below into a new helper,
> so that callers with their own prng state can reuse this code.
What Eric said. random.c is not "some library code" meant to be pulled
apart like this. If you think there are some good general purpose
arithmetic functions, by all means develop shared infrastructure in the
right place. But I think for this super simple/trivial _below function,
you can probably just place it additionally where you're using it,
without needing to touch random.c.
^ permalink raw reply
* Re: [PATCH v2 4/5] md/md-llbitmap: add CleanUnwritten state for RAID-5 proactive parity building
From: Xiao Ni @ 2026-03-13 3:16 UTC (permalink / raw)
To: Yu Kuai; +Cc: song, linan122, colyli, linux-raid, linux-kernel
In-Reply-To: <20260223024038.3084853-5-yukuai@fnnas.com>
On Mon, Feb 23, 2026 at 10:44 AM Yu Kuai <yukuai@fnnas.com> wrote:
>
> Add new states to the llbitmap state machine to support proactive XOR
> parity building for RAID-5 arrays. This allows users to pre-build parity
> data for unwritten regions before any user data is written.
>
> New states added:
> - BitNeedSyncUnwritten: Transitional state when proactive sync is triggered
> via sysfs on Unwritten regions.
> - BitSyncingUnwritten: Proactive sync in progress for unwritten region.
> - BitCleanUnwritten: XOR parity has been pre-built, but no user data
> written yet. When user writes to this region, it transitions to BitDirty.
>
> New actions added:
> - BitmapActionProactiveSync: Trigger for proactive XOR parity building.
> - BitmapActionClearUnwritten: Convert CleanUnwritten/NeedSyncUnwritten/
> SyncingUnwritten states back to Unwritten before recovery starts.
>
> State flows:
> - Current (lazy): Unwritten -> (write) -> NeedSync -> (sync) -> Dirty -> Clean
> - New (proactive): Unwritten -> (sysfs) -> NeedSyncUnwritten -> (sync) -> CleanUnwritten
> - On write to CleanUnwritten: CleanUnwritten -> (write) -> Dirty -> Clean
> - On disk replacement: CleanUnwritten regions are converted to Unwritten
> before recovery starts, so recovery only rebuilds regions with user data
>
> A new sysfs interface is added at /sys/block/mdX/md/llbitmap/proactive_sync
> (write-only) to trigger proactive sync. This only works for RAID-456 arrays.
>
> Signed-off-by: Yu Kuai <yukuai@fnnas.com>
> ---
> drivers/md/md-llbitmap.c | 140 +++++++++++++++++++++++++++++++++++----
> drivers/md/md.c | 6 +-
> 2 files changed, 132 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
> index 5f9e7004e3e3..461050b2771b 100644
> --- a/drivers/md/md-llbitmap.c
> +++ b/drivers/md/md-llbitmap.c
> @@ -208,6 +208,20 @@ enum llbitmap_state {
> BitNeedSync,
> /* data is synchronizing */
> BitSyncing,
> + /*
> + * Proactive sync requested for unwritten region (raid456 only).
> + * Triggered via sysfs when user wants to pre-build XOR parity
> + * for regions that have never been written.
> + */
> + BitNeedSyncUnwritten,
> + /* Proactive sync in progress for unwritten region */
> + BitSyncingUnwritten,
> + /*
> + * XOR parity has been pre-built for a region that has never had
> + * user data written. When user writes to this region, it transitions
> + * to BitDirty.
> + */
> + BitCleanUnwritten,
> BitStateCount,
> BitNone = 0xff,
> };
> @@ -232,6 +246,12 @@ enum llbitmap_action {
> * BitNeedSync.
> */
> BitmapActionStale,
> + /*
> + * Proactive sync trigger for raid456 - builds XOR parity for
> + * Unwritten regions without requiring user data write first.
> + */
> + BitmapActionProactiveSync,
> + BitmapActionClearUnwritten,
> BitmapActionCount,
> /* Init state is BitUnwritten */
> BitmapActionInit,
> @@ -304,6 +324,8 @@ static char state_machine[BitStateCount][BitmapActionCount] = {
> [BitmapActionDaemon] = BitNone,
> [BitmapActionDiscard] = BitNone,
> [BitmapActionStale] = BitNone,
> + [BitmapActionProactiveSync] = BitNeedSyncUnwritten,
> + [BitmapActionClearUnwritten] = BitNone,
> },
> [BitClean] = {
> [BitmapActionStartwrite] = BitDirty,
> @@ -314,6 +336,8 @@ static char state_machine[BitStateCount][BitmapActionCount] = {
> [BitmapActionDaemon] = BitNone,
> [BitmapActionDiscard] = BitUnwritten,
> [BitmapActionStale] = BitNeedSync,
> + [BitmapActionProactiveSync] = BitNone,
> + [BitmapActionClearUnwritten] = BitNone,
> },
> [BitDirty] = {
> [BitmapActionStartwrite] = BitNone,
> @@ -324,6 +348,8 @@ static char state_machine[BitStateCount][BitmapActionCount] = {
> [BitmapActionDaemon] = BitClean,
> [BitmapActionDiscard] = BitUnwritten,
> [BitmapActionStale] = BitNeedSync,
> + [BitmapActionProactiveSync] = BitNone,
> + [BitmapActionClearUnwritten] = BitNone,
> },
> [BitNeedSync] = {
> [BitmapActionStartwrite] = BitNone,
> @@ -334,6 +360,8 @@ static char state_machine[BitStateCount][BitmapActionCount] = {
> [BitmapActionDaemon] = BitNone,
> [BitmapActionDiscard] = BitUnwritten,
> [BitmapActionStale] = BitNone,
> + [BitmapActionProactiveSync] = BitNone,
> + [BitmapActionClearUnwritten] = BitNone,
> },
> [BitSyncing] = {
> [BitmapActionStartwrite] = BitNone,
> @@ -344,6 +372,44 @@ static char state_machine[BitStateCount][BitmapActionCount] = {
> [BitmapActionDaemon] = BitNone,
> [BitmapActionDiscard] = BitUnwritten,
> [BitmapActionStale] = BitNeedSync,
> + [BitmapActionProactiveSync] = BitNone,
> + [BitmapActionClearUnwritten] = BitNone,
> + },
> + [BitNeedSyncUnwritten] = {
> + [BitmapActionStartwrite] = BitNeedSync,
> + [BitmapActionStartsync] = BitSyncingUnwritten,
> + [BitmapActionEndsync] = BitNone,
> + [BitmapActionAbortsync] = BitUnwritten,
> + [BitmapActionReload] = BitUnwritten,
> + [BitmapActionDaemon] = BitNone,
> + [BitmapActionDiscard] = BitUnwritten,
> + [BitmapActionStale] = BitUnwritten,
> + [BitmapActionProactiveSync] = BitNone,
> + [BitmapActionClearUnwritten] = BitUnwritten,
> + },
> + [BitSyncingUnwritten] = {
> + [BitmapActionStartwrite] = BitSyncing,
> + [BitmapActionStartsync] = BitSyncingUnwritten,
Hi Kuai
Is it ok to set the above state to BitNone? The orignal state is
already BitSyncingUnwritten.
> + [BitmapActionEndsync] = BitCleanUnwritten,
> + [BitmapActionAbortsync] = BitUnwritten,
> + [BitmapActionReload] = BitUnwritten,
> + [BitmapActionDaemon] = BitNone,
> + [BitmapActionDiscard] = BitUnwritten,
> + [BitmapActionStale] = BitUnwritten,
> + [BitmapActionProactiveSync] = BitNone,
> + [BitmapActionClearUnwritten] = BitUnwritten,
> + },
> + [BitCleanUnwritten] = {
> + [BitmapActionStartwrite] = BitDirty,
> + [BitmapActionStartsync] = BitNone,
> + [BitmapActionEndsync] = BitNone,
> + [BitmapActionAbortsync] = BitNone,
> + [BitmapActionReload] = BitNone,
> + [BitmapActionDaemon] = BitNone,
> + [BitmapActionDiscard] = BitUnwritten,
> + [BitmapActionStale] = BitUnwritten,
> + [BitmapActionProactiveSync] = BitNone,
> + [BitmapActionClearUnwritten] = BitUnwritten,
> },
> };
>
> @@ -376,6 +442,7 @@ static void llbitmap_infect_dirty_bits(struct llbitmap *llbitmap,
> pctl->state[pos] = level_456 ? BitNeedSync : BitDirty;
> break;
> case BitClean:
> + case BitCleanUnwritten:
> pctl->state[pos] = BitDirty;
> break;
> }
> @@ -383,7 +450,7 @@ static void llbitmap_infect_dirty_bits(struct llbitmap *llbitmap,
> }
>
> static void llbitmap_set_page_dirty(struct llbitmap *llbitmap, int idx,
> - int offset)
> + int offset, bool infect)
> {
> struct llbitmap_page_ctl *pctl = llbitmap->pctl[idx];
> unsigned int io_size = llbitmap->io_size;
> @@ -398,7 +465,7 @@ static void llbitmap_set_page_dirty(struct llbitmap *llbitmap, int idx,
> * resync all the dirty bits, hence skip infect new dirty bits to
> * prevent resync unnecessary data.
> */
> - if (llbitmap->mddev->degraded) {
> + if (llbitmap->mddev->degraded || !infect) {
> set_bit(block, pctl->dirty);
> return;
> }
> @@ -438,7 +505,9 @@ static void llbitmap_write(struct llbitmap *llbitmap, enum llbitmap_state state,
>
> llbitmap->pctl[idx]->state[bit] = state;
> if (state == BitDirty || state == BitNeedSync)
> - llbitmap_set_page_dirty(llbitmap, idx, bit);
> + llbitmap_set_page_dirty(llbitmap, idx, bit, true);
> + else if (state == BitNeedSyncUnwritten)
> + llbitmap_set_page_dirty(llbitmap, idx, bit, false);
> }
>
> static struct page *llbitmap_read_page(struct llbitmap *llbitmap, int idx)
> @@ -627,11 +696,10 @@ static enum llbitmap_state llbitmap_state_machine(struct llbitmap *llbitmap,
> goto write_bitmap;
> }
>
> - if (c == BitNeedSync)
> + if (c == BitNeedSync || c == BitNeedSyncUnwritten)
> need_resync = !mddev->degraded;
>
> state = state_machine[c][action];
> -
> write_bitmap:
> if (unlikely(mddev->degraded)) {
> /* For degraded array, mark new data as need sync. */
> @@ -658,8 +726,7 @@ static enum llbitmap_state llbitmap_state_machine(struct llbitmap *llbitmap,
> }
>
> llbitmap_write(llbitmap, state, start);
> -
> - if (state == BitNeedSync)
> + if (state == BitNeedSync || state == BitNeedSyncUnwritten)
> need_resync = !mddev->degraded;
> else if (state == BitDirty &&
> !timer_pending(&llbitmap->pending_timer))
> @@ -1229,7 +1296,7 @@ static bool llbitmap_blocks_synced(struct mddev *mddev, sector_t offset)
> unsigned long p = offset >> llbitmap->chunkshift;
> enum llbitmap_state c = llbitmap_read(llbitmap, p);
>
> - return c == BitClean || c == BitDirty;
> + return c == BitClean || c == BitDirty || c == BitCleanUnwritten;
> }
>
> static sector_t llbitmap_skip_sync_blocks(struct mddev *mddev, sector_t offset)
> @@ -1243,6 +1310,10 @@ static sector_t llbitmap_skip_sync_blocks(struct mddev *mddev, sector_t offset)
> if (c == BitUnwritten)
> return blocks;
>
> + /* Skip CleanUnwritten - no user data, will be reset after recovery */
> + if (c == BitCleanUnwritten)
> + return blocks;
> +
> /* For degraded array, don't skip */
> if (mddev->degraded)
> return 0;
> @@ -1261,14 +1332,25 @@ static bool llbitmap_start_sync(struct mddev *mddev, sector_t offset,
> {
> struct llbitmap *llbitmap = mddev->bitmap;
> unsigned long p = offset >> llbitmap->chunkshift;
> + enum llbitmap_state state;
> +
> + /*
> + * Before recovery starts, convert CleanUnwritten to Unwritten.
> + * This ensures the new disk won't have stale parity data.
> + */
> + if (offset == 0 && test_bit(MD_RECOVERY_RECOVER, &mddev->recovery) &&
> + !test_bit(MD_RECOVERY_LAZY_RECOVER, &mddev->recovery))
> + llbitmap_state_machine(llbitmap, 0, llbitmap->chunks - 1,
> + BitmapActionClearUnwritten);
> +
>
> /*
> * Handle one bit at a time, this is much simpler. And it doesn't matter
> * if md_do_sync() loop more times.
> */
> *blocks = llbitmap->chunksize - (offset & (llbitmap->chunksize - 1));
> - return llbitmap_state_machine(llbitmap, p, p,
> - BitmapActionStartsync) == BitSyncing;
> + state = llbitmap_state_machine(llbitmap, p, p, BitmapActionStartsync);
> + return state == BitSyncing || state == BitSyncingUnwritten;
> }
>
> /* Something is wrong, sync_thread stop at @offset */
> @@ -1474,9 +1556,15 @@ static ssize_t bits_show(struct mddev *mddev, char *page)
> }
>
> mutex_unlock(&mddev->bitmap_info.mutex);
> - return sprintf(page, "unwritten %d\nclean %d\ndirty %d\nneed sync %d\nsyncing %d\n",
> + return sprintf(page,
> + "unwritten %d\nclean %d\ndirty %d\n"
> + "need sync %d\nsyncing %d\n"
> + "need sync unwritten %d\nsyncing unwritten %d\n"
> + "clean unwritten %d\n",
> bits[BitUnwritten], bits[BitClean], bits[BitDirty],
> - bits[BitNeedSync], bits[BitSyncing]);
> + bits[BitNeedSync], bits[BitSyncing],
> + bits[BitNeedSyncUnwritten], bits[BitSyncingUnwritten],
> + bits[BitCleanUnwritten]);
> }
>
> static struct md_sysfs_entry llbitmap_bits = __ATTR_RO(bits);
> @@ -1549,11 +1637,39 @@ barrier_idle_store(struct mddev *mddev, const char *buf, size_t len)
>
> static struct md_sysfs_entry llbitmap_barrier_idle = __ATTR_RW(barrier_idle);
>
> +static ssize_t
> +proactive_sync_store(struct mddev *mddev, const char *buf, size_t len)
> +{
> + struct llbitmap *llbitmap;
> +
> + /* Only for RAID-456 */
> + if (!raid_is_456(mddev))
> + return -EINVAL;
> +
> + mutex_lock(&mddev->bitmap_info.mutex);
> + llbitmap = mddev->bitmap;
> + if (!llbitmap || !llbitmap->pctl) {
> + mutex_unlock(&mddev->bitmap_info.mutex);
> + return -ENODEV;
> + }
> +
> + /* Trigger proactive sync on all Unwritten regions */
> + llbitmap_state_machine(llbitmap, 0, llbitmap->chunks - 1,
> + BitmapActionProactiveSync);
> +
> + mutex_unlock(&mddev->bitmap_info.mutex);
> + return len;
> +}
> +
> +static struct md_sysfs_entry llbitmap_proactive_sync =
> + __ATTR(proactive_sync, 0200, NULL, proactive_sync_store);
> +
> static struct attribute *md_llbitmap_attrs[] = {
> &llbitmap_bits.attr,
> &llbitmap_metadata.attr,
> &llbitmap_daemon_sleep.attr,
> &llbitmap_barrier_idle.attr,
> + &llbitmap_proactive_sync.attr,
> NULL
> };
>
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index 245785ad0ffd..b6543d81ac96 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -9878,8 +9878,10 @@ void md_do_sync(struct md_thread *thread)
> * Give other IO more of a chance.
> * The faster the devices, the less we wait.
> */
> - wait_event(mddev->recovery_wait,
> - !atomic_read(&mddev->recovery_active));
> + wait_event_timeout(
> + mddev->recovery_wait,
> + !atomic_read(&mddev->recovery_active),
> + HZ);
Could you explain this change? It no longer waits for synchronous I/O
to finish when the sync speed is fast. Does it have relasionship with
this major change of this patch?
Best Regards
Xiao
> }
> }
> }
> --
> 2.51.0
>
>
^ permalink raw reply
* Re: [PATCH v2 5/5] md/md-llbitmap: optimize initial sync with write_zeroes_unmap support
From: Xiao Ni @ 2026-03-13 8:12 UTC (permalink / raw)
To: Yu Kuai; +Cc: song, linan122, colyli, linux-raid, linux-kernel
In-Reply-To: <20260223024038.3084853-6-yukuai@fnnas.com>
On Mon, Feb 23, 2026 at 10:41 AM Yu Kuai <yukuai@fnnas.com> wrote:
>
> For RAID-456 arrays with llbitmap, if all underlying disks support
> write_zeroes with unmap, issue write_zeroes to zero all disk data
> regions and initialize the bitmap to BitCleanUnwritten instead of
> BitUnwritten.
>
> This optimization skips the initial XOR parity building because:
> 1. write_zeroes with unmap guarantees zeroed reads after the operation
> 2. For RAID-456, when all data is zero, parity is automatically
> consistent (0 XOR 0 XOR ... = 0)
> 3. BitCleanUnwritten indicates parity is valid but no user data
> has been written
>
> The implementation adds two helper functions:
> - llbitmap_all_disks_support_wzeroes_unmap(): Checks if all active
> disks support write_zeroes with unmap
> - llbitmap_zero_all_disks(): Issues blkdev_issue_zeroout() to each
> rdev's data region to zero all disks
>
> The zeroing and bitmap state setting happens in llbitmap_init_state()
> during bitmap initialization. If any disk fails to zero, we fall back
> to BitUnwritten and normal lazy recovery.
>
> This significantly reduces array initialization time for RAID-456
> arrays built on modern NVMe SSDs or other devices that support
> write_zeroes with unmap.
>
> Signed-off-by: Yu Kuai <yukuai@fnnas.com>
> ---
> drivers/md/md-llbitmap.c | 62 +++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 61 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
> index 461050b2771b..48bc6a639edd 100644
> --- a/drivers/md/md-llbitmap.c
> +++ b/drivers/md/md-llbitmap.c
> @@ -654,13 +654,73 @@ static int llbitmap_cache_pages(struct llbitmap *llbitmap)
> return 0;
> }
>
> +/*
> + * Check if all underlying disks support write_zeroes with unmap.
> + */
> +static bool llbitmap_all_disks_support_wzeroes_unmap(struct llbitmap *llbitmap)
> +{
> + struct mddev *mddev = llbitmap->mddev;
> + struct md_rdev *rdev;
> +
> + rdev_for_each(rdev, mddev) {
> + if (rdev->raid_disk < 0 || test_bit(Faulty, &rdev->flags))
> + continue;
> +
> + if (bdev_write_zeroes_unmap_sectors(rdev->bdev) == 0)
> + return false;
> + }
> +
> + return true;
> +}
> +
> +/*
> + * Issue write_zeroes to all underlying disks to zero their data regions.
> + * This ensures parity consistency for RAID-456 (0 XOR 0 = 0).
> + * Returns true if all disks were successfully zeroed.
> + */
> +static bool llbitmap_zero_all_disks(struct llbitmap *llbitmap)
> +{
> + struct mddev *mddev = llbitmap->mddev;
> + struct md_rdev *rdev;
> + sector_t dev_sectors = mddev->dev_sectors;
> + int ret;
> +
> + rdev_for_each(rdev, mddev) {
> + if (rdev->raid_disk < 0 || test_bit(Faulty, &rdev->flags))
> + continue;
> +
> + ret = blkdev_issue_zeroout(rdev->bdev,
> + rdev->data_offset,
> + dev_sectors,
> + GFP_KERNEL, 0);
> + if (ret) {
> + pr_warn("md/llbitmap: failed to zero disk %pg: %d\n",
> + rdev->bdev, ret);
> + return false;
> + }
> + }
> +
> + return true;
> +}
> +
> static void llbitmap_init_state(struct llbitmap *llbitmap)
> {
> + struct mddev *mddev = llbitmap->mddev;
> enum llbitmap_state state = BitUnwritten;
> unsigned long i;
>
> - if (test_and_clear_bit(BITMAP_CLEAN, &llbitmap->flags))
> + if (test_and_clear_bit(BITMAP_CLEAN, &llbitmap->flags)) {
> state = BitClean;
> + } else if (raid_is_456(mddev) &&
> + llbitmap_all_disks_support_wzeroes_unmap(llbitmap)) {
> + /*
> + * All disks support write_zeroes with unmap. Zero all disks
> + * to ensure parity consistency, then set BitCleanUnwritten
> + * to skip initial sync.
> + */
> + if (llbitmap_zero_all_disks(llbitmap))
> + state = BitCleanUnwritten;
> + }
>
> for (i = 0; i < llbitmap->chunks; i++)
> llbitmap_write(llbitmap, state, i);
> --
> 2.51.0
>
>
Hi Kuai
This patch looks good to me.
Reviewed-by: Xiao Ni <xni@redhat.com>
^ permalink raw reply
* [linux-next:master] [xor] ebbbf58989: BUG:KASAN:slab-out-of-bounds_in_xor_gen_avx
From: kernel test robot @ 2026-03-13 9:56 UTC (permalink / raw)
To: Christoph Hellwig
Cc: oe-lkp, lkp, Andrew Morton, Albert Ou, Alexander Gordeev,
Alexandre Ghiti, Andreas Larsson, Anton Ivanov, Arnd Bergmann,
Borislav Petkov (AMD), Catalin Marinas, Chris Mason,
Christian Borntraeger, Dan Williams, David S. Miller,
David Sterba, Heiko Carstens, Herbert Xu, H. Peter Anvin,
Huacai Chen, Ingo Molnar, Johannes Berg, Li Nan,
Madhavan Srinivasan, Magnus Lindholm, Matt Turner,
Michael Ellerman, Nicholas Piggin, Palmer Dabbelt,
Richard Henderson, Richard Weinberger, Russell King, Song Liu,
Sven Schnelle, Vasily Gorbik, WANG Xuerui, Will Deacon,
linux-raid, linux-kernel, linux-riscv, oliver.sang
Hello,
kernel test robot noticed "BUG:KASAN:slab-out-of-bounds_in_xor_gen_avx" on:
commit: ebbbf58989215f5c76b68c93e7d7a43e3a4b620a ("xor: pass the entire operation to the low-level ops")
https://git.kernel.org/cgit/linux/kernel/git/next/linux-next.git master
[test failed on linux-next/master f90aadf1c67c8b4969d1e5e6d4fd7227adb6e4d7]
in testcase: xfstests
version: xfstests-x86_64-63a29724-1_20260218
with following parameters:
disk: 6HDD
fs: btrfs
test: btrfs-group-14
config: x86_64-rhel-9.4-func
compiler: gcc-14
test machine: 8 threads 1 sockets Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz (Haswell) with 8G memory
(please refer to attached dmesg/kmsg for entire log/backtrace)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <oliver.sang@intel.com>
| Closes: https://lore.kernel.org/oe-lkp/202603131751.7ba6ffc8-lkp@intel.com
The kernel config and materials to reproduce are available at:
https://download.01.org/0day-ci/archive/20260313/202603131751.7ba6ffc8-lkp@intel.com
[ 79.007394][ T66] ==================================================================
[ 79.015339][ T66] BUG: KASAN: slab-out-of-bounds in xor_gen_avx+0x166/0x1b0 [xor]
[ 79.023007][ T66] Read of size 8 at addr ffff888135d50260 by task kworker/u32:2/66
[ 79.030748][ T66]
[ 79.032935][ T66] CPU: 4 UID: 0 PID: 66 Comm: kworker/u32:2 Tainted: G S 7.0.0-rc3-00077-gebbbf5898921 #1 PREEMPT(lazy)
[ 79.032940][ T66] Tainted: [S]=CPU_OUT_OF_SPEC
[ 79.032941][ T66] Hardware name: Dell Inc. OptiPlex 9020/0DNKMN, BIOS A05 12/05/2013
[ 79.032943][ T66] Workqueue: btrfs-rmw rmw_rbio_work [btrfs]
[ 79.033052][ T66] Call Trace:
[ 79.033054][ T66] <TASK>
[ 79.033056][ T66] dump_stack_lvl+0x47/0x70
[ 79.033062][ T66] print_address_description+0x88/0x320
[ 79.033067][ T66] ? xor_gen_avx+0x166/0x1b0 [xor]
[ 79.033071][ T66] print_report+0x106/0x1f4
[ 79.033074][ T66] ? __virt_addr_valid+0xc4/0x230
[ 79.033077][ T66] ? xor_gen_avx+0x166/0x1b0 [xor]
[ 79.033081][ T66] kasan_report+0xb5/0xf0
[ 79.033084][ T66] ? xor_gen_avx+0x166/0x1b0 [xor]
[ 79.033088][ T66] xor_gen_avx+0x166/0x1b0 [xor]
[ 79.033092][ T66] rmw_rbio+0xa8e/0x1230 [btrfs]
[ 79.033197][ T66] ? __pfx_rmw_rbio+0x10/0x10 [btrfs]
[ 79.033294][ T66] ? __pfx__raw_spin_lock+0x10/0x10
[ 79.033298][ T66] ? __switch_to+0x4c9/0xe70
[ 79.033303][ T66] ? lock_stripe_add+0x2a6/0x930 [btrfs]
[ 79.033390][ T66] process_one_work+0x668/0xf70
[ 79.033394][ T66] ? assign_work+0x131/0x3f0
[ 79.033396][ T66] worker_thread+0x505/0xd70
[ 79.033400][ T66] ? __pfx_worker_thread+0x10/0x10
[ 79.033405][ T66] kthread+0x353/0x470
[ 79.033419][ T66] ? recalc_sigpending+0x159/0x1f0
[ 79.033423][ T66] ? __pfx_kthread+0x10/0x10
[ 79.033425][ T66] ret_from_fork+0x32f/0x670
[ 79.033428][ T66] ? __pfx_ret_from_fork+0x10/0x10
[ 79.033431][ T66] ? switch_fpu+0x13/0x1f0
[ 79.033434][ T66] ? __switch_to+0x4c9/0xe70
[ 79.033446][ T66] ? __switch_to_asm+0x33/0x70
[ 79.033450][ T66] ? __pfx_kthread+0x10/0x10
[ 79.033452][ T66] ret_from_fork_asm+0x1a/0x30
[ 79.033465][ T66] </TASK>
[ 79.033467][ T66]
[ 79.207744][ T66] Allocated by task 8668:
[ 79.211925][ T66] kasan_save_stack+0x1e/0x70
[ 79.216456][ T66] kasan_save_track+0x10/0x30
[ 79.220993][ T66] __kasan_kmalloc+0x8b/0xb0
[ 79.225435][ T66] __kmalloc_noprof+0x1d8/0x5f0
[ 79.230156][ T66] alloc_rbio+0x230/0xcb0 [btrfs]
[ 79.235116][ T66] raid56_parity_write+0x41/0x530 [btrfs]
[ 79.240768][ T66] btrfs_submit_chunk+0x503/0x1670 [btrfs]
[ 79.246499][ T66] btrfs_submit_bbio+0x16/0x30 [btrfs]
[ 79.251882][ T66] submit_one_bio+0x20a/0x3b0 [btrfs]
[ 79.257189][ T66] submit_extent_folio+0x24e/0xb70 [btrfs]
[ 79.262928][ T66] extent_writepage_io+0x4ad/0x9f0 [btrfs]
[ 79.268666][ T66] extent_writepage+0x701/0x9b0 [btrfs]
[ 79.274145][ T66] extent_write_cache_pages+0x2c6/0xb70 [btrfs]
[ 79.280317][ T66] btrfs_writepages+0x1a9/0x470 [btrfs]
[ 79.285809][ T66] do_writepages+0x1d5/0x530
[ 79.290249][ T66] filemap_writeback+0x1d1/0x2b0
[ 79.295037][ T66] btrfs_fdatawrite_range+0x4f/0xf0 [btrfs]
[ 79.300861][ T66] btrfs_direct_write+0x4c0/0xaf0 [btrfs]
[ 79.306504][ T66] btrfs_do_write_iter+0x599/0x7b0 [btrfs]
[ 79.312240][ T66] vfs_write+0x4de/0xcf0
[ 79.316335][ T66] __x64_sys_pwrite64+0x18d/0x1f0
[ 79.321209][ T66] do_syscall_64+0x108/0x5b0
[ 79.325651][ T66] entry_SYSCALL_64_after_hwframe+0x76/0x7e
[ 79.331392][ T66]
[ 79.333578][ T66] The buggy address belongs to the object at ffff888135d50240
[ 79.333578][ T66] which belongs to the cache kmalloc-32 of size 32
[ 79.347302][ T66] The buggy address is located 0 bytes to the right of
[ 79.347302][ T66] allocated 32-byte region [ffff888135d50240, ffff888135d50260)
[ 79.361549][ T66]
[ 79.363733][ T66] The buggy address belongs to the physical page:
[ 79.369994][ T66] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x135d50
[ 79.378686][ T66] flags: 0x17ffffc0000000(node=0|zone=2|lastcpupid=0x1fffff)
[ 79.385905][ T66] page_type: f5(slab)
[ 79.389740][ T66] raw: 0017ffffc0000000 ffff88810c842780 dead000000000100 dead000000000122
[ 79.398171][ T66] raw: 0000000000000000 0000000800400040 00000000f5000000 0000000000000000
[ 79.406604][ T66] page dumped because: kasan: bad access detected
[ 79.412867][ T66]
[ 79.415052][ T66] Memory state around the buggy address:
[ 79.420534][ T66] ffff888135d50100: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
[ 79.428454][ T66] ffff888135d50180: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
[ 79.436375][ T66] >ffff888135d50200: fa fb fb fb fc fc fc fc 00 00 00 00 fc fc fc fc
[ 79.444285][ T66] ^
[ 79.451328][ T66] ffff888135d50280: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
[ 79.459237][ T66] ffff888135d50300: fa fb fb fb fc fc fc fc fa fb fb fb fc fc fc fc
[ 79.467146][ T66] ==================================================================
[ 79.475064][ T66] Disabling lock debugging due to kernel taint
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH 1/3] md: restore bitmap/location to fix wrong bitmap offset while growing
From: Glass Su @ 2026-03-15 8:56 UTC (permalink / raw)
To: yukuai; +Cc: Su Yue, linux-raid, song, xni, linan122, Heming Zhao
In-Reply-To: <04a3d0bf-838c-43b5-bb69-410829464042@fnnas.com>
[-- Attachment #1: Type: text/plain, Size: 5392 bytes --]
> On Mar 6, 2026, at 01:57, Yu Kuai <yukuai@fnnas.com> wrote:
>
> Hi,
>
> 在 2026/3/4 11:14, Su Yue 写道:
>> On Wed 04 Mar 2026 at 10:30, "Yu Kuai" <yukuai@fnnas.com> wrote:
>>
>>> Hi,
>>>
>>> 在 2026/3/3 11:37, Su Yue 写道:
>>>> Before commit fb8cc3b0d9db ("md/md-bitmap: delay registration of
>>>> bitmap_ops until creating bitmap")
>>>> if CONFIG_MD_BITMAP is enabled, both bitmap none, internal and
>>>> clustered have
>>>> the sysfs file bitmap/location.
>>>>
>>>> After the commit, if bitmap is none, bitmap/location doesn't exist
>>>> anymore.
>>>> It breaks 'grow' behavior of a md array of madam with
>>>> MD_FEATURE_BITMAP_OFFSET.
>>>> Take level=mirror and metadata=1.2 as an example:
>>>>
>>>> $ mdadm --create /dev/md0 -f --bitmap=none --raid-devices=2
>>>> --level=mirror \
>>>> --metadata=1.2 /dev/vdd /dev/vde
>>>> $ mdadm --grow /dev/md0 --bitmap=internal
>>>> $ cat /sys/block/md0/md/bitmap/location
>>>> Before:+8
>>>> After: +2
>>>>
>>>> While growing bitmap from none to internal, clustered and llbitmap,
>>>> mdadm/Grow.c:Grow_addbitmap() tries to detect bitmap/location first.
>>>> 1)If bitmap/location exists, it sets bitmap/location after
>>>> getinfo_super().
>>>> 2)If bitmap/location doesn't exist, mdadm just calls
>>>> md_set_array_info() then
>>>> mddev->bitmap_info.default_offset will be used.
>>>> Situation can be worse if growing none to clustered, bitmap offset
>>>> of the node
>>>> calling `madm --grow` will be changed but the other node are reading
>>>> bitmap sb from
>>>> the old location.
>>>
>>> Now that we have a new sysfs attribute bitmap_type, can we fix this by:
>>> - in the kernel, allow writing to this file in this case;
>>> - in mdadm and the grow case above, write to this file first, and change
>>> bitmap_type from none to bitmap(For llbitmap, there is still more
>>> work to do).
>>>
>> Yes. It's indeed feasible. But how about old versions mdadm? We can't
>> require
>> users' madadm + kernel combinations for old feature. Kernel part
>> should keep
>> compatibility with userspace. sysfs changes and broken haviros are not
>> ideal
>> especially userspace depends on it unless there's a strong reason.
>> That's why linux/Documentation/ABI exists.
>
> Okay, I can accept keep this old behavior.
>
> However, instead of introducing a new common_group with the same name "bitmap",
> I'll prefer to introducing a separate bitmap_ops for none bitmap as well, and
> you can define the attrs that are necessary.
>
After a try, the thing I realized is that a common group is unavoidable for bitmap and none bitmap.
mdadm writes to /sys/block/md0/md/bitmap/location, if remove_files() called by sysfs_remove_group()/
sysfs_update_group() on same kernfs node, recursive locks will be triggered:
[ 139.516750] ============================================
[ 139.517363] WARNING: possible recursive locking detected
[ 139.517953] 7.0.0-rc1-custom+ #282 Tainted: G OE
[ 139.518628] --------------------------------------------
[ 139.519233] mdadm/2346 is trying to acquire lock:
[ 139.519836] ffff8e6b24d85000 (kn->active#116){++++}-{0:0}, at: __kernfs_remove+0xd1/0x3e0
[ 139.520848]
but task is already holding lock:
[ 139.521561] ffff8e6b24d85000 (kn->active#116){++++}-{0:0}, at: kernfs_fop_write_iter+0x12d/0x250
[ 139.522603]
other info that might help us debug this:
[ 139.523383] Possible unsafe locking scenario:
[ 139.524169] CPU0
[ 139.524430] ----
[ 139.524682] lock(kn->active#116);
[ 139.525047] lock(kn->active#116);
[ 139.525398]
*** DEADLOCK ***
[ 139.525990] May be due to missing lock nesting notation
[ 139.526658] 4 locks held by mdadm/2346:
[ 139.527049] #0: ffff8e6acd5ec420 (sb_writers#5){.+.+}-{0:0}, at: ksys_write+0x6c/0xe0
[ 139.527838] #1: ffff8e6acd54fa88 (&of->mutex){+.+.}-{4:4}, at: kernfs_fop_write_iter+0x118/0x250
[ 139.528713] #2: ffff8e6b24d85000 (kn->active#116){++++}-{0:0}, at: kernfs_fop_write_iter+0x12d/0x250
[ 139.529594] #3: ffff8e6b26b51370 (&mddev->reconfig_mutex){+.+.}-{4:4}, at: location_store+0x6c/0x360 [md_mod]
[ 139.530535] dump_stack_lvl+0x68/0x90
[ 139.530540] print_deadlock_bug.cold+0xc0/0xcd
[ 139.530549] __lock_acquire+0x1324/0x2250
[ 139.530556] lock_acquire+0xc6/0x2f0
[ 139.530564] kernfs_drain+0x1eb/0x200
[ 139.530568] __kernfs_remove+0xd1/0x3e0
[ 139.530570] kernfs_remove_by_name_ns+0x5e/0xb0
[ 139.530572] internal_create_group+0x221/0x4d0
[ 139.530578] md_bitmap_create+0x122/0x130 [md_mod]
[ 139.530586] location_store+0x1e9/0x360 [md_mod]
[ 139.530594] md_attr_store+0xb8/0x1a0 [md_mod]
[ 139.530602] kernfs_fop_write_iter+0x176/0x250
[ 139.530605] vfs_write+0x21b/0x560
[ 139.530609] ksys_write+0x6c/0xe0
[ 139.530611] do_syscall_64+0x10f/0x5f0
[ 139.530623] entry_SYSCALL_64_after_hwframe+0x76/0x7e
The patch implemented by separated bitmap_ops is attached. This version looks pretty similar to the first
version because of the reason listed above and IMO ungraceful. Dummy ops and functions are a little unnecessary.
I would like to ask your opinion since you are the maintainer even though I prefer v1 version + (remove the location entry for llbitmap).
Thanks.
[-- Attachment #2: 000-md-add-dummy-bitmap-ops-for-none-to-fix-wrong-bitmap.patch --]
[-- Type: application/octet-stream, Size: 7408 bytes --]
From 1dd81c439b0097d0a9a5975682801ce75448e108 Mon Sep 17 00:00:00 2001
From: Su Yue <glass.su@suse.com>
Date: Sat, 7 Mar 2026 12:30:36 +0800
Subject: [PATCH 1/2] md: add dummy bitmap ops for none to fix wrong bitmap
offset
Before commit fb8cc3b0d9db ("md/md-bitmap: delay registration of bitmap_ops until creating bitmap")
if CONFIG_MD_BITMAP is enabled, both bitmap none, internal and clustered have
the sysfs file bitmap/location.
After the commit, if bitmap is none, bitmap/location doesn't exist anymore.
It breaks 'grow' behavior of a md array of madam with MD_FEATURE_BITMAP_OFFSET.
Take level=mirror and metadata=1.2 as an example:
$ mdadm --create /dev/md0 -f --bitmap=none --raid-devices=2 --level=mirror \
--metadata=1.2 /dev/vdd /dev/vde
$ mdadm --grow /dev/md0 --bitmap=internal
$ cat /sys/block/md0/md/bitmap/location
Before:+8
After: +2
While growing bitmap from none to internal, clustered and llbitmap,
mdadm/Grow.c:Grow_addbitmap() tries to detect bitmap/location first.
1)If bitmap/location exists, it sets bitmap/location after getinfo_super().
2)If bitmap/location doesn't exist, mdadm just calls md_set_array_info() then
mddev->bitmap_info.default_offset will be used.
Situation can be worse if growing none to clustered, bitmap offset of the node
calling `madm --grow` will be changed but the other node are reading bitmap sb from
the old location.
Here introducing a dummy bitmap_operations for ID_BITMAP_NONE to restore sysfs
file bitmap/location for ID_BITMAP_NONE and ID_BITMAP.
Fixes: fb8cc3b0d9db ("md/md-bitmap: delay registration of bitmap_ops until creating bitmap")
Signed-off-by: Su Yue <glass.su@suse.com>
---
drivers/md/md-bitmap.c | 48 ++++++++++++++++++++++++++++++++++++++++--
drivers/md/md.c | 46 ++++++++++++++++++++++++++++------------
2 files changed, 78 insertions(+), 16 deletions(-)
diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
index 83378c033c72..d5354cf35a65 100644
--- a/drivers/md/md-bitmap.c
+++ b/drivers/md/md-bitmap.c
@@ -240,6 +240,11 @@ static bool bitmap_enabled(void *data, bool flush)
bitmap->storage.filemap != NULL;
}
+static bool dummy_bitmap_enabled(void *data, bool flush)
+{
+ return false;
+}
+
/*
* check a page and, if necessary, allocate it (or hijack it if the alloc fails)
*
@@ -2201,6 +2206,11 @@ static int bitmap_create(struct mddev *mddev)
return 0;
}
+static int dummy_bitmap_create(struct mddev *mddev)
+{
+ return 0;
+}
+
static int bitmap_load(struct mddev *mddev)
{
int err = 0;
@@ -2956,7 +2966,7 @@ __ATTR(max_backlog_used, S_IRUGO | S_IWUSR,
behind_writes_used_show, behind_writes_used_reset);
static struct attribute *md_bitmap_attrs[] = {
- &bitmap_location.attr,
+// &bitmap_location.attr,
&bitmap_space.attr,
&bitmap_timeout.attr,
&bitmap_backlog.attr,
@@ -2972,6 +2982,17 @@ static struct attribute_group md_bitmap_group = {
.attrs = md_bitmap_attrs,
};
+/* Only necessary attrs for compatibility */
+static struct attribute *md_dummy_bitmap_attrs[] = {
+ &bitmap_location.attr,
+ NULL
+};
+
+static struct attribute_group md_dummy_bitmap_group = {
+ .name = "bitmap",
+ .attrs = md_dummy_bitmap_attrs,
+};
+
static struct bitmap_operations bitmap_ops = {
.head = {
.type = MD_BITMAP,
@@ -3016,18 +3037,41 @@ static struct bitmap_operations bitmap_ops = {
.group = &md_bitmap_group,
};
+static struct bitmap_operations dummy_bitmap_ops = {
+ .head = {
+ .type = MD_BITMAP,
+ .id = ID_BITMAP_NONE,
+ .name = "none",
+ },
+
+ .enabled = dummy_bitmap_enabled,
+ .create = dummy_bitmap_create,
+ .destroy = bitmap_destroy,
+ .load = bitmap_load,
+ .get_stats = bitmap_get_stats,
+ .free = md_bitmap_free,
+ .group = &md_dummy_bitmap_group,
+};
+
int md_bitmap_init(void)
{
+ int ret;
+
md_bitmap_wq = alloc_workqueue("md_bitmap", WQ_MEM_RECLAIM | WQ_UNBOUND,
0);
if (!md_bitmap_wq)
return -ENOMEM;
- return register_md_submodule(&bitmap_ops.head);
+ ret = register_md_submodule(&bitmap_ops.head);
+ if (ret)
+ return ret;
+
+ return register_md_submodule(&dummy_bitmap_ops.head);
}
void md_bitmap_exit(void)
{
destroy_workqueue(md_bitmap_wq);
unregister_md_submodule(&bitmap_ops.head);
+ unregister_md_submodule(&dummy_bitmap_ops.head);
}
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 3ce6f9e9d38e..94ded8efb725 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -682,9 +682,9 @@ static bool mddev_set_bitmap_ops(struct mddev *mddev)
{
struct bitmap_operations *old = mddev->bitmap_ops;
struct md_submodule_head *head;
+ int err = 0;
- if (mddev->bitmap_id == ID_BITMAP_NONE ||
- (old && old->head.id == mddev->bitmap_id))
+ if (old && old->head.id == mddev->bitmap_id)
return true;
xa_lock(&md_submodule);
@@ -704,13 +704,24 @@ static bool mddev_set_bitmap_ops(struct mddev *mddev)
xa_unlock(&md_submodule);
if (!mddev_is_dm(mddev) && mddev->bitmap_ops->group) {
- if (sysfs_create_group(&mddev->kobj, mddev->bitmap_ops->group))
+
+ if (old && old->head.id == ID_BITMAP_NONE) {
+ if (head->id == ID_BITMAP) {
+ err = sysfs_merge_group(&mddev->kobj, mddev->bitmap_ops->group);
+ } else if (head->id == ID_LLBITMAP) {
+ sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->group);
+ err = sysfs_create_group(&mddev->kobj, mddev->bitmap_ops->group);
+ }
+ } else {
+ err = sysfs_create_group(&mddev->kobj, mddev->bitmap_ops->group);
+ }
+
+ if (err)
pr_warn("md: cannot register extra bitmap attributes for %s\n",
mdname(mddev));
else
/*
- * Inform user with KOBJ_CHANGE about new bitmap
- * attributes.
+ * Inform user with KOBJ_CHANGE about bitmap attributes changes.
*/
kobject_uevent(&mddev->kobj, KOBJ_CHANGE);
}
@@ -723,11 +734,20 @@ static bool mddev_set_bitmap_ops(struct mddev *mddev)
static void mddev_clear_bitmap_ops(struct mddev *mddev)
{
- if (!mddev_is_dm(mddev) && mddev->bitmap_ops &&
- mddev->bitmap_ops->group)
- sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->group);
+ if (mddev_is_dm(mddev) || !mddev->bitmap_ops)
+ return;
- mddev->bitmap_ops = NULL;
+ if (mddev->bitmap_ops->head.id == ID_BITMAP_NONE)
+ return;
+
+ if (mddev->bitmap_ops->group)
+ sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->group);
+ /*
+ * group of ID_BITMAP_NONE is removed when new bitmap is
+ * creating in mddev_set_bitmap_ops().
+ */
+ mddev->bitmap_id = ID_BITMAP_NONE;
+ md_bitmap_create(mddev);
}
int mddev_init(struct mddev *mddev)
@@ -6449,9 +6469,6 @@ static int start_dirty_degraded;
static int md_bitmap_create(struct mddev *mddev)
{
- if (mddev->bitmap_id == ID_BITMAP_NONE)
- return -EINVAL;
-
if (!mddev_set_bitmap_ops(mddev))
return -ENOENT;
@@ -6610,8 +6627,9 @@ int md_run(struct mddev *mddev)
(unsigned long long)pers->size(mddev, 0, 0) / 2);
err = -EINVAL;
}
- if (err == 0 && pers->sync_request &&
- (mddev->bitmap_info.file || mddev->bitmap_info.offset)) {
+ if (err == 0 && pers->sync_request) {
+ if (mddev->bitmap_info.offset == 0)
+ mddev->bitmap_id = ID_BITMAP_NONE;
err = md_bitmap_create(mddev);
if (err)
pr_warn("%s: failed to create bitmap (%d)\n",
--
2.53.0
[-- Attachment #3: Type: text/plain, Size: 7309 bytes --]
—
Su
> For llbitmap, I think it's fine, we don't need this old sysfs attr anyway. I'll
> support to convert from none/bitmap to llbitmap by writing the new bitmap_type
> file.
>
>
>>
>> --
>> Su
>>
>>>>
>>>> Here restore sysfs file bitmap/location for ID_BITMAP_NONE and
>>>> ID_BITMAP.
>>>> And it d adds the entry for llbitmap too.
>>>>
>>>> New attribute_group md_bitmap_common_group is introduced and created in
>>>> md_alloc() as before commit fb8cc3b0d9db.
>>>> Add New operations register_group and unregister_group to struct
>>>> bitmap_operations.
>>>>
>>>> Fixes: fb8cc3b0d9db ("md/md-bitmap: delay registration of bitmap_ops
>>>> until creating bitmap")
>>>> Signed-off-by: Su Yue <glass.su@suse.com>
>>>> ---
>>>> drivers/md/md-bitmap.c | 32 +++++++++++++++++++++++++++++++-
>>>> drivers/md/md-bitmap.h | 5 +++++
>>>> drivers/md/md-llbitmap.c | 13 +++++++++++++
>>>> drivers/md/md.c | 16 ++++++++++++----
>>>> 4 files changed, 61 insertions(+), 5 deletions(-)
>>>>
>>>> diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
>>>> index 83378c033c72..8ff1dc94ed78 100644
>>>> --- a/drivers/md/md-bitmap.c
>>>> +++ b/drivers/md/md-bitmap.c
>>>> @@ -2956,7 +2956,6 @@ __ATTR(max_backlog_used, S_IRUGO | S_IWUSR,
>>>> behind_writes_used_show, behind_writes_used_reset);
>>>>
>>>> static struct attribute *md_bitmap_attrs[] = {
>>>> - &bitmap_location.attr,
>>>> &bitmap_space.attr,
>>>> &bitmap_timeout.attr,
>>>> &bitmap_backlog.attr,
>>>> @@ -2967,11 +2966,40 @@ static struct attribute *md_bitmap_attrs[] = {
>>>> NULL
>>>> };
>>>>
>>>> +static struct attribute *md_bitmap_common_attrs[] = {
>>>> + &bitmap_location.attr,
>>>> + NULL
>>>> +};
>>>> +
>>>> static struct attribute_group md_bitmap_group = {
>>>> .name = "bitmap",
>>>> .attrs = md_bitmap_attrs,
>>>> };
>>>>
>>>> +static struct attribute_group md_bitmap_common_group = {
>>>> + .name = "bitmap",
>>>> + .attrs = md_bitmap_common_attrs,
>>>> +};
>>>> +
>>>> +int md_sysfs_create_common_group(struct mddev *mddev)
>>>> +{
>>>> + return sysfs_create_group(&mddev->kobj, &md_bitmap_common_group);
>>>> +}
>>>> +
>>>> +static int bitmap_register_group(struct mddev *mddev)
>>>> +{
>>>> + /*
>>>> + * md_bitmap_group and md_bitmap_common_group are using same name
>>>> + * 'bitmap'.
>>>> + */
>>>> + return sysfs_merge_group(&mddev->kobj, &md_bitmap_group);
>>>> +}
>>>> +
>>>> +static void bitmap_unregister_group(struct mddev *mddev)
>>>> +{
>>>> + sysfs_unmerge_group(&mddev->kobj, &md_bitmap_group);
>>>> +}
>>>> +
>>>> static struct bitmap_operations bitmap_ops = {
>>>> .head = {
>>>> .type = MD_BITMAP,
>>>> @@ -3013,6 +3041,8 @@ static struct bitmap_operations bitmap_ops = {
>>>> .set_pages = bitmap_set_pages,
>>>> .free = md_bitmap_free,
>>>>
>>>> + .register_group = bitmap_register_group,
>>>> + .unregister_group = bitmap_unregister_group,
>>>> .group = &md_bitmap_group,
>>>> };
>>>>
>>>> diff --git a/drivers/md/md-bitmap.h b/drivers/md/md-bitmap.h
>>>> index b42a28fa83a0..371791e9011d 100644
>>>> --- a/drivers/md/md-bitmap.h
>>>> +++ b/drivers/md/md-bitmap.h
>>>> @@ -125,6 +125,9 @@ struct bitmap_operations {
>>>> void (*set_pages)(void *data, unsigned long pages);
>>>> void (*free)(void *data);
>>>>
>>>> + int (*register_group)(struct mddev *mddev);
>>>> + void (*unregister_group)(struct mddev *mddev);
>>>> +
>>>> struct attribute_group *group;
>>>> };
>>>>
>>>> @@ -169,6 +172,8 @@ static inline void md_bitmap_end_sync(struct
>>>> mddev *mddev, sector_t offset,
>>>> mddev->bitmap_ops->end_sync(mddev, offset, blocks);
>>>> }
>>>>
>>>> +int md_sysfs_create_common_group(struct mddev *mddev);
>>>> +
>>>> #ifdef CONFIG_MD_BITMAP
>>>> int md_bitmap_init(void);
>>>> void md_bitmap_exit(void);
>>>> diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
>>>> index bf398d7476b3..24ff5f7f8751 100644
>>>> --- a/drivers/md/md-llbitmap.c
>>>> +++ b/drivers/md/md-llbitmap.c
>>>> @@ -1561,6 +1561,16 @@ static struct attribute_group
>>>> md_llbitmap_group = {
>>>> .attrs = md_llbitmap_attrs,
>>>> };
>>>>
>>>> +static int llbitmap_register_group(struct mddev *mddev)
>>>> +{
>>>> + return sysfs_create_group(&mddev->kobj, &md_llbitmap_group);
>>>> +}
>>>> +
>>>> +static void llbitmap_unregister_group(struct mddev *mddev)
>>>> +{
>>>> + sysfs_remove_group(&mddev->kobj, &md_llbitmap_group);
>>>> +}
>>>> +
>>>> static struct bitmap_operations llbitmap_ops = {
>>>> .head = {
>>>> .type = MD_BITMAP,
>>>> @@ -1597,6 +1607,9 @@ static struct bitmap_operations llbitmap_ops = {
>>>> .dirty_bits = llbitmap_dirty_bits,
>>>> .write_all = llbitmap_write_all,
>>>>
>>>> + .register_group = llbitmap_register_group,
>>>> + .unregister_group = llbitmap_unregister_group,
>>>> +
>>>> .group = &md_llbitmap_group,
>>>> };
>>>>
>>>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>>>> index 3ce6f9e9d38e..ab969e950ea8 100644
>>>> --- a/drivers/md/md.c
>>>> +++ b/drivers/md/md.c
>>>> @@ -703,8 +703,8 @@ static bool mddev_set_bitmap_ops(struct mddev
>>>> *mddev)
>>>> mddev->bitmap_ops = (void *)head;
>>>> xa_unlock(&md_submodule);
>>>>
>>>> - if (!mddev_is_dm(mddev) && mddev->bitmap_ops->group) {
>>>> - if (sysfs_create_group(&mddev->kobj,
>>>> mddev->bitmap_ops->group))
>>>> + if (!mddev_is_dm(mddev) && mddev->bitmap_ops->register_group) {
>>>> + if (mddev->bitmap_ops->register_group(mddev))
>>>> pr_warn("md: cannot register extra bitmap attributes
>>>> for %s\n",
>>>> mdname(mddev));
>>>> else
>>>> @@ -724,8 +724,8 @@ static bool mddev_set_bitmap_ops(struct mddev
>>>> *mddev)
>>>> static void mddev_clear_bitmap_ops(struct mddev *mddev)
>>>> {
>>>> if (!mddev_is_dm(mddev) && mddev->bitmap_ops &&
>>>> - mddev->bitmap_ops->group)
>>>> - sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->group);
>>>> + mddev->bitmap_ops->unregister_group)
>>>> + mddev->bitmap_ops->unregister_group(mddev);
>>>>
>>>> mddev->bitmap_ops = NULL;
>>>> }
>>>> @@ -6369,6 +6369,14 @@ struct mddev *md_alloc(dev_t dev, char *name)
>>>> return ERR_PTR(error);
>>>> }
>>>>
>>>> + /*
>>>> + * md_sysfs_remove_common_group is not needed because
>>>> mddev_delayed_delete
>>>> + * calls kobject_put(&mddev->kobj) if mddev is to be deleted.
>>>> + */
>>>> + if (md_sysfs_create_common_group(mddev))
>>>> + pr_warn("md: cannot register common bitmap attributes for
>>>> %s\n",
>>>> + mdname(mddev));
>>>> +
>>>> kobject_uevent(&mddev->kobj, KOBJ_ADD);
>>>> mddev->sysfs_state = sysfs_get_dirent_safe(mddev->kobj.sd,
>>>> "array_state");
>>>> mddev->sysfs_level = sysfs_get_dirent_safe(mddev->kobj.sd,
>>>> "level");
>
> --
> Thansk,
> Kuai
^ permalink raw reply related
* Re: [PATCH v2 2/5] md/md-llbitmap: raise barrier before state machine transition
From: Yu Kuai @ 2026-03-15 16:57 UTC (permalink / raw)
To: Xiao Ni, yukuai; +Cc: song, linan122, colyli, linux-raid, linux-kernel
In-Reply-To: <CALTww2-oZrNS8hj+ZnBYmfxOrE6gjwqpvkWex_XCupHPCpyLBw@mail.gmail.com>
Hi,
在 2026/3/9 21:05, Xiao Ni 写道:
> On Mon, Feb 23, 2026 at 10:42 AM Yu Kuai <yukuai@fnnas.com> wrote:
>> Move the barrier raise operation before calling llbitmap_state_machine()
>> in both llbitmap_start_write() and llbitmap_start_discard(). This
>> ensures the barrier is in place before any state transitions occur,
>> preventing potential race conditions where the state machine could
>> complete before the barrier is properly raised.
> Hi Kuai
>
> In the above commit message, race conditions are mentioned. I want to
> give an example here to check if I understand correctly.
>
> raid1 with 2 disks is used in this case.
> T0: Thread A calls llbitmap_state_machine() and state of bit is set to BitDirty
> T1: Thread daemon calls llbitmap_daemon_work() and the state of the
> bit is set to BitClean from BitDirty.
> (Now the state is already wrong)
> T2: Thread A calls llbitmap_raise_barrier to wait Thread daemon
> finishes and go on working
> T3: data is written to disk1 and disk2. A power failure occurs.
> (The data on disk1 and disk2 maybe different, so a data curruption happens)
>
> Does this case belong the race conditions you mentioned in the commit
> message? Can you add one race condtion in your message?
This is not that complex, the reason state machine from fast path can concurrent
with each other is that they both change state to the same state, and obliviously
they can't concurrent from slow path, this is why the barrier is needed. And I
made this mistake in the first version.
>
> Best Regards
> Xiao
>
>> Cc: stable@vger.kernel.org
>> Fixes: 5ab829f1971d ("md/md-llbitmap: introduce new lockless bitmap")
>> Signed-off-by: Yu Kuai <yukuai@fnnas.com>
>> ---
>> drivers/md/md-llbitmap.c | 8 ++++----
>> 1 file changed, 4 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
>> index 30d7e36b22c4..5f9e7004e3e3 100644
>> --- a/drivers/md/md-llbitmap.c
>> +++ b/drivers/md/md-llbitmap.c
>> @@ -1070,12 +1070,12 @@ static void llbitmap_start_write(struct mddev *mddev, sector_t offset,
>> int page_start = (start + BITMAP_DATA_OFFSET) >> PAGE_SHIFT;
>> int page_end = (end + BITMAP_DATA_OFFSET) >> PAGE_SHIFT;
>>
>> - llbitmap_state_machine(llbitmap, start, end, BitmapActionStartwrite);
>> -
>> while (page_start <= page_end) {
>> llbitmap_raise_barrier(llbitmap, page_start);
>> page_start++;
>> }
>> +
>> + llbitmap_state_machine(llbitmap, start, end, BitmapActionStartwrite);
>> }
>>
>> static void llbitmap_end_write(struct mddev *mddev, sector_t offset,
>> @@ -1102,12 +1102,12 @@ static void llbitmap_start_discard(struct mddev *mddev, sector_t offset,
>> int page_start = (start + BITMAP_DATA_OFFSET) >> PAGE_SHIFT;
>> int page_end = (end + BITMAP_DATA_OFFSET) >> PAGE_SHIFT;
>>
>> - llbitmap_state_machine(llbitmap, start, end, BitmapActionDiscard);
>> -
>> while (page_start <= page_end) {
>> llbitmap_raise_barrier(llbitmap, page_start);
>> page_start++;
>> }
>> +
>> + llbitmap_state_machine(llbitmap, start, end, BitmapActionDiscard);
>> }
>>
>> static void llbitmap_end_discard(struct mddev *mddev, sector_t offset,
>> --
>> 2.51.0
>>
>>
--
Thansk,
Kuai
^ permalink raw reply
* Re: [PATCH v2 3/5] md: add fallback to correct bitmap_ops on version mismatch
From: Yu Kuai @ 2026-03-15 17:01 UTC (permalink / raw)
To: Xiao Ni, yukuai; +Cc: song, linan122, colyli, linux-raid, linux-kernel
In-Reply-To: <CALTww28rtki9J=_GB1zf4N5FBcdz+vzSvB1bNb-O2-AgrUzgSA@mail.gmail.com>
Hi,
在 2026/3/10 9:06, Xiao Ni 写道:
> On Mon, Feb 23, 2026 at 10:43 AM Yu Kuai <yukuai@fnnas.com> wrote:
>> If default bitmap version and on-disk version doesn't match, and mdadm
>> is not the latest version to set bitmap_type, set bitmap_ops based on
>> the disk version.
> Hi Kuai
>
> How can I do test to check if this patch works?
>
> 1. Create array with llbitmap
> 2. Stop the array
> 3. uninstall mdadm with llbitmap support and install mdadm without llbitmap
> 4. assemble the array
At first, my colleague met this problem during power off and reboot test, however
I can't reproduce this, after adding some debug info, I'm quite sure this is still
an mdadm issue, that somewhere write llbitmap to sysfs bitmap_type file is missing.
Also, this way can be check as well if this patch works.
>
> Is it the case you want to fix?
>
> Regards
> Xiao
>> Signed-off-by: Yu Kuai <yukuai@fnnas.com>
>> ---
>> drivers/md/md.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++-
>> 1 file changed, 110 insertions(+), 1 deletion(-)
>>
>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>> index 72a1c7267851..245785ad0ffd 100644
>> --- a/drivers/md/md.c
>> +++ b/drivers/md/md.c
>> @@ -6447,15 +6447,124 @@ static void md_safemode_timeout(struct timer_list *t)
>>
>> static int start_dirty_degraded;
>>
>> +/*
>> + * Read bitmap superblock and return the bitmap_id based on disk version.
>> + * This is used as fallback when default bitmap version and on-disk version
>> + * doesn't match, and mdadm is not the latest version to set bitmap_type.
>> + */
>> +static enum md_submodule_id md_bitmap_get_id_from_sb(struct mddev *mddev)
>> +{
>> + struct md_rdev *rdev;
>> + struct page *sb_page;
>> + bitmap_super_t *sb;
>> + enum md_submodule_id id = ID_BITMAP_NONE;
>> + sector_t sector;
>> + u32 version;
>> +
>> + if (!mddev->bitmap_info.offset)
>> + return ID_BITMAP_NONE;
>> +
>> + sb_page = alloc_page(GFP_KERNEL);
>> + if (!sb_page) {
>> + pr_warn("md: %s: failed to allocate memory for bitmap\n",
>> + mdname(mddev));
>> + return ID_BITMAP_NONE;
>> + }
>> +
>> + sector = mddev->bitmap_info.offset;
>> +
>> + rdev_for_each(rdev, mddev) {
>> + u32 iosize;
>> +
>> + if (!test_bit(In_sync, &rdev->flags) ||
>> + test_bit(Faulty, &rdev->flags) ||
>> + test_bit(Bitmap_sync, &rdev->flags))
>> + continue;
>> +
>> + iosize = roundup(sizeof(bitmap_super_t),
>> + bdev_logical_block_size(rdev->bdev));
>> + if (sync_page_io(rdev, sector, iosize, sb_page, REQ_OP_READ,
>> + true))
>> + goto read_ok;
>> + }
>> + pr_warn("md: %s: failed to read bitmap from any device\n",
>> + mdname(mddev));
>> + goto out;
>> +
>> +read_ok:
>> + sb = kmap_local_page(sb_page);
>> + if (sb->magic != cpu_to_le32(BITMAP_MAGIC)) {
>> + pr_warn("md: %s: invalid bitmap magic 0x%x\n",
>> + mdname(mddev), le32_to_cpu(sb->magic));
>> + goto out_unmap;
>> + }
>> +
>> + version = le32_to_cpu(sb->version);
>> + switch (version) {
>> + case BITMAP_MAJOR_LO:
>> + case BITMAP_MAJOR_HI:
>> + case BITMAP_MAJOR_CLUSTERED:
>> + id = ID_BITMAP;
>> + break;
>> + case BITMAP_MAJOR_LOCKLESS:
>> + id = ID_LLBITMAP;
>> + break;
>> + default:
>> + pr_warn("md: %s: unknown bitmap version %u\n",
>> + mdname(mddev), version);
>> + break;
>> + }
>> +
>> +out_unmap:
>> + kunmap_local(sb);
>> +out:
>> + __free_page(sb_page);
>> + return id;
>> +}
>> +
>> static int md_bitmap_create(struct mddev *mddev)
>> {
>> + enum md_submodule_id orig_id = mddev->bitmap_id;
>> + enum md_submodule_id sb_id;
>> + int err;
>> +
>> if (mddev->bitmap_id == ID_BITMAP_NONE)
>> return -EINVAL;
>>
>> if (!mddev_set_bitmap_ops(mddev))
>> return -ENOENT;
>>
>> - return mddev->bitmap_ops->create(mddev);
>> + err = mddev->bitmap_ops->create(mddev);
>> + if (!err)
>> + return 0;
>> +
>> + /*
>> + * Create failed, if default bitmap version and on-disk version
>> + * doesn't match, and mdadm is not the latest version to set
>> + * bitmap_type, set bitmap_ops based on the disk version.
>> + */
>> + mddev_clear_bitmap_ops(mddev);
>> +
>> + sb_id = md_bitmap_get_id_from_sb(mddev);
>> + if (sb_id == ID_BITMAP_NONE || sb_id == orig_id)
>> + return err;
>> +
>> + pr_info("md: %s: bitmap version mismatch, switching from %d to %d\n",
>> + mdname(mddev), orig_id, sb_id);
>> +
>> + mddev->bitmap_id = sb_id;
>> + if (!mddev_set_bitmap_ops(mddev)) {
>> + mddev->bitmap_id = orig_id;
>> + return -ENOENT;
>> + }
>> +
>> + err = mddev->bitmap_ops->create(mddev);
>> + if (err) {
>> + mddev_clear_bitmap_ops(mddev);
>> + mddev->bitmap_id = orig_id;
>> + }
>> +
>> + return err;
>> }
>>
>> static void md_bitmap_destroy(struct mddev *mddev)
>> --
>> 2.51.0
>>
>>
--
Thansk,
Kuai
^ permalink raw reply
* Re: md-raid support 50/60 & spare space support
From: Yu Kuai @ 2026-03-15 17:11 UTC (permalink / raw)
To: Anton Gavriliuk, linux-raid, yukuai
In-Reply-To: <CAAiJnjrPXEdgt5U5=13e62z4Vw=UzPaSZO8NpoqAoNV2+KQSTQ@mail.gmail.com>
Hi,
在 2026/3/6 19:17, Anton Gavriliuk 写道:
> Hi
>
> Are there any plans to add support for md-raid 50/60 ?
> Maximum recommended number of disks for single raid5 shouldn't exceed 8.
> So raid 50/60 would be very useful for systems with 64-96 disks.
We're probably won't invent a new raid 50/60. BTW we're working on performance
improvement for large raid5 arrays for at most 64 disks, results looks great.
However, it might take sometime before we push our work to upstream.
>
> And also any plans for spare space support (instead of spare disk) ?
Can you explain more about spare space?
>
> Anton
>
--
Thansk,
Kuai
^ permalink raw reply
* Re: [PATCH 1/3] md: restore bitmap/location to fix wrong bitmap offset while growing
From: Yu Kuai @ 2026-03-15 18:12 UTC (permalink / raw)
To: Glass Su, yukuai; +Cc: Su Yue, linux-raid, song, xni, linan122, Heming Zhao
In-Reply-To: <94F22BD7-A521-496B-AAAE-50DD825A4353@suse.com>
Hi,
在 2026/3/15 16:56, Glass Su 写道:
>
>> On Mar 6, 2026, at 01:57, Yu Kuai <yukuai@fnnas.com> wrote:
>>
>> Hi,
>>
>> 在 2026/3/4 11:14, Su Yue 写道:
>>> On Wed 04 Mar 2026 at 10:30, "Yu Kuai" <yukuai@fnnas.com> wrote:
>>>
>>>> Hi,
>>>>
>>>> 在 2026/3/3 11:37, Su Yue 写道:
>>>>> Before commit fb8cc3b0d9db ("md/md-bitmap: delay registration of
>>>>> bitmap_ops until creating bitmap")
>>>>> if CONFIG_MD_BITMAP is enabled, both bitmap none, internal and
>>>>> clustered have
>>>>> the sysfs file bitmap/location.
>>>>>
>>>>> After the commit, if bitmap is none, bitmap/location doesn't exist
>>>>> anymore.
>>>>> It breaks 'grow' behavior of a md array of madam with
>>>>> MD_FEATURE_BITMAP_OFFSET.
>>>>> Take level=mirror and metadata=1.2 as an example:
>>>>>
>>>>> $ mdadm --create /dev/md0 -f --bitmap=none --raid-devices=2
>>>>> --level=mirror \
>>>>> --metadata=1.2 /dev/vdd /dev/vde
>>>>> $ mdadm --grow /dev/md0 --bitmap=internal
>>>>> $ cat /sys/block/md0/md/bitmap/location
>>>>> Before:+8
>>>>> After: +2
>>>>>
>>>>> While growing bitmap from none to internal, clustered and llbitmap,
>>>>> mdadm/Grow.c:Grow_addbitmap() tries to detect bitmap/location first.
>>>>> 1)If bitmap/location exists, it sets bitmap/location after
>>>>> getinfo_super().
>>>>> 2)If bitmap/location doesn't exist, mdadm just calls
>>>>> md_set_array_info() then
>>>>> mddev->bitmap_info.default_offset will be used.
>>>>> Situation can be worse if growing none to clustered, bitmap offset
>>>>> of the node
>>>>> calling `madm --grow` will be changed but the other node are reading
>>>>> bitmap sb from
>>>>> the old location.
>>>> Now that we have a new sysfs attribute bitmap_type, can we fix this by:
>>>> - in the kernel, allow writing to this file in this case;
>>>> - in mdadm and the grow case above, write to this file first, and change
>>>> bitmap_type from none to bitmap(For llbitmap, there is still more
>>>> work to do).
>>>>
>>> Yes. It's indeed feasible. But how about old versions mdadm? We can't
>>> require
>>> users' madadm + kernel combinations for old feature. Kernel part
>>> should keep
>>> compatibility with userspace. sysfs changes and broken haviros are not
>>> ideal
>>> especially userspace depends on it unless there's a strong reason.
>>> That's why linux/Documentation/ABI exists.
>> Okay, I can accept keep this old behavior.
>>
>> However, instead of introducing a new common_group with the same name "bitmap",
>> I'll prefer to introducing a separate bitmap_ops for none bitmap as well, and
>> you can define the attrs that are necessary.
>>
> After a try, the thing I realized is that a common group is unavoidable for bitmap and none bitmap.
>
> mdadm writes to /sys/block/md0/md/bitmap/location, if remove_files() called by sysfs_remove_group()/
> sysfs_update_group() on same kernfs node, recursive locks will be triggered:
>
> [ 139.516750] ============================================
> [ 139.517363] WARNING: possible recursive locking detected
> [ 139.517953] 7.0.0-rc1-custom+ #282 Tainted: G OE
> [ 139.518628] --------------------------------------------
> [ 139.519233] mdadm/2346 is trying to acquire lock:
> [ 139.519836] ffff8e6b24d85000 (kn->active#116){++++}-{0:0}, at: __kernfs_remove+0xd1/0x3e0
> [ 139.520848]
> but task is already holding lock:
> [ 139.521561] ffff8e6b24d85000 (kn->active#116){++++}-{0:0}, at: kernfs_fop_write_iter+0x12d/0x250
> [ 139.522603]
> other info that might help us debug this:
> [ 139.523383] Possible unsafe locking scenario:
>
> [ 139.524169] CPU0
> [ 139.524430] ----
> [ 139.524682] lock(kn->active#116);
> [ 139.525047] lock(kn->active#116);
> [ 139.525398]
> *** DEADLOCK ***
>
> [ 139.525990] May be due to missing lock nesting notation
>
> [ 139.526658] 4 locks held by mdadm/2346:
> [ 139.527049] #0: ffff8e6acd5ec420 (sb_writers#5){.+.+}-{0:0}, at: ksys_write+0x6c/0xe0
> [ 139.527838] #1: ffff8e6acd54fa88 (&of->mutex){+.+.}-{4:4}, at: kernfs_fop_write_iter+0x118/0x250
> [ 139.528713] #2: ffff8e6b24d85000 (kn->active#116){++++}-{0:0}, at: kernfs_fop_write_iter+0x12d/0x250
> [ 139.529594] #3: ffff8e6b26b51370 (&mddev->reconfig_mutex){+.+.}-{4:4}, at: location_store+0x6c/0x360 [md_mod]
>
> [ 139.530535] dump_stack_lvl+0x68/0x90
> [ 139.530540] print_deadlock_bug.cold+0xc0/0xcd
> [ 139.530549] __lock_acquire+0x1324/0x2250
> [ 139.530556] lock_acquire+0xc6/0x2f0
> [ 139.530564] kernfs_drain+0x1eb/0x200
> [ 139.530568] __kernfs_remove+0xd1/0x3e0
> [ 139.530570] kernfs_remove_by_name_ns+0x5e/0xb0
> [ 139.530572] internal_create_group+0x221/0x4d0
> [ 139.530578] md_bitmap_create+0x122/0x130 [md_mod]
> [ 139.530586] location_store+0x1e9/0x360 [md_mod]
> [ 139.530594] md_attr_store+0xb8/0x1a0 [md_mod]
> [ 139.530602] kernfs_fop_write_iter+0x176/0x250
> [ 139.530605] vfs_write+0x21b/0x560
> [ 139.530609] ksys_write+0x6c/0xe0
> [ 139.530611] do_syscall_64+0x10f/0x5f0
> [ 139.530623] entry_SYSCALL_64_after_hwframe+0x76/0x7e
>
> The patch implemented by separated bitmap_ops is attached. This version looks pretty similar to the first
> version because of the reason listed above and IMO ungraceful. Dummy ops and functions are a little unnecessary.
>
> I would like to ask your opinion since you are the maintainer even though I prefer v1 version + (remove the location entry for llbitmap).
Hi, I don't think you'll need to remove and recreate sysfs entry here, looks like
this problem is introduced by patch 2?
1) split bitmap group into a common group that contain location attr; and a internal
bitmap group(the name is NULL), for example:
struct attribute_group *none_bitmap_group[] = {
&common_bitmap_group,
NULL
};
struct attribute_group *internal_bitmap_group[] = {
&common_bitmap_group,
&internal_bitmap_group,
NULL,
};
2) create none bitmap with common group only, and create internal bitmap with common group and
internal bitmap group; Notice we should convert to user sysfs_create_groups().
3) while growing from none to bitmap, create internal bitmap group attrs
4) while growing from bitmap to none, remove internal bitmap group attrs
> Thanks.
>
>
>
> —
> Su
>
>
>
>> For llbitmap, I think it's fine, we don't need this old sysfs attr anyway. I'll
>> support to convert from none/bitmap to llbitmap by writing the new bitmap_type
>> file.
>>
>>
>>> --
>>> Su
>>>
>>>>> Here restore sysfs file bitmap/location for ID_BITMAP_NONE and
>>>>> ID_BITMAP.
>>>>> And it d adds the entry for llbitmap too.
>>>>>
>>>>> New attribute_group md_bitmap_common_group is introduced and created in
>>>>> md_alloc() as before commit fb8cc3b0d9db.
>>>>> Add New operations register_group and unregister_group to struct
>>>>> bitmap_operations.
>>>>>
>>>>> Fixes: fb8cc3b0d9db ("md/md-bitmap: delay registration of bitmap_ops
>>>>> until creating bitmap")
>>>>> Signed-off-by: Su Yue <glass.su@suse.com>
>>>>> ---
>>>>> drivers/md/md-bitmap.c | 32 +++++++++++++++++++++++++++++++-
>>>>> drivers/md/md-bitmap.h | 5 +++++
>>>>> drivers/md/md-llbitmap.c | 13 +++++++++++++
>>>>> drivers/md/md.c | 16 ++++++++++++----
>>>>> 4 files changed, 61 insertions(+), 5 deletions(-)
>>>>>
>>>>> diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
>>>>> index 83378c033c72..8ff1dc94ed78 100644
>>>>> --- a/drivers/md/md-bitmap.c
>>>>> +++ b/drivers/md/md-bitmap.c
>>>>> @@ -2956,7 +2956,6 @@ __ATTR(max_backlog_used, S_IRUGO | S_IWUSR,
>>>>> behind_writes_used_show, behind_writes_used_reset);
>>>>>
>>>>> static struct attribute *md_bitmap_attrs[] = {
>>>>> - &bitmap_location.attr,
>>>>> &bitmap_space.attr,
>>>>> &bitmap_timeout.attr,
>>>>> &bitmap_backlog.attr,
>>>>> @@ -2967,11 +2966,40 @@ static struct attribute *md_bitmap_attrs[] = {
>>>>> NULL
>>>>> };
>>>>>
>>>>> +static struct attribute *md_bitmap_common_attrs[] = {
>>>>> + &bitmap_location.attr,
>>>>> + NULL
>>>>> +};
>>>>> +
>>>>> static struct attribute_group md_bitmap_group = {
>>>>> .name = "bitmap",
>>>>> .attrs = md_bitmap_attrs,
>>>>> };
>>>>>
>>>>> +static struct attribute_group md_bitmap_common_group = {
>>>>> + .name = "bitmap",
>>>>> + .attrs = md_bitmap_common_attrs,
>>>>> +};
>>>>> +
>>>>> +int md_sysfs_create_common_group(struct mddev *mddev)
>>>>> +{
>>>>> + return sysfs_create_group(&mddev->kobj, &md_bitmap_common_group);
>>>>> +}
>>>>> +
>>>>> +static int bitmap_register_group(struct mddev *mddev)
>>>>> +{
>>>>> + /*
>>>>> + * md_bitmap_group and md_bitmap_common_group are using same name
>>>>> + * 'bitmap'.
>>>>> + */
>>>>> + return sysfs_merge_group(&mddev->kobj, &md_bitmap_group);
>>>>> +}
>>>>> +
>>>>> +static void bitmap_unregister_group(struct mddev *mddev)
>>>>> +{
>>>>> + sysfs_unmerge_group(&mddev->kobj, &md_bitmap_group);
>>>>> +}
>>>>> +
>>>>> static struct bitmap_operations bitmap_ops = {
>>>>> .head = {
>>>>> .type = MD_BITMAP,
>>>>> @@ -3013,6 +3041,8 @@ static struct bitmap_operations bitmap_ops = {
>>>>> .set_pages = bitmap_set_pages,
>>>>> .free = md_bitmap_free,
>>>>>
>>>>> + .register_group = bitmap_register_group,
>>>>> + .unregister_group = bitmap_unregister_group,
>>>>> .group = &md_bitmap_group,
>>>>> };
>>>>>
>>>>> diff --git a/drivers/md/md-bitmap.h b/drivers/md/md-bitmap.h
>>>>> index b42a28fa83a0..371791e9011d 100644
>>>>> --- a/drivers/md/md-bitmap.h
>>>>> +++ b/drivers/md/md-bitmap.h
>>>>> @@ -125,6 +125,9 @@ struct bitmap_operations {
>>>>> void (*set_pages)(void *data, unsigned long pages);
>>>>> void (*free)(void *data);
>>>>>
>>>>> + int (*register_group)(struct mddev *mddev);
>>>>> + void (*unregister_group)(struct mddev *mddev);
>>>>> +
>>>>> struct attribute_group *group;
>>>>> };
>>>>>
>>>>> @@ -169,6 +172,8 @@ static inline void md_bitmap_end_sync(struct
>>>>> mddev *mddev, sector_t offset,
>>>>> mddev->bitmap_ops->end_sync(mddev, offset, blocks);
>>>>> }
>>>>>
>>>>> +int md_sysfs_create_common_group(struct mddev *mddev);
>>>>> +
>>>>> #ifdef CONFIG_MD_BITMAP
>>>>> int md_bitmap_init(void);
>>>>> void md_bitmap_exit(void);
>>>>> diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
>>>>> index bf398d7476b3..24ff5f7f8751 100644
>>>>> --- a/drivers/md/md-llbitmap.c
>>>>> +++ b/drivers/md/md-llbitmap.c
>>>>> @@ -1561,6 +1561,16 @@ static struct attribute_group
>>>>> md_llbitmap_group = {
>>>>> .attrs = md_llbitmap_attrs,
>>>>> };
>>>>>
>>>>> +static int llbitmap_register_group(struct mddev *mddev)
>>>>> +{
>>>>> + return sysfs_create_group(&mddev->kobj, &md_llbitmap_group);
>>>>> +}
>>>>> +
>>>>> +static void llbitmap_unregister_group(struct mddev *mddev)
>>>>> +{
>>>>> + sysfs_remove_group(&mddev->kobj, &md_llbitmap_group);
>>>>> +}
>>>>> +
>>>>> static struct bitmap_operations llbitmap_ops = {
>>>>> .head = {
>>>>> .type = MD_BITMAP,
>>>>> @@ -1597,6 +1607,9 @@ static struct bitmap_operations llbitmap_ops = {
>>>>> .dirty_bits = llbitmap_dirty_bits,
>>>>> .write_all = llbitmap_write_all,
>>>>>
>>>>> + .register_group = llbitmap_register_group,
>>>>> + .unregister_group = llbitmap_unregister_group,
>>>>> +
>>>>> .group = &md_llbitmap_group,
>>>>> };
>>>>>
>>>>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>>>>> index 3ce6f9e9d38e..ab969e950ea8 100644
>>>>> --- a/drivers/md/md.c
>>>>> +++ b/drivers/md/md.c
>>>>> @@ -703,8 +703,8 @@ static bool mddev_set_bitmap_ops(struct mddev
>>>>> *mddev)
>>>>> mddev->bitmap_ops = (void *)head;
>>>>> xa_unlock(&md_submodule);
>>>>>
>>>>> - if (!mddev_is_dm(mddev) && mddev->bitmap_ops->group) {
>>>>> - if (sysfs_create_group(&mddev->kobj,
>>>>> mddev->bitmap_ops->group))
>>>>> + if (!mddev_is_dm(mddev) && mddev->bitmap_ops->register_group) {
>>>>> + if (mddev->bitmap_ops->register_group(mddev))
>>>>> pr_warn("md: cannot register extra bitmap attributes
>>>>> for %s\n",
>>>>> mdname(mddev));
>>>>> else
>>>>> @@ -724,8 +724,8 @@ static bool mddev_set_bitmap_ops(struct mddev
>>>>> *mddev)
>>>>> static void mddev_clear_bitmap_ops(struct mddev *mddev)
>>>>> {
>>>>> if (!mddev_is_dm(mddev) && mddev->bitmap_ops &&
>>>>> - mddev->bitmap_ops->group)
>>>>> - sysfs_remove_group(&mddev->kobj, mddev->bitmap_ops->group);
>>>>> + mddev->bitmap_ops->unregister_group)
>>>>> + mddev->bitmap_ops->unregister_group(mddev);
>>>>>
>>>>> mddev->bitmap_ops = NULL;
>>>>> }
>>>>> @@ -6369,6 +6369,14 @@ struct mddev *md_alloc(dev_t dev, char *name)
>>>>> return ERR_PTR(error);
>>>>> }
>>>>>
>>>>> + /*
>>>>> + * md_sysfs_remove_common_group is not needed because
>>>>> mddev_delayed_delete
>>>>> + * calls kobject_put(&mddev->kobj) if mddev is to be deleted.
>>>>> + */
>>>>> + if (md_sysfs_create_common_group(mddev))
>>>>> + pr_warn("md: cannot register common bitmap attributes for
>>>>> %s\n",
>>>>> + mdname(mddev));
>>>>> +
>>>>> kobject_uevent(&mddev->kobj, KOBJ_ADD);
>>>>> mddev->sysfs_state = sysfs_get_dirent_safe(mddev->kobj.sd,
>>>>> "array_state");
>>>>> mddev->sysfs_level = sysfs_get_dirent_safe(mddev->kobj.sd,
>>>>> "level");
>> --
>> Thansk,
>> Kuai
>
--
Thansk,
Kuai
^ permalink raw reply
* Release mdadm-4.6
From: Xiao Ni @ 2026-03-16 15:45 UTC (permalink / raw)
To: linux-raid; +Cc: Song Liu, Yu Kuai, Nigel Croxon, Mariusz Tkaczyk, Martin Wilck
Hi all
I'm glad to announce mdadm-4.5. It is published to both remotes
(github and kernel.org). Here are some highlights from CHANGELOG.md.
# Release [mdadm-4.6](https://git.kernel.org/pub/scm/utils/mdadm/mdadm.git/log/?h=mdadm-4.6)
Features:
- Add support for new lockless bitmap from Yu Kuai
- Add "PROBING ddf_extended" option in mdadm.conf from Martin Wilck
- Add --detail to usage in ReadMe from Brian Matheson
Fixes:
- Fix uuid endianness mismatch issue in sysfs_rules_apply() from Abirami0904
- Fix mdcheck: don't stop mdcheck_continue.timer from Martin Wilck
- Deal with hot-unplugged devices in platform-intel from Jean Delvare
- Detect corosync and libdlm via pkg-config in Makefile from Maxin John
- Fix UEFI backward compatibility for RAID10D4 in imsm from Blazej Kucman
- Optimize DDF header search using mmap for better performance from lilinzhe
- Set sysfs name after assembling imsm array in incremental from Xiao Ni
- Use creation_time for ctime in imsm container info from Xiao Ni
- Fix sigterm variable to be volatile sig_atomic_t from Cristian Rodríguez
- Use 64-bit off_t across both musl and glibc from Ankur Tyagi
- Fix format overflow error in super-intel.c from Martin Wilck
- Fix compilation errors for unused variables with GCC 16 from Martin Wilck
- Load md_mod first to avoid module loading issues from Xiao Ni
There are some important issues which led to boot failure. These issues
have been fixed recently. It's better to make a new release. So users
can choose a version without these problems.
https://github.com/md-raid-utilities/mdadm/issues/249 has the details.
Best Regards
Xiao
^ permalink raw reply
* Re: Release mdadm-4.6
From: Xiao Ni @ 2026-03-16 15:50 UTC (permalink / raw)
To: linux-raid; +Cc: Song Liu, Yu Kuai, Nigel Croxon, Mariusz Tkaczyk, Martin Wilck
In-Reply-To: <CALTww2_2pjjOSsVj-WT++m35555CjnPWokU7fut+HwUJVV-=ZQ@mail.gmail.com>
On Mon, Mar 16, 2026 at 11:45 PM Xiao Ni <xni@redhat.com> wrote:
>
> Hi all
>
> I'm glad to announce mdadm-4.5. It is published to both remotes
Sorry, there was a typo above. s/mdadm-4.5/mdadm-4.6/g
Regards
Xiao
> (github and kernel.org). Here are some highlights from CHANGELOG.md.
>
> # Release [mdadm-4.6](https://git.kernel.org/pub/scm/utils/mdadm/mdadm.git/log/?h=mdadm-4.6)
>
> Features:
> - Add support for new lockless bitmap from Yu Kuai
> - Add "PROBING ddf_extended" option in mdadm.conf from Martin Wilck
> - Add --detail to usage in ReadMe from Brian Matheson
>
> Fixes:
> - Fix uuid endianness mismatch issue in sysfs_rules_apply() from Abirami0904
> - Fix mdcheck: don't stop mdcheck_continue.timer from Martin Wilck
> - Deal with hot-unplugged devices in platform-intel from Jean Delvare
> - Detect corosync and libdlm via pkg-config in Makefile from Maxin John
> - Fix UEFI backward compatibility for RAID10D4 in imsm from Blazej Kucman
> - Optimize DDF header search using mmap for better performance from lilinzhe
> - Set sysfs name after assembling imsm array in incremental from Xiao Ni
> - Use creation_time for ctime in imsm container info from Xiao Ni
> - Fix sigterm variable to be volatile sig_atomic_t from Cristian Rodríguez
> - Use 64-bit off_t across both musl and glibc from Ankur Tyagi
> - Fix format overflow error in super-intel.c from Martin Wilck
> - Fix compilation errors for unused variables with GCC 16 from Martin Wilck
> - Load md_mod first to avoid module loading issues from Xiao Ni
>
> There are some important issues which led to boot failure. These issues
> have been fixed recently. It's better to make a new release. So users
> can choose a version without these problems.
> https://github.com/md-raid-utilities/mdadm/issues/249 has the details.
>
> Best Regards
> Xiao
^ permalink raw reply
* Re: [linux-next:master] [xor] ebbbf58989: BUG:KASAN:slab-out-of-bounds_in_xor_gen_avx
From: Christoph Hellwig @ 2026-03-16 16:13 UTC (permalink / raw)
To: kernel test robot
Cc: Christoph Hellwig, oe-lkp, lkp, Andrew Morton, Albert Ou,
Alexander Gordeev, Alexandre Ghiti, Andreas Larsson, Anton Ivanov,
Arnd Bergmann, Borislav Petkov (AMD), Catalin Marinas,
Chris Mason, Christian Borntraeger, Dan Williams, David S. Miller,
David Sterba, Heiko Carstens, Herbert Xu, H. Peter Anvin,
Huacai Chen, Ingo Molnar, Johannes Berg, Li Nan,
Madhavan Srinivasan, Magnus Lindholm, Matt Turner,
Michael Ellerman, Nicholas Piggin, Palmer Dabbelt,
Richard Henderson, Richard Weinberger, Russell King, Song Liu,
Sven Schnelle, Vasily Gorbik, WANG Xuerui, Will Deacon,
linux-raid, linux-kernel, linux-riscv
In-Reply-To: <202603131751.7ba6ffc8-lkp@intel.com>
Note that this is the v1 series, and this is the case that Eric
mentioned and which thus got fixed.
^ permalink raw reply
* Re: [PATCH 10/27] alpha: move the XOR code to lib/raid/
From: Magnus Lindholm @ 2026-03-16 22:12 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Andrew Morton, Richard Henderson, Matt Turner, Russell King,
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,
David S. Miller, Andreas Larsson, Richard Weinberger,
Anton Ivanov, Johannes Berg, 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, Theodore Ts'o, Jason A. Donenfeld,
linux-alpha, linux-kernel, linux-arm-kernel, loongarch,
linuxppc-dev, linux-riscv, linux-s390, sparclinux, linux-um,
linux-crypto, linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260311070416.972667-11-hch@lst.de>
On Wed, Mar 11, 2026 at 8:06 AM Christoph Hellwig <hch@lst.de> wrote:
>
> Move the optimized XOR code out of line into lib/raid.
>
> Note that the giant inline assembly block might be better off as a
> separate assembly source file now, but I'll leave that to the alpha
> maintainers.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> arch/alpha/include/asm/xor.h | 853 +----------------------------------
> lib/raid/xor/Makefile | 2 +
> lib/raid/xor/alpha/xor.c | 849 ++++++++++++++++++++++++++++++++++
> 3 files changed, 855 insertions(+), 849 deletions(-)
> create mode 100644 lib/raid/xor/alpha/xor.c
>
Hi,
I applied this patch and ran it on my UP2000+
The kernel builds and boots, and I verified the new lib/raid/xor/alpha
implementation using the XOR KUnit test, the test passed, see below:
[ 25.705064] KTAP version 1
[ 25.705064] # Subtest: xor
[ 25.705064] # module: xor_kunit
[ 25.705064] 1..1
[ 28.957992] # xor_test: Test should be marked slow (runtime:
3.253413330s)
[ 28.958969] ok 1 xor_test
Acked-by: Magnus Lindholm <linmag7@gmail.com>
Tested-by: Magnus Lindholm <linmag7@gmail.com>
^ permalink raw reply
* Re: [PATCH v2 4/5] md/md-llbitmap: add CleanUnwritten state for RAID-5 proactive parity building
From: Yu Kuai @ 2026-03-17 3:36 UTC (permalink / raw)
To: Xiao Ni, yukuai; +Cc: song, linan122, colyli, linux-raid, linux-kernel
In-Reply-To: <CALTww2_cWUjHajL=7VRUSh-5jyzg4xK9ih2G_AhD3jMsF3qrAQ@mail.gmail.com>
Hi,
在 2026/3/13 11:16, Xiao Ni 写道:
> On Mon, Feb 23, 2026 at 10:44 AM Yu Kuai <yukuai@fnnas.com> wrote:
>> Add new states to the llbitmap state machine to support proactive XOR
>> parity building for RAID-5 arrays. This allows users to pre-build parity
>> data for unwritten regions before any user data is written.
>>
>> New states added:
>> - BitNeedSyncUnwritten: Transitional state when proactive sync is triggered
>> via sysfs on Unwritten regions.
>> - BitSyncingUnwritten: Proactive sync in progress for unwritten region.
>> - BitCleanUnwritten: XOR parity has been pre-built, but no user data
>> written yet. When user writes to this region, it transitions to BitDirty.
>>
>> New actions added:
>> - BitmapActionProactiveSync: Trigger for proactive XOR parity building.
>> - BitmapActionClearUnwritten: Convert CleanUnwritten/NeedSyncUnwritten/
>> SyncingUnwritten states back to Unwritten before recovery starts.
>>
>> State flows:
>> - Current (lazy): Unwritten -> (write) -> NeedSync -> (sync) -> Dirty -> Clean
>> - New (proactive): Unwritten -> (sysfs) -> NeedSyncUnwritten -> (sync) -> CleanUnwritten
>> - On write to CleanUnwritten: CleanUnwritten -> (write) -> Dirty -> Clean
>> - On disk replacement: CleanUnwritten regions are converted to Unwritten
>> before recovery starts, so recovery only rebuilds regions with user data
>>
>> A new sysfs interface is added at /sys/block/mdX/md/llbitmap/proactive_sync
>> (write-only) to trigger proactive sync. This only works for RAID-456 arrays.
>>
>> Signed-off-by: Yu Kuai <yukuai@fnnas.com>
>> ---
>> drivers/md/md-llbitmap.c | 140 +++++++++++++++++++++++++++++++++++----
>> drivers/md/md.c | 6 +-
>> 2 files changed, 132 insertions(+), 14 deletions(-)
>>
>> diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
>> index 5f9e7004e3e3..461050b2771b 100644
>> --- a/drivers/md/md-llbitmap.c
>> +++ b/drivers/md/md-llbitmap.c
>> @@ -208,6 +208,20 @@ enum llbitmap_state {
>> BitNeedSync,
>> /* data is synchronizing */
>> BitSyncing,
>> + /*
>> + * Proactive sync requested for unwritten region (raid456 only).
>> + * Triggered via sysfs when user wants to pre-build XOR parity
>> + * for regions that have never been written.
>> + */
>> + BitNeedSyncUnwritten,
>> + /* Proactive sync in progress for unwritten region */
>> + BitSyncingUnwritten,
>> + /*
>> + * XOR parity has been pre-built for a region that has never had
>> + * user data written. When user writes to this region, it transitions
>> + * to BitDirty.
>> + */
>> + BitCleanUnwritten,
>> BitStateCount,
>> BitNone = 0xff,
>> };
>> @@ -232,6 +246,12 @@ enum llbitmap_action {
>> * BitNeedSync.
>> */
>> BitmapActionStale,
>> + /*
>> + * Proactive sync trigger for raid456 - builds XOR parity for
>> + * Unwritten regions without requiring user data write first.
>> + */
>> + BitmapActionProactiveSync,
>> + BitmapActionClearUnwritten,
>> BitmapActionCount,
>> /* Init state is BitUnwritten */
>> BitmapActionInit,
>> @@ -304,6 +324,8 @@ static char state_machine[BitStateCount][BitmapActionCount] = {
>> [BitmapActionDaemon] = BitNone,
>> [BitmapActionDiscard] = BitNone,
>> [BitmapActionStale] = BitNone,
>> + [BitmapActionProactiveSync] = BitNeedSyncUnwritten,
>> + [BitmapActionClearUnwritten] = BitNone,
>> },
>> [BitClean] = {
>> [BitmapActionStartwrite] = BitDirty,
>> @@ -314,6 +336,8 @@ static char state_machine[BitStateCount][BitmapActionCount] = {
>> [BitmapActionDaemon] = BitNone,
>> [BitmapActionDiscard] = BitUnwritten,
>> [BitmapActionStale] = BitNeedSync,
>> + [BitmapActionProactiveSync] = BitNone,
>> + [BitmapActionClearUnwritten] = BitNone,
>> },
>> [BitDirty] = {
>> [BitmapActionStartwrite] = BitNone,
>> @@ -324,6 +348,8 @@ static char state_machine[BitStateCount][BitmapActionCount] = {
>> [BitmapActionDaemon] = BitClean,
>> [BitmapActionDiscard] = BitUnwritten,
>> [BitmapActionStale] = BitNeedSync,
>> + [BitmapActionProactiveSync] = BitNone,
>> + [BitmapActionClearUnwritten] = BitNone,
>> },
>> [BitNeedSync] = {
>> [BitmapActionStartwrite] = BitNone,
>> @@ -334,6 +360,8 @@ static char state_machine[BitStateCount][BitmapActionCount] = {
>> [BitmapActionDaemon] = BitNone,
>> [BitmapActionDiscard] = BitUnwritten,
>> [BitmapActionStale] = BitNone,
>> + [BitmapActionProactiveSync] = BitNone,
>> + [BitmapActionClearUnwritten] = BitNone,
>> },
>> [BitSyncing] = {
>> [BitmapActionStartwrite] = BitNone,
>> @@ -344,6 +372,44 @@ static char state_machine[BitStateCount][BitmapActionCount] = {
>> [BitmapActionDaemon] = BitNone,
>> [BitmapActionDiscard] = BitUnwritten,
>> [BitmapActionStale] = BitNeedSync,
>> + [BitmapActionProactiveSync] = BitNone,
>> + [BitmapActionClearUnwritten] = BitNone,
>> + },
>> + [BitNeedSyncUnwritten] = {
>> + [BitmapActionStartwrite] = BitNeedSync,
>> + [BitmapActionStartsync] = BitSyncingUnwritten,
>> + [BitmapActionEndsync] = BitNone,
>> + [BitmapActionAbortsync] = BitUnwritten,
>> + [BitmapActionReload] = BitUnwritten,
>> + [BitmapActionDaemon] = BitNone,
>> + [BitmapActionDiscard] = BitUnwritten,
>> + [BitmapActionStale] = BitUnwritten,
>> + [BitmapActionProactiveSync] = BitNone,
>> + [BitmapActionClearUnwritten] = BitUnwritten,
>> + },
>> + [BitSyncingUnwritten] = {
>> + [BitmapActionStartwrite] = BitSyncing,
>> + [BitmapActionStartsync] = BitSyncingUnwritten,
> Hi Kuai
>
> Is it ok to set the above state to BitNone? The orignal state is
> already BitSyncingUnwritten.
>
>> + [BitmapActionEndsync] = BitCleanUnwritten,
>> + [BitmapActionAbortsync] = BitUnwritten,
>> + [BitmapActionReload] = BitUnwritten,
>> + [BitmapActionDaemon] = BitNone,
>> + [BitmapActionDiscard] = BitUnwritten,
>> + [BitmapActionStale] = BitUnwritten,
>> + [BitmapActionProactiveSync] = BitNone,
>> + [BitmapActionClearUnwritten] = BitUnwritten,
>> + },
>> + [BitCleanUnwritten] = {
>> + [BitmapActionStartwrite] = BitDirty,
>> + [BitmapActionStartsync] = BitNone,
>> + [BitmapActionEndsync] = BitNone,
>> + [BitmapActionAbortsync] = BitNone,
>> + [BitmapActionReload] = BitNone,
>> + [BitmapActionDaemon] = BitNone,
>> + [BitmapActionDiscard] = BitUnwritten,
>> + [BitmapActionStale] = BitUnwritten,
>> + [BitmapActionProactiveSync] = BitNone,
>> + [BitmapActionClearUnwritten] = BitUnwritten,
>> },
>> };
>>
>> @@ -376,6 +442,7 @@ static void llbitmap_infect_dirty_bits(struct llbitmap *llbitmap,
>> pctl->state[pos] = level_456 ? BitNeedSync : BitDirty;
>> break;
>> case BitClean:
>> + case BitCleanUnwritten:
>> pctl->state[pos] = BitDirty;
>> break;
>> }
>> @@ -383,7 +450,7 @@ static void llbitmap_infect_dirty_bits(struct llbitmap *llbitmap,
>> }
>>
>> static void llbitmap_set_page_dirty(struct llbitmap *llbitmap, int idx,
>> - int offset)
>> + int offset, bool infect)
>> {
>> struct llbitmap_page_ctl *pctl = llbitmap->pctl[idx];
>> unsigned int io_size = llbitmap->io_size;
>> @@ -398,7 +465,7 @@ static void llbitmap_set_page_dirty(struct llbitmap *llbitmap, int idx,
>> * resync all the dirty bits, hence skip infect new dirty bits to
>> * prevent resync unnecessary data.
>> */
>> - if (llbitmap->mddev->degraded) {
>> + if (llbitmap->mddev->degraded || !infect) {
>> set_bit(block, pctl->dirty);
>> return;
>> }
>> @@ -438,7 +505,9 @@ static void llbitmap_write(struct llbitmap *llbitmap, enum llbitmap_state state,
>>
>> llbitmap->pctl[idx]->state[bit] = state;
>> if (state == BitDirty || state == BitNeedSync)
>> - llbitmap_set_page_dirty(llbitmap, idx, bit);
>> + llbitmap_set_page_dirty(llbitmap, idx, bit, true);
>> + else if (state == BitNeedSyncUnwritten)
>> + llbitmap_set_page_dirty(llbitmap, idx, bit, false);
>> }
>>
>> static struct page *llbitmap_read_page(struct llbitmap *llbitmap, int idx)
>> @@ -627,11 +696,10 @@ static enum llbitmap_state llbitmap_state_machine(struct llbitmap *llbitmap,
>> goto write_bitmap;
>> }
>>
>> - if (c == BitNeedSync)
>> + if (c == BitNeedSync || c == BitNeedSyncUnwritten)
>> need_resync = !mddev->degraded;
>>
>> state = state_machine[c][action];
>> -
>> write_bitmap:
>> if (unlikely(mddev->degraded)) {
>> /* For degraded array, mark new data as need sync. */
>> @@ -658,8 +726,7 @@ static enum llbitmap_state llbitmap_state_machine(struct llbitmap *llbitmap,
>> }
>>
>> llbitmap_write(llbitmap, state, start);
>> -
>> - if (state == BitNeedSync)
>> + if (state == BitNeedSync || state == BitNeedSyncUnwritten)
>> need_resync = !mddev->degraded;
>> else if (state == BitDirty &&
>> !timer_pending(&llbitmap->pending_timer))
>> @@ -1229,7 +1296,7 @@ static bool llbitmap_blocks_synced(struct mddev *mddev, sector_t offset)
>> unsigned long p = offset >> llbitmap->chunkshift;
>> enum llbitmap_state c = llbitmap_read(llbitmap, p);
>>
>> - return c == BitClean || c == BitDirty;
>> + return c == BitClean || c == BitDirty || c == BitCleanUnwritten;
>> }
>>
>> static sector_t llbitmap_skip_sync_blocks(struct mddev *mddev, sector_t offset)
>> @@ -1243,6 +1310,10 @@ static sector_t llbitmap_skip_sync_blocks(struct mddev *mddev, sector_t offset)
>> if (c == BitUnwritten)
>> return blocks;
>>
>> + /* Skip CleanUnwritten - no user data, will be reset after recovery */
>> + if (c == BitCleanUnwritten)
>> + return blocks;
>> +
>> /* For degraded array, don't skip */
>> if (mddev->degraded)
>> return 0;
>> @@ -1261,14 +1332,25 @@ static bool llbitmap_start_sync(struct mddev *mddev, sector_t offset,
>> {
>> struct llbitmap *llbitmap = mddev->bitmap;
>> unsigned long p = offset >> llbitmap->chunkshift;
>> + enum llbitmap_state state;
>> +
>> + /*
>> + * Before recovery starts, convert CleanUnwritten to Unwritten.
>> + * This ensures the new disk won't have stale parity data.
>> + */
>> + if (offset == 0 && test_bit(MD_RECOVERY_RECOVER, &mddev->recovery) &&
>> + !test_bit(MD_RECOVERY_LAZY_RECOVER, &mddev->recovery))
>> + llbitmap_state_machine(llbitmap, 0, llbitmap->chunks - 1,
>> + BitmapActionClearUnwritten);
>> +
>>
>> /*
>> * Handle one bit at a time, this is much simpler. And it doesn't matter
>> * if md_do_sync() loop more times.
>> */
>> *blocks = llbitmap->chunksize - (offset & (llbitmap->chunksize - 1));
>> - return llbitmap_state_machine(llbitmap, p, p,
>> - BitmapActionStartsync) == BitSyncing;
>> + state = llbitmap_state_machine(llbitmap, p, p, BitmapActionStartsync);
>> + return state == BitSyncing || state == BitSyncingUnwritten;
>> }
>>
>> /* Something is wrong, sync_thread stop at @offset */
>> @@ -1474,9 +1556,15 @@ static ssize_t bits_show(struct mddev *mddev, char *page)
>> }
>>
>> mutex_unlock(&mddev->bitmap_info.mutex);
>> - return sprintf(page, "unwritten %d\nclean %d\ndirty %d\nneed sync %d\nsyncing %d\n",
>> + return sprintf(page,
>> + "unwritten %d\nclean %d\ndirty %d\n"
>> + "need sync %d\nsyncing %d\n"
>> + "need sync unwritten %d\nsyncing unwritten %d\n"
>> + "clean unwritten %d\n",
>> bits[BitUnwritten], bits[BitClean], bits[BitDirty],
>> - bits[BitNeedSync], bits[BitSyncing]);
>> + bits[BitNeedSync], bits[BitSyncing],
>> + bits[BitNeedSyncUnwritten], bits[BitSyncingUnwritten],
>> + bits[BitCleanUnwritten]);
>> }
>>
>> static struct md_sysfs_entry llbitmap_bits = __ATTR_RO(bits);
>> @@ -1549,11 +1637,39 @@ barrier_idle_store(struct mddev *mddev, const char *buf, size_t len)
>>
>> static struct md_sysfs_entry llbitmap_barrier_idle = __ATTR_RW(barrier_idle);
>>
>> +static ssize_t
>> +proactive_sync_store(struct mddev *mddev, const char *buf, size_t len)
>> +{
>> + struct llbitmap *llbitmap;
>> +
>> + /* Only for RAID-456 */
>> + if (!raid_is_456(mddev))
>> + return -EINVAL;
>> +
>> + mutex_lock(&mddev->bitmap_info.mutex);
>> + llbitmap = mddev->bitmap;
>> + if (!llbitmap || !llbitmap->pctl) {
>> + mutex_unlock(&mddev->bitmap_info.mutex);
>> + return -ENODEV;
>> + }
>> +
>> + /* Trigger proactive sync on all Unwritten regions */
>> + llbitmap_state_machine(llbitmap, 0, llbitmap->chunks - 1,
>> + BitmapActionProactiveSync);
>> +
>> + mutex_unlock(&mddev->bitmap_info.mutex);
>> + return len;
>> +}
>> +
>> +static struct md_sysfs_entry llbitmap_proactive_sync =
>> + __ATTR(proactive_sync, 0200, NULL, proactive_sync_store);
>> +
>> static struct attribute *md_llbitmap_attrs[] = {
>> &llbitmap_bits.attr,
>> &llbitmap_metadata.attr,
>> &llbitmap_daemon_sleep.attr,
>> &llbitmap_barrier_idle.attr,
>> + &llbitmap_proactive_sync.attr,
>> NULL
>> };
>>
>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>> index 245785ad0ffd..b6543d81ac96 100644
>> --- a/drivers/md/md.c
>> +++ b/drivers/md/md.c
>> @@ -9878,8 +9878,10 @@ void md_do_sync(struct md_thread *thread)
>> * Give other IO more of a chance.
>> * The faster the devices, the less we wait.
>> */
>> - wait_event(mddev->recovery_wait,
>> - !atomic_read(&mddev->recovery_active));
>> + wait_event_timeout(
>> + mddev->recovery_wait,
>> + !atomic_read(&mddev->recovery_active),
>> + HZ);
> Could you explain this change? It no longer waits for synchronous I/O
> to finish when the sync speed is fast. Does it have relasionship with
> this major change of this patch?
I should separate this change into another patch, I didn't notice this was
amended into this patch.
And I'll also check the return value and goto repeat if timeout, instead of
waiting for all sync_io to be done, I think this is more graceful and can allow
sync io to be issued once current speed is less than sync speed min.
>
> Best Regards
> Xiao
>> }
>> }
>> }
>> --
>> 2.51.0
>>
>>
--
Thansk,
Kuai
^ permalink raw reply
* Bug in RAID10 'enough' when far_copies=2?
From: Wesley W. Terpstra @ 2026-03-17 9:34 UTC (permalink / raw)
To: linux-raid
Consider an array with raid_disks=4, near_copies=1, far_copies=2.
The function "enough" in raid10.c requires all four of these equations
to be true:
Eqn0 := 0 || 1 // device 0 or 1 is present
Eqn1 := 1 || 2
Eqn2 := 2 || 3
Eqn3 := 3 || 0
This behavior is specific to far_copies=2. When near_copies=2, only
Eqn0 and Eqn2 are required.
The documentation claims that for this configuration, the layout is:
A1 A2 A3 A4
A5 A6 A7 A8
.. .. .. ..
A2 A1 A4 A3
A6 A5 A8 A7
In this situation, the array should NOT be possible to reassemble from
(0, 1) or (2, 3). The 'enough' function rejects those; Eqn2 and Eqn0
fail respectively. The 'enough' function accepts (0, 2) and (1, 3),
which seems correct.
However, the 'enough' function REJECTS (1, 2) and (0, 3) due to Eqn3
and Eqn1 respectively. According to the documented layout, 'enough'
should accept these configurations.
^ permalink raw reply
* [PATCH] raid6: arm64: add SVE optimized implementation for syndrome generation
From: Demian Shulhan @ 2026-03-17 11:17 UTC (permalink / raw)
To: Song Liu, Yu Kuai; +Cc: Li Nan, linux-raid, linux-kernel, Demian Shulhan
Implement Scalable Vector Extension (SVE) optimized routines for RAID6
syndrome generation and recovery on ARM64.
The SVE instruction set allows for variable vector lengths (from 128 to
2048 bits), scaling automatically with the hardware capabilities. This
implementation handles arbitrary SVE vector lengths using the `cntb`
instruction to determine the runtime vector length.
The implementation introduces `svex1`, `svex2`, and `svex4` algorithms.
The `svex4` algorithm utilizes loop unrolling by 4 blocks per iteration
and manual software pipelining (interleaving memory loads with XORs)
to minimize instruction dependency stalls and maximize CPU pipeline
utilization and memory bandwidth.
Performance was tested on an AWS Graviton3 (Neoverse-V1) instance which
features 256-bit SVE vector length. The `svex4` implementation outperforms
the existing 128-bit `neonx4` baseline for syndrome generation:
raid6: svex4 gen() 19688 MB/s
raid6: svex2 gen() 18610 MB/s
raid6: svex1 gen() 19254 MB/s
raid6: neonx8 gen() 18554 MB/s
raid6: neonx4 gen() 19612 MB/s
raid6: neonx2 gen() 16248 MB/s
raid6: neonx1 gen() 13591 MB/s
raid6: using algorithm svex4 gen() 19688 MB/s
raid6: .... xor() 11212 MB/s, rmw enabled
raid6: using neon recovery algorithm
Note that for the recovery path (`xor_syndrome`), NEON may still be
selected dynamically by the algorithm benchmark, as the recovery
workload is heavily memory-bound.
Signed-off-by: Demian Shulhan <demyansh@gmail.com>
---
include/linux/raid/pq.h | 3 +
lib/raid6/Makefile | 5 +
lib/raid6/algos.c | 5 +
lib/raid6/sve.c | 675 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 688 insertions(+)
create mode 100644 lib/raid6/sve.c
diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h
index 2467b3be15c9..787cc57aea9d 100644
--- a/include/linux/raid/pq.h
+++ b/include/linux/raid/pq.h
@@ -140,6 +140,9 @@ extern const struct raid6_calls raid6_neonx1;
extern const struct raid6_calls raid6_neonx2;
extern const struct raid6_calls raid6_neonx4;
extern const struct raid6_calls raid6_neonx8;
+extern const struct raid6_calls raid6_svex1;
+extern const struct raid6_calls raid6_svex2;
+extern const struct raid6_calls raid6_svex4;
/* Algorithm list */
extern const struct raid6_calls * const raid6_algos[];
diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile
index 5be0a4e60ab1..6cdaa6f206fb 100644
--- a/lib/raid6/Makefile
+++ b/lib/raid6/Makefile
@@ -8,6 +8,7 @@ raid6_pq-$(CONFIG_X86) += recov_ssse3.o recov_avx2.o mmx.o sse1.o sse2.o avx2.o
raid6_pq-$(CONFIG_ALTIVEC) += altivec1.o altivec2.o altivec4.o altivec8.o \
vpermxor1.o vpermxor2.o vpermxor4.o vpermxor8.o
raid6_pq-$(CONFIG_KERNEL_MODE_NEON) += neon.o neon1.o neon2.o neon4.o neon8.o recov_neon.o recov_neon_inner.o
+raid6_pq-$(CONFIG_ARM64_SVE) += sve.o
raid6_pq-$(CONFIG_S390) += s390vx8.o recov_s390xc.o
raid6_pq-$(CONFIG_LOONGARCH) += loongarch_simd.o recov_loongarch_simd.o
raid6_pq-$(CONFIG_RISCV_ISA_V) += rvv.o recov_rvv.o
@@ -67,6 +68,10 @@ CFLAGS_REMOVE_neon2.o += $(CC_FLAGS_NO_FPU)
CFLAGS_REMOVE_neon4.o += $(CC_FLAGS_NO_FPU)
CFLAGS_REMOVE_neon8.o += $(CC_FLAGS_NO_FPU)
CFLAGS_REMOVE_recov_neon_inner.o += $(CC_FLAGS_NO_FPU)
+
+CFLAGS_sve.o += $(CC_FLAGS_FPU)
+CFLAGS_REMOVE_sve.o += $(CC_FLAGS_NO_FPU)
+
targets += neon1.c neon2.c neon4.c neon8.c
$(obj)/neon%.c: $(src)/neon.uc $(src)/unroll.awk FORCE
$(call if_changed,unroll)
diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index 799e0e5eac26..0ae73c3a4be3 100644
--- a/lib/raid6/algos.c
+++ b/lib/raid6/algos.c
@@ -66,6 +66,11 @@ const struct raid6_calls * const raid6_algos[] = {
&raid6_neonx2,
&raid6_neonx1,
#endif
+#ifdef CONFIG_ARM64_SVE
+ &raid6_svex4,
+ &raid6_svex2,
+ &raid6_svex1,
+#endif
#ifdef CONFIG_LOONGARCH
#ifdef CONFIG_CPU_HAS_LASX
&raid6_lasx,
diff --git a/lib/raid6/sve.c b/lib/raid6/sve.c
new file mode 100644
index 000000000000..afcf46b89a3d
--- /dev/null
+++ b/lib/raid6/sve.c
@@ -0,0 +1,675 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * RAID-6 syndrome calculation using ARM SVE instructions
+ */
+
+#include <linux/raid/pq.h>
+
+#ifdef __KERNEL__
+#include <asm/simd.h>
+#include <linux/cpufeature.h>
+#else
+#define scoped_ksimd()
+#define system_supports_sve() (1)
+#endif
+
+static void raid6_sve1_gen_syndrome_real(int disks, unsigned long bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int z0 = disks - 3;
+
+ p = dptr[z0 + 1];
+ q = dptr[z0 + 2];
+
+ asm volatile(
+ ".arch armv8.2-a+sve\n"
+ "ptrue p0.b\n"
+ "cntb x3\n"
+ "mov w4, #0x1d\n"
+ "dup z4.b, w4\n"
+ "mov x5, #0\n"
+
+ "0:\n"
+ "ldr x6, [%[dptr], %[z0], lsl #3]\n"
+ "ld1b z0.b, p0/z, [x6, x5]\n"
+ "mov z1.d, z0.d\n"
+
+ "mov w7, %w[z0]\n"
+ "sub w7, w7, #1\n"
+
+ "1:\n"
+ "cmp w7, #0\n"
+ "blt 2f\n"
+
+ "mov z3.d, z1.d\n"
+ "asr z3.b, p0/m, z3.b, #7\n"
+ "lsl z1.b, p0/m, z1.b, #1\n"
+
+ "and z3.d, z3.d, z4.d\n"
+ "eor z1.d, z1.d, z3.d\n"
+
+ "sxtw x8, w7\n"
+ "ldr x6, [%[dptr], x8, lsl #3]\n"
+ "ld1b z2.b, p0/z, [x6, x5]\n"
+
+ "eor z1.d, z1.d, z2.d\n"
+ "eor z0.d, z0.d, z2.d\n"
+
+ "sub w7, w7, #1\n"
+ "b 1b\n"
+ "2:\n"
+
+ "st1b z0.b, p0, [%[p], x5]\n"
+ "st1b z1.b, p0, [%[q], x5]\n"
+
+ "add x5, x5, x3\n"
+ "cmp x5, %[bytes]\n"
+ "blt 0b\n"
+ :
+ : [dptr] "r" (dptr), [z0] "r" (z0), [bytes] "r" (bytes),
+ [p] "r" (p), [q] "r" (q)
+ : "memory", "p0", "x3", "x4", "x5", "x6", "x7", "x8",
+ "z0", "z1", "z2", "z3", "z4"
+ );
+}
+
+static void raid6_sve1_xor_syndrome_real(int disks, int start, int stop,
+ unsigned long bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int z0 = stop;
+
+ p = dptr[disks - 2];
+ q = dptr[disks - 1];
+
+ asm volatile(
+ ".arch armv8.2-a+sve\n"
+ "ptrue p0.b\n"
+ "cntb x3\n"
+ "mov w4, #0x1d\n"
+ "dup z4.b, w4\n"
+ "mov x5, #0\n"
+
+ "0:\n"
+ "ldr x6, [%[dptr], %[z0], lsl #3]\n"
+ "ld1b z1.b, p0/z, [x6, x5]\n"
+ "ld1b z0.b, p0/z, [%[p], x5]\n"
+ "eor z0.d, z0.d, z1.d\n"
+
+ "mov w7, %w[z0]\n"
+ "sub w7, w7, #1\n"
+
+ "1:\n"
+ "cmp w7, %w[start]\n"
+ "blt 2f\n"
+
+ "mov z3.d, z1.d\n"
+ "asr z3.b, p0/m, z3.b, #7\n"
+ "lsl z1.b, p0/m, z1.b, #1\n"
+ "and z3.d, z3.d, z4.d\n"
+ "eor z1.d, z1.d, z3.d\n"
+
+ "sxtw x8, w7\n"
+ "ldr x6, [%[dptr], x8, lsl #3]\n"
+ "ld1b z2.b, p0/z, [x6, x5]\n"
+
+ "eor z1.d, z1.d, z2.d\n"
+ "eor z0.d, z0.d, z2.d\n"
+
+ "sub w7, w7, #1\n"
+ "b 1b\n"
+ "2:\n"
+
+ "mov w7, %w[start]\n"
+ "sub w7, w7, #1\n"
+ "3:\n"
+ "cmp w7, #0\n"
+ "blt 4f\n"
+
+ "mov z3.d, z1.d\n"
+ "asr z3.b, p0/m, z3.b, #7\n"
+ "lsl z1.b, p0/m, z1.b, #1\n"
+ "and z3.d, z3.d, z4.d\n"
+ "eor z1.d, z1.d, z3.d\n"
+
+ "sub w7, w7, #1\n"
+ "b 3b\n"
+ "4:\n"
+
+ "ld1b z2.b, p0/z, [%[q], x5]\n"
+ "eor z1.d, z1.d, z2.d\n"
+
+ "st1b z0.b, p0, [%[p], x5]\n"
+ "st1b z1.b, p0, [%[q], x5]\n"
+
+ "add x5, x5, x3\n"
+ "cmp x5, %[bytes]\n"
+ "blt 0b\n"
+ :
+ : [dptr] "r" (dptr), [z0] "r" (z0), [bytes] "r" (bytes),
+ [p] "r" (p), [q] "r" (q), [start] "r" (start)
+ : "memory", "p0", "x3", "x4", "x5", "x6", "x7", "x8",
+ "z0", "z1", "z2", "z3", "z4"
+ );
+}
+
+static void raid6_sve2_gen_syndrome_real(int disks, unsigned long bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int z0 = disks - 3;
+
+ p = dptr[z0 + 1];
+ q = dptr[z0 + 2];
+
+ asm volatile(
+ ".arch armv8.2-a+sve\n"
+ "ptrue p0.b\n"
+ "cntb x3\n"
+ "mov w4, #0x1d\n"
+ "dup z4.b, w4\n"
+ "mov x5, #0\n"
+
+ "0:\n"
+ "ldr x6, [%[dptr], %[z0], lsl #3]\n"
+ "ld1b z0.b, p0/z, [x6, x5]\n"
+ "add x8, x5, x3\n"
+ "ld1b z5.b, p0/z, [x6, x8]\n"
+ "mov z1.d, z0.d\n"
+ "mov z6.d, z5.d\n"
+
+ "mov w7, %w[z0]\n"
+ "sub w7, w7, #1\n"
+
+ "1:\n"
+ "cmp w7, #0\n"
+ "blt 2f\n"
+
+ "mov z3.d, z1.d\n"
+ "asr z3.b, p0/m, z3.b, #7\n"
+ "lsl z1.b, p0/m, z1.b, #1\n"
+ "and z3.d, z3.d, z4.d\n"
+ "eor z1.d, z1.d, z3.d\n"
+
+ "mov z8.d, z6.d\n"
+ "asr z8.b, p0/m, z8.b, #7\n"
+ "lsl z6.b, p0/m, z6.b, #1\n"
+ "and z8.d, z8.d, z4.d\n"
+ "eor z6.d, z6.d, z8.d\n"
+
+ "sxtw x8, w7\n"
+ "ldr x6, [%[dptr], x8, lsl #3]\n"
+ "ld1b z2.b, p0/z, [x6, x5]\n"
+ "add x8, x5, x3\n"
+ "ld1b z7.b, p0/z, [x6, x8]\n"
+
+ "eor z1.d, z1.d, z2.d\n"
+ "eor z0.d, z0.d, z2.d\n"
+
+ "eor z6.d, z6.d, z7.d\n"
+ "eor z5.d, z5.d, z7.d\n"
+
+ "sub w7, w7, #1\n"
+ "b 1b\n"
+ "2:\n"
+
+ "st1b z0.b, p0, [%[p], x5]\n"
+ "st1b z1.b, p0, [%[q], x5]\n"
+ "add x8, x5, x3\n"
+ "st1b z5.b, p0, [%[p], x8]\n"
+ "st1b z6.b, p0, [%[q], x8]\n"
+
+ "add x5, x5, x3\n"
+ "add x5, x5, x3\n"
+ "cmp x5, %[bytes]\n"
+ "blt 0b\n"
+ :
+ : [dptr] "r" (dptr), [z0] "r" (z0), [bytes] "r" (bytes),
+ [p] "r" (p), [q] "r" (q)
+ : "memory", "p0", "x3", "x4", "x5", "x6", "x7", "x8",
+ "z0", "z1", "z2", "z3", "z4",
+ "z5", "z6", "z7", "z8"
+ );
+}
+
+static void raid6_sve2_xor_syndrome_real(int disks, int start, int stop,
+ unsigned long bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int z0 = stop;
+
+ p = dptr[disks - 2];
+ q = dptr[disks - 1];
+
+ asm volatile(
+ ".arch armv8.2-a+sve\n"
+ "ptrue p0.b\n"
+ "cntb x3\n"
+ "mov w4, #0x1d\n"
+ "dup z4.b, w4\n"
+ "mov x5, #0\n"
+
+ "0:\n"
+ "ldr x6, [%[dptr], %[z0], lsl #3]\n"
+ "ld1b z1.b, p0/z, [x6, x5]\n"
+ "add x8, x5, x3\n"
+ "ld1b z6.b, p0/z, [x6, x8]\n"
+
+ "ld1b z0.b, p0/z, [%[p], x5]\n"
+ "ld1b z5.b, p0/z, [%[p], x8]\n"
+
+ "eor z0.d, z0.d, z1.d\n"
+ "eor z5.d, z5.d, z6.d\n"
+
+ "mov w7, %w[z0]\n"
+ "sub w7, w7, #1\n"
+
+ "1:\n"
+ "cmp w7, %w[start]\n"
+ "blt 2f\n"
+
+ "mov z3.d, z1.d\n"
+ "asr z3.b, p0/m, z3.b, #7\n"
+ "lsl z1.b, p0/m, z1.b, #1\n"
+ "and z3.d, z3.d, z4.d\n"
+ "eor z1.d, z1.d, z3.d\n"
+
+ "mov z8.d, z6.d\n"
+ "asr z8.b, p0/m, z8.b, #7\n"
+ "lsl z6.b, p0/m, z6.b, #1\n"
+ "and z8.d, z8.d, z4.d\n"
+ "eor z6.d, z6.d, z8.d\n"
+
+ "sxtw x8, w7\n"
+ "ldr x6, [%[dptr], x8, lsl #3]\n"
+ "ld1b z2.b, p0/z, [x6, x5]\n"
+ "add x8, x5, x3\n"
+ "ld1b z7.b, p0/z, [x6, x8]\n"
+
+ "eor z1.d, z1.d, z2.d\n"
+ "eor z0.d, z0.d, z2.d\n"
+
+ "eor z6.d, z6.d, z7.d\n"
+ "eor z5.d, z5.d, z7.d\n"
+
+ "sub w7, w7, #1\n"
+ "b 1b\n"
+ "2:\n"
+
+ "mov w7, %w[start]\n"
+ "sub w7, w7, #1\n"
+ "3:\n"
+ "cmp w7, #0\n"
+ "blt 4f\n"
+
+ "mov z3.d, z1.d\n"
+ "asr z3.b, p0/m, z3.b, #7\n"
+ "lsl z1.b, p0/m, z1.b, #1\n"
+ "and z3.d, z3.d, z4.d\n"
+ "eor z1.d, z1.d, z3.d\n"
+
+ "mov z8.d, z6.d\n"
+ "asr z8.b, p0/m, z8.b, #7\n"
+ "lsl z6.b, p0/m, z6.b, #1\n"
+ "and z8.d, z8.d, z4.d\n"
+ "eor z6.d, z6.d, z8.d\n"
+
+ "sub w7, w7, #1\n"
+ "b 3b\n"
+ "4:\n"
+
+ "ld1b z2.b, p0/z, [%[q], x5]\n"
+ "eor z1.d, z1.d, z2.d\n"
+ "st1b z0.b, p0, [%[p], x5]\n"
+ "st1b z1.b, p0, [%[q], x5]\n"
+
+ "add x8, x5, x3\n"
+ "ld1b z7.b, p0/z, [%[q], x8]\n"
+ "eor z6.d, z6.d, z7.d\n"
+ "st1b z5.b, p0, [%[p], x8]\n"
+ "st1b z6.b, p0, [%[q], x8]\n"
+
+ "add x5, x5, x3\n"
+ "add x5, x5, x3\n"
+ "cmp x5, %[bytes]\n"
+ "blt 0b\n"
+ :
+ : [dptr] "r" (dptr), [z0] "r" (z0), [bytes] "r" (bytes),
+ [p] "r" (p), [q] "r" (q), [start] "r" (start)
+ : "memory", "p0", "x3", "x4", "x5", "x6", "x7", "x8",
+ "z0", "z1", "z2", "z3", "z4",
+ "z5", "z6", "z7", "z8"
+ );
+}
+
+static void raid6_sve4_gen_syndrome_real(int disks, unsigned long bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int z0 = disks - 3;
+
+ p = dptr[z0 + 1];
+ q = dptr[z0 + 2];
+
+ asm volatile(
+ ".arch armv8.2-a+sve\n"
+ "ptrue p0.b\n"
+ "cntb x3\n"
+ "mov w4, #0x1d\n"
+ "dup z4.b, w4\n"
+ "mov x5, #0\n"
+
+ "0:\n"
+ "ldr x6, [%[dptr], %[z0], lsl #3]\n"
+ "ld1b z0.b, p0/z, [x6, x5]\n"
+ "add x8, x5, x3\n"
+ "ld1b z5.b, p0/z, [x6, x8]\n"
+ "add x8, x8, x3\n"
+ "ld1b z10.b, p0/z, [x6, x8]\n"
+ "add x8, x8, x3\n"
+ "ld1b z15.b, p0/z, [x6, x8]\n"
+
+ "mov z1.d, z0.d\n"
+ "mov z6.d, z5.d\n"
+ "mov z11.d, z10.d\n"
+ "mov z16.d, z15.d\n"
+
+ "mov w7, %w[z0]\n"
+ "sub w7, w7, #1\n"
+
+ "1:\n"
+ "cmp w7, #0\n"
+ "blt 2f\n"
+
+ // software pipelining: load data early
+ "sxtw x8, w7\n"
+ "ldr x6, [%[dptr], x8, lsl #3]\n"
+ "ld1b z2.b, p0/z, [x6, x5]\n"
+ "add x8, x5, x3\n"
+ "ld1b z7.b, p0/z, [x6, x8]\n"
+ "add x8, x8, x3\n"
+ "ld1b z12.b, p0/z, [x6, x8]\n"
+ "add x8, x8, x3\n"
+ "ld1b z17.b, p0/z, [x6, x8]\n"
+
+ // math block 1
+ "mov z3.d, z1.d\n"
+ "asr z3.b, p0/m, z3.b, #7\n"
+ "lsl z1.b, p0/m, z1.b, #1\n"
+ "and z3.d, z3.d, z4.d\n"
+ "eor z1.d, z1.d, z3.d\n"
+ "eor z1.d, z1.d, z2.d\n"
+ "eor z0.d, z0.d, z2.d\n"
+
+ // math block 2
+ "mov z8.d, z6.d\n"
+ "asr z8.b, p0/m, z8.b, #7\n"
+ "lsl z6.b, p0/m, z6.b, #1\n"
+ "and z8.d, z8.d, z4.d\n"
+ "eor z6.d, z6.d, z8.d\n"
+ "eor z6.d, z6.d, z7.d\n"
+ "eor z5.d, z5.d, z7.d\n"
+
+ // math block 3
+ "mov z13.d, z11.d\n"
+ "asr z13.b, p0/m, z13.b, #7\n"
+ "lsl z11.b, p0/m, z11.b, #1\n"
+ "and z13.d, z13.d, z4.d\n"
+ "eor z11.d, z11.d, z13.d\n"
+ "eor z11.d, z11.d, z12.d\n"
+ "eor z10.d, z10.d, z12.d\n"
+
+ // math block 4
+ "mov z18.d, z16.d\n"
+ "asr z18.b, p0/m, z18.b, #7\n"
+ "lsl z16.b, p0/m, z16.b, #1\n"
+ "and z18.d, z18.d, z4.d\n"
+ "eor z16.d, z16.d, z18.d\n"
+ "eor z16.d, z16.d, z17.d\n"
+ "eor z15.d, z15.d, z17.d\n"
+
+ "sub w7, w7, #1\n"
+ "b 1b\n"
+ "2:\n"
+
+ "st1b z0.b, p0, [%[p], x5]\n"
+ "st1b z1.b, p0, [%[q], x5]\n"
+ "add x8, x5, x3\n"
+ "st1b z5.b, p0, [%[p], x8]\n"
+ "st1b z6.b, p0, [%[q], x8]\n"
+ "add x8, x8, x3\n"
+ "st1b z10.b, p0, [%[p], x8]\n"
+ "st1b z11.b, p0, [%[q], x8]\n"
+ "add x8, x8, x3\n"
+ "st1b z15.b, p0, [%[p], x8]\n"
+ "st1b z16.b, p0, [%[q], x8]\n"
+
+ "add x8, x3, x3\n"
+ "add x5, x5, x8, lsl #1\n"
+ "cmp x5, %[bytes]\n"
+ "blt 0b\n"
+ :
+ : [dptr] "r" (dptr), [z0] "r" (z0), [bytes] "r" (bytes),
+ [p] "r" (p), [q] "r" (q)
+ : "memory", "p0", "x3", "x4", "x5", "x6", "x7", "x8",
+ "z0", "z1", "z2", "z3", "z4",
+ "z5", "z6", "z7", "z8",
+ "z10", "z11", "z12", "z13",
+ "z15", "z16", "z17", "z18"
+ );
+}
+
+static void raid6_sve4_xor_syndrome_real(int disks, int start, int stop,
+ unsigned long bytes, void **ptrs)
+{
+ u8 **dptr = (u8 **)ptrs;
+ u8 *p, *q;
+ int z0 = stop;
+
+ p = dptr[disks - 2];
+ q = dptr[disks - 1];
+
+ asm volatile(
+ ".arch armv8.2-a+sve\n"
+ "ptrue p0.b\n"
+ "cntb x3\n"
+ "mov w4, #0x1d\n"
+ "dup z4.b, w4\n"
+ "mov x5, #0\n"
+
+ "0:\n"
+ "ldr x6, [%[dptr], %[z0], lsl #3]\n"
+ "ld1b z1.b, p0/z, [x6, x5]\n"
+ "add x8, x5, x3\n"
+ "ld1b z6.b, p0/z, [x6, x8]\n"
+ "add x8, x8, x3\n"
+ "ld1b z11.b, p0/z, [x6, x8]\n"
+ "add x8, x8, x3\n"
+ "ld1b z16.b, p0/z, [x6, x8]\n"
+
+ "ld1b z0.b, p0/z, [%[p], x5]\n"
+ "add x8, x5, x3\n"
+ "ld1b z5.b, p0/z, [%[p], x8]\n"
+ "add x8, x8, x3\n"
+ "ld1b z10.b, p0/z, [%[p], x8]\n"
+ "add x8, x8, x3\n"
+ "ld1b z15.b, p0/z, [%[p], x8]\n"
+
+ "eor z0.d, z0.d, z1.d\n"
+ "eor z5.d, z5.d, z6.d\n"
+ "eor z10.d, z10.d, z11.d\n"
+ "eor z15.d, z15.d, z16.d\n"
+
+ "mov w7, %w[z0]\n"
+ "sub w7, w7, #1\n"
+
+ "1:\n"
+ "cmp w7, %w[start]\n"
+ "blt 2f\n"
+
+ // software pipelining: load data early
+ "sxtw x8, w7\n"
+ "ldr x6, [%[dptr], x8, lsl #3]\n"
+ "ld1b z2.b, p0/z, [x6, x5]\n"
+ "add x8, x5, x3\n"
+ "ld1b z7.b, p0/z, [x6, x8]\n"
+ "add x8, x8, x3\n"
+ "ld1b z12.b, p0/z, [x6, x8]\n"
+ "add x8, x8, x3\n"
+ "ld1b z17.b, p0/z, [x6, x8]\n"
+
+ // math block 1
+ "mov z3.d, z1.d\n"
+ "asr z3.b, p0/m, z3.b, #7\n"
+ "lsl z1.b, p0/m, z1.b, #1\n"
+ "and z3.d, z3.d, z4.d\n"
+ "eor z1.d, z1.d, z3.d\n"
+ "eor z1.d, z1.d, z2.d\n"
+ "eor z0.d, z0.d, z2.d\n"
+
+ // math block 2
+ "mov z8.d, z6.d\n"
+ "asr z8.b, p0/m, z8.b, #7\n"
+ "lsl z6.b, p0/m, z6.b, #1\n"
+ "and z8.d, z8.d, z4.d\n"
+ "eor z6.d, z6.d, z8.d\n"
+ "eor z6.d, z6.d, z7.d\n"
+ "eor z5.d, z5.d, z7.d\n"
+
+ // math block 3
+ "mov z13.d, z11.d\n"
+ "asr z13.b, p0/m, z13.b, #7\n"
+ "lsl z11.b, p0/m, z11.b, #1\n"
+ "and z13.d, z13.d, z4.d\n"
+ "eor z11.d, z11.d, z13.d\n"
+ "eor z11.d, z11.d, z12.d\n"
+ "eor z10.d, z10.d, z12.d\n"
+
+ // math block 4
+ "mov z18.d, z16.d\n"
+ "asr z18.b, p0/m, z18.b, #7\n"
+ "lsl z16.b, p0/m, z16.b, #1\n"
+ "and z18.d, z18.d, z4.d\n"
+ "eor z16.d, z16.d, z18.d\n"
+ "eor z16.d, z16.d, z17.d\n"
+ "eor z15.d, z15.d, z17.d\n"
+
+ "sub w7, w7, #1\n"
+ "b 1b\n"
+ "2:\n"
+
+ "mov w7, %w[start]\n"
+ "sub w7, w7, #1\n"
+ "3:\n"
+ "cmp w7, #0\n"
+ "blt 4f\n"
+
+ // math block 1
+ "mov z3.d, z1.d\n"
+ "asr z3.b, p0/m, z3.b, #7\n"
+ "lsl z1.b, p0/m, z1.b, #1\n"
+ "and z3.d, z3.d, z4.d\n"
+ "eor z1.d, z1.d, z3.d\n"
+
+ // math block 2
+ "mov z8.d, z6.d\n"
+ "asr z8.b, p0/m, z8.b, #7\n"
+ "lsl z6.b, p0/m, z6.b, #1\n"
+ "and z8.d, z8.d, z4.d\n"
+ "eor z6.d, z6.d, z8.d\n"
+
+ // math block 3
+ "mov z13.d, z11.d\n"
+ "asr z13.b, p0/m, z13.b, #7\n"
+ "lsl z11.b, p0/m, z11.b, #1\n"
+ "and z13.d, z13.d, z4.d\n"
+ "eor z11.d, z11.d, z13.d\n"
+
+ // math block 4
+ "mov z18.d, z16.d\n"
+ "asr z18.b, p0/m, z18.b, #7\n"
+ "lsl z16.b, p0/m, z16.b, #1\n"
+ "and z18.d, z18.d, z4.d\n"
+ "eor z16.d, z16.d, z18.d\n"
+
+ "sub w7, w7, #1\n"
+ "b 3b\n"
+ "4:\n"
+
+ // Load q and XOR
+ "ld1b z2.b, p0/z, [%[q], x5]\n"
+ "add x8, x5, x3\n"
+ "ld1b z7.b, p0/z, [%[q], x8]\n"
+ "add x8, x8, x3\n"
+ "ld1b z12.b, p0/z, [%[q], x8]\n"
+ "add x8, x8, x3\n"
+ "ld1b z17.b, p0/z, [%[q], x8]\n"
+
+ "eor z1.d, z1.d, z2.d\n"
+ "eor z6.d, z6.d, z7.d\n"
+ "eor z11.d, z11.d, z12.d\n"
+ "eor z16.d, z16.d, z17.d\n"
+
+ // Store results
+ "st1b z0.b, p0, [%[p], x5]\n"
+ "st1b z1.b, p0, [%[q], x5]\n"
+ "add x8, x5, x3\n"
+ "st1b z5.b, p0, [%[p], x8]\n"
+ "st1b z6.b, p0, [%[q], x8]\n"
+ "add x8, x8, x3\n"
+ "st1b z10.b, p0, [%[p], x8]\n"
+ "st1b z11.b, p0, [%[q], x8]\n"
+ "add x8, x8, x3\n"
+ "st1b z15.b, p0, [%[p], x8]\n"
+ "st1b z16.b, p0, [%[q], x8]\n"
+
+ "add x8, x3, x3\n"
+ "add x5, x5, x8, lsl #1\n"
+ "cmp x5, %[bytes]\n"
+ "blt 0b\n"
+ :
+ : [dptr] "r" (dptr), [z0] "r" (z0), [bytes] "r" (bytes),
+ [p] "r" (p), [q] "r" (q), [start] "r" (start)
+ : "memory", "p0", "x3", "x4", "x5", "x6", "x7", "x8",
+ "z0", "z1", "z2", "z3", "z4",
+ "z5", "z6", "z7", "z8",
+ "z10", "z11", "z12", "z13",
+ "z15", "z16", "z17", "z18"
+ );
+}
+
+#define RAID6_SVE_WRAPPER(_n) \
+ static void raid6_sve ## _n ## _gen_syndrome(int disks, \
+ size_t bytes, void **ptrs) \
+ { \
+ scoped_ksimd() \
+ raid6_sve ## _n ## _gen_syndrome_real(disks, \
+ (unsigned long)bytes, ptrs); \
+ } \
+ static void raid6_sve ## _n ## _xor_syndrome(int disks, \
+ int start, int stop, \
+ size_t bytes, void **ptrs) \
+ { \
+ scoped_ksimd() \
+ raid6_sve ## _n ## _xor_syndrome_real(disks, \
+ start, stop, (unsigned long)bytes, ptrs);\
+ } \
+ struct raid6_calls const raid6_svex ## _n = { \
+ raid6_sve ## _n ## _gen_syndrome, \
+ raid6_sve ## _n ## _xor_syndrome, \
+ raid6_have_sve, \
+ "svex" #_n, \
+ 0 \
+ }
+
+static int raid6_have_sve(void)
+{
+ return system_supports_sve();
+}
+
+RAID6_SVE_WRAPPER(1);
+RAID6_SVE_WRAPPER(2);
+RAID6_SVE_WRAPPER(4);
--
2.43.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