* Re: [PATCH 2/2] lib/raid6: use kmalloc() in raid6_select_algo()
From: David Laight @ 2026-05-20 13:06 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: Song Liu, Yu Kuai, Li Nan, Xiao Ni, linux-kernel, linux-mm,
linux-raid
In-Reply-To: <20260520-lib-v1-2-cb3045bef2d8@kernel.org>
On Wed, 20 May 2026 11:17:52 +0300
"Mike Rapoport (Microsoft)" <rppt@kernel.org> wrote:
> raid6_select_algo() allocates an order 3 (8 pages) buffer that is used
> as a scratch area for selection of the best algorithm.
Should this code really be using a 4k buffer rather than a PAGE_SIZE one?
-- David
>
> For such large allocations kmalloc() would fall back to alloc_pages() but
> still kmalloc() is a better API as it does not require unnecessary
> castings and may provide more debugging possibilities.
>
> Replace __get_free_pages() call with kmalloc().
>
> Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
> Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
> ---
> lib/raid6/algos.c | 6 +++---
> 1 file changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
> index 799e0e5eac26..89e627c62e30 100644
> --- a/lib/raid6/algos.c
> +++ b/lib/raid6/algos.c
> @@ -12,6 +12,7 @@
> */
>
> #include <linux/raid/pq.h>
> +#include <linux/slab.h>
> #ifndef __KERNEL__
> #include <sys/mman.h>
> #include <stdio.h>
> @@ -129,7 +130,6 @@ const struct raid6_recov_calls *const raid6_recov_algos[] = {
> #endif
>
> #define RAID6_TEST_DISKS 8
> -#define RAID6_TEST_DISKS_ORDER 3
>
> static inline const struct raid6_recov_calls *raid6_choose_recov(void)
> {
> @@ -250,7 +250,7 @@ int __init raid6_select_algo(void)
> int i, cycle;
>
> /* prepare the buffer and fill it circularly with gfmul table */
> - disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER);
> + disk_ptr = kmalloc(PAGE_SIZE * RAID6_TEST_DISKS, GFP_KERNEL);
> if (!disk_ptr) {
> pr_err("raid6: Yikes! No memory available.\n");
> return -ENOMEM;
> @@ -275,7 +275,7 @@ int __init raid6_select_algo(void)
> /* select raid recover functions */
> rec_best = raid6_choose_recov();
>
> - free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
> + kfree(disk_ptr);
>
> return gen_best && rec_best ? 0 : -EINVAL;
> }
>
^ permalink raw reply
* Re: [PATCH 1/2] lib/raid: use kmalloc() in calibrate_xor_blocks()
From: David Laight @ 2026-05-20 13:00 UTC (permalink / raw)
To: Mike Rapoport (Microsoft)
Cc: Song Liu, Yu Kuai, Li Nan, Xiao Ni, linux-kernel, linux-mm,
linux-raid
In-Reply-To: <20260520-lib-v1-1-cb3045bef2d8@kernel.org>
On Wed, 20 May 2026 11:17:51 +0300
"Mike Rapoport (Microsoft)" <rppt@kernel.org> wrote:
> The xor benchmark allocates an order 2 (4 pages) scratch buffer that is
> used purely as a CPU-only XOR working area.
>
> For such large allocations kmalloc() would fall back to alloc_pages() but
> still kmalloc() is a better API as it does not require unnecessary
> castings and may provide more debugging possibilities.
>
> Replace __get_free_pages() call with kmalloc().
You might want to use kvalloc() here.
It is less likely to fail.
-- David
>
> Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
> Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
> ---
> lib/raid/xor/xor-core.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/lib/raid/xor/xor-core.c b/lib/raid/xor/xor-core.c
> index bd4e6e434418..50931fbf0324 100644
> --- a/lib/raid/xor/xor-core.c
> +++ b/lib/raid/xor/xor-core.c
> @@ -8,6 +8,7 @@
>
> #include <linux/module.h>
> #include <linux/gfp.h>
> +#include <linux/slab.h>
> #include <linux/raid/xor.h>
> #include <linux/jiffies.h>
> #include <linux/preempt.h>
> @@ -114,7 +115,7 @@ static int __init calibrate_xor_blocks(void)
> if (forced_template)
> return 0;
>
> - b1 = (void *) __get_free_pages(GFP_KERNEL, 2);
> + b1 = kmalloc(PAGE_SIZE * 4, GFP_KERNEL);
> if (!b1) {
> pr_warn("xor: Yikes! No memory available.\n");
> return -ENOMEM;
> @@ -132,7 +133,7 @@ static int __init calibrate_xor_blocks(void)
> pr_info("xor: using function: %s (%d MB/sec)\n",
> fastest->name, fastest->speed);
>
> - free_pages((unsigned long)b1, 2);
> + kfree(b1);
> return 0;
> }
>
>
^ permalink raw reply
* Re: [PATCH v2] md: skip redundant raid_disks update when value is unchanged
From: Yu Kuai @ 2026-05-20 12:45 UTC (permalink / raw)
To: Abd-Alrhman Masalkhi, song, yukuai; +Cc: linux-raid, linux-kernel
In-Reply-To: <20260428130524.448063-1-abd.masalkhi@gmail.com>
在 2026/4/28 21:05, Abd-Alrhman Masalkhi 写道:
> Calling update_raid_disks() with the same value as the current one
> can trigger unnecessary work. For example, RAID1 will reallocate
> resources such as the mempool for r1bio.
>
> Signed-off-by: Abd-Alrhman Masalkhi<abd.masalkhi@gmail.com>
> ---
> drivers/md/md.c | 7 ++++---
> 1 file changed, 4 insertions(+), 3 deletions(-)
Applied to md-7.2
--
Thansk,
Kuai
^ permalink raw reply
* Re: [PATCH v2] dm-raid: only requeue bios when dm is suspending.
From: Yu Kuai @ 2026-05-20 12:10 UTC (permalink / raw)
To: Benjamin Marzinski, Yu Kuai, Song Liu
Cc: linux-raid, dm-devel, Yang Xiuwei, Xiao Ni, Nigel Croxon
In-Reply-To: <20260428232010.2785514-1-bmarzins@redhat.com>
在 2026/4/29 7:20, Benjamin Marzinski 写道:
> returning DM_MAPIO_REQUEUE from the target map() function only requeues
> the bio during noflush suspends. During regular operations or during
> flushing suspends, it fails the bio. Failing the bio during flushing
> suspends is the correct behavior here. We cannot handle the bio, and we
> cannot suspends while it is outstanding. But during normal operations,
> we should not push the bio back to dm. Instead, wait for the reshape
> to be resumed.
>
> Signed-off-by: Benjamin Marzinski<bmarzins@redhat.com>
> ---
>
> Changes from v1:
> - Track the dm device's suspending state in mddev->flags instead of
> adding a new integer to mddev.
>
> drivers/md/dm-raid.c | 6 ++++++
> drivers/md/md.h | 2 ++
> drivers/md/raid5.c | 7 +++++--
> 3 files changed, 13 insertions(+), 2 deletions(-)
Applied to md-7.2
--
Thansk,
Kuai
^ permalink raw reply
* Re: [PATCH V3 2/3] md: propagate BLK_FEAT_PCI_P2PDMA from member devices to RAID device
From: Yu Kuai @ 2026-05-20 11:56 UTC (permalink / raw)
To: Chaitanya Kulkarni, song, yukuai, linan122, kbusch, axboe, hch,
sagi
Cc: linux-raid, linux-nvme, kmodukuri
In-Reply-To: <20260416212633.72650-3-kch@nvidia.com>
在 2026/4/17 5:26, Chaitanya Kulkarni 写道:
> From: Kiran Kumar Modukuri<kmodukuri@nvidia.com>
>
> MD RAID does not propagate BLK_FEAT_PCI_P2PDMA from member devices to
> the RAID device, preventing peer-to-peer DMA through the RAID layer even
> when all underlying devices support it.
>
> Enable BLK_FEAT_PCI_P2PDMA unconditionally in raid0, raid1 and raid10
> personalities during queue limits setup. blk_stack_limits() clears it
> automatically if any member device lacks support, consistent with how
> BLK_FEAT_NOWAIT and BLK_FEAT_POLL are handled in the block core.
>
> Parity RAID personalities (raid4/5/6) are excluded because they require
> CPU access to data pages for parity computation, which is incompatible
> with P2P mappings.
>
> Tested with RAID0/1/10 arrays containing multiple NVMe devices with
> P2PDMA support, confirming that peer-to-peer transfers work correctly
> through the RAID layer.
>
> Signed-off-by: Kiran Kumar Modukuri<kmodukuri@nvidia.com>
> Signed-off-by: Chaitanya Kulkarni<kch@nvidia.com>
> ---
> drivers/md/raid0.c | 1 +
> drivers/md/raid1.c | 1 +
> drivers/md/raid10.c | 1 +
> 3 files changed, 3 insertions(+)
Reviewed-by: Yu Kuai <yukuai@fygo.io>
--
Thansk,
Kuai
^ permalink raw reply
* Re: [PATCH] md/raid10: fix divide-by-zero in setup_geo() with zero far_copies
From: Yu Kuai @ 2026-05-20 11:52 UTC (permalink / raw)
To: Yuhao Jiang, yukuai
Cc: Junrui Luo, Song Liu, Li Nan, NeilBrown, Jonathan Brassow,
linux-raid, linux-kernel, stable
In-Reply-To: <CAHYQsXQhTn905RGCrw-qeb--VHsRGR2KEWm5X0ZJEW+krTJaNA@mail.gmail.com>
Hi,
在 2026/4/28 16:37, Yuhao Jiang 写道:
> Hi Kuai,
>
> Looks like different maintainers have different rules. :(
> Can you send me the patchwork resource?
Usually just a link to lore url is enough.
>
> Thanks.
>
> On Tue, Apr 28, 2026 at 4:32 PM Yu Kuai <yukuai@fnnas.com> wrote:
>> Hi,
>>
>> 在 2026/4/19 13:59, Yuhao Jiang 写道:
>>> Hi Kuai,
>>>
>>> This report was reported by me, so Junrui added me as Reported-by.
>> This is fine, however, please do not add downstream reported-by tag.
>> If you want to add the reported-by tag, please report the problem to
>> patchwork first. :)
>>
>>> Thanks,
>>>
>>> On Sun, Apr 19, 2026 at 12:43 AM Yu Kuai <yukuai@fnnas.com> wrote:
>>>
>>> Hi,
>>>
>>> 在 2026/4/16 11:39, Junrui Luo 写道:
>>> > setup_geo() extracts near_copies (nc) and far_copies (fc) from the
>>> > user-provided layout parameter without checking for zero. When fc=0
>>> > with the "improved" far set layout selected, 'geo->far_set_size =
>>> > disks / fc' triggers a divide-by-zero.
>>> >
>>> > Validate nc and fc immediately after extraction, returning -1 if
>>> > either is zero.
>>> >
>>> > Fixes: 475901aff158 ("MD RAID10: Improve redundancy for 'far'
>>> and 'offset' algorithms (part 1)")
>>> > Reported-by: Yuhao Jiang<danisjiang@gmail.com>
>>>
>>> So again I can't find a report, and Reported-by usually should be
>>> followed
>>> by a Closes link to the original report.
>>>
>>> Applied with Reported-by tag removed.
>>>
>>> > Cc:stable@vger.kernel.org <mailto:Cc%3Astable@vger.kernel.org>
>>> > Signed-off-by: Junrui Luo<moonafterrain@outlook.com>
>>> > ---
>>> > drivers/md/raid10.c | 2 ++
>>> > 1 file changed, 2 insertions(+)
>>>
>>> --
>>> Thansk,
>>> Kuai
>>>
>>>
>>>
>>> --
>>> Yuhao Jiang
>> --
>> Thansk,
>> Kuai
>
>
--
Thansk,
Kuai
^ permalink raw reply
* Re: [PATCH RESEND] MAINTAINERS: Update Li Nan's E-mail address
From: Yu Kuai @ 2026-05-20 11:32 UTC (permalink / raw)
To: Li Nan, song, yukuai
Cc: zhangtonghao, linux-kernel, linux-raid, xiao, magiclinan
In-Reply-To: <tencent_8F8173BEDF20E98550D5429DF802F34A7108@qq.com>
在 2026/5/8 17:55, Li Nan 写道:
> From: Li Nan <magiclinan@didiglobal.com>
>
> Change to my new email address on didiglobal.com.
>
> Signed-off-by: Li Nan <magiclinan@didiglobal.com>
> ---
> add cc linux-raid
>
> MAINTAINERS | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 06c00e40999f..96702de58cc1 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -24763,7 +24763,7 @@ F: include/linux/property.h
> SOFTWARE RAID (Multiple Disks) SUPPORT
> M: Song Liu <song@kernel.org>
> M: Yu Kuai <yukuai@fnnas.com>
> -R: Li Nan <linan122@huawei.com>
> +R: Li Nan <magiclinan@didiglobal.com>
> R: Xiao Ni <xiao@kernel.org>
> L: linux-raid@vger.kernel.org
> S: Supported
Applied
--
Thansk,
Kuai
^ permalink raw reply
* [PATCH] MAINTAINERS: update Yu Kuai's email address
From: Yu Kuai @ 2026-05-20 11:26 UTC (permalink / raw)
To: linux-raid; +Cc: yukuai, linan122, xiao, linux-kernel
From: Yu Kuai <yukuai@fygo.io>
Update Yu Kuai's maintainer entries to use the new fygo.io address.
Signed-off-by: Yu Kuai <yukuai@fygo.io>
---
MAINTAINERS | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 882214b0e7db..89940659ef40 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4478,7 +4478,7 @@ F: Documentation/filesystems/befs.rst
F: fs/befs/
BFQ I/O SCHEDULER
-M: Yu Kuai <yukuai@fnnas.com>
+M: Yu Kuai <yukuai@fygo.io>
L: linux-block@vger.kernel.org
S: Odd Fixes
F: Documentation/block/bfq-iosched.rst
@@ -24796,7 +24796,7 @@ F: include/linux/property.h
SOFTWARE RAID (Multiple Disks) SUPPORT
M: Song Liu <song@kernel.org>
-M: Yu Kuai <yukuai@fnnas.com>
+M: Yu Kuai <yukuai@fygo.io>
R: Li Nan <linan122@huawei.com>
R: Xiao Ni <xiao@kernel.org>
L: linux-raid@vger.kernel.org
--
2.51.0
^ permalink raw reply related
* [PATCH RFC] btrfs: disguise single-data-RAID56 as RAID1/RAID1C3
From: Qu Wenruo @ 2026-05-20 10:57 UTC (permalink / raw)
To: linux-btrfs; +Cc: hch, linux-raid
Recently kernel RAID56 lib is trying to remove the unexpected
single-data-RAID56 (2 disks RAID5 or 3 disk RAID5) support, meanwhile
btrfs still supports such setup, which means in the long run btrfs has
to handle such corner case by ourselves.
Thankfully single-data-RAID56 is really RAID1/RAID1C3, since data and
P/Q stripes all match each other, rotation also makes no difference.
This patch will disguise those single-data-RAID56 chunks as
RAID1/RAID1C3 chunks.
This is done at two timings:
- Chunk read
- Chunk allocation
And this disguise only affect on-disk chunk map, not affecting the
corresponding block groups, so the extra bits like RAID1C3 or RAID56
compatible flags will not be affected.
This method has a minimal impact on the fs, all other operations like
scrub and read-repair, are all based on the chunk map type, so the
disguise method will require no extra modification to those call sites.
Although there are still some locations that are checking against
block_group->flags, e.g. scrub. Those call sites will still get extra
limits assuming the bg is RAID56. But it should not cause any extra
problem.
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
Reason for RFC:
Although the current patch works fine, it is a little fragile, e.g. at
chunk allocation time, we must use the bg->flags, instead of the
in-memory chunk map type.
I'm wondering if we should introduce some dedicated member, e.g.
btrfs_chunk_map::on_disk_type to handle it.
---
fs/btrfs/volumes.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index 93a923e4ecaf..848eafa5fbf7 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -6000,6 +6000,26 @@ struct btrfs_chunk_map *btrfs_alloc_chunk_map(int num_stripes, gfp_t gfp)
return map;
}
+/*
+ * For single data stripe RAID56, it's completely RAID1/RAID1C3, as data,
+ * P/Q stripes all match each other, and rotation makes no difference anymore.
+ *
+ * So we still keep the on-disk chunk/bg type, but in memory we change the
+ * chunk map type directly to RAID1/RAID1C3
+ */
+static void change_single_data_raid56_map(struct btrfs_chunk_map *map)
+{
+ if ((map->type & BTRFS_BLOCK_GROUP_RAID56_MASK) == 0)
+ return;
+ if (nr_data_stripes(map) > 1)
+ return;
+ if (map->type & BTRFS_BLOCK_GROUP_RAID5)
+ map->type |= BTRFS_BLOCK_GROUP_RAID1;
+ else
+ map->type |= BTRFS_BLOCK_GROUP_RAID1C3;
+ map->type &= ~BTRFS_BLOCK_GROUP_RAID56_MASK;
+}
+
static struct btrfs_block_group *create_chunk(struct btrfs_trans_handle *trans,
struct alloc_chunk_ctl *ctl,
struct btrfs_device_info *devices_info)
@@ -6023,6 +6043,7 @@ static struct btrfs_block_group *create_chunk(struct btrfs_trans_handle *trans,
map->io_width = BTRFS_STRIPE_LEN;
map->sub_stripes = ctl->sub_stripes;
map->num_stripes = ctl->num_stripes;
+ change_single_data_raid56_map(map);
for (int i = 0; i < ctl->ndevs; i++) {
for (int j = 0; j < ctl->dev_stripes; j++) {
@@ -6201,7 +6222,7 @@ int btrfs_chunk_alloc_add_chunk_item(struct btrfs_trans_handle *trans,
btrfs_set_stack_chunk_length(chunk, bg->length);
btrfs_set_stack_chunk_owner(chunk, BTRFS_EXTENT_TREE_OBJECTID);
btrfs_set_stack_chunk_stripe_len(chunk, BTRFS_STRIPE_LEN);
- btrfs_set_stack_chunk_type(chunk, map->type);
+ btrfs_set_stack_chunk_type(chunk, bg->flags);
btrfs_set_stack_chunk_num_stripes(chunk, map->num_stripes);
btrfs_set_stack_chunk_io_align(chunk, BTRFS_STRIPE_LEN);
btrfs_set_stack_chunk_io_width(chunk, BTRFS_STRIPE_LEN);
@@ -7596,6 +7617,7 @@ static int read_one_chunk(struct btrfs_key *key, struct extent_buffer *leaf,
*/
map->sub_stripes = btrfs_raid_array[index].sub_stripes;
map->verified_stripes = 0;
+ change_single_data_raid56_map(map);
if (num_stripes > 0)
map->stripe_size = btrfs_calc_stripe_length(map);
--
2.54.0
^ permalink raw reply related
* Re: [PATCH 01/19] btrfs: require at least 4 devices for RAID 6
From: Qu Wenruo @ 2026-05-20 8:41 UTC (permalink / raw)
To: Christoph Hellwig, H. Peter Anvin
Cc: kreijack, David Sterba, Andrew Morton, Catalin Marinas,
Will Deacon, Ard Biesheuvel, Huacai Chen, WANG Xuerui,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy (CS GROUP), Paul Walmsley, Palmer Dabbelt,
Albert Ou, Alexandre Ghiti, Heiko Carstens, Vasily Gorbik,
Alexander Gordeev, Christian Borntraeger, Sven Schnelle,
Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
Herbert Xu, Dan Williams, Chris Mason, David Sterba,
Arnd Bergmann, Song Liu, Yu Kuai, Li Nan, linux-kernel,
linux-arm-kernel, loongarch, linuxppc-dev, linux-riscv,
linux-s390, linux-crypto, linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260518051207.GB9374@lst.de>
在 2026/5/18 14:42, Christoph Hellwig 写道:
> On Fri, May 15, 2026 at 12:59:34PM -0700, H. Peter Anvin wrote:
>> I don't think this is a good idea. Error out; it is the btrfs maintainers' job to ensure user data isn't lost.
>>
>> The RAID-6 code has *never* supported only 3 units, and if it ever worked for *any* of the implementations it was purely by accident. Speaking as the original author I should know; this was deliberate as in some cases the degenerate case (3) would have required extra trays in the code to no user benefit.
>>
>> I would not be surprised if the kernel crashed or corrupted the page cache in that case.
>
> It does, that's why I wanted to exclude it. Anyway, for the about to be
> resent version I'll drop this btrfs patch over the stated objection and
> will otherwise not change anything. This means the (IMHO hypothetical)
> users of this configuration will get a WARN_ON_ONCE triggered, but
> otherwise keep working (or rather not working) as before.
>
For the btrfs part, I believe I can get the current 2-disk-raid5 and
3-disk-raid6 to fallback to raid1 inside btrfs.
I hope the btrfs part can be finished and reach the next merge window,
but I'm not 100% sure.
What is the planned cycle to merge this raid5/6 cleanup?
Thanks,
Qu
^ permalink raw reply
* [PATCH 2/2] lib/raid6: use kmalloc() in raid6_select_algo()
From: Mike Rapoport (Microsoft) @ 2026-05-20 8:17 UTC (permalink / raw)
To: Song Liu, Yu Kuai, Li Nan, Xiao Ni
Cc: Mike Rapoport, linux-kernel, linux-mm, linux-raid
In-Reply-To: <20260520-lib-v1-0-cb3045bef2d8@kernel.org>
raid6_select_algo() allocates an order 3 (8 pages) buffer that is used
as a scratch area for selection of the best algorithm.
For such large allocations kmalloc() would fall back to alloc_pages() but
still kmalloc() is a better API as it does not require unnecessary
castings and may provide more debugging possibilities.
Replace __get_free_pages() call with kmalloc().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
lib/raid6/algos.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index 799e0e5eac26..89e627c62e30 100644
--- a/lib/raid6/algos.c
+++ b/lib/raid6/algos.c
@@ -12,6 +12,7 @@
*/
#include <linux/raid/pq.h>
+#include <linux/slab.h>
#ifndef __KERNEL__
#include <sys/mman.h>
#include <stdio.h>
@@ -129,7 +130,6 @@ const struct raid6_recov_calls *const raid6_recov_algos[] = {
#endif
#define RAID6_TEST_DISKS 8
-#define RAID6_TEST_DISKS_ORDER 3
static inline const struct raid6_recov_calls *raid6_choose_recov(void)
{
@@ -250,7 +250,7 @@ int __init raid6_select_algo(void)
int i, cycle;
/* prepare the buffer and fill it circularly with gfmul table */
- disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER);
+ disk_ptr = kmalloc(PAGE_SIZE * RAID6_TEST_DISKS, GFP_KERNEL);
if (!disk_ptr) {
pr_err("raid6: Yikes! No memory available.\n");
return -ENOMEM;
@@ -275,7 +275,7 @@ int __init raid6_select_algo(void)
/* select raid recover functions */
rec_best = raid6_choose_recov();
- free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
+ kfree(disk_ptr);
return gen_best && rec_best ? 0 : -EINVAL;
}
--
2.53.0
^ permalink raw reply related
* [PATCH 1/2] lib/raid: use kmalloc() in calibrate_xor_blocks()
From: Mike Rapoport (Microsoft) @ 2026-05-20 8:17 UTC (permalink / raw)
To: Song Liu, Yu Kuai, Li Nan, Xiao Ni
Cc: Mike Rapoport, linux-kernel, linux-mm, linux-raid
In-Reply-To: <20260520-lib-v1-0-cb3045bef2d8@kernel.org>
The xor benchmark allocates an order 2 (4 pages) scratch buffer that is
used purely as a CPU-only XOR working area.
For such large allocations kmalloc() would fall back to alloc_pages() but
still kmalloc() is a better API as it does not require unnecessary
castings and may provide more debugging possibilities.
Replace __get_free_pages() call with kmalloc().
Link: https://lore.kernel.org/all/635405e4-9423-4a25-a6e7-e03c8ea0bcbe@redhat.com
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
lib/raid/xor/xor-core.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/lib/raid/xor/xor-core.c b/lib/raid/xor/xor-core.c
index bd4e6e434418..50931fbf0324 100644
--- a/lib/raid/xor/xor-core.c
+++ b/lib/raid/xor/xor-core.c
@@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/gfp.h>
+#include <linux/slab.h>
#include <linux/raid/xor.h>
#include <linux/jiffies.h>
#include <linux/preempt.h>
@@ -114,7 +115,7 @@ static int __init calibrate_xor_blocks(void)
if (forced_template)
return 0;
- b1 = (void *) __get_free_pages(GFP_KERNEL, 2);
+ b1 = kmalloc(PAGE_SIZE * 4, GFP_KERNEL);
if (!b1) {
pr_warn("xor: Yikes! No memory available.\n");
return -ENOMEM;
@@ -132,7 +133,7 @@ static int __init calibrate_xor_blocks(void)
pr_info("xor: using function: %s (%d MB/sec)\n",
fastest->name, fastest->speed);
- free_pages((unsigned long)b1, 2);
+ kfree(b1);
return 0;
}
--
2.53.0
^ permalink raw reply related
* [PATCH 0/2] lib/raid: replace __get_free_pages() call with kmalloc()
From: Mike Rapoport (Microsoft) @ 2026-05-20 8:17 UTC (permalink / raw)
To: Song Liu, Yu Kuai, Li Nan, Xiao Ni
Cc: Mike Rapoport, linux-kernel, linux-mm, linux-raid
This is a (tiny) part of larger work of replacing page allocator calls
with kmalloc:
Also in git:
https://git.kernel.org/pub/scm/linux/kernel/git/rppt/linux.git gfp-to-kmalloc/lib
Signed-off-by: Mike Rapoport (Microsoft) <rppt@kernel.org>
---
Mike Rapoport (Microsoft) (2):
lib/raid: use kmalloc() in calibrate_xor_blocks()
lib/raid6: use kmalloc() in raid6_select_algo()
lib/raid/xor/xor-core.c | 5 +++--
lib/raid6/algos.c | 6 +++---
2 files changed, 6 insertions(+), 5 deletions(-)
---
base-commit: 5d6919055dec134de3c40167a490f33c74c12581
change-id: 20260520-lib-8afb92134307
Best regards,
--
Sincerely yours,
Mike.
^ permalink raw reply
* Re: [PATCH V4 0/3] md/nvme: Enable PCI P2PDMA support for RAID0 and NVMe Multipath
From: Chaitanya Kulkarni @ 2026-05-20 0:11 UTC (permalink / raw)
To: axboe@kernel.dk
Cc: song@kernel.org, yukuai@fnnas.com, Chaitanya Kulkarni,
Christoph Hellwig, linan122@huawei.com, kbusch@kernel.org,
sagi@grimberg.me, linux-block@vger.kernel.org,
linux-raid@vger.kernel.org, linux-nvme@lists.infradead.org,
Kiran Modukuri
In-Reply-To: <20260515043535.GB3756@lst.de>
Jens,
On 5/14/26 9:35 PM, Christoph Hellwig wrote:
> Still looks good to me as per the reviews.
>
If there no objection, can we merge this ?
-Chaitanya
^ permalink raw reply
* --bitmap=lockless across server reboot
From: Anton Gavriliuk @ 2026-05-19 17:28 UTC (permalink / raw)
To: linux-raid
Hi
Does md-raid lockless bitmap support server's reboot ?
I don't see md-raid6 created with --bitmap=lockless after reboot.
Anton
^ permalink raw reply
* Re: cleanup the RAID6 P/Q library v3
From: Christoph Hellwig @ 2026-05-19 8:24 UTC (permalink / raw)
To: Andrew Morton
Cc: Christoph Hellwig, Catalin Marinas, Will Deacon, Ard Biesheuvel,
Huacai Chen, WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260518141205.c100f76eec5f58e78bbbf7af@linux-foundation.org>
On Mon, May 18, 2026 at 02:12:05PM -0700, Andrew Morton wrote:
> Cool, I'll add this to mm.git's mm-nonmm-unstable branch for some
> linux-next testing.
>
> AI review found quite a lot to talk about:
> https://sashiko.dev/#/patchset/20260518051804.462141-1-hch@lst.de
Not a lot of it is very useful, though:
raid6: turn the userspace test harness into a kunit test
- complains about basically adding need_resched, which we've decided
we won't do now that we have lazy preempt. This is probably going
to come up in lots of places because of the old training data
raid6: use named initializers for struct raid6_calls
- whining about keeping totally pointless comments
raid6: warn when using less than four devices
- complains about warning for btrfs which is clearly documented as the
outcome in the commit log
- and also complaining that the enforcement isn't hard enough, but the
WARN_ON is the best we can do here
raid6: rework registration of optimized algorithms
- less registration causing less kunit coverage: that's intentional
as it keeps testing time down and similar to other arch optimized
tests in crc and crypto code. It also doesn't really reduce
coverage as before this series there was none.
raid6: use static_call for gen_syndrom and xor_syndrom
- doesn't seem to know that bool fails when an initcall fails
raid6_kunit: use KUNIT_CASE_PARAM
- whining about the code style. I don't really like it either,
but the kunit case stuff is a mess
There are a few somewhat useful things, though.
raid6: hide internals
- yes, the -I is duplicate and should be fixed
raid6: rework registration of optimized algorithms
- avx2 instead of avx512 is probably the right thing for no
benchmarking, but if it was intentional (it wasn't), that should
be document. So I'll just switch back to the previous version to
keep the state of the art
^ permalink raw reply
* Re: [PATCH v2 3/3] md/raid1,raid10: fix bio accounting for split md cloned bios
From: Xiao Ni @ 2026-05-19 8:18 UTC (permalink / raw)
To: Abd-Alrhman Masalkhi
Cc: song, yukuai, xni, neilb, shli, linux-raid, linux-kernel
In-Reply-To: <20260501114652.590037-4-abd.masalkhi@gmail.com>
On Fri, May 1, 2026 at 7:48 PM Abd-Alrhman Masalkhi
<abd.masalkhi@gmail.com> wrote:
>
> Use md_cloned_bio() to control bio accounting instead of relying
> on r1bio_existed in raid1 or the io_accounting flag in raid10.
>
> The previous logic does not reliably reflect whether a bio is an
> md cloned bio. When a failed bio is split and resubmitted via
> bio_submit_split_bioset() on the error path, this can lead to either
> double accounting for md cloned bios, or missing accounting for bios
> returned from bio_submit_split_bioset()
>
> Fix this by using md_cloned_bio() to detect md cloned bios and
> skip accounting accordingly.
>
> Fixes: bb2a9acefaf9 ("md/raid1: switch to use md_account_bio() for io accounting")
> Fixes: 820455238366 ("md/raid10: switch to use md_account_bio() for io accounting")
> Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
> ---
> This patch depends on patch 1.
>
> Changes in v2:
> - New patch.
> ---
> drivers/md/raid1.c | 2 +-
> drivers/md/raid10.c | 8 ++++----
> 2 files changed, 5 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index c52ecd38c163..dfaf34141325 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -1396,7 +1396,7 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
> }
>
> r1_bio->read_disk = rdisk;
> - if (!r1bio_existed) {
> + if (likely(!md_cloned_bio(mddev, bio))) {
> md_account_bio(mddev, &bio);
> r1_bio->master_bio = bio;
> }
> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index 8c6fc398260e..93af7bbc9005 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -1146,7 +1146,7 @@ static bool regular_request_wait(struct mddev *mddev, struct r10conf *conf,
> }
>
> static void raid10_read_request(struct mddev *mddev, struct bio *bio,
> - struct r10bio *r10_bio, bool io_accounting)
> + struct r10bio *r10_bio)
> {
> struct r10conf *conf = mddev->private;
> struct bio *read_bio;
> @@ -1226,7 +1226,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
> }
> slot = r10_bio->read_slot;
>
> - if (io_accounting) {
> + if (likely(!md_cloned_bio(mddev, bio))) {
> md_account_bio(mddev, &bio);
> r10_bio->master_bio = bio;
> }
> @@ -1552,7 +1552,7 @@ static void __make_request(struct mddev *mddev, struct bio *bio, int sectors)
> conf->geo.raid_disks);
>
> if (bio_data_dir(bio) == READ)
> - raid10_read_request(mddev, bio, r10_bio, true);
> + raid10_read_request(mddev, bio, r10_bio);
> else
> raid10_write_request(mddev, bio, r10_bio);
> }
> @@ -2872,7 +2872,7 @@ static void handle_read_error(struct mddev *mddev, struct r10bio *r10_bio)
>
> rdev_dec_pending(rdev, mddev);
> r10_bio->state = 0;
> - raid10_read_request(mddev, r10_bio->master_bio, r10_bio, false);
> + raid10_read_request(mddev, r10_bio->master_bio, r10_bio);
> /*
> * allow_barrier after re-submit to ensure no sync io
> * can be issued while regular io pending.
> --
> 2.43.0
>
>
This patch looks good to me.
Reviewed-by: Xiao Ni <xiao@kernel.org>
^ permalink raw reply
* Re: [PATCH v2 2/3] md/raid1,raid10: fix error-path detection with md_cloned_bio()
From: Xiao Ni @ 2026-05-19 8:11 UTC (permalink / raw)
To: Abd-Alrhman Masalkhi
Cc: song, yukuai, xni, neilb, shli, linux-raid, linux-kernel
In-Reply-To: <20260501114652.590037-3-abd.masalkhi@gmail.com>
On Fri, May 1, 2026 at 7:48 PM Abd-Alrhman Masalkhi
<abd.masalkhi@gmail.com> wrote:
>
> Detect the error path using md_cloned_bio() instead of relying
> on r1_bio in raid1 or r10_bio->read_slot in raid10, which may be
> NULL or -1 after splitting and resubmitting a failed bio.
>
> As a result, the error path may not be recognized and memory
> allocations can incorrectly use GFP_NOIO instead of
> (GFP_NOIO | __GFP_HIGH), which can lead to a deadlock under
> memory pressure.
>
> Fixes: 689389a06ce7 ("md/raid1: simplify handle_read_error().")
> Fixes: 545250f24809 ("md/raid10: simplify handle_read_error()")
> Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
> ---
> This patch depends on patch 1.
>
> Changes in v2:
> - New patch.
> ---
> drivers/md/raid1.c | 13 ++++++++++---
> drivers/md/raid10.c | 20 ++++++++++++++------
> 2 files changed, 24 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index cc9914bd15c1..c52ecd38c163 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -1321,11 +1321,18 @@ static void raid1_read_request(struct mddev *mddev, struct bio *bio,
> bool r1bio_existed = !!r1_bio;
>
> /*
> - * If r1_bio is set, we are blocking the raid1d thread
> - * so there is a tiny risk of deadlock. So ask for
> + * An md cloned bio indicates we are in the error path.
> + * This is more reliable than checking r1_bio, which might
> + * be NULL even in the error path if a failed bio was split.
> + */
> + bool err_path = md_cloned_bio(mddev, bio);
> +
> + /*
> + * If we are in the error path, we are blocking the raid1d
> + * thread so there is a tiny risk of deadlock. So ask for
> * emergency memory if needed.
> */
> - gfp_t gfp = r1_bio ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO;
> + gfp_t gfp = err_path ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO;
Hi
This patch looks good to me.
Reviewed-by: Xiao Ni <xiao@kernel.org>
>
> /*
> * Still need barrier for READ in case that whole
> diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
> index 3a591e60a144..8c6fc398260e 100644
> --- a/drivers/md/raid10.c
> +++ b/drivers/md/raid10.c
> @@ -1155,7 +1155,20 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
> char b[BDEVNAME_SIZE];
> int slot = r10_bio->read_slot;
> struct md_rdev *err_rdev = NULL;
> - gfp_t gfp = GFP_NOIO;
> +
> + /*
> + * An md cloned bio indicates we are in the error path.
> + * This is more reliable than checking slot, which might
> + * be -1 even in the error path if a failed bio was split.
> + */
> + bool err_path = md_cloned_bio(mddev, bio);
> +
> + /*
> + * If we are in the error path, we are blocking the raid10d
> + * thread so there is a tiny risk of deadlock. So ask for
> + * emergency memory if needed.
> + */
> + gfp_t gfp = err_path ? (GFP_NOIO | __GFP_HIGH) : GFP_NOIO;
>
> if (slot >= 0 && r10_bio->devs[slot].rdev) {
> /*
> @@ -1166,11 +1179,6 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
> * we lose the device name in error messages.
> */
> int disk;
> - /*
> - * As we are blocking raid10, it is a little safer to
> - * use __GFP_HIGH.
> - */
> - gfp = GFP_NOIO | __GFP_HIGH;
>
> disk = r10_bio->devs[slot].devnum;
> err_rdev = conf->mirrors[disk].rdev;
> --
> 2.43.0
>
>
^ permalink raw reply
* Re: [PATCH v2 1/3] md/raid1,raid10: fix deadlock in read error recovery path
From: Xiao Ni @ 2026-05-19 7:46 UTC (permalink / raw)
To: Abd-Alrhman Masalkhi
Cc: song, yukuai, xni, neilb, shli, linux-raid, linux-kernel
In-Reply-To: <20260501114652.590037-2-abd.masalkhi@gmail.com>
On Fri, May 1, 2026 at 7:47 PM Abd-Alrhman Masalkhi
<abd.masalkhi@gmail.com> wrote:
>
> raid1d and raid10d may resubmit a split md cloned bio while handling
> a read error. In this case, resubmitting the bio can lead to a deadlock
> if the array is suspended before md_handle_request() acquires an
> active_io reference via percpu_ref_tryget_live().
>
> Since the cloned bio already holds an active_io reference,
> trying to acquire another reference via percpu_ref_tryget_live()
> can lead to a deadlock while the array is suspended.
>
> Fix this by using percpu_ref_get() for md cloned bios.
>
> Fixes: bb2a9acefaf9 ("md/raid1: switch to use md_account_bio() for io accounting")
> Fixes: 820455238366 ("md/raid10: switch to use md_account_bio() for io accounting")
> Signed-off-by: Abd-Alrhman Masalkhi <abd.masalkhi@gmail.com>
> ---
> Changes in v2:
> - Use md_cloned_bio() consistently to detect cloned bios.
> - Recognize that raid10 has the same issue and fix it in this series
> - Allow splitting bios.
> - Handle md cloned bios explicitly in md_handle_request()
> - Link v1: https://lore.kernel.org/linux-raid/20260427103446.300378-1-abd.masalkhi@gmail.com/
>
> Please let me know if I should add a Suggested-by tag for Yu Kuai,
> as the solution approach was suggested during review.
>
> Link to Yu Kuai' email: https://lore.kernel.org/linux-raid/m2lde74dtw.fsf@gmail.com/T/#m714020a38b60fc5f84b9a24f0c46acbe5d7342d6
>
> Thanks
> Abd-alrhman
> ---
> drivers/md/md.c | 25 ++++++++++++++++---------
> drivers/md/md.h | 5 +++++
> 2 files changed, 21 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index e926aef9ec43..96db1e7850e9 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -396,17 +396,24 @@ static bool is_suspended(struct mddev *mddev, struct bio *bio)
> bool md_handle_request(struct mddev *mddev, struct bio *bio)
> {
> check_suspended:
> - if (is_suspended(mddev, bio)) {
> - /* Bail out if REQ_NOWAIT is set for the bio */
> - if (bio->bi_opf & REQ_NOWAIT) {
> - bio_wouldblock_error(bio);
> - return true;
> + if (unlikely(md_cloned_bio(mddev, bio))) {
> + /*
> + * This bio is an MD cloned bio and already holds an
> + * active_io reference, so percpu_ref_get() is safe here.
> + */
> + percpu_ref_get(&mddev->active_io);
> + } else {
> + if (is_suspended(mddev, bio)) {
> + /* Bail out if REQ_NOWAIT is set for the bio */
> + if (bio->bi_opf & REQ_NOWAIT) {
> + bio_wouldblock_error(bio);
> + return true;
> + }
> + wait_event(mddev->sb_wait, !is_suspended(mddev, bio));
> }
> - wait_event(mddev->sb_wait, !is_suspended(mddev, bio));
> + if (!percpu_ref_tryget_live(&mddev->active_io))
> + goto check_suspended;
> }
> - if (!percpu_ref_tryget_live(&mddev->active_io))
> - goto check_suspended;
> -
> if (!mddev->pers->make_request(mddev, bio)) {
> percpu_ref_put(&mddev->active_io);
> if (mddev_is_dm(mddev) && mddev->pers->prepare_suspend)
> diff --git a/drivers/md/md.h b/drivers/md/md.h
> index 3bfbee595156..e44074d30cf9 100644
> --- a/drivers/md/md.h
> +++ b/drivers/md/md.h
> @@ -1038,6 +1038,11 @@ void mddev_update_io_opt(struct mddev *mddev, unsigned int nr_stripes);
>
> extern const struct block_device_operations md_fops;
>
> +static inline bool md_cloned_bio(struct mddev *mddev, struct bio *bio)
> +{
> + return bio->bi_pool == &mddev->io_clone_set;
> +}
> +
> /*
> * MD devices can be used undeneath by DM, in which case ->gendisk is NULL.
> */
> --
> 2.43.0
>
>
This patch looks good to me.
Reviewed-by: Xiao Ni <xiao@kernel.org>
^ permalink raw reply
* Re: cleanup the RAID6 P/Q library v3
From: Andrew Morton @ 2026-05-18 21:12 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Catalin Marinas, Will Deacon, Ard Biesheuvel, Huacai Chen,
WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260518051804.462141-1-hch@lst.de>
On Mon, 18 May 2026 07:17:43 +0200 Christoph Hellwig <hch@lst.de> wrote:
> this series cleans up the RAID6 P/Q library to match the recent updates
> to the RAID 5 XOR library and other CRC/crypto libraries. This includes
> providing properly documented external interfaces, hiding the internals,
> using static_call instead of indirect calls and turning the user space
> test suite into an in-kernel kunit test which is also extended to
> improve coverage.
Cool, I'll add this to mm.git's mm-nonmm-unstable branch for some
linux-next testing.
AI review found quite a lot to talk about:
https://sashiko.dev/#/patchset/20260518051804.462141-1-hch@lst.de
^ permalink raw reply
* [PATCH] md/raid5: cleanup reshape stripes when too many devices fail
From: Chen Cheng @ 2026-05-18 12:34 UTC (permalink / raw)
To: Yu Kuai, linux-raid; +Cc: Chen Cheng, linux-kernel
From: Chen Cheng <chencheng@fnnas.com>
When a raid5/6 reshape is in progress and the array loses more than
max_degraded devices, raid5_error() sets MD_BROKEN and MD_RECOVERY_INTR,
but the reshape stripes reshape_request() handed out are never released.
The "s.failed > conf->max_degraded" branch of handle_stripe() calls
handle_failed_stripe() / handle_failed_sync() for user IO and resync,
but has no equivalent for the expand case, so three kinds of stripes
leak conf->reshape_stripes and mddev->recovery_active:
1. Destination stripes with skipped_disk == 0: STRIPE_EXPANDING +
STRIPE_EXPAND_READY set, but on a broken array the normal
completion at "s.expanded && !reconstruct_state && s.locked == 0"
may never fire.
2. Destination stripes with skipped_disk == 1: only STRIPE_EXPANDING
set, no STRIPE_HANDLE. They sit idle in the cache waiting for
source data that can no longer be read; handle_stripe() is never
called on them directly.
3. Source stripes (STRIPE_EXPAND_SOURCE) hit the failure branch but
the bit is never cleared and the destinations they feed are never
released.
md_do_sync() exits its main loop on MD_RECOVERY_INTR but then blocks
forever at
wait_event(mddev->recovery_wait,
!atomic_read(&mddev->recovery_active));
A concurrent "echo frozen > sync_action" then blocks in
stop_sync_thread() waiting for MD_RECOVERY_RUNNING to clear, and the
array becomes unstoppable without a reboot.
Reproducer:
DEVS=(/dev/sdb /dev/sdc /dev/sdd /dev/sde /dev/sdf)
for i in 0 1 2 3 4; do
s=$(blockdev --getsz ${DEVS[$i]})
dmsetup create dust$i --table "0 $s dust ${DEVS[$i]} 0 4096"
dmsetup message dust$i 0 quiet
done
mdadm -C /dev/md0 -e 1.2 -l 5 -n 4 -c 64 --assume-clean \
/dev/mapper/dust{0..3}
for b in $(seq 0 8191); do
dmsetup message dust0 0 addbadblock $b
dmsetup message dust1 0 addbadblock $b
done
mdadm --manage /dev/md0 --add /dev/mapper/dust4
mdadm --grow /dev/md0 -n 5 --backup-file=/tmp/grow.backup &
while [[ $(cat /sys/block/md0/md/sync_action) != reshape ]]; do
sleep 0.1
done
dmsetup message dust0 0 enable
dmsetup message dust1 0 enable
sleep 5
echo frozen > /sys/block/md0/md/sync_action # hangs forever
Before the fix, the two tasks deadlock against each other:
task:md0_reshape state:D
schedule
md_do_sync.cold+0x818/0xc25 # wait_event(recovery_wait,
md_thread # !recovery_active)
kthread
task:bash state:D
schedule
stop_sync_thread+0x1a3/0x350 # wait_event(resync_wait,
action_store # !MD_RECOVERY_RUNNING)
md_attr_store
kernfs_fop_write_iter
vfs_write
ksys_write
After the fix handle_stripe() releases the leaked reshape stripes via
the new handle_failed_reshape(), recovery_active drains to zero,
md_do_sync() prints
md/raid:md0: Cannot continue operation (2/5 failed).
md: md0: reshape interrupted.
clears MD_RECOVERY_RUNNING and returns; the "echo frozen" write
returns in <1s; "mdadm --stop /dev/md0" completes normally and no
task is left in D state.
Fix it by adding handle_failed_reshape(), called from handle_stripe()
when the failure branch fires on a reshape stripe. If sh is a
destination, the helper drops STRIPE_EXPANDING / STRIPE_EXPAND_READY,
decrements conf->reshape_stripes, wakes wait_for_reshape and calls
md_done_sync() to return the sectors reshape_request() accounted on
recovery_active. If sh is a source, the helper drops
STRIPE_EXPAND_SOURCE and walks sh's non-parity data disks using the
same raid5_compute_blocknr() / raid5_compute_sector() mapping
handle_stripe_expansion() uses to forward data, looks up each matching
destination with R5_GAS_NOBLOCK | R5_GAS_NOQUIESCE and applies the
destination cleanup to it.
Signed-off-by: Chen Cheng <chencheng@fnnas.com>
---
drivers/md/raid5.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 91 insertions(+)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 0d76e82f4506..f7d159b46a01 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -4594,10 +4594,96 @@ static void handle_stripe_expansion(struct r5conf *conf, struct stripe_head *sh)
}
/* done submitting copies, wait for them to complete */
async_tx_quiesce(&tx);
}
+/*
+ * handle_failed_reshape - drop reshape state when too many devices have failed
+ *
+ * Called from handle_stripe() in the "s.failed > conf->max_degraded" branch
+ * when sh is participating in a reshape. raid5_error() has set MD_BROKEN
+ * and MD_RECOVERY_INTR); The reshape stripes that reshape_request() handed out
+ * must be released, otherwise they leak conf->reshape_stripes and
+ * mddev->recovery_active, and md_do_sync() hangs forever at
+ * wait_event(mddev->recovery_wait, !atomic_read(&mddev->recovery_active)).
+ *
+ * Three kinds of stripes can reach this path:
+ *
+ * 1. Destination stripes with skipped_disk = 0 in reshape_request()
+ * - the new stripe maps entirely past the old array end, so its
+ * blocks are zero-filled in place without any source read.
+ * STRIPE_EXPANDING, STRIPE_EXPAND_READY and STRIPE_HANDLE are all set,
+ * handle_stripe() sees them with s.expanded == 1.
+ *
+ * 2. Destination stripes with skipped_disk = 1 - the new stripe
+ * overlaps existing data and still needs source blocks copied in by
+ * handle_stripe_expansion(). Only STRIPE_EXPANDING is set, *not*
+ * STRIPE_HANDLE, so they sit idle in the stripe cache until a successful
+ * source expand re-handles them. In the failure path no one ever does,
+ * so handle_stripe() will never see them on its own; they are cleaned up
+ * from the source side in step (b) below.
+ *
+ * 3. Source stripes (STRIPE_EXPAND_SOURCE) - reach handle_stripe() via the
+ * read-error path once the source members start returning EIO and
+ * raid5_error() marks them Faulty.
+ *
+ * Handling:
+ *
+ * (a) If STRIPE_EXPANDING is set on sh, clear it together with
+ * STRIPE_EXPAND_READY, atomic_dec conf->reshape_stripes, wake
+ * wait_for_reshape and md_done_sync(RAID5_STRIPE_SECTORS) to return
+ * the sectors reshape_request() accounted on recovery_active.
+ *
+ * (b) If STRIPE_EXPAND_SOURCE is set on sh, clear it and walk sh's
+ * non-parity disks the same way handle_stripe_expansion() does
+ * (raid5_compute_blocknr previous=1 -> raid5_compute_sector previous=0)
+ * to find each destination, look it up with
+ * R5_GAS_NOBLOCK | R5_GAS_NOQUIESCE and apply step (a) to it.
+ * A NULL lookup means the destination never contributed to
+ * reshape_stripes - nothing to release.
+ */
+static void handle_failed_reshape(struct r5conf *conf, struct stripe_head *sh)
+{
+ int i;
+
+ if (test_and_clear_bit(STRIPE_EXPANDING, &sh->state)) {
+ atomic_dec(&conf->reshape_stripes);
+ wake_up(&conf->wait_for_reshape);
+ md_done_sync(conf->mddev, RAID5_STRIPE_SECTORS(conf));
+ }
+
+ clear_bit(STRIPE_EXPAND_READY, &sh->state);
+
+ if (test_and_clear_bit(STRIPE_EXPAND_SOURCE, &sh->state)) {
+ for (i = 0; i < sh->disks; i++) {
+ int dd_idx;
+ struct stripe_head *sh2;
+ sector_t bn, sec;
+
+ if (i == sh->pd_idx)
+ continue;
+ if (conf->level == 6 && i == sh->qd_idx)
+ continue;
+
+ bn = raid5_compute_blocknr(sh, i, 1);
+ sec = raid5_compute_sector(conf, bn, 0, &dd_idx, NULL);
+ sh2 = raid5_get_active_stripe(conf, NULL, sec,
+ R5_GAS_NOBLOCK | R5_GAS_NOQUIESCE);
+ if (!sh2)
+ continue;
+ if (test_and_clear_bit(STRIPE_EXPANDING, &sh2->state)) {
+ atomic_dec(&conf->reshape_stripes);
+ wake_up(&conf->wait_for_reshape);
+ md_done_sync(conf->mddev,
+ RAID5_STRIPE_SECTORS(conf));
+ }
+ clear_bit(STRIPE_EXPAND_READY, &sh2->state);
+ raid5_release_stripe(sh2);
+ }
+ }
+}
+
static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
{
struct r5conf *conf = sh->raid_conf;
int disks = sh->disks;
struct r5dev *dev;
@@ -5001,10 +5087,15 @@ static void handle_stripe(struct stripe_head *sh)
break_stripe_batch_list(sh, 0);
if (s.to_read+s.to_write+s.written)
handle_failed_stripe(conf, sh, &s, disks);
if (s.syncing + s.replacing)
handle_failed_sync(conf, sh, &s);
+ if (s.expanding || s.expanded) {
+ handle_failed_reshape(conf, sh);
+ s.expanding = 0;
+ s.expanded = 0;
+ }
}
/* Now we check to see if any write operations have recently
* completed
*/
--
2.54.0
^ permalink raw reply related
* [PATCH 18/18] raid6_kunit: randomize buffer alignment
From: Christoph Hellwig @ 2026-05-18 5:18 UTC (permalink / raw)
To: Andrew Morton
Cc: Catalin Marinas, Will Deacon, Ard Biesheuvel, Huacai Chen,
WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260518051804.462141-1-hch@lst.de>
Add code to add random alignment to the buffers to test the case where
they are not page aligned, and to move the buffers to the end of the
allocation so that they are next to the vmalloc guard page.
This does not include the recovery buffers as the recovery requires
page alignment.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Acked-by: Ard Biesheuvel <ardb@kernel.org>
Tested-by: Ard Biesheuvel <ardb@kernel.org> # kunit only on arm64
---
lib/raid/raid6/tests/raid6_kunit.c | 41 +++++++++++++++++++++++++-----
1 file changed, 35 insertions(+), 6 deletions(-)
diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c
index 71adf8932e93..9f3e671a1224 100644
--- a/lib/raid/raid6/tests/raid6_kunit.c
+++ b/lib/raid/raid6/tests/raid6_kunit.c
@@ -21,6 +21,7 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
static struct rnd_state rng;
static void *test_buffers[RAID6_KUNIT_MAX_BUFFERS];
+static void *aligned_buffers[RAID6_KUNIT_MAX_BUFFERS];
static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES];
static size_t test_buflen;
@@ -50,6 +51,14 @@ static unsigned int random_nr_buffers(void)
RAID6_MIN_DISKS;
}
+/* Generate a random alignment that is a multiple of 64. */
+static unsigned int random_alignment(unsigned int max_alignment)
+{
+ if (max_alignment == 0)
+ return 0;
+ return (rand32() % (max_alignment + 1)) & ~63;
+}
+
static void makedata(int start, int stop)
{
int i;
@@ -80,7 +89,7 @@ static void test_recover_one(struct kunit *test, unsigned int nr_buffers,
for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
memset(test_recov_buffers[i], 0xf0, test_buflen);
- memcpy(dataptrs, test_buffers, sizeof(dataptrs));
+ memcpy(dataptrs, aligned_buffers, sizeof(dataptrs));
dataptrs[faila] = test_recov_buffers[0];
dataptrs[failb] = test_recov_buffers[1];
@@ -102,13 +111,13 @@ static void test_recover_one(struct kunit *test, unsigned int nr_buffers,
ta->recov->data2(nr_buffers, len, faila, failb, dataptrs);
}
- KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0],
+ KUNIT_EXPECT_MEMEQ_MSG(test, aligned_buffers[faila], dataptrs[faila],
len,
"faila miscompared: %3d[%c] buffers %u len %u (failb=%3d[%c])\n",
faila, member_type(nr_buffers, faila),
nr_buffers, len,
failb, member_type(nr_buffers, failb));
- KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1],
+ KUNIT_EXPECT_MEMEQ_MSG(test, aligned_buffers[failb], dataptrs[failb],
len,
"failb miscompared: %3d[%c] buffers %u len %u (faila=%3d[%c])\n",
failb, member_type(nr_buffers, failb),
@@ -152,9 +161,9 @@ static void test_rmw_one(struct kunit *test, unsigned int nr_buffers,
{
const struct test_args *ta = test->param_value;
- ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers);
+ ta->gen->xor_syndrome(nr_buffers, p1, p2, len, aligned_buffers);
makedata(p1, p2);
- ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers);
+ ta->gen->xor_syndrome(nr_buffers, p1, p2, len, aligned_buffers);
test_recover(test, nr_buffers, len);
}
@@ -178,13 +187,33 @@ static void raid6_test_one(struct kunit *test)
const struct test_args *ta = test->param_value;
unsigned int nr_buffers = random_nr_buffers();
unsigned int len = random_length(RAID6_KUNIT_MAX_BYTES);
+ unsigned int max_alignment;
+ int i;
/* Nuke syndromes */
memset(test_buffers[nr_buffers - 2], 0xee, test_buflen);
memset(test_buffers[nr_buffers - 1], 0xee, test_buflen);
+ /*
+ * If we're not using the entire buffer size, inject randomize alignment
+ * into the buffer.
+ */
+ max_alignment = RAID6_KUNIT_MAX_BYTES - len;
+ if (rand32() % 2 == 0) {
+ /* Use random alignments mod 64 */
+ for (i = 0; i < nr_buffers; i++)
+ aligned_buffers[i] = test_buffers[i] +
+ random_alignment(max_alignment);
+ } else {
+ /* Go up to the guard page, to catch buffer overreads */
+ unsigned int align = test_buflen - len;
+
+ for (i = 0; i < nr_buffers; i++)
+ aligned_buffers[i] = test_buffers[i] + align;
+ }
+
/* Generate assumed good syndrome */
- ta->gen->gen_syndrome(nr_buffers, len, test_buffers);
+ ta->gen->gen_syndrome(nr_buffers, len, aligned_buffers);
test_recover(test, nr_buffers, len);
--
2.53.0
^ permalink raw reply related
* [PATCH 17/18] raid6_kunit: randomize parameters and increase limits
From: Christoph Hellwig @ 2026-05-18 5:18 UTC (permalink / raw)
To: Andrew Morton
Cc: Catalin Marinas, Will Deacon, Ard Biesheuvel, Huacai Chen,
WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260518051804.462141-1-hch@lst.de>
The current test has double-quadratic behavior in the selection for
the updated ("XORed") disks, and in the selection of updated pointers,
which makes scaling it to more tests difficult. At the same time it
only ever tests with the maximum number of disks, which leaves a
coverage hole for smaller ones.
Fix this by randomizing the total number, failed disks and regions
to update, and increasing the upper number of tests disks.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Acked-by: Ard Biesheuvel <ardb@kernel.org>
Tested-by: Ard Biesheuvel <ardb@kernel.org> # kunit only on arm64
---
lib/raid/raid6/tests/raid6_kunit.c | 189 ++++++++++++++++++++---------
1 file changed, 131 insertions(+), 58 deletions(-)
diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c
index 152d5d1c3a88..71adf8932e93 100644
--- a/lib/raid/raid6/tests/raid6_kunit.c
+++ b/lib/raid/raid6/tests/raid6_kunit.c
@@ -8,18 +8,21 @@
#include <kunit/test.h>
#include <linux/prandom.h>
#include <linux/vmalloc.h>
+#include <linux/raid/pq.h>
#include "../algos.h"
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
#define RAID6_KUNIT_SEED 42
+#define RAID6_KUNIT_NUM_TEST_ITERS 10
+#define RAID6_KUNIT_MAX_BUFFERS 64 /* Including P and Q */
#define RAID6_KUNIT_MAX_FAILURES 2
-
-#define NDISKS 16 /* Including P and Q */
+#define RAID6_KUNIT_MAX_BYTES PAGE_SIZE
static struct rnd_state rng;
-static void *test_buffers[NDISKS];
+static void *test_buffers[RAID6_KUNIT_MAX_BUFFERS];
static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES];
+static size_t test_buflen;
struct test_args {
unsigned int recov_idx;
@@ -30,102 +33,171 @@ struct test_args {
static struct test_args args;
+static u32 rand32(void)
+{
+ return prandom_u32_state(&rng);
+}
+
+/* Generate a random length that is a multiple of 512. */
+static unsigned int random_length(unsigned int max_length)
+{
+ return round_up((rand32() % max_length) + 1, 512);
+}
+
+static unsigned int random_nr_buffers(void)
+{
+ return (rand32() % (RAID6_KUNIT_MAX_BUFFERS - (RAID6_MIN_DISKS - 1))) +
+ RAID6_MIN_DISKS;
+}
+
static void makedata(int start, int stop)
{
int i;
for (i = start; i <= stop; i++)
- prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE);
+ prandom_bytes_state(&rng, test_buffers[i], test_buflen);
}
-static char member_type(int d)
+static char member_type(unsigned int nr_buffers, int d)
{
- switch (d) {
- case NDISKS-2:
+ if (d == nr_buffers - 2)
return 'P';
- case NDISKS-1:
+ if (d == nr_buffers - 1)
return 'Q';
- default:
- return 'D';
- }
+ return 'D';
}
-static void test_recover(struct kunit *test, int faila, int failb)
+static void test_recover_one(struct kunit *test, unsigned int nr_buffers,
+ unsigned int len, int faila, int failb)
{
const struct test_args *ta = test->param_value;
- void *dataptrs[NDISKS];
+ void *dataptrs[RAID6_KUNIT_MAX_BUFFERS];
int i;
+ if (faila > failb)
+ swap(faila, failb);
+
for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
- memset(test_recov_buffers[i], 0xf0, PAGE_SIZE);
+ memset(test_recov_buffers[i], 0xf0, test_buflen);
memcpy(dataptrs, test_buffers, sizeof(dataptrs));
dataptrs[faila] = test_recov_buffers[0];
dataptrs[failb] = test_recov_buffers[1];
- if (failb == NDISKS - 1) {
+ if (failb == nr_buffers - 1) {
/*
* We don't implement the data+Q failure scenario, since it
* is equivalent to a RAID-5 failure (XOR, then recompute Q).
*/
- if (faila != NDISKS - 2)
+ if (WARN_ON_ONCE(faila != nr_buffers - 2))
return;
/* P+Q failure. Just rebuild the syndrome. */
- ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs);
- } else if (failb == NDISKS - 2) {
+ ta->gen->gen_syndrome(nr_buffers, len, dataptrs);
+ } else if (failb == nr_buffers - 2) {
/* data+P failure. */
- ta->recov->datap(NDISKS, PAGE_SIZE, faila, dataptrs);
+ ta->recov->datap(nr_buffers, len, faila, dataptrs);
} else {
/* data+data failure. */
- ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs);
+ ta->recov->data2(nr_buffers, len, faila, failb, dataptrs);
}
KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0],
- PAGE_SIZE,
- "faila miscompared: %3d[%c] (failb=%3d[%c])\n",
- faila, member_type(faila),
- failb, member_type(failb));
+ len,
+ "faila miscompared: %3d[%c] buffers %u len %u (failb=%3d[%c])\n",
+ faila, member_type(nr_buffers, faila),
+ nr_buffers, len,
+ failb, member_type(nr_buffers, failb));
KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1],
- PAGE_SIZE,
- "failb miscompared: %3d[%c] (faila=%3d[%c])\n",
- failb, member_type(failb),
- faila, member_type(faila));
+ len,
+ "failb miscompared: %3d[%c] buffers %u len %u (faila=%3d[%c])\n",
+ failb, member_type(nr_buffers, failb),
+ nr_buffers, len,
+ faila, member_type(nr_buffers, faila));
}
-static void raid6_test(struct kunit *test)
+static void test_recover(struct kunit *test, unsigned int nr_buffers,
+ unsigned int len)
+{
+ unsigned int nr_data = nr_buffers - 2;
+ int iterations, i;
+
+ /* Test P+Q recovery */
+ test_recover_one(test, nr_buffers, len, nr_data, nr_buffers - 1);
+
+ /* Test data+P recovery */
+ for (i = 0; i < nr_buffers - 2; i++)
+ test_recover_one(test, nr_buffers, len, i, nr_data);
+
+ /* Double data failure is impossible with a single data disk */
+ if (nr_data == 1)
+ return;
+
+ /* Test data+data recovery using random sampling */
+ iterations = nr_buffers * 2; /* should provide good enough coverage */
+ for (i = 0; i < iterations; i++) {
+ int faila = rand32() % nr_data, failb;
+
+ do {
+ failb = rand32() % nr_data;
+ } while (failb == faila);
+
+ test_recover_one(test, nr_buffers, len, faila, failb);
+ }
+}
+
+/* Simulate rmw run */
+static void test_rmw_one(struct kunit *test, unsigned int nr_buffers,
+ unsigned int len, int p1, int p2)
{
const struct test_args *ta = test->param_value;
- int i, j, p1, p2;
+
+ ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers);
+ makedata(p1, p2);
+ ta->gen->xor_syndrome(nr_buffers, p1, p2, len, test_buffers);
+ test_recover(test, nr_buffers, len);
+}
+
+static void test_rmw(struct kunit *test, unsigned int nr_buffers,
+ unsigned int len)
+{
+ int iterations = nr_buffers / 2, i;
+
+ for (i = 0; i < iterations; i++) {
+ int p1 = rand32() % (nr_buffers - 2);
+ int p2 = rand32() % (nr_buffers - 2);
+
+ if (p2 < p1)
+ swap(p1, p2);
+ test_rmw_one(test, nr_buffers, len, p1, p2);
+ }
+}
+
+static void raid6_test_one(struct kunit *test)
+{
+ const struct test_args *ta = test->param_value;
+ unsigned int nr_buffers = random_nr_buffers();
+ unsigned int len = random_length(RAID6_KUNIT_MAX_BYTES);
/* Nuke syndromes */
- memset(test_buffers[NDISKS - 2], 0xee, PAGE_SIZE);
- memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE);
+ memset(test_buffers[nr_buffers - 2], 0xee, test_buflen);
+ memset(test_buffers[nr_buffers - 1], 0xee, test_buflen);
/* Generate assumed good syndrome */
- ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, test_buffers);
+ ta->gen->gen_syndrome(nr_buffers, len, test_buffers);
- for (i = 0; i < NDISKS - 1; i++)
- for (j = i + 1; j < NDISKS; j++)
- test_recover(test, i, j);
+ test_recover(test, nr_buffers, len);
- if (!ta->gen->xor_syndrome)
- return;
+ if (ta->gen->xor_syndrome)
+ test_rmw(test, nr_buffers, len);
+}
- for (p1 = 0; p1 < NDISKS - 2; p1++) {
- for (p2 = p1; p2 < NDISKS - 2; p2++) {
- /* Simulate rmw run */
- ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
- test_buffers);
- makedata(p1, p2);
- ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
- test_buffers);
-
- for (i = 0; i < NDISKS - 1; i++)
- for (j = i + 1; j < NDISKS; j++)
- test_recover(test, i, j);
- }
- }
+static void raid6_test(struct kunit *test)
+{
+ int i;
+
+ for (i = 0; i < RAID6_KUNIT_NUM_TEST_ITERS; i++)
+ raid6_test_one(test);
}
static const void *raid6_gen_params(struct kunit *test, const void *prev,
@@ -169,23 +241,24 @@ static int raid6_suite_init(struct kunit_suite *suite)
* so that it is immediately followed by a guard page. This allows
* buffer overreads to be detected, even in assembly code.
*/
+ test_buflen = round_up(RAID6_KUNIT_MAX_BYTES, PAGE_SIZE);
for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) {
- test_recov_buffers[i] = vmalloc(PAGE_SIZE);
+ test_recov_buffers[i] = vmalloc(test_buflen);
if (!test_recov_buffers[i])
goto out_free_recov_buffers;
}
- for (i = 0; i < NDISKS; i++) {
- test_buffers[i] = vmalloc(PAGE_SIZE);
+ for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++) {
+ test_buffers[i] = vmalloc(test_buflen);
if (!test_buffers[i])
goto out_free_buffers;
}
- makedata(0, NDISKS - 1);
+ makedata(0, RAID6_KUNIT_MAX_BUFFERS - 1);
return 0;
out_free_buffers:
- for (i = 0; i < NDISKS; i++)
+ for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++)
vfree(test_buffers[i]);
memset(test_buffers, 0, sizeof(test_buffers));
out_free_recov_buffers:
@@ -199,7 +272,7 @@ static void raid6_suite_exit(struct kunit_suite *suite)
{
int i;
- for (i = 0; i < NDISKS; i++)
+ for (i = 0; i < RAID6_KUNIT_MAX_BUFFERS; i++)
vfree(test_buffers[i]);
memset(test_buffers, 0, sizeof(test_buffers));
for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
--
2.53.0
^ permalink raw reply related
* [PATCH 16/18] raid6_kunit: cleanup dataptr handling
From: Christoph Hellwig @ 2026-05-18 5:17 UTC (permalink / raw)
To: Andrew Morton
Cc: Catalin Marinas, Will Deacon, Ard Biesheuvel, Huacai Chen,
WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260518051804.462141-1-hch@lst.de>
Move the global dataptr array into test_recover() as all sites that fill
data or parity can use test_buffers directly, and this localized the
override for the failed slots to the recovery testing routine.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Acked-by: Ard Biesheuvel <ardb@kernel.org>
Tested-by: Ard Biesheuvel <ardb@kernel.org> # kunit only on arm64
---
lib/raid/raid6/tests/raid6_kunit.c | 19 +++++++------------
1 file changed, 7 insertions(+), 12 deletions(-)
diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c
index 72c78834df7f..152d5d1c3a88 100644
--- a/lib/raid/raid6/tests/raid6_kunit.c
+++ b/lib/raid/raid6/tests/raid6_kunit.c
@@ -18,7 +18,6 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
#define NDISKS 16 /* Including P and Q */
static struct rnd_state rng;
-static void *dataptrs[NDISKS];
static void *test_buffers[NDISKS];
static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES];
@@ -35,10 +34,8 @@ static void makedata(int start, int stop)
{
int i;
- for (i = start; i <= stop; i++) {
+ for (i = start; i <= stop; i++)
prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE);
- dataptrs[i] = test_buffers[i];
- }
}
static char member_type(int d)
@@ -56,11 +53,13 @@ static char member_type(int d)
static void test_recover(struct kunit *test, int faila, int failb)
{
const struct test_args *ta = test->param_value;
+ void *dataptrs[NDISKS];
int i;
for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
memset(test_recov_buffers[i], 0xf0, PAGE_SIZE);
+ memcpy(dataptrs, test_buffers, sizeof(dataptrs));
dataptrs[faila] = test_recov_buffers[0];
dataptrs[failb] = test_recov_buffers[1];
@@ -70,7 +69,7 @@ static void test_recover(struct kunit *test, int faila, int failb)
* is equivalent to a RAID-5 failure (XOR, then recompute Q).
*/
if (faila != NDISKS - 2)
- goto skip;
+ return;
/* P+Q failure. Just rebuild the syndrome. */
ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs);
@@ -92,10 +91,6 @@ static void test_recover(struct kunit *test, int faila, int failb)
"failb miscompared: %3d[%c] (faila=%3d[%c])\n",
failb, member_type(failb),
faila, member_type(faila));
-
-skip:
- dataptrs[faila] = test_buffers[faila];
- dataptrs[failb] = test_buffers[failb];
}
static void raid6_test(struct kunit *test)
@@ -108,7 +103,7 @@ static void raid6_test(struct kunit *test)
memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE);
/* Generate assumed good syndrome */
- ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs);
+ ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, test_buffers);
for (i = 0; i < NDISKS - 1; i++)
for (j = i + 1; j < NDISKS; j++)
@@ -121,10 +116,10 @@ static void raid6_test(struct kunit *test)
for (p2 = p1; p2 < NDISKS - 2; p2++) {
/* Simulate rmw run */
ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
- (void **)&dataptrs);
+ test_buffers);
makedata(p1, p2);
ta->gen->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
- (void **)&dataptrs);
+ test_buffers);
for (i = 0; i < NDISKS - 1; i++)
for (j = i + 1; j < NDISKS; j++)
--
2.53.0
^ permalink raw reply related
* [PATCH 15/18] raid6_kunit: dynamically allocate data buffers using vmalloc
From: Christoph Hellwig @ 2026-05-18 5:17 UTC (permalink / raw)
To: Andrew Morton
Cc: Catalin Marinas, Will Deacon, Ard Biesheuvel, Huacai Chen,
WANG Xuerui, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy (CS GROUP), Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Heiko Carstens,
Vasily Gorbik, Alexander Gordeev, Christian Borntraeger,
Sven Schnelle, Thomas Gleixner, Ingo Molnar, Borislav Petkov,
Dave Hansen, x86, H. Peter Anvin, Herbert Xu, Dan Williams,
Chris Mason, David Sterba, Arnd Bergmann, Song Liu, Yu Kuai,
Li Nan, linux-kernel, linux-arm-kernel, loongarch, linuxppc-dev,
linux-riscv, linux-s390, linux-crypto, linux-btrfs, linux-arch,
linux-raid
In-Reply-To: <20260518051804.462141-1-hch@lst.de>
Use vmalloc for the data buffers instead of using static .data allocations.
This provides for better out of bounds checking and avoids wasting kernel
memory after the test has run. vmalloc is used instead of kmalloc to
provide for better out of bounds access checking as in other kunit tests.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Acked-by: Ard Biesheuvel <ardb@kernel.org>
Tested-by: Ard Biesheuvel <ardb@kernel.org> # kunit only on arm64
---
lib/raid/raid6/tests/raid6_kunit.c | 77 ++++++++++++++++++++++++------
1 file changed, 62 insertions(+), 15 deletions(-)
diff --git a/lib/raid/raid6/tests/raid6_kunit.c b/lib/raid/raid6/tests/raid6_kunit.c
index 9d06ed9b90e4..72c78834df7f 100644
--- a/lib/raid/raid6/tests/raid6_kunit.c
+++ b/lib/raid/raid6/tests/raid6_kunit.c
@@ -7,19 +7,20 @@
#include <kunit/test.h>
#include <linux/prandom.h>
+#include <linux/vmalloc.h>
#include "../algos.h"
MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
#define RAID6_KUNIT_SEED 42
+#define RAID6_KUNIT_MAX_FAILURES 2
#define NDISKS 16 /* Including P and Q */
static struct rnd_state rng;
static void *dataptrs[NDISKS];
-static char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
-static char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
-static char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+static void *test_buffers[NDISKS];
+static void *test_recov_buffers[RAID6_KUNIT_MAX_FAILURES];
struct test_args {
unsigned int recov_idx;
@@ -35,8 +36,8 @@ static void makedata(int start, int stop)
int i;
for (i = start; i <= stop; i++) {
- prandom_bytes_state(&rng, data[i], PAGE_SIZE);
- dataptrs[i] = data[i];
+ prandom_bytes_state(&rng, test_buffers[i], PAGE_SIZE);
+ dataptrs[i] = test_buffers[i];
}
}
@@ -55,12 +56,13 @@ static char member_type(int d)
static void test_recover(struct kunit *test, int faila, int failb)
{
const struct test_args *ta = test->param_value;
+ int i;
- memset(recovi, 0xf0, PAGE_SIZE);
- memset(recovj, 0xba, PAGE_SIZE);
+ for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
+ memset(test_recov_buffers[i], 0xf0, PAGE_SIZE);
- dataptrs[faila] = recovi;
- dataptrs[failb] = recovj;
+ dataptrs[faila] = test_recov_buffers[0];
+ dataptrs[failb] = test_recov_buffers[1];
if (failb == NDISKS - 1) {
/*
@@ -80,18 +82,20 @@ static void test_recover(struct kunit *test, int faila, int failb)
ta->recov->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs);
}
- KUNIT_EXPECT_MEMEQ_MSG(test, data[faila], recovi, PAGE_SIZE,
+ KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[faila], test_recov_buffers[0],
+ PAGE_SIZE,
"faila miscompared: %3d[%c] (failb=%3d[%c])\n",
faila, member_type(faila),
failb, member_type(failb));
- KUNIT_EXPECT_MEMEQ_MSG(test, data[failb], recovj, PAGE_SIZE,
+ KUNIT_EXPECT_MEMEQ_MSG(test, test_buffers[failb], test_recov_buffers[1],
+ PAGE_SIZE,
"failb miscompared: %3d[%c] (faila=%3d[%c])\n",
failb, member_type(failb),
faila, member_type(faila));
skip:
- dataptrs[faila] = data[faila];
- dataptrs[failb] = data[failb];
+ dataptrs[faila] = test_buffers[faila];
+ dataptrs[failb] = test_buffers[failb];
}
static void raid6_test(struct kunit *test)
@@ -100,8 +104,8 @@ static void raid6_test(struct kunit *test)
int i, j, p1, p2;
/* Nuke syndromes */
- memset(data[NDISKS - 2], 0xee, PAGE_SIZE);
- memset(data[NDISKS - 1], 0xee, PAGE_SIZE);
+ memset(test_buffers[NDISKS - 2], 0xee, PAGE_SIZE);
+ memset(test_buffers[NDISKS - 1], 0xee, PAGE_SIZE);
/* Generate assumed good syndrome */
ta->gen->gen_syndrome(NDISKS, PAGE_SIZE, (void **)&dataptrs);
@@ -161,15 +165,58 @@ static struct kunit_case raid6_test_cases[] = {
static int raid6_suite_init(struct kunit_suite *suite)
{
+ int i;
+
prandom_seed_state(&rng, RAID6_KUNIT_SEED);
+
+ /*
+ * Allocate the test buffer using vmalloc() with a page-aligned length
+ * so that it is immediately followed by a guard page. This allows
+ * buffer overreads to be detected, even in assembly code.
+ */
+ for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++) {
+ test_recov_buffers[i] = vmalloc(PAGE_SIZE);
+ if (!test_recov_buffers[i])
+ goto out_free_recov_buffers;
+ }
+ for (i = 0; i < NDISKS; i++) {
+ test_buffers[i] = vmalloc(PAGE_SIZE);
+ if (!test_buffers[i])
+ goto out_free_buffers;
+ }
+
makedata(0, NDISKS - 1);
+
return 0;
+
+out_free_buffers:
+ for (i = 0; i < NDISKS; i++)
+ vfree(test_buffers[i]);
+ memset(test_buffers, 0, sizeof(test_buffers));
+out_free_recov_buffers:
+ for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
+ vfree(test_recov_buffers[i]);
+ memset(test_recov_buffers, 0, sizeof(test_recov_buffers));
+ return -ENOMEM;
+}
+
+static void raid6_suite_exit(struct kunit_suite *suite)
+{
+ int i;
+
+ for (i = 0; i < NDISKS; i++)
+ vfree(test_buffers[i]);
+ memset(test_buffers, 0, sizeof(test_buffers));
+ for (i = 0; i < RAID6_KUNIT_MAX_FAILURES; i++)
+ vfree(test_recov_buffers[i]);
+ memset(test_recov_buffers, 0, sizeof(test_recov_buffers));
}
static struct kunit_suite raid6_test_suite = {
.name = "raid6",
.test_cases = raid6_test_cases,
.suite_init = raid6_suite_init,
+ .suite_exit = raid6_suite_exit,
};
kunit_test_suite(raid6_test_suite);
--
2.53.0
^ permalink raw reply related
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