* [PATCH v2 4/5] md: skip ID_BITMAP_NONE when show available bitmap types
From: Su Yue @ 2026-04-07 10:26 UTC (permalink / raw)
To: linux-raid; +Cc: song, xni, linan122, yukuai, heming.zhao, l, Su Yue
In-Reply-To: <20260407102625.5686-1-glass.su@suse.com>
As none_bitmap_ops is introduced, ID_BITMAP_NONE should be skipped while
iterating md submodules, otherwise:
$ cat /sys/block/md0/md/bitmap_type
[none] bitmap llbitmap [none]
Signed-off-by: Su Yue <glass.su@suse.com>
---
drivers/md/md.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 55a95b227b83..20a953676319 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -4269,6 +4269,8 @@ bitmap_type_show(struct mddev *mddev, char *page)
xa_for_each(&md_submodule, i, head) {
if (head->type != MD_BITMAP)
continue;
+ if (head->id == ID_BITMAP_NONE)
+ continue;
if (mddev->bitmap_id == head->id)
len += sprintf(page + len, "[%s] ", head->name);
--
2.53.0
^ permalink raw reply related
* [PATCH v2 5/5] md/md-bitmap: remove member group from bitmap_operations
From: Su Yue @ 2026-04-07 10:26 UTC (permalink / raw)
To: linux-raid; +Cc: song, xni, linan122, yukuai, heming.zhao, l, Su Yue
In-Reply-To: <20260407102625.5686-1-glass.su@suse.com>
The member is unused now so remove it.
Signed-off-by: Su Yue <glass.su@suse.com>
---
drivers/md/md-bitmap.h | 2 --
drivers/md/md-llbitmap.c | 1 -
2 files changed, 3 deletions(-)
diff --git a/drivers/md/md-bitmap.h b/drivers/md/md-bitmap.h
index 10bc6854798c..7fba97a09b66 100644
--- a/drivers/md/md-bitmap.h
+++ b/drivers/md/md-bitmap.h
@@ -127,8 +127,6 @@ struct bitmap_operations {
int (*register_groups)(struct mddev *mddev);
void (*unregister_groups)(struct mddev *mddev);
-
- struct attribute_group *group;
};
/* the bitmap API */
diff --git a/drivers/md/md-llbitmap.c b/drivers/md/md-llbitmap.c
index 9b3ea4f1d268..4fb9003cdc49 100644
--- a/drivers/md/md-llbitmap.c
+++ b/drivers/md/md-llbitmap.c
@@ -1609,7 +1609,6 @@ static struct bitmap_operations llbitmap_ops = {
.register_groups = llbitmap_register_groups,
.unregister_groups = llbitmap_unregister_groups,
- .group = &md_llbitmap_group,
};
int md_llbitmap_init(void)
--
2.53.0
^ permalink raw reply related
* Re: [RFC PATCH] md/raid5: Fix UAF on IO across the reshape position
From: Xiao Ni @ 2026-04-07 12:23 UTC (permalink / raw)
To: yukuai
Cc: Benjamin Marzinski, Yu Kuai, Song Liu, Li Nan, linux-raid,
dm-devel, Nigel Croxon
In-Reply-To: <f510f801-ae2c-4fe1-b3c2-0346d4237a9c@fnnas.com>
On Tue, Apr 7, 2026 at 2:25 PM Yu Kuai <yukuai@fnnas.com> wrote:
>
> Hi,
>
> 在 2026/4/1 8:22, Xiao Ni 写道:
> > On Wed, Apr 1, 2026 at 12:36 AM Benjamin Marzinski <bmarzins@redhat.com> wrote:
> >> On Tue, Mar 31, 2026 at 10:24:48AM +0800, Xiao Ni wrote:
> >>> On Tue, Mar 31, 2026 at 3:16 AM Benjamin Marzinski <bmarzins@redhat.com> wrote:
> >>>> On Mon, Mar 30, 2026 at 11:44:54PM +0800, Xiao Ni wrote:
> >>>>> On Tue, Mar 24, 2026 at 6:58 AM Benjamin Marzinski <bmarzins@redhat.com> wrote:
> >>>>>> If make_stripe_request() returns STRIPE_WAIT_RESHAPE,
> >>>>>> raid5_make_request() will free the cloned bio. But raid5_make_request()
> >>>>>> can call make_stripe_request() multiple times, writing to the various
> >>>>>> stripes. If that bio got added to the toread or towrite lists of a
> >>>>>> stripe disk in an earlier call to make_stripe_request(), then it's not
> >>>>>> safe to just free the bio if a later part of it is found to cross the
> >>>>>> reshape position. Doing so can lead to a UAF error, when bio_endio()
> >>>>>> is called on the bio for the earlier stripes.
> >>>>>>
> >>>>>> Instead, raid5_make_request() needs to wait until all parts of the bio
> >>>>>> have called bio_endio(). To do this, bios that cross the reshape
> >>>>>> position while the reshape can't make progress are flagged as needing a
> >>>>>> retry, and mddev tracks the number of bios needing a retry which have
> >>>>>> not yet completed. When raid5_make_request() has a bio that failed
> >>>>>> make_stripe_request() with STRIPE_WAIT_RESHAPE, it waits for this
> >>>>>> counter to reach zero. When the bio_endio() is called for the last time
> >>>>>> on a bio needing a retry, it decrements mddev's count of outstanding
> >>>>>> bios needing a retry. This guarantees that raid5_make_request() doesn't
> >>>>>> return until the cloned bio needing a retry for io across the reshape
> >>>>>> boundary is safely cleaned up.
> >>>>>>
> >>>>>> There is a simple reproducer available at [1]. Compile the kernel with
> >>>>>> KASAN for more useful reporting when the error is triggered (this is not
> >>>>>> necessary to see the bug).
> >>>>>>
> >>>>>> [1] https://gist.github.com/bmarzins/e48598824305cf2171289e47d7241fa5
> >>>>>>
> >>>>>> Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
> >>>>>> ---
> >>>>>>
> >>>>>> I've tested this for regressions with the lvm2-testsuite raid tests. I
> >>>>>> have not run any md-specific tests on it.
> >>>>>>
> >>>>>>
> >>>>>> drivers/md/md.c | 30 +++++++++---------------------
> >>>>>> drivers/md/md.h | 5 ++++-
> >>>>>> drivers/md/raid5.c | 8 +++++++-
> >>>>>> 3 files changed, 20 insertions(+), 23 deletions(-)
> >>>>>>
> >>>>>> diff --git a/drivers/md/md.c b/drivers/md/md.c
> >>>>>> index 3ce6f9e9d38e..5ec116b9da32 100644
> >>>>>> --- a/drivers/md/md.c
> >>>>>> +++ b/drivers/md/md.c
> >>>>>> @@ -776,9 +776,11 @@ int mddev_init(struct mddev *mddev)
> >>>>>> atomic_set(&mddev->active, 1);
> >>>>>> atomic_set(&mddev->openers, 0);
> >>>>>> atomic_set(&mddev->sync_seq, 0);
> >>>>>> + atomic_set(&mddev->pending_retry_bios, 0);
> >>>>>> spin_lock_init(&mddev->lock);
> >>>>>> init_waitqueue_head(&mddev->sb_wait);
> >>>>>> init_waitqueue_head(&mddev->recovery_wait);
> >>>>>> + init_waitqueue_head(&mddev->retry_bios_wait);
> >>>>>> mddev->reshape_position = MaxSector;
> >>>>>> mddev->reshape_backwards = 0;
> >>>>>> mddev->last_sync_action = ACTION_IDLE;
> >>>>>> @@ -9218,6 +9220,7 @@ 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;
> >>>>>> + unsigned int must_retry = md_io_clone->must_retry;
> >>>>>>
> >>>>>> if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
> >>>>>> md_bitmap_end(mddev, md_io_clone);
> >>>>>> @@ -9229,7 +9232,11 @@ static void md_end_clone_io(struct bio *bio)
> >>>>>> bio_end_io_acct(orig_bio, md_io_clone->start_time);
> >>>>>>
> >>>>>> bio_put(bio);
> >>>>>> - bio_endio(orig_bio);
> >>>>>> + if (unlikely(must_retry)) {
> >>>>>> + if (atomic_dec_and_test(&mddev->pending_retry_bios))
> >>>>>> + wake_up(&mddev->retry_bios_wait);
> >>>>>> + } else
> >>>>>> + bio_endio(orig_bio);
> >>>>>> percpu_ref_put(&mddev->active_io);
> >>>>>> }
> >>>>>>
> >>>>>> @@ -9243,6 +9250,7 @@ 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->must_retry = 0;
> >>>>>> if (blk_queue_io_stat(bdev->bd_disk->queue))
> >>>>>> md_io_clone->start_time = bio_start_io_acct(*bio);
> >>>>>>
> >>>>>> @@ -9265,26 +9273,6 @@ void md_account_bio(struct mddev *mddev, struct bio **bio)
> >>>>>> }
> >>>>>> EXPORT_SYMBOL_GPL(md_account_bio);
> >>>>>>
> >>>>>> -void md_free_cloned_bio(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;
> >>>>>> -
> >>>>>> - if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
> >>>>>> - md_bitmap_end(mddev, md_io_clone);
> >>>>>> -
> >>>>>> - if (bio->bi_status && !orig_bio->bi_status)
> >>>>>> - orig_bio->bi_status = bio->bi_status;
> >>>>>> -
> >>>>>> - if (md_io_clone->start_time)
> >>>>>> - bio_end_io_acct(orig_bio, md_io_clone->start_time);
> >>>>>> -
> >>>>>> - bio_put(bio);
> >>>>>> - percpu_ref_put(&mddev->active_io);
> >>>>>> -}
> >>>>>> -EXPORT_SYMBOL_GPL(md_free_cloned_bio);
> >>>>>> -
> >>>>>> /* md_allow_write(mddev)
> >>>>>> * Calling this ensures that the array is marked 'active' so that writes
> >>>>>> * may proceed without blocking. It is important to call this before
> >>>>>> diff --git a/drivers/md/md.h b/drivers/md/md.h
> >>>>>> index ac84289664cd..49a231f11676 100644
> >>>>>> --- a/drivers/md/md.h
> >>>>>> +++ b/drivers/md/md.h
> >>>>>> @@ -626,6 +626,9 @@ struct mddev {
> >>>>>>
> >>>>>> /* The sequence number for sync thread */
> >>>>>> atomic_t sync_seq;
> >>>>>> +
> >>>>>> + wait_queue_head_t retry_bios_wait;
> >>>>>> + atomic_t pending_retry_bios;
> >>>>>> };
> >>>>>>
> >>>>>> enum recovery_flags {
> >>>>>> @@ -877,6 +880,7 @@ struct md_io_clone {
> >>>>>> sector_t offset;
> >>>>>> unsigned long sectors;
> >>>>>> enum stat_group rw;
> >>>>>> + unsigned int must_retry;
> >>>>>> struct bio bio_clone;
> >>>>>> };
> >>>>>>
> >>>>>> @@ -917,7 +921,6 @@ extern void md_finish_reshape(struct mddev *mddev);
> >>>>>> void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
> >>>>>> struct bio *bio, sector_t start, sector_t size);
> >>>>>> void md_account_bio(struct mddev *mddev, struct bio **bio);
> >>>>>> -void md_free_cloned_bio(struct bio *bio);
> >>>>>>
> >>>>>> extern bool __must_check md_flush_request(struct mddev *mddev, struct bio *bio);
> >>>>>> void md_write_metadata(struct mddev *mddev, struct md_rdev *rdev,
> >>>>>> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> >>>>>> index a8e8d431071b..fb78a757f2fd 100644
> >>>>>> --- a/drivers/md/raid5.c
> >>>>>> +++ b/drivers/md/raid5.c
> >>>>>> @@ -6217,7 +6217,13 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
> >>>>>>
> >>>>>> mempool_free(ctx, conf->ctx_pool);
> >>>>>> if (res == STRIPE_WAIT_RESHAPE) {
> >>>>>> - md_free_cloned_bio(bi);
> >>>>>> + struct md_io_clone *md_io_clone = bi->bi_private;
> >>>>>> +
> >>>>>> + md_io_clone->must_retry = 1;
> >>>>>> + atomic_inc(&mddev->pending_retry_bios);
> >>>>>> + bio_endio(bi);
> >>>>>> + wait_event(mddev->retry_bios_wait,
> >>>>>> + atomic_read(&mddev->pending_retry_bios)==0);
> >>>>> Hi Ben
> >>>>>
> >>>>> There is a problem here. The new counter pending_retry_bios above
> >>>>> doesn't represent the bios which have been added to stripes. So uaf
> >>>>> still can happen.
> >>>> I don't think it can. That counter won't be zero until there are no bios
> >>>> that need to get retried. So we way end up waiting too long, but we
> >>>> shouldn't ever end up not waiting long enough.
> >>> Ah, yes, you're right. I thought wrongly. The counter tracks multiple
> >>> threads that encounter BLK_STS_RESOURCE. For each thread,
> >>> md_end_clone_bio is called only when all bios return. But why do
> >>> different threads need to sync at raid5_make_request?
> >>>
> >>>
> >>>>> Do we need to add a new counter?
> >>>> I did it that way because I wanted to avoid growing md_io_clone
> >>>> ("must_retry" fit nicely in a hole in the structure, so it didn't take
> >>>> up any extra space). But if you are fine with adding the extra
> >>> Nice design.
> >>>
> >>>> reshape_completion pointer to md_io_clone, then your way avoids the
> >>>> chance that we might wait when we don't need to.
> >>> If you mention the threads sync here, yes.
> >> I can certainly add comments explaining why I left off the READ_ONCE(),
> >> WRITE_ONCE() (actually, there's no real reason not to keep the
> >> WRITE_ONCE, since that's not in the fast path) if you think it makes
> >> sense to leave them off.
> >>
> >> I'm fine with either my or your code for to deal with the waiting,
> >> whichever you think makes more sense. Since this is a very hard bug to
> >> hit, and the fix involves adding extra data to every bio and extra code
> >> in the end_io function that all the raid bios call, I wanted to make the
> >> impact as small as possible. But md_io_clone is still 8 bytes away from
> >> filling up 3 cachelines (at 64 bytes a cacheline), so there's space to
> >> add the reshape_completion pointer without increaseing the number of
> >> cachelines, which means that your solution for waiting is probably the
> >> way to go, unless you think something else might end up needing that
> >> space.
>
> I think it's not good to add a global counter in mddev, and I agree it's
> not good to increase md_io_clone for this corner case.
>
> How about we remove the use of bi_private filed, and replace it with
> container_of() to get md_clone_io from md_end_clone_io. And then we can use
> this field to record the completion without adding new field.
This is a nice design. Ben, will you send V2?
Regards
Xiao
>
> > Thanks. After thinking, as you said, this is a corner case. So both
> > ways are fine with me also. Let's wait for Kuai's decision.
> >
> > Regards
> > Xiao
> >> -Ben
> >>
> >>>>> Because
> >>>>> md_end_clone_io is only called when all bios return. How about
> >>>>> something like:
> >>>>>
> >>>>> md_end_clone_io
> >>>>> + if (unlikely(READ_ONCE(md_io_clone->waiting_reshape))) {
> >>>>> + complete(md_io_clone->reshape_completion);
> >>>>> + } else {
> >>>>> + bio_endio(orig_bio);
> >>>>> + }
> >>>>>
> >>>>> raid5_make_request:
> >>>>>
> >>>>> if (res == STRIPE_WAIT_RESHAPE) {
> >>>>> - md_free_cloned_bio(bi);
> >>>>> + struct md_io_clone *md_io_clone = bi->bi_private;
> >>>>> + struct completion done;
> >>>>> +
> >>>>> + init_completion(&done);
> >>>>> + md_io_clone->reshape_completion = &done;
> >>>>> + WRITE_ONCE(md_io_clone->waiting_reshape, 1);
> >>>>> +
> >>>>> + bio_endio(bi);
> >>>>> +
> >>>>> + wait_for_completion(&done);
> >>>> This looks fine.
> >>>>
> >>>> I'm pretty sure that the READ_ONCE() and WRITE_ONCE() are unnecessary.
> >>>> First, theres no chance that these operations will ever happen at the
> >>>> same time and we're just writing a 1, so there's no chance of tearing.
> >>> For the first point, I have a question. They can't happen
> >>> simultaneously. But the cache of different CPUs may contain different
> >>> values.
> >>>
> >>>> The compiler can't reorder setting md_io_clone->waiting_reshape to afer
> >>>> bio_endio(), since bio_endio can free md_io_clone. Also the compiler
> >>>> can't reorder reading it to before calling md_end_clone_io() from
> >>>> bio_endio(), obviously.
> >>> Thanks very much for pointing this out.
> >>>
> >>>> If the bio was never chained, then there can't be a race, since only
> >>>> raid5_make_request() will be calling bio_endio(). waiting_reshape will
> >>>> be read by the same process that sets it.
> >>>>
> >>>> If the bio was chained, then like you said, md_end_clone_io() will only
> >>>> get called by the final caller of bio_endio(). This is determined by
> >>>> bio_remaining_done(), which guarantees a full memory barrier for
> >>>> atomic_dec_and_test(&bio->__bi_remaining).
> >>> Nice analysis! Thanks.
> >>>
> >>>> Since we know the compiler will keep these instructions on the proper
> >>>> size of a full memory barrier, I'm pretty sure that this is concurrency
> >>>> race free, even without the READ_ONCE() and WRITE_ONCE(). Like with
> >>>> trying to avoid growing md_io_clone, I was trying to keep
> >>>> md_end_clone_io() as fast as possible (at least on architectures where
> >>>> READ_ONCE can have more of a performance impact) Again, if I'm
> >>>> overthinking this (or if my concurrency argument is flawed) feel free to
> >>>> ignore me.
> >>> Thanks for your patience in explaining the concurrency problem. I
> >>> agree with you. And it's better to add comments that describe the
> >>> reason and it will help people understand it better.
> >>>
> >>> By the way, the md raid maintainer's email has changed. I fixed the
> >>> email address in to list.
> >>>
> >>> Regards
> >>> Xiao
> >>>
> >>>> -Ben
> >>>>
> >>>>> Best Regards
> >>>>> Xiao
> >>>>>
> >>>>>
> >>>>>> return false;
> >>>>>> }
> >>>>>> --
> >>>>>> 2.53.0
> >>>>>>
> >
> --
> Thansk,
> Kuai
>
^ permalink raw reply
* Re: [PATCH] md/raid5: fix soft lockup in retry_aligned_read()
From: Yu Kuai @ 2026-04-07 13:39 UTC (permalink / raw)
To: Chia-Ming Chang, song
Cc: linan122, shli, neil, linux-raid, linux-kernel, stable,
FengWei Shih, yukuai
In-Reply-To: <20260402061406.455755-1-chiamingc@synology.com>
在 2026/4/2 14:14, Chia-Ming Chang 写道:
> When retry_aligned_read() encounters an overlapped stripe, it releases
> the stripe via raid5_release_stripe() which puts it on the lockless
> released_stripes llist. In the next raid5d loop iteration,
> release_stripe_list() drains the stripe onto handle_list (since
> STRIPE_HANDLE is set by the original IO), but retry_aligned_read()
> runs before handle_active_stripes() and removes the stripe from
> handle_list via find_get_stripe() -> list_del_init(). This prevents
> handle_stripe() from ever processing the stripe to resolve the
> overlap, causing an infinite loop and soft lockup.
>
> Fix this by using __release_stripe() with temp_inactive_list instead
> of raid5_release_stripe() in the failure path, so the stripe does not
> go through the released_stripes llist. This allows raid5d to break out
> of its loop, and the overlap will be resolved when the stripe is
> eventually processed by handle_stripe().
>
> Fixes: 773ca82fa1ee ("raid5: make release_stripe lockless")
> Cc:stable@vger.kernel.org
> Signed-off-by: FengWei Shih<dannyshih@synology.com>
> Signed-off-by: Chia-Ming Chang<chiamingc@synology.com>
> ---
> drivers/md/raid5.c | 8 +++++++-
> 1 file changed, 7 insertions(+), 1 deletion(-)
Applied to md-7.1
--
Thansk,
Kuai
^ permalink raw reply
* Re: [PATCH] md/raid0: use kvzalloc/kvfree for strip_zone and devlist allocations
From: Gregory Price @ 2026-04-07 16:04 UTC (permalink / raw)
To: Yu Kuai
Cc: song, linan122, linux-raid, linux-kernel, linux-mm,
syzbot+924649752adf0d3ac9dd, akpm
In-Reply-To: <13f2856b-a5b6-410d-a39b-94cc48ca546b@fnnas.com>
On Tue, Apr 07, 2026 at 01:06:19PM +0800, Yu Kuai wrote:
> Hi,
>
> 在 2026/3/9 7:42, Gregory Price 写道:
> > syzbot reported a WARNING at mm/page_alloc.c:__alloc_frozen_pages_noprof()
> > triggered by create_strip_zones() in the RAID0 driver.
> >
> > When raid_disks is large, the allocation size exceeds MAX_PAGE_ORDER (4MB
> > on x86), causing WARN_ON_ONCE_GFP(order > MAX_PAGE_ORDER).
> >
> > Convert the strip_zone and devlist allocations from kzalloc/kzalloc_objs to
> > kvzalloc/kvzalloc_objs, which first attempts a contiguous allocation with
> > __GFP_NOWARN and then falls back to vmalloc for large sizes. Convert the
> > corresponding kfree calls to kvfree.
> >
> > Both arrays are pure metadata lookup tables (arrays of pointers and zone
> > descriptors) accessed only via indexing, so they do not require physically
> > contiguous memory.
> >
> > Reported-by:syzbot+924649752adf0d3ac9dd@syzkaller.appspotmail.com
>
> Reported-by should be followed by Closes tag, applied tom md-7.1 with following tag:
> Closes:[syzbot] [mm?] WARNING in create_strip_zones - syzbot <https://lore.kernel.org/all/69adaba8.a00a0220.b130.0005.GAE@google.com/>
>
Ah, gotcha, didn't realize there was automation here, first time i've
poked at a syzbot report.
Thanks!
> > Signed-off-by: Gregory Price<gourry@gourry.net>
> > ---
> > drivers/md/raid0.c | 18 +++++++++---------
> > 1 file changed, 9 insertions(+), 9 deletions(-)
>
> --
> Thansk,
> Kuai
^ permalink raw reply
* [PATCH v2] md/raid5: Fix UAF on IO across the reshape position
From: Benjamin Marzinski @ 2026-04-08 4:35 UTC (permalink / raw)
To: Yu Kuai, Song Liu, Li Nan, Xiao Ni; +Cc: linux-raid, dm-devel, Nigel Croxon
If make_stripe_request() returns STRIPE_WAIT_RESHAPE,
raid5_make_request() will free the cloned bio. But raid5_make_request()
can call make_stripe_request() multiple times, writing to the various
stripes. If that bio got added to the toread or towrite lists of a
stripe disk in an earlier call to make_stripe_request(), then it's not
safe to just free the bio if a later part of it is found to cross the
reshape position. Doing so can lead to a UAF error, when bio_endio()
is called on the bio for the earlier stripes.
Instead, raid5_make_request() needs to wait until all parts of the bio
have called bio_endio(). To do this, bios that cross the reshape
position while the reshape can't make progress are flagged as needing to
wait for all parts to complete. When raid5_make_request() has a bio that
failed make_stripe_request() with STRIPE_WAIT_RESHAPE, it sets
bi->bi_private to a completion struct and waits for completion after
ending the bio. When the bio_endio() is called for the last time on a
clone bio with bi->bi_private set, it wakes up the waiter. This
guarantees that raid5_make_request() doesn't return until the cloned bio
needing a retry for io across the reshape boundary is safely cleaned up.
There is a simple reproducer available at [1]. Compile the kernel with
KASAN for more useful reporting when the error is triggered (this is not
necessary to see the bug).
[1] https://gist.github.com/bmarzins/e48598824305cf2171289e47d7241fa5
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
Changes from v1:
- Removed mddev->pending_retry_bios, mddev->retry_bios_wait, and
md_io_clone->must_retry. Instead, use a completion struct
pointed to by bi->bi_private, as suggested by Xiao Ni and Yu Kuai.
drivers/md/md.c | 31 ++++++++-----------------------
drivers/md/md.h | 1 -
drivers/md/raid5.c | 7 ++++++-
3 files changed, 14 insertions(+), 25 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 3ce6f9e9d38e..4318d875a5f6 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -9215,9 +9215,11 @@ static void md_bitmap_end(struct mddev *mddev, struct md_io_clone *md_io_clone)
static void md_end_clone_io(struct bio *bio)
{
- struct md_io_clone *md_io_clone = bio->bi_private;
+ struct md_io_clone *md_io_clone = container_of(bio, struct md_io_clone,
+ bio_clone);
struct bio *orig_bio = md_io_clone->orig_bio;
struct mddev *mddev = md_io_clone->mddev;
+ struct completion *reshape_completion = bio->bi_private;
if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
md_bitmap_end(mddev, md_io_clone);
@@ -9229,7 +9231,10 @@ static void md_end_clone_io(struct bio *bio)
bio_end_io_acct(orig_bio, md_io_clone->start_time);
bio_put(bio);
- bio_endio(orig_bio);
+ if (unlikely(reshape_completion))
+ complete(reshape_completion);
+ else
+ bio_endio(orig_bio);
percpu_ref_put(&mddev->active_io);
}
@@ -9254,7 +9259,7 @@ static void md_clone_bio(struct mddev *mddev, struct bio **bio)
}
clone->bi_end_io = md_end_clone_io;
- clone->bi_private = md_io_clone;
+ clone->bi_private = NULL;
*bio = clone;
}
@@ -9265,26 +9270,6 @@ void md_account_bio(struct mddev *mddev, struct bio **bio)
}
EXPORT_SYMBOL_GPL(md_account_bio);
-void md_free_cloned_bio(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;
-
- if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
- md_bitmap_end(mddev, md_io_clone);
-
- if (bio->bi_status && !orig_bio->bi_status)
- orig_bio->bi_status = bio->bi_status;
-
- if (md_io_clone->start_time)
- bio_end_io_acct(orig_bio, md_io_clone->start_time);
-
- bio_put(bio);
- percpu_ref_put(&mddev->active_io);
-}
-EXPORT_SYMBOL_GPL(md_free_cloned_bio);
-
/* md_allow_write(mddev)
* Calling this ensures that the array is marked 'active' so that writes
* may proceed without blocking. It is important to call this before
diff --git a/drivers/md/md.h b/drivers/md/md.h
index ac84289664cd..5d57fee22901 100644
--- a/drivers/md/md.h
+++ b/drivers/md/md.h
@@ -917,7 +917,6 @@ extern void md_finish_reshape(struct mddev *mddev);
void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
struct bio *bio, sector_t start, sector_t size);
void md_account_bio(struct mddev *mddev, struct bio **bio);
-void md_free_cloned_bio(struct bio *bio);
extern bool __must_check md_flush_request(struct mddev *mddev, struct bio *bio);
void md_write_metadata(struct mddev *mddev, struct md_rdev *rdev,
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index a8e8d431071b..dc0c680ca199 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -6217,7 +6217,12 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
mempool_free(ctx, conf->ctx_pool);
if (res == STRIPE_WAIT_RESHAPE) {
- md_free_cloned_bio(bi);
+ DECLARE_COMPLETION_ONSTACK(done);
+ WRITE_ONCE(bi->bi_private, &done);
+
+ bio_endio(bi);
+
+ wait_for_completion(&done);
return false;
}
--
2.53.0
^ permalink raw reply related
* [GIT PULL] md-7.1-20260407
From: Yu Kuai @ 2026-04-08 5:05 UTC (permalink / raw)
To: axboe, linux-block, linux-raid
Cc: linux-kernel, song, linan122, abd.masalkhi, chiamingc, gourry,
moonafterrain, xni, yukuai
Hi Jens,
Please consider pulling the following changes into your for-7.1/block branch.
This pull request contains:
Bug Fixes:
- avoid a sysfs deadlock when clearing array state (Yu Kuai)
- validate raid5 journal payloads before reading metadata (Junrui Luo)
- fall back to the correct bitmap operations after version mismatches (Yu Kuai)
- serialize overlapping writes on writemostly raid1 disks (Xiao Ni)
- wake raid456 reshape waiters before suspend (Yu Kuai)
- prevent retry_aligned_read() from triggering soft lockups (Chia-Ming Chang)
Improvements:
- switch raid0 strip zone and devlist allocations to kvmalloc helpers (Gregory Price)
- track clean unwritten stripes for proactive RAID5 parity building (Yu Kuai)
- speed up initial llbitmap sync with write_zeroes_unmap support (Yu Kuai)
Cleanups:
- remove the unused static md workqueue definition (Abd-Alrhman Masalkhi)
Thanks,
Kuai
---
The following changes since commit 8b155f2e4a91f3507951e6ace4b413688ac28b96:
block: remove unused BVEC_ITER_ALL_INIT (2026-04-04 08:10:37 -0600)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/mdraid/linux.git tags/md-7.1-20260407
for you to fetch changes up to 7f9f7c697474268d9ef9479df3ddfe7cdcfbbffc:
md/raid5: fix soft lockup in retry_aligned_read() (2026-04-07 15:13:52 +0800)
----------------------------------------------------------------
Abd-Alrhman Masalkhi (1):
md: remove unused static md_wq workqueue
Chia-Ming Chang (1):
md/raid5: fix soft lockup in retry_aligned_read()
Gregory Price (1):
md/raid0: use kvzalloc/kvfree for strip_zone and devlist allocations
Junrui Luo (1):
md/raid5: validate payload size before accessing journal metadata
Xiao Ni (1):
md/raid1: serialize overlap io for writemostly disk
Yu Kuai (5):
md: fix array_state=clear sysfs deadlock
md: add fallback to correct bitmap_ops on version mismatch
md/md-llbitmap: add CleanUnwritten state for RAID-5 proactive parity building
md/md-llbitmap: optimize initial sync with write_zeroes_unmap support
md: wake raid456 reshape waiters before suspend
drivers/md/md-llbitmap.c | 202 ++++++++++++++++++++++++++++++++++++++++++++---
drivers/md/md.c | 139 +++++++++++++++++++++++++++++---
drivers/md/md.h | 5 +-
drivers/md/raid0.c | 18 ++---
drivers/md/raid1.c | 47 ++++++++---
drivers/md/raid5-cache.c | 48 +++++++----
drivers/md/raid5.c | 8 +-
7 files changed, 405 insertions(+), 62 deletions(-)
^ permalink raw reply
* [PATCH V2 0/2] md/nvme: Enable PCI P2PDMA support for RAID0 and NVMe Multipath
From: Chaitanya Kulkarni @ 2026-04-08 7:25 UTC (permalink / raw)
To: song, yukuai, linan122, kbusch, axboe, hch, sagi
Cc: linux-raid, linux-nvme, kmodukuri, Chaitanya Kulkarni
Hi,
This patch series extends PCI peer-to-peer DMA (P2PDMA) support to enable
direct data transfers between PCIe devices through RAID and NVMe multipath
block layers.
Current Linux kernel P2PDMA infrastructure supports direct peer-to-peer
transfers, but this support is not propagated through certain storage
stacks like MD RAID and NVMe multipath. This adds two patches for
MD RAID 0/1/10 and NVMe to propogate P2PDMA support through the
storage stack.
All four test scenarios demonstrate that P2PDMA capabilities are correctly
propagated through both the MD RAID layer (patch 1/2) and NVMe multipath
layer (patch 2/2). Direct peer-to-peer transfers complete successfully with
full data integrity verification, confirming that:
1. RAID devices properly inherit P2PDMA capability from member devices
2. NVMe multipath devices correctly expose P2PDMA support
3. P2P memory buffers can be used for transfers involving both types
4. Data integrity is maintained across all transfer combinations
I've added the patch specific tests and blktest log as well at the end.
Repo:-
git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux.git
Branch HEAD:-
commit cb793ff1353d4eabd18d880c684b5311c7dc6400 (origin/for-next)
Merge: cc91702dedc5 2d148a214b24
Author: Jens Axboe <axboe@kernel.dk>
Date: Tue Apr 7 08:22:30 2026 -0600
Merge branch 'for-7.1/block' into for-next
* for-7.1/block:
xfs: use bio_await in xfs_zone_gc_reset_sync
block: add a bio_submit_or_kill helper
block: factor out a bio_await helper
block: unify the synchronous bi_end_io callbacks
xfs: fix number of GC bvecs
-ck
Changes from V1:-
- Update patch 1 to explicitly support MD RAID 0/1/10.
- Fix signoff chain order for patch 2.
- Clear BLK_FEAT_PCI_P2PDMA in nvme_mpath_add_disk() when a newly
added path does not support it, to handle multipath across different
transports.
- Add nvme multipath test log for mixed transport TCP and PCIe.
Kiran Kumar Modukuri (2):
md: propagate BLK_FEAT_PCI_P2PDMA from member devices
nvme-multipath: enable PCI P2PDMA for multipath devices
drivers/md/md.c | 4 ++++
drivers/md/raid0.c | 1 +
drivers/md/raid1.c | 1 +
drivers/md/raid10.c | 1 +
drivers/nvme/host/multipath.c | 18 ++++++++++++++++++
5 files changed, 25 insertions(+)
========================================================
* MD RAID Personalities and NVMe testing :-
========================================================
* RAID test log :-
========================
RAID Level Personality P2PDMA Result
==============================================
RAID0 Striping Opted in MATCH -- works
RAID1 Mirror Opted in MATCH -- works
RAID10 Stripe+Mirror Opted in MATCH -- works
RAID4 Parity (dedicated) Not opted in Remote I/O error -- rejected
RAID5 Parity (distributed) Not opted in Remote I/O error -- rejected
lab@vm70:~/p2pmem-test$ nvme list -v
Subsystem Subsystem-NQN Controllers
---------------- ------------------------------------------------------------------------------------------------ ----------------
nvme-subsys0 nqn.2019-08.org.qemu:nqn.2019-08.org.qemu:shared-ns nvme0, nvme1
nvme-subsys2 nqn.2019-08.org.qemu:nvme3 nvme2
nvme-subsys3 nqn.2019-08.org.qemu:nvme4 nvme3
nvme-subsys4 nqn.2019-08.org.qemu:nvme5 nvme4
Device SN MN FR TxPort Address Slot Subsystem Namespaces
-------- -------------------- ---------------------------------------- -------- ------ -------------- ------ ------------ ----------------
nvme0 shared1 QEMU NVMe Ctrl 1.0 pcie 0000:0a:00.0 nvme-subsys0 nvme0n1
nvme1 shared1 QEMU NVMe Ctrl 1.0 pcie 0000:0b:00.0 nvme-subsys0 nvme0n1
nvme2 nvme3 QEMU NVMe Ctrl 1.0 pcie 0000:0c:00.0 nvme-subsys2 nvme2n1
nvme3 nvme4 QEMU NVMe Ctrl 1.0 pcie 0000:0d:00.0 nvme-subsys3 nvme3n1
nvme4 nvme5 QEMU NVMe Ctrl 1.0 pcie 0000:0e:00.0 nvme-subsys4 nvme4n1
Device Generic NSID Usage Format Controllers
------------ ------------ ---------- -------------------------- ---------------- ----------------
/dev/nvme0n1 /dev/ng0n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme0, nvme1
/dev/nvme2n1 /dev/ng2n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme2
/dev/nvme3n1 /dev/ng3n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme3
/dev/nvme4n1 /dev/ng4n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme4
lab@vm70:~/p2pmem-test$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda 253:0 0 50G 0 disk
├─vda1 253:1 0 49G 0 part /
├─vda14 253:14 0 4M 0 part
├─vda15 253:15 0 106M 0 part /boot/efi
└─vda16 259:0 0 913M 0 part /boot
nvme4n1 259:1 0 10G 0 disk
└─md0 9:0 0 10G 0 raid1 /mnt/raid1
nvme2n1 259:2 0 10G 0 disk
nvme3n1 259:3 0 10G 0 disk
└─md0 9:0 0 10G 0 raid1 /mnt/raid1
nvme0n1 259:5 0 10G 0 disk
lab@vm70:~$ uname -r
7.0.0-rc2-p2pdma-v2+
lab@vm70:~$ cat /proc/mdstat
Personalities : [raid0] [raid1]
md0 : active raid1 nvme4n1[1] nvme3n1[0]
10476544 blocks super 1.2 [2/2] [UU]
unused devices: <none>
lab@vm70:~$ cd ~/p2pmem-test/
lab@vm70:~/p2pmem-test$ echo "=== 4K x 100 chunks ===" && echo "lab123" | sudo ~/p2pmem-test/p2pmem-test /dev/nvme0n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 4096 -c 100 2>&1 && echo "=== 1MB x 10 chunks ===" && sudo ~/p2pmem-test/p2pmem-test /dev/nvme0n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 1048576 -c 10 2>&1 && echo "=== 4K x 100 multi-thread ===" && sudo ~/p2pmem-test/p2pmem-test /dev/nvme0n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 4096 -c 100 -t 4 2>&1'
> ^C
lab@vm70:~/p2pmem-test$ echo "=== 4K x 100 chunks ===" && echo "lab123" | sudo ~/p2pmem-test/p2pmem-test /dev/nvme0n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 4096 -c 100 2>&1 && echo "=== 1MB x 10 chunks ===" && sudo ~/p2pmem-test/p2pmem-test /dev/nvme0n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 1048576 -c 10 2>&1 && echo "=== 4K x 100 multi-thread ===" && sudo ~/p2pmem-test/p2pmem-test /dev/nvme0n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 4096 -c 100 -t 4
=== 4K x 100 chunks ===
Running p2pmem-test: reading /dev/nvme0n1 (10.74GB): writing /dev/md0 (10.73GB): p2pmem buffer /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 4096 : number of chunks = 100: total = 409.6kB : thread(s) = 1 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7fe4e84d2000 (p2pmem): mmap = 4.096kB
PAGE_SIZE = 4096B
Transfer:
409.60kB in 58.8 ms 6.96MB/s
=== 1MB x 10 chunks ===
Running p2pmem-test: reading /dev/nvme0n1 (10.74GB): writing /dev/md0 (10.73GB): p2pmem buffer /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 1048576 : number of chunks = 10: total = 10.49MB : thread(s) = 1 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7f12852c9000 (p2pmem): mmap = 1.049MB
PAGE_SIZE = 4096B
Transfer:
10.49MB in 3.4 s 3.11MB/s
=== 4K x 100 multi-thread ===
Running p2pmem-test: reading /dev/nvme0n1 (10.74GB): writing /dev/md0 (10.73GB): p2pmem buffer /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 4096 : number of chunks = 100: total = 409.6kB : thread(s) = 4 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7f6fc7e59000 (p2pmem): mmap = 16.38kB
PAGE_SIZE = 4096B
Transfer:
409.60kB in 47.0 ms 8.72MB/s
lab@vm70:~/p2pmem-test$ sudo ~/p2pmem-test/p2pmem-test /dev/nvme0n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate --check -s 1048576 -c 1
Running p2pmem-test: reading /dev/nvme0n1 (10.74GB): writing /dev/md0 (10.73GB): p2pmem buffer /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 1048576 : number of chunks = 1: total = 1.049MB : thread(s) = 1 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7fa9b106b000 (p2pmem): mmap = 1.049MB
PAGE_SIZE = 4096B
checking data with seed = 1774381052
MATCH on data check, 0x584cb982 = 0x584cb982.
Transfer:
1.05MB in 1.5 s 679.97kB/s
lab@vm70:~/p2pmem-test$ sudo ~/p2pmem-test/p2pmem-test /dev/nvme0n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate --check -s 1048576 -c 10
Running p2pmem-test: reading /dev/nvme0n1 (10.74GB): writing /dev/md0 (10.73GB): p2pmem buffer /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 1048576 : number of chunks = 10: total = 10.49MB : thread(s) = 1 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7f6d62bfb000 (p2pmem): mmap = 1.049MB
PAGE_SIZE = 4096B
checking data with seed = 1774381072
MATCH on data check, 0x4dc66c1 = 0x4dc66c1.
Transfer:
10.49MB in 3.4 s 3.12MB/s
lab@vm70:~/p2pmem-test$ sudo ~/p2pmem-test/p2pmem-test /dev/nvme0n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate --check -s 4096 -c 100
Running p2pmem-test: reading /dev/nvme0n1 (10.74GB): writing /dev/md0 (10.73GB): p2pmem buffer /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 4096 : number of chunks = 100: total = 409.6kB : thread(s) = 1 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7faa0ca47000 (p2pmem): mmap = 4.096kB
PAGE_SIZE = 4096B
checking data with seed = 1774381089
MATCH on data check, 0x246e0e13 = 0x246e0e13.
Transfer:
409.60kB in 66.3 ms 6.18MB/s
$ sudo mdadm --zero-superblock /dev/nvme2n1 /dev/nvme3n1 /dev/nvme4n1 2>&1 && sudo mdadm --create /dev/md0 --level=0 --raid-devices=3 /dev/nvme2n1 /dev/nvme3n1 /dev/nvme4n1 <<< "y" 2>&1 && echo "=== Done ===" && cat /proc/mdstat'
mdadm: stopped /dev/md0
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.
=== Done ===
Personalities : [raid0] [raid1] [raid4] [raid5] [raid6] [raid10]
md0 : active raid0 nvme4n1[2] nvme3n1[1] nvme2n1[0]
31429632 blocks super 1.2 512k chunks
unused devices: <none>
lab@vm70:~$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda 253:0 0 50G 0 disk
├─vda1 253:1 0 49G 0 part /
├─vda14 253:14 0 4M 0 part
├─vda15 253:15 0 106M 0 part /boot/efi
└─vda16 259:0 0 913M 0 part /boot
nvme0n1 259:2 0 10G 0 disk
nvme2n1 259:3 0 10G 0 disk
└─md0 9:0 0 30G 0 raid0
nvme3n1 259:4 0 10G 0 disk
└─md0 9:0 0 30G 0 raid0
nvme4n1 259:5 0 10G 0 disk
└─md0 9:0 0 30G 0 raid0
lab@vm70:~/p2pmem-test$ echo "=== 4K x 100 chunks ===" && echo "lab123" | sudo ~/p2pmem-test/p2pmem-test /dev/nvme0n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 4096 -c 100 2>&1 && echo "=== 1MB x 10 chunks ===" && sudo ~/p2pmem-test/p2pmem-test /dev/nvme0n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 1048576 -c 10 2>&1 && echo "=== 4K x 100 multi-thread ===" && sudo ~/p2pmem-test/p2pmem-test /dev/nvme0n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 4096 -c 100 -t 4 2>&1
=== 4K x 100 chunks ===
Running p2pmem-test: reading /dev/nvme0n1 (10.74GB): writing /dev/md0 (32.18GB): p2pmem buffer /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 4096 : number of chunks = 100: total = 409.6kB : thread(s) = 1 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7fa56ef6e000 (p2pmem): mmap = 4.096kB
PAGE_SIZE = 4096B
Transfer:
409.60kB in 38.2 ms 10.74MB/s
=== 1MB x 10 chunks ===
Running p2pmem-test: reading /dev/nvme0n1 (10.74GB): writing /dev/md0 (32.18GB): p2pmem buffer /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 1048576 : number of chunks = 10: total = 10.49MB : thread(s) = 1 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7f07c945e000 (p2pmem): mmap = 1.049MB
PAGE_SIZE = 4096B
Transfer:
10.49MB in 155.5 ms 67.44MB/s
=== 4K x 100 multi-thread ===
Running p2pmem-test: reading /dev/nvme0n1 (10.74GB): writing /dev/md0 (32.18GB): p2pmem buffer /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 4096 : number of chunks = 100: total = 409.6kB : thread(s) = 4 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7f32c8439000 (p2pmem): mmap = 16.38kB
PAGE_SIZE = 4096B
Transfer:
409.60kB in 24.1 ms 16.99MB/s
lab@vm70:~/p2pmem-test$ sudo mdadm --create /dev/md0 --level=4 --raid-devices=3 /dev/nvme2n1 /dev/nvme3n1 /dev/nvme4n1 --force <<< "y"
mdadm: /dev/nvme2n1 appears to be part of a raid array:
level=raid0 devices=3 ctime=Tue Mar 24 20:29:33 2026
mdadm: /dev/nvme3n1 appears to be part of a raid array:
level=raid0 devices=3 ctime=Tue Mar 24 20:29:33 2026
mdadm: /dev/nvme4n1 appears to be part of a raid array:
level=raid0 devices=3 ctime=Tue Mar 24 20:29:33 2026
Continue creating array? mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.
lab@vm70:~$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS
vda 253:0 0 50G 0 disk
├─vda1 253:1 0 49G 0 part /
├─vda14 253:14 0 4M 0 part
├─vda15 253:15 0 106M 0 part /boot/efi
└─vda16 259:0 0 913M 0 part /boot
nvme0n1 259:2 0 10G 0 disk
nvme2n1 259:3 0 10G 0 disk
└─md0 9:0 0 20G 0 raid4
nvme3n1 259:4 0 10G 0 disk
└─md0 9:0 0 20G 0 raid4
nvme4n1 259:5 0 10G 0 disk
└─md0 9:0 0 20G 0 raid4
== basic DD test ===
lab@vm70:~$ sudo dd if=/dev/urandom bs=4096 count=110 of=/dev/md0
110+0 records in
110+0 records out
450560 bytes (451 kB, 440 KiB) copied, 0.00700321 s, 64.3 MB/s
lab@vm70:~$ echo "lab123" | sudo -S dmesg -C && echo "=== P2P test on RAID4 ===" && echo "lab123" | sudo -S ~/p2pmem-test/p2pmem-test /dev/nvme0n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate --check -s 4096 -c 1 2>&1; echo "exit_code=$?" && echo "=== dmesg ===" && dmesg
=== P2P test on RAID4 ===
Running p2pmem-test: reading /dev/nvme0n1 (10.74GB): writing /dev/md0 (21.46GB): p2pmem buffer /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 4096 : number of chunks = 1: total = 4.096kB : thread(s) = 1 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7fd2c5a17000 (p2pmem): mmap = 4.096kB
PAGE_SIZE = 4096B
checking data with seed = 1774382878
pwrite: Remote I/O error
exit_code=1
=== dmesg ===
=======RAID10===========
sshpass -p 'lab123' ssh -o StrictHostKeyChecking=no -T lab@192.168.122.251 'echo "lab123" | sudo -S mdadm --stop /dev/md127 2>&1 && echo "lab123" | sudo -S mdadm --zero-superblock /dev/nvme2n1 /dev/nvme3n1 /dev/nvme4n1 /dev/nvme5n1 2>&1 && echo "=== Cleared ===" && echo "lab123" | sudo -S mdadm --create /dev/md0 --level=10 --raid-devices=4 /dev/nvme2n1 /dev/nvme3n1 /dev/nvme4n1 /dev/nvme5n1 <<< "y" 2>&1 && echo "=== RAID10 created ===" && cat /proc/mdstat'
mdadm: stopped /dev/md127
mdadm: Unrecognised md component device - /dev/nvme5n1
=== Cleared ===
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.
=== RAID10 created ===
Personalities : [raid0] [raid1] [raid4] [raid5] [raid6] [raid10]
md0 : active raid10 nvme5n1[3] nvme4n1[2] nvme3n1[1] nvme2n1[0]
20953088 blocks super 1.2 512K chunks 2 near-copies [4/4] [UUUU]
[>....................] resync = 0.0% (12800/20953088) finish=27.2min speed=12800K/sec
unused devices: <none>
lab@vm70:~$ sudo nvme list -v
Subsystem Subsystem-NQN Controllers
---------------- ------------------------------------------------------------------------------------------------ ----------------
nvme-subsys1 nqn.2019-08.org.qemu:nqn.2019-08.org.qemu:shared-ns nvme0, nvme1
nvme-subsys2 nqn.2019-08.org.qemu:nvme3 nvme2
nvme-subsys3 nqn.2019-08.org.qemu:nvme4 nvme3
nvme-subsys4 nqn.2019-08.org.qemu:nvme5 nvme4
nvme-subsys5 nqn.2019-08.org.qemu:nvme6 nvme5
Device SN MN FR TxPort Address Slot Subsystem Namespaces
-------- -------------------- ---------------------------------------- -------- ------ -------------- ------ ------------ ----------------
nvme0 shared2 QEMU NVMe Ctrl 1.0 pcie 0000:0a:00.0 nvme-subsys1 nvme1n1
nvme1 shared2 QEMU NVMe Ctrl 1.0 pcie 0000:0b:00.0 nvme-subsys1 nvme1n1
nvme2 nvme3 QEMU NVMe Ctrl 1.0 pcie 0000:0c:00.0 nvme-subsys2 nvme2n1
nvme3 nvme4 QEMU NVMe Ctrl 1.0 pcie 0000:0d:00.0 nvme-subsys3 nvme3n1
nvme4 nvme5 QEMU NVMe Ctrl 1.0 pcie 0000:0e:00.0 nvme-subsys4 nvme4n1
nvme5 nvme6 QEMU NVMe Ctrl 1.0 pcie 0000:11:00.0 nvme-subsys5 nvme5n1
Device Generic NSID Usage Format Controllers
------------ ------------ ---------- -------------------------- ---------------- ----------------
/dev/nvme1n1 /dev/ng1n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme0, nvme1
/dev/nvme2n1 /dev/ng2n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme2
/dev/nvme3n1 /dev/ng3n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme3
/dev/nvme4n1 /dev/ng4n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme4
/dev/nvme5n1 /dev/ng5n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme5
lab@vm70:~$ echo "=== 4K x 100 chunks ===" && echo "lab123" | sudo ~/p2pmem-test/p2pmem-test /dev/nvme1n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 4096 -c 100 2>&1 && echo "=== 1MB x 10 chunks ===" && sudo ~/p2pmem-test/p2pmem-test /dev/nvme1n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 1048576 -c 10 2>&1 && echo "=== 4K x 100 multi-thread ===" && sudo ~/p2pmem-test/p2pmem-test /dev/nvme1n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 4096 -c 100 -t 4 2>&1
=== 4K x 100 chunks ===
Running p2pmem-test: reading /dev/nvme1n1 (10.74GB): writing /dev/md0 (21.46GB): p2pmem buffer /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 4096 : number of chunks = 100: total = 409.6kB : thread(s) = 1 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7ffa11234000 (p2pmem): mmap = 4.096kB
PAGE_SIZE = 4096B
Transfer:
409.60kB in 76.9 ms 5.33MB/s
=== 1MB x 10 chunks ===
Running p2pmem-test: reading /dev/nvme1n1 (10.74GB): writing /dev/md0 (21.46GB): p2pmem buffer /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 1048576 : number of chunks = 10: total = 10.49MB : thread(s) = 1 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7fcd6c9c2000 (p2pmem): mmap = 1.049MB
PAGE_SIZE = 4096B
Transfer:
10.49MB in 425.4 ms 24.65MB/s
=== 4K x 100 multi-thread ===
Running p2pmem-test: reading /dev/nvme1n1 (10.74GB): writing /dev/md0 (21.46GB): p2pmem buffer /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 4096 : number of chunks = 100: total = 409.6kB : thread(s) = 4 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7fbcedb55000 (p2pmem): mmap = 16.38kB
PAGE_SIZE = 4096B
Transfer:
409.60kB in 46.5 ms 8.81MB/s
sudo -S mdadm --stop /dev/md0 2>&1 && echo "lab123" | sudo -S mdadm --zero-superblock /dev/nvme2n1 /dev/nvme3n1 /dev/nvme4n1 /dev/nvme5n1 2>&1 && echo "lab123" | sudo -S mdadm --create /dev/md0 --level=5 --raid-devices=4 /dev/nvme2n1 /dev/nvme3n1 /dev/nvme4n1 /dev/nvme5n1 <<< "y" 2>&1 && echo "=== Done ===" && cat /proc/mdstat'
mdadm: stopped /dev/md0
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md0 started.
=== Done ===
Personalities : [raid0] [raid1] [raid4] [raid5] [raid6] [raid10]
md0 : active raid5 nvme5n1[4](S) nvme4n1[2] nvme3n1[1] nvme2n1[0]
31429632 blocks super 1.2 level 5, 512k chunk, algorithm 2 [4/3] [UUU_]
unused devices: <none>
lab@vm70:~$ cat /proc/mdstat
Personalities : [raid0] [raid1] [raid4] [raid5] [raid6] [raid10]
md0 : active raid5 nvme5n1[4] nvme4n1[2] nvme3n1[1] nvme2n1[0]
31429632 blocks super 1.2 level 5, 512k chunk, algorithm 2 [4/4] [UUUU]
unused devices: <none>
lab@vm70:~$ echo "=== 4K x 100 chunks ===" && echo "lab123" | sudo ~/p2pmem-test/p2pmem-test /dev/nvme1n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 4096 -c 100 2>&1 && echo "=== 1MB x 10 chunks ===" && sudo ~/p2pmem-test/p2pmem-test /dev/nvme1n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 1048576 -c 10 2>&1 && echo "=== 4K x 100 multi-thread ===" && sudo ~/p2pmem-test/p2pmem-test /dev/nvme1n1 /dev/md0 /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -s 4096 -c 100 -t 4 2>&1
=== 4K x 100 chunks ===
Running p2pmem-test: reading /dev/nvme1n1 (10.74GB): writing /dev/md0 (32.18GB): p2pmem buffer /sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 4096 : number of chunks = 100: total = 409.6kB : thread(s) = 1 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7fc717569000 (p2pmem): mmap = 4.096kB
PAGE_SIZE = 4096B
pwrite: Remote I/O error
* NVMe Device Inventory (nvme list -v) - Before TCP Path
========================================================
Subsystem Subsystem-NQN Controllers
---------------- ---------------------------------------------------------------- ----------------
nvme-subsys0 nqn.2019-08.org.qemu:nqn.2019-08.org.qemu:shared-ns nvme0, nvme1
nvme-subsys2 nqn.2019-08.org.qemu:nvme3 nvme2
nvme-subsys3 nqn.2019-08.org.qemu:nvme4 nvme3
nvme-subsys4 nqn.2019-08.org.qemu:nvme5 nvme4
nvme-subsys5 nqn.2019-08.org.qemu:nvme6 nvme5
Device SN MN FR TxPort Address Subsystem Namespaces
-------- ---------- --------------- -------- ------ -------------- ------------ ----------
nvme0 shared1 QEMU NVMe Ctrl 1.0 pcie 0000:0a:00.0 nvme-subsys0 nvme0n1
nvme1 shared1 QEMU NVMe Ctrl 1.0 pcie 0000:0b:00.0 nvme-subsys0 nvme0n1
nvme2 nvme3 QEMU NVMe Ctrl 1.0 pcie 0000:0c:00.0 nvme-subsys2 nvme2n1
nvme3 nvme4 QEMU NVMe Ctrl 1.0 pcie 0000:0d:00.0 nvme-subsys3 nvme3n1
nvme4 nvme5 QEMU NVMe Ctrl 1.0 pcie 0000:0e:00.0 nvme-subsys4 nvme4n1
nvme5 nvme6 QEMU NVMe Ctrl 1.0 pcie 0000:11:00.0 nvme-subsys5 nvme5n1
Device NSID Usage Format Controllers
------------ ----- ----------------------- --------------- ----------------
/dev/nvme0n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme0, nvme1
/dev/nvme2n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme2
/dev/nvme3n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme3
/dev/nvme4n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme4
/dev/nvme5n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme5
--------------------------------------------------------------------------------
1.2 Shared Namespace Configuration (nvme-subsys0)
--------------------------------------------------------------------------------
nvme-subsys0 - NQN=nqn.2019-08.org.qemu:nqn.2019-08.org.qemu:shared-ns
hostnqn=nqn.2014-08.org.nvmexpress:uuid:148a9e69-3f22-420f-afea-bfd5d4b77f36
iopolicy=numa
\
+- nvme0 pcie 0000:0a:00.0 live optimized
+- nvme1 pcie 0000:0b:00.0 live optimized
Two PCIe paths (nvme0, nvme1) share a single 10.74 GB namespace (nvme0n1).
Both controllers have CMB (Controller Memory Buffer) enabled for P2PDMA.
--------------------------------------------------------------------------------
1.3 PCI P2PDMA / CMB Configuration
--------------------------------------------------------------------------------
CMB-enabled NVMe controllers:
0000:0a:00.0 (nvme0) - CMB 64 MB -> /sys/bus/pci/devices/0000:0a:00.0/p2pmem/
0000:0b:00.0 (nvme1) - CMB 64 MB -> /sys/bus/pci/devices/0000:0b:00.0/p2pmem/
0000:0c:00.0 (nvme2) - CMB 64 MB -> /sys/bus/pci/devices/0000:0c:00.0/p2pmem/
dmesg (boot):
nvme 0000:0a:00.0: added peer-to-peer DMA memory 0x1808000000-0x180bffffff
nvme 0000:0b:00.0: added peer-to-peer DMA memory 0x1804000000-0x1807ffffff
nvme 0000:0c:00.0: added peer-to-peer DMA memory 0x1800000000-0x1803ffffff
================================================================================
2. Test 1: NVMe Multipath P2PDMA with Mixed Transport Paths
================================================================================
Objective:
Verify that BLK_FEAT_PCI_P2PDMA is correctly set on the multipath head
device when all paths support P2PDMA (PCIe-only), and correctly cleared
when a non-P2P-capable path (NVMe-oF TCP) is added.
Test tool:
p2pmem-test v1.1 (https://github.com/sbates130272/p2pmem-test)
P2PMEM buffer:
/sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate (nvme0 CMB, 64 MB)
Target device:
/dev/nvme0n1 (multipath head, nvme-subsys0)
--------------------------------------------------------------------------------
2.1 Test 1a: P2PDMA with PCIe-only Multipath Paths (Expect PASS)
--------------------------------------------------------------------------------
Paths on nvme-subsys0:
+- nvme0 pcie 0000:0a:00.0 live optimized
+- nvme1 pcie 0000:0b:00.0 live optimized
Both paths are PCIe with CMB -> all paths support P2PDMA.
The patch sets BLK_FEAT_PCI_P2PDMA in nvme_mpath_alloc_disk() at head
device creation time because the first controller supports P2PDMA.
Command:
p2pmem-test /dev/nvme0n1 /dev/nvme0n1 \
/sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -c 1 -s 4k --check
Output:
Running p2pmem-test: reading /dev/nvme0n1 (10.74GB): writing /dev/nvme0n1
(10.74GB): p2pmem buffer
/sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 4096 : number of chunks = 1: total = 4.096kB :
thread(s) = 1 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7f2c38c87000 (p2pmem): mmap = 4.096kB
PAGE_SIZE = 4096B
checking data with seed = 1775572887
MATCH on data check, 0x319c9110 = 0x319c9110.
Transfer:
4.10kB in 1.1 ms 3.89MB/s
Exit code: 0
Result: PASS
P2PDMA transfer succeeded on multipath head device with data verification.
--------------------------------------------------------------------------------
2.2 Test 1b: Add NVMe-oF TCP Path, Then Test P2PDMA (Expect FAIL)
--------------------------------------------------------------------------------
Setup - NVMe-oF TCP target (nvmet) on loopback:
Subsystem NQN: nqn.2019-08.org.qemu:nqn.2019-08.org.qemu:shared-ns
Namespace 1: backed by /dev/nvme0n1 (the multipath head itself)
Device UUID: 00000000-0000-0000-0000-000000000000 (matches QEMU quirk)
Transport: TCP, 127.0.0.1:4420
CNTLID min: 10 (avoids conflict with PCIe controller IDs)
Connect command:
nvme connect -t tcp -n <NQN> -a 127.0.0.1 -s 4420
dmesg:
nvmet: adding nsid 1 to subsystem nqn.2019-08.org.qemu:...shared-ns
nvmet_tcp: enabling port 1 (127.0.0.1:4420)
nvmet: Created nvm controller 10 for subsystem nqn.2019-08.org.qemu:...
nvme nvme6: creating 2 I/O queues.
nvme nvme6: new ctrl: NQN "nqn.2019-08.org.qemu:...shared-ns",
addr 127.0.0.1:4420
Paths on nvme-subsys0 (after TCP connection):
+- nvme0 pcie 0000:0a:00.0 live optimized
+- nvme1 pcie 0000:0b:00.0 live optimized
+- nvme6 tcp 127.0.0.1:4420 live optimized
NVMe device inventory (nvme list -v) confirming TCP path in shared namespace:
Subsystem Subsystem-NQN Controllers
---------------- --------------------------------------------------------- ----------------
nvme-subsys0 nqn.2019-08.org.qemu:...shared-ns nvme0, nvme1, nvme6
Device SN MN FR TxPort Address Subsystem Namespaces
-------- -------- --------------- --------- ------ --------------- ------------- ----------
nvme0 shared1 QEMU NVMe Ctrl 7.0.0-rc pcie 0000:0a:00.0 nvme-subsys0 nvme0n1
nvme1 shared1 QEMU NVMe Ctrl 7.0.0-rc pcie 0000:0b:00.0 nvme-subsys0 nvme0n1
nvme6 shared1 QEMU NVMe Ctrl 7.0.0-rc tcp 127.0.0.1:4420 nvme-subsys0 nvme0n1
Device NSID Usage Format Controllers
------------ ----- ---------------------- --------------- -------------------
/dev/nvme0n1 0x1 10.74 GB / 10.74 GB 512 B + 0 B nvme0, nvme1, nvme6
nvme6 (TCP) has joined the shared namespace alongside nvme0 and nvme1 (PCIe).
TCP does not support PCI P2PDMA, so the patch clears BLK_FEAT_PCI_P2PDMA
on the head disk in nvme_mpath_add_disk().
Command:
p2pmem-test /dev/nvme0n1 /dev/nvme0n1 \
/sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate -c 1 -s 4k --check
Output:
pread: Remote I/O error
Running p2pmem-test: reading /dev/nvme0n1 (10.74GB): writing /dev/nvme0n1
(10.74GB): p2pmem buffer
/sys/bus/pci/devices/0000:0a:00.0/p2pmem/allocate.
chunk size = 4096 : number of chunks = 1: total = 4.096kB :
thread(s) = 1 : overlap = OFF.
skip-read = OFF : skip-write = OFF : duration = INF sec.
buffer = 0x7f1ad3065000 (p2pmem): mmap = 4.096kB
PAGE_SIZE = 4096B
checking data with seed = 1775573026
Exit code: 1
Result: PASS (expected failure)
P2PDMA transfer correctly rejected with "Remote I/O error".
BLK_FEAT_PCI_P2PDMA was cleared from the multipath head because the
TCP path (nvme6) does not support PCI P2PDMA.
================================================================================
3. Test Summary
================================================================================
Test Description Expected Actual Result
------ --------------------------------------------- --------- ------- ------
1a P2PDMA on multipath head (PCIe-only paths) PASS PASS OK
1b P2PDMA on multipath head (PCIe + TCP paths) FAIL FAIL OK
Both tests confirm the patch correctly:
- Sets BLK_FEAT_PCI_P2PDMA at multipath head allocation when the first
controller supports P2PDMA.
- Clears BLK_FEAT_PCI_P2PDMA in nvme_mpath_add_disk() when a newly added
path does not support P2PDMA, preventing unsafe P2P DMA through a
non-P2P-capable transport.
* BLKTEST Testing :-
++ for t in loop tcp
++ echo '################NVMET_TRTYPES=loop############'
################NVMET_TRTYPES=loop############
++ NVME_IMG_SIZE=1G
++ NVME_NUM_ITER=1
++ NVMET_TRTYPES=loop
++ ./check nvme
nvme/002 (tr=loop) (create many subsystems and test discovery) [passed]
runtime 28.709s ... 36.107s
nvme/003 (tr=loop) (test if we're sending keep-alives to a discovery controller) [passed]
runtime 10.170s ... 10.227s
nvme/004 (tr=loop) (test nvme and nvmet UUID NS descriptors) [passed]
runtime 0.543s ... 0.650s
nvme/005 (tr=loop) (reset local loopback target) [passed]
runtime 0.900s ... 0.989s
nvme/006 (tr=loop bd=device) (create an NVMeOF target) [passed]
runtime 0.065s ... 0.093s
nvme/006 (tr=loop bd=file) (create an NVMeOF target) [passed]
runtime 0.056s ... 0.087s
nvme/008 (tr=loop bd=device) (create an NVMeOF host) [passed]
runtime 0.554s ... 0.642s
nvme/008 (tr=loop bd=file) (create an NVMeOF host) [passed]
runtime 0.533s ... 0.645s
nvme/010 (tr=loop bd=device) (run data verification fio job) [passed]
runtime 5.737s ... 9.706s
nvme/010 (tr=loop bd=file) (run data verification fio job) [passed]
runtime 29.743s ... 40.205s
nvme/012 (tr=loop bd=device) (run mkfs and data verification fio) [passed]
runtime 25.589s ... 49.060s
nvme/012 (tr=loop bd=file) (run mkfs and data verification fio) [passed]
runtime 28.039s ... 38.910s
nvme/014 (tr=loop bd=device) (flush a command from host) [passed]
runtime 6.340s ... 9.628s
nvme/014 (tr=loop bd=file) (flush a command from host) [passed]
runtime 6.237s ... 9.359s
nvme/016 (tr=loop) (create/delete many NVMeOF block device-backed ns and test discovery) [passed]
runtime 0.110s ... 0.138s
nvme/017 (tr=loop) (create/delete many file-ns and test discovery) [passed]
runtime 0.116s ... 0.144s
nvme/018 (tr=loop) (unit test NVMe-oF out of range access on a file backend) [passed]
runtime 0.544s ... 0.628s
nvme/019 (tr=loop bd=device) (test NVMe DSM Discard command) [passed]
runtime 0.544s ... 0.643s
nvme/019 (tr=loop bd=file) (test NVMe DSM Discard command) [passed]
runtime 0.529s ... 0.619s
nvme/021 (tr=loop bd=device) (test NVMe list command) [passed]
runtime 0.538s ... 0.645s
nvme/021 (tr=loop bd=file) (test NVMe list command) [passed]
runtime 0.523s ... 0.618s
nvme/022 (tr=loop bd=device) (test NVMe reset command) [passed]
runtime 0.916s ... 0.989s
nvme/022 (tr=loop bd=file) (test NVMe reset command) [passed]
runtime 0.917s ... 0.998s
nvme/023 (tr=loop bd=device) (test NVMe smart-log command) [passed]
runtime 0.537s ... 0.614s
nvme/023 (tr=loop bd=file) (test NVMe smart-log command) [passed]
runtime 0.525s ... 0.640s
nvme/025 (tr=loop bd=device) (test NVMe effects-log) [passed]
runtime 0.547s ... 0.639s
nvme/025 (tr=loop bd=file) (test NVMe effects-log) [passed]
runtime 0.531s ... 0.629s
nvme/026 (tr=loop bd=device) (test NVMe ns-descs) [passed]
runtime 0.538s ... 0.629s
nvme/026 (tr=loop bd=file) (test NVMe ns-descs) [passed]
runtime 0.532s ... 0.646s
nvme/027 (tr=loop bd=device) (test NVMe ns-rescan command) [passed]
runtime 0.567s ... 0.665s
nvme/027 (tr=loop bd=file) (test NVMe ns-rescan command) [passed]
runtime 0.557s ... 0.639s
nvme/028 (tr=loop bd=device) (test NVMe list-subsys) [passed]
runtime 0.524s ... 0.618s
nvme/028 (tr=loop bd=file) (test NVMe list-subsys) [passed]
runtime 0.532s ... 0.619s
nvme/029 (tr=loop) (test userspace IO via nvme-cli read/write interface) [passed]
runtime 0.723s ... 0.963s
nvme/030 (tr=loop) (ensure the discovery generation counter is updated appropriately) [passed]
runtime 0.391s ... 0.396s
nvme/031 (tr=loop) (test deletion of NVMeOF controllers immediately after setup) [passed]
runtime 5.120s ... 5.812s
nvme/038 (tr=loop) (test deletion of NVMeOF subsystem without enabling) [passed]
runtime 0.019s ... 0.034s
nvme/040 (tr=loop) (test nvme fabrics controller reset/disconnect operation during I/O) [passed]
runtime 6.977s ... 6.994s
nvme/041 (tr=loop) (Create authenticated connections) [passed]
runtime 0.602s ... 0.658s
nvme/042 (tr=loop) (Test dhchap key types for authenticated connections) [passed]
runtime 3.454s ... 3.831s
nvme/043 (tr=loop) (Test hash and DH group variations for authenticated connections) [passed]
runtime 4.373s ... 4.797s
nvme/044 (tr=loop) (Test bi-directional authentication) [passed]
runtime 1.144s ... 1.299s
nvme/045 (tr=loop) (Test re-authentication) [passed]
runtime 1.266s ... 1.591s
nvme/047 (tr=loop) (test different queue types for fabric transports) [not run]
nvme_trtype=loop is not supported in this test
nvme/048 (tr=loop) (Test queue count changes on reconnect) [not run]
nvme_trtype=loop is not supported in this test
nvme/051 (tr=loop) (test nvmet concurrent ns enable/disable) [passed]
runtime 1.319s ... 1.397s
nvme/052 (tr=loop) (Test file-ns creation/deletion under one subsystem) [passed]
runtime 5.657s ... 6.286s
nvme/054 (tr=loop) (Test the NVMe reservation feature) [passed]
runtime 0.597s ... 0.756s
nvme/055 (tr=loop) (Test nvme write to a loop target ns just after ns is disabled) [not run]
kernel option DEBUG_ATOMIC_SLEEP has not been enabled
nvme/056 (tr=loop) (enable zero copy offload and run rw traffic) [not run]
Remote target required but NVME_TARGET_CONTROL is not set
nvme_trtype=loop is not supported in this test
kernel option ULP_DDP has not been enabled
module nvme_tcp does not have parameter ddp_offload
KERNELSRC not set
Kernel sources do not have tools/net/ynl/cli.py
NVME_IFACE not set
nvme/057 (tr=loop) (test nvme fabrics controller ANA failover during I/O) [passed]
runtime 27.007s ... 27.193s
nvme/058 (tr=loop) (test rapid namespace remapping) [passed]
runtime 3.878s ... 4.693s
nvme/060 (tr=loop) (test nvme fabrics target reset) [not run]
nvme_trtype=loop is not supported in this test
nvme/061 (tr=loop) (test fabric target teardown and setup during I/O) [not run]
nvme_trtype=loop is not supported in this test
nvme/062 (tr=loop) (Create TLS-encrypted connections) [not run]
nvme_trtype=loop is not supported in this test
nvme/063 (tr=loop) (Create authenticated TCP connections with secure concatenation) [not run]
nvme_trtype=loop is not supported in this test
nvme/065 (tr=loop) (test unmap write zeroes sysfs interface with nvmet devices) [passed]
runtime 1.956s ... 2.316s
++ for t in loop tcp
++ echo '################NVMET_TRTYPES=tcp############'
################NVMET_TRTYPES=tcp############
++ NVME_IMG_SIZE=1G
++ NVME_NUM_ITER=1
++ NVMET_TRTYPES=tcp
++ ./check nvme
nvme/002 (tr=tcp) (create many subsystems and test discovery) [not run]
nvme_trtype=tcp is not supported in this test
nvme/003 (tr=tcp) (test if we're sending keep-alives to a discovery controller) [passed]
runtime 10.185s ... 10.235s
nvme/004 (tr=tcp) (test nvme and nvmet UUID NS descriptors) [passed]
runtime 0.264s ... 0.370s
nvme/005 (tr=tcp) (reset local loopback target) [passed]
runtime 0.329s ... 0.442s
nvme/006 (tr=tcp bd=device) (create an NVMeOF target) [passed]
runtime 0.076s ... 0.097s
nvme/006 (tr=tcp bd=file) (create an NVMeOF target) [passed]
runtime 0.067s ... 0.098s
nvme/008 (tr=tcp bd=device) (create an NVMeOF host) [passed]
runtime 0.253s ... 0.364s
nvme/008 (tr=tcp bd=file) (create an NVMeOF host) [passed]
runtime 0.247s ... 0.371s
nvme/010 (tr=tcp bd=device) (run data verification fio job) [passed]
runtime 38.475s ... 76.177s
nvme/010 (tr=tcp bd=file) (run data verification fio job) [passed]
runtime 68.995s ... 118.690s
nvme/012 (tr=tcp bd=device) (run mkfs and data verification fio) [passed]
runtime 43.183s ... 83.665s
nvme/012 (tr=tcp bd=file) (run mkfs and data verification fio) [passed]
runtime 70.449s ... 117.129s
nvme/014 (tr=tcp bd=device) (flush a command from host) [passed]
runtime 6.430s ... 10.019s
nvme/014 (tr=tcp bd=file) (flush a command from host) [passed]
runtime 6.445s ... 9.952s
nvme/016 (tr=tcp) (create/delete many NVMeOF block device-backed ns and test discovery) [not run]
nvme_trtype=tcp is not supported in this test
nvme/017 (tr=tcp) (create/delete many file-ns and test discovery) [not run]
nvme_trtype=tcp is not supported in this test
nvme/018 (tr=tcp) (unit test NVMe-oF out of range access on a file backend) [passed]
runtime 0.248s ... 0.373s
nvme/019 (tr=tcp bd=device) (test NVMe DSM Discard command) [passed]
runtime 0.257s ... 0.364s
nvme/019 (tr=tcp bd=file) (test NVMe DSM Discard command) [passed]
runtime 0.247s ... 0.354s
nvme/021 (tr=tcp bd=device) (test NVMe list command) [passed]
runtime 0.250s ... 0.374s
nvme/021 (tr=tcp bd=file) (test NVMe list command) [passed]
runtime 0.248s ... 0.356s
nvme/022 (tr=tcp bd=device) (test NVMe reset command) [passed]
runtime 0.338s ... 0.471s
nvme/022 (tr=tcp bd=file) (test NVMe reset command) [passed]
runtime 0.329s ... 0.475s
nvme/023 (tr=tcp bd=device) (test NVMe smart-log command) [passed]
runtime 0.250s ... 0.362s
nvme/023 (tr=tcp bd=file) (test NVMe smart-log command) [passed]
runtime 0.237s ... 0.348s
nvme/025 (tr=tcp bd=device) (test NVMe effects-log) [passed]
runtime 0.261s ... 0.374s
nvme/025 (tr=tcp bd=file) (test NVMe effects-log) [passed]
runtime 0.250s ... 0.375s
nvme/026 (tr=tcp bd=device) (test NVMe ns-descs) [passed]
runtime 0.252s ... 0.356s
nvme/026 (tr=tcp bd=file) (test NVMe ns-descs) [passed]
runtime 0.238s ... 0.348s
nvme/027 (tr=tcp bd=device) (test NVMe ns-rescan command) [passed]
runtime 0.276s ... 0.371s
nvme/027 (tr=tcp bd=file) (test NVMe ns-rescan command) [passed]
runtime 0.256s ... 0.384s
nvme/028 (tr=tcp bd=device) (test NVMe list-subsys) [passed]
runtime 0.255s ... 0.331s
nvme/028 (tr=tcp bd=file) (test NVMe list-subsys) [passed]
runtime 0.233s ... 0.338s
nvme/029 (tr=tcp) (test userspace IO via nvme-cli read/write interface) [passed]
runtime 0.456s ... 0.745s
nvme/030 (tr=tcp) (ensure the discovery generation counter is updated appropriately) [passed]
runtime 0.389s ... 0.410s
nvme/031 (tr=tcp) (test deletion of NVMeOF controllers immediately after setup) [passed]
runtime 2.221s ... 3.007s
nvme/038 (tr=tcp) (test deletion of NVMeOF subsystem without enabling) [passed]
runtime 0.025s ... 0.043s
nvme/040 (tr=tcp) (test nvme fabrics controller reset/disconnect operation during I/O) [passed]
runtime 6.380s ... 6.446s
nvme/041 (tr=tcp) (Create authenticated connections) [passed]
runtime 0.307s ... 0.408s
nvme/042 (tr=tcp) (Test dhchap key types for authenticated connections) [passed]
runtime 1.330s ... 1.791s
nvme/043 (tr=tcp) (Test hash and DH group variations for authenticated connections) [passed]
runtime 1.960s ... 2.407s
nvme/044 (tr=tcp) (Test bi-directional authentication) [passed]
runtime 0.545s ... 0.718s
nvme/045 (tr=tcp) (Test re-authentication) [passed]
runtime 0.967s ... 1.484s
nvme/047 (tr=tcp) (test different queue types for fabric transports) [passed]
runtime 1.125s ... 1.892s
nvme/048 (tr=tcp) (Test queue count changes on reconnect) [passed]
runtime 6.350s ... 4.522s
nvme/051 (tr=tcp) (test nvmet concurrent ns enable/disable) [passed]
runtime 1.468s ... 1.396s
nvme/052 (tr=tcp) (Test file-ns creation/deletion under one subsystem) [not run]
nvme_trtype=tcp is not supported in this test
nvme/054 (tr=tcp) (Test the NVMe reservation feature) [passed]
runtime 0.324s ... 0.494s
nvme/055 (tr=tcp) (Test nvme write to a loop target ns just after ns is disabled) [not run]
nvme_trtype=tcp is not supported in this test
kernel option DEBUG_ATOMIC_SLEEP has not been enabled
nvme/056 (tr=tcp) (enable zero copy offload and run rw traffic) [not run]
Remote target required but NVME_TARGET_CONTROL is not set
kernel option ULP_DDP has not been enabled
module nvme_tcp does not have parameter ddp_offload
KERNELSRC not set
Kernel sources do not have tools/net/ynl/cli.py
NVME_IFACE not set
nvme/057 (tr=tcp) (test nvme fabrics controller ANA failover during I/O) [passed]
runtime 25.800s ... 25.956s
nvme/058 (tr=tcp) (test rapid namespace remapping) [passed]
runtime 2.522s ... 2.731s
nvme/060 (tr=tcp) (test nvme fabrics target reset) [passed]
runtime 19.013s ... 19.427s
nvme/061 (tr=tcp) (test fabric target teardown and setup during I/O) [passed]
runtime 9.203s ... 8.568s
nvme/062 (tr=tcp) (Create TLS-encrypted connections) [failed]
runtime 1.092s ... 5.228s
--- tests/nvme/062.out 2026-01-28 12:04:48.888356244 -0800
+++ /mnt/sda/blktests/results/nodev_tr_tcp/nvme/062.out.bad 2026-04-08 00:14:12.257544184 -0700
@@ -2,9 +2,13 @@
Test unencrypted connection w/ tls not required
disconnected 1 controller(s)
Test encrypted connection w/ tls not required
-disconnected 1 controller(s)
+FAIL: nvme connect return error code
+WARNING: connection is not encrypted
+disconnected 0 controller(s)
...
(Run 'diff -u tests/nvme/062.out /mnt/sda/blktests/results/nodev_tr_tcp/nvme/062.out.bad' to see the entire diff)
nvme/063 (tr=tcp) (Create authenticated TCP connections with secure concatenation) [passed]
runtime 1.296s ... 1.951s
nvme/065 (tr=tcp) (test unmap write zeroes sysfs interface with nvmet devices) [passed]
runtime 1.365s ... 1.679s
++ ./manage-rdma-nvme.sh --cleanup
====== RDMA NVMe Cleanup ======
[INFO] Disconnecting NVMe RDMA controllers...
[INFO] No NVMe RDMA controllers to disconnect
[INFO] Removing RDMA links...
[INFO] No RDMA links to remove
[INFO] Unloading NVMe RDMA modules...
[INFO] NVMe RDMA modules unloaded successfully
[INFO] Unloading soft-RDMA modules...
[INFO] Soft-RDMA modules unloaded successfully
[INFO] Verifying cleanup...
[INFO] Verification passed
[INFO] RDMA cleanup completed successfully
====== RDMA Network Configuration Status ======
Loaded Modules:
None
RDMA Links:
None
Network Interfaces (RDMA-capable):
None
blktests Configuration:
Not configured (run --setup first)
NVMe RDMA Controllers:
None
=================================================
++ ./manage-rdma-nvme.sh --setup
====== RDMA NVMe Setup ======
RDMA Type: siw
Interface: auto-detect
[INFO] Checking prerequisites...
[INFO] Prerequisites check passed
[INFO] Loading RDMA module: siw
[INFO] Module siw loaded successfully
[INFO] Creating RDMA links...
[INFO] Creating RDMA link: ens5_siw
[INFO] Created RDMA link: ens5_siw -> ens5
++ ./manage-rdma-nvme.sh --status
====== RDMA Configuration Status ======
====== RDMA Network Configuration Status ======
Loaded Modules:
siw 217088
RDMA Links:
link ens5_siw/1 state ACTIVE physical_state LINK_UP netdev ens5
Network Interfaces (RDMA-capable):
Interface: ens5
IPv4: 192.168.0.46
IPv6: fe80::5054:98ff:fe76:5440%ens5
blktests Configuration:
Transport Address: 192.168.0.46:4420
Transport Type: rdma
Command: NVMET_TRTYPES=rdma ./check nvme/
NVMe RDMA Controllers:
None
=================================================
++ echo '################NVMET_TRTYPES=rdma############'
################NVMET_TRTYPES=rdma############
++ NVME_IMG_SIZE=1G
++ NVME_NUM_ITER=1
++ nvme_trtype=rdma
++ ./check nvme
nvme/002 (tr=rdma) (create many subsystems and test discovery) [not run]
nvme_trtype=rdma is not supported in this test
nvme/003 (tr=rdma) (test if we're sending keep-alives to a discovery controller) [passed]
runtime 10.252s ... 10.315s
nvme/004 (tr=rdma) (test nvme and nvmet UUID NS descriptors) [passed]
runtime 0.446s ... 0.689s
nvme/005 (tr=rdma) (reset local loopback target) [passed]
runtime 0.674s ... 0.980s
nvme/006 (tr=rdma bd=device) (create an NVMeOF target) [passed]
runtime 0.100s ... 0.137s
nvme/006 (tr=rdma bd=file) (create an NVMeOF target) [passed]
runtime 0.094s ... 0.139s
nvme/008 (tr=rdma bd=device) (create an NVMeOF host) [passed]
runtime 0.439s ... 0.700s
nvme/008 (tr=rdma bd=file) (create an NVMeOF host) [passed]
runtime 0.432s ... 0.653s
nvme/010 (tr=rdma bd=device) (run data verification fio job) [passed]
runtime 17.755s ... 34.757s
nvme/010 (tr=rdma bd=file) (run data verification fio job) [passed]
runtime 36.496s ... 66.017s
nvme/012 (tr=rdma bd=device) (run mkfs and data verification fio) [passed]
runtime 25.971s ... 41.224s
nvme/012 (tr=rdma bd=file) (run mkfs and data verification fio) [passed]
runtime 37.642s ... 74.188s
nvme/014 (tr=rdma bd=device) (flush a command from host) [passed]
runtime 6.299s ... 9.645s
nvme/014 (tr=rdma bd=file) (flush a command from host) [passed]
runtime 6.283s ... 8.407s
nvme/016 (tr=rdma) (create/delete many NVMeOF block device-backed ns and test discovery) [not run]
nvme_trtype=rdma is not supported in this test
nvme/017 (tr=rdma) (create/delete many file-ns and test discovery) [not run]
nvme_trtype=rdma is not supported in this test
nvme/018 (tr=rdma) (unit test NVMe-oF out of range access on a file backend) [passed]
runtime 0.415s ... 0.681s
nvme/019 (tr=rdma bd=device) (test NVMe DSM Discard command) [passed]
runtime 0.433s ... 0.676s
nvme/019 (tr=rdma bd=file) (test NVMe DSM Discard command) [passed]
runtime 0.419s ... 0.656s
nvme/021 (tr=rdma bd=device) (test NVMe list command) [passed]
runtime 0.422s ... 0.689s
nvme/021 (tr=rdma bd=file) (test NVMe list command) [passed]
runtime 0.424s ... 0.673s
nvme/022 (tr=rdma bd=device) (test NVMe reset command) [passed]
runtime 0.660s ... 1.004s
nvme/022 (tr=rdma bd=file) (test NVMe reset command) [passed]
runtime 0.664s ... 0.996s
nvme/023 (tr=rdma bd=device) (test NVMe smart-log command) [passed]
runtime 0.414s ... 0.682s
nvme/023 (tr=rdma bd=file) (test NVMe smart-log command) [passed]
runtime 0.438s ... 0.660s
nvme/025 (tr=rdma bd=device) (test NVMe effects-log) [passed]
runtime 0.432s ... 0.678s
nvme/025 (tr=rdma bd=file) (test NVMe effects-log) [passed]
runtime 0.423s ... 0.694s
nvme/026 (tr=rdma bd=device) (test NVMe ns-descs) [passed]
runtime 0.442s ... 0.680s
nvme/026 (tr=rdma bd=file) (test NVMe ns-descs) [passed]
runtime 0.429s ... 0.665s
nvme/027 (tr=rdma bd=device) (test NVMe ns-rescan command) [passed]
runtime 0.439s ... 0.703s
nvme/027 (tr=rdma bd=file) (test NVMe ns-rescan command) [passed]
runtime 0.440s ... 0.711s
nvme/028 (tr=rdma bd=device) (test NVMe list-subsys) [passed]
runtime 0.420s ... 0.664s
nvme/028 (tr=rdma bd=file) (test NVMe list-subsys) [passed]
runtime 0.419s ... 0.659s
nvme/029 (tr=rdma) (test userspace IO via nvme-cli read/write interface) [passed]
runtime 0.655s ... 1.036s
nvme/030 (tr=rdma) (ensure the discovery generation counter is updated appropriately) [passed]
runtime 0.463s ... 0.509s
nvme/031 (tr=rdma) (test deletion of NVMeOF controllers immediately after setup) [passed]
runtime 3.843s ... 5.647s
nvme/038 (tr=rdma) (test deletion of NVMeOF subsystem without enabling) [passed]
runtime 0.047s ... 0.083s
nvme/040 (tr=rdma) (test nvme fabrics controller reset/disconnect operation during I/O) [passed]
runtime 6.615s ... 7.016s
nvme/041 (tr=rdma) (Create authenticated connections) [passed]
runtime 0.493s ... 0.731s
nvme/042 (tr=rdma) (Test dhchap key types for authenticated connections) [passed]
runtime 2.513s ... 3.721s
nvme/043 (tr=rdma) (Test hash and DH group variations for authenticated connections) [passed]
runtime 3.328s ... 4.570s
nvme/044 (tr=rdma) (Test bi-directional authentication) [passed]
runtime 0.895s ... 1.316s
nvme/045 (tr=rdma) (Test re-authentication) [passed]
runtime 1.362s ... 1.748s
nvme/047 (tr=rdma) (test different queue types for fabric transports) [passed]
runtime 1.668s ... 2.669s
nvme/048 (tr=rdma) (Test queue count changes on reconnect) [passed]
runtime 6.492s ... 6.834s
nvme/051 (tr=rdma) (test nvmet concurrent ns enable/disable) [passed]
runtime 1.348s ... 1.413s
nvme/052 (tr=rdma) (Test file-ns creation/deletion under one subsystem) [not run]
nvme_trtype=rdma is not supported in this test
nvme/054 (tr=rdma) (Test the NVMe reservation feature) [passed]
runtime 0.494s ... 0.795s
nvme/055 (tr=rdma) (Test nvme write to a loop target ns just after ns is disabled) [not run]
nvme_trtype=rdma is not supported in this test
kernel option DEBUG_ATOMIC_SLEEP has not been enabled
nvme/056 (tr=rdma) (enable zero copy offload and run rw traffic) [not run]
Remote target required but NVME_TARGET_CONTROL is not set
nvme_trtype=rdma is not supported in this test
kernel option ULP_DDP has not been enabled
module nvme_tcp does not have parameter ddp_offload
KERNELSRC not set
Kernel sources do not have tools/net/ynl/cli.py
NVME_IFACE not set
nvme/057 (tr=rdma) (test nvme fabrics controller ANA failover during I/O) [passed]
runtime 26.364s ... 26.931s
nvme/058 (tr=rdma) (test rapid namespace remapping) [passed]
runtime 3.229s ... 4.240s
nvme/060 (tr=rdma) (test nvme fabrics target reset) [passed]
runtime 20.898s ... 20.733s
nvme/061 (tr=rdma) (test fabric target teardown and setup during I/O) [passed]
runtime 14.810s ... 15.460s
nvme/062 (tr=rdma) (Create TLS-encrypted connections) [not run]
nvme_trtype=rdma is not supported in this test
nvme/063 (tr=rdma) (Create authenticated TCP connections with secure concatenation) [not run]
nvme_trtype=rdma is not supported in this test
nvme/065 (tr=rdma) (test unmap write zeroes sysfs interface with nvmet devices) [passed]
runtime 1.764s ... 2.293s
++ ./manage-rdma-nvme.sh --cleanup
====== RDMA NVMe Cleanup ======
[INFO] Disconnecting NVMe RDMA controllers...
[INFO] No NVMe RDMA controllers to disconnect
[INFO] Removing RDMA links...
[INFO] No RDMA links to remove
[INFO] Unloading NVMe RDMA modules...
[INFO] NVMe RDMA modules unloaded successfully
[INFO] Unloading soft-RDMA modules...
[INFO] Soft-RDMA modules unloaded successfully
[INFO] Verifying cleanup...
[INFO] Verification passed
[INFO] RDMA cleanup completed successfully
====== RDMA Network Configuration Status ======
Loaded Modules:
None
RDMA Links:
None
Network Interfaces (RDMA-capable):
None
blktests Configuration:
Not configured (run --setup first)
NVMe RDMA Controllers:
None
=================================================
blktests (master) #
--
2.39.5
^ permalink raw reply
* [PATCH V2 1/2] md: propagate BLK_FEAT_PCI_P2PDMA from member devices
From: Chaitanya Kulkarni @ 2026-04-08 7:25 UTC (permalink / raw)
To: song, yukuai, linan122, kbusch, axboe, hch, sagi
Cc: linux-raid, linux-nvme, kmodukuri, Chaitanya Kulkarni
In-Reply-To: <20260408072537.46540-1-kch@nvidia.com>
From: Kiran Kumar Modukuri <kmodukuri@nvidia.com>
MD RAID does not propagate BLK_FEAT_PCI_P2PDMA from member devices to
the RAID device, preventing peer-to-peer DMA through the RAID layer even
when all underlying devices support it.
Enable BLK_FEAT_PCI_P2PDMA in raid0, raid1 and raid10 personalities
during queue limits setup and clear it in mddev_stack_rdev_limits()
during array init and mddev_stack_new_rdev() during hot-add if any
member device lacks support. Parity RAID personalities (raid4/5/6) are
excluded because they need CPU access to data pages for parity
computation, which is incompatible with P2P mappings.
Tested with RAID0/1/10 arrays containing multiple NVMe devices with P2PDMA
support, confirming that peer-to-peer transfers work correctly through
the RAID layer.
Signed-off-by: Kiran Kumar Modukuri <kmodukuri@nvidia.com>
Signed-off-by: Chaitanya Kulkarni <kch@nvidia.com>
---
drivers/md/md.c | 4 ++++
drivers/md/raid0.c | 1 +
drivers/md/raid1.c | 1 +
drivers/md/raid10.c | 1 +
4 files changed, 7 insertions(+)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 521d9b34cd9e..48d7a3ca8c66 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -6176,6 +6176,8 @@ int mddev_stack_rdev_limits(struct mddev *mddev, struct queue_limits *lim,
if ((flags & MDDEV_STACK_INTEGRITY) &&
!queue_limits_stack_integrity_bdev(lim, rdev->bdev))
return -EINVAL;
+ if (!blk_queue_pci_p2pdma(rdev->bdev->bd_disk->queue))
+ lim->features &= ~BLK_FEAT_PCI_P2PDMA;
}
/*
@@ -6231,6 +6233,8 @@ int mddev_stack_new_rdev(struct mddev *mddev, struct md_rdev *rdev)
lim = queue_limits_start_update(mddev->gendisk->queue);
queue_limits_stack_bdev(&lim, rdev->bdev, rdev->data_offset,
mddev->gendisk->disk_name);
+ if (!blk_queue_pci_p2pdma(rdev->bdev->bd_disk->queue))
+ lim.features &= ~BLK_FEAT_PCI_P2PDMA;
if (!queue_limits_stack_integrity_bdev(&lim, rdev->bdev)) {
pr_err("%s: incompatible integrity profile for %pg\n",
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index ef0045db409f..1cdcafd31744 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -392,6 +392,7 @@ static int raid0_set_limits(struct mddev *mddev)
lim.io_opt = lim.io_min * mddev->raid_disks;
lim.chunk_sectors = mddev->chunk_sectors;
lim.features |= BLK_FEAT_ATOMIC_WRITES;
+ lim.features |= BLK_FEAT_PCI_P2PDMA;
err = mddev_stack_rdev_limits(mddev, &lim, MDDEV_STACK_INTEGRITY);
if (err)
return err;
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 16f671ab12c0..b25e661e9738 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -3192,6 +3192,7 @@ static int raid1_set_limits(struct mddev *mddev)
lim.max_hw_wzeroes_unmap_sectors = 0;
lim.logical_block_size = mddev->logical_block_size;
lim.features |= BLK_FEAT_ATOMIC_WRITES;
+ lim.features |= BLK_FEAT_PCI_P2PDMA;
err = mddev_stack_rdev_limits(mddev, &lim, MDDEV_STACK_INTEGRITY);
if (err)
return err;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 4901ebe45c87..07a5b734c8f3 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -3939,6 +3939,7 @@ static int raid10_set_queue_limits(struct mddev *mddev)
lim.chunk_sectors = mddev->chunk_sectors;
lim.io_opt = lim.io_min * raid10_nr_stripes(conf);
lim.features |= BLK_FEAT_ATOMIC_WRITES;
+ lim.features |= BLK_FEAT_PCI_P2PDMA;
err = mddev_stack_rdev_limits(mddev, &lim, MDDEV_STACK_INTEGRITY);
if (err)
return err;
--
2.39.5
^ permalink raw reply related
* [PATCH V2 2/2] nvme-multipath: enable PCI P2PDMA for multipath devices
From: Chaitanya Kulkarni @ 2026-04-08 7:25 UTC (permalink / raw)
To: song, yukuai, linan122, kbusch, axboe, hch, sagi
Cc: linux-raid, linux-nvme, kmodukuri, Chaitanya Kulkarni
In-Reply-To: <20260408072537.46540-1-kch@nvidia.com>
From: Kiran Kumar Modukuri <kmodukuri@nvidia.com>
NVMe multipath does not expose BLK_FEAT_PCI_P2PDMA on the head disk
even when the underlying controller supports it.
Set BLK_FEAT_PCI_P2PDMA in nvme_mpath_alloc_disk() when the controller
advertises P2PDMA support via ctrl->ops->supports_pci_p2pdma.
Since multipath can match paths across different transports (e.g. PCIe
and FC), not all paths are guaranteed to support P2PDMA. Clear
BLK_FEAT_PCI_P2PDMA from the head disk in nvme_mpath_add_disk() if the
newly added path does not support it, ensuring the feature is only
advertised when every member supports it.
Signed-off-by: Kiran Kumar Modukuri <kmodukuri@nvidia.com>
Signed-off-by: Chaitanya Kulkarni <kch@nvidia.com>
---
drivers/nvme/host/multipath.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
index ba00f0b72b85..48d920ce803f 100644
--- a/drivers/nvme/host/multipath.c
+++ b/drivers/nvme/host/multipath.c
@@ -737,6 +737,9 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
BLK_FEAT_POLL | BLK_FEAT_ATOMIC_WRITES;
if (head->ids.csi == NVME_CSI_ZNS)
lim.features |= BLK_FEAT_ZONED;
+ if (ctrl->ops && ctrl->ops->supports_pci_p2pdma &&
+ ctrl->ops->supports_pci_p2pdma(ctrl))
+ lim.features |= BLK_FEAT_PCI_P2PDMA;
head->disk = blk_alloc_disk(&lim, ctrl->numa_node);
if (IS_ERR(head->disk))
@@ -1248,6 +1251,21 @@ void nvme_mpath_remove_sysfs_link(struct nvme_ns *ns)
void nvme_mpath_add_disk(struct nvme_ns *ns, __le32 anagrpid)
{
+ struct nvme_ns_head *head = ns->head;
+
+ /*
+ * Clear BLK_FEAT_PCI_P2PDMA on the head disk if this path does not
+ * support it. Multipath may span different transports (e.g. PCIe and
+ * FC), so every member must support P2PDMA for it to be safe on the
+ * head disk.
+ */
+ if (head->disk && !blk_queue_pci_p2pdma(ns->queue)) {
+ struct queue_limits lim =
+ queue_limits_start_update(head->disk->queue);
+ lim.features &= ~BLK_FEAT_PCI_P2PDMA;
+ queue_limits_commit_update(head->disk->queue, &lim);
+ }
+
if (nvme_ctrl_use_ana(ns->ctrl)) {
struct nvme_ana_group_desc desc = {
.grpid = anagrpid,
--
2.39.5
^ permalink raw reply related
* Re: [PATCH v2] md/raid5: Fix UAF on IO across the reshape position
From: Xiao Ni @ 2026-04-08 11:22 UTC (permalink / raw)
To: Benjamin Marzinski
Cc: Yu Kuai, Song Liu, Li Nan, linux-raid, dm-devel, Nigel Croxon
In-Reply-To: <20260408043548.1695157-1-bmarzins@redhat.com>
On Wed, Apr 8, 2026 at 12:35 PM Benjamin Marzinski <bmarzins@redhat.com> wrote:
>
> If make_stripe_request() returns STRIPE_WAIT_RESHAPE,
> raid5_make_request() will free the cloned bio. But raid5_make_request()
> can call make_stripe_request() multiple times, writing to the various
> stripes. If that bio got added to the toread or towrite lists of a
> stripe disk in an earlier call to make_stripe_request(), then it's not
> safe to just free the bio if a later part of it is found to cross the
> reshape position. Doing so can lead to a UAF error, when bio_endio()
> is called on the bio for the earlier stripes.
>
> Instead, raid5_make_request() needs to wait until all parts of the bio
> have called bio_endio(). To do this, bios that cross the reshape
> position while the reshape can't make progress are flagged as needing to
> wait for all parts to complete. When raid5_make_request() has a bio that
> failed make_stripe_request() with STRIPE_WAIT_RESHAPE, it sets
> bi->bi_private to a completion struct and waits for completion after
> ending the bio. When the bio_endio() is called for the last time on a
> clone bio with bi->bi_private set, it wakes up the waiter. This
> guarantees that raid5_make_request() doesn't return until the cloned bio
> needing a retry for io across the reshape boundary is safely cleaned up.
>
> There is a simple reproducer available at [1]. Compile the kernel with
> KASAN for more useful reporting when the error is triggered (this is not
> necessary to see the bug).
>
> [1] https://gist.github.com/bmarzins/e48598824305cf2171289e47d7241fa5
>
> Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
> ---
>
> Changes from v1:
> - Removed mddev->pending_retry_bios, mddev->retry_bios_wait, and
> md_io_clone->must_retry. Instead, use a completion struct
> pointed to by bi->bi_private, as suggested by Xiao Ni and Yu Kuai.
>
> drivers/md/md.c | 31 ++++++++-----------------------
> drivers/md/md.h | 1 -
> drivers/md/raid5.c | 7 ++++++-
> 3 files changed, 14 insertions(+), 25 deletions(-)
>
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index 3ce6f9e9d38e..4318d875a5f6 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -9215,9 +9215,11 @@ static void md_bitmap_end(struct mddev *mddev, struct md_io_clone *md_io_clone)
>
> static void md_end_clone_io(struct bio *bio)
> {
> - struct md_io_clone *md_io_clone = bio->bi_private;
> + struct md_io_clone *md_io_clone = container_of(bio, struct md_io_clone,
> + bio_clone);
> struct bio *orig_bio = md_io_clone->orig_bio;
> struct mddev *mddev = md_io_clone->mddev;
> + struct completion *reshape_completion = bio->bi_private;
>
> if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
> md_bitmap_end(mddev, md_io_clone);
> @@ -9229,7 +9231,10 @@ static void md_end_clone_io(struct bio *bio)
> bio_end_io_acct(orig_bio, md_io_clone->start_time);
>
> bio_put(bio);
> - bio_endio(orig_bio);
> + if (unlikely(reshape_completion))
> + complete(reshape_completion);
> + else
> + bio_endio(orig_bio);
> percpu_ref_put(&mddev->active_io);
> }
>
> @@ -9254,7 +9259,7 @@ static void md_clone_bio(struct mddev *mddev, struct bio **bio)
> }
>
> clone->bi_end_io = md_end_clone_io;
> - clone->bi_private = md_io_clone;
> + clone->bi_private = NULL;
> *bio = clone;
> }
>
> @@ -9265,26 +9270,6 @@ void md_account_bio(struct mddev *mddev, struct bio **bio)
> }
> EXPORT_SYMBOL_GPL(md_account_bio);
>
> -void md_free_cloned_bio(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;
> -
> - if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
> - md_bitmap_end(mddev, md_io_clone);
> -
> - if (bio->bi_status && !orig_bio->bi_status)
> - orig_bio->bi_status = bio->bi_status;
> -
> - if (md_io_clone->start_time)
> - bio_end_io_acct(orig_bio, md_io_clone->start_time);
> -
> - bio_put(bio);
> - percpu_ref_put(&mddev->active_io);
> -}
> -EXPORT_SYMBOL_GPL(md_free_cloned_bio);
> -
> /* md_allow_write(mddev)
> * Calling this ensures that the array is marked 'active' so that writes
> * may proceed without blocking. It is important to call this before
> diff --git a/drivers/md/md.h b/drivers/md/md.h
> index ac84289664cd..5d57fee22901 100644
> --- a/drivers/md/md.h
> +++ b/drivers/md/md.h
> @@ -917,7 +917,6 @@ extern void md_finish_reshape(struct mddev *mddev);
> void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
> struct bio *bio, sector_t start, sector_t size);
> void md_account_bio(struct mddev *mddev, struct bio **bio);
> -void md_free_cloned_bio(struct bio *bio);
>
> extern bool __must_check md_flush_request(struct mddev *mddev, struct bio *bio);
> void md_write_metadata(struct mddev *mddev, struct md_rdev *rdev,
> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> index a8e8d431071b..dc0c680ca199 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -6217,7 +6217,12 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
>
> mempool_free(ctx, conf->ctx_pool);
> if (res == STRIPE_WAIT_RESHAPE) {
> - md_free_cloned_bio(bi);
> + DECLARE_COMPLETION_ONSTACK(done);
> + WRITE_ONCE(bi->bi_private, &done);
> +
> + bio_endio(bi);
Hi Ben
You gave an explanation why it doesn't need WRITE_ONCE. As you said,
bio_endio uses atomic_dec_and_test, so it guarantees a full memory
barrier. Why do you use WRITE_ONCE here?
Regards
Xiao
> +
> + wait_for_completion(&done);
> return false;
> }
>
> --
> 2.53.0
>
^ permalink raw reply
* Re: [GIT PULL] md-7.1-20260407
From: Jens Axboe @ 2026-04-08 12:55 UTC (permalink / raw)
To: Yu Kuai, linux-block, linux-raid
Cc: linux-kernel, song, linan122, abd.masalkhi, chiamingc, gourry,
moonafterrain, xni
In-Reply-To: <20260408050517.2109269-1-yukuai@fnnas.com>
On 4/7/26 11:05 PM, Yu Kuai wrote:
> Hi Jens,
>
> Please consider pulling the following changes into your for-7.1/block branch.
>
> This pull request contains:
>
> Bug Fixes:
> - avoid a sysfs deadlock when clearing array state (Yu Kuai)
> - validate raid5 journal payloads before reading metadata (Junrui Luo)
> - fall back to the correct bitmap operations after version mismatches (Yu Kuai)
> - serialize overlapping writes on writemostly raid1 disks (Xiao Ni)
> - wake raid456 reshape waiters before suspend (Yu Kuai)
> - prevent retry_aligned_read() from triggering soft lockups (Chia-Ming Chang)
>
> Improvements:
> - switch raid0 strip zone and devlist allocations to kvmalloc helpers (Gregory Price)
> - track clean unwritten stripes for proactive RAID5 parity building (Yu Kuai)
> - speed up initial llbitmap sync with write_zeroes_unmap support (Yu Kuai)
>
> Cleanups:
> - remove the unused static md workqueue definition (Abd-Alrhman Masalkhi)
Pulled, thanks.
--
Jens Axboe
^ permalink raw reply
* Re: [PATCH v2] md/raid5: Fix UAF on IO across the reshape position
From: Benjamin Marzinski @ 2026-04-08 19:57 UTC (permalink / raw)
To: Xiao Ni; +Cc: Yu Kuai, Song Liu, Li Nan, linux-raid, dm-devel, Nigel Croxon
In-Reply-To: <CALTww28H_Qs_Oh0WkDFvVynvbyUhgUK9u0cuU7bpoF+jOg1mYA@mail.gmail.com>
On Wed, Apr 08, 2026 at 07:22:38PM +0800, Xiao Ni wrote:
> On Wed, Apr 8, 2026 at 12:35 PM Benjamin Marzinski <bmarzins@redhat.com> wrote:
> >
> > If make_stripe_request() returns STRIPE_WAIT_RESHAPE,
> > raid5_make_request() will free the cloned bio. But raid5_make_request()
> > can call make_stripe_request() multiple times, writing to the various
> > stripes. If that bio got added to the toread or towrite lists of a
> > stripe disk in an earlier call to make_stripe_request(), then it's not
> > safe to just free the bio if a later part of it is found to cross the
> > reshape position. Doing so can lead to a UAF error, when bio_endio()
> > is called on the bio for the earlier stripes.
> >
> > Instead, raid5_make_request() needs to wait until all parts of the bio
> > have called bio_endio(). To do this, bios that cross the reshape
> > position while the reshape can't make progress are flagged as needing to
> > wait for all parts to complete. When raid5_make_request() has a bio that
> > failed make_stripe_request() with STRIPE_WAIT_RESHAPE, it sets
> > bi->bi_private to a completion struct and waits for completion after
> > ending the bio. When the bio_endio() is called for the last time on a
> > clone bio with bi->bi_private set, it wakes up the waiter. This
> > guarantees that raid5_make_request() doesn't return until the cloned bio
> > needing a retry for io across the reshape boundary is safely cleaned up.
> >
> > There is a simple reproducer available at [1]. Compile the kernel with
> > KASAN for more useful reporting when the error is triggered (this is not
> > necessary to see the bug).
> >
> > [1] https://gist.github.com/bmarzins/e48598824305cf2171289e47d7241fa5
> >
> > Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
> > ---
> >
> > Changes from v1:
> > - Removed mddev->pending_retry_bios, mddev->retry_bios_wait, and
> > md_io_clone->must_retry. Instead, use a completion struct
> > pointed to by bi->bi_private, as suggested by Xiao Ni and Yu Kuai.
> >
> > drivers/md/md.c | 31 ++++++++-----------------------
> > drivers/md/md.h | 1 -
> > drivers/md/raid5.c | 7 ++++++-
> > 3 files changed, 14 insertions(+), 25 deletions(-)
> >
> > diff --git a/drivers/md/md.c b/drivers/md/md.c
> > index 3ce6f9e9d38e..4318d875a5f6 100644
> > --- a/drivers/md/md.c
> > +++ b/drivers/md/md.c
> > @@ -9215,9 +9215,11 @@ static void md_bitmap_end(struct mddev *mddev, struct md_io_clone *md_io_clone)
> >
> > static void md_end_clone_io(struct bio *bio)
> > {
> > - struct md_io_clone *md_io_clone = bio->bi_private;
> > + struct md_io_clone *md_io_clone = container_of(bio, struct md_io_clone,
> > + bio_clone);
> > struct bio *orig_bio = md_io_clone->orig_bio;
> > struct mddev *mddev = md_io_clone->mddev;
> > + struct completion *reshape_completion = bio->bi_private;
> >
> > if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
> > md_bitmap_end(mddev, md_io_clone);
> > @@ -9229,7 +9231,10 @@ static void md_end_clone_io(struct bio *bio)
> > bio_end_io_acct(orig_bio, md_io_clone->start_time);
> >
> > bio_put(bio);
> > - bio_endio(orig_bio);
> > + if (unlikely(reshape_completion))
> > + complete(reshape_completion);
> > + else
> > + bio_endio(orig_bio);
> > percpu_ref_put(&mddev->active_io);
> > }
> >
> > @@ -9254,7 +9259,7 @@ static void md_clone_bio(struct mddev *mddev, struct bio **bio)
> > }
> >
> > clone->bi_end_io = md_end_clone_io;
> > - clone->bi_private = md_io_clone;
> > + clone->bi_private = NULL;
> > *bio = clone;
> > }
> >
> > @@ -9265,26 +9270,6 @@ void md_account_bio(struct mddev *mddev, struct bio **bio)
> > }
> > EXPORT_SYMBOL_GPL(md_account_bio);
> >
> > -void md_free_cloned_bio(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;
> > -
> > - if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
> > - md_bitmap_end(mddev, md_io_clone);
> > -
> > - if (bio->bi_status && !orig_bio->bi_status)
> > - orig_bio->bi_status = bio->bi_status;
> > -
> > - if (md_io_clone->start_time)
> > - bio_end_io_acct(orig_bio, md_io_clone->start_time);
> > -
> > - bio_put(bio);
> > - percpu_ref_put(&mddev->active_io);
> > -}
> > -EXPORT_SYMBOL_GPL(md_free_cloned_bio);
> > -
> > /* md_allow_write(mddev)
> > * Calling this ensures that the array is marked 'active' so that writes
> > * may proceed without blocking. It is important to call this before
> > diff --git a/drivers/md/md.h b/drivers/md/md.h
> > index ac84289664cd..5d57fee22901 100644
> > --- a/drivers/md/md.h
> > +++ b/drivers/md/md.h
> > @@ -917,7 +917,6 @@ extern void md_finish_reshape(struct mddev *mddev);
> > void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
> > struct bio *bio, sector_t start, sector_t size);
> > void md_account_bio(struct mddev *mddev, struct bio **bio);
> > -void md_free_cloned_bio(struct bio *bio);
> >
> > extern bool __must_check md_flush_request(struct mddev *mddev, struct bio *bio);
> > void md_write_metadata(struct mddev *mddev, struct md_rdev *rdev,
> > diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> > index a8e8d431071b..dc0c680ca199 100644
> > --- a/drivers/md/raid5.c
> > +++ b/drivers/md/raid5.c
> > @@ -6217,7 +6217,12 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
> >
> > mempool_free(ctx, conf->ctx_pool);
> > if (res == STRIPE_WAIT_RESHAPE) {
> > - md_free_cloned_bio(bi);
> > + DECLARE_COMPLETION_ONSTACK(done);
> > + WRITE_ONCE(bi->bi_private, &done);
> > +
> > + bio_endio(bi);
>
> Hi Ben
>
> You gave an explanation why it doesn't need WRITE_ONCE. As you said,
> bio_endio uses atomic_dec_and_test, so it guarantees a full memory
> barrier. Why do you use WRITE_ONCE here?
You're correct. I don't believe it's necessary. The compiler has to
update bi->bi_private before calling bio_endio(bi), which can free bi,
and either the bio was never chained, and bi->bi_private will be read by
the same process that set it, or it was chained, and the
atomic_dec_and_test() in bio_remaining_done() will guarantee a memory
barrier.
I just patterned my updated fix off your idea, and left the WRITE_ONCE
there because it doesn't really hurt anything, since this is already the
slow (and unlikely) path. I can pull it out if you'd like.
I actually have another question about this code. My patch doesn't mess
with the code at the end of make_stripe_request() to return
STRIPE_WAIT_RESHAPE, but I'm not sure that it's right. That code
includes:
bi->bi_status = BLK_STS_RESOURCE;
This will update the orig_bio's bi_status in md_end_clone_io():
if (bio->bi_status && !orig_bio->bi_status)
orig_bio->bi_status = bio->bi_status;
For dm-raid, that orig_bio is itself a clone, and will eventually
get ended with DM_MAPIO_REQUEUE, which will requeue the actual
original bio (assuming that this happens when the device is
in a noflush suspend).
But for md, md_handle_request() can just loop and retry it. If the
mddev->pers->make_request() call succeeds on retry, the orig_bio will
still have the BLK_STS_RESOURCE status that got set when the earlier
call to make_stripe_request() returned STRIPE_WAIT_RESHAPE.
Perhaps make_stripe_request() shouldn't set bi->bi_status if
it's going to return STRIPE_WAIT_RESHAPE. The only thing I can see that
it does is set orig_bio->bi_status, which I don't think we want it to
do. Am I missing something here?
-Ben
>
> Regards
> Xiao
>
>
> > +
> > + wait_for_completion(&done);
> > return false;
> > }
> >
> > --
> > 2.53.0
> >
^ permalink raw reply
* Re: [PATCH v2] md/raid5: Fix UAF on IO across the reshape position
From: Xiao Ni @ 2026-04-09 2:31 UTC (permalink / raw)
To: Benjamin Marzinski
Cc: Yu Kuai, Song Liu, Li Nan, linux-raid, dm-devel, Nigel Croxon
In-Reply-To: <adazQdvXjE1RlMid@redhat.com>
On Thu, Apr 9, 2026 at 3:58 AM Benjamin Marzinski <bmarzins@redhat.com> wrote:
>
> On Wed, Apr 08, 2026 at 07:22:38PM +0800, Xiao Ni wrote:
> > On Wed, Apr 8, 2026 at 12:35 PM Benjamin Marzinski <bmarzins@redhat.com> wrote:
> > >
> > > If make_stripe_request() returns STRIPE_WAIT_RESHAPE,
> > > raid5_make_request() will free the cloned bio. But raid5_make_request()
> > > can call make_stripe_request() multiple times, writing to the various
> > > stripes. If that bio got added to the toread or towrite lists of a
> > > stripe disk in an earlier call to make_stripe_request(), then it's not
> > > safe to just free the bio if a later part of it is found to cross the
> > > reshape position. Doing so can lead to a UAF error, when bio_endio()
> > > is called on the bio for the earlier stripes.
> > >
> > > Instead, raid5_make_request() needs to wait until all parts of the bio
> > > have called bio_endio(). To do this, bios that cross the reshape
> > > position while the reshape can't make progress are flagged as needing to
> > > wait for all parts to complete. When raid5_make_request() has a bio that
> > > failed make_stripe_request() with STRIPE_WAIT_RESHAPE, it sets
> > > bi->bi_private to a completion struct and waits for completion after
> > > ending the bio. When the bio_endio() is called for the last time on a
> > > clone bio with bi->bi_private set, it wakes up the waiter. This
> > > guarantees that raid5_make_request() doesn't return until the cloned bio
> > > needing a retry for io across the reshape boundary is safely cleaned up.
> > >
> > > There is a simple reproducer available at [1]. Compile the kernel with
> > > KASAN for more useful reporting when the error is triggered (this is not
> > > necessary to see the bug).
> > >
> > > [1] https://gist.github.com/bmarzins/e48598824305cf2171289e47d7241fa5
> > >
> > > Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
> > > ---
> > >
> > > Changes from v1:
> > > - Removed mddev->pending_retry_bios, mddev->retry_bios_wait, and
> > > md_io_clone->must_retry. Instead, use a completion struct
> > > pointed to by bi->bi_private, as suggested by Xiao Ni and Yu Kuai.
> > >
> > > drivers/md/md.c | 31 ++++++++-----------------------
> > > drivers/md/md.h | 1 -
> > > drivers/md/raid5.c | 7 ++++++-
> > > 3 files changed, 14 insertions(+), 25 deletions(-)
> > >
> > > diff --git a/drivers/md/md.c b/drivers/md/md.c
> > > index 3ce6f9e9d38e..4318d875a5f6 100644
> > > --- a/drivers/md/md.c
> > > +++ b/drivers/md/md.c
> > > @@ -9215,9 +9215,11 @@ static void md_bitmap_end(struct mddev *mddev, struct md_io_clone *md_io_clone)
> > >
> > > static void md_end_clone_io(struct bio *bio)
> > > {
> > > - struct md_io_clone *md_io_clone = bio->bi_private;
> > > + struct md_io_clone *md_io_clone = container_of(bio, struct md_io_clone,
> > > + bio_clone);
> > > struct bio *orig_bio = md_io_clone->orig_bio;
> > > struct mddev *mddev = md_io_clone->mddev;
> > > + struct completion *reshape_completion = bio->bi_private;
> > >
> > > if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
> > > md_bitmap_end(mddev, md_io_clone);
> > > @@ -9229,7 +9231,10 @@ static void md_end_clone_io(struct bio *bio)
> > > bio_end_io_acct(orig_bio, md_io_clone->start_time);
> > >
> > > bio_put(bio);
> > > - bio_endio(orig_bio);
> > > + if (unlikely(reshape_completion))
> > > + complete(reshape_completion);
> > > + else
> > > + bio_endio(orig_bio);
> > > percpu_ref_put(&mddev->active_io);
> > > }
> > >
> > > @@ -9254,7 +9259,7 @@ static void md_clone_bio(struct mddev *mddev, struct bio **bio)
> > > }
> > >
> > > clone->bi_end_io = md_end_clone_io;
> > > - clone->bi_private = md_io_clone;
> > > + clone->bi_private = NULL;
> > > *bio = clone;
> > > }
> > >
> > > @@ -9265,26 +9270,6 @@ void md_account_bio(struct mddev *mddev, struct bio **bio)
> > > }
> > > EXPORT_SYMBOL_GPL(md_account_bio);
> > >
> > > -void md_free_cloned_bio(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;
> > > -
> > > - if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
> > > - md_bitmap_end(mddev, md_io_clone);
> > > -
> > > - if (bio->bi_status && !orig_bio->bi_status)
> > > - orig_bio->bi_status = bio->bi_status;
> > > -
> > > - if (md_io_clone->start_time)
> > > - bio_end_io_acct(orig_bio, md_io_clone->start_time);
> > > -
> > > - bio_put(bio);
> > > - percpu_ref_put(&mddev->active_io);
> > > -}
> > > -EXPORT_SYMBOL_GPL(md_free_cloned_bio);
> > > -
> > > /* md_allow_write(mddev)
> > > * Calling this ensures that the array is marked 'active' so that writes
> > > * may proceed without blocking. It is important to call this before
> > > diff --git a/drivers/md/md.h b/drivers/md/md.h
> > > index ac84289664cd..5d57fee22901 100644
> > > --- a/drivers/md/md.h
> > > +++ b/drivers/md/md.h
> > > @@ -917,7 +917,6 @@ extern void md_finish_reshape(struct mddev *mddev);
> > > void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
> > > struct bio *bio, sector_t start, sector_t size);
> > > void md_account_bio(struct mddev *mddev, struct bio **bio);
> > > -void md_free_cloned_bio(struct bio *bio);
> > >
> > > extern bool __must_check md_flush_request(struct mddev *mddev, struct bio *bio);
> > > void md_write_metadata(struct mddev *mddev, struct md_rdev *rdev,
> > > diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> > > index a8e8d431071b..dc0c680ca199 100644
> > > --- a/drivers/md/raid5.c
> > > +++ b/drivers/md/raid5.c
> > > @@ -6217,7 +6217,12 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
> > >
> > > mempool_free(ctx, conf->ctx_pool);
> > > if (res == STRIPE_WAIT_RESHAPE) {
> > > - md_free_cloned_bio(bi);
> > > + DECLARE_COMPLETION_ONSTACK(done);
> > > + WRITE_ONCE(bi->bi_private, &done);
> > > +
> > > + bio_endio(bi);
> >
> > Hi Ben
> >
> > You gave an explanation why it doesn't need WRITE_ONCE. As you said,
> > bio_endio uses atomic_dec_and_test, so it guarantees a full memory
> > barrier. Why do you use WRITE_ONCE here?
>
> You're correct. I don't believe it's necessary. The compiler has to
> update bi->bi_private before calling bio_endio(bi), which can free bi,
> and either the bio was never chained, and bi->bi_private will be read by
> the same process that set it, or it was chained, and the
> atomic_dec_and_test() in bio_remaining_done() will guarantee a memory
> barrier.
>
> I just patterned my updated fix off your idea, and left the WRITE_ONCE
> there because it doesn't really hurt anything, since this is already the
> slow (and unlikely) path. I can pull it out if you'd like.
Thanks for the explanation. It's good to me to keep it.
>
> I actually have another question about this code. My patch doesn't mess
> with the code at the end of make_stripe_request() to return
> STRIPE_WAIT_RESHAPE, but I'm not sure that it's right. That code
> includes:
>
> bi->bi_status = BLK_STS_RESOURCE;
>
> This will update the orig_bio's bi_status in md_end_clone_io():
>
> if (bio->bi_status && !orig_bio->bi_status)
> orig_bio->bi_status = bio->bi_status;
>
> For dm-raid, that orig_bio is itself a clone, and will eventually
> get ended with DM_MAPIO_REQUEUE, which will requeue the actual
> original bio (assuming that this happens when the device is
> in a noflush suspend).
>
> But for md, md_handle_request() can just loop and retry it. If the
> mddev->pers->make_request() call succeeds on retry, the orig_bio will
> still have the BLK_STS_RESOURCE status that got set when the earlier
> call to make_stripe_request() returned STRIPE_WAIT_RESHAPE.
>
> Perhaps make_stripe_request() shouldn't set bi->bi_status if
> it's going to return STRIPE_WAIT_RESHAPE. The only thing I can see that
> it does is set orig_bio->bi_status, which I don't think we want it to
> do. Am I missing something here?
It's good to me to remove the line of setting BLK_STS_RESOURCE.
Regards
Xiao
>
> -Ben
>
> >
> > Regards
> > Xiao
> >
> >
> > > +
> > > + wait_for_completion(&done);
> > > return false;
> > > }
> > >
> > > --
> > > 2.53.0
> > >
>
^ permalink raw reply
* [PATCH] md/raid5: fix race between reshape and chunk-aligned read
From: FengWei Shih @ 2026-04-09 5:17 UTC (permalink / raw)
To: song, yukuai; +Cc: linan122, linux-raid, linux-kernel, FengWei Shih
raid5_make_request() checks mddev->reshape_position to decide whether
to allow chunk-aligned reads. However in raid5_start_reshape(), the
layout configuration (raid_disks, algorithm, etc.) is updated before
mddev->reshape_position is set:
reshape (raid5_start_reshape) read (raid5_make_request)
============================== ===========================
write_seqcount_begin
update raid_disks, algorithm...
set conf->reshape_progress
write_seqcount_end
check mddev->reshape_position
* still MaxSector, allow
raid5_read_one_chunk()
* use new layout
raid5_quiesce()
set mddev->reshape_position
Since reshape_position is not yet updated, raid5_make_request()
considers no reshape is in progress and proceeds with the
chunk-aligned path, but the layout has already changed, causing
raid5_compute_sector() to return an incorrect physical address.
Fix this by reading conf->reshape_progress under gen_lock in
raid5_read_one_chunk() and falling back to the stripe path if a
reshape is in progress.
Signed-off-by: FengWei Shih <dannyshih@synology.com>
---
drivers/md/raid5.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index a8e8d431071b..bded2b86f0ef 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -5421,6 +5421,11 @@ static int raid5_read_one_chunk(struct mddev *mddev, struct bio *raid_bio)
sector_t sector, end_sector;
int dd_idx;
bool did_inc;
+ int seq;
+
+ seq = read_seqcount_begin(&conf->gen_lock);
+ if (unlikely(conf->reshape_progress != MaxSector))
+ return 0;
if (!in_chunk_boundary(mddev, raid_bio)) {
pr_debug("%s: non aligned\n", __func__);
@@ -5431,6 +5436,9 @@ static int raid5_read_one_chunk(struct mddev *mddev, struct bio *raid_bio)
&dd_idx, NULL);
end_sector = sector + bio_sectors(raid_bio);
+ if (read_seqcount_retry(&conf->gen_lock, seq))
+ return 0;
+
if (r5c_big_stripe_cached(conf, sector))
return 0;
--
2.25.1
Disclaimer: The contents of this e-mail message and any attachments are confidential and are intended solely for addressee. The information may also be legally privileged. This transmission is sent in trust, for the sole purpose of delivery to the intended recipient. If you have received this transmission in error, any use, reproduction or dissemination of this transmission is strictly prohibited. If you are not the intended recipient, please immediately notify the sender by reply e-mail or phone and delete this message and its attachments, if any.
^ permalink raw reply related
* Re: [PATCH V2 2/2] nvme-multipath: enable PCI P2PDMA for multipath devices
From: Christoph Hellwig @ 2026-04-09 6:26 UTC (permalink / raw)
To: Chaitanya Kulkarni
Cc: song, yukuai, linan122, kbusch, axboe, hch, sagi, linux-raid,
linux-nvme, kmodukuri
In-Reply-To: <20260408072537.46540-3-kch@nvidia.com>
On Wed, Apr 08, 2026 at 12:25:37AM -0700, Chaitanya Kulkarni wrote:
> From: Kiran Kumar Modukuri <kmodukuri@nvidia.com>
>
> NVMe multipath does not expose BLK_FEAT_PCI_P2PDMA on the head disk
> even when the underlying controller supports it.
>
> Set BLK_FEAT_PCI_P2PDMA in nvme_mpath_alloc_disk() when the controller
> advertises P2PDMA support via ctrl->ops->supports_pci_p2pdma.
>
> Since multipath can match paths across different transports (e.g. PCIe
> and FC), not all paths are guaranteed to support P2PDMA. Clear
> BLK_FEAT_PCI_P2PDMA from the head disk in nvme_mpath_add_disk() if the
> newly added path does not support it, ensuring the feature is only
> advertised when every member supports it.
>
> Signed-off-by: Kiran Kumar Modukuri <kmodukuri@nvidia.com>
> Signed-off-by: Chaitanya Kulkarni <kch@nvidia.com>
> ---
> drivers/nvme/host/multipath.c | 18 ++++++++++++++++++
> 1 file changed, 18 insertions(+)
>
> diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c
> index ba00f0b72b85..48d920ce803f 100644
> --- a/drivers/nvme/host/multipath.c
> +++ b/drivers/nvme/host/multipath.c
> @@ -737,6 +737,9 @@ int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, struct nvme_ns_head *head)
> BLK_FEAT_POLL | BLK_FEAT_ATOMIC_WRITES;
> if (head->ids.csi == NVME_CSI_ZNS)
> lim.features |= BLK_FEAT_ZONED;
> + if (ctrl->ops && ctrl->ops->supports_pci_p2pdma &&
> + ctrl->ops->supports_pci_p2pdma(ctrl))
> + lim.features |= BLK_FEAT_PCI_P2PDMA;
I think we can just add this unconditionally here, similar to all the
other feaures above the ZNS conditional as any non-supporting controller
will clear it later.
>
> void nvme_mpath_add_disk(struct nvme_ns *ns, __le32 anagrpid)
> {
> + struct nvme_ns_head *head = ns->head;
> +
> + /*
> + * Clear BLK_FEAT_PCI_P2PDMA on the head disk if this path does not
> + * support it. Multipath may span different transports (e.g. PCIe and
> + * FC), so every member must support P2PDMA for it to be safe on the
> + * head disk.
> + */
> + if (head->disk && !blk_queue_pci_p2pdma(ns->queue)) {
> + struct queue_limits lim =
> + queue_limits_start_update(head->disk->queue);
> + lim.features &= ~BLK_FEAT_PCI_P2PDMA;
> + queue_limits_commit_update(head->disk->queue, &lim);
> + }
And this really should go into the core block code in blk_stack_limits,
so that BLK_FEAT_PCI_P2PDMA is cleared whenever a non-confirming
device is added, similar to BLK_FEAT_NOWAIT and BLK_FEAT_POLL.
^ permalink raw reply
* Re: [PATCH V2 1/2] md: propagate BLK_FEAT_PCI_P2PDMA from member devices
From: Christoph Hellwig @ 2026-04-09 6:27 UTC (permalink / raw)
To: Chaitanya Kulkarni
Cc: song, yukuai, linan122, kbusch, axboe, hch, sagi, linux-raid,
linux-nvme, kmodukuri
In-Reply-To: <20260408072537.46540-2-kch@nvidia.com>
On Wed, Apr 08, 2026 at 12:25:36AM -0700, Chaitanya Kulkarni wrote:
> From: Kiran Kumar Modukuri <kmodukuri@nvidia.com>
>
> MD RAID does not propagate BLK_FEAT_PCI_P2PDMA from member devices to
> the RAID device, preventing peer-to-peer DMA through the RAID layer even
> when all underlying devices support it.
>
> Enable BLK_FEAT_PCI_P2PDMA in raid0, raid1 and raid10 personalities
> during queue limits setup and clear it in mddev_stack_rdev_limits()
> during array init and mddev_stack_new_rdev() during hot-add if any
> member device lacks support. Parity RAID personalities (raid4/5/6) are
> excluded because they need CPU access to data pages for parity
> computation, which is incompatible with P2P mappings.
>
> Tested with RAID0/1/10 arrays containing multiple NVMe devices with P2PDMA
> support, confirming that peer-to-peer transfers work correctly through
> the RAID layer.
Same thing as for nvme-multipath applies here - just set
BLK_FEAT_PCI_P2PDMA unconditionally at setup time for the personality
that support it, and then rely on an updated blk_stack_limits to clear
it.
^ permalink raw reply
* Re: [PATCH 22/28] xor: add a better public API
From: Dan Williams @ 2026-04-10 3:03 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Richard Henderson, Matt Turner, Magnus Lindholm, Russell King,
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, David S. Miller, Andreas Larsson,
Richard Weinberger, Anton Ivanov, Johannes Berg, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Herbert Xu, Dan Williams, Chris Mason, David Sterba,
Arnd Bergmann, Song Liu, Yu Kuai, Li Nan, Theodore Ts'o,
Jason A. Donenfeld, linux-alpha, linux-kernel, linux-arm-kernel,
loongarch, linuxppc-dev, linux-riscv, linux-s390, sparclinux,
linux-um, linux-crypto, linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260327061704.3707577-23-hch@lst.de>
Christoph Hellwig wrote:
> xor_blocks is very annoying to use, because it is limited to 4 + 1
> sources / destinations, has an odd argument order and is completely
> undocumented.
>
> Lift the code that loops around it from btrfs and async_tx/async_xor into
> common code under the name xor_gen and properly document it.
Apologies for the latency on taking a look at this set from me. This and
the touch to async_xor() look good.
Reviewed-by: Dan Williams <djbw@kernel.org>
^ permalink raw reply
* Re: [PATCH 24/28] async_xor: use xor_gen
From: Dan Williams @ 2026-04-10 3:19 UTC (permalink / raw)
To: Christoph Hellwig
Cc: Richard Henderson, Matt Turner, Magnus Lindholm, Russell King,
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, David S. Miller, Andreas Larsson,
Richard Weinberger, Anton Ivanov, Johannes Berg, Thomas Gleixner,
Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
Herbert Xu, Dan Williams, Chris Mason, David Sterba,
Arnd Bergmann, Song Liu, Yu Kuai, Li Nan, Theodore Ts'o,
Jason A. Donenfeld, linux-alpha, linux-kernel, linux-arm-kernel,
loongarch, linuxppc-dev, linux-riscv, linux-s390, sparclinux,
linux-um, linux-crypto, linux-btrfs, linux-arch, linux-raid
In-Reply-To: <20260327061704.3707577-25-hch@lst.de>
Christoph Hellwig wrote:
> Replace use of the loop around xor_blocks with the easier to use xor_gen
> API.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> crypto/async_tx/async_xor.c | 34 ++++++++++------------------------
> 1 file changed, 10 insertions(+), 24 deletions(-)
[..]
> @@ -168,11 +156,10 @@ dma_xor_aligned_offsets(struct dma_device *device, unsigned int offset,
> *
> * honored flags: ASYNC_TX_ACK, ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DST
> *
> - * xor_blocks always uses the dest as a source so the
> - * ASYNC_TX_XOR_ZERO_DST flag must be set to not include dest data in
> - * the calculation. The assumption with dma engines is that they only
> - * use the destination buffer as a source when it is explicitly specified
> - * in the source list.
> + * xor_gen always uses the dest as a source so the ASYNC_TX_XOR_ZERO_DST flag
> + * must be set to not include dest data in the calculation. The assumption with
> + * dma engines is that they only use the destination buffer as a source when it
> + * is explicitly specified in the source list.
> *
> * src_list note: if the dest is also a source it must be at index zero.
> * The contents of this array will be overwritten if a scribble region
> @@ -259,11 +246,10 @@ EXPORT_SYMBOL_GPL(async_xor_offs);
> *
> * honored flags: ASYNC_TX_ACK, ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DST
> *
> - * xor_blocks always uses the dest as a source so the
> - * ASYNC_TX_XOR_ZERO_DST flag must be set to not include dest data in
> - * the calculation. The assumption with dma engines is that they only
> - * use the destination buffer as a source when it is explicitly specified
> - * in the source list.
> + * xor_gen always uses the dest as a source so the ASYNC_TX_XOR_ZERO_DST flag
> + * must be set to not include dest data in the calculation. The assumption with
> + * dma engines is that they only use the destination buffer as a source when it
> + * is explicitly specified in the source list.
In retrospect, no need to duplicate this help, but as is:
Reviewed-by: Dan Williams <djbw@kernel.org>
^ permalink raw reply
* [PATCH] md: fix kobject reference leak in md_import_device()
From: Guangshuo Li @ 2026-04-12 15:42 UTC (permalink / raw)
To: Song Liu, Yu Kuai, Greg Kroah-Hartman, linux-raid, linux-kernel
Cc: Guangshuo Li, stable
md_import_device() initializes rdev->kobj with kobject_init() before
checking the device size and loading the superblock.
When one of the later checks fails, the error path still frees rdev
directly with kfree(). This bypasses the kobject release path and leaves
the kobject reference unbalanced.
After kobject_init(), release rdev through kobject_put() instead of
kfree().
Fixes: f9cb074bff8e ("Kobject: rename kobject_init_ng() to kobject_init()")
Cc: stable@vger.kernel.org
Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
---
drivers/md/md.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 6d73f6e196a9..4ce7512dc834 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -3871,6 +3871,9 @@ static struct md_rdev *md_import_device(dev_t newdev, int super_format, int supe
out_blkdev_put:
fput(rdev->bdev_file);
+ md_rdev_clear(rdev);
+ kobject_put(&rdev->kobj);
+ return ERR_PTR(err);
out_clear_rdev:
md_rdev_clear(rdev);
out_free_rdev:
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v2] md/raid5: Fix UAF on IO across the reshape position
From: Xiao Ni @ 2026-04-13 2:07 UTC (permalink / raw)
To: Benjamin Marzinski, Yu Kuai, Song Liu, Li Nan
Cc: linux-raid, dm-devel, Nigel Croxon
In-Reply-To: <20260408043548.1695157-1-bmarzins@redhat.com>
在 2026/4/8 12:35, Benjamin Marzinski 写道:
> If make_stripe_request() returns STRIPE_WAIT_RESHAPE,
> raid5_make_request() will free the cloned bio. But raid5_make_request()
> can call make_stripe_request() multiple times, writing to the various
> stripes. If that bio got added to the toread or towrite lists of a
> stripe disk in an earlier call to make_stripe_request(), then it's not
> safe to just free the bio if a later part of it is found to cross the
> reshape position. Doing so can lead to a UAF error, when bio_endio()
> is called on the bio for the earlier stripes.
>
> Instead, raid5_make_request() needs to wait until all parts of the bio
> have called bio_endio(). To do this, bios that cross the reshape
> position while the reshape can't make progress are flagged as needing to
> wait for all parts to complete. When raid5_make_request() has a bio that
> failed make_stripe_request() with STRIPE_WAIT_RESHAPE, it sets
> bi->bi_private to a completion struct and waits for completion after
> ending the bio. When the bio_endio() is called for the last time on a
> clone bio with bi->bi_private set, it wakes up the waiter. This
> guarantees that raid5_make_request() doesn't return until the cloned bio
> needing a retry for io across the reshape boundary is safely cleaned up.
>
> There is a simple reproducer available at [1]. Compile the kernel with
> KASAN for more useful reporting when the error is triggered (this is not
> necessary to see the bug).
>
> [1] https://gist.github.com/bmarzins/e48598824305cf2171289e47d7241fa5
>
> Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
> ---
>
> Changes from v1:
> - Removed mddev->pending_retry_bios, mddev->retry_bios_wait, and
> md_io_clone->must_retry. Instead, use a completion struct
> pointed to by bi->bi_private, as suggested by Xiao Ni and Yu Kuai.
>
> drivers/md/md.c | 31 ++++++++-----------------------
> drivers/md/md.h | 1 -
> drivers/md/raid5.c | 7 ++++++-
> 3 files changed, 14 insertions(+), 25 deletions(-)
>
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index 3ce6f9e9d38e..4318d875a5f6 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -9215,9 +9215,11 @@ static void md_bitmap_end(struct mddev *mddev, struct md_io_clone *md_io_clone)
>
> static void md_end_clone_io(struct bio *bio)
> {
> - struct md_io_clone *md_io_clone = bio->bi_private;
> + struct md_io_clone *md_io_clone = container_of(bio, struct md_io_clone,
> + bio_clone);
> struct bio *orig_bio = md_io_clone->orig_bio;
> struct mddev *mddev = md_io_clone->mddev;
> + struct completion *reshape_completion = bio->bi_private;
>
> if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
> md_bitmap_end(mddev, md_io_clone);
> @@ -9229,7 +9231,10 @@ static void md_end_clone_io(struct bio *bio)
> bio_end_io_acct(orig_bio, md_io_clone->start_time);
>
> bio_put(bio);
> - bio_endio(orig_bio);
> + if (unlikely(reshape_completion))
> + complete(reshape_completion);
> + else
> + bio_endio(orig_bio);
> percpu_ref_put(&mddev->active_io);
> }
>
> @@ -9254,7 +9259,7 @@ static void md_clone_bio(struct mddev *mddev, struct bio **bio)
> }
>
> clone->bi_end_io = md_end_clone_io;
> - clone->bi_private = md_io_clone;
> + clone->bi_private = NULL;
> *bio = clone;
> }
>
> @@ -9265,26 +9270,6 @@ void md_account_bio(struct mddev *mddev, struct bio **bio)
> }
> EXPORT_SYMBOL_GPL(md_account_bio);
>
> -void md_free_cloned_bio(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;
> -
> - if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
> - md_bitmap_end(mddev, md_io_clone);
> -
> - if (bio->bi_status && !orig_bio->bi_status)
> - orig_bio->bi_status = bio->bi_status;
> -
> - if (md_io_clone->start_time)
> - bio_end_io_acct(orig_bio, md_io_clone->start_time);
> -
> - bio_put(bio);
> - percpu_ref_put(&mddev->active_io);
> -}
> -EXPORT_SYMBOL_GPL(md_free_cloned_bio);
> -
> /* md_allow_write(mddev)
> * Calling this ensures that the array is marked 'active' so that writes
> * may proceed without blocking. It is important to call this before
> diff --git a/drivers/md/md.h b/drivers/md/md.h
> index ac84289664cd..5d57fee22901 100644
> --- a/drivers/md/md.h
> +++ b/drivers/md/md.h
> @@ -917,7 +917,6 @@ extern void md_finish_reshape(struct mddev *mddev);
> void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
> struct bio *bio, sector_t start, sector_t size);
> void md_account_bio(struct mddev *mddev, struct bio **bio);
> -void md_free_cloned_bio(struct bio *bio);
>
> extern bool __must_check md_flush_request(struct mddev *mddev, struct bio *bio);
> void md_write_metadata(struct mddev *mddev, struct md_rdev *rdev,
> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> index a8e8d431071b..dc0c680ca199 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -6217,7 +6217,12 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
>
> mempool_free(ctx, conf->ctx_pool);
> if (res == STRIPE_WAIT_RESHAPE) {
> - md_free_cloned_bio(bi);
> + DECLARE_COMPLETION_ONSTACK(done);
> + WRITE_ONCE(bi->bi_private, &done);
> +
> + bio_endio(bi);
> +
> + wait_for_completion(&done);
> return false;
> }
>
The patch looks good to me.
Reviewed-by: Xiao Ni <xni@redhat.com>
^ permalink raw reply
* Re: [PATCH v2] md/raid5: Fix UAF on IO across the reshape position
From: Xiao Ni @ 2026-04-13 2:08 UTC (permalink / raw)
To: Benjamin Marzinski
Cc: Yu Kuai, Song Liu, Li Nan, linux-raid, dm-devel, Nigel Croxon
In-Reply-To: <CALTww2_EE5ZZKEUjxONrkfsAQJE0qp7H=S-RJTXSBGNiCHgraw@mail.gmail.com>
On Thu, Apr 9, 2026 at 10:31 AM Xiao Ni <xni@redhat.com> wrote:
>
> On Thu, Apr 9, 2026 at 3:58 AM Benjamin Marzinski <bmarzins@redhat.com> wrote:
> >
> > On Wed, Apr 08, 2026 at 07:22:38PM +0800, Xiao Ni wrote:
> > > On Wed, Apr 8, 2026 at 12:35 PM Benjamin Marzinski <bmarzins@redhat.com> wrote:
> > > >
> > > > If make_stripe_request() returns STRIPE_WAIT_RESHAPE,
> > > > raid5_make_request() will free the cloned bio. But raid5_make_request()
> > > > can call make_stripe_request() multiple times, writing to the various
> > > > stripes. If that bio got added to the toread or towrite lists of a
> > > > stripe disk in an earlier call to make_stripe_request(), then it's not
> > > > safe to just free the bio if a later part of it is found to cross the
> > > > reshape position. Doing so can lead to a UAF error, when bio_endio()
> > > > is called on the bio for the earlier stripes.
> > > >
> > > > Instead, raid5_make_request() needs to wait until all parts of the bio
> > > > have called bio_endio(). To do this, bios that cross the reshape
> > > > position while the reshape can't make progress are flagged as needing to
> > > > wait for all parts to complete. When raid5_make_request() has a bio that
> > > > failed make_stripe_request() with STRIPE_WAIT_RESHAPE, it sets
> > > > bi->bi_private to a completion struct and waits for completion after
> > > > ending the bio. When the bio_endio() is called for the last time on a
> > > > clone bio with bi->bi_private set, it wakes up the waiter. This
> > > > guarantees that raid5_make_request() doesn't return until the cloned bio
> > > > needing a retry for io across the reshape boundary is safely cleaned up.
> > > >
> > > > There is a simple reproducer available at [1]. Compile the kernel with
> > > > KASAN for more useful reporting when the error is triggered (this is not
> > > > necessary to see the bug).
> > > >
> > > > [1] https://gist.github.com/bmarzins/e48598824305cf2171289e47d7241fa5
> > > >
> > > > Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
> > > > ---
> > > >
> > > > Changes from v1:
> > > > - Removed mddev->pending_retry_bios, mddev->retry_bios_wait, and
> > > > md_io_clone->must_retry. Instead, use a completion struct
> > > > pointed to by bi->bi_private, as suggested by Xiao Ni and Yu Kuai.
> > > >
> > > > drivers/md/md.c | 31 ++++++++-----------------------
> > > > drivers/md/md.h | 1 -
> > > > drivers/md/raid5.c | 7 ++++++-
> > > > 3 files changed, 14 insertions(+), 25 deletions(-)
> > > >
> > > > diff --git a/drivers/md/md.c b/drivers/md/md.c
> > > > index 3ce6f9e9d38e..4318d875a5f6 100644
> > > > --- a/drivers/md/md.c
> > > > +++ b/drivers/md/md.c
> > > > @@ -9215,9 +9215,11 @@ static void md_bitmap_end(struct mddev *mddev, struct md_io_clone *md_io_clone)
> > > >
> > > > static void md_end_clone_io(struct bio *bio)
> > > > {
> > > > - struct md_io_clone *md_io_clone = bio->bi_private;
> > > > + struct md_io_clone *md_io_clone = container_of(bio, struct md_io_clone,
> > > > + bio_clone);
> > > > struct bio *orig_bio = md_io_clone->orig_bio;
> > > > struct mddev *mddev = md_io_clone->mddev;
> > > > + struct completion *reshape_completion = bio->bi_private;
> > > >
> > > > if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
> > > > md_bitmap_end(mddev, md_io_clone);
> > > > @@ -9229,7 +9231,10 @@ static void md_end_clone_io(struct bio *bio)
> > > > bio_end_io_acct(orig_bio, md_io_clone->start_time);
> > > >
> > > > bio_put(bio);
> > > > - bio_endio(orig_bio);
> > > > + if (unlikely(reshape_completion))
> > > > + complete(reshape_completion);
> > > > + else
> > > > + bio_endio(orig_bio);
> > > > percpu_ref_put(&mddev->active_io);
> > > > }
> > > >
> > > > @@ -9254,7 +9259,7 @@ static void md_clone_bio(struct mddev *mddev, struct bio **bio)
> > > > }
> > > >
> > > > clone->bi_end_io = md_end_clone_io;
> > > > - clone->bi_private = md_io_clone;
> > > > + clone->bi_private = NULL;
> > > > *bio = clone;
> > > > }
> > > >
> > > > @@ -9265,26 +9270,6 @@ void md_account_bio(struct mddev *mddev, struct bio **bio)
> > > > }
> > > > EXPORT_SYMBOL_GPL(md_account_bio);
> > > >
> > > > -void md_free_cloned_bio(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;
> > > > -
> > > > - if (bio_data_dir(orig_bio) == WRITE && md_bitmap_enabled(mddev, false))
> > > > - md_bitmap_end(mddev, md_io_clone);
> > > > -
> > > > - if (bio->bi_status && !orig_bio->bi_status)
> > > > - orig_bio->bi_status = bio->bi_status;
> > > > -
> > > > - if (md_io_clone->start_time)
> > > > - bio_end_io_acct(orig_bio, md_io_clone->start_time);
> > > > -
> > > > - bio_put(bio);
> > > > - percpu_ref_put(&mddev->active_io);
> > > > -}
> > > > -EXPORT_SYMBOL_GPL(md_free_cloned_bio);
> > > > -
> > > > /* md_allow_write(mddev)
> > > > * Calling this ensures that the array is marked 'active' so that writes
> > > > * may proceed without blocking. It is important to call this before
> > > > diff --git a/drivers/md/md.h b/drivers/md/md.h
> > > > index ac84289664cd..5d57fee22901 100644
> > > > --- a/drivers/md/md.h
> > > > +++ b/drivers/md/md.h
> > > > @@ -917,7 +917,6 @@ extern void md_finish_reshape(struct mddev *mddev);
> > > > void md_submit_discard_bio(struct mddev *mddev, struct md_rdev *rdev,
> > > > struct bio *bio, sector_t start, sector_t size);
> > > > void md_account_bio(struct mddev *mddev, struct bio **bio);
> > > > -void md_free_cloned_bio(struct bio *bio);
> > > >
> > > > extern bool __must_check md_flush_request(struct mddev *mddev, struct bio *bio);
> > > > void md_write_metadata(struct mddev *mddev, struct md_rdev *rdev,
> > > > diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> > > > index a8e8d431071b..dc0c680ca199 100644
> > > > --- a/drivers/md/raid5.c
> > > > +++ b/drivers/md/raid5.c
> > > > @@ -6217,7 +6217,12 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi)
> > > >
> > > > mempool_free(ctx, conf->ctx_pool);
> > > > if (res == STRIPE_WAIT_RESHAPE) {
> > > > - md_free_cloned_bio(bi);
> > > > + DECLARE_COMPLETION_ONSTACK(done);
> > > > + WRITE_ONCE(bi->bi_private, &done);
> > > > +
> > > > + bio_endio(bi);
> > >
> > > Hi Ben
> > >
> > > You gave an explanation why it doesn't need WRITE_ONCE. As you said,
> > > bio_endio uses atomic_dec_and_test, so it guarantees a full memory
> > > barrier. Why do you use WRITE_ONCE here?
> >
> > You're correct. I don't believe it's necessary. The compiler has to
> > update bi->bi_private before calling bio_endio(bi), which can free bi,
> > and either the bio was never chained, and bi->bi_private will be read by
> > the same process that set it, or it was chained, and the
> > atomic_dec_and_test() in bio_remaining_done() will guarantee a memory
> > barrier.
> >
> > I just patterned my updated fix off your idea, and left the WRITE_ONCE
> > there because it doesn't really hurt anything, since this is already the
> > slow (and unlikely) path. I can pull it out if you'd like.
>
> Thanks for the explanation. It's good to me to keep it.
>
> >
> > I actually have another question about this code. My patch doesn't mess
> > with the code at the end of make_stripe_request() to return
> > STRIPE_WAIT_RESHAPE, but I'm not sure that it's right. That code
> > includes:
> >
> > bi->bi_status = BLK_STS_RESOURCE;
> >
> > This will update the orig_bio's bi_status in md_end_clone_io():
> >
> > if (bio->bi_status && !orig_bio->bi_status)
> > orig_bio->bi_status = bio->bi_status;
> >
> > For dm-raid, that orig_bio is itself a clone, and will eventually
> > get ended with DM_MAPIO_REQUEUE, which will requeue the actual
> > original bio (assuming that this happens when the device is
> > in a noflush suspend).
> >
> > But for md, md_handle_request() can just loop and retry it. If the
> > mddev->pers->make_request() call succeeds on retry, the orig_bio will
> > still have the BLK_STS_RESOURCE status that got set when the earlier
> > call to make_stripe_request() returned STRIPE_WAIT_RESHAPE.
> >
> > Perhaps make_stripe_request() shouldn't set bi->bi_status if
> > it's going to return STRIPE_WAIT_RESHAPE. The only thing I can see that
> > it does is set orig_bio->bi_status, which I don't think we want it to
> > do. Am I missing something here?
>
> It's good to me to remove the line of setting BLK_STS_RESOURCE.
Hi Ben
Could you send this in a seperate patch?
Best Regards
Xiao
^ permalink raw reply
* Re: [PATCH] md/raid5: fix race between reshape and chunk-aligned read
From: Li Nan @ 2026-04-13 7:19 UTC (permalink / raw)
To: FengWei Shih, song, yukuai; +Cc: linux-raid, linux-kernel, yangerkun@huawei.com
In-Reply-To: <20260409051722.2865321-1-dannyshih@synology.com>
在 2026/4/9 13:17, FengWei Shih 写道:
> raid5_make_request() checks mddev->reshape_position to decide whether
> to allow chunk-aligned reads. However in raid5_start_reshape(), the
> layout configuration (raid_disks, algorithm, etc.) is updated before
> mddev->reshape_position is set:
>
> reshape (raid5_start_reshape) read (raid5_make_request)
> ============================== ===========================
> write_seqcount_begin
> update raid_disks, algorithm...
> set conf->reshape_progress
> write_seqcount_end
> check mddev->reshape_position
> * still MaxSector, allow
> raid5_read_one_chunk()
> * use new layout
> raid5_quiesce()
> set mddev->reshape_position
>
> Since reshape_position is not yet updated, raid5_make_request()
> considers no reshape is in progress and proceeds with the
> chunk-aligned path, but the layout has already changed, causing
> raid5_compute_sector() to return an incorrect physical address.
>
> Fix this by reading conf->reshape_progress under gen_lock in
> raid5_read_one_chunk() and falling back to the stripe path if a
> reshape is in progress.
>
> Signed-off-by: FengWei Shih <dannyshih@synology.com>
> ---
> drivers/md/raid5.c | 8 ++++++++
> 1 file changed, 8 insertions(+)
>
> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> index a8e8d431071b..bded2b86f0ef 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -5421,6 +5421,11 @@ static int raid5_read_one_chunk(struct mddev *mddev, struct bio *raid_bio)
> sector_t sector, end_sector;
> int dd_idx;
> bool did_inc;
> + int seq;
> +
> + seq = read_seqcount_begin(&conf->gen_lock);
> + if (unlikely(conf->reshape_progress != MaxSector))
> + return 0;
>
> if (!in_chunk_boundary(mddev, raid_bio)) {
> pr_debug("%s: non aligned\n", __func__);
> @@ -5431,6 +5436,9 @@ static int raid5_read_one_chunk(struct mddev *mddev, struct bio *raid_bio)
> &dd_idx, NULL);
> end_sector = sector + bio_sectors(raid_bio);
>
> + if (read_seqcount_retry(&conf->gen_lock, seq))
> + return 0;
> +
> if (r5c_big_stripe_cached(conf, sector))
> return 0;
>
It seems that there might be race issues wherever raid5_compute_sector is
used? This fix only addresses one of the problems.
--
Thanks,
Nan
^ permalink raw reply
* Re: [PATCH v2 1/5] md/md-bitmap: call md_bitmap_create,destroy in location_store
From: Li Nan @ 2026-04-13 7:47 UTC (permalink / raw)
To: Su Yue, linux-raid; +Cc: song, xni, yukuai, heming.zhao, l
In-Reply-To: <20260407102625.5686-2-glass.su@suse.com>
在 2026/4/7 18:26, Su Yue 写道:
> If bitmap/location is present, mdadm will call update_array_info()
> while growing bitmap from none to internal via location_store().
> md_bitmap_create() is needed to set mddev->bitmap_ops otherwise
> mddev->bitmap_ops->get_stats() in update_array_info() will trigger
> kernel NULL pointer dereference.
>
> Fixes: fb8cc3b0d9db ("md/md-bitmap: delay registration of bitmap_ops until creating bitmap")
> Signed-off-by: Su Yue <glass.su@suse.com>
> ---
> drivers/md/md-bitmap.c | 11 ++++++++---
> drivers/md/md.c | 4 ++--
> drivers/md/md.h | 2 ++
> 3 files changed, 12 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/md/md-bitmap.c b/drivers/md/md-bitmap.c
> index 83378c033c72..2f24aae05552 100644
> --- a/drivers/md/md-bitmap.c
> +++ b/drivers/md/md-bitmap.c
> @@ -2618,7 +2618,7 @@ location_store(struct mddev *mddev, const char *buf, size_t len)
> goto out;
> }
>
> - bitmap_destroy(mddev);
> + md_bitmap_destroy(mddev);
> mddev->bitmap_info.offset = 0;
> if (mddev->bitmap_info.file) {
> struct file *f = mddev->bitmap_info.file;
> @@ -2653,15 +2653,20 @@ location_store(struct mddev *mddev, const char *buf, size_t len)
> goto out;
> }
>
> + /*
> + * lockless bitmap shoudle have set bitmap_id
> + * using bitmap_type, so always ID_BITMAP.
> + */
> + mddev->bitmap_id = ID_BITMAP;
> mddev->bitmap_info.offset = offset;
> - rv = bitmap_create(mddev);
> + rv = md_bitmap_create(mddev);
> if (rv)
> goto out;
>
> rv = bitmap_load(mddev);
mddev->bitmap_ops->load() should also be used here.
--
Thanks,
Nan
^ permalink raw reply
* [RFC PATCH 0/2] Decouple ftrace/livepatch from module loader via notifier priority and reverse traversal
From: chensong_2000 @ 2026-04-13 8:01 UTC (permalink / raw)
To: rafael, lenb, mturquette, sboyd, viresh.kumar, agk, snitzer,
mpatocka, bmarzins, song, yukuai, linan122, jason.wessel, danielt,
dianders, horms, davem, edumazet, kuba, pabeni, paulmck, frederic,
mcgrof, petr.pavlu, da.gomez, samitolvanen, atomlin, jpoimboe,
jikos, mbenes, pmladek, joe.lawrence, rostedt, mhiramat,
mark.rutland, mathieu.desnoyers
Cc: linux-modules, linux-kernel, linux-trace-kernel, linux-acpi,
linux-clk, linux-pm, live-patching, dm-devel, linux-raid,
kgdb-bugreport, netdev, Song Chen
From: Song Chen <chensong_2000@189.cn>
This patchset addresses a long-standing tight coupling between the
module loader and two of its key consumers: ftrace and livepatch.
Background:
The module loader currently hard-codes direct calls to
ftrace_module_enable(), klp_module_coming(), klp_module_going() and
ftrace_release_mod() inside prepare_coming_module() and the module
unload path. This hard-coding was necessary because the module notifier
chain could not guarantee the strict call ordering that ftrace and
livepatch require:
During MODULE_STATE_COMING, ftrace must run before livepatch, so
that per-module function records are ready before livepatch registers
its ftrace hooks.
During MODULE_STATE_GOING, livepatch must run before ftrace, so that
livepatch removes its hooks before ftrace releases those records.
This symmetric setup/teardown ordering could not be expressed through
the notifier chain because the chain only supported forward (descending
priority) traversal. Without reverse traversal, it was impossible to
guarantee that the GOING order would be the strict inverse of the
COMING order using a single priority value per notifier.
Patch 1 - notifier: replace single-linked list with double-linked list.
Patch 2 - ftrace/klp: decouple from module loader using notifier
priority.
headsup: somehow the smtp of my mailbox doesn't work very well lately,
if i receive return letter, i have to resend, sorry in advance.
Song Chen (2):
kernel/notifier: replace single-linked list with double-linked list
for reverse traversal
kernel/module: Decouple klp and ftrace from load_module
drivers/acpi/sleep.c | 1 -
drivers/clk/clk.c | 2 +-
drivers/cpufreq/cpufreq.c | 2 +-
drivers/md/dm-integrity.c | 1 -
drivers/md/md.c | 1 -
include/linux/module.h | 8 ++
include/linux/notifier.h | 26 ++---
kernel/debug/debug_core.c | 1 -
kernel/livepatch/core.c | 29 ++++-
kernel/module/main.c | 34 +++---
kernel/notifier.c | 219 ++++++++++++++++++++++++++++++++------
kernel/trace/ftrace.c | 38 +++++++
net/ipv4/nexthop.c | 2 +-
13 files changed, 290 insertions(+), 74 deletions(-)
--
2.43.0
^ permalink raw reply
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