* Re: [PATCH 01/19] btrfs: require at least 4 devices for RAID 6
From: Christoph Hellwig @ 2026-05-15 4:37 UTC (permalink / raw)
To: H. Peter Anvin
Cc: kreijack, Goffredo Baroncelli, Christoph Hellwig, 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: <0507CCEF-0548-442F-8703-1D006B5E068B@zytor.com>
On Thu, May 14, 2026 at 12:57:53PM -0700, H. Peter Anvin wrote:
> That's what I'm saying – it should invoke the RAID-1 code under the
> cover (as with 3 disks, D = P = Q.)
Yes, if the btrfs maintainer cared for this setup that is what should
be done.
^ permalink raw reply
* [PATCH v2 0/2] md/raid10: fix r10bio width mismatches across reshape
From: Chen Cheng @ 2026-05-15 9:27 UTC (permalink / raw)
To: Yu Kuai; +Cc: Chen Cheng, linux-raid, linux-kernel
From: Chen Cheng <chencheng@fnnas.com>
Hi,
This series fixes slab out-of-bounds accesses in raid10 when reshape changes
the number of raid disks while regular I/O is still reusing r10bio objects
allocated under the previous geometry.
The bug is reproducible with a simple 4-disk to 5-disk reshape under write
load, for example:
mdadm -C /dev/md777 -l10 -n4 /dev/sda /dev/sdb /dev/sdc /dev/sdd
mkfs.ext4 /dev/md777
mount /dev/md777 /mnt/test
fsstress -d /mnt/test -n 24000 -p 8 -l 24 &
mdadm /dev/md777 --add /dev/sde
mdadm --grow /dev/md777 --raid-devices=5 \
--backup-file=/tmp/md-reshape-backup
Without these changes, an r10bio allocated under the old geometry can later be
reused, initialized, or freed after conf->geo.raid_disks has switched to the
new geometry. This creates width mismatches between the object and the current
devs[] walk/initialization width, which can trigger KASAN reports such as
slab-out-of-bounds in __make_request(), put_all_bios(), or find_bio_disk().
This series addresses the problem in two steps:
1. make the regular r10bio pool fixed-size across reshape transitions, and
move the pool rebuild into the freeze window before the live geometry
switch;
2. track the number of valid devs[] entries in each reused r10bio and use
that recorded width when walking devs[] after reshape.
Changes in v2:
- add this cover letter
- convert r10bio_pool to a fixed-size kmalloc mempool
- rebuild r10bio_pool inside the freeze window before switching live reshape
geometry
- switch raid10_quiesce() to freeze_array()/unfreeze_array()
Open issues:
One point where this v2 series still differs from raid1 is the pool-switch
semantics during reshape.
raid1 handles this by:
- converting r1bio_pool to a fixed-size pool,
- freezing the array,
- swapping in the new pool while the array is frozen,
- switching the live geometry/state,
- unfreezing the array, and
- destroying the old pool afterwards.
In other words, raid1 keeps the old and new regular I/O pools logically
separated across the reshape transition.
This raid10 v2 series follows the same high-level direction by converting
r10bio_pool to a fixed-size pool and moving the pool rebuild into the freeze
window before the live geometry switch. However, it does not yet mirror
raid1 completely: queued regular r10bios may still exist on retry_list or
bio_end_io_list at the time of the pool replacement, and raid10's current
freeze semantics only guarantee that in-flight I/O has either completed or
been queued.
My current understanding is that there are two possible directions to make
this fully robust:
1. strengthen raid10 freeze semantics so that the reshape-time pool switch
guarantees that no old regular r10bio can survive across the transition;
or
2. explicitly associate in-flight regular r10bios with the pool they were
allocated from, so they can always be returned to the correct pool even
if old and new pools overlap in time.
There is also a pre-existing boundary issue in find_bio_disk(): if the bio
is not found in devs[], the code can still walk past the recorded width.
That issue is not addressed in this series.
Testing:
- reproduced the original KASAN slab-out-of-bounds on 4-disk -> 5-disk
raid10 reshape with fsstress
- verified that this series fixes that reproducer
- exercised the 5-disk -> 4-disk reshape direction as well
Thanks,
Chen Cheng
Chen Cheng (2):
md/raid10: make r10bio_pool use fixed-size objects
md/raid10: bound reused r10bio devs[] walks by used_nr_devs
drivers/md/raid10.c | 63 +++++++++++++++++++++++++++++++++------------
drivers/md/raid10.h | 4 ++-
2 files changed, 49 insertions(+), 18 deletions(-)
--
2.54.0
^ permalink raw reply
* [PATCH v2 1/2] md/raid10: make r10bio_pool use fixed-size objects
From: Chen Cheng @ 2026-05-15 9:27 UTC (permalink / raw)
To: Yu Kuai; +Cc: Chen Cheng, linux-raid, linux-kernel
In-Reply-To: <20260515092707.3436464-1-chencheng@fnnas.com>
From: Chen Cheng <chencheng@fnnas.com>
raid10 currently allocates r10bio_pool objects with conf->geo.raid_disks,
which makes regular r10bio objects geometry-dependent.
That model breaks down across reshape. mempool objects are preallocated and
reused, so a reshape that changes the number of raid disks can leave old
r10bio objects in the regular I/O pool with a devs[] array sized for the
previous geometry. After the geometry switch, those stale objects may be
reused or later freed under the new layout, creating a width mismatch
between the reused r10bio and the current array geometry.
For example, during a 4-disk to 5-disk reshape, an r10bio allocated before
the geometry switch has room for only 4 devs[] entries. After reshape
updates conf->geo.raid_disks to 5, that stale object can be reused under
the new geometry. Code such as __make_request(), put_all_bios(), and
find_bio_disk() may then access devs[] using the new geometry and step
past the end of the old 4-slot object, leading to slab out-of-bounds
accesses.
The root problem is that regular r10bio pool objects are geometry-dependent,
while mempool elements are preallocated and reused across requests.
Switch r10bio_pool to a fixed-size kmalloc mempool so regular I/O objects no
longer carry an allocation width tied to the current geometry. Use the same
fixed-size allocation rule for the standalone r10bio allocated from
r10buf_pool_alloc().
Because reshape updates live array state such as conf->mirrors, conf->geo,
reshape_progress, and reshape_safe, the geometry switch must happen only
after normal I/O has gone fully quiet. raise_barrier() alone is not strong
enough here: freeze_array() also marks that an array freeze is in progress,
flushes pending writes, and waits until in-flight I/O has either completed
or been queued.
Freeze the array before switching reshape geometry, rebuild r10bio_pool for
the new width inside that freeze window, and switch raid10_quiesce() to use
freeze_array()/unfreeze_array() as well. This keeps new requests from reusing
stale-width regular I/O objects after the geometry change.
Signed-off-by: Chen Cheng <chencheng@fnnas.com>
---
drivers/md/raid10.c | 57 +++++++++++++++++++++++++++++++++------------
drivers/md/raid10.h | 2 +-
2 files changed, 43 insertions(+), 16 deletions(-)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 39085e7dd6d2..886bbe6b1ebc 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -103,13 +103,28 @@ static inline struct r10bio *get_resync_r10bio(struct bio *bio)
return get_resync_pages(bio)->raid_bio;
}
-static void * r10bio_pool_alloc(gfp_t gfp_flags, void *data)
+static inline unsigned int calc_r10bio_pool_disks(struct mddev *mddev)
{
- struct r10conf *conf = data;
- int size = offsetof(struct r10bio, devs[conf->geo.raid_disks]);
+ /* If delta_disks < 0, use bigger r10bio->devs[] is ok. */
+ return mddev->raid_disks + max(0, mddev->delta_disks);
+}
+
+static inline int calc_r10bio_size(struct mddev *mddev)
+{
+ return offsetof(struct r10bio, devs[calc_r10bio_pool_disks(mddev)]);
+}
+
+static mempool_t *create_r10bio_pool(struct mddev *mddev)
+{
+ int size = calc_r10bio_size(mddev);
+
+ return mempool_create_kmalloc_pool(NR_RAID_BIOS, size);
+}
+
+static struct r10bio *alloc_r10bio(struct mddev *mddev, gfp_t gfp_flags)
+{
+ int size = calc_r10bio_size(mddev);
- /* allocate a r10bio with room for raid_disks entries in the
- * bios array */
return kzalloc(size, gfp_flags);
}
@@ -137,7 +152,7 @@ static void * r10buf_pool_alloc(gfp_t gfp_flags, void *data)
int nalloc, nalloc_rp;
struct resync_pages *rps;
- r10_bio = r10bio_pool_alloc(gfp_flags, conf);
+ r10_bio = alloc_r10bio(conf->mddev, gfp_flags);
if (!r10_bio)
return NULL;
@@ -277,7 +292,7 @@ static void free_r10bio(struct r10bio *r10_bio)
struct r10conf *conf = r10_bio->mddev->private;
put_all_bios(conf, r10_bio);
- mempool_free(r10_bio, &conf->r10bio_pool);
+ mempool_free(r10_bio, conf->r10bio_pool);
}
static void put_buf(struct r10bio *r10_bio)
@@ -1531,7 +1546,7 @@ static void __make_request(struct mddev *mddev, struct bio *bio, int sectors)
struct r10conf *conf = mddev->private;
struct r10bio *r10_bio;
- r10_bio = mempool_alloc(&conf->r10bio_pool, GFP_NOIO);
+ r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO);
r10_bio->master_bio = bio;
r10_bio->sectors = sectors;
@@ -1723,7 +1738,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
(last_stripe_index << geo->chunk_shift);
retry_discard:
- r10_bio = mempool_alloc(&conf->r10bio_pool, GFP_NOIO);
+ r10_bio = mempool_alloc(conf->r10bio_pool, GFP_NOIO);
r10_bio->mddev = mddev;
r10_bio->state = 0;
r10_bio->sectors = 0;
@@ -3823,7 +3838,7 @@ static void raid10_free_conf(struct r10conf *conf)
if (!conf)
return;
- mempool_exit(&conf->r10bio_pool);
+ mempool_destroy(conf->r10bio_pool);
kfree(conf->mirrors);
kfree(conf->mirrors_old);
kfree(conf->mirrors_new);
@@ -3870,9 +3885,8 @@ static struct r10conf *setup_conf(struct mddev *mddev)
conf->geo = geo;
conf->copies = copies;
- err = mempool_init(&conf->r10bio_pool, NR_RAID_BIOS, r10bio_pool_alloc,
- rbio_pool_free, conf);
- if (err)
+ conf->r10bio_pool = create_r10bio_pool(mddev);
+ if (!conf->r10bio_pool)
goto out;
err = bioset_init(&conf->bio_split, BIO_POOL_SIZE, 0, 0);
@@ -4131,9 +4145,9 @@ static void raid10_quiesce(struct mddev *mddev, int quiesce)
struct r10conf *conf = mddev->private;
if (quiesce)
- raise_barrier(conf, 0);
+ freeze_array(conf, 0);
else
- lower_barrier(conf);
+ unfreeze_array(conf);
}
static int raid10_resize(struct mddev *mddev, sector_t sectors)
@@ -4365,6 +4379,7 @@ static int raid10_start_reshape(struct mddev *mddev)
struct md_rdev *rdev;
int spares = 0;
int ret;
+ mempool_t *new_pool;
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
return -EBUSY;
@@ -4400,7 +4415,17 @@ static int raid10_start_reshape(struct mddev *mddev)
if (spares < mddev->delta_disks)
return -EINVAL;
+ freeze_array(conf, 0);
conf->offset_diff = min_offset_diff;
+ if (mddev->delta_disks > 0) {
+ new_pool = create_r10bio_pool(mddev);
+ if (!new_pool) {
+ unfreeze_array(conf);
+ return -ENOMEM;
+ }
+ mempool_destroy(conf->r10bio_pool);
+ conf->r10bio_pool = new_pool;
+ }
spin_lock_irq(&conf->device_lock);
if (conf->mirrors_new) {
memcpy(conf->mirrors_new, conf->mirrors,
@@ -4417,6 +4442,7 @@ static int raid10_start_reshape(struct mddev *mddev)
sector_t size = raid10_size(mddev, 0, 0);
if (size < mddev->array_sectors) {
spin_unlock_irq(&conf->device_lock);
+ unfreeze_array(conf);
pr_warn("md/raid10:%s: array size must be reduce before number of disks\n",
mdname(mddev));
return -EINVAL;
@@ -4427,6 +4453,7 @@ static int raid10_start_reshape(struct mddev *mddev)
conf->reshape_progress = 0;
conf->reshape_safe = conf->reshape_progress;
spin_unlock_irq(&conf->device_lock);
+ unfreeze_array(conf);
if (mddev->delta_disks && mddev->bitmap) {
struct mdp_superblock_1 *sb = NULL;
diff --git a/drivers/md/raid10.h b/drivers/md/raid10.h
index ec79d87fb92f..b711626a5db7 100644
--- a/drivers/md/raid10.h
+++ b/drivers/md/raid10.h
@@ -87,7 +87,7 @@ struct r10conf {
*/
wait_queue_head_t wait_barrier;
- mempool_t r10bio_pool;
+ mempool_t *r10bio_pool;
mempool_t r10buf_pool;
struct page *tmppage;
struct bio_set bio_split;
--
2.54.0
^ permalink raw reply related
* [PATCH v2 2/2] md/raid10: bound reused r10bio devs[] walks by used_nr_devs
From: Chen Cheng @ 2026-05-15 9:27 UTC (permalink / raw)
To: Yu Kuai; +Cc: Chen Cheng, linux-raid, linux-kernel
In-Reply-To: <20260515092707.3436464-1-chencheng@fnnas.com>
From: Chen Cheng <chencheng@fnnas.com>
After reshape changes raid_disks, an in-flight r10bio from the old geometry
can still be completed or freed later. In that case, using the current
geometry to walk r10_bio->devs[] is unsafe. A failure was reproduced with a
simple write workload while reshaping a raid10 array from 4 disks to 5 disks.
e.g.:
mdadm -C /dev/md777 -l10 -n4 /dev/sda /dev/sdb /dev/sdc /dev/sdd
mkfs.ext4 /dev/md777
mount /dev/md777 /mnt/test
fsstress -d /mnt/test -n 24000 -p 8 -l 24 &
mdadm /dev/md777 --add /dev/sde
mdadm --grow /dev/md777 --raid-devices=5 \
--backup-file=/tmp/md-reshape-backup
the sequence above can trigger:
BUG: KASAN: slab-out-of-bounds in free_r10bio+0x1c4/0x260 [raid10]
Read of size 8 at addr ffff00008c2dfac8 by task ksoftirqd/0/15
free_r10bio
raid_end_bio_io
one_write_done
raid10_end_write_request
The buggy object was 200 bytes long, which matches an r10bio with space for
only four devs[] entries. However, put_all_bios() and find_bio_disk() walk
r10_bio->devs[] using the current conf->geo.raid_disks value. Once reshape
switches conf->geo.raid_disks from 4 to 5, an old 4-slot r10bio can be
completed or freed as if it had 5 slots, and the walk overruns devs[4]. The
same stale-width mismatch can also surface during a 5-disk to 4-disk reshape.
Track the number of valid devs[] entries in each reused r10bio with
used_nr_devs. Initialize it whenever an r10bio is prepared for regular I/O,
discard, or resync/recovery/reshape work, and use it to bound devs[] walks
in put_all_bios() and find_bio_disk().
Signed-off-by: Chen Cheng <chencheng@fnnas.com>
---
drivers/md/raid10.c | 8 ++++++--
drivers/md/raid10.h | 2 ++
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 886bbe6b1ebc..42865d822d95 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -275,7 +275,7 @@ static void put_all_bios(struct r10conf *conf, struct r10bio *r10_bio)
{
int i;
- for (i = 0; i < conf->geo.raid_disks; i++) {
+ for (i = 0; i < r10_bio->used_nr_devs; i++) {
struct bio **bio = & r10_bio->devs[i].bio;
if (!BIO_SPECIAL(*bio))
bio_put(*bio);
@@ -372,7 +372,7 @@ static int find_bio_disk(struct r10conf *conf, struct r10bio *r10_bio,
int slot;
int repl = 0;
- for (slot = 0; slot < conf->geo.raid_disks; slot++) {
+ for (slot = 0; slot < r10_bio->used_nr_devs; slot++) {
if (r10_bio->devs[slot].bio == bio)
break;
if (r10_bio->devs[slot].repl_bio == bio) {
@@ -1555,6 +1555,7 @@ static void __make_request(struct mddev *mddev, struct bio *bio, int sectors)
r10_bio->sector = bio->bi_iter.bi_sector;
r10_bio->state = 0;
r10_bio->read_slot = -1;
+ r10_bio->used_nr_devs = conf->geo.raid_disks;
memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) *
conf->geo.raid_disks);
@@ -1742,6 +1743,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
r10_bio->mddev = mddev;
r10_bio->state = 0;
r10_bio->sectors = 0;
+ r10_bio->used_nr_devs = geo->raid_disks;
memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) * geo->raid_disks);
wait_blocked_dev(mddev, r10_bio);
@@ -3076,6 +3078,8 @@ static struct r10bio *raid10_alloc_init_r10buf(struct r10conf *conf)
else
nalloc = 2; /* recovery */
+ r10bio->used_nr_devs = nalloc;
+
for (i = 0; i < nalloc; i++) {
bio = r10bio->devs[i].bio;
rp = bio->bi_private;
diff --git a/drivers/md/raid10.h b/drivers/md/raid10.h
index b711626a5db7..4751119f9770 100644
--- a/drivers/md/raid10.h
+++ b/drivers/md/raid10.h
@@ -127,6 +127,8 @@ struct r10bio {
* if the IO is in READ direction, then this is where we read
*/
int read_slot;
+ /* Used to bound devs[] walks when the object is reused. */
+ unsigned int used_nr_devs;
struct list_head retry_list;
/*
--
2.54.0
^ permalink raw reply related
* [PATCH] raid10_handle_discard() reuses r10bio objects from r10bio_pool.
From: Chen Cheng @ 2026-05-15 9:30 UTC (permalink / raw)
To: Yu Kuai; +Cc: Chen Cheng, linux-raid, linux-kernel
From: Chen Cheng <chencheng@fnnas.com>
put_all_bios() always drops devs[i].bio, but it only drops
devs[i].repl_bio when r10_bio->read_slot < 0. If discard reuses an
r10bio that was previously used for a read, read_slot can still be
non-negative, and discard cleanup can skip bio_put() on repl_bio.
Reset read_slot to -1 when preparing an r10bio for discard so the
replacement bio is always released correctly.
Signed-off-by: Chen Cheng <chencheng@fnnas.com>
---
drivers/md/raid10.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 39085e7dd6d2..7dc2a5a127e8 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1727,6 +1727,7 @@ static int raid10_handle_discard(struct mddev *mddev, struct bio *bio)
r10_bio->mddev = mddev;
r10_bio->state = 0;
r10_bio->sectors = 0;
+ r10_bio->read_slot = -1;
memset(r10_bio->devs, 0, sizeof(r10_bio->devs[0]) * geo->raid_disks);
wait_blocked_dev(mddev, r10_bio);
--
2.54.0
^ permalink raw reply related
* [PATCH] md/linear,raid0: introduce badblocks handling
From: Chen Cheng @ 2026-05-15 12:00 UTC (permalink / raw)
To: Yu Kuai, linux-raid; +Cc: Chen Cheng, linux-kernel
From: Chen Cheng <chencheng@fnnas.com>
md/linear and raid0 do not currently consult rdev badblocks, so I/O
can still be submitted to ranges that are already known to be bad.
The existing submit-path disk_live() fast-fail only covers removed
devices. It does not help when a member device is still present but a
mapped read fails, and immediately calling md_error() for every I/O
failure would make these arrays unnecessarily fragile.
Add badblocks handling for both raid-0 and md-linear personalities.
Before submitting a mapped bio, check the target rdev badblocks. If the
bio starts on a known bad range, fail it immediately. If it crosses into
a bad range, split it so that only the leading good sectors are submitted.
Also remember the mapped target rdev and sector range in md_io_clone, so
md_end_clone_io() can record badblocks for linear/raid0 failures.
This allows later I/O to fail fast on known bad sectors while avoiding
escalation to md_error() on every read failure. If badblocks cannot be
recorded, rdev_set_badblocks() will still trigger md_error().
Signed-off-by: Chen Cheng <chencheng@fnnas.com>
---
drivers/md/md-linear.c | 33 +++++++++++++++++++++++++++------
drivers/md/md.c | 16 ++++++++++++++++
drivers/md/md.h | 11 +++++++++--
drivers/md/raid0.c | 32 ++++++++++++++++++++++++++------
4 files changed, 78 insertions(+), 14 deletions(-)
diff --git a/drivers/md/md-linear.c b/drivers/md/md-linear.c
index fdff250d0d51..c6695658b698 100644
--- a/drivers/md/md-linear.c
+++ b/drivers/md/md-linear.c
@@ -237,6 +237,12 @@ static bool linear_make_request(struct mddev *mddev, struct bio *bio)
struct dev_info *tmp_dev;
sector_t start_sector, end_sector, data_offset;
sector_t bio_sector = bio->bi_iter.bi_sector;
+ sector_t first_bad, bad_sectors, good_sectors;
+ sector_t target_start_sector, bio_start_sector;
+ struct md_io_clone *md_io_clone;
+ unsigned int target_nr_sectors;
+ enum req_op op = bio_op(bio);
+ bool is_rw = (op == REQ_OP_READ || op == REQ_OP_WRITE);
if (unlikely(bio->bi_opf & REQ_PREFLUSH)
&& md_flush_request(mddev, bio))
@@ -251,12 +257,6 @@ static bool linear_make_request(struct mddev *mddev, struct bio *bio)
bio_sector < start_sector))
goto out_of_bounds;
- if (unlikely(is_rdev_broken(tmp_dev->rdev))) {
- md_error(mddev, tmp_dev->rdev);
- bio_io_error(bio);
- return true;
- }
-
if (unlikely(bio_end_sector(bio) > end_sector)) {
/* This bio crosses a device boundary, so we have to split it */
bio = bio_submit_split_bioset(bio, end_sector - bio_sector,
@@ -265,10 +265,31 @@ static bool linear_make_request(struct mddev *mddev, struct bio *bio)
return true;
}
+ bio_start_sector = bio->bi_iter.bi_sector - start_sector;
+
+ if (is_rw && is_badblock(tmp_dev->rdev, bio_start_sector,
+ bio_sectors(bio), &first_bad, &bad_sectors)) {
+ if (first_bad == bio_start_sector) {
+ bio_io_error(bio);
+ return true;
+ }
+
+ good_sectors = first_bad - bio_start_sector;
+ bio = bio_submit_split_bioset(bio, good_sectors, &mddev->bio_set);
+ if (!bio)
+ return true;
+ }
+
+ target_start_sector = bio->bi_iter.bi_sector - start_sector;
+ target_nr_sectors = bio_sectors(bio);
+
md_account_bio(mddev, &bio);
bio_set_dev(bio, tmp_dev->rdev->bdev);
bio->bi_iter.bi_sector = bio->bi_iter.bi_sector -
start_sector + data_offset;
+ md_io_clone = bio->bi_private;
+ md_set_clone_target(md_io_clone, tmp_dev->rdev,
+ target_start_sector, target_nr_sectors);
if (unlikely((bio_op(bio) == REQ_OP_DISCARD) &&
!bdev_max_discard_sectors(bio->bi_bdev))) {
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 3ce6f9e9d38e..995a8fa5f6a3 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -9218,6 +9218,13 @@ static void md_end_clone_io(struct bio *bio)
struct md_io_clone *md_io_clone = bio->bi_private;
struct bio *orig_bio = md_io_clone->orig_bio;
struct mddev *mddev = md_io_clone->mddev;
+ struct md_rdev *target_rdev = md_io_clone->target_rdev;
+ sector_t target_start_sector = md_io_clone->target_start_sector;
+ unsigned int target_nr_sectors = md_io_clone->target_nr_sectors;
+ enum md_submodule_id id = mddev->pers->head.id;
+ bool is_raid0_or_linear = (id == ID_LINEAR || id == ID_RAID0);
+ enum req_op op = bio_op(orig_bio);
+ bool is_rw = (op == REQ_OP_READ || op == REQ_OP_WRITE);
if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
md_bitmap_end(mddev, md_io_clone);
@@ -9225,6 +9232,12 @@ static void md_end_clone_io(struct bio *bio)
if (bio->bi_status && !orig_bio->bi_status)
orig_bio->bi_status = bio->bi_status;
+ if (bio->bi_status && target_rdev && target_nr_sectors &&
+ is_raid0_or_linear && is_rw) {
+ rdev_set_badblocks(target_rdev, target_start_sector,
+ target_nr_sectors, 0);
+ }
+
if (md_io_clone->start_time)
bio_end_io_acct(orig_bio, md_io_clone->start_time);
@@ -9243,6 +9256,9 @@ static void md_clone_bio(struct mddev *mddev, struct bio **bio)
md_io_clone = container_of(clone, struct md_io_clone, bio_clone);
md_io_clone->orig_bio = *bio;
md_io_clone->mddev = mddev;
+ md_io_clone->target_rdev = NULL;
+ md_io_clone->target_start_sector = 0;
+ md_io_clone->target_nr_sectors = 0;
if (blk_queue_io_stat(bdev->bd_disk->queue))
md_io_clone->start_time = bio_start_io_acct(*bio);
diff --git a/drivers/md/md.h b/drivers/md/md.h
index ac84289664cd..3122c66ef379 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -872,6 +872,9 @@ struct md_thread {
struct md_io_clone {
struct mddev *mddev;
+ struct md_rdev *target_rdev;
+ sector_t target_start_sector;
+ unsigned int target_nr_sectors;
struct bio *orig_bio;
unsigned long start_time;
sector_t offset;
@@ -961,9 +964,13 @@ extern void mddev_destroy_serial_pool(struct mddev *mddev,
struct md_rdev *md_find_rdev_nr_rcu(struct mddev *mddev, int nr);
struct md_rdev *md_find_rdev_rcu(struct mddev *mddev, dev_t dev);
-static inline bool is_rdev_broken(struct md_rdev *rdev)
+static inline void
+md_set_clone_target(struct md_io_clone *clone, struct md_rdev *rdev,
+ sector_t start_sector, unsigned int nr_sectors)
{
- return !disk_live(rdev->bdev->bd_disk);
+ clone->target_rdev = rdev;
+ clone->target_start_sector = start_sector;
+ clone->target_nr_sectors = nr_sectors;
}
static inline void rdev_dec_pending(struct md_rdev *rdev, struct mddev *mddev)
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index ef0045db409f..b95a16139fcd 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -559,8 +559,12 @@ static void raid0_map_submit_bio(struct mddev *mddev, struct bio *bio)
struct md_rdev *tmp_dev;
sector_t bio_sector = bio->bi_iter.bi_sector;
sector_t sector = bio_sector;
-
- md_account_bio(mddev, &bio);
+ sector_t bio_start_sector, target_start_sector;
+ sector_t first_bad, bad_sectors, good_sectors;
+ unsigned int target_nr_sectors;
+ struct md_io_clone *md_io_clone;
+ enum req_op op = bio_op(bio);
+ bool is_rw = (op == REQ_OP_READ || op == REQ_OP_WRITE);
zone = find_zone(mddev->private, §or);
switch (conf->layout) {
@@ -576,13 +580,29 @@ static void raid0_map_submit_bio(struct mddev *mddev, struct bio *bio)
return;
}
- if (unlikely(is_rdev_broken(tmp_dev))) {
- bio_io_error(bio);
- md_error(mddev, tmp_dev);
- return;
+ bio_start_sector = sector + zone->dev_start;
+
+ if (is_rw && is_badblock(tmp_dev, bio_start_sector, bio_sectors(bio),
+ &first_bad, &bad_sectors)) {
+ if (first_bad == bio_start_sector) {
+ bio_io_error(bio);
+ return;
+ }
+
+ good_sectors = first_bad - bio_start_sector;
+ bio = bio_submit_split_bioset(bio, good_sectors, &mddev->bio_set);
+ if (!bio)
+ return;
}
+ target_start_sector = sector + zone->dev_start;
+ target_nr_sectors = bio_sectors(bio);
+
+ md_account_bio(mddev, &bio);
bio_set_dev(bio, tmp_dev->bdev);
+ md_io_clone = bio->bi_private;
+ md_set_clone_target(md_io_clone, tmp_dev, target_start_sector,
+ target_nr_sectors);
bio->bi_iter.bi_sector = sector + zone->dev_start +
tmp_dev->data_offset;
mddev_trace_remap(mddev, bio, bio_sector);
--
2.54.0
^ permalink raw reply related
* Re: [PATCH 01/19] btrfs: require at least 4 devices for RAID 6
From: David Sterba @ 2026-05-15 14:51 UTC (permalink / raw)
To: H. Peter Anvin
Cc: kreijack, Goffredo Baroncelli, Christoph Hellwig, 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: <0507CCEF-0548-442F-8703-1D006B5E068B@zytor.com>
On Thu, May 14, 2026 at 12:57:53PM -0700, H. Peter Anvin wrote:
> On May 14, 2026 12:51:59 PM PDT, Goffredo Baroncelli <kreijack@libero.it> wrote:
> >On 13/05/2026 07.47, Christoph Hellwig wrote:
> >> On Tue, May 12, 2026 at 01:42:31PM +0200, David Sterba wrote:
> >
> >>
> >>> The degenerate modes of
> >>> raid0, 5, or 6 are explicit as a possible middle step when converting
> >>> profiles. We can use a fallback implementation for this case if the
> >>> accelerated implementations cannot do it.
> >>
> >> This is not about a degenerated mode. For a degenerated RAID 6, parity
> >> generation uses the RAID 5 XOR routines as the second parity will be
> >> missing. This is about generating two parities for a single data disk,
> >> which must be explicitly selected.
> >>
> >
> >I think that the David concern is : "what happens for an already
> >existing btrfs raid6 3 disks filesystem when the user upgrade the kernel ?"
> >(I am thinking when a new BG needs to be allocated)...
>
> That's what I'm saying – it should invoke the RAID-1 code under the cover (as with 3 disks, D = P = Q.)
Thanks, it was not clear to me what you meant. For the two edge cases
the code should do simple memcpy for both calculations of parity and
recovery.
^ permalink raw reply
* [REGRESSION] block: virtio-blk + LVM raid1 spurious sector-0 read failures on libaio/threads submit since 5ff3f74e145a ("block: simplify direct io validity check")
From: Vjaceslavs Klimovs @ 2026-05-15 16:52 UTC (permalink / raw)
To: Jens Axboe
Cc: Keith Busch, Hannes Reinecke, Martin K. Petersen,
Christoph Hellwig, linux-block, linux-raid, dm-devel,
linux-kernel, regressions
Summary
-------
On v6.18, starting a libvirt/QEMU guest with virtio-blk backed by an
LVM "--type raid1" LV (drivers/md/dm-raid.c stacked on
drivers/md/raid1.c) makes md/raid1 register read failures at LV
sector 0 within seconds of "virsh start" and mark rimage_0 Faulty
once max_corrected_read_errors (default 20) is exceeded. Reads
succeed via the redirect path so guests boot, but every guest disk
ends up degraded on every VM start. Same workload on legacy
"--type mirror" (drivers/md/dm-raid1.c) crashes the host: a
zero-length READ reaches the NVMe controller, is rejected with
"Invalid Field in Command", and the dm-mirror recovery path oopses.
Symptom on dm-raid raid1 (post --type raid1)
--------------------------------------------
Per LV, at virsh start, in host dmesg:
kernel: raid1_end_read_request: 95 callbacks suppressed
kernel: raid1_read_request: 95 callbacks suppressed
kernel: md/raid1:mdX: dm-58: rescheduling sector 0
kernel: md/raid1:mdX: redirecting sector 0 to other mirror: dm-58
kernel: md/raid1:mdX: dm-58: rescheduling sector 0
kernel: md/raid1:mdX: redirecting sector 0 to other mirror: dm-58
[... 10 rescheduling/redirecting pairs ...]
kernel: md/raid1:mdX: dm-58: Raid device exceeded read_error
threshold [cur 21:max 20]
kernel: md/raid1:mdX: dm-58: Failing raid device
kernel: md/raid1:mdX: Disk failure on dm-58, disabling device.
kernel: md/raid1:mdX: Operation continuing on 1 devices.
dmeventd: WARNING: Device #0 of raid1 array, vg0-iris_boot, has failed.
dmeventd: WARNING: Waiting for resynchronization to finish before
initiating repair on RAID device vg0-iris_boot.
dmeventd: Use 'lvconvert --repair vg0/iris_boot' to replace failed device.
Subsequent "lvs -a":
WARNING: RaidLV vg0/iris_boot needs to be refreshed!
See character 'r' at position 9 in the RaidLV's attributes and its SubLV(s).
dmesg | grep nvme is EMPTY on this path. The NVMe driver is not
involved in producing the error; the failure originates between the
virtio-blk bio submission and raid1_end_read_request().
Symptom on legacy dm-mirror (pre-conversion --type mirror)
----------------------------------------------------------
Same workload on drivers/md/dm-raid1.c reaches the NVMe controller
as a zero-length READ and panics the host through dm-mirror's
recovery path:
kernel: operation not supported error, dev nvme1n1, sector 935446535
op 0x0:(READ) flags 0x0 phys_seg 0 prio class 2
kernel: nvme1n1: I/O Cmd(0x2) @ LBA 935446535, 0 blocks, I/O Error
(sct 0x0 / sc 0x2)
[... 10+ identical bursts at same timestamp ...]
dmeventd: Primary mirror device 252:58 read failed.
dmeventd: vg0-iris_boot is now in-sync.
[kernel oops in dm_mirror recovery path, full trace lost to console flash]
The "phys_seg 0", "0 blocks", "sct 0x0/sc 0x2" trio (NVMe Generic,
Invalid Field in Command, NVMe spec 4.1.1.2) is unambiguous: a bio
with bi_iter.bi_size == 0 and bi_vcnt == 0 left the block layer and
hit the controller. dm-raid raid1 hides this by retrying on the
surviving leg, but the upstream-of-md trigger is identical.
Bisect
------
git bisect, v6.12..v6.18, 16 deterministic GOOD/BAD steps, no skips,
~104 minutes:
5ff3f74e145adc79b49668adb8de276446acf6be is the first bad commit
block: simplify direct io validity check
--- a/block/fops.c
+++ b/block/fops.c
@@ -38,8 +38,8 @@ static blk_opf_t dio_bio_write_op(struct kiocb *iocb)
static bool blkdev_dio_invalid(struct block_device *bdev, struct kiocb *iocb,
struct iov_iter *iter)
{
- return iocb->ki_pos & (bdev_logical_block_size(bdev) - 1) ||
- !bdev_iter_is_aligned(bdev, iter);
+ return (iocb->ki_pos | iov_iter_count(iter)) &
+ (bdev_logical_block_size(bdev) - 1);
}
The dropped bdev_iter_is_aligned() used to walk the iov_iter and
reject per-segment misaligned/degenerate vectors at the blkdev fops
entry point. The replacement only validates ki_pos and total length
against the logical block size. Cases that now pass that no longer
get rejected:
- iter with iov_iter_count(iter) == 0 (degenerate; total length is
"sector-aligned" since 0 % 512 == 0)
- iter where total length is sector-aligned but a segment isn't
The commit message justifies the removal with "The block layer
checks all the segments for validity later". This is true for the
io_uring submit path (which enters __blkdev_direct_IO directly and
does its own validation) but not for the libaio aio_read/write_iter
or the worker-pool sync read/write_iter paths that enter via
blkdev_{read,write}_iter() -> blkdev_dio_invalid(). For those paths,
the segment check has no replacement.
Reproducing
----------------------------------------------------------
The trigger requires QEMU virtio-blk's specific submission shape AND
a non-io_uring submit. Userspace libaio alone, userspace
preadv-in-a-thread alone, and QEMU's raw-driver open probes (which
qemu-img info exercises identically) are all insufficient. The
combination that hits the bug is "guest-driven I/O through
virtio-blk-pci with cache.direct=on and aio in {native, threads}".
#regzbot introduced: 5ff3f74e145adc79b49668adb8de276446acf6be
Thanks,
Vjaceslavs Klimovs
^ permalink raw reply
* Re: [PATCH 01/19] btrfs: require at least 4 devices for RAID 6
From: Goffredo Baroncelli @ 2026-05-15 16:50 UTC (permalink / raw)
To: Christoph Hellwig
Cc: 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, 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: <20260515043705.GA3855@lst.de>
On 15/05/2026 06.37, Christoph Hellwig wrote:
> On Thu, May 14, 2026 at 09:51:59PM +0200, Goffredo Baroncelli wrote:
>> I think that the David concern is : "what happens for an already
>> existing btrfs raid6 3 disks filesystem when the user upgrade the kernel ?"
>> (I am thinking when a new BG needs to be allocated)...
>
> Then it will cleanly fail to mount instead of constantly corrupting data
> and memory with every write, yes. Which clearly suggest that such
> file systems don't exist in the wild.
>
> But if btrfs wants to keep supporting this I'll just add a _unsafe
> version without the check in the core library.
>
I am not arguing about this part. My point is that the change shouldn't have impacted the
BTRFS interface versus the user (as patch 01/19 does), but instead the change should
have modify the interface raid code <-> btrfs (e.g. doing a memcpy....), or at least the
cover letter should warn that the raid6 code requires a number of disk >= 4, pointing
to BTRFS as "client doing wrong things".
At least, the message was received: don't relay to the raid6 code when the number of disk is
less than 4.
BR
GB
--
gpg @keyserver.linux.it: Goffredo Baroncelli <kreijackATinwind.it>
Key fingerprint BBF5 1610 0B64 DAC6 5F7D 17B2 0EDA 9B37 8B82 E0B5
^ permalink raw reply
* Re: [PATCH 01/19] btrfs: require at least 4 devices for RAID 6
From: H. Peter Anvin @ 2026-05-15 19:59 UTC (permalink / raw)
To: Christoph Hellwig, kreijack
Cc: 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: <20260515043705.GA3855@lst.de>
On May 14, 2026 9:37:05 PM PDT, Christoph Hellwig <hch@lst.de> wrote:
>On Thu, May 14, 2026 at 09:51:59PM +0200, Goffredo Baroncelli wrote:
>> I think that the David concern is : "what happens for an already
>> existing btrfs raid6 3 disks filesystem when the user upgrade the kernel ?"
>> (I am thinking when a new BG needs to be allocated)...
>
>Then it will cleanly fail to mount instead of constantly corrupting data
>and memory with every write, yes. Which clearly suggest that such
>file systems don't exist in the wild.
>
>But if btrfs wants to keep supporting this I'll just add a _unsafe
>version without the check in the core library.
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.
^ permalink raw reply
* Re: [REGRESSION] block: virtio-blk + LVM raid1 spurious sector-0 read failures on libaio/threads submit since 5ff3f74e145a ("block: simplify direct io validity check")
From: Thorsten Leemhuis @ 2026-05-16 5:09 UTC (permalink / raw)
To: Vjaceslavs Klimovs, Jens Axboe
Cc: Keith Busch, Hannes Reinecke, Martin K. Petersen,
Christoph Hellwig, linux-block, linux-raid, dm-devel,
linux-kernel, regressions
In-Reply-To: <CAC_j7i1R7oy+nRhxEjCTba=DUgn02w9X+p94DCu0aHv5+5tKnQ@mail.gmail.com>
On 5/15/26 18:52, Vjaceslavs Klimovs wrote:
> Summary
> -------
> On v6.18, starting a libvirt/QEMU guest with virtio-blk backed by an
> LVM "--type raid1" LV (drivers/md/dm-raid.c stacked on
> drivers/md/raid1.c) makes md/raid1 register read failures at LV
> sector 0 within seconds of "virsh start" and mark rimage_0 Faulty
> once max_corrected_read_errors (default 20) is exceeded. Reads
> succeed via the redirect path so guests boot, but every guest disk
> ends up degraded on every VM start. Same workload on legacy
> "--type mirror" (drivers/md/dm-raid1.c) crashes the host: a
> zero-length READ reaches the NVMe controller, is rejected with
> "Invalid Field in Command", and the dm-mirror recovery path oopses.
That sounds somewhat like
https://lore.kernel.org/all/2982107.4sosBPzcNG@electra/
Have you tried latest 7.1-rc? It contains a fix for the problem
mentioned in said thread: f7b24c7b41f23b ("md/raid1,raid10: don't fail
devices for invalid IO errors") [v7.1-rc2]
Ciao, Thorsten
> Symptom on dm-raid raid1 (post --type raid1)
> --------------------------------------------
> Per LV, at virsh start, in host dmesg:
>
> kernel: raid1_end_read_request: 95 callbacks suppressed
> kernel: raid1_read_request: 95 callbacks suppressed
> kernel: md/raid1:mdX: dm-58: rescheduling sector 0
> kernel: md/raid1:mdX: redirecting sector 0 to other mirror: dm-58
> kernel: md/raid1:mdX: dm-58: rescheduling sector 0
> kernel: md/raid1:mdX: redirecting sector 0 to other mirror: dm-58
> [... 10 rescheduling/redirecting pairs ...]
> kernel: md/raid1:mdX: dm-58: Raid device exceeded read_error
> threshold [cur 21:max 20]
> kernel: md/raid1:mdX: dm-58: Failing raid device
> kernel: md/raid1:mdX: Disk failure on dm-58, disabling device.
> kernel: md/raid1:mdX: Operation continuing on 1 devices.
>
> dmeventd: WARNING: Device #0 of raid1 array, vg0-iris_boot, has failed.
> dmeventd: WARNING: Waiting for resynchronization to finish before
> initiating repair on RAID device vg0-iris_boot.
> dmeventd: Use 'lvconvert --repair vg0/iris_boot' to replace failed device.
>
> Subsequent "lvs -a":
>
> WARNING: RaidLV vg0/iris_boot needs to be refreshed!
> See character 'r' at position 9 in the RaidLV's attributes and its SubLV(s).
>
> dmesg | grep nvme is EMPTY on this path. The NVMe driver is not
> involved in producing the error; the failure originates between the
> virtio-blk bio submission and raid1_end_read_request().
>
> Symptom on legacy dm-mirror (pre-conversion --type mirror)
> ----------------------------------------------------------
> Same workload on drivers/md/dm-raid1.c reaches the NVMe controller
> as a zero-length READ and panics the host through dm-mirror's
> recovery path:
>
> kernel: operation not supported error, dev nvme1n1, sector 935446535
> op 0x0:(READ) flags 0x0 phys_seg 0 prio class 2
> kernel: nvme1n1: I/O Cmd(0x2) @ LBA 935446535, 0 blocks, I/O Error
> (sct 0x0 / sc 0x2)
> [... 10+ identical bursts at same timestamp ...]
> dmeventd: Primary mirror device 252:58 read failed.
> dmeventd: vg0-iris_boot is now in-sync.
> [kernel oops in dm_mirror recovery path, full trace lost to console flash]
>
> The "phys_seg 0", "0 blocks", "sct 0x0/sc 0x2" trio (NVMe Generic,
> Invalid Field in Command, NVMe spec 4.1.1.2) is unambiguous: a bio
> with bi_iter.bi_size == 0 and bi_vcnt == 0 left the block layer and
> hit the controller. dm-raid raid1 hides this by retrying on the
> surviving leg, but the upstream-of-md trigger is identical.
>
> Bisect
> ------
> git bisect, v6.12..v6.18, 16 deterministic GOOD/BAD steps, no skips,
> ~104 minutes:
>
> 5ff3f74e145adc79b49668adb8de276446acf6be is the first bad commit
> block: simplify direct io validity check
>
> --- a/block/fops.c
> +++ b/block/fops.c
> @@ -38,8 +38,8 @@ static blk_opf_t dio_bio_write_op(struct kiocb *iocb)
> static bool blkdev_dio_invalid(struct block_device *bdev, struct kiocb *iocb,
> struct iov_iter *iter)
> {
> - return iocb->ki_pos & (bdev_logical_block_size(bdev) - 1) ||
> - !bdev_iter_is_aligned(bdev, iter);
> + return (iocb->ki_pos | iov_iter_count(iter)) &
> + (bdev_logical_block_size(bdev) - 1);
> }
>
> The dropped bdev_iter_is_aligned() used to walk the iov_iter and
> reject per-segment misaligned/degenerate vectors at the blkdev fops
> entry point. The replacement only validates ki_pos and total length
> against the logical block size. Cases that now pass that no longer
> get rejected:
>
> - iter with iov_iter_count(iter) == 0 (degenerate; total length is
> "sector-aligned" since 0 % 512 == 0)
> - iter where total length is sector-aligned but a segment isn't
>
> The commit message justifies the removal with "The block layer
> checks all the segments for validity later". This is true for the
> io_uring submit path (which enters __blkdev_direct_IO directly and
> does its own validation) but not for the libaio aio_read/write_iter
> or the worker-pool sync read/write_iter paths that enter via
> blkdev_{read,write}_iter() -> blkdev_dio_invalid(). For those paths,
> the segment check has no replacement.
>
> Reproducing
> ----------------------------------------------------------
>
> The trigger requires QEMU virtio-blk's specific submission shape AND
> a non-io_uring submit. Userspace libaio alone, userspace
> preadv-in-a-thread alone, and QEMU's raw-driver open probes (which
> qemu-img info exercises identically) are all insufficient. The
> combination that hits the bug is "guest-driven I/O through
> virtio-blk-pci with cache.direct=on and aio in {native, threads}".
>
> #regzbot introduced: 5ff3f74e145adc79b49668adb8de276446acf6be
>
> Thanks,
> Vjaceslavs Klimovs
>
^ permalink raw reply
* Re: [REGRESSION] block: virtio-blk + LVM raid1 spurious sector-0 read failures on libaio/threads submit since 5ff3f74e145a ("block: simplify direct io validity check")
From: Vjaceslavs Klimovs @ 2026-05-17 22:34 UTC (permalink / raw)
To: Thorsten Leemhuis
Cc: Jens Axboe, Keith Busch, Hannes Reinecke, Martin K. Petersen,
Christoph Hellwig, linux-block, linux-raid, dm-devel,
linux-kernel, regressions
In-Reply-To: <c3e576c8-f548-4ee4-87de-cb9151f8a061@leemhuis.info>
Yes, that was exactly it. The patch works for raid1 logical volumes
but, for obvious reasons (these are dm raid) this still oopses on
legacy mirror logical volumes:
[ 2.168054] device-mapper: raid1: Mirror read failed from 252:0.
Trying alternative device.
[ 2.169241] BUG: unable to handle page fault for address: fffff580045f4bc8
[ 2.170256] #PF: supervisor read access in kernel mode
[ 2.170997] #PF: error_code(0x0000) - not-present page
[ 2.171706] PGD 7ff9d067 P4D 7ff9d067 PUD 7ff9c067 PMD 0
[ 2.172433] Oops: Oops: 0000 [#1] SMP PTI
[ 2.173003] CPU: 0 UID: 0 PID: 11 Comm: kworker/0:1 Not tainted
6.18.29+ #19 PREEMPT(lazy)
[ 2.174118] Hardware name: QEMU Standard PC (Q35 + ICH9, 2009),
BIOS 1.16.3-20250108_150619-localhost 04/01/2014
[ 2.175472] Workqueue: kmirrord do_mirror
[ 2.176040] RIP: 0010:bio_add_page+0x8c/0x340
[ 2.176676] Code: 07 4d 8b 48 08 41 f6 c1 01 0f 85 d6 01 00 00 0f
1f 44 00 00 4d 89 c1 49 8b 11 48 c1 ea 33 83 e2 07 83 fa 04 0f 84 bf
00 00 00 <48> 8b 56 08 4c 8d 4a ff f6 c2 01 75
08 0f 1f 44 00 00 49 89 f1 49
[ 2.179169] RSP: 0018:ffffcea500063bc8 EFLAGS: 00010293
[ 2.179933] RAX: 0000000000000001 RBX: ffff8d53149af400 RCX:
0000000000000580
[ 2.180947] RDX: 0000000000000001 RSI: fffff580045f4bc0 RDI:
ffff8d53149af488
[ 2.181969] RBP: 0000000000000000 R08: fffff580005f4c00 R09:
fffff580005f4c00
[ 2.182978] R10: ffffcea500063c14 R11: 0000000000000a80 R12:
ffff8d5303192a80
[ 2.183997] R13: ffffcea500063c20 R14: 0000000000000001 R15:
ffffcea500063cf8
[ 2.185022] FS: 0000000000000000(0000) GS:ffff8d53ed4d5000(0000)
knlGS:0000000000000000
[ 2.186180] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2.187035] CR2: fffff580045f4bc8 CR3: 0000000002c44002 CR4:
0000000000372ef0
[ 2.188047] Call Trace:
[ 2.188417] <TASK>
[ 2.188756] do_region+0x21d/0x270
[ 2.189313] dispatch_io+0xf1/0x150
[ 2.189832] ? __pfx_bio_get_page+0x10/0x10
[ 2.190424] ? __pfx_bio_next_page+0x10/0x10
[ 2.191046] dm_io+0x136/0x240
[ 2.191503] ? __pfx_read_callback+0x10/0x10
[ 2.192108] ? __pfx_bio_get_page+0x10/0x10
[ 2.192708] ? __pfx_bio_next_page+0x10/0x10
[ 2.193319] do_reads+0x13e/0x210
[ 2.193807] ? __pfx_read_callback+0x10/0x10
[ 2.194411] do_mirror+0x117/0x2a0
[ 2.194912] process_one_work+0x18d/0x340
[ 2.195508] worker_thread+0x196/0x300
[ 2.196022] ? __pfx_worker_thread+0x10/0x10
[ 2.196617] kthread+0xfc/0x240
[ 2.197073] ? __pfx_kthread+0x10/0x10
[ 2.197606] ? __pfx_kthread+0x10/0x10
[ 2.198116] ret_from_fork+0x158/0x170
[ 2.198645] ? __pfx_kthread+0x10/0x10
[ 2.199161] ret_from_fork_asm+0x1a/0x30
[ 2.199736] </TASK>
[ 2.200053] Modules linked in:
[ 2.200493] CR2: fffff580045f4bc8
[ 2.200951] ---[ end trace 0000000000000000 ]---
[ 2.201599] RIP: 0010:bio_add_page+0x8c/0x340
[ 2.202193] Code: 07 4d 8b 48 08 41 f6 c1 01 0f 85 d6 01 00 00 0f
1f 44 00 00 4d 89 c1 49 8b 11 48 c1 ea 33 83 e2 07 83 fa 04 0f 84 bf
00 00 00 <48> 8b 56 08 4c 8d 4a ff f6 c2 01 75
08 0f 1f 44 00 00 49 89 f1 49
[ 2.204690] RSP: 0018:ffffcea500063bc8 EFLAGS: 00010293
[ 2.205390] RAX: 0000000000000001 RBX: ffff8d53149af400 RCX:
0000000000000580
[ 2.206368] RDX: 0000000000000001 RSI: fffff580045f4bc0 RDI:
ffff8d53149af488
[ 2.207333] RBP: 0000000000000000 R08: fffff580005f4c00 R09:
fffff580005f4c00
[ 2.208297] R10: ffffcea500063c14 R11: 0000000000000a80 R12:
ffff8d5303192a80
[ 2.209257] R13: ffffcea500063c20 R14: 0000000000000001 R15:
ffffcea500063cf8
[ 2.210265] FS: 0000000000000000(0000) GS:ffff8d53ed4d5000(0000)
knlGS:0000000000000000
[ 2.211391] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2.212201] CR2: fffff580045f4bc8 CR3: 0000000002c44002 CR4:
0000000000372ef0
[ 2.213196] Kernel panic - not syncing: Fatal exception
[ 2.214313] Kernel Offset: 0xc200000 from 0xffffffff81000000
(relocation range: 0xffffffff80000000-0xffffffffbfffffff)
[ 2.215981] Rebooting in 10 seconds..
On Fri, May 15, 2026 at 10:10 PM Thorsten Leemhuis
<regressions@leemhuis.info> wrote:
>
> On 5/15/26 18:52, Vjaceslavs Klimovs wrote:
> > Summary
> > -------
> > On v6.18, starting a libvirt/QEMU guest with virtio-blk backed by an
> > LVM "--type raid1" LV (drivers/md/dm-raid.c stacked on
> > drivers/md/raid1.c) makes md/raid1 register read failures at LV
> > sector 0 within seconds of "virsh start" and mark rimage_0 Faulty
> > once max_corrected_read_errors (default 20) is exceeded. Reads
> > succeed via the redirect path so guests boot, but every guest disk
> > ends up degraded on every VM start. Same workload on legacy
> > "--type mirror" (drivers/md/dm-raid1.c) crashes the host: a
> > zero-length READ reaches the NVMe controller, is rejected with
> > "Invalid Field in Command", and the dm-mirror recovery path oopses.
>
> That sounds somewhat like
> https://lore.kernel.org/all/2982107.4sosBPzcNG@electra/
>
> Have you tried latest 7.1-rc? It contains a fix for the problem
> mentioned in said thread: f7b24c7b41f23b ("md/raid1,raid10: don't fail
> devices for invalid IO errors") [v7.1-rc2]
>
> Ciao, Thorsten
>
> > Symptom on dm-raid raid1 (post --type raid1)
> > --------------------------------------------
> > Per LV, at virsh start, in host dmesg:
> >
> > kernel: raid1_end_read_request: 95 callbacks suppressed
> > kernel: raid1_read_request: 95 callbacks suppressed
> > kernel: md/raid1:mdX: dm-58: rescheduling sector 0
> > kernel: md/raid1:mdX: redirecting sector 0 to other mirror: dm-58
> > kernel: md/raid1:mdX: dm-58: rescheduling sector 0
> > kernel: md/raid1:mdX: redirecting sector 0 to other mirror: dm-58
> > [... 10 rescheduling/redirecting pairs ...]
> > kernel: md/raid1:mdX: dm-58: Raid device exceeded read_error
> > threshold [cur 21:max 20]
> > kernel: md/raid1:mdX: dm-58: Failing raid device
> > kernel: md/raid1:mdX: Disk failure on dm-58, disabling device.
> > kernel: md/raid1:mdX: Operation continuing on 1 devices.
> >
> > dmeventd: WARNING: Device #0 of raid1 array, vg0-iris_boot, has failed.
> > dmeventd: WARNING: Waiting for resynchronization to finish before
> > initiating repair on RAID device vg0-iris_boot.
> > dmeventd: Use 'lvconvert --repair vg0/iris_boot' to replace failed device.
> >
> > Subsequent "lvs -a":
> >
> > WARNING: RaidLV vg0/iris_boot needs to be refreshed!
> > See character 'r' at position 9 in the RaidLV's attributes and its SubLV(s).
> >
> > dmesg | grep nvme is EMPTY on this path. The NVMe driver is not
> > involved in producing the error; the failure originates between the
> > virtio-blk bio submission and raid1_end_read_request().
> >
> > Symptom on legacy dm-mirror (pre-conversion --type mirror)
> > ----------------------------------------------------------
> > Same workload on drivers/md/dm-raid1.c reaches the NVMe controller
> > as a zero-length READ and panics the host through dm-mirror's
> > recovery path:
> >
> > kernel: operation not supported error, dev nvme1n1, sector 935446535
> > op 0x0:(READ) flags 0x0 phys_seg 0 prio class 2
> > kernel: nvme1n1: I/O Cmd(0x2) @ LBA 935446535, 0 blocks, I/O Error
> > (sct 0x0 / sc 0x2)
> > [... 10+ identical bursts at same timestamp ...]
> > dmeventd: Primary mirror device 252:58 read failed.
> > dmeventd: vg0-iris_boot is now in-sync.
> > [kernel oops in dm_mirror recovery path, full trace lost to console flash]
> >
> > The "phys_seg 0", "0 blocks", "sct 0x0/sc 0x2" trio (NVMe Generic,
> > Invalid Field in Command, NVMe spec 4.1.1.2) is unambiguous: a bio
> > with bi_iter.bi_size == 0 and bi_vcnt == 0 left the block layer and
> > hit the controller. dm-raid raid1 hides this by retrying on the
> > surviving leg, but the upstream-of-md trigger is identical.
> >
> > Bisect
> > ------
> > git bisect, v6.12..v6.18, 16 deterministic GOOD/BAD steps, no skips,
> > ~104 minutes:
> >
> > 5ff3f74e145adc79b49668adb8de276446acf6be is the first bad commit
> > block: simplify direct io validity check
> >
> > --- a/block/fops.c
> > +++ b/block/fops.c
> > @@ -38,8 +38,8 @@ static blk_opf_t dio_bio_write_op(struct kiocb *iocb)
> > static bool blkdev_dio_invalid(struct block_device *bdev, struct kiocb *iocb,
> > struct iov_iter *iter)
> > {
> > - return iocb->ki_pos & (bdev_logical_block_size(bdev) - 1) ||
> > - !bdev_iter_is_aligned(bdev, iter);
> > + return (iocb->ki_pos | iov_iter_count(iter)) &
> > + (bdev_logical_block_size(bdev) - 1);
> > }
> >
> > The dropped bdev_iter_is_aligned() used to walk the iov_iter and
> > reject per-segment misaligned/degenerate vectors at the blkdev fops
> > entry point. The replacement only validates ki_pos and total length
> > against the logical block size. Cases that now pass that no longer
> > get rejected:
> >
> > - iter with iov_iter_count(iter) == 0 (degenerate; total length is
> > "sector-aligned" since 0 % 512 == 0)
> > - iter where total length is sector-aligned but a segment isn't
> >
> > The commit message justifies the removal with "The block layer
> > checks all the segments for validity later". This is true for the
> > io_uring submit path (which enters __blkdev_direct_IO directly and
> > does its own validation) but not for the libaio aio_read/write_iter
> > or the worker-pool sync read/write_iter paths that enter via
> > blkdev_{read,write}_iter() -> blkdev_dio_invalid(). For those paths,
> > the segment check has no replacement.
> >
> > Reproducing
> > ----------------------------------------------------------
> >
> > The trigger requires QEMU virtio-blk's specific submission shape AND
> > a non-io_uring submit. Userspace libaio alone, userspace
> > preadv-in-a-thread alone, and QEMU's raw-driver open probes (which
> > qemu-img info exercises identically) are all insufficient. The
> > combination that hits the bug is "guest-driven I/O through
> > virtio-blk-pci with cache.direct=on and aio in {native, threads}".
> >
> > #regzbot introduced: 5ff3f74e145adc79b49668adb8de276446acf6be
> >
> > Thanks,
> > Vjaceslavs Klimovs
> >
>
^ permalink raw reply
* Re: [PATCH 01/19] btrfs: require at least 4 devices for RAID 6
From: Christoph Hellwig @ 2026-05-18 5:12 UTC (permalink / raw)
To: H. Peter Anvin
Cc: Christoph Hellwig, 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: <34C16854-1065-4542-8836-DDED58EC1844@zytor.com>
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.
^ permalink raw reply
* [PATCH 01/18] raid6: turn the userspace test harness into a kunit test
From: Christoph Hellwig @ 2026-05-18 5:12 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: <20260518051245.451860-1-hch@lst.de>
Currently the raid6 code can be compiled as userspace code to run the
test suite. Convert that to be a kunit case with minimal changes to
avoid mutating global state so that we can drop this requirement.
Note that this is not a good kunit test case yet and will need a lot more
work, but that is deferred until the raid6 code is moved to it's new
place, which is easier if the userspace makefile doesn't need adjustments
for the new location first.
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
---
include/linux/raid/pq.h | 3 -
lib/Kconfig | 11 +++
lib/raid6/Makefile | 2 +-
lib/raid6/algos.c | 5 +-
lib/raid6/recov.c | 34 ---------
lib/raid6/test/Makefile | 155 +--------------------------------------
lib/raid6/test/test.c | 158 +++++++++++++++++++++-------------------
7 files changed, 101 insertions(+), 267 deletions(-)
diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h
index 2467b3be15c9..08c5995ea980 100644
--- a/include/linux/raid/pq.h
+++ b/include/linux/raid/pq.h
@@ -144,7 +144,6 @@ extern const struct raid6_calls raid6_neonx8;
/* Algorithm list */
extern const struct raid6_calls * const raid6_algos[];
extern const struct raid6_recov_calls *const raid6_recov_algos[];
-int raid6_select_algo(void);
/* Return values from chk_syndrome */
#define RAID6_OK 0
@@ -165,8 +164,6 @@ extern void (*raid6_2data_recov)(int disks, size_t bytes, int faila, int failb,
void **ptrs);
extern void (*raid6_datap_recov)(int disks, size_t bytes, int faila,
void **ptrs);
-void raid6_dual_recov(int disks, size_t bytes, int faila, int failb,
- void **ptrs);
/* Some definitions to allow code to be compiled for testing in userspace */
#ifndef __KERNEL__
diff --git a/lib/Kconfig b/lib/Kconfig
index 00a9509636c1..bffe015a6c10 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -11,6 +11,17 @@ menu "Library routines"
config RAID6_PQ
tristate
+config RAID6_PQ_KUNIT_TEST
+ tristate "KUnit tests for RAID6 PQ functions" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ depends on RAID6_PQ
+ default KUNIT_ALL_TESTS
+ help
+ Unit tests for the RAID6 PQ library functions.
+
+ This is intended to help people writing architecture-specific
+ optimized versions. If unsure, say N.
+
config RAID6_PQ_BENCHMARK
bool "Automatically choose fastest RAID6 PQ functions"
depends on RAID6_PQ
diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile
index 5be0a4e60ab1..6fd048c127b6 100644
--- a/lib/raid6/Makefile
+++ b/lib/raid6/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_RAID6_PQ) += raid6_pq.o
+obj-$(CONFIG_RAID6_PQ) += raid6_pq.o test/
raid6_pq-y += algos.o recov.o tables.o int1.o int2.o int4.o \
int8.o
diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index 799e0e5eac26..5a9f4882e18d 100644
--- a/lib/raid6/algos.c
+++ b/lib/raid6/algos.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/gfp.h>
#endif
+#include <kunit/visibility.h>
struct raid6_calls raid6_call;
EXPORT_SYMBOL_GPL(raid6_call);
@@ -86,6 +87,7 @@ const struct raid6_calls * const raid6_algos[] = {
&raid6_intx1,
NULL
};
+EXPORT_SYMBOL_IF_KUNIT(raid6_algos);
void (*raid6_2data_recov)(int, size_t, int, int, void **);
EXPORT_SYMBOL_GPL(raid6_2data_recov);
@@ -119,6 +121,7 @@ const struct raid6_recov_calls *const raid6_recov_algos[] = {
&raid6_recov_intx1,
NULL
};
+EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algos);
#ifdef __KERNEL__
#define RAID6_TIME_JIFFIES_LG2 4
@@ -239,7 +242,7 @@ static inline const struct raid6_calls *raid6_choose_gen(
/* Try to pick the best algorithm */
/* This code uses the gfmul table as convenient data set to abuse */
-int __init raid6_select_algo(void)
+static int __init raid6_select_algo(void)
{
const int disks = RAID6_TEST_DISKS;
diff --git a/lib/raid6/recov.c b/lib/raid6/recov.c
index b5e47c008b41..8d113196632e 100644
--- a/lib/raid6/recov.c
+++ b/lib/raid6/recov.c
@@ -99,37 +99,3 @@ const struct raid6_recov_calls raid6_recov_intx1 = {
.name = "intx1",
.priority = 0,
};
-
-#ifndef __KERNEL__
-/* Testing only */
-
-/* Recover two failed blocks. */
-void raid6_dual_recov(int disks, size_t bytes, int faila, int failb, void **ptrs)
-{
- if ( faila > failb ) {
- int tmp = faila;
- faila = failb;
- failb = tmp;
- }
-
- if ( failb == disks-1 ) {
- if ( faila == disks-2 ) {
- /* P+Q failure. Just rebuild the syndrome. */
- raid6_call.gen_syndrome(disks, bytes, ptrs);
- } else {
- /* data+Q failure. Reconstruct data from P,
- then rebuild syndrome. */
- /* NOT IMPLEMENTED - equivalent to RAID-5 */
- }
- } else {
- if ( failb == disks-2 ) {
- /* data+P failure. */
- raid6_datap_recov(disks, bytes, faila, ptrs);
- } else {
- /* data+data failure. */
- raid6_2data_recov(disks, bytes, faila, failb, ptrs);
- }
- }
-}
-
-#endif
diff --git a/lib/raid6/test/Makefile b/lib/raid6/test/Makefile
index 09bbe2b14cce..520381ea71d7 100644
--- a/lib/raid6/test/Makefile
+++ b/lib/raid6/test/Makefile
@@ -1,156 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-#
-# This is a simple Makefile to test some of the RAID-6 code
-# from userspace.
-#
-pound := \#
+obj-$(CONFIG_RAID6_PQ_KUNIT_TEST) += raid6_kunit.o
-# Adjust as desired
-CC = gcc
-OPTFLAGS = -O2
-CFLAGS = -I.. -I ../../../include -g $(OPTFLAGS)
-LD = ld
-AWK = awk -f
-AR = ar
-RANLIB = ranlib
-OBJS = int1.o int2.o int4.o int8.o int16.o int32.o recov.o algos.o tables.o
-
-ARCH := $(shell uname -m 2>/dev/null | sed -e /s/i.86/i386/)
-ifeq ($(ARCH),i386)
- CFLAGS += -DCONFIG_X86_32
- IS_X86 = yes
-endif
-ifeq ($(ARCH),x86_64)
- CFLAGS += -DCONFIG_X86_64
- IS_X86 = yes
-endif
-
-ifeq ($(ARCH),arm)
- CFLAGS += -I../../../arch/arm/include -mfpu=neon
- HAS_NEON = yes
-endif
-ifeq ($(ARCH),aarch64)
- CFLAGS += -I../../../arch/arm64/include
- HAS_NEON = yes
-endif
-
-ifeq ($(findstring riscv,$(ARCH)),riscv)
- CFLAGS += -I../../../arch/riscv/include -DCONFIG_RISCV=1
- HAS_RVV = yes
-endif
-
-ifeq ($(findstring ppc,$(ARCH)),ppc)
- CFLAGS += -I../../../arch/powerpc/include
- HAS_ALTIVEC := $(shell printf '$(pound)include <altivec.h>\nvector int a;\n' |\
- gcc -c -x c - >/dev/null && rm ./-.o && echo yes)
-endif
-
-ifeq ($(ARCH),loongarch64)
- CFLAGS += -I../../../arch/loongarch/include -DCONFIG_LOONGARCH=1
- CFLAGS += $(shell echo 'vld $$vr0, $$zero, 0' | \
- gcc -c -x assembler - >/dev/null 2>&1 && \
- rm ./-.o && echo -DCONFIG_CPU_HAS_LSX=1)
- CFLAGS += $(shell echo 'xvld $$xr0, $$zero, 0' | \
- gcc -c -x assembler - >/dev/null 2>&1 && \
- rm ./-.o && echo -DCONFIG_CPU_HAS_LASX=1)
-endif
-
-ifeq ($(IS_X86),yes)
- OBJS += mmx.o sse1.o sse2.o avx2.o recov_ssse3.o recov_avx2.o avx512.o recov_avx512.o
- CFLAGS += -DCONFIG_X86
-else ifeq ($(HAS_NEON),yes)
- OBJS += neon.o neon1.o neon2.o neon4.o neon8.o recov_neon.o recov_neon_inner.o
- CFLAGS += -DCONFIG_KERNEL_MODE_NEON=1
-else ifeq ($(HAS_ALTIVEC),yes)
- CFLAGS += -DCONFIG_ALTIVEC
- OBJS += altivec1.o altivec2.o altivec4.o altivec8.o \
- vpermxor1.o vpermxor2.o vpermxor4.o vpermxor8.o
-else ifeq ($(ARCH),loongarch64)
- OBJS += loongarch_simd.o recov_loongarch_simd.o
-else ifeq ($(HAS_RVV),yes)
- OBJS += rvv.o recov_rvv.o
- CFLAGS += -DCONFIG_RISCV_ISA_V=1
-endif
-
-.c.o:
- $(CC) $(CFLAGS) -c -o $@ $<
-
-%.c: ../%.c
- cp -f $< $@
-
-%.uc: ../%.uc
- cp -f $< $@
-
-all: raid6.a raid6test
-
-raid6.a: $(OBJS)
- rm -f $@
- $(AR) cq $@ $^
- $(RANLIB) $@
-
-raid6test: test.c raid6.a
- $(CC) $(CFLAGS) -o raid6test $^
-
-neon1.c: neon.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=1 < neon.uc > $@
-
-neon2.c: neon.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=2 < neon.uc > $@
-
-neon4.c: neon.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=4 < neon.uc > $@
-
-neon8.c: neon.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=8 < neon.uc > $@
-
-altivec1.c: altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=1 < altivec.uc > $@
-
-altivec2.c: altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=2 < altivec.uc > $@
-
-altivec4.c: altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=4 < altivec.uc > $@
-
-altivec8.c: altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=8 < altivec.uc > $@
-
-vpermxor1.c: vpermxor.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=1 < vpermxor.uc > $@
-
-vpermxor2.c: vpermxor.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=2 < vpermxor.uc > $@
-
-vpermxor4.c: vpermxor.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=4 < vpermxor.uc > $@
-
-vpermxor8.c: vpermxor.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=8 < vpermxor.uc > $@
-
-int1.c: int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=1 < int.uc > $@
-
-int2.c: int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=2 < int.uc > $@
-
-int4.c: int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=4 < int.uc > $@
-
-int8.c: int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=8 < int.uc > $@
-
-int16.c: int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=16 < int.uc > $@
-
-int32.c: int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=32 < int.uc > $@
-
-tables.c: mktables
- ./mktables > tables.c
-
-clean:
- rm -f *.o *.a mktables mktables.c *.uc int*.c altivec*.c vpermxor*.c neon*.c tables.c raid6test
-
-spotless: clean
- rm -f *~
+raid6_kunit-y += test.o
diff --git a/lib/raid6/test/test.c b/lib/raid6/test/test.c
index 841a55242aba..9db287b4a48f 100644
--- a/lib/raid6/test/test.c
+++ b/lib/raid6/test/test.c
@@ -1,43 +1,37 @@
// SPDX-License-Identifier: GPL-2.0-or-later
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved
- *
- * ----------------------------------------------------------------------- */
-
/*
- * raid6test.c
+ * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved
*
- * Test RAID-6 recovery with various algorithms
+ * Test RAID-6 recovery algorithms.
*/
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
+#include <kunit/test.h>
+#include <linux/prandom.h>
#include <linux/raid/pq.h>
-#define NDISKS 16 /* Including P and Q */
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+
+#define RAID6_KUNIT_SEED 42
-const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+#define NDISKS 16 /* Including P and Q */
-char *dataptrs[NDISKS];
-char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
-char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
-char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+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 makedata(int start, int stop)
{
- int i, j;
+ int i;
for (i = start; i <= stop; i++) {
- for (j = 0; j < PAGE_SIZE; j++)
- data[i][j] = rand();
-
+ prandom_bytes_state(&rng, data[i], PAGE_SIZE);
dataptrs[i] = data[i];
}
}
-static char disk_type(int d)
+static char member_type(int d)
{
switch (d) {
case NDISKS-2:
@@ -49,104 +43,118 @@ static char disk_type(int d)
}
}
-static int test_disks(int i, int j)
+static void test_disks(struct kunit *test, const struct raid6_calls *calls,
+ const struct raid6_recov_calls *ra, int faila, int failb)
{
- int erra, errb;
-
memset(recovi, 0xf0, PAGE_SIZE);
memset(recovj, 0xba, PAGE_SIZE);
- dataptrs[i] = recovi;
- dataptrs[j] = recovj;
-
- raid6_dual_recov(NDISKS, PAGE_SIZE, i, j, (void **)&dataptrs);
-
- erra = memcmp(data[i], recovi, PAGE_SIZE);
- errb = memcmp(data[j], recovj, PAGE_SIZE);
-
- if (i < NDISKS-2 && j == NDISKS-1) {
- /* We don't implement the DQ failure scenario, since it's
- equivalent to a RAID-5 failure (XOR, then recompute Q) */
- erra = errb = 0;
+ dataptrs[faila] = recovi;
+ dataptrs[failb] = recovj;
+
+ if (failb == NDISKS - 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)
+ goto skip;
+
+ /* P+Q failure. Just rebuild the syndrome. */
+ calls->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs);
+ } else if (failb == NDISKS - 2) {
+ /* data+P failure. */
+ ra->datap(NDISKS, PAGE_SIZE, faila, dataptrs);
} else {
- printf("algo=%-8s faila=%3d(%c) failb=%3d(%c) %s\n",
- raid6_call.name,
- i, disk_type(i),
- j, disk_type(j),
- (!erra && !errb) ? "OK" :
- !erra ? "ERRB" :
- !errb ? "ERRA" : "ERRAB");
+ /* data+data failure. */
+ ra->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs);
}
- dataptrs[i] = data[i];
- dataptrs[j] = data[j];
-
- return erra || errb;
+ KUNIT_EXPECT_MEMEQ_MSG(test, data[faila], recovi, PAGE_SIZE,
+ "algo=%-8s/%-8s faila miscompared: %3d[%c] (failb=%3d[%c])\n",
+ calls->name, ra->name,
+ faila, member_type(faila),
+ failb, member_type(failb));
+ KUNIT_EXPECT_MEMEQ_MSG(test, data[failb], recovj, PAGE_SIZE,
+ "algo=%-8s/%-8s failb miscompared: %3d[%c] (faila=%3d[%c])\n",
+ calls->name, ra->name,
+ failb, member_type(failb),
+ faila, member_type(faila));
+
+skip:
+ dataptrs[faila] = data[faila];
+ dataptrs[failb] = data[failb];
}
-int main(int argc, char *argv[])
+static void raid6_test(struct kunit *test)
{
const struct raid6_calls *const *algo;
const struct raid6_recov_calls *const *ra;
int i, j, p1, p2;
- int err = 0;
-
- makedata(0, NDISKS-1);
for (ra = raid6_recov_algos; *ra; ra++) {
if ((*ra)->valid && !(*ra)->valid())
continue;
- raid6_2data_recov = (*ra)->data2;
- raid6_datap_recov = (*ra)->datap;
-
- printf("using recovery %s\n", (*ra)->name);
-
for (algo = raid6_algos; *algo; algo++) {
- if ((*algo)->valid && !(*algo)->valid())
- continue;
+ const struct raid6_calls *calls = *algo;
- raid6_call = **algo;
+ if (calls->valid && !calls->valid())
+ continue;
/* Nuke syndromes */
- memset(data[NDISKS-2], 0xee, 2*PAGE_SIZE);
+ memset(data[NDISKS - 2], 0xee, PAGE_SIZE);
+ memset(data[NDISKS - 1], 0xee, PAGE_SIZE);
/* Generate assumed good syndrome */
- raid6_call.gen_syndrome(NDISKS, PAGE_SIZE,
+ calls->gen_syndrome(NDISKS, PAGE_SIZE,
(void **)&dataptrs);
for (i = 0; i < NDISKS-1; i++)
for (j = i+1; j < NDISKS; j++)
- err += test_disks(i, j);
+ test_disks(test, calls, *ra, i, j);
- if (!raid6_call.xor_syndrome)
+ if (!calls->xor_syndrome)
continue;
for (p1 = 0; p1 < NDISKS-2; p1++)
for (p2 = p1; p2 < NDISKS-2; p2++) {
/* Simulate rmw run */
- raid6_call.xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
+ calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
(void **)&dataptrs);
makedata(p1, p2);
- raid6_call.xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
+ calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
(void **)&dataptrs);
for (i = 0; i < NDISKS-1; i++)
for (j = i+1; j < NDISKS; j++)
- err += test_disks(i, j);
+ test_disks(test, calls,
+ *ra, i, j);
}
}
- printf("\n");
}
+}
- printf("\n");
- /* Pick the best algorithm test */
- raid6_select_algo();
-
- if (err)
- printf("\n*** ERRORS FOUND ***\n");
+static struct kunit_case raid6_test_cases[] = {
+ KUNIT_CASE(raid6_test),
+ {},
+};
- return err;
+static int raid6_suite_init(struct kunit_suite *suite)
+{
+ prandom_seed_state(&rng, RAID6_KUNIT_SEED);
+ makedata(0, NDISKS - 1);
+ return 0;
}
+
+static struct kunit_suite raid6_test_suite = {
+ .name = "raid6",
+ .test_cases = raid6_test_cases,
+ .suite_init = raid6_suite_init,
+};
+kunit_test_suite(raid6_test_suite);
+
+MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related
* cleanup the RAID6 P/Q library v3
From: Christoph Hellwig @ 2026-05-18 5:12 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
Hi all,
this series cleans up the RAID6 P/Q library to match the recent updates
to the RAID 5 XOR library and other CRC/crypto libraries. This includes
providing properly documented external interfaces, hiding the internals,
using static_call instead of indirect calls and turning the user space
test suite into an in-kernel kunit test which is also extended to
improve coverage.
Note that this changes registration so that non-priority algorithms are
not registered, which greatly helps with the benchmark time at boot time.
I'd like to encourage all architecture maintainers to see if they can
further optimized this by registering as few as possible algorithms when
there is a clear benefit in optimized or more unrolled implementations.
This series sits on top of the "cleanup the RAID5 XOR library v3" series.
A git tree is also available here:
git://git.infradead.org/users/hch/misc.git lib-raid6
Gitweb:
https://git.infradead.org/?p=users/hch/misc.git;a=shortlog;h=refs/heads/lib-raid6
Changes since v2:
- merge two patches that should have been one
- split out enforcing the 4-device minimum into a well-documented patch
explaining the rationale
- drop the btrfs patch to restrict the number of devices again
- fix them module description for the kunit test
- add -I $(src) to fix out of srctree builds for architectures with
headers in the architecture-specific directories
- always add the least optimized/unrolled algorithms first to keep the
existing no-benchmark behavior
- drop the delayed benchmarking for now to avoid corner cases
- improve a few commit messages
Changes since v1:
- fix arm64 objdir != srcdir builds
- call the kunit module raid6_kunit.ko from the beginning
- update MAINTAINERS
- don't require preemptible context and apply the same restrictions as
the merged version of the XOR API
- fix the arm64 default in Kconfig
- pick the last registered (and presumably most optimized) algorithm when
benchmarking is disabled
- port over the randomization fixes from the XOR series
- misc other kunit cleanups
- require at least 4 devices for RAID6 to skip broken special cases
Diffstat:
b/Documentation/crypto/async-tx-api.rst | 4
b/MAINTAINERS | 2
b/crypto/async_tx/async_pq.c | 9
b/crypto/async_tx/async_raid6_recov.c | 9
b/drivers/dma/bcm-sba-raid.c | 1
b/drivers/md/raid5.c | 4
b/fs/btrfs/raid56.c | 8
b/include/linux/raid/pq.h | 216 ------------
b/include/linux/raid/pq_tables.h | 19 +
b/lib/Kconfig | 11
b/lib/Makefile | 1
b/lib/raid/Kconfig | 33 +
b/lib/raid/Makefile | 2
b/lib/raid/raid6/Makefile | 128 +++++++
b/lib/raid/raid6/algos.c | 377 ++++++++++++++++++++++
b/lib/raid/raid6/algos.h | 41 ++
b/lib/raid/raid6/arm/neon.c | 23 -
b/lib/raid/raid6/arm/neon.uc | 2
b/lib/raid/raid6/arm/pq_arch.h | 21 +
b/lib/raid/raid6/arm/recov_neon.c | 27 -
b/lib/raid/raid6/arm/recov_neon_inner.c | 2
b/lib/raid/raid6/arm64/pq_arch.h | 1
b/lib/raid/raid6/int.uc | 10
b/lib/raid/raid6/loongarch/loongarch_simd.c | 31 -
b/lib/raid/raid6/loongarch/pq_arch.h | 23 +
b/lib/raid/raid6/loongarch/recov_loongarch_simd.c | 39 --
b/lib/raid/raid6/mktables.c | 28 -
b/lib/raid/raid6/powerpc/altivec.uc | 32 -
b/lib/raid/raid6/powerpc/pq_arch.h | 32 +
b/lib/raid/raid6/powerpc/vpermxor.uc | 29 -
b/lib/raid/raid6/recov.c | 62 ---
b/lib/raid/raid6/riscv/pq_arch.h | 21 +
b/lib/raid/raid6/riscv/recov_rvv.c | 14
b/lib/raid/raid6/riscv/rvv.h | 26 -
b/lib/raid/raid6/s390/pq_arch.h | 15
b/lib/raid/raid6/s390/recov_s390xc.c | 14
b/lib/raid/raid6/s390/s390vx.uc | 15
b/lib/raid/raid6/tests/Makefile | 3
b/lib/raid/raid6/tests/raid6_kunit.c | 321 ++++++++++++++++++
b/lib/raid/raid6/x86/avx2.c | 47 --
b/lib/raid/raid6/x86/avx512.c | 57 +--
b/lib/raid/raid6/x86/mmx.c | 39 --
b/lib/raid/raid6/x86/pq_arch.h | 96 +++++
b/lib/raid/raid6/x86/recov_avx2.c | 22 -
b/lib/raid/raid6/x86/recov_avx512.c | 26 -
b/lib/raid/raid6/x86/recov_ssse3.c | 23 -
b/lib/raid/raid6/x86/sse1.c | 49 --
b/lib/raid/raid6/x86/sse2.c | 47 --
lib/raid6/Makefile | 83 ----
lib/raid6/algos.c | 291 ----------------
lib/raid6/loongarch.h | 38 --
lib/raid6/test/.gitignore | 3
lib/raid6/test/Makefile | 156 ---------
lib/raid6/test/test.c | 152 --------
lib/raid6/x86.h | 75 ----
55 files changed, 1349 insertions(+), 1511 deletions(-)
^ permalink raw reply
* [PATCH 02/18] raid6: remove __KERNEL__ ifdefs
From: Christoph Hellwig @ 2026-05-18 5:12 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: <20260518051245.451860-1-hch@lst.de>
With the test code ported to kernel space, none of this is required.
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
---
include/linux/raid/pq.h | 90 --------------------------------
lib/raid6/algos.c | 12 -----
lib/raid6/altivec.uc | 10 +---
lib/raid6/avx2.c | 2 +-
lib/raid6/avx512.c | 2 +-
lib/raid6/loongarch.h | 38 --------------
lib/raid6/loongarch_simd.c | 3 +-
lib/raid6/mktables.c | 14 -----
lib/raid6/mmx.c | 2 +-
lib/raid6/neon.c | 6 ---
lib/raid6/recov_avx2.c | 2 +-
lib/raid6/recov_avx512.c | 2 +-
lib/raid6/recov_loongarch_simd.c | 3 +-
lib/raid6/recov_neon.c | 6 ---
lib/raid6/recov_ssse3.c | 2 +-
lib/raid6/rvv.h | 11 +---
lib/raid6/sse1.c | 2 +-
lib/raid6/sse2.c | 2 +-
lib/raid6/vpermxor.uc | 7 ---
lib/raid6/x86.h | 75 --------------------------
20 files changed, 15 insertions(+), 276 deletions(-)
delete mode 100644 lib/raid6/loongarch.h
delete mode 100644 lib/raid6/x86.h
diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h
index 08c5995ea980..d26788fada58 100644
--- a/include/linux/raid/pq.h
+++ b/include/linux/raid/pq.h
@@ -8,8 +8,6 @@
#ifndef LINUX_RAID_RAID6_H
#define LINUX_RAID_RAID6_H
-#ifdef __KERNEL__
-
#include <linux/blkdev.h>
#include <linux/mm.h>
@@ -19,59 +17,6 @@ static inline void *raid6_get_zero_page(void)
return page_address(ZERO_PAGE(0));
}
-#else /* ! __KERNEL__ */
-/* Used for testing in user space */
-
-#include <errno.h>
-#include <inttypes.h>
-#include <stddef.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/time.h>
-#include <sys/types.h>
-
-/* Not standard, but glibc defines it */
-#define BITS_PER_LONG __WORDSIZE
-
-typedef uint8_t u8;
-typedef uint16_t u16;
-typedef uint32_t u32;
-typedef uint64_t u64;
-
-#ifndef PAGE_SIZE
-# define PAGE_SIZE 4096
-#endif
-#ifndef PAGE_SHIFT
-# define PAGE_SHIFT 12
-#endif
-extern const char raid6_empty_zero_page[PAGE_SIZE];
-
-#define __init
-#define __exit
-#ifndef __attribute_const__
-# define __attribute_const__ __attribute__((const))
-#endif
-#define noinline __attribute__((noinline))
-
-#define preempt_enable()
-#define preempt_disable()
-#define cpu_has_feature(x) 1
-#define enable_kernel_altivec()
-#define disable_kernel_altivec()
-
-#undef EXPORT_SYMBOL
-#define EXPORT_SYMBOL(sym)
-#undef EXPORT_SYMBOL_GPL
-#define EXPORT_SYMBOL_GPL(sym)
-#define MODULE_LICENSE(licence)
-#define MODULE_DESCRIPTION(desc)
-#define subsys_initcall(x)
-#define module_exit(x)
-
-#define IS_ENABLED(x) (x)
-#define CONFIG_RAID6_PQ_BENCHMARK 1
-#endif /* __KERNEL__ */
-
/* Routine choices */
struct raid6_calls {
void (*gen_syndrome)(int, size_t, void **);
@@ -165,39 +110,4 @@ extern void (*raid6_2data_recov)(int disks, size_t bytes, int faila, int failb,
extern void (*raid6_datap_recov)(int disks, size_t bytes, int faila,
void **ptrs);
-/* Some definitions to allow code to be compiled for testing in userspace */
-#ifndef __KERNEL__
-
-# define jiffies raid6_jiffies()
-# define printk printf
-# define pr_err(format, ...) fprintf(stderr, format, ## __VA_ARGS__)
-# define pr_info(format, ...) fprintf(stdout, format, ## __VA_ARGS__)
-# define GFP_KERNEL 0
-# define __get_free_pages(x, y) ((unsigned long)mmap(NULL, PAGE_SIZE << (y), \
- PROT_READ|PROT_WRITE, \
- MAP_PRIVATE|MAP_ANONYMOUS,\
- 0, 0))
-# define free_pages(x, y) munmap((void *)(x), PAGE_SIZE << (y))
-
-static inline void cpu_relax(void)
-{
- /* Nothing */
-}
-
-#undef HZ
-#define HZ 1000
-static inline uint32_t raid6_jiffies(void)
-{
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return tv.tv_sec*1000 + tv.tv_usec/1000;
-}
-
-static inline void *raid6_get_zero_page(void)
-{
- return raid6_empty_zero_page;
-}
-
-#endif /* ! __KERNEL__ */
-
#endif /* LINUX_RAID_RAID6_H */
diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index 5a9f4882e18d..985c60bb00a4 100644
--- a/lib/raid6/algos.c
+++ b/lib/raid6/algos.c
@@ -12,13 +12,8 @@
*/
#include <linux/raid/pq.h>
-#ifndef __KERNEL__
-#include <sys/mman.h>
-#include <stdio.h>
-#else
#include <linux/module.h>
#include <linux/gfp.h>
-#endif
#include <kunit/visibility.h>
struct raid6_calls raid6_call;
@@ -123,14 +118,7 @@ const struct raid6_recov_calls *const raid6_recov_algos[] = {
};
EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algos);
-#ifdef __KERNEL__
#define RAID6_TIME_JIFFIES_LG2 4
-#else
-/* Need more time to be stable in userspace */
-#define RAID6_TIME_JIFFIES_LG2 9
-#define time_before(x, y) ((x) < (y))
-#endif
-
#define RAID6_TEST_DISKS 8
#define RAID6_TEST_DISKS_ORDER 3
diff --git a/lib/raid6/altivec.uc b/lib/raid6/altivec.uc
index d20ed0d11411..2c59963e58f9 100644
--- a/lib/raid6/altivec.uc
+++ b/lib/raid6/altivec.uc
@@ -27,10 +27,8 @@
#ifdef CONFIG_ALTIVEC
#include <altivec.h>
-#ifdef __KERNEL__
-# include <asm/cputable.h>
-# include <asm/switch_to.h>
-#endif /* __KERNEL__ */
+#include <asm/cputable.h>
+#include <asm/switch_to.h>
/*
* This is the C data type to use. We use a vector of
@@ -113,11 +111,7 @@ int raid6_have_altivec(void);
int raid6_have_altivec(void)
{
/* This assumes either all CPUs have Altivec or none does */
-# ifdef __KERNEL__
return cpu_has_feature(CPU_FTR_ALTIVEC);
-# else
- return 1;
-# endif
}
#endif
diff --git a/lib/raid6/avx2.c b/lib/raid6/avx2.c
index 059024234dce..a1a5213918af 100644
--- a/lib/raid6/avx2.c
+++ b/lib/raid6/avx2.c
@@ -14,7 +14,7 @@
*/
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
static const struct raid6_avx2_constants {
u64 x1d[4];
diff --git a/lib/raid6/avx512.c b/lib/raid6/avx512.c
index 009bd0adeebf..874998bcd7d7 100644
--- a/lib/raid6/avx512.c
+++ b/lib/raid6/avx512.c
@@ -18,7 +18,7 @@
*/
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
static const struct raid6_avx512_constants {
u64 x1d[8];
diff --git a/lib/raid6/loongarch.h b/lib/raid6/loongarch.h
deleted file mode 100644
index acfc33ce7056..000000000000
--- a/lib/raid6/loongarch.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2023 WANG Xuerui <git@xen0n.name>
- *
- * raid6/loongarch.h
- *
- * Definitions common to LoongArch RAID-6 code only
- */
-
-#ifndef _LIB_RAID6_LOONGARCH_H
-#define _LIB_RAID6_LOONGARCH_H
-
-#ifdef __KERNEL__
-
-#include <asm/cpu-features.h>
-#include <asm/fpu.h>
-
-#else /* for user-space testing */
-
-#include <sys/auxv.h>
-
-/* have to supply these defines for glibc 2.37- and musl */
-#ifndef HWCAP_LOONGARCH_LSX
-#define HWCAP_LOONGARCH_LSX (1 << 4)
-#endif
-#ifndef HWCAP_LOONGARCH_LASX
-#define HWCAP_LOONGARCH_LASX (1 << 5)
-#endif
-
-#define kernel_fpu_begin()
-#define kernel_fpu_end()
-
-#define cpu_has_lsx (getauxval(AT_HWCAP) & HWCAP_LOONGARCH_LSX)
-#define cpu_has_lasx (getauxval(AT_HWCAP) & HWCAP_LOONGARCH_LASX)
-
-#endif /* __KERNEL__ */
-
-#endif /* _LIB_RAID6_LOONGARCH_H */
diff --git a/lib/raid6/loongarch_simd.c b/lib/raid6/loongarch_simd.c
index aa5d9f924ca3..72f4d92d4876 100644
--- a/lib/raid6/loongarch_simd.c
+++ b/lib/raid6/loongarch_simd.c
@@ -10,7 +10,8 @@
*/
#include <linux/raid/pq.h>
-#include "loongarch.h"
+#include <asm/cpu-features.h>
+#include <asm/fpu.h>
/*
* The vector algorithms are currently priority 0, which means the generic
diff --git a/lib/raid6/mktables.c b/lib/raid6/mktables.c
index 3be03793237c..3de1dbf6846c 100644
--- a/lib/raid6/mktables.c
+++ b/lib/raid6/mktables.c
@@ -56,9 +56,7 @@ int main(int argc, char *argv[])
uint8_t v;
uint8_t exptbl[256], invtbl[256];
- printf("#ifdef __KERNEL__\n");
printf("#include <linux/export.h>\n");
- printf("#endif\n");
printf("#include <linux/raid/pq.h>\n");
/* Compute multiplication table */
@@ -76,9 +74,7 @@ int main(int argc, char *argv[])
printf("\t},\n");
}
printf("};\n");
- printf("#ifdef __KERNEL__\n");
printf("EXPORT_SYMBOL(raid6_gfmul);\n");
- printf("#endif\n");
/* Compute vector multiplication table */
printf("\nconst u8 __attribute__((aligned(256)))\n"
@@ -101,9 +97,7 @@ int main(int argc, char *argv[])
printf("\t},\n");
}
printf("};\n");
- printf("#ifdef __KERNEL__\n");
printf("EXPORT_SYMBOL(raid6_vgfmul);\n");
- printf("#endif\n");
/* Compute power-of-2 table (exponent) */
v = 1;
@@ -120,9 +114,7 @@ int main(int argc, char *argv[])
}
}
printf("};\n");
- printf("#ifdef __KERNEL__\n");
printf("EXPORT_SYMBOL(raid6_gfexp);\n");
- printf("#endif\n");
/* Compute log-of-2 table */
printf("\nconst u8 __attribute__((aligned(256)))\n"
@@ -140,9 +132,7 @@ int main(int argc, char *argv[])
}
}
printf("};\n");
- printf("#ifdef __KERNEL__\n");
printf("EXPORT_SYMBOL(raid6_gflog);\n");
- printf("#endif\n");
/* Compute inverse table x^-1 == x^254 */
printf("\nconst u8 __attribute__((aligned(256)))\n"
@@ -155,9 +145,7 @@ int main(int argc, char *argv[])
}
}
printf("};\n");
- printf("#ifdef __KERNEL__\n");
printf("EXPORT_SYMBOL(raid6_gfinv);\n");
- printf("#endif\n");
/* Compute inv(2^x + 1) (exponent-xor-inverse) table */
printf("\nconst u8 __attribute__((aligned(256)))\n"
@@ -169,9 +157,7 @@ int main(int argc, char *argv[])
(j == 7) ? '\n' : ' ');
}
printf("};\n");
- printf("#ifdef __KERNEL__\n");
printf("EXPORT_SYMBOL(raid6_gfexi);\n");
- printf("#endif\n");
return 0;
}
diff --git a/lib/raid6/mmx.c b/lib/raid6/mmx.c
index 3a5bf53a297b..e411f0cfbd95 100644
--- a/lib/raid6/mmx.c
+++ b/lib/raid6/mmx.c
@@ -14,7 +14,7 @@
#ifdef CONFIG_X86_32
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
/* Shared with raid6/sse1.c */
const struct raid6_mmx_constants {
diff --git a/lib/raid6/neon.c b/lib/raid6/neon.c
index 6d9474ce6da9..47b8bb0afc65 100644
--- a/lib/raid6/neon.c
+++ b/lib/raid6/neon.c
@@ -6,13 +6,7 @@
*/
#include <linux/raid/pq.h>
-
-#ifdef __KERNEL__
#include <asm/simd.h>
-#else
-#define scoped_ksimd()
-#define cpu_has_neon() (1)
-#endif
/*
* There are 2 reasons these wrappers are kept in a separate compilation unit
diff --git a/lib/raid6/recov_avx2.c b/lib/raid6/recov_avx2.c
index 97d598d2535c..19fbd9c4dce6 100644
--- a/lib/raid6/recov_avx2.c
+++ b/lib/raid6/recov_avx2.c
@@ -5,7 +5,7 @@
*/
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
static int raid6_has_avx2(void)
{
diff --git a/lib/raid6/recov_avx512.c b/lib/raid6/recov_avx512.c
index 7986120ca444..143f4976b2ad 100644
--- a/lib/raid6/recov_avx512.c
+++ b/lib/raid6/recov_avx512.c
@@ -7,7 +7,7 @@
*/
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
static int raid6_has_avx512(void)
{
diff --git a/lib/raid6/recov_loongarch_simd.c b/lib/raid6/recov_loongarch_simd.c
index 93dc515997a1..eb3a1e79f01f 100644
--- a/lib/raid6/recov_loongarch_simd.c
+++ b/lib/raid6/recov_loongarch_simd.c
@@ -11,7 +11,8 @@
*/
#include <linux/raid/pq.h>
-#include "loongarch.h"
+#include <asm/cpu-features.h>
+#include <asm/fpu.h>
/*
* Unlike with the syndrome calculation algorithms, there's no boot-time
diff --git a/lib/raid6/recov_neon.c b/lib/raid6/recov_neon.c
index 9d99aeabd31a..13d5df718c15 100644
--- a/lib/raid6/recov_neon.c
+++ b/lib/raid6/recov_neon.c
@@ -5,14 +5,8 @@
*/
#include <linux/raid/pq.h>
-
-#ifdef __KERNEL__
#include <asm/simd.h>
#include "neon.h"
-#else
-#define scoped_ksimd()
-#define cpu_has_neon() (1)
-#endif
static int raid6_has_neon(void)
{
diff --git a/lib/raid6/recov_ssse3.c b/lib/raid6/recov_ssse3.c
index 2e849185c32b..146cdbf465bd 100644
--- a/lib/raid6/recov_ssse3.c
+++ b/lib/raid6/recov_ssse3.c
@@ -4,7 +4,7 @@
*/
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
static int raid6_has_ssse3(void)
{
diff --git a/lib/raid6/rvv.h b/lib/raid6/rvv.h
index 6d0708a2c8a4..b0a71b375962 100644
--- a/lib/raid6/rvv.h
+++ b/lib/raid6/rvv.h
@@ -7,17 +7,8 @@
* Definitions for RISC-V RAID-6 code
*/
-#ifdef __KERNEL__
-#include <asm/vector.h>
-#else
-#define kernel_vector_begin()
-#define kernel_vector_end()
-#include <sys/auxv.h>
-#include <asm/hwcap.h>
-#define has_vector() (getauxval(AT_HWCAP) & COMPAT_HWCAP_ISA_V)
-#endif
-
#include <linux/raid/pq.h>
+#include <asm/vector.h>
static int rvv_has_vector(void)
{
diff --git a/lib/raid6/sse1.c b/lib/raid6/sse1.c
index 692fa3a93bf0..794d5cfa0306 100644
--- a/lib/raid6/sse1.c
+++ b/lib/raid6/sse1.c
@@ -19,7 +19,7 @@
#ifdef CONFIG_X86_32
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
/* Defined in raid6/mmx.c */
extern const struct raid6_mmx_constants {
diff --git a/lib/raid6/sse2.c b/lib/raid6/sse2.c
index 2930220249c9..f9edf8a8d1c4 100644
--- a/lib/raid6/sse2.c
+++ b/lib/raid6/sse2.c
@@ -13,7 +13,7 @@
*/
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
static const struct raid6_sse_constants {
u64 x1d[2];
diff --git a/lib/raid6/vpermxor.uc b/lib/raid6/vpermxor.uc
index 1bfb127fbfe8..a8e76b1c956e 100644
--- a/lib/raid6/vpermxor.uc
+++ b/lib/raid6/vpermxor.uc
@@ -25,10 +25,8 @@
#include <altivec.h>
#include <asm/ppc-opcode.h>
-#ifdef __KERNEL__
#include <asm/cputable.h>
#include <asm/switch_to.h>
-#endif
typedef vector unsigned char unative_t;
#define NSIZE sizeof(unative_t)
@@ -85,13 +83,8 @@ int raid6_have_altivec_vpermxor(void);
int raid6_have_altivec_vpermxor(void)
{
/* Check if arch has both altivec and the vpermxor instructions */
-# ifdef __KERNEL__
return (cpu_has_feature(CPU_FTR_ALTIVEC_COMP) &&
cpu_has_feature(CPU_FTR_ARCH_207S));
-# else
- return 1;
-#endif
-
}
#endif
diff --git a/lib/raid6/x86.h b/lib/raid6/x86.h
deleted file mode 100644
index 9a6ff37115e7..000000000000
--- a/lib/raid6/x86.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* ----------------------------------------------------------------------- *
- *
- * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6/x86.h
- *
- * Definitions common to x86 and x86-64 RAID-6 code only
- */
-
-#ifndef LINUX_RAID_RAID6X86_H
-#define LINUX_RAID_RAID6X86_H
-
-#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arch_um__)
-
-#ifdef __KERNEL__ /* Real code */
-
-#include <asm/fpu/api.h>
-
-#else /* Dummy code for user space testing */
-
-static inline void kernel_fpu_begin(void)
-{
-}
-
-static inline void kernel_fpu_end(void)
-{
-}
-
-#define __aligned(x) __attribute__((aligned(x)))
-
-#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */
-#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions
- * (fast save and restore) */
-#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */
-#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */
-#define X86_FEATURE_XMM3 (4*32+ 0) /* "pni" SSE-3 */
-#define X86_FEATURE_SSSE3 (4*32+ 9) /* Supplemental SSE-3 */
-#define X86_FEATURE_AVX (4*32+28) /* Advanced Vector Extensions */
-#define X86_FEATURE_AVX2 (9*32+ 5) /* AVX2 instructions */
-#define X86_FEATURE_AVX512F (9*32+16) /* AVX-512 Foundation */
-#define X86_FEATURE_AVX512DQ (9*32+17) /* AVX-512 DQ (Double/Quad granular)
- * Instructions
- */
-#define X86_FEATURE_AVX512BW (9*32+30) /* AVX-512 BW (Byte/Word granular)
- * Instructions
- */
-#define X86_FEATURE_AVX512VL (9*32+31) /* AVX-512 VL (128/256 Vector Length)
- * Extensions
- */
-#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */
-
-/* Should work well enough on modern CPUs for testing */
-static inline int boot_cpu_has(int flag)
-{
- u32 eax, ebx, ecx, edx;
-
- eax = (flag & 0x100) ? 7 :
- (flag & 0x20) ? 0x80000001 : 1;
- ecx = 0;
-
- asm volatile("cpuid"
- : "+a" (eax), "=b" (ebx), "=d" (edx), "+c" (ecx));
-
- return ((flag & 0x100 ? ebx :
- (flag & 0x80) ? ecx : edx) >> (flag & 31)) & 1;
-}
-
-#endif /* ndef __KERNEL__ */
-
-#endif
-#endif
--
2.53.0
^ permalink raw reply related
* cleanup the RAID6 P/Q library v3
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
Hi all,
this series cleans up the RAID6 P/Q library to match the recent updates
to the RAID 5 XOR library and other CRC/crypto libraries. This includes
providing properly documented external interfaces, hiding the internals,
using static_call instead of indirect calls and turning the user space
test suite into an in-kernel kunit test which is also extended to
improve coverage.
Note that this changes registration so that non-priority algorithms are
not registered, which greatly helps with the benchmark time at boot time.
I'd like to encourage all architecture maintainers to see if they can
further optimized this by registering as few as possible algorithms when
there is a clear benefit in optimized or more unrolled implementations.
This series sits on top of the "cleanup the RAID5 XOR library v3" series.
A git tree is also available here:
git://git.infradead.org/users/hch/misc.git lib-raid6
Gitweb:
https://git.infradead.org/?p=users/hch/misc.git;a=shortlog;h=refs/heads/lib-raid6
Changes since v2:
- merge two patches that should have been one
- split out enforcing the 4-device minimum into a well-documented patch
explaining the rationale
- drop the btrfs patch to restrict the number of devices again
- fix them module description for the kunit test
- add -I $(src) to fix out of srctree builds for architectures with
headers in the architecture-specific directories
- always add the least optimized/unrolled algorithms first to keep the
existing no-benchmark behavior
- drop the delayed benchmarking for now to avoid corner cases
- improve a few commit messages
Changes since v1:
- fix arm64 objdir != srcdir builds
- call the kunit module raid6_kunit.ko from the beginning
- update MAINTAINERS
- don't require preemptible context and apply the same restrictions as
the merged version of the XOR API
- fix the arm64 default in Kconfig
- pick the last registered (and presumably most optimized) algorithm when
benchmarking is disabled
- port over the randomization fixes from the XOR series
- misc other kunit cleanups
- require at least 4 devices for RAID6 to skip broken special cases
Diffstat:
b/Documentation/crypto/async-tx-api.rst | 4
b/MAINTAINERS | 2
b/crypto/async_tx/async_pq.c | 9
b/crypto/async_tx/async_raid6_recov.c | 9
b/drivers/dma/bcm-sba-raid.c | 1
b/drivers/md/raid5.c | 4
b/fs/btrfs/raid56.c | 8
b/include/linux/raid/pq.h | 216 ------------
b/include/linux/raid/pq_tables.h | 19 +
b/lib/Kconfig | 11
b/lib/Makefile | 1
b/lib/raid/Kconfig | 33 +
b/lib/raid/Makefile | 2
b/lib/raid/raid6/Makefile | 128 +++++++
b/lib/raid/raid6/algos.c | 377 ++++++++++++++++++++++
b/lib/raid/raid6/algos.h | 41 ++
b/lib/raid/raid6/arm/neon.c | 23 -
b/lib/raid/raid6/arm/neon.uc | 2
b/lib/raid/raid6/arm/pq_arch.h | 21 +
b/lib/raid/raid6/arm/recov_neon.c | 27 -
b/lib/raid/raid6/arm/recov_neon_inner.c | 2
b/lib/raid/raid6/arm64/pq_arch.h | 1
b/lib/raid/raid6/int.uc | 10
b/lib/raid/raid6/loongarch/loongarch_simd.c | 31 -
b/lib/raid/raid6/loongarch/pq_arch.h | 23 +
b/lib/raid/raid6/loongarch/recov_loongarch_simd.c | 39 --
b/lib/raid/raid6/mktables.c | 28 -
b/lib/raid/raid6/powerpc/altivec.uc | 32 -
b/lib/raid/raid6/powerpc/pq_arch.h | 32 +
b/lib/raid/raid6/powerpc/vpermxor.uc | 29 -
b/lib/raid/raid6/recov.c | 62 ---
b/lib/raid/raid6/riscv/pq_arch.h | 21 +
b/lib/raid/raid6/riscv/recov_rvv.c | 14
b/lib/raid/raid6/riscv/rvv.h | 26 -
b/lib/raid/raid6/s390/pq_arch.h | 15
b/lib/raid/raid6/s390/recov_s390xc.c | 14
b/lib/raid/raid6/s390/s390vx.uc | 15
b/lib/raid/raid6/tests/Makefile | 3
b/lib/raid/raid6/tests/raid6_kunit.c | 321 ++++++++++++++++++
b/lib/raid/raid6/x86/avx2.c | 47 --
b/lib/raid/raid6/x86/avx512.c | 57 +--
b/lib/raid/raid6/x86/mmx.c | 39 --
b/lib/raid/raid6/x86/pq_arch.h | 96 +++++
b/lib/raid/raid6/x86/recov_avx2.c | 22 -
b/lib/raid/raid6/x86/recov_avx512.c | 26 -
b/lib/raid/raid6/x86/recov_ssse3.c | 23 -
b/lib/raid/raid6/x86/sse1.c | 49 --
b/lib/raid/raid6/x86/sse2.c | 47 --
lib/raid6/Makefile | 83 ----
lib/raid6/algos.c | 291 ----------------
lib/raid6/loongarch.h | 38 --
lib/raid6/test/.gitignore | 3
lib/raid6/test/Makefile | 156 ---------
lib/raid6/test/test.c | 152 --------
lib/raid6/x86.h | 75 ----
55 files changed, 1349 insertions(+), 1511 deletions(-)
^ permalink raw reply
* [PATCH 01/18] raid6: turn the userspace test harness into a kunit test
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>
Currently the raid6 code can be compiled as userspace code to run the
test suite. Convert that to be a kunit case with minimal changes to
avoid mutating global state so that we can drop this requirement.
Note that this is not a good kunit test case yet and will need a lot more
work, but that is deferred until the raid6 code is moved to it's new
place, which is easier if the userspace makefile doesn't need adjustments
for the new location first.
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
---
include/linux/raid/pq.h | 3 -
lib/Kconfig | 11 +++
lib/raid6/Makefile | 2 +-
lib/raid6/algos.c | 5 +-
lib/raid6/recov.c | 34 ---------
lib/raid6/test/Makefile | 155 +--------------------------------------
lib/raid6/test/test.c | 158 +++++++++++++++++++++-------------------
7 files changed, 101 insertions(+), 267 deletions(-)
diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h
index 2467b3be15c9..08c5995ea980 100644
--- a/include/linux/raid/pq.h
+++ b/include/linux/raid/pq.h
@@ -144,7 +144,6 @@ extern const struct raid6_calls raid6_neonx8;
/* Algorithm list */
extern const struct raid6_calls * const raid6_algos[];
extern const struct raid6_recov_calls *const raid6_recov_algos[];
-int raid6_select_algo(void);
/* Return values from chk_syndrome */
#define RAID6_OK 0
@@ -165,8 +164,6 @@ extern void (*raid6_2data_recov)(int disks, size_t bytes, int faila, int failb,
void **ptrs);
extern void (*raid6_datap_recov)(int disks, size_t bytes, int faila,
void **ptrs);
-void raid6_dual_recov(int disks, size_t bytes, int faila, int failb,
- void **ptrs);
/* Some definitions to allow code to be compiled for testing in userspace */
#ifndef __KERNEL__
diff --git a/lib/Kconfig b/lib/Kconfig
index 00a9509636c1..bffe015a6c10 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -11,6 +11,17 @@ menu "Library routines"
config RAID6_PQ
tristate
+config RAID6_PQ_KUNIT_TEST
+ tristate "KUnit tests for RAID6 PQ functions" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ depends on RAID6_PQ
+ default KUNIT_ALL_TESTS
+ help
+ Unit tests for the RAID6 PQ library functions.
+
+ This is intended to help people writing architecture-specific
+ optimized versions. If unsure, say N.
+
config RAID6_PQ_BENCHMARK
bool "Automatically choose fastest RAID6 PQ functions"
depends on RAID6_PQ
diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile
index 5be0a4e60ab1..6fd048c127b6 100644
--- a/lib/raid6/Makefile
+++ b/lib/raid6/Makefile
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_RAID6_PQ) += raid6_pq.o
+obj-$(CONFIG_RAID6_PQ) += raid6_pq.o test/
raid6_pq-y += algos.o recov.o tables.o int1.o int2.o int4.o \
int8.o
diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index 799e0e5eac26..5a9f4882e18d 100644
--- a/lib/raid6/algos.c
+++ b/lib/raid6/algos.c
@@ -19,6 +19,7 @@
#include <linux/module.h>
#include <linux/gfp.h>
#endif
+#include <kunit/visibility.h>
struct raid6_calls raid6_call;
EXPORT_SYMBOL_GPL(raid6_call);
@@ -86,6 +87,7 @@ const struct raid6_calls * const raid6_algos[] = {
&raid6_intx1,
NULL
};
+EXPORT_SYMBOL_IF_KUNIT(raid6_algos);
void (*raid6_2data_recov)(int, size_t, int, int, void **);
EXPORT_SYMBOL_GPL(raid6_2data_recov);
@@ -119,6 +121,7 @@ const struct raid6_recov_calls *const raid6_recov_algos[] = {
&raid6_recov_intx1,
NULL
};
+EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algos);
#ifdef __KERNEL__
#define RAID6_TIME_JIFFIES_LG2 4
@@ -239,7 +242,7 @@ static inline const struct raid6_calls *raid6_choose_gen(
/* Try to pick the best algorithm */
/* This code uses the gfmul table as convenient data set to abuse */
-int __init raid6_select_algo(void)
+static int __init raid6_select_algo(void)
{
const int disks = RAID6_TEST_DISKS;
diff --git a/lib/raid6/recov.c b/lib/raid6/recov.c
index b5e47c008b41..8d113196632e 100644
--- a/lib/raid6/recov.c
+++ b/lib/raid6/recov.c
@@ -99,37 +99,3 @@ const struct raid6_recov_calls raid6_recov_intx1 = {
.name = "intx1",
.priority = 0,
};
-
-#ifndef __KERNEL__
-/* Testing only */
-
-/* Recover two failed blocks. */
-void raid6_dual_recov(int disks, size_t bytes, int faila, int failb, void **ptrs)
-{
- if ( faila > failb ) {
- int tmp = faila;
- faila = failb;
- failb = tmp;
- }
-
- if ( failb == disks-1 ) {
- if ( faila == disks-2 ) {
- /* P+Q failure. Just rebuild the syndrome. */
- raid6_call.gen_syndrome(disks, bytes, ptrs);
- } else {
- /* data+Q failure. Reconstruct data from P,
- then rebuild syndrome. */
- /* NOT IMPLEMENTED - equivalent to RAID-5 */
- }
- } else {
- if ( failb == disks-2 ) {
- /* data+P failure. */
- raid6_datap_recov(disks, bytes, faila, ptrs);
- } else {
- /* data+data failure. */
- raid6_2data_recov(disks, bytes, faila, failb, ptrs);
- }
- }
-}
-
-#endif
diff --git a/lib/raid6/test/Makefile b/lib/raid6/test/Makefile
index 09bbe2b14cce..520381ea71d7 100644
--- a/lib/raid6/test/Makefile
+++ b/lib/raid6/test/Makefile
@@ -1,156 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-#
-# This is a simple Makefile to test some of the RAID-6 code
-# from userspace.
-#
-pound := \#
+obj-$(CONFIG_RAID6_PQ_KUNIT_TEST) += raid6_kunit.o
-# Adjust as desired
-CC = gcc
-OPTFLAGS = -O2
-CFLAGS = -I.. -I ../../../include -g $(OPTFLAGS)
-LD = ld
-AWK = awk -f
-AR = ar
-RANLIB = ranlib
-OBJS = int1.o int2.o int4.o int8.o int16.o int32.o recov.o algos.o tables.o
-
-ARCH := $(shell uname -m 2>/dev/null | sed -e /s/i.86/i386/)
-ifeq ($(ARCH),i386)
- CFLAGS += -DCONFIG_X86_32
- IS_X86 = yes
-endif
-ifeq ($(ARCH),x86_64)
- CFLAGS += -DCONFIG_X86_64
- IS_X86 = yes
-endif
-
-ifeq ($(ARCH),arm)
- CFLAGS += -I../../../arch/arm/include -mfpu=neon
- HAS_NEON = yes
-endif
-ifeq ($(ARCH),aarch64)
- CFLAGS += -I../../../arch/arm64/include
- HAS_NEON = yes
-endif
-
-ifeq ($(findstring riscv,$(ARCH)),riscv)
- CFLAGS += -I../../../arch/riscv/include -DCONFIG_RISCV=1
- HAS_RVV = yes
-endif
-
-ifeq ($(findstring ppc,$(ARCH)),ppc)
- CFLAGS += -I../../../arch/powerpc/include
- HAS_ALTIVEC := $(shell printf '$(pound)include <altivec.h>\nvector int a;\n' |\
- gcc -c -x c - >/dev/null && rm ./-.o && echo yes)
-endif
-
-ifeq ($(ARCH),loongarch64)
- CFLAGS += -I../../../arch/loongarch/include -DCONFIG_LOONGARCH=1
- CFLAGS += $(shell echo 'vld $$vr0, $$zero, 0' | \
- gcc -c -x assembler - >/dev/null 2>&1 && \
- rm ./-.o && echo -DCONFIG_CPU_HAS_LSX=1)
- CFLAGS += $(shell echo 'xvld $$xr0, $$zero, 0' | \
- gcc -c -x assembler - >/dev/null 2>&1 && \
- rm ./-.o && echo -DCONFIG_CPU_HAS_LASX=1)
-endif
-
-ifeq ($(IS_X86),yes)
- OBJS += mmx.o sse1.o sse2.o avx2.o recov_ssse3.o recov_avx2.o avx512.o recov_avx512.o
- CFLAGS += -DCONFIG_X86
-else ifeq ($(HAS_NEON),yes)
- OBJS += neon.o neon1.o neon2.o neon4.o neon8.o recov_neon.o recov_neon_inner.o
- CFLAGS += -DCONFIG_KERNEL_MODE_NEON=1
-else ifeq ($(HAS_ALTIVEC),yes)
- CFLAGS += -DCONFIG_ALTIVEC
- OBJS += altivec1.o altivec2.o altivec4.o altivec8.o \
- vpermxor1.o vpermxor2.o vpermxor4.o vpermxor8.o
-else ifeq ($(ARCH),loongarch64)
- OBJS += loongarch_simd.o recov_loongarch_simd.o
-else ifeq ($(HAS_RVV),yes)
- OBJS += rvv.o recov_rvv.o
- CFLAGS += -DCONFIG_RISCV_ISA_V=1
-endif
-
-.c.o:
- $(CC) $(CFLAGS) -c -o $@ $<
-
-%.c: ../%.c
- cp -f $< $@
-
-%.uc: ../%.uc
- cp -f $< $@
-
-all: raid6.a raid6test
-
-raid6.a: $(OBJS)
- rm -f $@
- $(AR) cq $@ $^
- $(RANLIB) $@
-
-raid6test: test.c raid6.a
- $(CC) $(CFLAGS) -o raid6test $^
-
-neon1.c: neon.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=1 < neon.uc > $@
-
-neon2.c: neon.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=2 < neon.uc > $@
-
-neon4.c: neon.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=4 < neon.uc > $@
-
-neon8.c: neon.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=8 < neon.uc > $@
-
-altivec1.c: altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=1 < altivec.uc > $@
-
-altivec2.c: altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=2 < altivec.uc > $@
-
-altivec4.c: altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=4 < altivec.uc > $@
-
-altivec8.c: altivec.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=8 < altivec.uc > $@
-
-vpermxor1.c: vpermxor.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=1 < vpermxor.uc > $@
-
-vpermxor2.c: vpermxor.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=2 < vpermxor.uc > $@
-
-vpermxor4.c: vpermxor.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=4 < vpermxor.uc > $@
-
-vpermxor8.c: vpermxor.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=8 < vpermxor.uc > $@
-
-int1.c: int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=1 < int.uc > $@
-
-int2.c: int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=2 < int.uc > $@
-
-int4.c: int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=4 < int.uc > $@
-
-int8.c: int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=8 < int.uc > $@
-
-int16.c: int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=16 < int.uc > $@
-
-int32.c: int.uc ../unroll.awk
- $(AWK) ../unroll.awk -vN=32 < int.uc > $@
-
-tables.c: mktables
- ./mktables > tables.c
-
-clean:
- rm -f *.o *.a mktables mktables.c *.uc int*.c altivec*.c vpermxor*.c neon*.c tables.c raid6test
-
-spotless: clean
- rm -f *~
+raid6_kunit-y += test.o
diff --git a/lib/raid6/test/test.c b/lib/raid6/test/test.c
index 841a55242aba..9db287b4a48f 100644
--- a/lib/raid6/test/test.c
+++ b/lib/raid6/test/test.c
@@ -1,43 +1,37 @@
// SPDX-License-Identifier: GPL-2.0-or-later
-/* -*- linux-c -*- ------------------------------------------------------- *
- *
- * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved
- *
- * ----------------------------------------------------------------------- */
-
/*
- * raid6test.c
+ * Copyright 2002-2007 H. Peter Anvin - All Rights Reserved
*
- * Test RAID-6 recovery with various algorithms
+ * Test RAID-6 recovery algorithms.
*/
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
+#include <kunit/test.h>
+#include <linux/prandom.h>
#include <linux/raid/pq.h>
-#define NDISKS 16 /* Including P and Q */
+MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING");
+
+#define RAID6_KUNIT_SEED 42
-const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+#define NDISKS 16 /* Including P and Q */
-char *dataptrs[NDISKS];
-char data[NDISKS][PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
-char recovi[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
-char recovj[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE)));
+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 makedata(int start, int stop)
{
- int i, j;
+ int i;
for (i = start; i <= stop; i++) {
- for (j = 0; j < PAGE_SIZE; j++)
- data[i][j] = rand();
-
+ prandom_bytes_state(&rng, data[i], PAGE_SIZE);
dataptrs[i] = data[i];
}
}
-static char disk_type(int d)
+static char member_type(int d)
{
switch (d) {
case NDISKS-2:
@@ -49,104 +43,118 @@ static char disk_type(int d)
}
}
-static int test_disks(int i, int j)
+static void test_disks(struct kunit *test, const struct raid6_calls *calls,
+ const struct raid6_recov_calls *ra, int faila, int failb)
{
- int erra, errb;
-
memset(recovi, 0xf0, PAGE_SIZE);
memset(recovj, 0xba, PAGE_SIZE);
- dataptrs[i] = recovi;
- dataptrs[j] = recovj;
-
- raid6_dual_recov(NDISKS, PAGE_SIZE, i, j, (void **)&dataptrs);
-
- erra = memcmp(data[i], recovi, PAGE_SIZE);
- errb = memcmp(data[j], recovj, PAGE_SIZE);
-
- if (i < NDISKS-2 && j == NDISKS-1) {
- /* We don't implement the DQ failure scenario, since it's
- equivalent to a RAID-5 failure (XOR, then recompute Q) */
- erra = errb = 0;
+ dataptrs[faila] = recovi;
+ dataptrs[failb] = recovj;
+
+ if (failb == NDISKS - 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)
+ goto skip;
+
+ /* P+Q failure. Just rebuild the syndrome. */
+ calls->gen_syndrome(NDISKS, PAGE_SIZE, dataptrs);
+ } else if (failb == NDISKS - 2) {
+ /* data+P failure. */
+ ra->datap(NDISKS, PAGE_SIZE, faila, dataptrs);
} else {
- printf("algo=%-8s faila=%3d(%c) failb=%3d(%c) %s\n",
- raid6_call.name,
- i, disk_type(i),
- j, disk_type(j),
- (!erra && !errb) ? "OK" :
- !erra ? "ERRB" :
- !errb ? "ERRA" : "ERRAB");
+ /* data+data failure. */
+ ra->data2(NDISKS, PAGE_SIZE, faila, failb, dataptrs);
}
- dataptrs[i] = data[i];
- dataptrs[j] = data[j];
-
- return erra || errb;
+ KUNIT_EXPECT_MEMEQ_MSG(test, data[faila], recovi, PAGE_SIZE,
+ "algo=%-8s/%-8s faila miscompared: %3d[%c] (failb=%3d[%c])\n",
+ calls->name, ra->name,
+ faila, member_type(faila),
+ failb, member_type(failb));
+ KUNIT_EXPECT_MEMEQ_MSG(test, data[failb], recovj, PAGE_SIZE,
+ "algo=%-8s/%-8s failb miscompared: %3d[%c] (faila=%3d[%c])\n",
+ calls->name, ra->name,
+ failb, member_type(failb),
+ faila, member_type(faila));
+
+skip:
+ dataptrs[faila] = data[faila];
+ dataptrs[failb] = data[failb];
}
-int main(int argc, char *argv[])
+static void raid6_test(struct kunit *test)
{
const struct raid6_calls *const *algo;
const struct raid6_recov_calls *const *ra;
int i, j, p1, p2;
- int err = 0;
-
- makedata(0, NDISKS-1);
for (ra = raid6_recov_algos; *ra; ra++) {
if ((*ra)->valid && !(*ra)->valid())
continue;
- raid6_2data_recov = (*ra)->data2;
- raid6_datap_recov = (*ra)->datap;
-
- printf("using recovery %s\n", (*ra)->name);
-
for (algo = raid6_algos; *algo; algo++) {
- if ((*algo)->valid && !(*algo)->valid())
- continue;
+ const struct raid6_calls *calls = *algo;
- raid6_call = **algo;
+ if (calls->valid && !calls->valid())
+ continue;
/* Nuke syndromes */
- memset(data[NDISKS-2], 0xee, 2*PAGE_SIZE);
+ memset(data[NDISKS - 2], 0xee, PAGE_SIZE);
+ memset(data[NDISKS - 1], 0xee, PAGE_SIZE);
/* Generate assumed good syndrome */
- raid6_call.gen_syndrome(NDISKS, PAGE_SIZE,
+ calls->gen_syndrome(NDISKS, PAGE_SIZE,
(void **)&dataptrs);
for (i = 0; i < NDISKS-1; i++)
for (j = i+1; j < NDISKS; j++)
- err += test_disks(i, j);
+ test_disks(test, calls, *ra, i, j);
- if (!raid6_call.xor_syndrome)
+ if (!calls->xor_syndrome)
continue;
for (p1 = 0; p1 < NDISKS-2; p1++)
for (p2 = p1; p2 < NDISKS-2; p2++) {
/* Simulate rmw run */
- raid6_call.xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
+ calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
(void **)&dataptrs);
makedata(p1, p2);
- raid6_call.xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
+ calls->xor_syndrome(NDISKS, p1, p2, PAGE_SIZE,
(void **)&dataptrs);
for (i = 0; i < NDISKS-1; i++)
for (j = i+1; j < NDISKS; j++)
- err += test_disks(i, j);
+ test_disks(test, calls,
+ *ra, i, j);
}
}
- printf("\n");
}
+}
- printf("\n");
- /* Pick the best algorithm test */
- raid6_select_algo();
-
- if (err)
- printf("\n*** ERRORS FOUND ***\n");
+static struct kunit_case raid6_test_cases[] = {
+ KUNIT_CASE(raid6_test),
+ {},
+};
- return err;
+static int raid6_suite_init(struct kunit_suite *suite)
+{
+ prandom_seed_state(&rng, RAID6_KUNIT_SEED);
+ makedata(0, NDISKS - 1);
+ return 0;
}
+
+static struct kunit_suite raid6_test_suite = {
+ .name = "raid6",
+ .test_cases = raid6_test_cases,
+ .suite_init = raid6_suite_init,
+};
+kunit_test_suite(raid6_test_suite);
+
+MODULE_DESCRIPTION("Unit test for the RAID P/Q library functions");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related
* [PATCH 02/18] raid6: remove __KERNEL__ ifdefs
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>
With the test code ported to kernel space, none of this is required.
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
---
include/linux/raid/pq.h | 90 --------------------------------
lib/raid6/algos.c | 12 -----
lib/raid6/altivec.uc | 10 +---
lib/raid6/avx2.c | 2 +-
lib/raid6/avx512.c | 2 +-
lib/raid6/loongarch.h | 38 --------------
lib/raid6/loongarch_simd.c | 3 +-
lib/raid6/mktables.c | 14 -----
lib/raid6/mmx.c | 2 +-
lib/raid6/neon.c | 6 ---
lib/raid6/recov_avx2.c | 2 +-
lib/raid6/recov_avx512.c | 2 +-
lib/raid6/recov_loongarch_simd.c | 3 +-
lib/raid6/recov_neon.c | 6 ---
lib/raid6/recov_ssse3.c | 2 +-
lib/raid6/rvv.h | 11 +---
lib/raid6/sse1.c | 2 +-
lib/raid6/sse2.c | 2 +-
lib/raid6/vpermxor.uc | 7 ---
lib/raid6/x86.h | 75 --------------------------
20 files changed, 15 insertions(+), 276 deletions(-)
delete mode 100644 lib/raid6/loongarch.h
delete mode 100644 lib/raid6/x86.h
diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h
index 08c5995ea980..d26788fada58 100644
--- a/include/linux/raid/pq.h
+++ b/include/linux/raid/pq.h
@@ -8,8 +8,6 @@
#ifndef LINUX_RAID_RAID6_H
#define LINUX_RAID_RAID6_H
-#ifdef __KERNEL__
-
#include <linux/blkdev.h>
#include <linux/mm.h>
@@ -19,59 +17,6 @@ static inline void *raid6_get_zero_page(void)
return page_address(ZERO_PAGE(0));
}
-#else /* ! __KERNEL__ */
-/* Used for testing in user space */
-
-#include <errno.h>
-#include <inttypes.h>
-#include <stddef.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <sys/time.h>
-#include <sys/types.h>
-
-/* Not standard, but glibc defines it */
-#define BITS_PER_LONG __WORDSIZE
-
-typedef uint8_t u8;
-typedef uint16_t u16;
-typedef uint32_t u32;
-typedef uint64_t u64;
-
-#ifndef PAGE_SIZE
-# define PAGE_SIZE 4096
-#endif
-#ifndef PAGE_SHIFT
-# define PAGE_SHIFT 12
-#endif
-extern const char raid6_empty_zero_page[PAGE_SIZE];
-
-#define __init
-#define __exit
-#ifndef __attribute_const__
-# define __attribute_const__ __attribute__((const))
-#endif
-#define noinline __attribute__((noinline))
-
-#define preempt_enable()
-#define preempt_disable()
-#define cpu_has_feature(x) 1
-#define enable_kernel_altivec()
-#define disable_kernel_altivec()
-
-#undef EXPORT_SYMBOL
-#define EXPORT_SYMBOL(sym)
-#undef EXPORT_SYMBOL_GPL
-#define EXPORT_SYMBOL_GPL(sym)
-#define MODULE_LICENSE(licence)
-#define MODULE_DESCRIPTION(desc)
-#define subsys_initcall(x)
-#define module_exit(x)
-
-#define IS_ENABLED(x) (x)
-#define CONFIG_RAID6_PQ_BENCHMARK 1
-#endif /* __KERNEL__ */
-
/* Routine choices */
struct raid6_calls {
void (*gen_syndrome)(int, size_t, void **);
@@ -165,39 +110,4 @@ extern void (*raid6_2data_recov)(int disks, size_t bytes, int faila, int failb,
extern void (*raid6_datap_recov)(int disks, size_t bytes, int faila,
void **ptrs);
-/* Some definitions to allow code to be compiled for testing in userspace */
-#ifndef __KERNEL__
-
-# define jiffies raid6_jiffies()
-# define printk printf
-# define pr_err(format, ...) fprintf(stderr, format, ## __VA_ARGS__)
-# define pr_info(format, ...) fprintf(stdout, format, ## __VA_ARGS__)
-# define GFP_KERNEL 0
-# define __get_free_pages(x, y) ((unsigned long)mmap(NULL, PAGE_SIZE << (y), \
- PROT_READ|PROT_WRITE, \
- MAP_PRIVATE|MAP_ANONYMOUS,\
- 0, 0))
-# define free_pages(x, y) munmap((void *)(x), PAGE_SIZE << (y))
-
-static inline void cpu_relax(void)
-{
- /* Nothing */
-}
-
-#undef HZ
-#define HZ 1000
-static inline uint32_t raid6_jiffies(void)
-{
- struct timeval tv;
- gettimeofday(&tv, NULL);
- return tv.tv_sec*1000 + tv.tv_usec/1000;
-}
-
-static inline void *raid6_get_zero_page(void)
-{
- return raid6_empty_zero_page;
-}
-
-#endif /* ! __KERNEL__ */
-
#endif /* LINUX_RAID_RAID6_H */
diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index 5a9f4882e18d..985c60bb00a4 100644
--- a/lib/raid6/algos.c
+++ b/lib/raid6/algos.c
@@ -12,13 +12,8 @@
*/
#include <linux/raid/pq.h>
-#ifndef __KERNEL__
-#include <sys/mman.h>
-#include <stdio.h>
-#else
#include <linux/module.h>
#include <linux/gfp.h>
-#endif
#include <kunit/visibility.h>
struct raid6_calls raid6_call;
@@ -123,14 +118,7 @@ const struct raid6_recov_calls *const raid6_recov_algos[] = {
};
EXPORT_SYMBOL_IF_KUNIT(raid6_recov_algos);
-#ifdef __KERNEL__
#define RAID6_TIME_JIFFIES_LG2 4
-#else
-/* Need more time to be stable in userspace */
-#define RAID6_TIME_JIFFIES_LG2 9
-#define time_before(x, y) ((x) < (y))
-#endif
-
#define RAID6_TEST_DISKS 8
#define RAID6_TEST_DISKS_ORDER 3
diff --git a/lib/raid6/altivec.uc b/lib/raid6/altivec.uc
index d20ed0d11411..2c59963e58f9 100644
--- a/lib/raid6/altivec.uc
+++ b/lib/raid6/altivec.uc
@@ -27,10 +27,8 @@
#ifdef CONFIG_ALTIVEC
#include <altivec.h>
-#ifdef __KERNEL__
-# include <asm/cputable.h>
-# include <asm/switch_to.h>
-#endif /* __KERNEL__ */
+#include <asm/cputable.h>
+#include <asm/switch_to.h>
/*
* This is the C data type to use. We use a vector of
@@ -113,11 +111,7 @@ int raid6_have_altivec(void);
int raid6_have_altivec(void)
{
/* This assumes either all CPUs have Altivec or none does */
-# ifdef __KERNEL__
return cpu_has_feature(CPU_FTR_ALTIVEC);
-# else
- return 1;
-# endif
}
#endif
diff --git a/lib/raid6/avx2.c b/lib/raid6/avx2.c
index 059024234dce..a1a5213918af 100644
--- a/lib/raid6/avx2.c
+++ b/lib/raid6/avx2.c
@@ -14,7 +14,7 @@
*/
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
static const struct raid6_avx2_constants {
u64 x1d[4];
diff --git a/lib/raid6/avx512.c b/lib/raid6/avx512.c
index 009bd0adeebf..874998bcd7d7 100644
--- a/lib/raid6/avx512.c
+++ b/lib/raid6/avx512.c
@@ -18,7 +18,7 @@
*/
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
static const struct raid6_avx512_constants {
u64 x1d[8];
diff --git a/lib/raid6/loongarch.h b/lib/raid6/loongarch.h
deleted file mode 100644
index acfc33ce7056..000000000000
--- a/lib/raid6/loongarch.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/*
- * Copyright (C) 2023 WANG Xuerui <git@xen0n.name>
- *
- * raid6/loongarch.h
- *
- * Definitions common to LoongArch RAID-6 code only
- */
-
-#ifndef _LIB_RAID6_LOONGARCH_H
-#define _LIB_RAID6_LOONGARCH_H
-
-#ifdef __KERNEL__
-
-#include <asm/cpu-features.h>
-#include <asm/fpu.h>
-
-#else /* for user-space testing */
-
-#include <sys/auxv.h>
-
-/* have to supply these defines for glibc 2.37- and musl */
-#ifndef HWCAP_LOONGARCH_LSX
-#define HWCAP_LOONGARCH_LSX (1 << 4)
-#endif
-#ifndef HWCAP_LOONGARCH_LASX
-#define HWCAP_LOONGARCH_LASX (1 << 5)
-#endif
-
-#define kernel_fpu_begin()
-#define kernel_fpu_end()
-
-#define cpu_has_lsx (getauxval(AT_HWCAP) & HWCAP_LOONGARCH_LSX)
-#define cpu_has_lasx (getauxval(AT_HWCAP) & HWCAP_LOONGARCH_LASX)
-
-#endif /* __KERNEL__ */
-
-#endif /* _LIB_RAID6_LOONGARCH_H */
diff --git a/lib/raid6/loongarch_simd.c b/lib/raid6/loongarch_simd.c
index aa5d9f924ca3..72f4d92d4876 100644
--- a/lib/raid6/loongarch_simd.c
+++ b/lib/raid6/loongarch_simd.c
@@ -10,7 +10,8 @@
*/
#include <linux/raid/pq.h>
-#include "loongarch.h"
+#include <asm/cpu-features.h>
+#include <asm/fpu.h>
/*
* The vector algorithms are currently priority 0, which means the generic
diff --git a/lib/raid6/mktables.c b/lib/raid6/mktables.c
index 3be03793237c..3de1dbf6846c 100644
--- a/lib/raid6/mktables.c
+++ b/lib/raid6/mktables.c
@@ -56,9 +56,7 @@ int main(int argc, char *argv[])
uint8_t v;
uint8_t exptbl[256], invtbl[256];
- printf("#ifdef __KERNEL__\n");
printf("#include <linux/export.h>\n");
- printf("#endif\n");
printf("#include <linux/raid/pq.h>\n");
/* Compute multiplication table */
@@ -76,9 +74,7 @@ int main(int argc, char *argv[])
printf("\t},\n");
}
printf("};\n");
- printf("#ifdef __KERNEL__\n");
printf("EXPORT_SYMBOL(raid6_gfmul);\n");
- printf("#endif\n");
/* Compute vector multiplication table */
printf("\nconst u8 __attribute__((aligned(256)))\n"
@@ -101,9 +97,7 @@ int main(int argc, char *argv[])
printf("\t},\n");
}
printf("};\n");
- printf("#ifdef __KERNEL__\n");
printf("EXPORT_SYMBOL(raid6_vgfmul);\n");
- printf("#endif\n");
/* Compute power-of-2 table (exponent) */
v = 1;
@@ -120,9 +114,7 @@ int main(int argc, char *argv[])
}
}
printf("};\n");
- printf("#ifdef __KERNEL__\n");
printf("EXPORT_SYMBOL(raid6_gfexp);\n");
- printf("#endif\n");
/* Compute log-of-2 table */
printf("\nconst u8 __attribute__((aligned(256)))\n"
@@ -140,9 +132,7 @@ int main(int argc, char *argv[])
}
}
printf("};\n");
- printf("#ifdef __KERNEL__\n");
printf("EXPORT_SYMBOL(raid6_gflog);\n");
- printf("#endif\n");
/* Compute inverse table x^-1 == x^254 */
printf("\nconst u8 __attribute__((aligned(256)))\n"
@@ -155,9 +145,7 @@ int main(int argc, char *argv[])
}
}
printf("};\n");
- printf("#ifdef __KERNEL__\n");
printf("EXPORT_SYMBOL(raid6_gfinv);\n");
- printf("#endif\n");
/* Compute inv(2^x + 1) (exponent-xor-inverse) table */
printf("\nconst u8 __attribute__((aligned(256)))\n"
@@ -169,9 +157,7 @@ int main(int argc, char *argv[])
(j == 7) ? '\n' : ' ');
}
printf("};\n");
- printf("#ifdef __KERNEL__\n");
printf("EXPORT_SYMBOL(raid6_gfexi);\n");
- printf("#endif\n");
return 0;
}
diff --git a/lib/raid6/mmx.c b/lib/raid6/mmx.c
index 3a5bf53a297b..e411f0cfbd95 100644
--- a/lib/raid6/mmx.c
+++ b/lib/raid6/mmx.c
@@ -14,7 +14,7 @@
#ifdef CONFIG_X86_32
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
/* Shared with raid6/sse1.c */
const struct raid6_mmx_constants {
diff --git a/lib/raid6/neon.c b/lib/raid6/neon.c
index 6d9474ce6da9..47b8bb0afc65 100644
--- a/lib/raid6/neon.c
+++ b/lib/raid6/neon.c
@@ -6,13 +6,7 @@
*/
#include <linux/raid/pq.h>
-
-#ifdef __KERNEL__
#include <asm/simd.h>
-#else
-#define scoped_ksimd()
-#define cpu_has_neon() (1)
-#endif
/*
* There are 2 reasons these wrappers are kept in a separate compilation unit
diff --git a/lib/raid6/recov_avx2.c b/lib/raid6/recov_avx2.c
index 97d598d2535c..19fbd9c4dce6 100644
--- a/lib/raid6/recov_avx2.c
+++ b/lib/raid6/recov_avx2.c
@@ -5,7 +5,7 @@
*/
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
static int raid6_has_avx2(void)
{
diff --git a/lib/raid6/recov_avx512.c b/lib/raid6/recov_avx512.c
index 7986120ca444..143f4976b2ad 100644
--- a/lib/raid6/recov_avx512.c
+++ b/lib/raid6/recov_avx512.c
@@ -7,7 +7,7 @@
*/
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
static int raid6_has_avx512(void)
{
diff --git a/lib/raid6/recov_loongarch_simd.c b/lib/raid6/recov_loongarch_simd.c
index 93dc515997a1..eb3a1e79f01f 100644
--- a/lib/raid6/recov_loongarch_simd.c
+++ b/lib/raid6/recov_loongarch_simd.c
@@ -11,7 +11,8 @@
*/
#include <linux/raid/pq.h>
-#include "loongarch.h"
+#include <asm/cpu-features.h>
+#include <asm/fpu.h>
/*
* Unlike with the syndrome calculation algorithms, there's no boot-time
diff --git a/lib/raid6/recov_neon.c b/lib/raid6/recov_neon.c
index 9d99aeabd31a..13d5df718c15 100644
--- a/lib/raid6/recov_neon.c
+++ b/lib/raid6/recov_neon.c
@@ -5,14 +5,8 @@
*/
#include <linux/raid/pq.h>
-
-#ifdef __KERNEL__
#include <asm/simd.h>
#include "neon.h"
-#else
-#define scoped_ksimd()
-#define cpu_has_neon() (1)
-#endif
static int raid6_has_neon(void)
{
diff --git a/lib/raid6/recov_ssse3.c b/lib/raid6/recov_ssse3.c
index 2e849185c32b..146cdbf465bd 100644
--- a/lib/raid6/recov_ssse3.c
+++ b/lib/raid6/recov_ssse3.c
@@ -4,7 +4,7 @@
*/
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
static int raid6_has_ssse3(void)
{
diff --git a/lib/raid6/rvv.h b/lib/raid6/rvv.h
index 6d0708a2c8a4..b0a71b375962 100644
--- a/lib/raid6/rvv.h
+++ b/lib/raid6/rvv.h
@@ -7,17 +7,8 @@
* Definitions for RISC-V RAID-6 code
*/
-#ifdef __KERNEL__
-#include <asm/vector.h>
-#else
-#define kernel_vector_begin()
-#define kernel_vector_end()
-#include <sys/auxv.h>
-#include <asm/hwcap.h>
-#define has_vector() (getauxval(AT_HWCAP) & COMPAT_HWCAP_ISA_V)
-#endif
-
#include <linux/raid/pq.h>
+#include <asm/vector.h>
static int rvv_has_vector(void)
{
diff --git a/lib/raid6/sse1.c b/lib/raid6/sse1.c
index 692fa3a93bf0..794d5cfa0306 100644
--- a/lib/raid6/sse1.c
+++ b/lib/raid6/sse1.c
@@ -19,7 +19,7 @@
#ifdef CONFIG_X86_32
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
/* Defined in raid6/mmx.c */
extern const struct raid6_mmx_constants {
diff --git a/lib/raid6/sse2.c b/lib/raid6/sse2.c
index 2930220249c9..f9edf8a8d1c4 100644
--- a/lib/raid6/sse2.c
+++ b/lib/raid6/sse2.c
@@ -13,7 +13,7 @@
*/
#include <linux/raid/pq.h>
-#include "x86.h"
+#include <asm/fpu/api.h>
static const struct raid6_sse_constants {
u64 x1d[2];
diff --git a/lib/raid6/vpermxor.uc b/lib/raid6/vpermxor.uc
index 1bfb127fbfe8..a8e76b1c956e 100644
--- a/lib/raid6/vpermxor.uc
+++ b/lib/raid6/vpermxor.uc
@@ -25,10 +25,8 @@
#include <altivec.h>
#include <asm/ppc-opcode.h>
-#ifdef __KERNEL__
#include <asm/cputable.h>
#include <asm/switch_to.h>
-#endif
typedef vector unsigned char unative_t;
#define NSIZE sizeof(unative_t)
@@ -85,13 +83,8 @@ int raid6_have_altivec_vpermxor(void);
int raid6_have_altivec_vpermxor(void)
{
/* Check if arch has both altivec and the vpermxor instructions */
-# ifdef __KERNEL__
return (cpu_has_feature(CPU_FTR_ALTIVEC_COMP) &&
cpu_has_feature(CPU_FTR_ARCH_207S));
-# else
- return 1;
-#endif
-
}
#endif
diff --git a/lib/raid6/x86.h b/lib/raid6/x86.h
deleted file mode 100644
index 9a6ff37115e7..000000000000
--- a/lib/raid6/x86.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-or-later */
-/* ----------------------------------------------------------------------- *
- *
- * Copyright 2002-2004 H. Peter Anvin - All Rights Reserved
- *
- * ----------------------------------------------------------------------- */
-
-/*
- * raid6/x86.h
- *
- * Definitions common to x86 and x86-64 RAID-6 code only
- */
-
-#ifndef LINUX_RAID_RAID6X86_H
-#define LINUX_RAID_RAID6X86_H
-
-#if (defined(__i386__) || defined(__x86_64__)) && !defined(__arch_um__)
-
-#ifdef __KERNEL__ /* Real code */
-
-#include <asm/fpu/api.h>
-
-#else /* Dummy code for user space testing */
-
-static inline void kernel_fpu_begin(void)
-{
-}
-
-static inline void kernel_fpu_end(void)
-{
-}
-
-#define __aligned(x) __attribute__((aligned(x)))
-
-#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */
-#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions
- * (fast save and restore) */
-#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */
-#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */
-#define X86_FEATURE_XMM3 (4*32+ 0) /* "pni" SSE-3 */
-#define X86_FEATURE_SSSE3 (4*32+ 9) /* Supplemental SSE-3 */
-#define X86_FEATURE_AVX (4*32+28) /* Advanced Vector Extensions */
-#define X86_FEATURE_AVX2 (9*32+ 5) /* AVX2 instructions */
-#define X86_FEATURE_AVX512F (9*32+16) /* AVX-512 Foundation */
-#define X86_FEATURE_AVX512DQ (9*32+17) /* AVX-512 DQ (Double/Quad granular)
- * Instructions
- */
-#define X86_FEATURE_AVX512BW (9*32+30) /* AVX-512 BW (Byte/Word granular)
- * Instructions
- */
-#define X86_FEATURE_AVX512VL (9*32+31) /* AVX-512 VL (128/256 Vector Length)
- * Extensions
- */
-#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */
-
-/* Should work well enough on modern CPUs for testing */
-static inline int boot_cpu_has(int flag)
-{
- u32 eax, ebx, ecx, edx;
-
- eax = (flag & 0x100) ? 7 :
- (flag & 0x20) ? 0x80000001 : 1;
- ecx = 0;
-
- asm volatile("cpuid"
- : "+a" (eax), "=b" (ebx), "=d" (edx), "+c" (ecx));
-
- return ((flag & 0x100 ? ebx :
- (flag & 0x80) ? ecx : edx) >> (flag & 31)) & 1;
-}
-
-#endif /* ndef __KERNEL__ */
-
-#endif
-#endif
--
2.53.0
^ permalink raw reply related
* [PATCH 03/18] raid6: move to lib/raid/
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 raid6 code to live in lib/raid/ with the XOR code, and change
the internal organization so that each architecture has a subdirectory
similar to the CRC, crypto and XOR libraries, and fix up the Makefile to
only build files actually needed.
Also move the kunit test case from the history test/ subdirectory to
tests/ and use the normal naming scheme for it.
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
---
MAINTAINERS | 2 +-
lib/Kconfig | 22 ----
lib/Makefile | 1 -
lib/raid/Kconfig | 22 ++++
lib/raid/Makefile | 2 +-
lib/{ => raid}/raid6/.gitignore | 0
lib/raid/raid6/Makefile | 122 ++++++++++++++++++
lib/{ => raid}/raid6/algos.c | 0
lib/{raid6 => raid/raid6/arm}/neon.c | 0
lib/{raid6 => raid/raid6/arm}/neon.h | 0
lib/{raid6 => raid/raid6/arm}/neon.uc | 2 +-
lib/{raid6 => raid/raid6/arm}/recov_neon.c | 2 +-
.../raid6/arm}/recov_neon_inner.c | 2 +-
lib/{ => raid}/raid6/int.uc | 0
.../raid6/loongarch}/loongarch_simd.c | 0
.../raid6/loongarch}/recov_loongarch_simd.c | 0
lib/{ => raid}/raid6/mktables.c | 0
lib/{raid6 => raid/raid6/powerpc}/altivec.uc | 4 -
lib/{raid6 => raid/raid6/powerpc}/vpermxor.uc | 3 -
lib/{ => raid}/raid6/recov.c | 0
lib/{raid6 => raid/raid6/riscv}/recov_rvv.c | 0
lib/{raid6 => raid/raid6/riscv}/rvv.c | 0
lib/{raid6 => raid/raid6/riscv}/rvv.h | 0
lib/{raid6 => raid/raid6/s390}/recov_s390xc.c | 0
lib/{raid6 => raid/raid6/s390}/s390vx.uc | 0
lib/{raid6/test => raid/raid6/tests}/Makefile | 2 -
.../test.c => raid/raid6/tests/raid6_kunit.c} | 0
lib/{ => raid}/raid6/unroll.awk | 0
lib/{raid6 => raid/raid6/x86}/avx2.c | 0
lib/{raid6 => raid/raid6/x86}/avx512.c | 0
lib/{raid6 => raid/raid6/x86}/mmx.c | 4 -
lib/{raid6 => raid/raid6/x86}/recov_avx2.c | 0
lib/{raid6 => raid/raid6/x86}/recov_avx512.c | 0
lib/{raid6 => raid/raid6/x86}/recov_ssse3.c | 0
lib/{raid6 => raid/raid6/x86}/sse1.c | 4 -
lib/{raid6 => raid/raid6/x86}/sse2.c | 0
lib/raid6/Makefile | 83 ------------
lib/raid6/test/.gitignore | 3 -
38 files changed, 149 insertions(+), 131 deletions(-)
rename lib/{ => raid}/raid6/.gitignore (100%)
create mode 100644 lib/raid/raid6/Makefile
rename lib/{ => raid}/raid6/algos.c (100%)
rename lib/{raid6 => raid/raid6/arm}/neon.c (100%)
rename lib/{raid6 => raid/raid6/arm}/neon.h (100%)
rename lib/{raid6 => raid/raid6/arm}/neon.uc (99%)
rename lib/{raid6 => raid/raid6/arm}/recov_neon.c (99%)
rename lib/{raid6 => raid/raid6/arm}/recov_neon_inner.c (99%)
rename lib/{ => raid}/raid6/int.uc (100%)
rename lib/{raid6 => raid/raid6/loongarch}/loongarch_simd.c (100%)
rename lib/{raid6 => raid/raid6/loongarch}/recov_loongarch_simd.c (100%)
rename lib/{ => raid}/raid6/mktables.c (100%)
rename lib/{raid6 => raid/raid6/powerpc}/altivec.uc (98%)
rename lib/{raid6 => raid/raid6/powerpc}/vpermxor.uc (98%)
rename lib/{ => raid}/raid6/recov.c (100%)
rename lib/{raid6 => raid/raid6/riscv}/recov_rvv.c (100%)
rename lib/{raid6 => raid/raid6/riscv}/rvv.c (100%)
rename lib/{raid6 => raid/raid6/riscv}/rvv.h (100%)
rename lib/{raid6 => raid/raid6/s390}/recov_s390xc.c (100%)
rename lib/{raid6 => raid/raid6/s390}/s390vx.uc (100%)
rename lib/{raid6/test => raid/raid6/tests}/Makefile (77%)
rename lib/{raid6/test/test.c => raid/raid6/tests/raid6_kunit.c} (100%)
rename lib/{ => raid}/raid6/unroll.awk (100%)
rename lib/{raid6 => raid/raid6/x86}/avx2.c (100%)
rename lib/{raid6 => raid/raid6/x86}/avx512.c (100%)
rename lib/{raid6 => raid/raid6/x86}/mmx.c (99%)
rename lib/{raid6 => raid/raid6/x86}/recov_avx2.c (100%)
rename lib/{raid6 => raid/raid6/x86}/recov_avx512.c (100%)
rename lib/{raid6 => raid/raid6/x86}/recov_ssse3.c (100%)
rename lib/{raid6 => raid/raid6/x86}/sse1.c (99%)
rename lib/{raid6 => raid/raid6/x86}/sse2.c (100%)
delete mode 100644 lib/raid6/Makefile
delete mode 100644 lib/raid6/test/.gitignore
diff --git a/MAINTAINERS b/MAINTAINERS
index c2c6d79275c6..e6f778339a3f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24821,7 +24821,7 @@ F: drivers/md/md*
F: drivers/md/raid*
F: include/linux/raid/
F: include/uapi/linux/raid/
-F: lib/raid6/
+F: lib/raid/raid6/
SOLIDRUN CLEARFOG SUPPORT
M: Russell King <linux@armlinux.org.uk>
diff --git a/lib/Kconfig b/lib/Kconfig
index bffe015a6c10..b87f954a14bc 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -8,28 +8,6 @@ config BINARY_PRINTF
menu "Library routines"
-config RAID6_PQ
- tristate
-
-config RAID6_PQ_KUNIT_TEST
- tristate "KUnit tests for RAID6 PQ functions" if !KUNIT_ALL_TESTS
- depends on KUNIT
- depends on RAID6_PQ
- default KUNIT_ALL_TESTS
- help
- Unit tests for the RAID6 PQ library functions.
-
- This is intended to help people writing architecture-specific
- optimized versions. If unsure, say N.
-
-config RAID6_PQ_BENCHMARK
- bool "Automatically choose fastest RAID6 PQ functions"
- depends on RAID6_PQ
- default y
- help
- Benchmark all available RAID6 PQ functions on init and choose the
- fastest one.
-
config LINEAR_RANGES
tristate
diff --git a/lib/Makefile b/lib/Makefile
index f33a24bf1c19..6e72d2c1cce7 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -167,7 +167,6 @@ obj-$(CONFIG_LZ4_DECOMPRESS) += lz4/
obj-$(CONFIG_ZSTD_COMPRESS) += zstd/
obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd/
obj-$(CONFIG_XZ_DEC) += xz/
-obj-$(CONFIG_RAID6_PQ) += raid6/
lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o
lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o
diff --git a/lib/raid/Kconfig b/lib/raid/Kconfig
index 5ab2b0a7be4c..e39f6d667792 100644
--- a/lib/raid/Kconfig
+++ b/lib/raid/Kconfig
@@ -28,3 +28,25 @@ config XOR_KUNIT_TEST
This is intended to help people writing architecture-specific
optimized versions. If unsure, say N.
+
+config RAID6_PQ
+ tristate
+
+config RAID6_PQ_KUNIT_TEST
+ tristate "KUnit tests for RAID6 PQ functions" if !KUNIT_ALL_TESTS
+ depends on KUNIT
+ depends on RAID6_PQ
+ default KUNIT_ALL_TESTS
+ help
+ Unit tests for the RAID6 PQ library functions.
+
+ This is intended to help people writing architecture-specific
+ optimized versions. If unsure, say N.
+
+config RAID6_PQ_BENCHMARK
+ bool "Automatically choose fastest RAID6 PQ functions"
+ depends on RAID6_PQ
+ default y
+ help
+ Benchmark all available RAID6 PQ functions on init and choose the
+ fastest one.
diff --git a/lib/raid/Makefile b/lib/raid/Makefile
index 3540fe846dc4..6fc5eeb53df0 100644
--- a/lib/raid/Makefile
+++ b/lib/raid/Makefile
@@ -1,3 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
-obj-y += xor/
+obj-y += xor/ raid6/
diff --git a/lib/raid6/.gitignore b/lib/raid/raid6/.gitignore
similarity index 100%
rename from lib/raid6/.gitignore
rename to lib/raid/raid6/.gitignore
diff --git a/lib/raid/raid6/Makefile b/lib/raid/raid6/Makefile
new file mode 100644
index 000000000000..7cb31b8a5c17
--- /dev/null
+++ b/lib/raid/raid6/Makefile
@@ -0,0 +1,122 @@
+# SPDX-License-Identifier: GPL-2.0
+
+ccflags-y += -I $(src)
+
+obj-$(CONFIG_RAID6_PQ) += raid6_pq.o tests/
+
+raid6_pq-y += algos.o tables.o
+
+# generic integer generation and recovery implementation
+raid6_pq-y += int1.o int2.o int4.o int8.o
+raid6_pq-y += recov.o
+
+# architecture-specific generation and recovery implementations:
+raid6_pq-$(CONFIG_KERNEL_MODE_NEON) += arm/neon.o \
+ arm/neon1.o \
+ arm/neon2.o \
+ arm/neon4.o \
+ arm/neon8.o \
+ arm/recov_neon.o \
+ arm/recov_neon_inner.o
+raid6_pq-$(CONFIG_LOONGARCH) += loongarch/loongarch_simd.o \
+ loongarch/recov_loongarch_simd.o
+raid6_pq-$(CONFIG_ALTIVEC) += powerpc/altivec1.o \
+ powerpc/altivec2.o \
+ powerpc/altivec4.o \
+ powerpc/altivec8.o \
+ powerpc/vpermxor1.o \
+ powerpc/vpermxor2.o \
+ powerpc/vpermxor4.o \
+ powerpc/vpermxor8.o
+raid6_pq-$(CONFIG_RISCV_ISA_V) += riscv/rvv.o \
+ riscv/recov_rvv.o
+raid6_pq-$(CONFIG_S390) += s390/s390vx8.o \
+ s390/recov_s390xc.o
+ifeq ($(CONFIG_X86),y)
+raid6_pq-$(CONFIG_X86_32) += x86/mmx.o \
+ x86/sse1.o
+endif
+raid6_pq-$(CONFIG_X86) += x86/sse2.o \
+ x86/avx2.o \
+ x86/avx512.o \
+ x86/recov_ssse3.o \
+ x86/recov_avx2.o \
+ x86/recov_avx512.o
+
+hostprogs += mktables
+
+CFLAGS_arm/neon1.o += $(CC_FLAGS_FPU)
+CFLAGS_arm/neon2.o += $(CC_FLAGS_FPU)
+CFLAGS_arm/neon4.o += $(CC_FLAGS_FPU)
+CFLAGS_arm/neon8.o += $(CC_FLAGS_FPU)
+CFLAGS_arm/recov_neon_inner.o += $(CC_FLAGS_FPU)
+CFLAGS_REMOVE_arm/neon1.o += $(CC_FLAGS_NO_FPU)
+CFLAGS_REMOVE_arm/neon2.o += $(CC_FLAGS_NO_FPU)
+CFLAGS_REMOVE_arm/neon4.o += $(CC_FLAGS_NO_FPU)
+CFLAGS_REMOVE_arm/neon8.o += $(CC_FLAGS_NO_FPU)
+CFLAGS_REMOVE_arm/recov_neon_inner.o += $(CC_FLAGS_NO_FPU)
+
+ifeq ($(CONFIG_ALTIVEC),y)
+altivec_flags := -maltivec $(call cc-option,-mabi=altivec)
+# Enable <altivec.h>
+altivec_flags += -isystem $(shell $(CC) -print-file-name=include)
+
+CFLAGS_powerpc/altivec1.o += $(altivec_flags)
+CFLAGS_powerpc/altivec2.o += $(altivec_flags)
+CFLAGS_powerpc/altivec4.o += $(altivec_flags)
+CFLAGS_powerpc/altivec8.o += $(altivec_flags)
+CFLAGS_powerpc/vpermxor1.o += $(altivec_flags)
+CFLAGS_powerpc/vpermxor2.o += $(altivec_flags)
+CFLAGS_powerpc/vpermxor4.o += $(altivec_flags)
+CFLAGS_powerpc/vpermxor8.o += $(altivec_flags)
+
+ifdef CONFIG_CC_IS_CLANG
+# clang ppc port does not yet support -maltivec when -msoft-float is
+# enabled. A future release of clang will resolve this
+# https://llvm.org/pr31177
+CFLAGS_REMOVE_powerpc/altivec1.o += -msoft-float
+CFLAGS_REMOVE_powerpc/altivec2.o += -msoft-float
+CFLAGS_REMOVE_powerpc/altivec4.o += -msoft-float
+CFLAGS_REMOVE_powerpc/altivec8.o += -msoft-float
+CFLAGS_REMOVE_powerpc/vpermxor1.o += -msoft-float
+CFLAGS_REMOVE_powerpc/vpermxor2.o += -msoft-float
+CFLAGS_REMOVE_powerpc/vpermxor4.o += -msoft-float
+CFLAGS_REMOVE_powerpc/vpermxor8.o += -msoft-float
+endif # CONFIG_CC_IS_CLANG
+endif # CONFIG_ALTIVEC
+
+quiet_cmd_mktable = TABLE $@
+ cmd_mktable = $(obj)/mktables > $@
+
+targets += tables.c
+$(obj)/tables.c: $(obj)/mktables FORCE
+ $(call if_changed,mktable)
+
+quiet_cmd_unroll = UNROLL $@
+ cmd_unroll = $(AWK) -v N=$* -f $(src)/unroll.awk < $< > $@
+
+targets += int1.c int2.c int4.c int8.c
+$(obj)/int%.c: $(src)/int.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
+
+targets += arm/neon1.c arm/neon2.c arm/neon4.c arm/neon8.c
+$(obj)/arm/neon%.c: $(src)/arm/neon.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
+
+targets += powerpc/altivec1.c \
+ powerpc/altivec2.c \
+ powerpc/altivec4.c \
+ powerpc/altivec8.c
+$(obj)/powerpc/altivec%.c: $(src)/powerpc/altivec.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
+
+targets += powerpc/vpermxor1.c \
+ powerpc/vpermxor2.c \
+ powerpc/vpermxor4.c \
+ powerpc/vpermxor8.c
+$(obj)/powerpc/vpermxor%.c: $(src)/powerpc/vpermxor.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
+
+targets += s390/s390vx8.c
+$(obj)/s390/s390vx%.c: $(src)/s390/s390vx.uc $(src)/unroll.awk FORCE
+ $(call if_changed,unroll)
diff --git a/lib/raid6/algos.c b/lib/raid/raid6/algos.c
similarity index 100%
rename from lib/raid6/algos.c
rename to lib/raid/raid6/algos.c
diff --git a/lib/raid6/neon.c b/lib/raid/raid6/arm/neon.c
similarity index 100%
rename from lib/raid6/neon.c
rename to lib/raid/raid6/arm/neon.c
diff --git a/lib/raid6/neon.h b/lib/raid/raid6/arm/neon.h
similarity index 100%
rename from lib/raid6/neon.h
rename to lib/raid/raid6/arm/neon.h
diff --git a/lib/raid6/neon.uc b/lib/raid/raid6/arm/neon.uc
similarity index 99%
rename from lib/raid6/neon.uc
rename to lib/raid/raid6/arm/neon.uc
index 355270af0cd6..14a9fc2c60fa 100644
--- a/lib/raid6/neon.uc
+++ b/lib/raid/raid6/arm/neon.uc
@@ -25,7 +25,7 @@
*/
#include <arm_neon.h>
-#include "neon.h"
+#include "arm/neon.h"
typedef uint8x16_t unative_t;
diff --git a/lib/raid6/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c
similarity index 99%
rename from lib/raid6/recov_neon.c
rename to lib/raid/raid6/arm/recov_neon.c
index 13d5df718c15..5a48fcc762e8 100644
--- a/lib/raid6/recov_neon.c
+++ b/lib/raid/raid6/arm/recov_neon.c
@@ -6,7 +6,7 @@
#include <linux/raid/pq.h>
#include <asm/simd.h>
-#include "neon.h"
+#include "arm/neon.h"
static int raid6_has_neon(void)
{
diff --git a/lib/raid6/recov_neon_inner.c b/lib/raid/raid6/arm/recov_neon_inner.c
similarity index 99%
rename from lib/raid6/recov_neon_inner.c
rename to lib/raid/raid6/arm/recov_neon_inner.c
index f9e7e8f5a151..53c355efa7ff 100644
--- a/lib/raid6/recov_neon_inner.c
+++ b/lib/raid/raid6/arm/recov_neon_inner.c
@@ -5,7 +5,7 @@
*/
#include <arm_neon.h>
-#include "neon.h"
+#include "arm/neon.h"
#ifdef CONFIG_ARM
/*
diff --git a/lib/raid6/int.uc b/lib/raid/raid6/int.uc
similarity index 100%
rename from lib/raid6/int.uc
rename to lib/raid/raid6/int.uc
diff --git a/lib/raid6/loongarch_simd.c b/lib/raid/raid6/loongarch/loongarch_simd.c
similarity index 100%
rename from lib/raid6/loongarch_simd.c
rename to lib/raid/raid6/loongarch/loongarch_simd.c
diff --git a/lib/raid6/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c
similarity index 100%
rename from lib/raid6/recov_loongarch_simd.c
rename to lib/raid/raid6/loongarch/recov_loongarch_simd.c
diff --git a/lib/raid6/mktables.c b/lib/raid/raid6/mktables.c
similarity index 100%
rename from lib/raid6/mktables.c
rename to lib/raid/raid6/mktables.c
diff --git a/lib/raid6/altivec.uc b/lib/raid/raid6/powerpc/altivec.uc
similarity index 98%
rename from lib/raid6/altivec.uc
rename to lib/raid/raid6/powerpc/altivec.uc
index 2c59963e58f9..130d3d3dd42c 100644
--- a/lib/raid6/altivec.uc
+++ b/lib/raid/raid6/powerpc/altivec.uc
@@ -24,8 +24,6 @@
#include <linux/raid/pq.h>
-#ifdef CONFIG_ALTIVEC
-
#include <altivec.h>
#include <asm/cputable.h>
#include <asm/switch_to.h>
@@ -122,5 +120,3 @@ const struct raid6_calls raid6_altivec$# = {
"altivecx$#",
0
};
-
-#endif /* CONFIG_ALTIVEC */
diff --git a/lib/raid6/vpermxor.uc b/lib/raid/raid6/powerpc/vpermxor.uc
similarity index 98%
rename from lib/raid6/vpermxor.uc
rename to lib/raid/raid6/powerpc/vpermxor.uc
index a8e76b1c956e..595f20aaf4cf 100644
--- a/lib/raid6/vpermxor.uc
+++ b/lib/raid/raid6/powerpc/vpermxor.uc
@@ -21,8 +21,6 @@
*/
#include <linux/raid/pq.h>
-#ifdef CONFIG_ALTIVEC
-
#include <altivec.h>
#include <asm/ppc-opcode.h>
#include <asm/cputable.h>
@@ -95,4 +93,3 @@ const struct raid6_calls raid6_vpermxor$# = {
"vpermxor$#",
0
};
-#endif
diff --git a/lib/raid6/recov.c b/lib/raid/raid6/recov.c
similarity index 100%
rename from lib/raid6/recov.c
rename to lib/raid/raid6/recov.c
diff --git a/lib/raid6/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c
similarity index 100%
rename from lib/raid6/recov_rvv.c
rename to lib/raid/raid6/riscv/recov_rvv.c
diff --git a/lib/raid6/rvv.c b/lib/raid/raid6/riscv/rvv.c
similarity index 100%
rename from lib/raid6/rvv.c
rename to lib/raid/raid6/riscv/rvv.c
diff --git a/lib/raid6/rvv.h b/lib/raid/raid6/riscv/rvv.h
similarity index 100%
rename from lib/raid6/rvv.h
rename to lib/raid/raid6/riscv/rvv.h
diff --git a/lib/raid6/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c
similarity index 100%
rename from lib/raid6/recov_s390xc.c
rename to lib/raid/raid6/s390/recov_s390xc.c
diff --git a/lib/raid6/s390vx.uc b/lib/raid/raid6/s390/s390vx.uc
similarity index 100%
rename from lib/raid6/s390vx.uc
rename to lib/raid/raid6/s390/s390vx.uc
diff --git a/lib/raid6/test/Makefile b/lib/raid/raid6/tests/Makefile
similarity index 77%
rename from lib/raid6/test/Makefile
rename to lib/raid/raid6/tests/Makefile
index 520381ea71d7..87a001b22847 100644
--- a/lib/raid6/test/Makefile
+++ b/lib/raid/raid6/tests/Makefile
@@ -1,5 +1,3 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_RAID6_PQ_KUNIT_TEST) += raid6_kunit.o
-
-raid6_kunit-y += test.o
diff --git a/lib/raid6/test/test.c b/lib/raid/raid6/tests/raid6_kunit.c
similarity index 100%
rename from lib/raid6/test/test.c
rename to lib/raid/raid6/tests/raid6_kunit.c
diff --git a/lib/raid6/unroll.awk b/lib/raid/raid6/unroll.awk
similarity index 100%
rename from lib/raid6/unroll.awk
rename to lib/raid/raid6/unroll.awk
diff --git a/lib/raid6/avx2.c b/lib/raid/raid6/x86/avx2.c
similarity index 100%
rename from lib/raid6/avx2.c
rename to lib/raid/raid6/x86/avx2.c
diff --git a/lib/raid6/avx512.c b/lib/raid/raid6/x86/avx512.c
similarity index 100%
rename from lib/raid6/avx512.c
rename to lib/raid/raid6/x86/avx512.c
diff --git a/lib/raid6/mmx.c b/lib/raid/raid6/x86/mmx.c
similarity index 99%
rename from lib/raid6/mmx.c
rename to lib/raid/raid6/x86/mmx.c
index e411f0cfbd95..7e9810669347 100644
--- a/lib/raid6/mmx.c
+++ b/lib/raid/raid6/x86/mmx.c
@@ -11,8 +11,6 @@
* MMX implementation of RAID-6 syndrome functions
*/
-#ifdef CONFIG_X86_32
-
#include <linux/raid/pq.h>
#include <asm/fpu/api.h>
@@ -135,5 +133,3 @@ const struct raid6_calls raid6_mmxx2 = {
"mmxx2",
0
};
-
-#endif
diff --git a/lib/raid6/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c
similarity index 100%
rename from lib/raid6/recov_avx2.c
rename to lib/raid/raid6/x86/recov_avx2.c
diff --git a/lib/raid6/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c
similarity index 100%
rename from lib/raid6/recov_avx512.c
rename to lib/raid/raid6/x86/recov_avx512.c
diff --git a/lib/raid6/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c
similarity index 100%
rename from lib/raid6/recov_ssse3.c
rename to lib/raid/raid6/x86/recov_ssse3.c
diff --git a/lib/raid6/sse1.c b/lib/raid/raid6/x86/sse1.c
similarity index 99%
rename from lib/raid6/sse1.c
rename to lib/raid/raid6/x86/sse1.c
index 794d5cfa0306..deecdd72ceec 100644
--- a/lib/raid6/sse1.c
+++ b/lib/raid/raid6/x86/sse1.c
@@ -16,8 +16,6 @@
* worthwhile as a separate implementation.
*/
-#ifdef CONFIG_X86_32
-
#include <linux/raid/pq.h>
#include <asm/fpu/api.h>
@@ -155,5 +153,3 @@ const struct raid6_calls raid6_sse1x2 = {
"sse1x2",
1 /* Has cache hints */
};
-
-#endif
diff --git a/lib/raid6/sse2.c b/lib/raid/raid6/x86/sse2.c
similarity index 100%
rename from lib/raid6/sse2.c
rename to lib/raid/raid6/x86/sse2.c
diff --git a/lib/raid6/Makefile b/lib/raid6/Makefile
deleted file mode 100644
index 6fd048c127b6..000000000000
--- a/lib/raid6/Makefile
+++ /dev/null
@@ -1,83 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-obj-$(CONFIG_RAID6_PQ) += raid6_pq.o test/
-
-raid6_pq-y += algos.o recov.o tables.o int1.o int2.o int4.o \
- int8.o
-
-raid6_pq-$(CONFIG_X86) += recov_ssse3.o recov_avx2.o mmx.o sse1.o sse2.o avx2.o avx512.o recov_avx512.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_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
-
-hostprogs += mktables
-
-ifeq ($(CONFIG_ALTIVEC),y)
-altivec_flags := -maltivec $(call cc-option,-mabi=altivec)
-# Enable <altivec.h>
-altivec_flags += -isystem $(shell $(CC) -print-file-name=include)
-
-ifdef CONFIG_CC_IS_CLANG
-# clang ppc port does not yet support -maltivec when -msoft-float is
-# enabled. A future release of clang will resolve this
-# https://llvm.org/pr31177
-CFLAGS_REMOVE_altivec1.o += -msoft-float
-CFLAGS_REMOVE_altivec2.o += -msoft-float
-CFLAGS_REMOVE_altivec4.o += -msoft-float
-CFLAGS_REMOVE_altivec8.o += -msoft-float
-CFLAGS_REMOVE_vpermxor1.o += -msoft-float
-CFLAGS_REMOVE_vpermxor2.o += -msoft-float
-CFLAGS_REMOVE_vpermxor4.o += -msoft-float
-CFLAGS_REMOVE_vpermxor8.o += -msoft-float
-endif
-endif
-
-quiet_cmd_unroll = UNROLL $@
- cmd_unroll = $(AWK) -v N=$* -f $(src)/unroll.awk < $< > $@
-
-targets += int1.c int2.c int4.c int8.c
-$(obj)/int%.c: $(src)/int.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-CFLAGS_altivec1.o += $(altivec_flags)
-CFLAGS_altivec2.o += $(altivec_flags)
-CFLAGS_altivec4.o += $(altivec_flags)
-CFLAGS_altivec8.o += $(altivec_flags)
-targets += altivec1.c altivec2.c altivec4.c altivec8.c
-$(obj)/altivec%.c: $(src)/altivec.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-CFLAGS_vpermxor1.o += $(altivec_flags)
-CFLAGS_vpermxor2.o += $(altivec_flags)
-CFLAGS_vpermxor4.o += $(altivec_flags)
-CFLAGS_vpermxor8.o += $(altivec_flags)
-targets += vpermxor1.c vpermxor2.c vpermxor4.c vpermxor8.c
-$(obj)/vpermxor%.c: $(src)/vpermxor.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-CFLAGS_neon1.o += $(CC_FLAGS_FPU)
-CFLAGS_neon2.o += $(CC_FLAGS_FPU)
-CFLAGS_neon4.o += $(CC_FLAGS_FPU)
-CFLAGS_neon8.o += $(CC_FLAGS_FPU)
-CFLAGS_recov_neon_inner.o += $(CC_FLAGS_FPU)
-CFLAGS_REMOVE_neon1.o += $(CC_FLAGS_NO_FPU)
-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)
-targets += neon1.c neon2.c neon4.c neon8.c
-$(obj)/neon%.c: $(src)/neon.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-targets += s390vx8.c
-$(obj)/s390vx%.c: $(src)/s390vx.uc $(src)/unroll.awk FORCE
- $(call if_changed,unroll)
-
-quiet_cmd_mktable = TABLE $@
- cmd_mktable = $(obj)/mktables > $@
-
-targets += tables.c
-$(obj)/tables.c: $(obj)/mktables FORCE
- $(call if_changed,mktable)
diff --git a/lib/raid6/test/.gitignore b/lib/raid6/test/.gitignore
deleted file mode 100644
index 1b68a77f348f..000000000000
--- a/lib/raid6/test/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-/int.uc
-/neon.uc
-/raid6test
--
2.53.0
^ permalink raw reply related
* [PATCH 04/18] raid6: remove unused defines in pq.h
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>
These are not used anywhere in the kernel.
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
---
include/linux/raid/pq.h | 6 ------
1 file changed, 6 deletions(-)
diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h
index d26788fada58..5e7e743b83f5 100644
--- a/include/linux/raid/pq.h
+++ b/include/linux/raid/pq.h
@@ -90,12 +90,6 @@ extern const struct raid6_calls raid6_neonx8;
extern const struct raid6_calls * const raid6_algos[];
extern const struct raid6_recov_calls *const raid6_recov_algos[];
-/* Return values from chk_syndrome */
-#define RAID6_OK 0
-#define RAID6_P_BAD 1
-#define RAID6_Q_BAD 2
-#define RAID6_PQ_BAD 3
-
/* Galois field tables */
extern const u8 raid6_gfmul[256][256] __attribute__((aligned(256)));
extern const u8 raid6_vgfmul[256][32] __attribute__((aligned(256)));
--
2.53.0
^ permalink raw reply related
* [PATCH 05/18] raid6: remove raid6_get_zero_page
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>
Just open code it as in other places in the kernel.
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
---
crypto/async_tx/async_pq.c | 2 +-
crypto/async_tx/async_raid6_recov.c | 4 ++--
include/linux/raid/pq.h | 6 ------
lib/raid/raid6/arm/recov_neon.c | 6 +++---
lib/raid/raid6/loongarch/recov_loongarch_simd.c | 12 ++++++------
lib/raid/raid6/recov.c | 6 +++---
lib/raid/raid6/riscv/recov_rvv.c | 6 +++---
lib/raid/raid6/s390/recov_s390xc.c | 6 +++---
lib/raid/raid6/x86/recov_avx2.c | 6 +++---
lib/raid/raid6/x86/recov_avx512.c | 6 +++---
lib/raid/raid6/x86/recov_ssse3.c | 6 +++---
11 files changed, 30 insertions(+), 36 deletions(-)
diff --git a/crypto/async_tx/async_pq.c b/crypto/async_tx/async_pq.c
index 9e4bb7fbde25..0ce6f07b4e0d 100644
--- a/crypto/async_tx/async_pq.c
+++ b/crypto/async_tx/async_pq.c
@@ -119,7 +119,7 @@ do_sync_gen_syndrome(struct page **blocks, unsigned int *offsets, int disks,
for (i = 0; i < disks; i++) {
if (blocks[i] == NULL) {
BUG_ON(i > disks - 3); /* P or Q can't be zero */
- srcs[i] = raid6_get_zero_page();
+ srcs[i] = page_address(ZERO_PAGE(0));
} else {
srcs[i] = page_address(blocks[i]) + offsets[i];
diff --git a/crypto/async_tx/async_raid6_recov.c b/crypto/async_tx/async_raid6_recov.c
index 539ea5b378dc..f2dc6af6e6a7 100644
--- a/crypto/async_tx/async_raid6_recov.c
+++ b/crypto/async_tx/async_raid6_recov.c
@@ -414,7 +414,7 @@ async_raid6_2data_recov(int disks, size_t bytes, int faila, int failb,
async_tx_quiesce(&submit->depend_tx);
for (i = 0; i < disks; i++)
if (blocks[i] == NULL)
- ptrs[i] = raid6_get_zero_page();
+ ptrs[i] = page_address(ZERO_PAGE(0));
else
ptrs[i] = page_address(blocks[i]) + offs[i];
@@ -497,7 +497,7 @@ async_raid6_datap_recov(int disks, size_t bytes, int faila,
async_tx_quiesce(&submit->depend_tx);
for (i = 0; i < disks; i++)
if (blocks[i] == NULL)
- ptrs[i] = raid6_get_zero_page();
+ ptrs[i] = page_address(ZERO_PAGE(0));
else
ptrs[i] = page_address(blocks[i]) + offs[i];
diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h
index 5e7e743b83f5..f27a866c287f 100644
--- a/include/linux/raid/pq.h
+++ b/include/linux/raid/pq.h
@@ -11,12 +11,6 @@
#include <linux/blkdev.h>
#include <linux/mm.h>
-/* This should be const but the raid6 code is too convoluted for that. */
-static inline void *raid6_get_zero_page(void)
-{
- return page_address(ZERO_PAGE(0));
-}
-
/* Routine choices */
struct raid6_calls {
void (*gen_syndrome)(int, size_t, void **);
diff --git a/lib/raid/raid6/arm/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c
index 5a48fcc762e8..9993bda5d3a6 100644
--- a/lib/raid/raid6/arm/recov_neon.c
+++ b/lib/raid/raid6/arm/recov_neon.c
@@ -29,10 +29,10 @@ static void raid6_2data_recov_neon(int disks, size_t bytes, int faila,
* delta p and delta q
*/
dp = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 2] = dp;
dq = (u8 *)ptrs[failb];
- ptrs[failb] = raid6_get_zero_page();
+ ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
@@ -66,7 +66,7 @@ static void raid6_datap_recov_neon(int disks, size_t bytes, int faila,
* Use the dead data page as temporary storage for delta q
*/
dq = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
diff --git a/lib/raid/raid6/loongarch/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c
index eb3a1e79f01f..4d4563209647 100644
--- a/lib/raid/raid6/loongarch/recov_loongarch_simd.c
+++ b/lib/raid/raid6/loongarch/recov_loongarch_simd.c
@@ -43,10 +43,10 @@ static void raid6_2data_recov_lsx(int disks, size_t bytes, int faila,
* delta p and delta q
*/
dp = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 2] = dp;
dq = (u8 *)ptrs[failb];
- ptrs[failb] = raid6_get_zero_page();
+ ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
@@ -198,7 +198,7 @@ static void raid6_datap_recov_lsx(int disks, size_t bytes, int faila,
* Use the dead data page as temporary storage for delta q
*/
dq = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
@@ -317,10 +317,10 @@ static void raid6_2data_recov_lasx(int disks, size_t bytes, int faila,
* delta p and delta q
*/
dp = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 2] = dp;
dq = (u8 *)ptrs[failb];
- ptrs[failb] = raid6_get_zero_page();
+ ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
@@ -437,7 +437,7 @@ static void raid6_datap_recov_lasx(int disks, size_t bytes, int faila,
* Use the dead data page as temporary storage for delta q
*/
dq = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
diff --git a/lib/raid/raid6/recov.c b/lib/raid/raid6/recov.c
index 8d113196632e..211e1df28963 100644
--- a/lib/raid/raid6/recov.c
+++ b/lib/raid/raid6/recov.c
@@ -31,10 +31,10 @@ static void raid6_2data_recov_intx1(int disks, size_t bytes, int faila,
Use the dead data pages as temporary storage for
delta p and delta q */
dp = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-2] = dp;
dq = (u8 *)ptrs[failb];
- ptrs[failb] = raid6_get_zero_page();
+ ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
@@ -72,7 +72,7 @@ static void raid6_datap_recov_intx1(int disks, size_t bytes, int faila,
/* Compute syndrome with zero for the missing data page
Use the dead data page as temporary storage for delta q */
dq = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
diff --git a/lib/raid/raid6/riscv/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c
index 40c393206b6a..f77d9c430687 100644
--- a/lib/raid/raid6/riscv/recov_rvv.c
+++ b/lib/raid/raid6/riscv/recov_rvv.c
@@ -158,10 +158,10 @@ static void raid6_2data_recov_rvv(int disks, size_t bytes, int faila,
* delta p and delta q
*/
dp = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 2] = dp;
dq = (u8 *)ptrs[failb];
- ptrs[failb] = raid6_get_zero_page();
+ ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
@@ -196,7 +196,7 @@ static void raid6_datap_recov_rvv(int disks, size_t bytes, int faila,
* Use the dead data page as temporary storage for delta q
*/
dq = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
diff --git a/lib/raid/raid6/s390/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c
index 487018f81192..0f32217b7123 100644
--- a/lib/raid/raid6/s390/recov_s390xc.c
+++ b/lib/raid/raid6/s390/recov_s390xc.c
@@ -34,10 +34,10 @@ static void raid6_2data_recov_s390xc(int disks, size_t bytes, int faila,
Use the dead data pages as temporary storage for
delta p and delta q */
dp = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-2] = dp;
dq = (u8 *)ptrs[failb];
- ptrs[failb] = raid6_get_zero_page();
+ ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
@@ -81,7 +81,7 @@ static void raid6_datap_recov_s390xc(int disks, size_t bytes, int faila,
/* Compute syndrome with zero for the missing data page
Use the dead data page as temporary storage for delta q */
dq = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
diff --git a/lib/raid/raid6/x86/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c
index 19fbd9c4dce6..325310c81e1c 100644
--- a/lib/raid/raid6/x86/recov_avx2.c
+++ b/lib/raid/raid6/x86/recov_avx2.c
@@ -28,10 +28,10 @@ static void raid6_2data_recov_avx2(int disks, size_t bytes, int faila,
Use the dead data pages as temporary storage for
delta p and delta q */
dp = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-2] = dp;
dq = (u8 *)ptrs[failb];
- ptrs[failb] = raid6_get_zero_page();
+ ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
@@ -196,7 +196,7 @@ static void raid6_datap_recov_avx2(int disks, size_t bytes, int faila,
/* Compute syndrome with zero for the missing data page
Use the dead data page as temporary storage for delta q */
dq = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
diff --git a/lib/raid/raid6/x86/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c
index 143f4976b2ad..08de77fcb8bd 100644
--- a/lib/raid/raid6/x86/recov_avx512.c
+++ b/lib/raid/raid6/x86/recov_avx512.c
@@ -37,10 +37,10 @@ static void raid6_2data_recov_avx512(int disks, size_t bytes, int faila,
*/
dp = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-2] = dp;
dq = (u8 *)ptrs[failb];
- ptrs[failb] = raid6_get_zero_page();
+ ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
@@ -238,7 +238,7 @@ static void raid6_datap_recov_avx512(int disks, size_t bytes, int faila,
*/
dq = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
diff --git a/lib/raid/raid6/x86/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c
index 146cdbf465bd..002bef1e0847 100644
--- a/lib/raid/raid6/x86/recov_ssse3.c
+++ b/lib/raid/raid6/x86/recov_ssse3.c
@@ -30,10 +30,10 @@ static void raid6_2data_recov_ssse3(int disks, size_t bytes, int faila,
Use the dead data pages as temporary storage for
delta p and delta q */
dp = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-2] = dp;
dq = (u8 *)ptrs[failb];
- ptrs[failb] = raid6_get_zero_page();
+ ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
@@ -203,7 +203,7 @@ static void raid6_datap_recov_ssse3(int disks, size_t bytes, int faila,
/* Compute syndrome with zero for the missing data page
Use the dead data page as temporary storage for delta q */
dq = (u8 *)ptrs[faila];
- ptrs[faila] = raid6_get_zero_page();
+ ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
raid6_call.gen_syndrome(disks, bytes, ptrs);
--
2.53.0
^ permalink raw reply related
* [PATCH 06/18] raid6: use named initializers for struct raid6_calls
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>
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/arm/neon.c | 9 +++----
lib/raid/raid6/int.uc | 8 +++---
lib/raid/raid6/loongarch/loongarch_simd.c | 18 ++++++-------
lib/raid/raid6/powerpc/altivec.uc | 8 +++---
lib/raid/raid6/powerpc/vpermxor.uc | 8 +++---
lib/raid/raid6/riscv/rvv.h | 9 +++----
lib/raid/raid6/s390/s390vx.uc | 10 +++----
lib/raid/raid6/x86/avx2.c | 33 ++++++++++++-----------
lib/raid/raid6/x86/avx512.c | 33 ++++++++++++-----------
lib/raid/raid6/x86/mmx.c | 16 +++++------
lib/raid/raid6/x86/sse1.c | 18 ++++++-------
lib/raid/raid6/x86/sse2.c | 30 ++++++++++-----------
12 files changed, 95 insertions(+), 105 deletions(-)
diff --git a/lib/raid/raid6/arm/neon.c b/lib/raid/raid6/arm/neon.c
index 47b8bb0afc65..c21da59ab48f 100644
--- a/lib/raid/raid6/arm/neon.c
+++ b/lib/raid/raid6/arm/neon.c
@@ -40,11 +40,10 @@
start, stop, (unsigned long)bytes, ptrs);\
} \
struct raid6_calls const raid6_neonx ## _n = { \
- raid6_neon ## _n ## _gen_syndrome, \
- raid6_neon ## _n ## _xor_syndrome, \
- raid6_have_neon, \
- "neonx" #_n, \
- 0 \
+ .gen_syndrome = raid6_neon ## _n ## _gen_syndrome, \
+ .xor_syndrome = raid6_neon ## _n ## _xor_syndrome, \
+ .valid = raid6_have_neon, \
+ .name = "neonx" #_n, \
}
static int raid6_have_neon(void)
diff --git a/lib/raid/raid6/int.uc b/lib/raid/raid6/int.uc
index 1ba56c3fa482..4f5f2869e21e 100644
--- a/lib/raid/raid6/int.uc
+++ b/lib/raid/raid6/int.uc
@@ -139,9 +139,7 @@ static void raid6_int$#_xor_syndrome(int disks, int start, int stop,
}
const struct raid6_calls raid6_intx$# = {
- raid6_int$#_gen_syndrome,
- raid6_int$#_xor_syndrome,
- NULL, /* always valid */
- "int" NSTRING "x$#",
- 0
+ .gen_syndrome = raid6_int$#_gen_syndrome,
+ .xor_syndrome = raid6_int$#_xor_syndrome,
+ .name = "int" NSTRING "x$#",
};
diff --git a/lib/raid/raid6/loongarch/loongarch_simd.c b/lib/raid/raid6/loongarch/loongarch_simd.c
index 72f4d92d4876..1b4cd1512d05 100644
--- a/lib/raid/raid6/loongarch/loongarch_simd.c
+++ b/lib/raid/raid6/loongarch/loongarch_simd.c
@@ -244,11 +244,10 @@ static void raid6_lsx_xor_syndrome(int disks, int start, int stop,
}
const struct raid6_calls raid6_lsx = {
- raid6_lsx_gen_syndrome,
- raid6_lsx_xor_syndrome,
- raid6_has_lsx,
- "lsx",
- .priority = 0 /* see the comment near the top of the file for reason */
+ .gen_syndrome = raid6_lsx_gen_syndrome,
+ .xor_syndrome = raid6_lsx_xor_syndrome,
+ .valid = raid6_has_lsx,
+ .name = "lsx",
};
#undef NSIZE
@@ -413,11 +412,10 @@ static void raid6_lasx_xor_syndrome(int disks, int start, int stop,
}
const struct raid6_calls raid6_lasx = {
- raid6_lasx_gen_syndrome,
- raid6_lasx_xor_syndrome,
- raid6_has_lasx,
- "lasx",
- .priority = 0 /* see the comment near the top of the file for reason */
+ .gen_syndrome = raid6_lasx_gen_syndrome,
+ .xor_syndrome = raid6_lasx_xor_syndrome,
+ .valid = raid6_has_lasx,
+ .name = "lasx",
};
#undef NSIZE
#endif /* CONFIG_CPU_HAS_LASX */
diff --git a/lib/raid/raid6/powerpc/altivec.uc b/lib/raid/raid6/powerpc/altivec.uc
index 130d3d3dd42c..084ead768ddb 100644
--- a/lib/raid/raid6/powerpc/altivec.uc
+++ b/lib/raid/raid6/powerpc/altivec.uc
@@ -114,9 +114,7 @@ int raid6_have_altivec(void)
#endif
const struct raid6_calls raid6_altivec$# = {
- raid6_altivec$#_gen_syndrome,
- NULL, /* XOR not yet implemented */
- raid6_have_altivec,
- "altivecx$#",
- 0
+ .gen_syndrome = raid6_altivec$#_gen_syndrome,
+ .valid = raid6_have_altivec,
+ .name = "altivecx$#",
};
diff --git a/lib/raid/raid6/powerpc/vpermxor.uc b/lib/raid/raid6/powerpc/vpermxor.uc
index 595f20aaf4cf..bb2c3a316ae8 100644
--- a/lib/raid/raid6/powerpc/vpermxor.uc
+++ b/lib/raid/raid6/powerpc/vpermxor.uc
@@ -87,9 +87,7 @@ int raid6_have_altivec_vpermxor(void)
#endif
const struct raid6_calls raid6_vpermxor$# = {
- raid6_vpermxor$#_gen_syndrome,
- NULL,
- raid6_have_altivec_vpermxor,
- "vpermxor$#",
- 0
+ .gen_syndrome = raid6_vpermxor$#_gen_syndrome,
+ .valid = raid6_have_altivec_vpermxor,
+ .name = "vpermxor$#",
};
diff --git a/lib/raid/raid6/riscv/rvv.h b/lib/raid/raid6/riscv/rvv.h
index b0a71b375962..0d430a4c5f08 100644
--- a/lib/raid/raid6/riscv/rvv.h
+++ b/lib/raid/raid6/riscv/rvv.h
@@ -39,9 +39,8 @@ static int rvv_has_vector(void)
kernel_vector_end(); \
} \
struct raid6_calls const raid6_rvvx ## _n = { \
- raid6_rvv ## _n ## _gen_syndrome, \
- raid6_rvv ## _n ## _xor_syndrome, \
- rvv_has_vector, \
- "rvvx" #_n, \
- 0 \
+ .gen_syndrome = raid6_rvv ## _n ## _gen_syndrome, \
+ .xor_syndrome = raid6_rvv ## _n ## _xor_syndrome, \
+ .valid = rvv_has_vector, \
+ .name = "rvvx" #_n, \
}
diff --git a/lib/raid/raid6/s390/s390vx.uc b/lib/raid/raid6/s390/s390vx.uc
index 8aa53eb2f395..97c5d5d9dcf9 100644
--- a/lib/raid/raid6/s390/s390vx.uc
+++ b/lib/raid/raid6/s390/s390vx.uc
@@ -127,9 +127,9 @@ static int raid6_s390vx$#_valid(void)
}
const struct raid6_calls raid6_s390vx$# = {
- raid6_s390vx$#_gen_syndrome,
- raid6_s390vx$#_xor_syndrome,
- raid6_s390vx$#_valid,
- "vx128x$#",
- 1
+ .gen_syndrome = raid6_s390vx$#_gen_syndrome,
+ .xor_syndrome = raid6_s390vx$#_xor_syndrome,
+ .valid = raid6_s390vx$#_valid,
+ .name = "vx128x$#",
+ .priority = 1,
};
diff --git a/lib/raid/raid6/x86/avx2.c b/lib/raid/raid6/x86/avx2.c
index a1a5213918af..aab8b624c635 100644
--- a/lib/raid/raid6/x86/avx2.c
+++ b/lib/raid/raid6/x86/avx2.c
@@ -128,11 +128,12 @@ static void raid6_avx21_xor_syndrome(int disks, int start, int stop,
}
const struct raid6_calls raid6_avx2x1 = {
- raid6_avx21_gen_syndrome,
- raid6_avx21_xor_syndrome,
- raid6_have_avx2,
- "avx2x1",
- .priority = 2 /* Prefer AVX2 over priority 1 (SSE2 and others) */
+ .gen_syndrome = raid6_avx21_gen_syndrome,
+ .xor_syndrome = raid6_avx21_xor_syndrome,
+ .valid = raid6_have_avx2,
+ .name = "avx2x1",
+ /* Prefer AVX2 over priority 1 (SSE2 and others) */
+ .priority = 2,
};
/*
@@ -258,11 +259,12 @@ static void raid6_avx22_xor_syndrome(int disks, int start, int stop,
}
const struct raid6_calls raid6_avx2x2 = {
- raid6_avx22_gen_syndrome,
- raid6_avx22_xor_syndrome,
- raid6_have_avx2,
- "avx2x2",
- .priority = 2 /* Prefer AVX2 over priority 1 (SSE2 and others) */
+ .gen_syndrome = raid6_avx22_gen_syndrome,
+ .xor_syndrome = raid6_avx22_xor_syndrome,
+ .valid = raid6_have_avx2,
+ .name = "avx2x2",
+ /* Prefer AVX2 over priority 1 (SSE2 and others) */
+ .priority = 2,
};
#ifdef CONFIG_X86_64
@@ -461,10 +463,11 @@ static void raid6_avx24_xor_syndrome(int disks, int start, int stop,
}
const struct raid6_calls raid6_avx2x4 = {
- raid6_avx24_gen_syndrome,
- raid6_avx24_xor_syndrome,
- raid6_have_avx2,
- "avx2x4",
- .priority = 2 /* Prefer AVX2 over priority 1 (SSE2 and others) */
+ .gen_syndrome = raid6_avx24_gen_syndrome,
+ .xor_syndrome = raid6_avx24_xor_syndrome,
+ .valid = raid6_have_avx2,
+ .name = "avx2x4",
+ /* Prefer AVX2 over priority 1 (SSE2 and others) */
+ .priority = 2,
};
#endif /* CONFIG_X86_64 */
diff --git a/lib/raid/raid6/x86/avx512.c b/lib/raid/raid6/x86/avx512.c
index 874998bcd7d7..47636b16632f 100644
--- a/lib/raid/raid6/x86/avx512.c
+++ b/lib/raid/raid6/x86/avx512.c
@@ -156,11 +156,12 @@ static void raid6_avx5121_xor_syndrome(int disks, int start, int stop,
}
const struct raid6_calls raid6_avx512x1 = {
- raid6_avx5121_gen_syndrome,
- raid6_avx5121_xor_syndrome,
- raid6_have_avx512,
- "avx512x1",
- .priority = 2 /* Prefer AVX512 over priority 1 (SSE2 and others) */
+ .gen_syndrome = raid6_avx5121_gen_syndrome,
+ .xor_syndrome = raid6_avx5121_xor_syndrome,
+ .valid = raid6_have_avx512,
+ .name = "avx512x1",
+ /* Prefer AVX512 over priority 1 (SSE2 and others) */
+ .priority = 2,
};
/*
@@ -313,11 +314,12 @@ static void raid6_avx5122_xor_syndrome(int disks, int start, int stop,
}
const struct raid6_calls raid6_avx512x2 = {
- raid6_avx5122_gen_syndrome,
- raid6_avx5122_xor_syndrome,
- raid6_have_avx512,
- "avx512x2",
- .priority = 2 /* Prefer AVX512 over priority 1 (SSE2 and others) */
+ .gen_syndrome = raid6_avx5122_gen_syndrome,
+ .xor_syndrome = raid6_avx5122_xor_syndrome,
+ .valid = raid6_have_avx512,
+ .name = "avx512x2",
+ /* Prefer AVX512 over priority 1 (SSE2 and others) */
+ .priority = 2,
};
#ifdef CONFIG_X86_64
@@ -551,10 +553,11 @@ static void raid6_avx5124_xor_syndrome(int disks, int start, int stop,
kernel_fpu_end();
}
const struct raid6_calls raid6_avx512x4 = {
- raid6_avx5124_gen_syndrome,
- raid6_avx5124_xor_syndrome,
- raid6_have_avx512,
- "avx512x4",
- .priority = 2 /* Prefer AVX512 over priority 1 (SSE2 and others) */
+ .gen_syndrome = raid6_avx5124_gen_syndrome,
+ .xor_syndrome = raid6_avx5124_xor_syndrome,
+ .valid = raid6_have_avx512,
+ .name = "avx512x4",
+ /* Prefer AVX512 over priority 1 (SSE2 and others) */
+ .priority = 2,
};
#endif
diff --git a/lib/raid/raid6/x86/mmx.c b/lib/raid/raid6/x86/mmx.c
index 7e9810669347..22b9fdaa705f 100644
--- a/lib/raid/raid6/x86/mmx.c
+++ b/lib/raid/raid6/x86/mmx.c
@@ -68,11 +68,9 @@ static void raid6_mmx1_gen_syndrome(int disks, size_t bytes, void **ptrs)
}
const struct raid6_calls raid6_mmxx1 = {
- raid6_mmx1_gen_syndrome,
- NULL, /* XOR not yet implemented */
- raid6_have_mmx,
- "mmxx1",
- 0
+ .gen_syndrome = raid6_mmx1_gen_syndrome,
+ .valid = raid6_have_mmx,
+ .name = "mmxx1",
};
/*
@@ -127,9 +125,7 @@ static void raid6_mmx2_gen_syndrome(int disks, size_t bytes, void **ptrs)
}
const struct raid6_calls raid6_mmxx2 = {
- raid6_mmx2_gen_syndrome,
- NULL, /* XOR not yet implemented */
- raid6_have_mmx,
- "mmxx2",
- 0
+ .gen_syndrome = raid6_mmx2_gen_syndrome,
+ .valid = raid6_have_mmx,
+ .name = "mmxx2",
};
diff --git a/lib/raid/raid6/x86/sse1.c b/lib/raid/raid6/x86/sse1.c
index deecdd72ceec..fad214a430d8 100644
--- a/lib/raid/raid6/x86/sse1.c
+++ b/lib/raid/raid6/x86/sse1.c
@@ -84,11 +84,10 @@ static void raid6_sse11_gen_syndrome(int disks, size_t bytes, void **ptrs)
}
const struct raid6_calls raid6_sse1x1 = {
- raid6_sse11_gen_syndrome,
- NULL, /* XOR not yet implemented */
- raid6_have_sse1_or_mmxext,
- "sse1x1",
- 1 /* Has cache hints */
+ .gen_syndrome = raid6_sse11_gen_syndrome,
+ .valid = raid6_have_sse1_or_mmxext,
+ .name = "sse1x1",
+ .priority = 1, /* Has cache hints */
};
/*
@@ -147,9 +146,8 @@ static void raid6_sse12_gen_syndrome(int disks, size_t bytes, void **ptrs)
}
const struct raid6_calls raid6_sse1x2 = {
- raid6_sse12_gen_syndrome,
- NULL, /* XOR not yet implemented */
- raid6_have_sse1_or_mmxext,
- "sse1x2",
- 1 /* Has cache hints */
+ .gen_syndrome = raid6_sse12_gen_syndrome,
+ .valid = raid6_have_sse1_or_mmxext,
+ .name = "sse1x2",
+ .priority = 1, /* Has cache hints */
};
diff --git a/lib/raid/raid6/x86/sse2.c b/lib/raid/raid6/x86/sse2.c
index f9edf8a8d1c4..1b28e858a1d4 100644
--- a/lib/raid/raid6/x86/sse2.c
+++ b/lib/raid/raid6/x86/sse2.c
@@ -133,11 +133,11 @@ static void raid6_sse21_xor_syndrome(int disks, int start, int stop,
}
const struct raid6_calls raid6_sse2x1 = {
- raid6_sse21_gen_syndrome,
- raid6_sse21_xor_syndrome,
- raid6_have_sse2,
- "sse2x1",
- 1 /* Has cache hints */
+ .gen_syndrome = raid6_sse21_gen_syndrome,
+ .xor_syndrome = raid6_sse21_xor_syndrome,
+ .valid = raid6_have_sse2,
+ .name = "sse2x1",
+ .priority = 1, /* Has cache hints */
};
/*
@@ -263,11 +263,11 @@ static void raid6_sse22_xor_syndrome(int disks, int start, int stop,
}
const struct raid6_calls raid6_sse2x2 = {
- raid6_sse22_gen_syndrome,
- raid6_sse22_xor_syndrome,
- raid6_have_sse2,
- "sse2x2",
- 1 /* Has cache hints */
+ .gen_syndrome = raid6_sse22_gen_syndrome,
+ .xor_syndrome = raid6_sse22_xor_syndrome,
+ .valid = raid6_have_sse2,
+ .name = "sse2x2",
+ .priority = 1, /* Has cache hints */
};
#ifdef CONFIG_X86_64
@@ -470,11 +470,11 @@ static void raid6_sse24_xor_syndrome(int disks, int start, int stop,
const struct raid6_calls raid6_sse2x4 = {
- raid6_sse24_gen_syndrome,
- raid6_sse24_xor_syndrome,
- raid6_have_sse2,
- "sse2x4",
- 1 /* Has cache hints */
+ .gen_syndrome = raid6_sse24_gen_syndrome,
+ .xor_syndrome = raid6_sse24_xor_syndrome,
+ .valid = raid6_have_sse2,
+ .name = "sse2x4",
+ .priority = 1, /* Has cache hints */
};
#endif /* CONFIG_X86_64 */
--
2.53.0
^ permalink raw reply related
* [PATCH 07/18] raid6: improve the public interface
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>
Stop directly calling into function pointers from users of the RAID6 PQ
API, and provide exported functions with proper documentation and
API guarantees asserts where applicable instead.
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
---
Documentation/crypto/async-tx-api.rst | 4 +-
crypto/async_tx/async_pq.c | 6 +-
crypto/async_tx/async_raid6_recov.c | 4 +-
drivers/md/raid5.c | 4 +-
fs/btrfs/raid56.c | 8 +-
include/linux/raid/pq.h | 19 +--
lib/raid/raid6/algos.c | 137 +++++++++++++++++-
lib/raid/raid6/arm/recov_neon.c | 4 +-
.../raid6/loongarch/recov_loongarch_simd.c | 8 +-
lib/raid/raid6/recov.c | 4 +-
lib/raid/raid6/riscv/recov_rvv.c | 4 +-
lib/raid/raid6/s390/recov_s390xc.c | 4 +-
lib/raid/raid6/x86/recov_avx2.c | 4 +-
lib/raid/raid6/x86/recov_avx512.c | 4 +-
lib/raid/raid6/x86/recov_ssse3.c | 4 +-
15 files changed, 170 insertions(+), 48 deletions(-)
diff --git a/Documentation/crypto/async-tx-api.rst b/Documentation/crypto/async-tx-api.rst
index f88a7809385e..49fcfc66314a 100644
--- a/Documentation/crypto/async-tx-api.rst
+++ b/Documentation/crypto/async-tx-api.rst
@@ -82,9 +82,9 @@ xor_val xor a series of source buffers and set a flag if the
pq generate the p+q (raid6 syndrome) from a series of source buffers
pq_val validate that a p and or q buffer are in sync with a given series of
sources
-datap (raid6_datap_recov) recover a raid6 data block and the p block
+datap (raid6_recov_datap) recover a raid6 data block and the p block
from the given sources
-2data (raid6_2data_recov) recover 2 raid6 data blocks from the given
+2data (raid6_recov_2data) recover 2 raid6 data blocks from the given
sources
======== ====================================================================
diff --git a/crypto/async_tx/async_pq.c b/crypto/async_tx/async_pq.c
index 0ce6f07b4e0d..f3574f80d1df 100644
--- a/crypto/async_tx/async_pq.c
+++ b/crypto/async_tx/async_pq.c
@@ -131,11 +131,11 @@ do_sync_gen_syndrome(struct page **blocks, unsigned int *offsets, int disks,
}
}
if (submit->flags & ASYNC_TX_PQ_XOR_DST) {
- BUG_ON(!raid6_call.xor_syndrome);
+ BUG_ON(!raid6_can_xor_syndrome());
if (start >= 0)
- raid6_call.xor_syndrome(disks, start, stop, len, srcs);
+ raid6_xor_syndrome(disks, start, stop, len, srcs);
} else
- raid6_call.gen_syndrome(disks, len, srcs);
+ raid6_gen_syndrome(disks, len, srcs);
async_tx_sync_epilog(submit);
}
diff --git a/crypto/async_tx/async_raid6_recov.c b/crypto/async_tx/async_raid6_recov.c
index f2dc6af6e6a7..305ea1421a3e 100644
--- a/crypto/async_tx/async_raid6_recov.c
+++ b/crypto/async_tx/async_raid6_recov.c
@@ -418,7 +418,7 @@ async_raid6_2data_recov(int disks, size_t bytes, int faila, int failb,
else
ptrs[i] = page_address(blocks[i]) + offs[i];
- raid6_2data_recov(disks, bytes, faila, failb, ptrs);
+ raid6_recov_2data(disks, bytes, faila, failb, ptrs);
async_tx_sync_epilog(submit);
@@ -501,7 +501,7 @@ async_raid6_datap_recov(int disks, size_t bytes, int faila,
else
ptrs[i] = page_address(blocks[i]) + offs[i];
- raid6_datap_recov(disks, bytes, faila, ptrs);
+ raid6_recov_datap(disks, bytes, faila, ptrs);
async_tx_sync_epilog(submit);
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 0d76e82f4506..ebcb19317670 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6955,7 +6955,7 @@ raid5_store_rmw_level(struct mddev *mddev, const char *page, size_t len)
if (kstrtoul(page, 10, &new))
return -EINVAL;
- if (new != PARITY_DISABLE_RMW && !raid6_call.xor_syndrome)
+ if (new != PARITY_DISABLE_RMW && !raid6_can_xor_syndrome())
return -EINVAL;
if (new != PARITY_DISABLE_RMW &&
@@ -7646,7 +7646,7 @@ static struct r5conf *setup_conf(struct mddev *mddev)
conf->level = mddev->new_level;
if (conf->level == 6) {
conf->max_degraded = 2;
- if (raid6_call.xor_syndrome)
+ if (raid6_can_xor_syndrome())
conf->rmw_level = PARITY_ENABLE_RMW;
else
conf->rmw_level = PARITY_DISABLE_RMW;
diff --git a/fs/btrfs/raid56.c b/fs/btrfs/raid56.c
index 08ee8f316d96..dabc9522e881 100644
--- a/fs/btrfs/raid56.c
+++ b/fs/btrfs/raid56.c
@@ -1410,7 +1410,7 @@ static void generate_pq_vertical_step(struct btrfs_raid_bio *rbio, unsigned int
rbio_qstripe_paddr(rbio, sector_nr, step_nr));
assert_rbio(rbio);
- raid6_call.gen_syndrome(rbio->real_stripes, step, pointers);
+ raid6_gen_syndrome(rbio->real_stripes, step, pointers);
} else {
/* raid5 */
memcpy(pointers[rbio->nr_data], pointers[0], step);
@@ -1987,10 +1987,10 @@ static void recover_vertical_step(struct btrfs_raid_bio *rbio,
}
if (failb == rbio->real_stripes - 2) {
- raid6_datap_recov(rbio->real_stripes, step,
+ raid6_recov_datap(rbio->real_stripes, step,
faila, pointers);
} else {
- raid6_2data_recov(rbio->real_stripes, step,
+ raid6_recov_2data(rbio->real_stripes, step,
faila, failb, pointers);
}
} else {
@@ -2644,7 +2644,7 @@ static bool verify_one_parity_step(struct btrfs_raid_bio *rbio,
if (has_qstripe) {
assert_rbio(rbio);
/* RAID6, call the library function to fill in our P/Q. */
- raid6_call.gen_syndrome(rbio->real_stripes, step, pointers);
+ raid6_gen_syndrome(rbio->real_stripes, step, pointers);
} else {
/* RAID5. */
memcpy(pointers[nr_data], pointers[0], step);
diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h
index f27a866c287f..425a227591c0 100644
--- a/include/linux/raid/pq.h
+++ b/include/linux/raid/pq.h
@@ -11,6 +11,16 @@
#include <linux/blkdev.h>
#include <linux/mm.h>
+void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs);
+void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes,
+ void **ptrs);
+bool raid6_can_xor_syndrome(void);
+
+void raid6_recov_2data(int disks, size_t bytes, int faila, int failb,
+ void **ptrs);
+void raid6_recov_datap(int disks, size_t bytes, int faila,
+ void **ptrs);
+
/* Routine choices */
struct raid6_calls {
void (*gen_syndrome)(int, size_t, void **);
@@ -20,9 +30,6 @@ struct raid6_calls {
int priority; /* Relative priority ranking if non-zero */
};
-/* Selected algorithm */
-extern struct raid6_calls raid6_call;
-
/* Various routine sets */
extern const struct raid6_calls raid6_intx1;
extern const struct raid6_calls raid6_intx2;
@@ -92,10 +99,4 @@ extern const u8 raid6_gflog[256] __attribute__((aligned(256)));
extern const u8 raid6_gfinv[256] __attribute__((aligned(256)));
extern const u8 raid6_gfexi[256] __attribute__((aligned(256)));
-/* Recovery routines */
-extern void (*raid6_2data_recov)(int disks, size_t bytes, int faila, int failb,
- void **ptrs);
-extern void (*raid6_datap_recov)(int disks, size_t bytes, int faila,
- void **ptrs);
-
#endif /* LINUX_RAID_RAID6_H */
diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c
index 985c60bb00a4..b0ba31f6d48e 100644
--- a/lib/raid/raid6/algos.c
+++ b/lib/raid/raid6/algos.c
@@ -16,8 +16,83 @@
#include <linux/gfp.h>
#include <kunit/visibility.h>
-struct raid6_calls raid6_call;
-EXPORT_SYMBOL_GPL(raid6_call);
+static const struct raid6_recov_calls *raid6_recov_algo;
+
+/* Selected algorithm */
+static struct raid6_calls raid6_call;
+
+/**
+ * raid6_gen_syndrome - generate RAID6 P/Q parity
+ * @disks: number of "disks" to operate on including parity
+ * @bytes: length in bytes of each vector
+ * @ptrs: @disks size array of memory pointers
+ *
+ * Generate @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and
+ * @ptrs[@disks - 1] respectively from the memory pointed to by @ptrs[0] to
+ * @ptrs[@disks - 3].
+ *
+ * @disks must be at least 4, and the memory pointed to by each member of @ptrs
+ * must be at least 64-byte aligned. @bytes must be non-zero and a multiple of
+ * 512.
+ *
+ * See https://kernel.org/pub/linux/kernel/people/hpa/raid6.pdf for underlying
+ * algorithm.
+ */
+void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs)
+{
+ WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
+ WARN_ON_ONCE(bytes & 511);
+
+ raid6_call.gen_syndrome(disks, bytes, ptrs);
+}
+EXPORT_SYMBOL_GPL(raid6_gen_syndrome);
+
+/**
+ * raid6_xor_syndrome - update RAID6 P/Q parity
+ * @disks: number of "disks" to operate on including parity
+ * @start: first index into @disk to update
+ * @stop: last index into @disk to update
+ * @bytes: length in bytes of each vector
+ * @ptrs: @disks size array of memory pointers
+ *
+ * Update @bytes worth of RAID6 P and Q parity in @ptrs[@disks - 2] and
+ * @ptrs[@disks - 1] respectively for the memory pointed to by
+ * @ptrs[@start..@stop].
+ *
+ * This is used to update parity in place using the following sequence:
+ *
+ * 1) call raid6_xor_syndrome(disk, start, stop, ...) for the existing data.
+ * 2) update the the data in @ptrs[@start..@stop].
+ * 3) call raid6_xor_syndrome(disk, start, stop, ...) for the new data.
+ *
+ * Data between @start and @stop that is not changed should be filled
+ * with a pointer to the kernel zero page.
+ *
+ * @disks must be at least 4, and the memory pointed to by each member of @ptrs
+ * must be at least 64-byte aligned. @bytes must be non-zero and a multiple of
+ * 512. @stop must be larger or equal to @start.
+ */
+void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes,
+ void **ptrs)
+{
+ WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
+ WARN_ON_ONCE(bytes & 511);
+ WARN_ON_ONCE(stop < start);
+
+ raid6_call.xor_syndrome(disks, start, stop, bytes, ptrs);
+}
+EXPORT_SYMBOL_GPL(raid6_xor_syndrome);
+
+/*
+ * raid6_can_xor_syndrome - check if raid6_xor_syndrome() can be used
+ *
+ * Returns %true if raid6_can_xor_syndrome() can be used, else %false.
+ */
+bool raid6_can_xor_syndrome(void)
+{
+ return !!raid6_call.xor_syndrome;
+}
+EXPORT_SYMBOL_GPL(raid6_can_xor_syndrome);
const struct raid6_calls * const raid6_algos[] = {
#if defined(__i386__) && !defined(__arch_um__)
@@ -84,11 +159,58 @@ const struct raid6_calls * const raid6_algos[] = {
};
EXPORT_SYMBOL_IF_KUNIT(raid6_algos);
-void (*raid6_2data_recov)(int, size_t, int, int, void **);
-EXPORT_SYMBOL_GPL(raid6_2data_recov);
+/**
+ * raid6_recov_2data - recover two missing data disks
+ * @disks: number of "disks" to operate on including parity
+ * @bytes: length in bytes of each vector
+ * @faila: first failed data disk index
+ * @failb: second failed data disk index
+ * @ptrs: @disks size array of memory pointers
+ *
+ * Rebuild @bytes of missing data in @ptrs[@faila] and @ptrs[@failb] from the
+ * data in the remaining disks and the two parities pointed to by the other
+ * indices between 0 and @disks - 1 in @ptrs. @disks includes the data disks
+ * and the two parities. @faila must be smaller than @failb.
+ *
+ * Memory pointed to by each pointer in @ptrs must be page aligned and is
+ * limited to %PAGE_SIZE.
+ */
+void raid6_recov_2data(int disks, size_t bytes, int faila, int failb,
+ void **ptrs)
+{
+ WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
+ WARN_ON_ONCE(bytes & 511);
+ WARN_ON_ONCE(bytes > PAGE_SIZE);
+ WARN_ON_ONCE(failb <= faila);
+
+ raid6_recov_algo->data2(disks, bytes, faila, failb, ptrs);
+}
+EXPORT_SYMBOL_GPL(raid6_recov_2data);
+
+/**
+ * raid6_recov_datap - recover a missing data disk and missing P-parity
+ * @disks: number of "disks" to operate on including parity
+ * @bytes: length in bytes of each vector
+ * @faila: failed data disk index
+ * @ptrs: @disks size array of memory pointers
+ *
+ * Rebuild @bytes of missing data in @ptrs[@faila] and the missing P-parity in
+ * @ptrs[@disks - 2] from the data in the remaining disks and the Q-parity
+ * pointed to by the other indices between 0 and @disks - 1 in @ptrs. @disks
+ * includes the data disks and the two parities.
+ *
+ * Memory pointed to by each pointer in @ptrs must be page aligned and is
+ * limited to %PAGE_SIZE.
+ */
+void raid6_recov_datap(int disks, size_t bytes, int faila, void **ptrs)
+{
+ WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
+ WARN_ON_ONCE(bytes & 511);
+ WARN_ON_ONCE(bytes > PAGE_SIZE);
-void (*raid6_datap_recov)(int, size_t, int, void **);
-EXPORT_SYMBOL_GPL(raid6_datap_recov);
+ raid6_recov_algo->datap(disks, bytes, faila, ptrs);
+}
+EXPORT_SYMBOL_GPL(raid6_recov_datap);
const struct raid6_recov_calls *const raid6_recov_algos[] = {
#ifdef CONFIG_X86
@@ -133,8 +255,7 @@ static inline const struct raid6_recov_calls *raid6_choose_recov(void)
best = *algo;
if (best) {
- raid6_2data_recov = best->data2;
- raid6_datap_recov = best->datap;
+ raid6_recov_algo = best;
pr_info("raid6: using %s recovery algorithm\n", best->name);
} else
diff --git a/lib/raid/raid6/arm/recov_neon.c b/lib/raid/raid6/arm/recov_neon.c
index 9993bda5d3a6..4eb0efb44750 100644
--- a/lib/raid/raid6/arm/recov_neon.c
+++ b/lib/raid/raid6/arm/recov_neon.c
@@ -35,7 +35,7 @@ static void raid6_2data_recov_neon(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -69,7 +69,7 @@ static void raid6_datap_recov_neon(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
diff --git a/lib/raid/raid6/loongarch/recov_loongarch_simd.c b/lib/raid/raid6/loongarch/recov_loongarch_simd.c
index 4d4563209647..7d4d349322b3 100644
--- a/lib/raid/raid6/loongarch/recov_loongarch_simd.c
+++ b/lib/raid/raid6/loongarch/recov_loongarch_simd.c
@@ -49,7 +49,7 @@ static void raid6_2data_recov_lsx(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -201,7 +201,7 @@ static void raid6_datap_recov_lsx(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
@@ -323,7 +323,7 @@ static void raid6_2data_recov_lasx(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -440,7 +440,7 @@ static void raid6_datap_recov_lasx(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
diff --git a/lib/raid/raid6/recov.c b/lib/raid/raid6/recov.c
index 211e1df28963..cc7e4dc1eaa6 100644
--- a/lib/raid/raid6/recov.c
+++ b/lib/raid/raid6/recov.c
@@ -37,7 +37,7 @@ static void raid6_2data_recov_intx1(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -75,7 +75,7 @@ static void raid6_datap_recov_intx1(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
diff --git a/lib/raid/raid6/riscv/recov_rvv.c b/lib/raid/raid6/riscv/recov_rvv.c
index f77d9c430687..3ff39826e33f 100644
--- a/lib/raid/raid6/riscv/recov_rvv.c
+++ b/lib/raid/raid6/riscv/recov_rvv.c
@@ -164,7 +164,7 @@ static void raid6_2data_recov_rvv(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -199,7 +199,7 @@ static void raid6_datap_recov_rvv(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks - 1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
diff --git a/lib/raid/raid6/s390/recov_s390xc.c b/lib/raid/raid6/s390/recov_s390xc.c
index 0f32217b7123..2bc4c85174de 100644
--- a/lib/raid/raid6/s390/recov_s390xc.c
+++ b/lib/raid/raid6/s390/recov_s390xc.c
@@ -40,7 +40,7 @@ static void raid6_2data_recov_s390xc(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -84,7 +84,7 @@ static void raid6_datap_recov_s390xc(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
diff --git a/lib/raid/raid6/x86/recov_avx2.c b/lib/raid/raid6/x86/recov_avx2.c
index 325310c81e1c..bef82a38d8eb 100644
--- a/lib/raid/raid6/x86/recov_avx2.c
+++ b/lib/raid/raid6/x86/recov_avx2.c
@@ -34,7 +34,7 @@ static void raid6_2data_recov_avx2(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -199,7 +199,7 @@ static void raid6_datap_recov_avx2(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
diff --git a/lib/raid/raid6/x86/recov_avx512.c b/lib/raid/raid6/x86/recov_avx512.c
index 08de77fcb8bd..06c70e771eaa 100644
--- a/lib/raid/raid6/x86/recov_avx512.c
+++ b/lib/raid/raid6/x86/recov_avx512.c
@@ -43,7 +43,7 @@ static void raid6_2data_recov_avx512(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -241,7 +241,7 @@ static void raid6_datap_recov_avx512(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
diff --git a/lib/raid/raid6/x86/recov_ssse3.c b/lib/raid/raid6/x86/recov_ssse3.c
index 002bef1e0847..5ca7d56f23d8 100644
--- a/lib/raid/raid6/x86/recov_ssse3.c
+++ b/lib/raid/raid6/x86/recov_ssse3.c
@@ -36,7 +36,7 @@ static void raid6_2data_recov_ssse3(int disks, size_t bytes, int faila,
ptrs[failb] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dp;
@@ -206,7 +206,7 @@ static void raid6_datap_recov_ssse3(int disks, size_t bytes, int faila,
ptrs[faila] = page_address(ZERO_PAGE(0));
ptrs[disks-1] = dq;
- raid6_call.gen_syndrome(disks, bytes, ptrs);
+ raid6_gen_syndrome(disks, bytes, ptrs);
/* Restore pointer table */
ptrs[faila] = dq;
--
2.53.0
^ permalink raw reply related
* [PATCH 08/18] raid6: warn when using less than four devices
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>
Quoting H. Peter Anvin who came up with the RAID6 P/Q algorithm, and
who wrote the initial implementation, then still part of the md driver:
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.
While md never allowed less than 4 devices, btrfs does. This new
warning will trigger for such file systems, but given how it already
causes havoc that is a good thing. If btrfs wants to fix third, it
should switch to transparently use three-way mirroring underneath,
which will work as P and Q are copies of the single data device by
the definition of the Linux RAID 6 P/Q algorithm.
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
---
include/linux/raid/pq.h | 2 ++
lib/raid/raid6/algos.c | 2 ++
2 files changed, 4 insertions(+)
diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h
index 425a227591c0..87e3cb55bf07 100644
--- a/include/linux/raid/pq.h
+++ b/include/linux/raid/pq.h
@@ -11,6 +11,8 @@
#include <linux/blkdev.h>
#include <linux/mm.h>
+#define RAID6_MIN_DISKS 4
+
void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs);
void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes,
void **ptrs);
diff --git a/lib/raid/raid6/algos.c b/lib/raid/raid6/algos.c
index b0ba31f6d48e..63d1945ba63c 100644
--- a/lib/raid/raid6/algos.c
+++ b/lib/raid/raid6/algos.c
@@ -42,6 +42,7 @@ void raid6_gen_syndrome(int disks, size_t bytes, void **ptrs)
{
WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
WARN_ON_ONCE(bytes & 511);
+ WARN_ON_ONCE(disks < RAID6_MIN_DISKS);
raid6_call.gen_syndrome(disks, bytes, ptrs);
}
@@ -77,6 +78,7 @@ void raid6_xor_syndrome(int disks, int start, int stop, size_t bytes,
{
WARN_ON_ONCE(!in_task() || irqs_disabled() || softirq_count());
WARN_ON_ONCE(bytes & 511);
+ WARN_ON_ONCE(disks < RAID6_MIN_DISKS);
WARN_ON_ONCE(stop < start);
raid6_call.xor_syndrome(disks, start, stop, bytes, ptrs);
--
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