* [PATCH AUTOSEL 6.19-6.18] md raid: fix hang when stopping arrays with metadata through dm-raid
From: Sasha Levin @ 2026-02-11 12:30 UTC (permalink / raw)
To: patches, stable; +Cc: Heinz Mauelshagen, Yu Kuai, Sasha Levin, song, linux-raid
In-Reply-To: <20260211123112.1330287-1-sashal@kernel.org>
From: Heinz Mauelshagen <heinzm@redhat.com>
[ Upstream commit cefcb9297fbdb6d94b61787b4f8d84f55b741470 ]
When using device-mapper's dm-raid target, stopping a RAID array can cause
the system to hang under specific conditions.
This occurs when:
- A dm-raid managed device tree is suspended from top to bottom
(the top-level RAID device is suspended first, followed by its
underlying metadata and data devices)
- The top-level RAID device is then removed
Removing the top-level device triggers a hang in the following sequence:
the dm-raid destructor calls md_stop(), which tries to flush the
write-intent bitmap by writing to the metadata sub-devices. However, these
devices are already suspended, making them unable to complete the write-intent
operations and causing an indefinite block.
Fix:
- Prevent bitmap flushing when md_stop() is called from dm-raid
destructor context
and avoid a quiescing/unquescing cycle which could also cause I/O
- Still allow write-intent bitmap flushing when called from dm-raid
suspend context
This ensures that RAID array teardown can complete successfully even when the
underlying devices are in a suspended state.
This second patch uses md_is_rdwr() to distinguish between suspend and
destructor paths as elaborated on above.
Link: https://lore.kernel.org/linux-raid/CAM23VxqYrwkhKEBeQrZeZwQudbiNey2_8B_SEOLqug=pXxaFrA@mail.gmail.com
Signed-off-by: Heinz Mauelshagen <heinzm@redhat.com>
Signed-off-by: Yu Kuai <yukuai@fnnas.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
LLM Generated explanations, may be completely bogus:
## Complete Analysis
### 1. What the Bug Is
This commit fixes a **system hang** (indefinite block/deadlock) when
stopping dm-raid managed RAID arrays. The hang scenario is:
1. A dm-raid device tree is suspended top-to-bottom (top-level RAID
device first, then underlying metadata/data devices)
2. The top-level RAID device is removed (`raid_dtr` destructor)
3. `raid_dtr()` → `md_stop()` → `__md_stop_writes()` tries to:
- Quiesce the RAID personality (`pers->quiesce()`)
- Flush the write-intent bitmap to metadata devices
(`bitmap_ops->flush()`)
4. But the metadata devices are already suspended and **cannot complete
I/O**
5. The flush waits indefinitely → **system hang**
This is a real, user-reported bug (Link in commit message points to a
lore report).
### 2. The Fix
The fix adds a conditional guard around the quiesce and bitmap flush in
`__md_stop_writes()`:
```c
if (md_is_rdwr(mddev) || !mddev_is_dm(mddev)) {
// quiesce + bitmap flush
}
```
This condition skips the quiesce and bitmap flush **only** when:
- The device is a dm-raid device (`mddev_is_dm()` returns true), AND
- The device is NOT in read-write mode (`md_is_rdwr()` returns false)
The clever trick: `raid_postsuspend()` (suspend path) already calls
`md_stop_writes()` while the device is still `MD_RDWR`, so the bitmap
flush proceeds normally during suspend. Then it sets `rs->md.ro =
MD_RDONLY`. Later when `raid_dtr()` calls `md_stop()` →
`__md_stop_writes()`, the device is `MD_RDONLY`, so the condition is
false and the dangerous I/O is skipped.
For non-dm md arrays (`!mddev_is_dm()` is true), the condition is always
true and behavior is unchanged.
### 3. Code Change Scope
- **1 file changed**: `drivers/md/md.c`
- **8 insertions, 6 deletions** (net +2 lines)
- Only touches the `__md_stop_writes()` function
- Small and surgical
### 4. Critical Dependency Issue
The commit message explicitly says **"This second patch"**, indicating
it's part of a 2-patch series. The first patch is `55dcfdf8af9c3` ("dm
raid: use proper md_ro_state enumerators"), which:
- Added `rs->md.ro = MD_RDONLY;` to `raid_postsuspend()` in `dm-raid.c`
- Without this line, when `raid_dtr` runs, `mddev->ro` is still
`MD_RDWR` (from `raid_resume`), so `md_is_rdwr()` returns true, and
the quiesce/flush is NOT skipped → the hang still occurs!
**This means the fix (`cefcb9297fbdb`) CANNOT work without the
prerequisite (`55dcfdf8af9c3`).**
The prerequisite `55dcfdf8af9c3` was merged for v6.18. It is:
- Present in 6.18.y
- **NOT** present in 6.12.y, 6.6.y, 6.1.y, or 5.15.y
### 5. Additional Backport Complications
For older stable trees (6.12.y and earlier), the code context has
changed significantly:
- v6.12+: `bitmap_ops->flush()` (ops-based interface)
- v6.11 and earlier: `md_bitmap_flush()` (direct function call)
- The `md_bitmap_enabled()` guard was added between v6.12 and current
- The `serialize_policy` field changed to a flag bit
- `md_stop_writes()` in v6.6 includes `set_bit(MD_RECOVERY_FROZEN)` and
sync thread reaping, which was later moved to the callers
### 6. Is the Bug Present in Older Stable Trees?
**Yes**, the underlying bug (bitmap flush blocking on suspended metadata
devices) exists in all kernel versions with dm-raid. In v6.6/v6.1,
`__md_stop_writes()` also calls `md_bitmap_flush()` and
`pers->quiesce()` unconditionally, and the same hang can occur when
`raid_dtr` runs after `raid_postsuspend`.
However, to fix it in older trees, you would need BOTH:
1. The prerequisite that sets `ro = MD_RDONLY` in `raid_postsuspend`
(equivalent of `55dcfdf8af9c3`)
2. This fix that checks `md_is_rdwr()` in `__md_stop_writes` (this
commit)
And both would need adaptation to the different code context.
### 7. Risk Assessment
- **Severity of bug**: HIGH — system hang is a serious issue, especially
for enterprise users of dm-raid (LVM RAID, etc.)
- **Risk of regression**: LOW-MEDIUM — the fix only changes behavior for
dm-raid devices in the destructor path (when `!md_is_rdwr &&
mddev_is_dm`); all other paths are unchanged
- **Size**: Very small (8 insertions, 6 deletions)
- **Dependency complexity**: HIGH — requires prerequisite commit and
significant code context differences across versions
### 8. Verdict
This commit fixes a **real, user-reported system hang** in dm-raid
teardown, which is clearly important for stable. The fix itself is
small, surgical, and well-understood. However, it has a **hard
dependency** on `55dcfdf8af9c3` ("dm raid: use proper md_ro_state
enumerators") which adds the critical `rs->md.ro = MD_RDONLY` line in
`raid_postsuspend()`. Without that prerequisite, the `md_is_rdwr()`
check in this fix would never distinguish between suspend and destructor
paths, and the fix would be completely ineffective.
For 6.18.y, the prerequisite is present and this patch could apply
(possibly with minor context adjustments). For older LTS trees (6.12.y,
6.6.y, 6.1.y), both this commit and the prerequisite (with appropriate
context adaptations) would need to be backported together.
Given that:
1. It fixes a definite system hang (high severity)
2. It's small and well-contained
3. It has a clear dependency that must also be backported
4. dm-raid (LVM RAID) is widely used in enterprise environments
5. The author is from Red Hat (enterprise focus) and it was reported by
a real user
This is a **YES** for backporting, with the strong caveat that the
prerequisite commit `55dcfdf8af9c3` must be included in any backport.
**YES**
drivers/md/md.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 6d73f6e196a9f..ac71640ff3a81 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -6848,13 +6848,15 @@ static void __md_stop_writes(struct mddev *mddev)
{
timer_delete_sync(&mddev->safemode_timer);
- if (mddev->pers && mddev->pers->quiesce) {
- mddev->pers->quiesce(mddev, 1);
- mddev->pers->quiesce(mddev, 0);
- }
+ if (md_is_rdwr(mddev) || !mddev_is_dm(mddev)) {
+ if (mddev->pers && mddev->pers->quiesce) {
+ mddev->pers->quiesce(mddev, 1);
+ mddev->pers->quiesce(mddev, 0);
+ }
- if (md_bitmap_enabled(mddev, true))
- mddev->bitmap_ops->flush(mddev);
+ if (md_bitmap_enabled(mddev, true))
+ mddev->bitmap_ops->flush(mddev);
+ }
if (md_is_rdwr(mddev) &&
((!mddev->in_sync && !mddev_is_clustered(mddev)) ||
--
2.51.0
^ permalink raw reply related
* Re: [PATCH 2/2] md/raid1: serialize overlapped io for writemostly disk
From: Xiao Ni @ 2026-02-11 3:26 UTC (permalink / raw)
To: yukuai; +Cc: song, linux-raid, linan666
In-Reply-To: <a753611f-91ee-4cee-97c8-706154d1cade@fnnas.com>
On Wed, Feb 11, 2026 at 11:15 AM Yu Kuai <yukuai@fnnas.com> wrote:
>
> Hi,
>
> 在 2026/2/11 10:44, Xiao Ni 写道:
> > On Wed, Feb 11, 2026 at 10:32 AM Yu Kuai <yukuai@fnnas.com> wrote:
> >> Hi,
> >>
> >> 在 2026/2/6 13:38, Xiao Ni 写道:
> >>> In behind mode, overlap bios for writemostly device are queued. They
> >>> In behind mode, bios which overlap the bio from the interval tree
> >>> need to wait in waitqueue. Those bios will be handled once the in-tree
> >>> bio finishes. They are waken up and try to get the tree lock. The bio
> >>> which gets the lock will be handled. So the sequence can't be guaranteed.
> >>>
> >>> For example
> >>> bio1(100,200)
> >>> bio2(150,200)
> >>> bio3(150,300)
> >>> The write sequence of fast device is bio1,bio2,bio3. But the write
> >>> sequence of slow device is bio1,bio3,bio2. This causes data corruption.
> >>> Replace waitqueue with a fifo list to guarantee the write sequence.
> >> To be honest, I think this is fine with the respect user won't send overlap
> >> bios. In this case, bio1 is done in fast disk and pending in slow disk, then
> >> bio2 will stuck while issuing and waiting for bio1 to return from slow disk.
> >> So send bio3 in this case is not expected, and if you do you can't guarantee
> >> the complete order from fast disk as bio2, bio3 as well.
> > Hi Kuai
> >
> > In write behind mode, raid1 returns bio1 to the upper layer once the
> > write on the fast disk finishes. The upper layer (app/filesystem)
> > thinks that bio1 has finished, while the write of bio1 to the slow
> > disk is still in progress. So bio2 will not be stuck while issuing, am
> > I right? If so, bio2 can be submitted to raid1 once the write of bio1
> > to the fast disk finishes. And bio3 can be submitted to raid1 once the
> > write of bio2 to fast disk finishes.
>
> I just take a look at raid1 code, turns out raid1_write_request() just allocate
> bio and issue it directly in each rdev iteration, I misunderstood.
>
> This solution looks fine to me, however, I'll suggest to keep the wait_queue and
> use prepare_to_wait_exclusive() to guarantee the fifo order.
Thanks for the suggestion. I'll use prepare_to_wait_exclusive and send v2.
Best Regards
Xiao
>
> >
> > Best Regards
> > Xiao
> >>> Signed-off-by: Xiao Ni <xni@redhat.com>
> >>> ---
> >>> drivers/md/md.c | 1 -
> >>> drivers/md/md.h | 4 +++-
> >>> drivers/md/raid1.c | 39 +++++++++++++++++++++++++++++----------
> >>> 3 files changed, 32 insertions(+), 12 deletions(-)
> >>>
> >>> diff --git a/drivers/md/md.c b/drivers/md/md.c
> >>> index 59cd303548de..ab91d17c2d68 100644
> >>> --- a/drivers/md/md.c
> >>> +++ b/drivers/md/md.c
> >>> @@ -188,7 +188,6 @@ static int rdev_init_serial(struct md_rdev *rdev)
> >>>
> >>> spin_lock_init(&serial_tmp->serial_lock);
> >>> serial_tmp->serial_rb = RB_ROOT_CACHED;
> >>> - init_waitqueue_head(&serial_tmp->serial_io_wait);
> >>> }
> >>>
> >>> rdev->serial = serial;
> >>> diff --git a/drivers/md/md.h b/drivers/md/md.h
> >>> index ac84289664cd..64ffbb2efb87 100644
> >>> --- a/drivers/md/md.h
> >>> +++ b/drivers/md/md.h
> >>> @@ -126,7 +126,6 @@ enum sync_action {
> >>> struct serial_in_rdev {
> >>> struct rb_root_cached serial_rb;
> >>> spinlock_t serial_lock;
> >>> - wait_queue_head_t serial_io_wait;
> >>> };
> >>>
> >>> /*
> >>> @@ -382,6 +381,9 @@ struct serial_info {
> >>> sector_t start; /* start sector of rb node */
> >>> sector_t last; /* end sector of rb node */
> >>> sector_t _subtree_last; /* highest sector in subtree of rb node */
> >>> + struct list_head list_node;
> >>> + struct list_head waiters;
> >>> + struct completion ready;
> >>> };
> >>>
> >>> /*
> >>> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> >>> index a41b1ec3d695..38c73538d038 100644
> >>> --- a/drivers/md/raid1.c
> >>> +++ b/drivers/md/raid1.c
> >>> @@ -57,19 +57,25 @@ INTERVAL_TREE_DEFINE(struct serial_info, node, sector_t, _subtree_last,
> >>> START, LAST, static inline, raid1_rb);
> >>>
> >>> static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
> >>> - struct serial_info *si, int idx)
> >>> + struct serial_info *si)
> >>> {
> >>> unsigned long flags;
> >>> int ret = 0;
> >>> sector_t lo = r1_bio->sector;
> >>> sector_t hi = lo + r1_bio->sectors - 1;
> >>> + int idx = sector_to_idx(r1_bio->sector);
> >>> struct serial_in_rdev *serial = &rdev->serial[idx];
> >>> + struct serial_info *head_si;
> >>>
> >>> spin_lock_irqsave(&serial->serial_lock, flags);
> >>> /* collision happened */
> >>> - if (raid1_rb_iter_first(&serial->serial_rb, lo, hi))
> >>> + head_si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> >>> + if (head_si && head_si != si) {
> >>> + si->start = lo;
> >>> + si->last = hi;
> >>> + list_add_tail(&si->list_node, &head_si->waiters);
> >>> ret = -EBUSY;
> >>> - else {
> >>> + } else if (!head_si) {
> >>> si->start = lo;
> >>> si->last = hi;
> >>> raid1_rb_insert(si, &serial->serial_rb);
> >>> @@ -83,19 +89,23 @@ static void wait_for_serialization(struct md_rdev *rdev, struct r1bio *r1_bio)
> >>> {
> >>> struct mddev *mddev = rdev->mddev;
> >>> struct serial_info *si;
> >>> - int idx = sector_to_idx(r1_bio->sector);
> >>> - struct serial_in_rdev *serial = &rdev->serial[idx];
> >>>
> >>> if (WARN_ON(!mddev->serial_info_pool))
> >>> return;
> >>> si = mempool_alloc(mddev->serial_info_pool, GFP_NOIO);
> >>> - wait_event(serial->serial_io_wait,
> >>> - check_and_add_serial(rdev, r1_bio, si, idx) == 0);
> >>> + INIT_LIST_HEAD(&si->waiters);
> >>> + INIT_LIST_HEAD(&si->list_node);
> >>> + init_completion(&si->ready);
> >>> + while (check_and_add_serial(rdev, r1_bio, si)) {
> >>> + wait_for_completion(&si->ready);
> >>> + reinit_completion(&si->ready);
> >>> + }
> >>> }
> >>>
> >>> static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
> >>> {
> >>> struct serial_info *si;
> >>> + struct serial_info *first_si;
> >>> unsigned long flags;
> >>> int found = 0;
> >>> struct mddev *mddev = rdev->mddev;
> >>> @@ -106,16 +116,25 @@ static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
> >>> for (si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> >>> si; si = raid1_rb_iter_next(si, lo, hi)) {
> >>> if (si->start == lo && si->last == hi) {
> >>> - raid1_rb_remove(si, &serial->serial_rb);
> >>> - mempool_free(si, mddev->serial_info_pool);
> >>> found = 1;
> >>> break;
> >>> }
> >>> }
> >>> if (!found)
> >>> WARN(1, "The write IO is not recorded for serialization\n");
> >>> + else {
> >>> + raid1_rb_remove(si, &serial->serial_rb);
> >>> + if (!list_empty(&si->waiters)) {
> >>> + first_si = list_first_entry(&si->waiters,
> >>> + struct serial_info, list_node);
> >>> + list_del_init(&first_si->list_node);
> >>> + list_splice_init(&si->waiters, &first_si->waiters);
> >>> + raid1_rb_insert(first_si, &serial->serial_rb);
> >>> + complete(&first_si->ready);
> >>> + }
> >>> + mempool_free(si, mddev->serial_info_pool);
> >>> + }
> >>> spin_unlock_irqrestore(&serial->serial_lock, flags);
> >>> - wake_up(&serial->serial_io_wait);
> >>> }
> >>>
> >>> /*
> >> --
> >> Thansk,
> >> Kuai
> >>
> >
> --
> Thansk,
> Kuai
>
^ permalink raw reply
* Re: [PATCH 2/2] md/raid1: serialize overlapped io for writemostly disk
From: Yu Kuai @ 2026-02-11 3:15 UTC (permalink / raw)
To: Xiao Ni; +Cc: song, linux-raid, linan666, yukuai
In-Reply-To: <CALTww29NpmNbEb79KbVCX-rAF4o39rEd3-P1TjiDTc1OWpQiAw@mail.gmail.com>
Hi,
在 2026/2/11 10:44, Xiao Ni 写道:
> On Wed, Feb 11, 2026 at 10:32 AM Yu Kuai <yukuai@fnnas.com> wrote:
>> Hi,
>>
>> 在 2026/2/6 13:38, Xiao Ni 写道:
>>> In behind mode, overlap bios for writemostly device are queued. They
>>> In behind mode, bios which overlap the bio from the interval tree
>>> need to wait in waitqueue. Those bios will be handled once the in-tree
>>> bio finishes. They are waken up and try to get the tree lock. The bio
>>> which gets the lock will be handled. So the sequence can't be guaranteed.
>>>
>>> For example
>>> bio1(100,200)
>>> bio2(150,200)
>>> bio3(150,300)
>>> The write sequence of fast device is bio1,bio2,bio3. But the write
>>> sequence of slow device is bio1,bio3,bio2. This causes data corruption.
>>> Replace waitqueue with a fifo list to guarantee the write sequence.
>> To be honest, I think this is fine with the respect user won't send overlap
>> bios. In this case, bio1 is done in fast disk and pending in slow disk, then
>> bio2 will stuck while issuing and waiting for bio1 to return from slow disk.
>> So send bio3 in this case is not expected, and if you do you can't guarantee
>> the complete order from fast disk as bio2, bio3 as well.
> Hi Kuai
>
> In write behind mode, raid1 returns bio1 to the upper layer once the
> write on the fast disk finishes. The upper layer (app/filesystem)
> thinks that bio1 has finished, while the write of bio1 to the slow
> disk is still in progress. So bio2 will not be stuck while issuing, am
> I right? If so, bio2 can be submitted to raid1 once the write of bio1
> to the fast disk finishes. And bio3 can be submitted to raid1 once the
> write of bio2 to fast disk finishes.
I just take a look at raid1 code, turns out raid1_write_request() just allocate
bio and issue it directly in each rdev iteration, I misunderstood.
This solution looks fine to me, however, I'll suggest to keep the wait_queue and
use prepare_to_wait_exclusive() to guarantee the fifo order.
>
> Best Regards
> Xiao
>>> Signed-off-by: Xiao Ni <xni@redhat.com>
>>> ---
>>> drivers/md/md.c | 1 -
>>> drivers/md/md.h | 4 +++-
>>> drivers/md/raid1.c | 39 +++++++++++++++++++++++++++++----------
>>> 3 files changed, 32 insertions(+), 12 deletions(-)
>>>
>>> diff --git a/drivers/md/md.c b/drivers/md/md.c
>>> index 59cd303548de..ab91d17c2d68 100644
>>> --- a/drivers/md/md.c
>>> +++ b/drivers/md/md.c
>>> @@ -188,7 +188,6 @@ static int rdev_init_serial(struct md_rdev *rdev)
>>>
>>> spin_lock_init(&serial_tmp->serial_lock);
>>> serial_tmp->serial_rb = RB_ROOT_CACHED;
>>> - init_waitqueue_head(&serial_tmp->serial_io_wait);
>>> }
>>>
>>> rdev->serial = serial;
>>> diff --git a/drivers/md/md.h b/drivers/md/md.h
>>> index ac84289664cd..64ffbb2efb87 100644
>>> --- a/drivers/md/md.h
>>> +++ b/drivers/md/md.h
>>> @@ -126,7 +126,6 @@ enum sync_action {
>>> struct serial_in_rdev {
>>> struct rb_root_cached serial_rb;
>>> spinlock_t serial_lock;
>>> - wait_queue_head_t serial_io_wait;
>>> };
>>>
>>> /*
>>> @@ -382,6 +381,9 @@ struct serial_info {
>>> sector_t start; /* start sector of rb node */
>>> sector_t last; /* end sector of rb node */
>>> sector_t _subtree_last; /* highest sector in subtree of rb node */
>>> + struct list_head list_node;
>>> + struct list_head waiters;
>>> + struct completion ready;
>>> };
>>>
>>> /*
>>> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
>>> index a41b1ec3d695..38c73538d038 100644
>>> --- a/drivers/md/raid1.c
>>> +++ b/drivers/md/raid1.c
>>> @@ -57,19 +57,25 @@ INTERVAL_TREE_DEFINE(struct serial_info, node, sector_t, _subtree_last,
>>> START, LAST, static inline, raid1_rb);
>>>
>>> static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
>>> - struct serial_info *si, int idx)
>>> + struct serial_info *si)
>>> {
>>> unsigned long flags;
>>> int ret = 0;
>>> sector_t lo = r1_bio->sector;
>>> sector_t hi = lo + r1_bio->sectors - 1;
>>> + int idx = sector_to_idx(r1_bio->sector);
>>> struct serial_in_rdev *serial = &rdev->serial[idx];
>>> + struct serial_info *head_si;
>>>
>>> spin_lock_irqsave(&serial->serial_lock, flags);
>>> /* collision happened */
>>> - if (raid1_rb_iter_first(&serial->serial_rb, lo, hi))
>>> + head_si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
>>> + if (head_si && head_si != si) {
>>> + si->start = lo;
>>> + si->last = hi;
>>> + list_add_tail(&si->list_node, &head_si->waiters);
>>> ret = -EBUSY;
>>> - else {
>>> + } else if (!head_si) {
>>> si->start = lo;
>>> si->last = hi;
>>> raid1_rb_insert(si, &serial->serial_rb);
>>> @@ -83,19 +89,23 @@ static void wait_for_serialization(struct md_rdev *rdev, struct r1bio *r1_bio)
>>> {
>>> struct mddev *mddev = rdev->mddev;
>>> struct serial_info *si;
>>> - int idx = sector_to_idx(r1_bio->sector);
>>> - struct serial_in_rdev *serial = &rdev->serial[idx];
>>>
>>> if (WARN_ON(!mddev->serial_info_pool))
>>> return;
>>> si = mempool_alloc(mddev->serial_info_pool, GFP_NOIO);
>>> - wait_event(serial->serial_io_wait,
>>> - check_and_add_serial(rdev, r1_bio, si, idx) == 0);
>>> + INIT_LIST_HEAD(&si->waiters);
>>> + INIT_LIST_HEAD(&si->list_node);
>>> + init_completion(&si->ready);
>>> + while (check_and_add_serial(rdev, r1_bio, si)) {
>>> + wait_for_completion(&si->ready);
>>> + reinit_completion(&si->ready);
>>> + }
>>> }
>>>
>>> static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
>>> {
>>> struct serial_info *si;
>>> + struct serial_info *first_si;
>>> unsigned long flags;
>>> int found = 0;
>>> struct mddev *mddev = rdev->mddev;
>>> @@ -106,16 +116,25 @@ static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
>>> for (si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
>>> si; si = raid1_rb_iter_next(si, lo, hi)) {
>>> if (si->start == lo && si->last == hi) {
>>> - raid1_rb_remove(si, &serial->serial_rb);
>>> - mempool_free(si, mddev->serial_info_pool);
>>> found = 1;
>>> break;
>>> }
>>> }
>>> if (!found)
>>> WARN(1, "The write IO is not recorded for serialization\n");
>>> + else {
>>> + raid1_rb_remove(si, &serial->serial_rb);
>>> + if (!list_empty(&si->waiters)) {
>>> + first_si = list_first_entry(&si->waiters,
>>> + struct serial_info, list_node);
>>> + list_del_init(&first_si->list_node);
>>> + list_splice_init(&si->waiters, &first_si->waiters);
>>> + raid1_rb_insert(first_si, &serial->serial_rb);
>>> + complete(&first_si->ready);
>>> + }
>>> + mempool_free(si, mddev->serial_info_pool);
>>> + }
>>> spin_unlock_irqrestore(&serial->serial_lock, flags);
>>> - wake_up(&serial->serial_io_wait);
>>> }
>>>
>>> /*
>> --
>> Thansk,
>> Kuai
>>
>
--
Thansk,
Kuai
^ permalink raw reply
* Re: [PATCH 2/2] md/raid1: serialize overlapped io for writemostly disk
From: Xiao Ni @ 2026-02-11 2:50 UTC (permalink / raw)
To: yukuai; +Cc: song, linux-raid, linan666
In-Reply-To: <CALTww29NpmNbEb79KbVCX-rAF4o39rEd3-P1TjiDTc1OWpQiAw@mail.gmail.com>
On Wed, Feb 11, 2026 at 10:44 AM Xiao Ni <xni@redhat.com> wrote:
>
> On Wed, Feb 11, 2026 at 10:32 AM Yu Kuai <yukuai@fnnas.com> wrote:
> >
> > Hi,
> >
> > 在 2026/2/6 13:38, Xiao Ni 写道:
> > > In behind mode, overlap bios for writemostly device are queued. They
> > > In behind mode, bios which overlap the bio from the interval tree
> > > need to wait in waitqueue. Those bios will be handled once the in-tree
> > > bio finishes. They are waken up and try to get the tree lock. The bio
> > > which gets the lock will be handled. So the sequence can't be guaranteed.
> > >
> > > For example
> > > bio1(100,200)
> > > bio2(150,200)
> > > bio3(150,300)
> > > The write sequence of fast device is bio1,bio2,bio3. But the write
> > > sequence of slow device is bio1,bio3,bio2. This causes data corruption.
> > > Replace waitqueue with a fifo list to guarantee the write sequence.
> >
> > To be honest, I think this is fine with the respect user won't send overlap
> > bios. In this case, bio1 is done in fast disk and pending in slow disk, then
> > bio2 will stuck while issuing and waiting for bio1 to return from slow disk.
> > So send bio3 in this case is not expected, and if you do you can't guarantee
> > the complete order from fast disk as bio2, bio3 as well.
>
> Hi Kuai
>
> In write behind mode, raid1 returns bio1 to the upper layer once the
> write on the fast disk finishes. The upper layer (app/filesystem)
> thinks that bio1 has finished, while the write of bio1 to the slow
> disk is still in progress. So bio2 will not be stuck while issuing, am
> I right? If so, bio2 can be submitted to raid1 once the write of bio1
> to the fast disk finishes. And bio3 can be submitted to raid1 once the
> write of bio2 to fast disk finishes.
By the way, it's reported by our customer, there is a data corruption
by the following test script:
#!/bin/sh
disk1=/dev/loop0
disk2=/dev/loop1
mddev=/dev/md0
mount_md=/data-md
mount_disk1=/data-1
mount_disk2=/data-2
bitmap=/root/bitmap
test_prg=/root/test-serializewrite
test_num=1000
test_str=abcdefghijklmnopqrstuvwxyz
loop=0
touch MD-TEST
while [ -f MD-TEST ]; do
set -x
loop=$((loop + 1))
echo "START LOOP=$loop at $(date)"
echo "# lsblk"
lsblk
echo "# dmsetup"
dmsetup create disk-500ms --table "0 $(blockdev --getsz $disk2)
delay $disk2 0 500"
sleep 2
echo "# mdadm"
#mdadm --create $mddev --run --assume-clean --force --level=1
--write-behind=1024 --bitmap=$bitmap --raid-devices=2 $disk1
--write-mostly /dev/mapper/disk-500ms
mdadm --create $mddev --run --assume-clean --force --level=1
--write-behind=1024 --bitmap=internal --raid-devices=2 $disk1
--write-mostly /dev/mapper/disk-500ms
echo "# cat /proc/mdstat"
cat /proc/mdstat
#echo "# mdadm -X $bitmap"
#mdadm -X $bitmap
echo "# mkfs.xfs -f $mddev"
mkfs.xfs -f $mddev
echo "# mount $mddev $mount_md"
mount $mddev $mount_md
set +x
echo "# $test_prg"
for n in $(seq 1 $test_num); do
$test_prg $mount_md/test.$n $test_str &
done
while true; do
sleep 2
pgrep -f $(basename $test_prg) > /dev/null
if [ $? -ne 0 ]; then
break
fi
done
set -x
echo "# cat /proc/mdstat"
cat /proc/mdstat
#echo "# mdadm -X $bitmap"
#mdadm -X $bitmap
echo "# umount $mount_md"
sleep 10
while true; do
sleep 2
umount $mount_md
if [ $? -eq 0 ]; then
break
fi
done
echo "# mdadm --stop $mddev"
mdadm --stop $mddev
echo "# dmsetup remove disk-500ms"
while true; do
dmsetup remove disk-500ms
if [ $? -ne 0 ]; then
sleep 5
else
break
fi
done
offset=$(mdadm --examine $disk1 | awk -F': ' '/Data Offset/ {print
$2}' | awk '{print $1}')
offset=$((offset * 512))
echo "# mount -o ro,offset=$offset,nouuid $disk1 $mount_disk1"
mount -o ro,offset=$offset,nouuid $disk1 $mount_disk1
offset=$(mdadm --examine $disk2 | awk -F': ' '/Data Offset/ {print
$2}' | awk '{print $1}')
#offset=$(mdadm --examine /dev/mapper/disk-500ms | awk -F': '
'/Data Offset/ {print $2}' | awk '{print $1}')
offset=$((offset * 512))
echo "# mount -o ro,offset=$offset,nouuid $disk2 $mount_disk2"
#echo "# mount -o ro,offset=$offset,nouuid /dev/mapper/disk-500ms
$mount_disk2"
mount -o ro,offset=$offset,nouuid $disk2 $mount_disk2
#mount -o ro,offset=$offset,nouuid /dev/mapper/disk-500ms $mount_disk2
echo "# diff -rq $mount_disk1 $mount_disk2"
diff -rq $mount_disk1 $mount_disk2
if [ $? -eq 0 ]; then
echo "@@@ PASS (loop=$loop)"
else
echo "@@@ FAIL (loop=$loop)"
fi
umount $mount_disk1
umount $mount_disk2
mdadm --zero-superblock $disk1
mdadm --zero-superblock $disk2
sleep 10
set +x
done
>
> Best Regards
> Xiao
> >
> > >
> > > Signed-off-by: Xiao Ni <xni@redhat.com>
> > > ---
> > > drivers/md/md.c | 1 -
> > > drivers/md/md.h | 4 +++-
> > > drivers/md/raid1.c | 39 +++++++++++++++++++++++++++++----------
> > > 3 files changed, 32 insertions(+), 12 deletions(-)
> > >
> > > diff --git a/drivers/md/md.c b/drivers/md/md.c
> > > index 59cd303548de..ab91d17c2d68 100644
> > > --- a/drivers/md/md.c
> > > +++ b/drivers/md/md.c
> > > @@ -188,7 +188,6 @@ static int rdev_init_serial(struct md_rdev *rdev)
> > >
> > > spin_lock_init(&serial_tmp->serial_lock);
> > > serial_tmp->serial_rb = RB_ROOT_CACHED;
> > > - init_waitqueue_head(&serial_tmp->serial_io_wait);
> > > }
> > >
> > > rdev->serial = serial;
> > > diff --git a/drivers/md/md.h b/drivers/md/md.h
> > > index ac84289664cd..64ffbb2efb87 100644
> > > --- a/drivers/md/md.h
> > > +++ b/drivers/md/md.h
> > > @@ -126,7 +126,6 @@ enum sync_action {
> > > struct serial_in_rdev {
> > > struct rb_root_cached serial_rb;
> > > spinlock_t serial_lock;
> > > - wait_queue_head_t serial_io_wait;
> > > };
> > >
> > > /*
> > > @@ -382,6 +381,9 @@ struct serial_info {
> > > sector_t start; /* start sector of rb node */
> > > sector_t last; /* end sector of rb node */
> > > sector_t _subtree_last; /* highest sector in subtree of rb node */
> > > + struct list_head list_node;
> > > + struct list_head waiters;
> > > + struct completion ready;
> > > };
> > >
> > > /*
> > > diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> > > index a41b1ec3d695..38c73538d038 100644
> > > --- a/drivers/md/raid1.c
> > > +++ b/drivers/md/raid1.c
> > > @@ -57,19 +57,25 @@ INTERVAL_TREE_DEFINE(struct serial_info, node, sector_t, _subtree_last,
> > > START, LAST, static inline, raid1_rb);
> > >
> > > static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
> > > - struct serial_info *si, int idx)
> > > + struct serial_info *si)
> > > {
> > > unsigned long flags;
> > > int ret = 0;
> > > sector_t lo = r1_bio->sector;
> > > sector_t hi = lo + r1_bio->sectors - 1;
> > > + int idx = sector_to_idx(r1_bio->sector);
> > > struct serial_in_rdev *serial = &rdev->serial[idx];
> > > + struct serial_info *head_si;
> > >
> > > spin_lock_irqsave(&serial->serial_lock, flags);
> > > /* collision happened */
> > > - if (raid1_rb_iter_first(&serial->serial_rb, lo, hi))
> > > + head_si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> > > + if (head_si && head_si != si) {
> > > + si->start = lo;
> > > + si->last = hi;
> > > + list_add_tail(&si->list_node, &head_si->waiters);
> > > ret = -EBUSY;
> > > - else {
> > > + } else if (!head_si) {
> > > si->start = lo;
> > > si->last = hi;
> > > raid1_rb_insert(si, &serial->serial_rb);
> > > @@ -83,19 +89,23 @@ static void wait_for_serialization(struct md_rdev *rdev, struct r1bio *r1_bio)
> > > {
> > > struct mddev *mddev = rdev->mddev;
> > > struct serial_info *si;
> > > - int idx = sector_to_idx(r1_bio->sector);
> > > - struct serial_in_rdev *serial = &rdev->serial[idx];
> > >
> > > if (WARN_ON(!mddev->serial_info_pool))
> > > return;
> > > si = mempool_alloc(mddev->serial_info_pool, GFP_NOIO);
> > > - wait_event(serial->serial_io_wait,
> > > - check_and_add_serial(rdev, r1_bio, si, idx) == 0);
> > > + INIT_LIST_HEAD(&si->waiters);
> > > + INIT_LIST_HEAD(&si->list_node);
> > > + init_completion(&si->ready);
> > > + while (check_and_add_serial(rdev, r1_bio, si)) {
> > > + wait_for_completion(&si->ready);
> > > + reinit_completion(&si->ready);
> > > + }
> > > }
> > >
> > > static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
> > > {
> > > struct serial_info *si;
> > > + struct serial_info *first_si;
> > > unsigned long flags;
> > > int found = 0;
> > > struct mddev *mddev = rdev->mddev;
> > > @@ -106,16 +116,25 @@ static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
> > > for (si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> > > si; si = raid1_rb_iter_next(si, lo, hi)) {
> > > if (si->start == lo && si->last == hi) {
> > > - raid1_rb_remove(si, &serial->serial_rb);
> > > - mempool_free(si, mddev->serial_info_pool);
> > > found = 1;
> > > break;
> > > }
> > > }
> > > if (!found)
> > > WARN(1, "The write IO is not recorded for serialization\n");
> > > + else {
> > > + raid1_rb_remove(si, &serial->serial_rb);
> > > + if (!list_empty(&si->waiters)) {
> > > + first_si = list_first_entry(&si->waiters,
> > > + struct serial_info, list_node);
> > > + list_del_init(&first_si->list_node);
> > > + list_splice_init(&si->waiters, &first_si->waiters);
> > > + raid1_rb_insert(first_si, &serial->serial_rb);
> > > + complete(&first_si->ready);
> > > + }
> > > + mempool_free(si, mddev->serial_info_pool);
> > > + }
> > > spin_unlock_irqrestore(&serial->serial_lock, flags);
> > > - wake_up(&serial->serial_io_wait);
> > > }
> > >
> > > /*
> >
> > --
> > Thansk,
> > Kuai
> >
^ permalink raw reply
* Re: [PATCH 2/2] md/raid1: serialize overlapped io for writemostly disk
From: Xiao Ni @ 2026-02-11 2:44 UTC (permalink / raw)
To: yukuai; +Cc: song, linux-raid, linan666
In-Reply-To: <9bd6581f-289a-4490-af6d-f15589446e1e@fnnas.com>
On Wed, Feb 11, 2026 at 10:32 AM Yu Kuai <yukuai@fnnas.com> wrote:
>
> Hi,
>
> 在 2026/2/6 13:38, Xiao Ni 写道:
> > In behind mode, overlap bios for writemostly device are queued. They
> > In behind mode, bios which overlap the bio from the interval tree
> > need to wait in waitqueue. Those bios will be handled once the in-tree
> > bio finishes. They are waken up and try to get the tree lock. The bio
> > which gets the lock will be handled. So the sequence can't be guaranteed.
> >
> > For example
> > bio1(100,200)
> > bio2(150,200)
> > bio3(150,300)
> > The write sequence of fast device is bio1,bio2,bio3. But the write
> > sequence of slow device is bio1,bio3,bio2. This causes data corruption.
> > Replace waitqueue with a fifo list to guarantee the write sequence.
>
> To be honest, I think this is fine with the respect user won't send overlap
> bios. In this case, bio1 is done in fast disk and pending in slow disk, then
> bio2 will stuck while issuing and waiting for bio1 to return from slow disk.
> So send bio3 in this case is not expected, and if you do you can't guarantee
> the complete order from fast disk as bio2, bio3 as well.
Hi Kuai
In write behind mode, raid1 returns bio1 to the upper layer once the
write on the fast disk finishes. The upper layer (app/filesystem)
thinks that bio1 has finished, while the write of bio1 to the slow
disk is still in progress. So bio2 will not be stuck while issuing, am
I right? If so, bio2 can be submitted to raid1 once the write of bio1
to the fast disk finishes. And bio3 can be submitted to raid1 once the
write of bio2 to fast disk finishes.
Best Regards
Xiao
>
> >
> > Signed-off-by: Xiao Ni <xni@redhat.com>
> > ---
> > drivers/md/md.c | 1 -
> > drivers/md/md.h | 4 +++-
> > drivers/md/raid1.c | 39 +++++++++++++++++++++++++++++----------
> > 3 files changed, 32 insertions(+), 12 deletions(-)
> >
> > diff --git a/drivers/md/md.c b/drivers/md/md.c
> > index 59cd303548de..ab91d17c2d68 100644
> > --- a/drivers/md/md.c
> > +++ b/drivers/md/md.c
> > @@ -188,7 +188,6 @@ static int rdev_init_serial(struct md_rdev *rdev)
> >
> > spin_lock_init(&serial_tmp->serial_lock);
> > serial_tmp->serial_rb = RB_ROOT_CACHED;
> > - init_waitqueue_head(&serial_tmp->serial_io_wait);
> > }
> >
> > rdev->serial = serial;
> > diff --git a/drivers/md/md.h b/drivers/md/md.h
> > index ac84289664cd..64ffbb2efb87 100644
> > --- a/drivers/md/md.h
> > +++ b/drivers/md/md.h
> > @@ -126,7 +126,6 @@ enum sync_action {
> > struct serial_in_rdev {
> > struct rb_root_cached serial_rb;
> > spinlock_t serial_lock;
> > - wait_queue_head_t serial_io_wait;
> > };
> >
> > /*
> > @@ -382,6 +381,9 @@ struct serial_info {
> > sector_t start; /* start sector of rb node */
> > sector_t last; /* end sector of rb node */
> > sector_t _subtree_last; /* highest sector in subtree of rb node */
> > + struct list_head list_node;
> > + struct list_head waiters;
> > + struct completion ready;
> > };
> >
> > /*
> > diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> > index a41b1ec3d695..38c73538d038 100644
> > --- a/drivers/md/raid1.c
> > +++ b/drivers/md/raid1.c
> > @@ -57,19 +57,25 @@ INTERVAL_TREE_DEFINE(struct serial_info, node, sector_t, _subtree_last,
> > START, LAST, static inline, raid1_rb);
> >
> > static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
> > - struct serial_info *si, int idx)
> > + struct serial_info *si)
> > {
> > unsigned long flags;
> > int ret = 0;
> > sector_t lo = r1_bio->sector;
> > sector_t hi = lo + r1_bio->sectors - 1;
> > + int idx = sector_to_idx(r1_bio->sector);
> > struct serial_in_rdev *serial = &rdev->serial[idx];
> > + struct serial_info *head_si;
> >
> > spin_lock_irqsave(&serial->serial_lock, flags);
> > /* collision happened */
> > - if (raid1_rb_iter_first(&serial->serial_rb, lo, hi))
> > + head_si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> > + if (head_si && head_si != si) {
> > + si->start = lo;
> > + si->last = hi;
> > + list_add_tail(&si->list_node, &head_si->waiters);
> > ret = -EBUSY;
> > - else {
> > + } else if (!head_si) {
> > si->start = lo;
> > si->last = hi;
> > raid1_rb_insert(si, &serial->serial_rb);
> > @@ -83,19 +89,23 @@ static void wait_for_serialization(struct md_rdev *rdev, struct r1bio *r1_bio)
> > {
> > struct mddev *mddev = rdev->mddev;
> > struct serial_info *si;
> > - int idx = sector_to_idx(r1_bio->sector);
> > - struct serial_in_rdev *serial = &rdev->serial[idx];
> >
> > if (WARN_ON(!mddev->serial_info_pool))
> > return;
> > si = mempool_alloc(mddev->serial_info_pool, GFP_NOIO);
> > - wait_event(serial->serial_io_wait,
> > - check_and_add_serial(rdev, r1_bio, si, idx) == 0);
> > + INIT_LIST_HEAD(&si->waiters);
> > + INIT_LIST_HEAD(&si->list_node);
> > + init_completion(&si->ready);
> > + while (check_and_add_serial(rdev, r1_bio, si)) {
> > + wait_for_completion(&si->ready);
> > + reinit_completion(&si->ready);
> > + }
> > }
> >
> > static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
> > {
> > struct serial_info *si;
> > + struct serial_info *first_si;
> > unsigned long flags;
> > int found = 0;
> > struct mddev *mddev = rdev->mddev;
> > @@ -106,16 +116,25 @@ static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
> > for (si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> > si; si = raid1_rb_iter_next(si, lo, hi)) {
> > if (si->start == lo && si->last == hi) {
> > - raid1_rb_remove(si, &serial->serial_rb);
> > - mempool_free(si, mddev->serial_info_pool);
> > found = 1;
> > break;
> > }
> > }
> > if (!found)
> > WARN(1, "The write IO is not recorded for serialization\n");
> > + else {
> > + raid1_rb_remove(si, &serial->serial_rb);
> > + if (!list_empty(&si->waiters)) {
> > + first_si = list_first_entry(&si->waiters,
> > + struct serial_info, list_node);
> > + list_del_init(&first_si->list_node);
> > + list_splice_init(&si->waiters, &first_si->waiters);
> > + raid1_rb_insert(first_si, &serial->serial_rb);
> > + complete(&first_si->ready);
> > + }
> > + mempool_free(si, mddev->serial_info_pool);
> > + }
> > spin_unlock_irqrestore(&serial->serial_lock, flags);
> > - wake_up(&serial->serial_io_wait);
> > }
> >
> > /*
>
> --
> Thansk,
> Kuai
>
^ permalink raw reply
* Re: [PATCH 1/2] md/raid1: fix the comparing region of interval tree
From: Xiao Ni @ 2026-02-11 2:35 UTC (permalink / raw)
To: yukuai; +Cc: song, linux-raid, linan666
In-Reply-To: <02c05b44-ee43-44c0-ae39-d1c77d31158f@fnnas.com>
On Wed, Feb 11, 2026 at 10:19 AM Yu Kuai <yukuai@fnnas.com> wrote:
>
> Hi,
>
> 在 2026/2/6 13:38, Xiao Ni 写道:
> > Interval tree uses [start, end] as a region which stores in the tree.
> > In raid1, it uses the wrong end value. For example:
> > bio(A,B) is too big and needs to be split to bio1(A,C-1), bio2(C,B).
> > The region of bio1 is [A,C] and the region of bio2 is [C,B]. So bio1 and
> > bio2 overlap which is not right.
> >
> > Fix this problem by using right end value of the region.
> >
> > Signed-off-by: Xiao Ni <xni@redhat.com>
> > ---
> > drivers/md/raid1.c | 4 ++--
> > 1 file changed, 2 insertions(+), 2 deletions(-)
>
> Perhaps also add a fix tag, with this:
Thanks for the suggestion. I'll add it and send V2
>
> Reviewed-by: Yu Kuai <yukuai@fnnas.com>
>
> >
> > diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> > index 867db18bc3ba..a41b1ec3d695 100644
> > --- a/drivers/md/raid1.c
> > +++ b/drivers/md/raid1.c
> > @@ -62,7 +62,7 @@ static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
> > unsigned long flags;
> > int ret = 0;
> > sector_t lo = r1_bio->sector;
> > - sector_t hi = lo + r1_bio->sectors;
> > + sector_t hi = lo + r1_bio->sectors - 1;
> > struct serial_in_rdev *serial = &rdev->serial[idx];
> >
> > spin_lock_irqsave(&serial->serial_lock, flags);
> > @@ -453,7 +453,7 @@ static void raid1_end_write_request(struct bio *bio)
> > int mirror = find_bio_disk(r1_bio, bio);
> > struct md_rdev *rdev = conf->mirrors[mirror].rdev;
> > sector_t lo = r1_bio->sector;
> > - sector_t hi = r1_bio->sector + r1_bio->sectors;
> > + sector_t hi = r1_bio->sector + r1_bio->sectors - 1;
> > bool ignore_error = !raid1_should_handle_error(bio) ||
> > (bio->bi_status && bio_op(bio) == REQ_OP_DISCARD);
> >
>
> --
> Thansk,
> Kuai
>
^ permalink raw reply
* Re: [PATCH 2/2] md/raid1: serialize overlapped io for writemostly disk
From: Yu Kuai @ 2026-02-11 2:31 UTC (permalink / raw)
To: Xiao Ni; +Cc: song, linux-raid, linan666, yukuai
In-Reply-To: <20260206053826.37416-3-xni@redhat.com>
Hi,
在 2026/2/6 13:38, Xiao Ni 写道:
> In behind mode, overlap bios for writemostly device are queued. They
> In behind mode, bios which overlap the bio from the interval tree
> need to wait in waitqueue. Those bios will be handled once the in-tree
> bio finishes. They are waken up and try to get the tree lock. The bio
> which gets the lock will be handled. So the sequence can't be guaranteed.
>
> For example
> bio1(100,200)
> bio2(150,200)
> bio3(150,300)
> The write sequence of fast device is bio1,bio2,bio3. But the write
> sequence of slow device is bio1,bio3,bio2. This causes data corruption.
> Replace waitqueue with a fifo list to guarantee the write sequence.
To be honest, I think this is fine with the respect user won't send overlap
bios. In this case, bio1 is done in fast disk and pending in slow disk, then
bio2 will stuck while issuing and waiting for bio1 to return from slow disk.
So send bio3 in this case is not expected, and if you do you can't guarantee
the complete order from fast disk as bio2, bio3 as well.
>
> Signed-off-by: Xiao Ni <xni@redhat.com>
> ---
> drivers/md/md.c | 1 -
> drivers/md/md.h | 4 +++-
> drivers/md/raid1.c | 39 +++++++++++++++++++++++++++++----------
> 3 files changed, 32 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index 59cd303548de..ab91d17c2d68 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -188,7 +188,6 @@ static int rdev_init_serial(struct md_rdev *rdev)
>
> spin_lock_init(&serial_tmp->serial_lock);
> serial_tmp->serial_rb = RB_ROOT_CACHED;
> - init_waitqueue_head(&serial_tmp->serial_io_wait);
> }
>
> rdev->serial = serial;
> diff --git a/drivers/md/md.h b/drivers/md/md.h
> index ac84289664cd..64ffbb2efb87 100644
> --- a/drivers/md/md.h
> +++ b/drivers/md/md.h
> @@ -126,7 +126,6 @@ enum sync_action {
> struct serial_in_rdev {
> struct rb_root_cached serial_rb;
> spinlock_t serial_lock;
> - wait_queue_head_t serial_io_wait;
> };
>
> /*
> @@ -382,6 +381,9 @@ struct serial_info {
> sector_t start; /* start sector of rb node */
> sector_t last; /* end sector of rb node */
> sector_t _subtree_last; /* highest sector in subtree of rb node */
> + struct list_head list_node;
> + struct list_head waiters;
> + struct completion ready;
> };
>
> /*
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index a41b1ec3d695..38c73538d038 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -57,19 +57,25 @@ INTERVAL_TREE_DEFINE(struct serial_info, node, sector_t, _subtree_last,
> START, LAST, static inline, raid1_rb);
>
> static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
> - struct serial_info *si, int idx)
> + struct serial_info *si)
> {
> unsigned long flags;
> int ret = 0;
> sector_t lo = r1_bio->sector;
> sector_t hi = lo + r1_bio->sectors - 1;
> + int idx = sector_to_idx(r1_bio->sector);
> struct serial_in_rdev *serial = &rdev->serial[idx];
> + struct serial_info *head_si;
>
> spin_lock_irqsave(&serial->serial_lock, flags);
> /* collision happened */
> - if (raid1_rb_iter_first(&serial->serial_rb, lo, hi))
> + head_si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> + if (head_si && head_si != si) {
> + si->start = lo;
> + si->last = hi;
> + list_add_tail(&si->list_node, &head_si->waiters);
> ret = -EBUSY;
> - else {
> + } else if (!head_si) {
> si->start = lo;
> si->last = hi;
> raid1_rb_insert(si, &serial->serial_rb);
> @@ -83,19 +89,23 @@ static void wait_for_serialization(struct md_rdev *rdev, struct r1bio *r1_bio)
> {
> struct mddev *mddev = rdev->mddev;
> struct serial_info *si;
> - int idx = sector_to_idx(r1_bio->sector);
> - struct serial_in_rdev *serial = &rdev->serial[idx];
>
> if (WARN_ON(!mddev->serial_info_pool))
> return;
> si = mempool_alloc(mddev->serial_info_pool, GFP_NOIO);
> - wait_event(serial->serial_io_wait,
> - check_and_add_serial(rdev, r1_bio, si, idx) == 0);
> + INIT_LIST_HEAD(&si->waiters);
> + INIT_LIST_HEAD(&si->list_node);
> + init_completion(&si->ready);
> + while (check_and_add_serial(rdev, r1_bio, si)) {
> + wait_for_completion(&si->ready);
> + reinit_completion(&si->ready);
> + }
> }
>
> static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
> {
> struct serial_info *si;
> + struct serial_info *first_si;
> unsigned long flags;
> int found = 0;
> struct mddev *mddev = rdev->mddev;
> @@ -106,16 +116,25 @@ static void remove_serial(struct md_rdev *rdev, sector_t lo, sector_t hi)
> for (si = raid1_rb_iter_first(&serial->serial_rb, lo, hi);
> si; si = raid1_rb_iter_next(si, lo, hi)) {
> if (si->start == lo && si->last == hi) {
> - raid1_rb_remove(si, &serial->serial_rb);
> - mempool_free(si, mddev->serial_info_pool);
> found = 1;
> break;
> }
> }
> if (!found)
> WARN(1, "The write IO is not recorded for serialization\n");
> + else {
> + raid1_rb_remove(si, &serial->serial_rb);
> + if (!list_empty(&si->waiters)) {
> + first_si = list_first_entry(&si->waiters,
> + struct serial_info, list_node);
> + list_del_init(&first_si->list_node);
> + list_splice_init(&si->waiters, &first_si->waiters);
> + raid1_rb_insert(first_si, &serial->serial_rb);
> + complete(&first_si->ready);
> + }
> + mempool_free(si, mddev->serial_info_pool);
> + }
> spin_unlock_irqrestore(&serial->serial_lock, flags);
> - wake_up(&serial->serial_io_wait);
> }
>
> /*
--
Thansk,
Kuai
^ permalink raw reply
* Re: [PATCH 1/2] md/raid1: fix the comparing region of interval tree
From: Yu Kuai @ 2026-02-11 2:18 UTC (permalink / raw)
To: Xiao Ni; +Cc: song, linux-raid, linan666, yukuai
In-Reply-To: <20260206053826.37416-2-xni@redhat.com>
Hi,
在 2026/2/6 13:38, Xiao Ni 写道:
> Interval tree uses [start, end] as a region which stores in the tree.
> In raid1, it uses the wrong end value. For example:
> bio(A,B) is too big and needs to be split to bio1(A,C-1), bio2(C,B).
> The region of bio1 is [A,C] and the region of bio2 is [C,B]. So bio1 and
> bio2 overlap which is not right.
>
> Fix this problem by using right end value of the region.
>
> Signed-off-by: Xiao Ni <xni@redhat.com>
> ---
> drivers/md/raid1.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
Perhaps also add a fix tag, with this:
Reviewed-by: Yu Kuai <yukuai@fnnas.com>
>
> diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
> index 867db18bc3ba..a41b1ec3d695 100644
> --- a/drivers/md/raid1.c
> +++ b/drivers/md/raid1.c
> @@ -62,7 +62,7 @@ static int check_and_add_serial(struct md_rdev *rdev, struct r1bio *r1_bio,
> unsigned long flags;
> int ret = 0;
> sector_t lo = r1_bio->sector;
> - sector_t hi = lo + r1_bio->sectors;
> + sector_t hi = lo + r1_bio->sectors - 1;
> struct serial_in_rdev *serial = &rdev->serial[idx];
>
> spin_lock_irqsave(&serial->serial_lock, flags);
> @@ -453,7 +453,7 @@ static void raid1_end_write_request(struct bio *bio)
> int mirror = find_bio_disk(r1_bio, bio);
> struct md_rdev *rdev = conf->mirrors[mirror].rdev;
> sector_t lo = r1_bio->sector;
> - sector_t hi = r1_bio->sector + r1_bio->sectors;
> + sector_t hi = r1_bio->sector + r1_bio->sectors - 1;
> bool ignore_error = !raid1_should_handle_error(bio) ||
> (bio->bi_status && bio_op(bio) == REQ_OP_DISCARD);
>
--
Thansk,
Kuai
^ permalink raw reply
* [PATCH v2] md/raid10: fix deadlock with check operation and nowait requests
From: Josh Hunt @ 2026-02-10 16:51 UTC (permalink / raw)
To: song, yukuai, linan122, linux-raid; +Cc: ncroxon, Josh Hunt, stable
When an array check is running it will raise the barrier at which point
normal requests will become blocked and increment the nr_pending value to
signal there is work pending inside of wait_barrier(). NOWAIT requests
do not block and so will return immediately with an error, and additionally
do not increment nr_pending in wait_barrier(). Upstream change
43806c3d5b9b ("raid10: cleanup memleak at raid10_make_request") added a
call to raid_end_bio_io() to fix a memory leak when NOWAIT requests hit
this condition. raid_end_bio_io() eventually calls allow_barrier() and
it will unconditionally do an atomic_dec_and_test(&conf->nr_pending) even
though the corresponding increment on nr_pending didn't happen in the
NOWAIT case.
This can be easily seen by starting a check operation while an application is
doing nowait IO on the same array. This results in a deadlocked state due to
nr_pending value underflowing and so the md resync thread gets stuck waiting
for nr_pending to == 0.
Output of r10conf state of the array when we hit this condition:
crash> struct r10conf.barrier,nr_pending,nr_waiting,nr_queued <addr of r10conf>
barrier = 1,
nr_pending = {
counter = -41
},
nr_waiting = 15,
nr_queued = 0,
Example of md_sync thread stuck waiting on raise_barrier() and other requests
stuck in wait_barrier():
md1_resync
[<0>] raise_barrier+0xce/0x1c0
[<0>] raid10_sync_request+0x1ca/0x1ed0
[<0>] md_do_sync+0x779/0x1110
[<0>] md_thread+0x90/0x160
[<0>] kthread+0xbe/0xf0
[<0>] ret_from_fork+0x34/0x50
[<0>] ret_from_fork_asm+0x1a/0x30
kworker/u1040:2+flush-253:4
[<0>] wait_barrier+0x1de/0x220
[<0>] regular_request_wait+0x30/0x180
[<0>] raid10_make_request+0x261/0x1000
[<0>] md_handle_request+0x13b/0x230
[<0>] __submit_bio+0x107/0x1f0
[<0>] submit_bio_noacct_nocheck+0x16f/0x390
[<0>] ext4_io_submit+0x24/0x40
[<0>] ext4_do_writepages+0x254/0xc80
[<0>] ext4_writepages+0x84/0x120
[<0>] do_writepages+0x7a/0x260
[<0>] __writeback_single_inode+0x3d/0x300
[<0>] writeback_sb_inodes+0x1dd/0x470
[<0>] __writeback_inodes_wb+0x4c/0xe0
[<0>] wb_writeback+0x18b/0x2d0
[<0>] wb_workfn+0x2a1/0x400
[<0>] process_one_work+0x149/0x330
[<0>] worker_thread+0x2d2/0x410
[<0>] kthread+0xbe/0xf0
[<0>] ret_from_fork+0x34/0x50
[<0>] ret_from_fork_asm+0x1a/0x30
Fixes: 43806c3d5b9b ("raid10: cleanup memleak at raid10_make_request")
Cc: stable@vger.kernel.org
Signed-off-by: Josh Hunt <johunt@akamai.com>
---
drivers/md/raid10.c | 40 +++++++++++++++++++++++++++-------------
1 file changed, 27 insertions(+), 13 deletions(-)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 9debb20cf129..b05066dde693 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -68,6 +68,7 @@
*/
static void allow_barrier(struct r10conf *conf);
+static void allow_barrier_nowait(struct r10conf *conf);
static void lower_barrier(struct r10conf *conf);
static int _enough(struct r10conf *conf, int previous, int ignore);
static int enough(struct r10conf *conf, int ignore);
@@ -317,7 +318,7 @@ static void reschedule_retry(struct r10bio *r10_bio)
* operation and are ready to return a success/failure code to the buffer
* cache layer.
*/
-static void raid_end_bio_io(struct r10bio *r10_bio)
+static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
{
struct bio *bio = r10_bio->master_bio;
struct r10conf *conf = r10_bio->mddev->private;
@@ -332,7 +333,10 @@ static void raid_end_bio_io(struct r10bio *r10_bio)
* Wake up any possible resync thread that waits for the device
* to go idle.
*/
- allow_barrier(conf);
+ if (adjust_pending)
+ allow_barrier(conf);
+ else
+ allow_barrier_nowait(conf);
free_r10bio(r10_bio);
}
@@ -414,7 +418,7 @@ static void raid10_end_read_request(struct bio *bio)
uptodate = 1;
}
if (uptodate) {
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, true);
rdev_dec_pending(rdev, conf->mddev);
} else {
/*
@@ -446,7 +450,7 @@ static void one_write_done(struct r10bio *r10_bio)
if (test_bit(R10BIO_MadeGood, &r10_bio->state))
reschedule_retry(r10_bio);
else
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, true);
}
}
}
@@ -1030,13 +1034,23 @@ static bool wait_barrier(struct r10conf *conf, bool nowait)
return ret;
}
-static void allow_barrier(struct r10conf *conf)
+static void __allow_barrier(struct r10conf *conf, bool adjust_pending)
{
- if ((atomic_dec_and_test(&conf->nr_pending)) ||
+ if ((adjust_pending && atomic_dec_and_test(&conf->nr_pending)) ||
(conf->array_freeze_pending))
wake_up_barrier(conf);
}
+static void allow_barrier(struct r10conf *conf)
+{
+ __allow_barrier(conf, true);
+}
+
+static void allow_barrier_nowait(struct r10conf *conf)
+{
+ __allow_barrier(conf, false);
+}
+
static void freeze_array(struct r10conf *conf, int extra)
{
/* stop syncio and normal IO and wait for everything to
@@ -1184,7 +1198,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
}
if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) {
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, false);
return;
}
@@ -1195,7 +1209,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
mdname(mddev), b,
(unsigned long long)r10_bio->sector);
}
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, true);
return;
}
if (err_rdev)
@@ -1240,7 +1254,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
return;
err_handle:
atomic_dec(&rdev->nr_pending);
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, true);
}
static void raid10_write_one_disk(struct mddev *mddev, struct r10bio *r10_bio,
@@ -1372,7 +1386,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio,
sectors = r10_bio->sectors;
if (!regular_request_wait(mddev, conf, bio, sectors)) {
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, false);
return;
}
@@ -1523,7 +1537,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio,
}
}
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, true);
}
static void __make_request(struct mddev *mddev, struct bio *bio, int sectors)
@@ -2952,7 +2966,7 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio)
if (test_bit(R10BIO_WriteError,
&r10_bio->state))
close_write(r10_bio);
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, true);
}
}
}
@@ -2987,7 +3001,7 @@ static void raid10d(struct md_thread *thread)
if (test_bit(R10BIO_WriteError,
&r10_bio->state))
close_write(r10_bio);
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, true);
}
}
--
2.34.1
^ permalink raw reply related
* Re: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
From: Josh Hunt @ 2026-02-10 11:56 UTC (permalink / raw)
To: kernel test robot, song, yukuai, linan122, linux-raid
Cc: oe-kbuild-all, ncroxon, stable
In-Reply-To: <202602101220.J4BofeDD-lkp@intel.com>
On 2/10/26 3:14 AM, kernel test robot wrote:
> !-------------------------------------------------------------------|
> This Message Is From an External Sender
> This message came from outside your organization.
> |-------------------------------------------------------------------!
>
> Hi Josh,
>
> kernel test robot noticed the following build errors:
>
> [auto build test ERROR on linus/master]
> [also build test ERROR on v6.19 next-20260209]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://urldefense.com/v3/__https://git-scm.com/docs/git-format-patch*_base_tree_information__;Iw!!GjvTz_vk!QT9JklLw7YgSInuzDS_0EDSnJkZTTG057ef2CiWO2UwIN0aUk9RGNuVtQ6XCy8sTbfadURM$ ]
>
> url: https://urldefense.com/v3/__https://github.com/intel-lab-lkp/linux/commits/Josh-Hunt/md-raid10-fix-deadlock-with-check-operation-and-nowait-requests/20260210-135305__;!!GjvTz_vk!QT9JklLw7YgSInuzDS_0EDSnJkZTTG057ef2CiWO2UwIN0aUk9RGNuVtQ6XCy8sTilUfWlw$
> base: linus/master
> patch link: https://urldefense.com/v3/__https://lore.kernel.org/r/20260210050942.3731656-1-johunt*40akamai.com__;JQ!!GjvTz_vk!QT9JklLw7YgSInuzDS_0EDSnJkZTTG057ef2CiWO2UwIN0aUk9RGNuVtQ6XCy8sT0QqCm3Y$
> patch subject: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
> config: x86_64-rhel-9.4 (https://urldefense.com/v3/__https://download.01.org/0day-ci/archive/20260210/202602101220.J4BofeDD-lkp@intel.com/config__;!!GjvTz_vk!QT9JklLw7YgSInuzDS_0EDSnJkZTTG057ef2CiWO2UwIN0aUk9RGNuVtQ6XCy8sTZp6wg4c$ )
> compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
> reproduce (this is a W=1 build): (https://urldefense.com/v3/__https://download.01.org/0day-ci/archive/20260210/202602101220.J4BofeDD-lkp@intel.com/reproduce__;!!GjvTz_vk!QT9JklLw7YgSInuzDS_0EDSnJkZTTG057ef2CiWO2UwIN0aUk9RGNuVtQ6XCy8sTFU3cM60$ )
>
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://urldefense.com/v3/__https://lore.kernel.org/oe-kbuild-all/202602101220.J4BofeDD-lkp@intel.com/__;!!GjvTz_vk!QT9JklLw7YgSInuzDS_0EDSnJkZTTG057ef2CiWO2UwIN0aUk9RGNuVtQ6XCy8sTUPVdYDI$
>
> All errors (new ones prefixed by >>):
>
> drivers/md/raid10.c: In function 'raid10_read_request':
>>> drivers/md/raid10.c:1257:9: error: too few arguments to function 'raid_end_bio_io'
> 1257 | raid_end_bio_io(r10_bio);
> | ^~~~~~~~~~~~~~~
> drivers/md/raid10.c:321:13: note: declared here
> 321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
> | ^~~~~~~~~~~~~~~
> drivers/md/raid10.c: In function 'raid10_write_request':
> drivers/md/raid10.c:1540:9: error: too few arguments to function 'raid_end_bio_io'
> 1540 | raid_end_bio_io(r10_bio);
> | ^~~~~~~~~~~~~~~
> drivers/md/raid10.c:321:13: note: declared here
> 321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
> | ^~~~~~~~~~~~~~~
>
>
> vim +/raid_end_bio_io +1257 drivers/md/raid10.c
>
> caea3c47ad5152 Guoqing Jiang 2018-12-07 1161
> bb5f1ed70bc3bb Robert LeBlanc 2016-12-05 1162 static void raid10_read_request(struct mddev *mddev, struct bio *bio,
> 820455238366a7 Yu Kuai 2023-06-22 1163 struct r10bio *r10_bio, bool io_accounting)
> ^1da177e4c3f41 Linus Torvalds 2005-04-16 1164 {
> e879a8793f915a NeilBrown 2011-10-11 1165 struct r10conf *conf = mddev->private;
> ^1da177e4c3f41 Linus Torvalds 2005-04-16 1166 struct bio *read_bio;
> d4432c23be957f NeilBrown 2011-07-28 1167 int max_sectors;
> bb5f1ed70bc3bb Robert LeBlanc 2016-12-05 1168 struct md_rdev *rdev;
> 545250f2480911 NeilBrown 2017-04-05 1169 char b[BDEVNAME_SIZE];
> 545250f2480911 NeilBrown 2017-04-05 1170 int slot = r10_bio->read_slot;
> 545250f2480911 NeilBrown 2017-04-05 1171 struct md_rdev *err_rdev = NULL;
> 545250f2480911 NeilBrown 2017-04-05 1172 gfp_t gfp = GFP_NOIO;
> 9b622e2bbcf049 Tomasz Majchrzak 2016-07-28 1173
> 93decc563637c4 Kevin Vigor 2020-11-06 1174 if (slot >= 0 && r10_bio->devs[slot].rdev) {
> 545250f2480911 NeilBrown 2017-04-05 1175 /*
> 545250f2480911 NeilBrown 2017-04-05 1176 * This is an error retry, but we cannot
> 545250f2480911 NeilBrown 2017-04-05 1177 * safely dereference the rdev in the r10_bio,
> 545250f2480911 NeilBrown 2017-04-05 1178 * we must use the one in conf.
> 545250f2480911 NeilBrown 2017-04-05 1179 * If it has already been disconnected (unlikely)
> 545250f2480911 NeilBrown 2017-04-05 1180 * we lose the device name in error messages.
> 545250f2480911 NeilBrown 2017-04-05 1181 */
> 545250f2480911 NeilBrown 2017-04-05 1182 int disk;
> 545250f2480911 NeilBrown 2017-04-05 1183 /*
> 545250f2480911 NeilBrown 2017-04-05 1184 * As we are blocking raid10, it is a little safer to
> 545250f2480911 NeilBrown 2017-04-05 1185 * use __GFP_HIGH.
> 545250f2480911 NeilBrown 2017-04-05 1186 */
> 545250f2480911 NeilBrown 2017-04-05 1187 gfp = GFP_NOIO | __GFP_HIGH;
> 545250f2480911 NeilBrown 2017-04-05 1188
> 545250f2480911 NeilBrown 2017-04-05 1189 disk = r10_bio->devs[slot].devnum;
> a448af25becf4b Yu Kuai 2023-11-25 1190 err_rdev = conf->mirrors[disk].rdev;
> 545250f2480911 NeilBrown 2017-04-05 1191 if (err_rdev)
> 900d156bac2bc4 Christoph Hellwig 2022-07-13 1192 snprintf(b, sizeof(b), "%pg", err_rdev->bdev);
> 545250f2480911 NeilBrown 2017-04-05 1193 else {
> 545250f2480911 NeilBrown 2017-04-05 1194 strcpy(b, "???");
> 545250f2480911 NeilBrown 2017-04-05 1195 /* This never gets dereferenced */
> 545250f2480911 NeilBrown 2017-04-05 1196 err_rdev = r10_bio->devs[slot].rdev;
> 545250f2480911 NeilBrown 2017-04-05 1197 }
> 545250f2480911 NeilBrown 2017-04-05 1198 }
> 856e08e23762df NeilBrown 2011-07-28 1199
> 43806c3d5b9bb7 Nigel Croxon 2025-07-03 1200 if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) {
> 4e9814d1943b0e Josh Hunt 2026-02-10 1201 raid_end_bio_io(r10_bio, false);
> c9aa889b035fca Vishal Verma 2021-12-21 1202 return;
> 43806c3d5b9bb7 Nigel Croxon 2025-07-03 1203 }
> 43806c3d5b9bb7 Nigel Croxon 2025-07-03 1204
> 96c3fd1f380237 NeilBrown 2011-12-23 1205 rdev = read_balance(conf, r10_bio, &max_sectors);
> 96c3fd1f380237 NeilBrown 2011-12-23 1206 if (!rdev) {
> 545250f2480911 NeilBrown 2017-04-05 1207 if (err_rdev) {
> 545250f2480911 NeilBrown 2017-04-05 1208 pr_crit_ratelimited("md/raid10:%s: %s: unrecoverable I/O read error for block %llu\n",
> 545250f2480911 NeilBrown 2017-04-05 1209 mdname(mddev), b,
> 545250f2480911 NeilBrown 2017-04-05 1210 (unsigned long long)r10_bio->sector);
> 545250f2480911 NeilBrown 2017-04-05 1211 }
> 4e9814d1943b0e Josh Hunt 2026-02-10 1212 raid_end_bio_io(r10_bio, true);
> 5a7bbad27a4103 Christoph Hellwig 2011-09-12 1213 return;
> ^1da177e4c3f41 Linus Torvalds 2005-04-16 1214 }
> 545250f2480911 NeilBrown 2017-04-05 1215 if (err_rdev)
> 913cce5a1e588e Christoph Hellwig 2022-05-12 1216 pr_err_ratelimited("md/raid10:%s: %pg: redirecting sector %llu to another mirror\n",
> 545250f2480911 NeilBrown 2017-04-05 1217 mdname(mddev),
> 913cce5a1e588e Christoph Hellwig 2022-05-12 1218 rdev->bdev,
> 545250f2480911 NeilBrown 2017-04-05 1219 (unsigned long long)r10_bio->sector);
> fc9977dd069e4f NeilBrown 2017-04-05 1220 if (max_sectors < bio_sectors(bio)) {
> e820d55cb99dd9 Guoqing Jiang 2018-12-19 1221 allow_barrier(conf);
> 6fc07785d9b892 Yu Kuai 2025-09-10 1222 bio = bio_submit_split_bioset(bio, max_sectors,
> 6fc07785d9b892 Yu Kuai 2025-09-10 1223 &conf->bio_split);
> c9aa889b035fca Vishal Verma 2021-12-21 1224 wait_barrier(conf, false);
> 6fc07785d9b892 Yu Kuai 2025-09-10 1225 if (!bio) {
> 6fc07785d9b892 Yu Kuai 2025-09-10 1226 set_bit(R10BIO_Returned, &r10_bio->state);
> 4cf58d95290973 John Garry 2024-11-11 1227 goto err_handle;
> 4cf58d95290973 John Garry 2024-11-11 1228 }
> 22f166218f7313 Yu Kuai 2025-09-10 1229
> fc9977dd069e4f NeilBrown 2017-04-05 1230 r10_bio->master_bio = bio;
> fc9977dd069e4f NeilBrown 2017-04-05 1231 r10_bio->sectors = max_sectors;
> fc9977dd069e4f NeilBrown 2017-04-05 1232 }
> 96c3fd1f380237 NeilBrown 2011-12-23 1233 slot = r10_bio->read_slot;
> ^1da177e4c3f41 Linus Torvalds 2005-04-16 1234
> 820455238366a7 Yu Kuai 2023-06-22 1235 if (io_accounting) {
> 820455238366a7 Yu Kuai 2023-06-22 1236 md_account_bio(mddev, &bio);
> 820455238366a7 Yu Kuai 2023-06-22 1237 r10_bio->master_bio = bio;
> 820455238366a7 Yu Kuai 2023-06-22 1238 }
> abfc426d1b2fb2 Christoph Hellwig 2022-02-02 1239 read_bio = bio_alloc_clone(rdev->bdev, bio, gfp, &mddev->bio_set);
> 5fa31c49928139 Zheng Qixing 2025-07-02 1240 read_bio->bi_opf &= ~REQ_NOWAIT;
> ^1da177e4c3f41 Linus Torvalds 2005-04-16 1241
> ^1da177e4c3f41 Linus Torvalds 2005-04-16 1242 r10_bio->devs[slot].bio = read_bio;
> abbf098e6e1e23 NeilBrown 2011-12-23 1243 r10_bio->devs[slot].rdev = rdev;
> ^1da177e4c3f41 Linus Torvalds 2005-04-16 1244
> 4f024f3797c43c Kent Overstreet 2013-10-11 1245 read_bio->bi_iter.bi_sector = r10_bio->devs[slot].addr +
> f8c9e74ff0832f NeilBrown 2012-05-21 1246 choose_data_offset(r10_bio, rdev);
> ^1da177e4c3f41 Linus Torvalds 2005-04-16 1247 read_bio->bi_end_io = raid10_end_read_request;
> 8d3ca83dcf9ca3 NeilBrown 2016-11-18 1248 if (test_bit(FailFast, &rdev->flags) &&
> 8d3ca83dcf9ca3 NeilBrown 2016-11-18 1249 test_bit(R10BIO_FailFast, &r10_bio->state))
> 8d3ca83dcf9ca3 NeilBrown 2016-11-18 1250 read_bio->bi_opf |= MD_FAILFAST;
> ^1da177e4c3f41 Linus Torvalds 2005-04-16 1251 read_bio->bi_private = r10_bio;
> c396b90e502691 Christoph Hellwig 2024-03-03 1252 mddev_trace_remap(mddev, read_bio, r10_bio->sector);
> ed00aabd5eb9fb Christoph Hellwig 2020-07-01 1253 submit_bio_noacct(read_bio);
> 5a7bbad27a4103 Christoph Hellwig 2011-09-12 1254 return;
> 4cf58d95290973 John Garry 2024-11-11 1255 err_handle:
> 4cf58d95290973 John Garry 2024-11-11 1256 atomic_dec(&rdev->nr_pending);
> 4cf58d95290973 John Garry 2024-11-11 @1257 raid_end_bio_io(r10_bio);
> ^1da177e4c3f41 Linus Torvalds 2005-04-16 1258 }
> ^1da177e4c3f41 Linus Torvalds 2005-04-16 1259
>
Apologies. I cherry-picked this from my 6.12.y branch which is where we
hit the issue, but looks like I forgot to build test it. Will send a v2.
Josh
^ permalink raw reply
* Re: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
From: kernel test robot @ 2026-02-10 11:14 UTC (permalink / raw)
To: Josh Hunt, song, yukuai, linan122, linux-raid
Cc: oe-kbuild-all, ncroxon, Josh Hunt, stable
In-Reply-To: <20260210050942.3731656-1-johunt@akamai.com>
Hi Josh,
kernel test robot noticed the following build errors:
[auto build test ERROR on linus/master]
[also build test ERROR on v6.19 next-20260209]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Josh-Hunt/md-raid10-fix-deadlock-with-check-operation-and-nowait-requests/20260210-135305
base: linus/master
patch link: https://lore.kernel.org/r/20260210050942.3731656-1-johunt%40akamai.com
patch subject: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
config: x86_64-rhel-9.4 (https://download.01.org/0day-ci/archive/20260210/202602101220.J4BofeDD-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260210/202602101220.J4BofeDD-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602101220.J4BofeDD-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/md/raid10.c: In function 'raid10_read_request':
>> drivers/md/raid10.c:1257:9: error: too few arguments to function 'raid_end_bio_io'
1257 | raid_end_bio_io(r10_bio);
| ^~~~~~~~~~~~~~~
drivers/md/raid10.c:321:13: note: declared here
321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
| ^~~~~~~~~~~~~~~
drivers/md/raid10.c: In function 'raid10_write_request':
drivers/md/raid10.c:1540:9: error: too few arguments to function 'raid_end_bio_io'
1540 | raid_end_bio_io(r10_bio);
| ^~~~~~~~~~~~~~~
drivers/md/raid10.c:321:13: note: declared here
321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
| ^~~~~~~~~~~~~~~
vim +/raid_end_bio_io +1257 drivers/md/raid10.c
caea3c47ad5152 Guoqing Jiang 2018-12-07 1161
bb5f1ed70bc3bb Robert LeBlanc 2016-12-05 1162 static void raid10_read_request(struct mddev *mddev, struct bio *bio,
820455238366a7 Yu Kuai 2023-06-22 1163 struct r10bio *r10_bio, bool io_accounting)
^1da177e4c3f41 Linus Torvalds 2005-04-16 1164 {
e879a8793f915a NeilBrown 2011-10-11 1165 struct r10conf *conf = mddev->private;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1166 struct bio *read_bio;
d4432c23be957f NeilBrown 2011-07-28 1167 int max_sectors;
bb5f1ed70bc3bb Robert LeBlanc 2016-12-05 1168 struct md_rdev *rdev;
545250f2480911 NeilBrown 2017-04-05 1169 char b[BDEVNAME_SIZE];
545250f2480911 NeilBrown 2017-04-05 1170 int slot = r10_bio->read_slot;
545250f2480911 NeilBrown 2017-04-05 1171 struct md_rdev *err_rdev = NULL;
545250f2480911 NeilBrown 2017-04-05 1172 gfp_t gfp = GFP_NOIO;
9b622e2bbcf049 Tomasz Majchrzak 2016-07-28 1173
93decc563637c4 Kevin Vigor 2020-11-06 1174 if (slot >= 0 && r10_bio->devs[slot].rdev) {
545250f2480911 NeilBrown 2017-04-05 1175 /*
545250f2480911 NeilBrown 2017-04-05 1176 * This is an error retry, but we cannot
545250f2480911 NeilBrown 2017-04-05 1177 * safely dereference the rdev in the r10_bio,
545250f2480911 NeilBrown 2017-04-05 1178 * we must use the one in conf.
545250f2480911 NeilBrown 2017-04-05 1179 * If it has already been disconnected (unlikely)
545250f2480911 NeilBrown 2017-04-05 1180 * we lose the device name in error messages.
545250f2480911 NeilBrown 2017-04-05 1181 */
545250f2480911 NeilBrown 2017-04-05 1182 int disk;
545250f2480911 NeilBrown 2017-04-05 1183 /*
545250f2480911 NeilBrown 2017-04-05 1184 * As we are blocking raid10, it is a little safer to
545250f2480911 NeilBrown 2017-04-05 1185 * use __GFP_HIGH.
545250f2480911 NeilBrown 2017-04-05 1186 */
545250f2480911 NeilBrown 2017-04-05 1187 gfp = GFP_NOIO | __GFP_HIGH;
545250f2480911 NeilBrown 2017-04-05 1188
545250f2480911 NeilBrown 2017-04-05 1189 disk = r10_bio->devs[slot].devnum;
a448af25becf4b Yu Kuai 2023-11-25 1190 err_rdev = conf->mirrors[disk].rdev;
545250f2480911 NeilBrown 2017-04-05 1191 if (err_rdev)
900d156bac2bc4 Christoph Hellwig 2022-07-13 1192 snprintf(b, sizeof(b), "%pg", err_rdev->bdev);
545250f2480911 NeilBrown 2017-04-05 1193 else {
545250f2480911 NeilBrown 2017-04-05 1194 strcpy(b, "???");
545250f2480911 NeilBrown 2017-04-05 1195 /* This never gets dereferenced */
545250f2480911 NeilBrown 2017-04-05 1196 err_rdev = r10_bio->devs[slot].rdev;
545250f2480911 NeilBrown 2017-04-05 1197 }
545250f2480911 NeilBrown 2017-04-05 1198 }
856e08e23762df NeilBrown 2011-07-28 1199
43806c3d5b9bb7 Nigel Croxon 2025-07-03 1200 if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) {
4e9814d1943b0e Josh Hunt 2026-02-10 1201 raid_end_bio_io(r10_bio, false);
c9aa889b035fca Vishal Verma 2021-12-21 1202 return;
43806c3d5b9bb7 Nigel Croxon 2025-07-03 1203 }
43806c3d5b9bb7 Nigel Croxon 2025-07-03 1204
96c3fd1f380237 NeilBrown 2011-12-23 1205 rdev = read_balance(conf, r10_bio, &max_sectors);
96c3fd1f380237 NeilBrown 2011-12-23 1206 if (!rdev) {
545250f2480911 NeilBrown 2017-04-05 1207 if (err_rdev) {
545250f2480911 NeilBrown 2017-04-05 1208 pr_crit_ratelimited("md/raid10:%s: %s: unrecoverable I/O read error for block %llu\n",
545250f2480911 NeilBrown 2017-04-05 1209 mdname(mddev), b,
545250f2480911 NeilBrown 2017-04-05 1210 (unsigned long long)r10_bio->sector);
545250f2480911 NeilBrown 2017-04-05 1211 }
4e9814d1943b0e Josh Hunt 2026-02-10 1212 raid_end_bio_io(r10_bio, true);
5a7bbad27a4103 Christoph Hellwig 2011-09-12 1213 return;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1214 }
545250f2480911 NeilBrown 2017-04-05 1215 if (err_rdev)
913cce5a1e588e Christoph Hellwig 2022-05-12 1216 pr_err_ratelimited("md/raid10:%s: %pg: redirecting sector %llu to another mirror\n",
545250f2480911 NeilBrown 2017-04-05 1217 mdname(mddev),
913cce5a1e588e Christoph Hellwig 2022-05-12 1218 rdev->bdev,
545250f2480911 NeilBrown 2017-04-05 1219 (unsigned long long)r10_bio->sector);
fc9977dd069e4f NeilBrown 2017-04-05 1220 if (max_sectors < bio_sectors(bio)) {
e820d55cb99dd9 Guoqing Jiang 2018-12-19 1221 allow_barrier(conf);
6fc07785d9b892 Yu Kuai 2025-09-10 1222 bio = bio_submit_split_bioset(bio, max_sectors,
6fc07785d9b892 Yu Kuai 2025-09-10 1223 &conf->bio_split);
c9aa889b035fca Vishal Verma 2021-12-21 1224 wait_barrier(conf, false);
6fc07785d9b892 Yu Kuai 2025-09-10 1225 if (!bio) {
6fc07785d9b892 Yu Kuai 2025-09-10 1226 set_bit(R10BIO_Returned, &r10_bio->state);
4cf58d95290973 John Garry 2024-11-11 1227 goto err_handle;
4cf58d95290973 John Garry 2024-11-11 1228 }
22f166218f7313 Yu Kuai 2025-09-10 1229
fc9977dd069e4f NeilBrown 2017-04-05 1230 r10_bio->master_bio = bio;
fc9977dd069e4f NeilBrown 2017-04-05 1231 r10_bio->sectors = max_sectors;
fc9977dd069e4f NeilBrown 2017-04-05 1232 }
96c3fd1f380237 NeilBrown 2011-12-23 1233 slot = r10_bio->read_slot;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1234
820455238366a7 Yu Kuai 2023-06-22 1235 if (io_accounting) {
820455238366a7 Yu Kuai 2023-06-22 1236 md_account_bio(mddev, &bio);
820455238366a7 Yu Kuai 2023-06-22 1237 r10_bio->master_bio = bio;
820455238366a7 Yu Kuai 2023-06-22 1238 }
abfc426d1b2fb2 Christoph Hellwig 2022-02-02 1239 read_bio = bio_alloc_clone(rdev->bdev, bio, gfp, &mddev->bio_set);
5fa31c49928139 Zheng Qixing 2025-07-02 1240 read_bio->bi_opf &= ~REQ_NOWAIT;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1241
^1da177e4c3f41 Linus Torvalds 2005-04-16 1242 r10_bio->devs[slot].bio = read_bio;
abbf098e6e1e23 NeilBrown 2011-12-23 1243 r10_bio->devs[slot].rdev = rdev;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1244
4f024f3797c43c Kent Overstreet 2013-10-11 1245 read_bio->bi_iter.bi_sector = r10_bio->devs[slot].addr +
f8c9e74ff0832f NeilBrown 2012-05-21 1246 choose_data_offset(r10_bio, rdev);
^1da177e4c3f41 Linus Torvalds 2005-04-16 1247 read_bio->bi_end_io = raid10_end_read_request;
8d3ca83dcf9ca3 NeilBrown 2016-11-18 1248 if (test_bit(FailFast, &rdev->flags) &&
8d3ca83dcf9ca3 NeilBrown 2016-11-18 1249 test_bit(R10BIO_FailFast, &r10_bio->state))
8d3ca83dcf9ca3 NeilBrown 2016-11-18 1250 read_bio->bi_opf |= MD_FAILFAST;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1251 read_bio->bi_private = r10_bio;
c396b90e502691 Christoph Hellwig 2024-03-03 1252 mddev_trace_remap(mddev, read_bio, r10_bio->sector);
ed00aabd5eb9fb Christoph Hellwig 2020-07-01 1253 submit_bio_noacct(read_bio);
5a7bbad27a4103 Christoph Hellwig 2011-09-12 1254 return;
4cf58d95290973 John Garry 2024-11-11 1255 err_handle:
4cf58d95290973 John Garry 2024-11-11 1256 atomic_dec(&rdev->nr_pending);
4cf58d95290973 John Garry 2024-11-11 @1257 raid_end_bio_io(r10_bio);
^1da177e4c3f41 Linus Torvalds 2005-04-16 1258 }
^1da177e4c3f41 Linus Torvalds 2005-04-16 1259
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
From: kernel test robot @ 2026-02-10 10:18 UTC (permalink / raw)
To: Josh Hunt, song, yukuai, linan122, linux-raid
Cc: oe-kbuild-all, ncroxon, Josh Hunt, stable
In-Reply-To: <20260210050942.3731656-1-johunt@akamai.com>
Hi Josh,
kernel test robot noticed the following build errors:
[auto build test ERROR on linus/master]
[also build test ERROR on v6.19 next-20260209]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Josh-Hunt/md-raid10-fix-deadlock-with-check-operation-and-nowait-requests/20260210-135305
base: linus/master
patch link: https://lore.kernel.org/r/20260210050942.3731656-1-johunt%40akamai.com
patch subject: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
config: m68k-defconfig (https://download.01.org/0day-ci/archive/20260210/202602101850.DC3BeMD5-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260210/202602101850.DC3BeMD5-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602101850.DC3BeMD5-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/md/raid10.c: In function 'raid10_read_request':
>> drivers/md/raid10.c:1257:9: error: too few arguments to function 'raid_end_bio_io'; expected 2, have 1
1257 | raid_end_bio_io(r10_bio);
| ^~~~~~~~~~~~~~~
drivers/md/raid10.c:321:13: note: declared here
321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
| ^~~~~~~~~~~~~~~
drivers/md/raid10.c: In function 'raid10_write_request':
drivers/md/raid10.c:1540:9: error: too few arguments to function 'raid_end_bio_io'; expected 2, have 1
1540 | raid_end_bio_io(r10_bio);
| ^~~~~~~~~~~~~~~
drivers/md/raid10.c:321:13: note: declared here
321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
| ^~~~~~~~~~~~~~~
vim +/raid_end_bio_io +1257 drivers/md/raid10.c
caea3c47ad5152 Guoqing Jiang 2018-12-07 1161
bb5f1ed70bc3bb Robert LeBlanc 2016-12-05 1162 static void raid10_read_request(struct mddev *mddev, struct bio *bio,
820455238366a7 Yu Kuai 2023-06-22 1163 struct r10bio *r10_bio, bool io_accounting)
^1da177e4c3f41 Linus Torvalds 2005-04-16 1164 {
e879a8793f915a NeilBrown 2011-10-11 1165 struct r10conf *conf = mddev->private;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1166 struct bio *read_bio;
d4432c23be957f NeilBrown 2011-07-28 1167 int max_sectors;
bb5f1ed70bc3bb Robert LeBlanc 2016-12-05 1168 struct md_rdev *rdev;
545250f2480911 NeilBrown 2017-04-05 1169 char b[BDEVNAME_SIZE];
545250f2480911 NeilBrown 2017-04-05 1170 int slot = r10_bio->read_slot;
545250f2480911 NeilBrown 2017-04-05 1171 struct md_rdev *err_rdev = NULL;
545250f2480911 NeilBrown 2017-04-05 1172 gfp_t gfp = GFP_NOIO;
9b622e2bbcf049 Tomasz Majchrzak 2016-07-28 1173
93decc563637c4 Kevin Vigor 2020-11-06 1174 if (slot >= 0 && r10_bio->devs[slot].rdev) {
545250f2480911 NeilBrown 2017-04-05 1175 /*
545250f2480911 NeilBrown 2017-04-05 1176 * This is an error retry, but we cannot
545250f2480911 NeilBrown 2017-04-05 1177 * safely dereference the rdev in the r10_bio,
545250f2480911 NeilBrown 2017-04-05 1178 * we must use the one in conf.
545250f2480911 NeilBrown 2017-04-05 1179 * If it has already been disconnected (unlikely)
545250f2480911 NeilBrown 2017-04-05 1180 * we lose the device name in error messages.
545250f2480911 NeilBrown 2017-04-05 1181 */
545250f2480911 NeilBrown 2017-04-05 1182 int disk;
545250f2480911 NeilBrown 2017-04-05 1183 /*
545250f2480911 NeilBrown 2017-04-05 1184 * As we are blocking raid10, it is a little safer to
545250f2480911 NeilBrown 2017-04-05 1185 * use __GFP_HIGH.
545250f2480911 NeilBrown 2017-04-05 1186 */
545250f2480911 NeilBrown 2017-04-05 1187 gfp = GFP_NOIO | __GFP_HIGH;
545250f2480911 NeilBrown 2017-04-05 1188
545250f2480911 NeilBrown 2017-04-05 1189 disk = r10_bio->devs[slot].devnum;
a448af25becf4b Yu Kuai 2023-11-25 1190 err_rdev = conf->mirrors[disk].rdev;
545250f2480911 NeilBrown 2017-04-05 1191 if (err_rdev)
900d156bac2bc4 Christoph Hellwig 2022-07-13 1192 snprintf(b, sizeof(b), "%pg", err_rdev->bdev);
545250f2480911 NeilBrown 2017-04-05 1193 else {
545250f2480911 NeilBrown 2017-04-05 1194 strcpy(b, "???");
545250f2480911 NeilBrown 2017-04-05 1195 /* This never gets dereferenced */
545250f2480911 NeilBrown 2017-04-05 1196 err_rdev = r10_bio->devs[slot].rdev;
545250f2480911 NeilBrown 2017-04-05 1197 }
545250f2480911 NeilBrown 2017-04-05 1198 }
856e08e23762df NeilBrown 2011-07-28 1199
43806c3d5b9bb7 Nigel Croxon 2025-07-03 1200 if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) {
4e9814d1943b0e Josh Hunt 2026-02-10 1201 raid_end_bio_io(r10_bio, false);
c9aa889b035fca Vishal Verma 2021-12-21 1202 return;
43806c3d5b9bb7 Nigel Croxon 2025-07-03 1203 }
43806c3d5b9bb7 Nigel Croxon 2025-07-03 1204
96c3fd1f380237 NeilBrown 2011-12-23 1205 rdev = read_balance(conf, r10_bio, &max_sectors);
96c3fd1f380237 NeilBrown 2011-12-23 1206 if (!rdev) {
545250f2480911 NeilBrown 2017-04-05 1207 if (err_rdev) {
545250f2480911 NeilBrown 2017-04-05 1208 pr_crit_ratelimited("md/raid10:%s: %s: unrecoverable I/O read error for block %llu\n",
545250f2480911 NeilBrown 2017-04-05 1209 mdname(mddev), b,
545250f2480911 NeilBrown 2017-04-05 1210 (unsigned long long)r10_bio->sector);
545250f2480911 NeilBrown 2017-04-05 1211 }
4e9814d1943b0e Josh Hunt 2026-02-10 1212 raid_end_bio_io(r10_bio, true);
5a7bbad27a4103 Christoph Hellwig 2011-09-12 1213 return;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1214 }
545250f2480911 NeilBrown 2017-04-05 1215 if (err_rdev)
913cce5a1e588e Christoph Hellwig 2022-05-12 1216 pr_err_ratelimited("md/raid10:%s: %pg: redirecting sector %llu to another mirror\n",
545250f2480911 NeilBrown 2017-04-05 1217 mdname(mddev),
913cce5a1e588e Christoph Hellwig 2022-05-12 1218 rdev->bdev,
545250f2480911 NeilBrown 2017-04-05 1219 (unsigned long long)r10_bio->sector);
fc9977dd069e4f NeilBrown 2017-04-05 1220 if (max_sectors < bio_sectors(bio)) {
e820d55cb99dd9 Guoqing Jiang 2018-12-19 1221 allow_barrier(conf);
6fc07785d9b892 Yu Kuai 2025-09-10 1222 bio = bio_submit_split_bioset(bio, max_sectors,
6fc07785d9b892 Yu Kuai 2025-09-10 1223 &conf->bio_split);
c9aa889b035fca Vishal Verma 2021-12-21 1224 wait_barrier(conf, false);
6fc07785d9b892 Yu Kuai 2025-09-10 1225 if (!bio) {
6fc07785d9b892 Yu Kuai 2025-09-10 1226 set_bit(R10BIO_Returned, &r10_bio->state);
4cf58d95290973 John Garry 2024-11-11 1227 goto err_handle;
4cf58d95290973 John Garry 2024-11-11 1228 }
22f166218f7313 Yu Kuai 2025-09-10 1229
fc9977dd069e4f NeilBrown 2017-04-05 1230 r10_bio->master_bio = bio;
fc9977dd069e4f NeilBrown 2017-04-05 1231 r10_bio->sectors = max_sectors;
fc9977dd069e4f NeilBrown 2017-04-05 1232 }
96c3fd1f380237 NeilBrown 2011-12-23 1233 slot = r10_bio->read_slot;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1234
820455238366a7 Yu Kuai 2023-06-22 1235 if (io_accounting) {
820455238366a7 Yu Kuai 2023-06-22 1236 md_account_bio(mddev, &bio);
820455238366a7 Yu Kuai 2023-06-22 1237 r10_bio->master_bio = bio;
820455238366a7 Yu Kuai 2023-06-22 1238 }
abfc426d1b2fb2 Christoph Hellwig 2022-02-02 1239 read_bio = bio_alloc_clone(rdev->bdev, bio, gfp, &mddev->bio_set);
5fa31c49928139 Zheng Qixing 2025-07-02 1240 read_bio->bi_opf &= ~REQ_NOWAIT;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1241
^1da177e4c3f41 Linus Torvalds 2005-04-16 1242 r10_bio->devs[slot].bio = read_bio;
abbf098e6e1e23 NeilBrown 2011-12-23 1243 r10_bio->devs[slot].rdev = rdev;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1244
4f024f3797c43c Kent Overstreet 2013-10-11 1245 read_bio->bi_iter.bi_sector = r10_bio->devs[slot].addr +
f8c9e74ff0832f NeilBrown 2012-05-21 1246 choose_data_offset(r10_bio, rdev);
^1da177e4c3f41 Linus Torvalds 2005-04-16 1247 read_bio->bi_end_io = raid10_end_read_request;
8d3ca83dcf9ca3 NeilBrown 2016-11-18 1248 if (test_bit(FailFast, &rdev->flags) &&
8d3ca83dcf9ca3 NeilBrown 2016-11-18 1249 test_bit(R10BIO_FailFast, &r10_bio->state))
8d3ca83dcf9ca3 NeilBrown 2016-11-18 1250 read_bio->bi_opf |= MD_FAILFAST;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1251 read_bio->bi_private = r10_bio;
c396b90e502691 Christoph Hellwig 2024-03-03 1252 mddev_trace_remap(mddev, read_bio, r10_bio->sector);
ed00aabd5eb9fb Christoph Hellwig 2020-07-01 1253 submit_bio_noacct(read_bio);
5a7bbad27a4103 Christoph Hellwig 2011-09-12 1254 return;
4cf58d95290973 John Garry 2024-11-11 1255 err_handle:
4cf58d95290973 John Garry 2024-11-11 1256 atomic_dec(&rdev->nr_pending);
4cf58d95290973 John Garry 2024-11-11 @1257 raid_end_bio_io(r10_bio);
^1da177e4c3f41 Linus Torvalds 2005-04-16 1258 }
^1da177e4c3f41 Linus Torvalds 2005-04-16 1259
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
From: kernel test robot @ 2026-02-10 10:17 UTC (permalink / raw)
To: Josh Hunt, song, yukuai, linan122, linux-raid
Cc: oe-kbuild-all, ncroxon, Josh Hunt, stable
In-Reply-To: <20260210050942.3731656-1-johunt@akamai.com>
Hi Josh,
kernel test robot noticed the following build errors:
[auto build test ERROR on linus/master]
[also build test ERROR on v6.19 next-20260209]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Josh-Hunt/md-raid10-fix-deadlock-with-check-operation-and-nowait-requests/20260210-135305
base: linus/master
patch link: https://lore.kernel.org/r/20260210050942.3731656-1-johunt%40akamai.com
patch subject: [PATCH] md/raid10: fix deadlock with check operation and nowait requests
config: nios2-allmodconfig (https://download.01.org/0day-ci/archive/20260210/202602101844.0pRyZv4D-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260210/202602101844.0pRyZv4D-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602101844.0pRyZv4D-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/md/raid10.c: In function 'raid10_read_request':
>> drivers/md/raid10.c:1257:9: error: too few arguments to function 'raid_end_bio_io'
1257 | raid_end_bio_io(r10_bio);
| ^~~~~~~~~~~~~~~
drivers/md/raid10.c:321:13: note: declared here
321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
| ^~~~~~~~~~~~~~~
drivers/md/raid10.c: In function 'raid10_write_request':
drivers/md/raid10.c:1540:9: error: too few arguments to function 'raid_end_bio_io'
1540 | raid_end_bio_io(r10_bio);
| ^~~~~~~~~~~~~~~
drivers/md/raid10.c:321:13: note: declared here
321 | static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
| ^~~~~~~~~~~~~~~
vim +/raid_end_bio_io +1257 drivers/md/raid10.c
caea3c47ad5152 Guoqing Jiang 2018-12-07 1161
bb5f1ed70bc3bb Robert LeBlanc 2016-12-05 1162 static void raid10_read_request(struct mddev *mddev, struct bio *bio,
820455238366a7 Yu Kuai 2023-06-22 1163 struct r10bio *r10_bio, bool io_accounting)
^1da177e4c3f41 Linus Torvalds 2005-04-16 1164 {
e879a8793f915a NeilBrown 2011-10-11 1165 struct r10conf *conf = mddev->private;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1166 struct bio *read_bio;
d4432c23be957f NeilBrown 2011-07-28 1167 int max_sectors;
bb5f1ed70bc3bb Robert LeBlanc 2016-12-05 1168 struct md_rdev *rdev;
545250f2480911 NeilBrown 2017-04-05 1169 char b[BDEVNAME_SIZE];
545250f2480911 NeilBrown 2017-04-05 1170 int slot = r10_bio->read_slot;
545250f2480911 NeilBrown 2017-04-05 1171 struct md_rdev *err_rdev = NULL;
545250f2480911 NeilBrown 2017-04-05 1172 gfp_t gfp = GFP_NOIO;
9b622e2bbcf049 Tomasz Majchrzak 2016-07-28 1173
93decc563637c4 Kevin Vigor 2020-11-06 1174 if (slot >= 0 && r10_bio->devs[slot].rdev) {
545250f2480911 NeilBrown 2017-04-05 1175 /*
545250f2480911 NeilBrown 2017-04-05 1176 * This is an error retry, but we cannot
545250f2480911 NeilBrown 2017-04-05 1177 * safely dereference the rdev in the r10_bio,
545250f2480911 NeilBrown 2017-04-05 1178 * we must use the one in conf.
545250f2480911 NeilBrown 2017-04-05 1179 * If it has already been disconnected (unlikely)
545250f2480911 NeilBrown 2017-04-05 1180 * we lose the device name in error messages.
545250f2480911 NeilBrown 2017-04-05 1181 */
545250f2480911 NeilBrown 2017-04-05 1182 int disk;
545250f2480911 NeilBrown 2017-04-05 1183 /*
545250f2480911 NeilBrown 2017-04-05 1184 * As we are blocking raid10, it is a little safer to
545250f2480911 NeilBrown 2017-04-05 1185 * use __GFP_HIGH.
545250f2480911 NeilBrown 2017-04-05 1186 */
545250f2480911 NeilBrown 2017-04-05 1187 gfp = GFP_NOIO | __GFP_HIGH;
545250f2480911 NeilBrown 2017-04-05 1188
545250f2480911 NeilBrown 2017-04-05 1189 disk = r10_bio->devs[slot].devnum;
a448af25becf4b Yu Kuai 2023-11-25 1190 err_rdev = conf->mirrors[disk].rdev;
545250f2480911 NeilBrown 2017-04-05 1191 if (err_rdev)
900d156bac2bc4 Christoph Hellwig 2022-07-13 1192 snprintf(b, sizeof(b), "%pg", err_rdev->bdev);
545250f2480911 NeilBrown 2017-04-05 1193 else {
545250f2480911 NeilBrown 2017-04-05 1194 strcpy(b, "???");
545250f2480911 NeilBrown 2017-04-05 1195 /* This never gets dereferenced */
545250f2480911 NeilBrown 2017-04-05 1196 err_rdev = r10_bio->devs[slot].rdev;
545250f2480911 NeilBrown 2017-04-05 1197 }
545250f2480911 NeilBrown 2017-04-05 1198 }
856e08e23762df NeilBrown 2011-07-28 1199
43806c3d5b9bb7 Nigel Croxon 2025-07-03 1200 if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) {
4e9814d1943b0e Josh Hunt 2026-02-10 1201 raid_end_bio_io(r10_bio, false);
c9aa889b035fca Vishal Verma 2021-12-21 1202 return;
43806c3d5b9bb7 Nigel Croxon 2025-07-03 1203 }
43806c3d5b9bb7 Nigel Croxon 2025-07-03 1204
96c3fd1f380237 NeilBrown 2011-12-23 1205 rdev = read_balance(conf, r10_bio, &max_sectors);
96c3fd1f380237 NeilBrown 2011-12-23 1206 if (!rdev) {
545250f2480911 NeilBrown 2017-04-05 1207 if (err_rdev) {
545250f2480911 NeilBrown 2017-04-05 1208 pr_crit_ratelimited("md/raid10:%s: %s: unrecoverable I/O read error for block %llu\n",
545250f2480911 NeilBrown 2017-04-05 1209 mdname(mddev), b,
545250f2480911 NeilBrown 2017-04-05 1210 (unsigned long long)r10_bio->sector);
545250f2480911 NeilBrown 2017-04-05 1211 }
4e9814d1943b0e Josh Hunt 2026-02-10 1212 raid_end_bio_io(r10_bio, true);
5a7bbad27a4103 Christoph Hellwig 2011-09-12 1213 return;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1214 }
545250f2480911 NeilBrown 2017-04-05 1215 if (err_rdev)
913cce5a1e588e Christoph Hellwig 2022-05-12 1216 pr_err_ratelimited("md/raid10:%s: %pg: redirecting sector %llu to another mirror\n",
545250f2480911 NeilBrown 2017-04-05 1217 mdname(mddev),
913cce5a1e588e Christoph Hellwig 2022-05-12 1218 rdev->bdev,
545250f2480911 NeilBrown 2017-04-05 1219 (unsigned long long)r10_bio->sector);
fc9977dd069e4f NeilBrown 2017-04-05 1220 if (max_sectors < bio_sectors(bio)) {
e820d55cb99dd9 Guoqing Jiang 2018-12-19 1221 allow_barrier(conf);
6fc07785d9b892 Yu Kuai 2025-09-10 1222 bio = bio_submit_split_bioset(bio, max_sectors,
6fc07785d9b892 Yu Kuai 2025-09-10 1223 &conf->bio_split);
c9aa889b035fca Vishal Verma 2021-12-21 1224 wait_barrier(conf, false);
6fc07785d9b892 Yu Kuai 2025-09-10 1225 if (!bio) {
6fc07785d9b892 Yu Kuai 2025-09-10 1226 set_bit(R10BIO_Returned, &r10_bio->state);
4cf58d95290973 John Garry 2024-11-11 1227 goto err_handle;
4cf58d95290973 John Garry 2024-11-11 1228 }
22f166218f7313 Yu Kuai 2025-09-10 1229
fc9977dd069e4f NeilBrown 2017-04-05 1230 r10_bio->master_bio = bio;
fc9977dd069e4f NeilBrown 2017-04-05 1231 r10_bio->sectors = max_sectors;
fc9977dd069e4f NeilBrown 2017-04-05 1232 }
96c3fd1f380237 NeilBrown 2011-12-23 1233 slot = r10_bio->read_slot;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1234
820455238366a7 Yu Kuai 2023-06-22 1235 if (io_accounting) {
820455238366a7 Yu Kuai 2023-06-22 1236 md_account_bio(mddev, &bio);
820455238366a7 Yu Kuai 2023-06-22 1237 r10_bio->master_bio = bio;
820455238366a7 Yu Kuai 2023-06-22 1238 }
abfc426d1b2fb2 Christoph Hellwig 2022-02-02 1239 read_bio = bio_alloc_clone(rdev->bdev, bio, gfp, &mddev->bio_set);
5fa31c49928139 Zheng Qixing 2025-07-02 1240 read_bio->bi_opf &= ~REQ_NOWAIT;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1241
^1da177e4c3f41 Linus Torvalds 2005-04-16 1242 r10_bio->devs[slot].bio = read_bio;
abbf098e6e1e23 NeilBrown 2011-12-23 1243 r10_bio->devs[slot].rdev = rdev;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1244
4f024f3797c43c Kent Overstreet 2013-10-11 1245 read_bio->bi_iter.bi_sector = r10_bio->devs[slot].addr +
f8c9e74ff0832f NeilBrown 2012-05-21 1246 choose_data_offset(r10_bio, rdev);
^1da177e4c3f41 Linus Torvalds 2005-04-16 1247 read_bio->bi_end_io = raid10_end_read_request;
8d3ca83dcf9ca3 NeilBrown 2016-11-18 1248 if (test_bit(FailFast, &rdev->flags) &&
8d3ca83dcf9ca3 NeilBrown 2016-11-18 1249 test_bit(R10BIO_FailFast, &r10_bio->state))
8d3ca83dcf9ca3 NeilBrown 2016-11-18 1250 read_bio->bi_opf |= MD_FAILFAST;
^1da177e4c3f41 Linus Torvalds 2005-04-16 1251 read_bio->bi_private = r10_bio;
c396b90e502691 Christoph Hellwig 2024-03-03 1252 mddev_trace_remap(mddev, read_bio, r10_bio->sector);
ed00aabd5eb9fb Christoph Hellwig 2020-07-01 1253 submit_bio_noacct(read_bio);
5a7bbad27a4103 Christoph Hellwig 2011-09-12 1254 return;
4cf58d95290973 John Garry 2024-11-11 1255 err_handle:
4cf58d95290973 John Garry 2024-11-11 1256 atomic_dec(&rdev->nr_pending);
4cf58d95290973 John Garry 2024-11-11 @1257 raid_end_bio_io(r10_bio);
^1da177e4c3f41 Linus Torvalds 2005-04-16 1258 }
^1da177e4c3f41 Linus Torvalds 2005-04-16 1259
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* [PATCH] md: suppress spurious superblock update error message for dm-raid
From: Chen Cheng @ 2026-02-10 13:38 UTC (permalink / raw)
To: linux-raid; +Cc: linux-kernel, pmenzel, tj.iam.tj, Chen Cheng
dm-raid has external metadata management (mddev->external = 1) and
no persistent superblock (mddev->persistent = 0). For these arrays,
there's no superblock to update, so the error message is spurious.
The error appears as:
[ 123.456789] md_update_sb: can't update sb for read-only array md0
Link: https://lore.kernel.org/all/20260128082430.96788-1-tj.iam.tj@proton.me/
Fixes: 6a5cb53aaa1d ("md: don't ignore read-only array in md_update_sb()")
Signed-off-by: Chen Cheng <chencheng@fnnas.com>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
---
drivers/md/md.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 6d73f6e196a..e30b658641e 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -2790,7 +2790,9 @@ void md_update_sb(struct mddev *mddev, int force_change)
if (!md_is_rdwr(mddev)) {
if (force_change)
set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags);
- pr_err("%s: can't update sb for read-only array %s\n", __func__, mdname(mddev));
+ if (!mddev_is_dm(mddev))
+ pr_err_ratelimited("%s: can't update sb for read-only array %s\n",
+ __func__, mdname(mddev));
return;
}
--
2.51.0
^ permalink raw reply related
* Re: [PATCH] md: suppress spurious superblock update warning for dm-raid
From: Paul Menzel @ 2026-02-10 7:49 UTC (permalink / raw)
To: chencheng; +Cc: song, yukuai, linux-raid, linux-kernel
In-Reply-To: <20260210124114.169207-1-chencheng@fnnas.com>
Dear Chen,
Thank you for your patch.
Am 10.02.26 um 13:41 schrieb chencheng:
> dm-raid have external metadata management (mddev->external = 1) and
> no persistent superblock (mddev->persistent = 0). For these arrays,
> there's no superblock to update, so the warning is spurious.
Please paste the warning.
Please also add a Fixes: tag.
> Signed-off-by: chencheng <chencheng@fnnas.com>
Would Chen Cheng be the official (transcribed) spelling of your name. If
you yes, it’d be great, if you updated it:
git config --global user.name "Chen Cheng"
git commit --amend -s --author="Chen Cheng <chencheng@fnnas.com>"
> ---
> drivers/md/md.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index 6d73f6e196a..e30b658641e 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -2790,7 +2790,9 @@ void md_update_sb(struct mddev *mddev, int force_change)
> if (!md_is_rdwr(mddev)) {
> if (force_change)
> set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags);
> - pr_err("%s: can't update sb for read-only array %s\n", __func__, mdname(mddev));
> + if (!mddev_is_dm(mddev))
> + pr_err_ratelimited("%s: can't update sb for read-only array %s\n",
> + __func__, mdname(mddev));
(It looks like it’s an error level message and not a warning level message.)
> return;
> }
With the commit message suggestions addressed, feel free to add:
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
Kind regards,
Paul
^ permalink raw reply
* [PATCH] md: suppress spurious superblock update warning for dm-raid
From: chencheng @ 2026-02-10 12:41 UTC (permalink / raw)
To: song, yukuai, linux-raid, linux-kernel; +Cc: chencheng
dm-raid have external metadata management (mddev->external = 1) and
no persistent superblock (mddev->persistent = 0). For these arrays,
there's no superblock to update, so the warning is spurious.
Signed-off-by: chencheng <chencheng@fnnas.com>
---
drivers/md/md.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 6d73f6e196a..e30b658641e 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -2790,7 +2790,9 @@ void md_update_sb(struct mddev *mddev, int force_change)
if (!md_is_rdwr(mddev)) {
if (force_change)
set_bit(MD_SB_CHANGE_DEVS, &mddev->sb_flags);
- pr_err("%s: can't update sb for read-only array %s\n", __func__, mdname(mddev));
+ if (!mddev_is_dm(mddev))
+ pr_err_ratelimited("%s: can't update sb for read-only array %s\n",
+ __func__, mdname(mddev));
return;
}
--
2.51.0
^ permalink raw reply related
* [PATCH] md/raid10: fix deadlock with check operation and nowait requests
From: Josh Hunt @ 2026-02-10 5:09 UTC (permalink / raw)
To: song, yukuai, linan122, linux-raid; +Cc: ncroxon, Josh Hunt, stable
When an array check is running it will raise the barrier at which point
normal requests will become blocked and increment the nr_pending value to
signal there is work pending inside of wait_barrier(). NOWAIT requests
do not block and so will return immediately with an error, and additionally
do not increment nr_pending in wait_barrier(). Upstream change
43806c3d5b9b ("raid10: cleanup memleak at raid10_make_request") added a
call to raid_end_bio_io() to fix a memory leak when NOWAIT requests hit
this condition. raid_end_bio_io() eventually calls allow_barrier() and
it will unconditionally do an atomic_dec_and_test(&conf->nr_pending) even
though the corresponding increment on nr_pending didn't happen in the
NOWAIT case.
This can be easily seen by starting a check operation while an application
is doing nowait IO on the same array. This results in a deadlocked state
due to nr_pending value underflowing and so the md resync thread gets
stuck waiting for nr_pending to == 0.
Output of r10conf state of the array when we hit this condition:
crash> struct r10conf.barrier,nr_pending,nr_waiting,nr_queued <addr of r10conf>
barrier = 1,
nr_pending = {
counter = -41
},
nr_waiting = 15,
nr_queued = 0,
Example of md_sync thread stuck waiting on raise_barrier() and other
requests stuck in wait_barrier():
md1_resync
[<0>] raise_barrier+0xce/0x1c0
[<0>] raid10_sync_request+0x1ca/0x1ed0
[<0>] md_do_sync+0x779/0x1110
[<0>] md_thread+0x90/0x160
[<0>] kthread+0xbe/0xf0
[<0>] ret_from_fork+0x34/0x50
[<0>] ret_from_fork_asm+0x1a/0x30
kworker/u1040:2+flush-253:4
[<0>] wait_barrier+0x1de/0x220
[<0>] regular_request_wait+0x30/0x180
[<0>] raid10_make_request+0x261/0x1000
[<0>] md_handle_request+0x13b/0x230
[<0>] __submit_bio+0x107/0x1f0
[<0>] submit_bio_noacct_nocheck+0x16f/0x390
[<0>] ext4_io_submit+0x24/0x40
[<0>] ext4_do_writepages+0x254/0xc80
[<0>] ext4_writepages+0x84/0x120
[<0>] do_writepages+0x7a/0x260
[<0>] __writeback_single_inode+0x3d/0x300
[<0>] writeback_sb_inodes+0x1dd/0x470
[<0>] __writeback_inodes_wb+0x4c/0xe0
[<0>] wb_writeback+0x18b/0x2d0
[<0>] wb_workfn+0x2a1/0x400
[<0>] process_one_work+0x149/0x330
[<0>] worker_thread+0x2d2/0x410
[<0>] kthread+0xbe/0xf0
[<0>] ret_from_fork+0x34/0x50
[<0>] ret_from_fork_asm+0x1a/0x30
Fixes: 43806c3d5b9b ("raid10: cleanup memleak at raid10_make_request")
Cc: stable@vger.kernel.org
Signed-off-by: Josh Hunt <johunt@akamai.com>
---
drivers/md/raid10.c | 36 +++++++++++++++++++++++++-----------
1 file changed, 25 insertions(+), 11 deletions(-)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 9debb20cf129..184b5b3906d1 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -68,6 +68,7 @@
*/
static void allow_barrier(struct r10conf *conf);
+static void allow_barrier_nowait(struct r10conf *conf);
static void lower_barrier(struct r10conf *conf);
static int _enough(struct r10conf *conf, int previous, int ignore);
static int enough(struct r10conf *conf, int ignore);
@@ -317,7 +318,7 @@ static void reschedule_retry(struct r10bio *r10_bio)
* operation and are ready to return a success/failure code to the buffer
* cache layer.
*/
-static void raid_end_bio_io(struct r10bio *r10_bio)
+static void raid_end_bio_io(struct r10bio *r10_bio, bool adjust_pending)
{
struct bio *bio = r10_bio->master_bio;
struct r10conf *conf = r10_bio->mddev->private;
@@ -332,7 +333,10 @@ static void raid_end_bio_io(struct r10bio *r10_bio)
* Wake up any possible resync thread that waits for the device
* to go idle.
*/
- allow_barrier(conf);
+ if (adjust_pending)
+ allow_barrier(conf);
+ else
+ allow_barrier_nowait(conf);
free_r10bio(r10_bio);
}
@@ -414,7 +418,7 @@ static void raid10_end_read_request(struct bio *bio)
uptodate = 1;
}
if (uptodate) {
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, true);
rdev_dec_pending(rdev, conf->mddev);
} else {
/*
@@ -446,7 +450,7 @@ static void one_write_done(struct r10bio *r10_bio)
if (test_bit(R10BIO_MadeGood, &r10_bio->state))
reschedule_retry(r10_bio);
else
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, true);
}
}
}
@@ -1030,13 +1034,23 @@ static bool wait_barrier(struct r10conf *conf, bool nowait)
return ret;
}
-static void allow_barrier(struct r10conf *conf)
+static void __allow_barrier(struct r10conf *conf, bool adjust_pending)
{
- if ((atomic_dec_and_test(&conf->nr_pending)) ||
+ if ((adjust_pending && atomic_dec_and_test(&conf->nr_pending)) ||
(conf->array_freeze_pending))
wake_up_barrier(conf);
}
+static void allow_barrier(struct r10conf *conf)
+{
+ __allow_barrier(conf, true);
+}
+
+static void allow_barrier_nowait(struct r10conf *conf)
+{
+ __allow_barrier(conf, false);
+}
+
static void freeze_array(struct r10conf *conf, int extra)
{
/* stop syncio and normal IO and wait for everything to
@@ -1184,7 +1198,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
}
if (!regular_request_wait(mddev, conf, bio, r10_bio->sectors)) {
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, false);
return;
}
@@ -1195,7 +1209,7 @@ static void raid10_read_request(struct mddev *mddev, struct bio *bio,
mdname(mddev), b,
(unsigned long long)r10_bio->sector);
}
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, true);
return;
}
if (err_rdev)
@@ -1372,7 +1386,7 @@ static void raid10_write_request(struct mddev *mddev, struct bio *bio,
sectors = r10_bio->sectors;
if (!regular_request_wait(mddev, conf, bio, sectors)) {
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, false);
return;
}
@@ -2952,7 +2966,7 @@ static void handle_write_completed(struct r10conf *conf, struct r10bio *r10_bio)
if (test_bit(R10BIO_WriteError,
&r10_bio->state))
close_write(r10_bio);
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, true);
}
}
}
@@ -2987,7 +3001,7 @@ static void raid10d(struct md_thread *thread)
if (test_bit(R10BIO_WriteError,
&r10_bio->state))
close_write(r10_bio);
- raid_end_bio_io(r10_bio);
+ raid_end_bio_io(r10_bio, true);
}
}
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
From: kernel test robot @ 2026-02-07 7:22 UTC (permalink / raw)
To: sunliming, song, yukuai
Cc: oe-kbuild-all, linux-raid, linux-kernel, linan666, sunliming
In-Reply-To: <20260206054341.106878-3-sunliming@linux.dev>
Hi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on akpm-mm/mm-nonmm-unstable]
[also build test WARNING on linus/master v6.19-rc8]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/sunliming-linux-dev/lib-raid6-Divide-the-raid6-algorithm-selection-process-into-two-parts/20260206-134850
base: https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-nonmm-unstable
patch link: https://lore.kernel.org/r/20260206054341.106878-3-sunliming%40linux.dev
patch subject: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
config: i386-buildonly-randconfig-001-20260207 (https://download.01.org/0day-ci/archive/20260207/202602071500.ctQs6ZeY-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260207/202602071500.ctQs6ZeY-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602071500.ctQs6ZeY-lkp@intel.com/
All warnings (new ones prefixed by >>, old ones prefixed by <<):
>> WARNING: modpost: vmlinux: section mismatch in reference: raid6_exit+0x9 (section: .text) -> raid6_benchmark_work (section: .init.data)
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
From: kernel test robot @ 2026-02-07 6:51 UTC (permalink / raw)
To: sunliming, song, yukuai
Cc: oe-kbuild-all, linux-raid, linux-kernel, linan666, sunliming
In-Reply-To: <20260206054341.106878-3-sunliming@linux.dev>
Hi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on akpm-mm/mm-nonmm-unstable]
[also build test WARNING on linus/master v6.19-rc8]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/sunliming-linux-dev/lib-raid6-Divide-the-raid6-algorithm-selection-process-into-two-parts/20260206-134850
base: https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-nonmm-unstable
patch link: https://lore.kernel.org/r/20260206054341.106878-3-sunliming%40linux.dev
patch subject: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
config: arc-randconfig-001-20260207 (https://download.01.org/0day-ci/archive/20260207/202602071412.HND36w5Q-lkp@intel.com/config)
compiler: arc-linux-gcc (GCC) 9.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260207/202602071412.HND36w5Q-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602071412.HND36w5Q-lkp@intel.com/
All warnings (new ones prefixed by >>, old ones prefixed by <<):
>> WARNING: modpost: lib/raid6/raid6_pq: section mismatch in reference: raid6_exit+0x2 (section: .text) -> raid6_benchmark_work (section: .init.data)
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
From: kernel test robot @ 2026-02-06 19:34 UTC (permalink / raw)
To: sunliming, song, yukuai
Cc: llvm, oe-kbuild-all, linux-raid, linux-kernel, linan666,
sunliming
In-Reply-To: <20260206054341.106878-3-sunliming@linux.dev>
Hi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on akpm-mm/mm-nonmm-unstable]
[also build test WARNING on linus/master v6.16-rc1 next-20260205]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/sunliming-linux-dev/lib-raid6-Divide-the-raid6-algorithm-selection-process-into-two-parts/20260206-134850
base: https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-nonmm-unstable
patch link: https://lore.kernel.org/r/20260206054341.106878-3-sunliming%40linux.dev
patch subject: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
config: x86_64-kexec (https://download.01.org/0day-ci/archive/20260206/202602062049.sdwQ1NiZ-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260206/202602062049.sdwQ1NiZ-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602062049.sdwQ1NiZ-lkp@intel.com/
All warnings (new ones prefixed by >>, old ones prefixed by <<):
>> WARNING: modpost: lib/raid6/raid6_pq: section mismatch in reference: cleanup_module+0xc (section: .text) -> raid6_benchmark_work (section: .init.data)
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
From: kernel test robot @ 2026-02-06 19:22 UTC (permalink / raw)
To: sunliming, song, yukuai
Cc: oe-kbuild-all, linux-raid, linux-kernel, linan666, sunliming
In-Reply-To: <20260206054341.106878-3-sunliming@linux.dev>
Hi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on akpm-mm/mm-nonmm-unstable]
[also build test WARNING on linus/master v6.16-rc1 next-20260205]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/sunliming-linux-dev/lib-raid6-Divide-the-raid6-algorithm-selection-process-into-two-parts/20260206-134850
base: https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm.git mm-nonmm-unstable
patch link: https://lore.kernel.org/r/20260206054341.106878-3-sunliming%40linux.dev
patch subject: [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
config: arm64-allnoconfig-bpf (https://download.01.org/0day-ci/archive/20260206/202602062027.j08UeCWD-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 15.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260206/202602062027.j08UeCWD-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602062027.j08UeCWD-lkp@intel.com/
All warnings (new ones prefixed by >>, old ones prefixed by <<):
>> WARNING: modpost: lib/raid6/raid6_pq: section mismatch in reference: raid6_exit+0xc (section: .text) -> raid6_benchmark_work (section: .init.data)
WARNING: modpost: lib/raid6/raid6_pq: section mismatch in reference: raid6_exit+0x10 (section: .text) -> raid6_benchmark_work (section: .init.data)
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH v2 03/14] md/raid1: use folio for tmppage
From: Li Nan @ 2026-02-06 9:07 UTC (permalink / raw)
To: yukuai, Li Nan, song; +Cc: xni, linux-raid, linux-kernel, yangerkun, yi.zhang
In-Reply-To: <44521568-3ac0-4374-9c3b-756b8e18192f@fnnas.com>
在 2026/2/5 15:33, Yu Kuai 写道:
> Hi,
>
> 在 2026/2/5 15:23, Li Nan 写道:
>> Yeah, should we introduce safe_put_folio()? Or just check NULL here.
>
> Yes, and don't copy implementation. Just convert safe_put_page(page) to
> safe_put_folio(page_folio(page)) first.
>
page cannot be NULL in page_folio(), and the code would be:
if (page)
safe_put_folio(page_folio(page))
This also looks odd. Keeping both functions and removing the page one
after the last reference to it in RAID5 is removed seems better. What do
you think?
--
Thanks,
Nan
^ permalink raw reply
* [PATCH v2 3/3] lib/raid6: Delete the RAID6_PQ_BENCHMARK config
From: sunliming @ 2026-02-06 5:43 UTC (permalink / raw)
To: song, yukuai; +Cc: linux-raid, linux-kernel, linan666, sunliming
In-Reply-To: <20260206054341.106878-1-sunliming@linux.dev>
From: sunliming <sunliming@kylinos.cn>
Now RAID6 PQ functions is automatically choosed and the
RAID6_PQ_BENCHMARK is not needed.
Signed-off-by: sunliming <sunliming@kylinos.cn>
---
include/linux/raid/pq.h | 3 ---
lib/Kconfig | 8 --------
2 files changed, 11 deletions(-)
diff --git a/include/linux/raid/pq.h b/include/linux/raid/pq.h
index 2467b3be15c9..6378ec4ae4ba 100644
--- a/include/linux/raid/pq.h
+++ b/include/linux/raid/pq.h
@@ -67,9 +67,6 @@ extern const char raid6_empty_zero_page[PAGE_SIZE];
#define MODULE_DESCRIPTION(desc)
#define subsys_initcall(x)
#define module_exit(x)
-
-#define IS_ENABLED(x) (x)
-#define CONFIG_RAID6_PQ_BENCHMARK 1
#endif /* __KERNEL__ */
/* Routine choices */
diff --git a/lib/Kconfig b/lib/Kconfig
index 2923924bea78..841a0245a2c4 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -11,14 +11,6 @@ menu "Library routines"
config RAID6_PQ
tristate
-config RAID6_PQ_BENCHMARK
- bool "Automatically choose fastest RAID6 PQ functions"
- depends on RAID6_PQ
- default y
- help
- Benchmark all available RAID6 PQ functions on init and choose the
- fastest one.
-
config LINEAR_RANGES
tristate
--
2.25.1
^ permalink raw reply related
* [PATCH v2 1/3] lib/raid6: Divide the raid6 algorithm selection process into two parts
From: sunliming @ 2026-02-06 5:43 UTC (permalink / raw)
To: song, yukuai; +Cc: linux-raid, linux-kernel, linan666, sunliming
In-Reply-To: <20260206054341.106878-1-sunliming@linux.dev>
From: sunliming <sunliming@kylinos.cn>
Divide the RAID6 algorithm selection process into two parts: fast selection
and benchmark selection. To prepare for the asynchronous processing of
the benchmark phase.
Signed-off-by: sunliming <sunliming@kylinos.cn>
---
lib/raid6/algos.c | 85 +++++++++++++++++++++++++++++++----------------
1 file changed, 57 insertions(+), 28 deletions(-)
diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index 799e0e5eac26..a14469388b0c 100644
--- a/lib/raid6/algos.c
+++ b/lib/raid6/algos.c
@@ -134,7 +134,7 @@ const struct raid6_recov_calls *const raid6_recov_algos[] = {
static inline const struct raid6_recov_calls *raid6_choose_recov(void)
{
const struct raid6_recov_calls *const *algo;
- const struct raid6_recov_calls *best;
+ const struct raid6_recov_calls *best = NULL;
for (best = NULL, algo = raid6_recov_algos; *algo; algo++)
if (!best || (*algo)->priority > best->priority)
@@ -152,24 +152,43 @@ static inline const struct raid6_recov_calls *raid6_choose_recov(void)
return best;
}
-static inline const struct raid6_calls *raid6_choose_gen(
- void *(*const dptrs)[RAID6_TEST_DISKS], const int disks)
+/* Quick selection: selects the first valid algorithm. */
+static inline int raid6_choose_gen_fast(void)
+{
+ int ret = 0;
+ const struct raid6_calls *const *algo;
+ const struct raid6_calls *best = NULL;
+
+ for (best = NULL, algo = raid6_algos; *algo; algo++)
+ if (!best || (*algo)->priority > best->priority)
+ if (!(*algo)->valid || (*algo)->valid())
+ best = *algo;
+
+ if (best) {
+ raid6_call = *best;
+ pr_info("raid6: skipped pq benchmark and selected %s\n",
+ best->name);
+ } else {
+ pr_err("raid6: No valid algorithm found even for fast selection!\n");
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static inline const struct raid6_calls *raid6_gen_benchmark(
+ void *(*const dptrs)[RAID6_TEST_DISKS], const int disks)
{
unsigned long perf, bestgenperf, j0, j1;
int start = (disks>>1)-1, stop = disks-3; /* work on the second half of the disks */
const struct raid6_calls *const *algo;
- const struct raid6_calls *best;
+ const struct raid6_calls *best = NULL;
for (bestgenperf = 0, best = NULL, algo = raid6_algos; *algo; algo++) {
if (!best || (*algo)->priority >= best->priority) {
if ((*algo)->valid && !(*algo)->valid())
continue;
- if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) {
- best = *algo;
- break;
- }
-
perf = 0;
preempt_disable();
@@ -200,12 +219,6 @@ static inline const struct raid6_calls *raid6_choose_gen(
raid6_call = *best;
- if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK)) {
- pr_info("raid6: skipped pq benchmark and selected %s\n",
- best->name);
- goto out;
- }
-
pr_info("raid6: using algorithm %s gen() %ld MB/s\n",
best->name,
(bestgenperf * HZ * (disks - 2)) >>
@@ -230,24 +243,19 @@ static inline const struct raid6_calls *raid6_choose_gen(
(perf * HZ * (disks - 2)) >>
(20 - PAGE_SHIFT + RAID6_TIME_JIFFIES_LG2 + 1));
}
-
out:
return best;
}
-
/* Try to pick the best algorithm */
/* This code uses the gfmul table as convenient data set to abuse */
-
-int __init raid6_select_algo(void)
+static int raid6_choose_gen_benmark(void)
{
const int disks = RAID6_TEST_DISKS;
-
- const struct raid6_calls *gen_best;
- const struct raid6_recov_calls *rec_best;
char *disk_ptr, *p;
void *dptrs[RAID6_TEST_DISKS];
- int i, cycle;
+ const struct raid6_calls *best = NULL;
+ int i, cycle, ret = 0;
/* prepare the buffer and fill it circularly with gfmul table */
disk_ptr = (char *)__get_free_pages(GFP_KERNEL, RAID6_TEST_DISKS_ORDER);
@@ -269,15 +277,36 @@ int __init raid6_select_algo(void)
if ((disks - 2) * PAGE_SIZE % 65536)
memcpy(p, raid6_gfmul, (disks - 2) * PAGE_SIZE % 65536);
- /* select raid gen_syndrome function */
- gen_best = raid6_choose_gen(&dptrs, disks);
+ best = raid6_gen_benchmark(&dptrs, disks);
+ if (!best)
+ ret = -EINVAL;
+
+ free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
+
+ return ret;
+}
+
+int __init raid6_select_algo(void)
+{
+ int ret = 0;
+ const struct raid6_recov_calls *rec_best = NULL;
+
+ /* select raid gen_syndrome functions */
+ if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK))
+ ret = raid6_choose_gen_fast();
+ else
+ ret = raid6_choose_gen_benmark();
+
+ if (ret < 0)
+ goto out;
/* select raid recover functions */
rec_best = raid6_choose_recov();
+ if (!rec_best)
+ ret = -EINVAL;
- free_pages((unsigned long)disk_ptr, RAID6_TEST_DISKS_ORDER);
-
- return gen_best && rec_best ? 0 : -EINVAL;
+out:
+ return ret;
}
static void raid6_exit(void)
--
2.25.1
^ permalink raw reply related
* [PATCH v2 2/3] lib/raid6: Optimizing the raid6_select_algo time through asynchronous processing
From: sunliming @ 2026-02-06 5:43 UTC (permalink / raw)
To: song, yukuai; +Cc: linux-raid, linux-kernel, linan666, sunliming
In-Reply-To: <20260206054341.106878-1-sunliming@linux.dev>
From: sunliming <sunliming@kylinos.cn>
Optimizing the raid6_select_algo time. In raid6_select_algo(), an raid6 gen
algorithm is first selected quickly through synchronous processing, while
the time-consuming process of selecting the optimal algorithm via benchmarking
is handled asynchronously. This approach speeds up the overall startup time
and ultimately ensures the selection of an optimal algorithm.
Signed-off-by: sunliming <sunliming@kylinos.cn>
---
lib/raid6/algos.c | 30 ++++++++++++++++++++----------
1 file changed, 20 insertions(+), 10 deletions(-)
diff --git a/lib/raid6/algos.c b/lib/raid6/algos.c
index a14469388b0c..a3c94641a8e5 100644
--- a/lib/raid6/algos.c
+++ b/lib/raid6/algos.c
@@ -12,6 +12,7 @@
*/
#include <linux/raid/pq.h>
+#include <linux/workqueue.h>
#ifndef __KERNEL__
#include <sys/mman.h>
#include <stdio.h>
@@ -166,7 +167,7 @@ static inline int raid6_choose_gen_fast(void)
if (best) {
raid6_call = *best;
- pr_info("raid6: skipped pq benchmark and selected %s\n",
+ pr_info("raid6: fast selected %s, async benchmark pending\n",
best->name);
} else {
pr_err("raid6: No valid algorithm found even for fast selection!\n");
@@ -213,7 +214,7 @@ static inline const struct raid6_calls *raid6_gen_benchmark(
}
if (!best) {
- pr_err("raid6: Yikes! No algorithm found!\n");
+ pr_warn("raid6: async benchmark failed to find any algorithm\n");
goto out;
}
@@ -286,24 +287,33 @@ static int raid6_choose_gen_benmark(void)
return ret;
}
+static struct work_struct raid6_benchmark_work __initdata;
+
+static __init void benchmark_work_func(struct work_struct *work)
+{
+ raid6_choose_gen_benmark();
+}
+
int __init raid6_select_algo(void)
{
int ret = 0;
const struct raid6_recov_calls *rec_best = NULL;
- /* select raid gen_syndrome functions */
- if (!IS_ENABLED(CONFIG_RAID6_PQ_BENCHMARK))
- ret = raid6_choose_gen_fast();
- else
- ret = raid6_choose_gen_benmark();
-
+ /* phase 1: synchronous fast selection generation algorithm */
+ ret = raid6_choose_gen_fast();
if (ret < 0)
goto out;
/* select raid recover functions */
rec_best = raid6_choose_recov();
- if (!rec_best)
+ if (!rec_best) {
ret = -EINVAL;
+ goto out;
+ }
+
+ /* phase 2: asynchronous performance benchmarking */
+ INIT_WORK(&raid6_benchmark_work, benchmark_work_func);
+ schedule_work(&raid6_benchmark_work);
out:
return ret;
@@ -311,7 +321,7 @@ int __init raid6_select_algo(void)
static void raid6_exit(void)
{
- do { } while (0);
+ cancel_work_sync(&raid6_benchmark_work);
}
subsys_initcall(raid6_select_algo);
--
2.25.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox