mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v4 1/3] md/raid5: freeze and interrupt reshape when handling failed reshape
       [not found] <20261016042537.277516-1-me@dougvj.me>
@ 2026-10-16  4:25 ` Doug V Johnson
  2026-10-22  7:36   ` Chen Cheng
  2026-10-16  4:25 ` [PATCH v4 3/3] md/raid5: warn when a read fails due in some part to bad-block metadata Doug V Johnson
  2026-10-16  4:25 ` [PATCH v4 2/3] md/raid5: do not reshape if overlapping rdev bad block ranges prevent it Doug V Johnson
  2 siblings, 1 reply; 4+ messages in thread
From: Doug V Johnson @ 2026-10-16  4:25 UTC (permalink / raw)
  To: linux-raid
  Cc: song, yukuai, magiclinan, xiao, chencheng, linux-kernel, Doug V Johnson

Set the reshape freeze flag to prevent automatic reshape resumption
and interrupt the current reshape with md_sync_error() so that a
reshape always stops no matter how the failed reshape is triggered.

Previously, if there were no faulty or out of sync devices but there
was an unrecoverable read, the reshape continued and was not cancelled
after handle_failed_reshape(). This caused some data corruption as
block writes were cancelled but the reshape nonetheless continued.

Prior to Chen's fix, the kernel would deadlock with a failed stripe
read.  That case also caused some data corruption, so Chen's patch
did not introduce corruption in this case.

An example test case showing how this happened before this fix is
demonstrated by the following:

Simplified example (schematic logical sector numbers):

  1. Before reshape, destination D contains data for logical 20-27.
     The new geometry will use D for logical 100-107.

  2. Inject bad-block metadata on enough members that the old source
     stripe cannot be reconstructed. Logical sector 100 now returns
     a read error; sectors 101-107 are still readable.

  3. Reshape tries to read the source block for logical 100-107.
     The bad-block records prevent a successful read/reconstruction.
     Failure cleanup cancels the destination write:

       Source: logical 100-107 --X--> destination D
                                     still contains data for 20-27

  4. Without interruption, reshape continues and completes.
     The new mapping now sends reads of logical 100-107 to D.

  5. Reading logical 100-107 succeeds, but returns data for 20-27.
     Previously readable sectors 101-107 now return wrong data.

Code that injects bad blocks to demonstrate this problem before and
after Chen's fix as well as after this patch can be found here:

Link: https://github.com/dougvj/md-raid-reshape-badblock-corruption-test

