drivers/md/md.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
md_do_sync()'s main loop advances io_sectors only when I/O is actually
issued (skipped == 0). When sync_request() keeps returning skipped == 1,
io_sectors never increases, the "last_check + window > io_sectors" test
stays true, and every iteration takes the continue branch:
sectors = mddev->pers->sync_request(mddev, j, max_sectors, &skipped);
...
if (!skipped)
io_sectors += sectors;
j += sectors;
...
if (last_check + window > io_sectors || j == max_sectors)
continue;
During recovery or resync of a large array with a sparse bitmap, many
regions that need no syncing are skipped:
raid10_sync_request()
md_bitmap_start_sync() -> must_sync = false (no bitmap page)
/* every mirror skipped */
biolist == NULL -> *skipped = 1; return max_sync;
j then traverses the whole skipped range while io_sectors stays
unchanged. On a non-preemptive kernel the resync thread (mdX_resync)
hogs the CPU for a long time and eventually triggers a soft lockup:
watchdog: BUG: soft lockup - CPU#149 stuck for 313s! [mdX_resync]
md_bitmap_start_sync+0x6f/0xe0
raid10_sync_request+0x2c9/0x1530 [raid10]
md_do_sync+0x810/0x1030
md_thread+0xa7/0x150
Add cond_resched() to this continue path.
Signed-off-by: Yunye Zhao <yunye.zhao@linux.alibaba.com>
---
drivers/md/md.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index d1465bcd86c8..e7411b033490 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -9881,9 +9881,10 @@ void md_do_sync(struct md_thread *thread)
*/
md_new_event();
- if (last_check + window > io_sectors || j == max_sectors)
+ if (last_check + window > io_sectors || j == max_sectors) {
+ cond_resched();
continue;
-
+ }
last_check = io_sectors;
repeat:
if (time_after_eq(jiffies, mark[last_mark] + SYNC_MARK_STEP )) {
--
2.19.1.6.gb485710b
This series fixes two independent problems in raid10 recovery, plus a
safety net in md core.
Problem 1: silent data corruption when recovering with more than one
mirror missing.
Commit fe6a19d40ceb ("md/md-bitmap: merge md_bitmap_start_sync() into
bitmap_operations") inverted still_degraded in raid10_sync_request()
(int 1 -> bool false; the raid1 side of the same conversion is correct).
Recovering one disk while another is still missing therefore clears
bitmap bits the missing disk still needs; after its re-add the dirty
regions are skipped and the disk is marked In_sync with stale data.
Patch 1 restores the correct value.
Problem 2: soft lockup / CPU burn on recovery of a large sparse array
(the original report behind v1).
Recovering a degraded RAID10 with a mostly-clean bitmap makes
raid10_sync_request() take the "everything skipped" path, which returns
only 128 sectors per call even though the bitmap already reported a much
larger clean span. md_do_sync() then crawls across the whole device in
128-sector steps with no reschedule; on a 2^40-sector array that is 2^33
no-op iterations:
watchdog: BUG: soft lockup - CPU#149 stuck for 313s! [mdX_resync]
md_bitmap_start_sync+0x6f/0xe0
raid10_sync_request+0x2c9/0x1530 [raid10]
md_do_sync+0x810/0x1030
md_thread+0xa7/0x150
Patch 2 adds a cond_resched() to md_do_sync()'s skip path (safety net,
any personality/layout). Patch 3 fixes the root cause for near layouts:
convert the bitmap-reported clean span from array to per-device sectors
and skip it in one step. The conversion cannot be done generically in
the bitmap layer because the recovery cursor is in per-device space
(llbitmap's skip_sync_blocks() likewise returns 0 when degraded), and
resync paths already skip whole clean spans, so the fix lives in raid10.
Tested on QEMU/KVM on the latest mainline (raid10 near=2, 4 disks,
internal bitmap):
- data consistency: write, fail one disk of each pair, write 256MiB
while degraded, re-add both, wait for recovery, run "check":
mismatch_cnt=262272 and changed file hashes before patch 1,
mismatch_cnt=0 and identical hashes after.
- recovery of two re-added disks on a sparse 8T-per-disk array: one
37.2s recovery sweep of 100% CPU spinning before, 6.4 ms and 12.8 ms
for the two recovery passes after patch 3 (dmesg "recover of RAID
array" -> "recover done": 44.886958 -> 44.893378 and
44.923957 -> 44.936716); no soft lockup with softlockup_panic=1.
Changes since v1 (a single cond_resched patch):
- patch 1 (new): fix the still_degraded inversion, a silent data
corruption found while testing.
- patch 2 (was v1): rewrite the changelog and retitle; the v1 claim
that "io_sectors never increases" was wrong (Yu Kuai).
- patch 3 (new): fix the 128-sector recovery crawl at the source
instead of only silencing the watchdog.
v1: https://lore.kernel.org/linux-raid/20260717062743.128189-1-yunye.zhao@linux.alibaba.com/
Yunye Zhao (3):
md/raid10: fix still_degraded being inverted in raid10_sync_request()
md: add cond_resched() to md_do_sync()'s skip path
md/raid10: skip clean regions in bulk during recovery
drivers/md/md.c | 4 ++-
drivers/md/raid10.c | 57 ++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 56 insertions(+), 5 deletions(-)
base-commit: 4539944e515183668109bdf4d0c3d7d228383d88
--
2.19.1.6.gb485710b
Commit fe6a19d40ceb ("md/md-bitmap: merge md_bitmap_start_sync() into
bitmap_operations") converted still_degraded from int to bool, but
inverted the assignment in the loop that checks whether the array will
still be degraded after the current device is recovered:
"still_degraded = 1" became "still_degraded = false".
As a result, recovering a device while another mirror is still missing
calls md_bitmap_start_sync() with degraded == false, which clears bitmap
bits that the still-missing device needs. When that device is re-added,
its bitmap-based recovery finds the bits already cleared and skips every
region written while the array was degraded, so it is marked In_sync
while holding stale data: silent corruption.
Reproducer (raid10 near=2, 4 disks, internal bitmap):
- fail and remove one disk of each mirror pair
- write to the degraded array
- re-add both disks and let recovery finish
- "check" reports mismatch_cnt=262272 after 256 MiB of degraded
writes and file contents differ; the second disk's "recovery"
completes in milliseconds because everything is skipped
The same conversion in raid1 got it right (still_degraded = true).
Restore the correct value.
Fixes: fe6a19d40ceb ("md/md-bitmap: merge md_bitmap_start_sync() into bitmap_operations")
Cc: stable@vger.kernel.org
Signed-off-by: Yunye Zhao <yunye.zhao@linux.alibaba.com>
---
drivers/md/raid10.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 0a3cfdd3f5df..54cddb3a98cd 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -3365,7 +3365,7 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
struct md_rdev *rdev = conf->mirrors[j].rdev;
if (rdev == NULL || test_bit(Faulty, &rdev->flags)) {
- still_degraded = false;
+ still_degraded = true;
break;
}
}
base-commit: 4539944e515183668109bdf4d0c3d7d228383d88
--
2.19.1.6.gb485710b
When sync_request() reports a skipped region (*skipped == 1),
md_do_sync()'s main loop advances the cursor and takes an early
continue:
j += sectors;
...
if (last_check + window > io_sectors || j == max_sectors)
continue;
If the personality returns a small span per call (raid10 recovery
returns only 128 sectors), syncing a large, mostly clean array iterates
this branch an enormous number of times without ever yielding the CPU.
On a non-preemptive kernel the resync thread then trips the soft-lockup
watchdog:
watchdog: BUG: soft lockup - CPU#149 stuck for 313s! [mdX_resync]
md_bitmap_start_sync+0x6f/0xe0
raid10_sync_request+0x2c9/0x1530 [raid10]
md_do_sync+0x810/0x1030
md_thread+0xa7/0x150
Add a cond_resched(). This does not reduce the wasted iterations; the
excessive iteration count is a raid10 problem addressed separately.
Cc: stable@vger.kernel.org
Signed-off-by: Yunye Zhao <yunye.zhao@linux.alibaba.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 d1465bcd86c8..c2e8b203c4db 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -9881,8 +9881,10 @@ void md_do_sync(struct md_thread *thread)
*/
md_new_event();
- if (last_check + window > io_sectors || j == max_sectors)
+ if (last_check + window > io_sectors || j == max_sectors) {
+ cond_resched();
continue;
+ }
last_check = io_sectors;
repeat:
--
2.19.1.6.gb485710b
During recovery of a degraded RAID10 with a mostly-clean bitmap, the
"everything skipped" path (biolist == NULL) returns max_sync == 128
sectors, even though md_bitmap_start_sync() already reported a much
larger clean range (an unallocated bitmap page spans 2^31 sectors with
a 512M bitmap chunk). On the reported 2^40-sector array md_do_sync()
then crawls through 2^33 no-op iterations, burning a CPU for the whole
sweep; the cond_resched() in md_do_sync()'s skip path only stops the
watchdog firing.
The skip path cannot simply return sync_blocks: sync_blocks is measured
in array sectors while the return value advances the per-device recovery
cursor, and the two spaces differ by the raid10 layout. For a near
layout (far_copies == 1 && !far_offset) the mapping is linear with slope
raid_disks / near_copies; when near_copies evenly divides raid_disks the
conversion is exact. Track the minimum clean span across the skipped
devices, convert it to device sectors and skip it in one step.
far/offset layouts are not a linear scale and keep the previous
behaviour.
Only devices skipped as clean feed that minimum, and the skip is
suppressed when a device needed rebuilding but had no readable source
(missing_source), so the cursor never jumps past sectors that still
need recovery.
The generic bitmap skip_sync_blocks() path cannot be used here: it does
not see mrdev/mreplace (the recovery target and its replacement), and
replacement targets must rebuild even bitmap-clean chunks.
On a mostly-clean array with 8T per device (near=2, 4 disks) the
recovery sweep drops from 37.2s of CPU spinning to about 10ms.
Signed-off-by: Yunye Zhao <yunye.zhao@linux.alibaba.com>
---
drivers/md/raid10.c | 46 ++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 43 insertions(+), 3 deletions(-)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 54cddb3a98cd..8acbd5ef618b 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -3176,6 +3176,9 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
int i;
int max_sync;
sector_t sync_blocks;
+ sector_t min_sync_blocks = MaxSector;
+ sector_t sync_end;
+ bool missing_source = false;
sector_t chunk_mask = conf->geo.chunk_mask;
int page_idx = 0;
@@ -3258,6 +3261,14 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
if (max_sector > mddev->resync_max)
max_sector = mddev->resync_max; /* Don't do IO beyond here */
+ /*
+ * The chunk clamp below caps a single sync I/O to at most one chunk.
+ * The bitmap-clean recovery skip issues no I/O, so remember the real
+ * end here to bound the skip against it rather than the chunk
+ * boundary.
+ */
+ sync_end = max_sector;
+
/* make sure whole request will fit in a chunk - if chunks
* are meaningful
*/
@@ -3334,9 +3345,13 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
if (!must_sync &&
mreplace == NULL &&
!conf->fullsync) {
- /* yep, skip the sync_blocks here, but don't assume
- * that there will never be anything to do here
+ /* Skip the clean sync_blocks here; don't
+ * assume there will never be anything to
+ * do. Only a genuinely skipped clean span
+ * may widen the bulk skip below.
*/
+ if (sync_blocks < min_sync_blocks)
+ min_sync_blocks = sync_blocks;
continue;
}
if (mrdev)
@@ -3459,6 +3474,7 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
if (j == conf->copies) {
/* Cannot recover, so abort the recovery or
* record a bad block */
+ missing_source = true;
if (any_working) {
/* problem is that there are bad blocks
* on other device(s)
@@ -3514,6 +3530,8 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
}
}
if (biolist == NULL) {
+ sector_t skip_sectors = max_sync;
+
while (r10_bio) {
struct r10bio *rb2 = r10_bio;
r10_bio = (struct r10bio*) rb2->master_bio;
@@ -3521,7 +3539,29 @@ static sector_t raid10_sync_request(struct mddev *mddev, sector_t sector_nr,
put_buf(rb2);
}
*skipped = 1;
- return max_sync;
+
+ /*
+ * min_sync_blocks is in array sectors, but the return
+ * value advances the per-device cursor; for a near
+ * layout the two spaces differ by the factor
+ * raid_disks / near_copies.
+ */
+ if (!missing_source &&
+ conf->geo.far_copies == 1 && !conf->geo.far_offset &&
+ conf->geo.raid_disks % conf->geo.near_copies == 0 &&
+ min_sync_blocks != MaxSector) {
+ sector_t clean_sectors = min_sync_blocks;
+
+ clean_sectors *= conf->geo.near_copies;
+ sector_div(clean_sectors, conf->geo.raid_disks);
+ clean_sectors &= ~chunk_mask;
+ if (clean_sectors > sync_end - sector_nr)
+ clean_sectors = sync_end - sector_nr;
+ if (clean_sectors > skip_sectors)
+ skip_sectors = clean_sectors;
+ }
+
+ return skip_sectors;
}
} else {
/* resync. Schedule a read for every block at this virt offset */
--
2.19.1.6.gb485710b
Hi,
在 2026/7/17 14:27, Yunye Zhao 写道:
> md_do_sync()'s main loop advances io_sectors only when I/O is actually
> issued (skipped == 0). When sync_request() keeps returning skipped == 1,
> io_sectors never increases, the "last_check + window > io_sectors" test
> stays true, and every iteration takes the continue branch:
That's not expected, io_sectors should always increase in the skip case.
>
> sectors = mddev->pers->sync_request(mddev, j, max_sectors, &skipped);
> ...
> if (!skipped)
> io_sectors += sectors;
> j += sectors;
> ...
> if (last_check + window > io_sectors || j == max_sectors)
> continue;
>
> During recovery or resync of a large array with a sparse bitmap, many
> regions that need no syncing are skipped:
>
> raid10_sync_request()
> md_bitmap_start_sync() -> must_sync = false (no bitmap page)
> /* every mirror skipped */
> biolist == NULL -> *skipped = 1; return max_sync;
>
> j then traverses the whole skipped range while io_sectors stays
> unchanged. On a non-preemptive kernel the resync thread (mdX_resync)
> hogs the CPU for a long time and eventually triggers a soft lockup:
>
> watchdog: BUG: soft lockup - CPU#149 stuck for 313s! [mdX_resync]
> md_bitmap_start_sync+0x6f/0xe0
> raid10_sync_request+0x2c9/0x1530 [raid10]
> md_do_sync+0x810/0x1030
> md_thread+0xa7/0x150
What kernel version you're testing? If this is latest kernel, bitmap_start_sync()
need to be fixed. It can't return skip while setting skipping sectors to 0. And
since this is dead loop, a cond_resched() will not fix anything.
>
> Add cond_resched() to this continue path.
>
> Signed-off-by: Yunye Zhao <yunye.zhao@linux.alibaba.com>
> ---
> drivers/md/md.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/md/md.c b/drivers/md/md.c
> index d1465bcd86c8..e7411b033490 100644
> --- a/drivers/md/md.c
> +++ b/drivers/md/md.c
> @@ -9881,9 +9881,10 @@ void md_do_sync(struct md_thread *thread)
> */
> md_new_event();
>
> - if (last_check + window > io_sectors || j == max_sectors)
> + if (last_check + window > io_sectors || j == max_sectors) {
> + cond_resched();
> continue;
> -
> + }
> last_check = io_sectors;
> repeat:
> if (time_after_eq(jiffies, mark[last_mark] + SYNC_MARK_STEP )) {
--
Thanks,
Kuai
Hi Kuai,
On 2026/7/17 14:27, Yu Kuai wrote:
>> md_do_sync()'s main loop advances io_sectors only when I/O is actually
>> issued (skipped == 0). When sync_request() keeps returning skipped == 1,
>> io_sectors never increases, [...]
>
> That's not expected, io_sectors should always increase in the skip case.
Sorry, my description was not accurate. The problem is not that io_sectors
stays 0. md_do_sync()'s loop exit condition is j == max_sectors, and in
raid10_sync_request()'s recovery path sectors is always 128, so for a very
large max_sectors the loop iterates a huge number of times.
raid10_sync_request(), recovery branch:
max_sync = RESYNC_PAGES << (PAGE_SHIFT-9); /* 128 */
must_sync = md_bitmap_start_sync(mddev, sect, &sync_blocks, true);
if (sync_blocks < max_sync) /* sync_blocks huge, never true */
max_sync = sync_blocks; /* so max_sync stays 128 */
...
if (biolist == NULL) {
*skipped = 1;
return max_sync; /* 128; the large span is discarded */
}
Back in md_do_sync()'s main loop, j then crawls forward 128 sectors per
call until it reaches max_sectors:
while (j < max_sectors) {
sectors = mddev->pers->sync_request(mddev, j, max_sectors, &skipped);
if (!skipped) /* skipped == 1 -> io_sectors stays 0 */
io_sectors += sectors;
j += sectors; /* j += 128 only */
if (last_check + window > io_sectors || j == max_sectors)
continue;
}
From the vmcore:
sync_blocks returned = 0x5ED03680 (~1.59e8 sectors), clamped to 128
recovery max_sectors = dev_sectors = 2^40
iterations = 2^40 / 128 = 2^33 (~8.6e9)
> What kernel version you're testing?
6.6.102. I also tested mainline and hit the same problem.
> If this is latest kernel, bitmap_start_sync() need to be fixed. It can't
> return skip while setting skipping sectors to 0.
bitmap_start_sync() is actually fine -- it returns a large clean span
(0x5ED03680 above). The recovery path just discards it: max_sync is only
ever clamped down, so it returns 128 regardless.
> And since this is dead loop, a cond_resched() will not fix anything.
Agreed -- cond_resched() only stops the watchdog; the thread still spins
~8.6e9 no-op iterations and pins a CPU for ~313s.
For v2 I'd fix this at the source: make the recovery path honour the clean
span reported by the bitmap instead of capping the skip at 128. Does that
direction look right to you?
Thanks,
Yunye
Hi,
在 2026/7/20 14:20, Yunye Zhao 写道:
> Hi Kuai,
>
> On 2026/7/17 14:27, Yu Kuai wrote:
>>> md_do_sync()'s main loop advances io_sectors only when I/O is actually
>>> issued (skipped == 0). When sync_request() keeps returning skipped == 1,
>>> io_sectors never increases, [...]
>> That's not expected, io_sectors should always increase in the skip case.
> Sorry, my description was not accurate. The problem is not that io_sectors
> stays 0. md_do_sync()'s loop exit condition is j == max_sectors, and in
> raid10_sync_request()'s recovery path sectors is always 128, so for a very
> large max_sectors the loop iterates a huge number of times.
>
> raid10_sync_request(), recovery branch:
>
> max_sync = RESYNC_PAGES << (PAGE_SHIFT-9); /* 128 */
> must_sync = md_bitmap_start_sync(mddev, sect, &sync_blocks, true);
> if (sync_blocks < max_sync) /* sync_blocks huge, never true */
> max_sync = sync_blocks; /* so max_sync stays 128 */
> ...
> if (biolist == NULL) {
> *skipped = 1;
> return max_sync; /* 128; the large span is discarded */
> }
>
> Back in md_do_sync()'s main loop, j then crawls forward 128 sectors per
> call until it reaches max_sectors:
>
> while (j < max_sectors) {
> sectors = mddev->pers->sync_request(mddev, j, max_sectors, &skipped);
> if (!skipped) /* skipped == 1 -> io_sectors stays 0 */
> io_sectors += sectors;
> j += sectors; /* j += 128 only */
> if (last_check + window > io_sectors || j == max_sectors)
> continue;
> }
>
> From the vmcore:
>
> sync_blocks returned = 0x5ED03680 (~1.59e8 sectors), clamped to 128
> recovery max_sectors = dev_sectors = 2^40
> iterations = 2^40 / 128 = 2^33 (~8.6e9)
Please update and fix the commit message "io_sectors never increases", this
really is misleading.
>
>> What kernel version you're testing?
> 6.6.102. I also tested mainline and hit the same problem.
>
>> If this is latest kernel, bitmap_start_sync() need to be fixed. It can't
>> return skip while setting skipping sectors to 0.
> bitmap_start_sync() is actually fine -- it returns a large clean span
> (0x5ED03680 above). The recovery path just discards it: max_sync is only
> ever clamped down, so it returns 128 regardless.
>
>> And since this is dead loop, a cond_resched() will not fix anything.
> Agreed -- cond_resched() only stops the watchdog; the thread still spins
> ~8.6e9 no-op iterations and pins a CPU for ~313s.
>
> For v2 I'd fix this at the source: make the recovery path honour the clean
> span reported by the bitmap instead of capping the skip at 128. Does that
> direction look right to you?
This patch is still valuable, even a 5s lockup is not acceptable.
And I think this is not a problem for llbitmap, it skips one bit at a time and
bits are limited to at most 4k*128. Perhaps bitmap can do this as well, it can
definably return much bigger skipping sectors from bitmap_start_sync().
>
> Thanks,
> Yunye
--
Thanks,
Kuai
© 2016 - 2026 Red Hat, Inc.