Fixes: 788e4139463f ("md/raid5: fix reshape deadlock while failed devices more than max degraded")
Link: https://patch.msgid.link/20260624075824.2601110-1-chencheng@fnnas.com
Assisted-by: LLM
Signed-off-by: Doug V Johnson <me@dougvj.me>
---
 drivers/md/raid5.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index b91545ce090d..86ea65559576 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -3773,6 +3773,10 @@ static void handle_failed_reshape(struct r5conf *conf, struct stripe_head *sh,
 	bool was_expanding = test_and_clear_bit(STRIPE_EXPANDING, &sh->state);
 	bool was_ready = test_and_clear_bit(STRIPE_EXPAND_READY, &sh->state);
 
+	/* Freeze reshaping to prevent data loss */
+	set_bit(MD_RECOVERY_FROZEN, &conf->mddev->recovery);
+	md_sync_error(conf->mddev);
+
 	if (was_expanding || was_ready) {
 		atomic_dec(&conf->reshape_stripes);
 		wake_up(&conf->wait_for_reshape);
-- 
2.54.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v4 2/3] md/raid5: do not reshape if overlapping rdev bad block ranges prevent it
       [not found] <20261016042537.277516-1-me@dougvj.me>
  2026-10-16  4:25 ` [PATCH v4 1/3] md/raid5: freeze and interrupt reshape when handling failed reshape Doug V Johnson
  2026-10-16  4:25 ` [PATCH v4 3/3] md/raid5: warn when a read fails due in some part to bad-block metadata Doug V Johnson
@ 2026-10-16  4:25 ` Doug V Johnson
  2 siblings, 0 replies; 4+ messages in thread
From: Doug V Johnson @ 2026-10-16  4:25 UTC (permalink / raw)
  To: linux-raid
  Cc: song, yukuai, magiclinan, xiao, chencheng, linux-kernel,
	Doug V Johnson, Yu Kuai

Ensure that a reshape is not started if there are more than max_degraded
devices in the array that are either out of sync, faulty, missing,
or have overlapping bad blocks such that a read is not recoverable.

Cheaply verify that the number of bad or unavailable devices exceeds
the max number of degraded drives and if so sweep the bad block lists
on each device for overlaps using a new function array_has_badblock().

Invoke this function in raid5_start_reshape() and if the array has
unreadable blocks, return -EIO to report it to userspace.

Leave in place the existing functionality to handle a failed reshape
in progress because it's possible for bad blocks to be discovered or
placed in metadata after a reshape has already been initiated.

Link: https://lore.kernel.org/r/21f250f9-04b5-76f6-84c9-1fd245b748c6@huaweicloud.com
Link: https://lore.kernel.org/r/a9119707-4fa1-58cb-377c-63df67f88957@huaweicloud.com
Suggested-by: Yu Kuai <yukuai1@huaweicloud.com>
Assisted-by: LLM
Signed-off-by: Doug V Johnson <me@dougvj.me>
---
 drivers/md/raid5.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)

diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 86ea65559576..23ff549fa363 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -8636,6 +8636,89 @@ static int check_reshape(struct mddev *mddev)
 				     + mddev->delta_disks));
 }
 
+static int rdev_is_unavailable(struct md_rdev *rdev)
+{
+	return !rdev || test_bit(Faulty, &rdev->flags) ||
+	       !test_bit(In_sync, &rdev->flags);
+}
+
+static int array_has_badblock(struct r5conf *conf)
+{
+	sector_t dev_sectors = conf->mddev->dev_sectors;
+	sector_t stripe_sectors = RAID5_STRIPE_SECTORS(conf);
+	sector_t max_sectors = round_down((sector_t)INT_MAX, stripe_sectors);
+	sector_t offset = 0;
+	int num_bad_devs = 0;
+	int i;
+
+	/* Check if there are even enough bad disks for a bad block condition */
+	for (i = 0; i < conf->raid_disks; i++) {
+		struct md_rdev *rdev = conf->disks[i].rdev;
+
+		if (rdev_is_unavailable(rdev) || rdev->badblocks.count)
+			num_bad_devs++;
+	}
+	if (num_bad_devs <= conf->max_degraded)
+		return 0;
+
+	/*
+	 * Sweep the sectors of the array for bad blocks in constituent devices.
+	 * Within a stripe if there are more bad disks than can be recoverable
+	 * (max degraded) then the array has bad blocks.
+	 *
+	 * The offset tracks the sweep. We advance to the first bad stripe
+	 * boundary found in any device.
+	 */
+	while (offset < dev_sectors) {
+		sector_t len = min(dev_sectors - offset, max_sectors);
+		int bad_disks = 0;
+
+		for (i = 0; i < conf->raid_disks; i++) {
+			struct md_rdev *rdev = conf->disks[i].rdev;
+			sector_t first_bad, bad_sectors, end;
+
+			if (rdev_is_unavailable(rdev)) {
+				bad_disks++;
+				continue;
+			}
+			if (!rdev->badblocks.count)
+				continue;
+			if (!badblocks_check(&rdev->badblocks,
+					     rdev->data_offset + offset, len,
+					     &first_bad, &bad_sectors))
+				continue;
+
+			/*
+			 * Rebase the bad blocks start and end to the device's data
+			 * offset for consistent comparision
+			 */
+			end = first_bad + bad_sectors - rdev->data_offset;
+			first_bad = max(first_bad, rdev->data_offset) -
+				    rdev->data_offset;
+
+			/* Snap the range to the stripe boundary extent */
+			first_bad = round_down(first_bad, stripe_sectors);
+			end = round_up(end, stripe_sectors);
+
+			/*
+			 * This device has a bad block at or overlapping the current
+			 * sweep position, so it's a bad disk
+			 */
+			if (first_bad <= offset) {
+				len = min(len, end - offset);
+				bad_disks++;
+			} else {
+				len = min(len, first_bad - offset);
+			}
+		}
+		if (bad_disks > conf->max_degraded)
+			return 1;
+		offset += len;
+		cond_resched();
+	}
+	return 0;
+}
+
 static int raid5_start_reshape(struct mddev *mddev)
 {
 	struct r5conf *conf = mddev->private;
@@ -8685,6 +8768,11 @@ static int raid5_start_reshape(struct mddev *mddev)
 			mdname(mddev));
 		return -EINVAL;
 	}
+	if (array_has_badblock(conf)) {
+		pr_warn("md/raid:%s: reshape not possible due to bad block list\n",
+			mdname(mddev));
+		return -EIO;
+	}
 	if (md_bitmap_enabled(mddev, false) &&
 	    mddev->bitmap_id == ID_LLBITMAP) {
 		i = mddev->bitmap_ops->resize(mddev, mddev->dev_sectors, 0);
-- 
2.54.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v4 3/3] md/raid5: warn when a read fails due in some part to bad-block metadata
       [not found] <20261016042537.277516-1-me@dougvj.me>
  2026-10-16  4:25 ` [PATCH v4 1/3] md/raid5: freeze and interrupt reshape when handling failed reshape Doug V Johnson
@ 2026-10-16  4:25 ` Doug V Johnson
  2026-10-16  4:25 ` [PATCH v4 2/3] md/raid5: do not reshape if overlapping rdev bad block ranges prevent it Doug V Johnson
  2 siblings, 0 replies; 4+ messages in thread
From: Doug V Johnson @ 2026-10-16  4:25 UTC (permalink / raw)
  To: linux-raid
  Cc: song, yukuai, magiclinan, xiao, chencheng, linux-kernel, Doug V Johnson

Report that bad block metadata contributed to a read failure on the
array so that it's clear that the failure may not be directly due to
an immediate underlying device read failure.

There has been a documented case of bad block metadata being erroneously
copied around contributing to mystery read failures with no underlying
device failure.  This warning will help users understand why the
failure exists and may lead to the discovery of this condition sooner.

Link: https://lore.kernel.org/r/20250224090209.2077-2-dougvj@dougvj.net
Link: https://lkml.iu.edu/2501.3/01088.html
Assisted-by: LLM
Signed-off-by: Doug V Johnson <me@dougvj.me>
---
 drivers/md/raid5.c | 16 ++++++++++++++++
 drivers/md/raid5.h |  2 +-
 2 files changed, 17 insertions(+), 1 deletion(-)

diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 23ff549fa363..f721eb97f655 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -3672,6 +3672,16 @@ handle_failed_stripe(struct r5conf *conf, struct stripe_head *sh,
 				wake_up_bit(&sh->dev[i].flags, R5_Overlap);
 			if (bi)
 				s->to_read--;
+			/*
+			 * If we recorded bad blocks from the metadata
+			 * on any of the devices then report this to
+			 * userspace in case anyone might suspect
+			 * something more fundamental instead
+			 */
+			if (bi && s->bad_blocks)
+				pr_warn_ratelimited("%s: read encountered block in device bad block list at %llu\n",
+						    mdname(conf->mddev),
+						    (unsigned long long)sh->sector);
 			while (bi && bi->bi_iter.bi_sector <
 			       sh->dev[i].sector + RAID5_STRIPE_SECTORS(conf)) {
 				struct bio *nextbi =
@@ -4764,6 +4774,12 @@ static void analyse_stripe(struct stripe_head *sh, struct stripe_head_state *s)
 		if (rdev) {
 			is_bad = rdev_has_badblock(rdev, sh->sector,
 						   RAID5_STRIPE_SECTORS(conf));
+			if (is_bad) {
+				s->bad_blocks++;
+				pr_debug("bad blocks encountered dev %i sector %llu %lu\n",
+					 i, (unsigned long long)sh->sector,
+					 (unsigned long)RAID5_STRIPE_SECTORS(conf));
+			}
 			if (s->blocked_rdev == NULL) {
 				if (is_bad < 0)
 					set_bit(BlockedBadBlocks, &rdev->flags);
diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h
index cb5feae04db2..6b90c9565ec0 100644
--- a/drivers/md/raid5.h
+++ b/drivers/md/raid5.h
@@ -282,7 +282,7 @@ struct stripe_head_state {
 	 * read all devices, just the replacement targets.
 	 */
 	int syncing, expanding, expanded, replacing;
-	int locked, uptodate, to_read, to_write, failed, written;
+	int locked, uptodate, to_read, to_write, failed, written, bad_blocks;
 	int to_fill, compute, req_compute, non_overwrite;
 	int injournal, just_cached;
 	int failed_num[2];
-- 
2.54.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v4 1/3] md/raid5: freeze and interrupt reshape when handling failed reshape
  2026-10-16  4:25 ` [PATCH v4 1/3] md/raid5: freeze and interrupt reshape when handling failed reshape Doug V Johnson
@ 2026-10-22  7:36   ` Chen Cheng
  0 siblings, 0 replies; 4+ messages in thread
From: Chen Cheng @ 2026-10-22  7:36 UTC (permalink / raw)
  To: Doug V Johnson, linux-raid; +Cc: song, yukuai, magiclinan, xiao, linux-kernel

On 2026/10/16 12:25, Doug V Johnson wrote:
> Set the reshape freeze flag to prevent automatic reshape resumption
> and interrupt the current reshape with md_sync_error() so that a
> reshape always stops no matter how the failed reshape is triggered.
> 
> Previously, if there were no faulty or out of sync devices but there
> was an unrecoverable read, the reshape continued and was not cancelled
> after handle_failed_reshape(). This caused some data corruption as
> block writes were cancelled but the reshape nonetheless continued.
> 
> Prior to Chen's fix, the kernel would deadlock with a failed stripe
> read.  That case also caused some data corruption, so Chen's patch
> did not introduce corruption in this case.
> 
> An example test case showing how this happened before this fix is
> demonstrated by the following:
> 
> Simplified example (schematic logical sector numbers):
> 
>    1. Before reshape, destination D contains data for logical 20-27.
>       The new geometry will use D for logical 100-107.
> 
>    2. Inject bad-block metadata on enough members that the old source
>       stripe cannot be reconstructed. Logical sector 100 now returns
>       a read error; sectors 101-107 are still readable.
> 
>    3. Reshape tries to read the source block for logical 100-107.
>       The bad-block records prevent a successful read/reconstruction.
>       Failure cleanup cancels the destination write:
> 
>         Source: logical 100-107 --X--> destination D
>                                       still contains data for 20-27
> 
>    4. Without interruption, reshape continues and completes.
>       The new mapping now sends reads of logical 100-107 to D.
> 
>    5. Reading logical 100-107 succeeds, but returns data for 20-27.
>       Previously readable sectors 101-107 now return wrong data.
> 
> Code that injects bad blocks to demonstrate this problem before and
> after Chen's fix as well as after this patch can be found here:
> 
> Link: https://github.com/dougvj/md-raid-reshape-badblock-corruption-test
> 
> Fixes: 788e4139463f ("md/raid5: fix reshape deadlock while failed devices more than max degraded")
> Link: https://patch.msgid.link/20260624075824.2601110-1-chencheng@fnnas.com
> Assisted-by: LLM
> Signed-off-by: Doug V Johnson <me@dougvj.me>
> ---
>   drivers/md/raid5.c | 4 ++++
>   1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
> index b91545ce090d..86ea65559576 100644
> --- a/drivers/md/raid5.c
> +++ b/drivers/md/raid5.c
> @@ -3773,6 +3773,10 @@ static void handle_failed_reshape(struct r5conf *conf, struct stripe_head *sh,
>   	bool was_expanding = test_and_clear_bit(STRIPE_EXPANDING, &sh->state);
>   	bool was_ready = test_and_clear_bit(STRIPE_EXPAND_READY, &sh->state);
>   
> +	/* Freeze reshaping to prevent data loss */
> +	set_bit(MD_RECOVERY_FROZEN, &conf->mddev->recovery);
> +	md_sync_error(conf->mddev);
> +
>   	if (was_expanding || was_ready) {
>   		atomic_dec(&conf->reshape_stripes);
>   		wake_up(&conf->wait_for_reshape);

Reviewed-by: Chen Cheng <chencheng@fnnas.com>

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-10-22  7:37 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <20261016042537.277516-1-me@dougvj.me>
2026-10-16  4:25 ` [PATCH v4 1/3] md/raid5: freeze and interrupt reshape when handling failed reshape Doug V Johnson
2026-10-22  7:36   ` Chen Cheng
2026-10-16  4:25 ` [PATCH v4 3/3] md/raid5: warn when a read fails due in some part to bad-block metadata Doug V Johnson
2026-10-16  4:25 ` [PATCH v4 2/3] md/raid5: do not reshape if overlapping rdev bad block ranges prevent it Doug V Johnson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®