[PATCH V2 1/6] md/raid0: don't leave mddev in an inconsistent state when takeover fails
From: Zizhi Wo
Date: Sun Oct 18 2026 - 21:20:54 EST
From: Zizhi Wo <wozizhi@xxxxxxxxxx>
raid0_takeover_raid45(), raid0_takeover_raid10() and raid0_takeover_raid1()
update mddev before create_strip_zones(), which can fail.
On failure, level_store() only rolls back new_level, new_layout,
new_chunk_sectors, raid_disks and delta_disks. The old personality is
therefore left with a reset resync_offset, cleared feature flags, and, for
raid1, the wrong chunk size.
Set only what create_strip_zones() needs before calling it, and move the
rest after it succeeds.
create_strip_zones() also rounds rdev->sectors, but the old levels do not
use it for I/O, so that is harmless. And mddev->degraded is already reset
by level_store() on success, so drop the redundant assignment in
raid0_takeover_raid10().
Fixes: 9af204cf720c ("md: Add support for Raid5->Raid0 and Raid10->Raid0 takeover")
Fixes: fc3a08b85b7a ("Add raid1->raid0 takeover support")
Signed-off-by: Zizhi Wo <wozizhi@xxxxxxxxxx>
---
drivers/md/raid0.c | 36 +++++++++++++++++++++++-------------
1 file changed, 23 insertions(+), 13 deletions(-)
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index 35e103f0c2c3..8157e1058a24 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -669,15 +669,16 @@ static void *raid0_takeover_raid45(struct mddev *mddev)
mddev->new_level = 0;
mddev->new_layout = 0;
mddev->new_chunk_sectors = mddev->chunk_sectors;
mddev->raid_disks--;
mddev->delta_disks = -1;
- /* make sure it will be not marked as dirty */
- mddev->resync_offset = MaxSector;
- mddev_clear_unsupported_flags(mddev, UNSUPPORTED_MDDEV_FLAGS);
- create_strip_zones(mddev, &priv_conf);
+ if (!create_strip_zones(mddev, &priv_conf)) {
+ /* make sure it will be not marked as dirty */
+ mddev->resync_offset = MaxSector;
+ mddev_clear_unsupported_flags(mddev, UNSUPPORTED_MDDEV_FLAGS);
+ }
return priv_conf;
}
static void *raid0_takeover_raid10(struct mddev *mddev)
@@ -711,23 +712,25 @@ static void *raid0_takeover_raid10(struct mddev *mddev)
mddev->new_level = 0;
mddev->new_layout = 0;
mddev->new_chunk_sectors = mddev->chunk_sectors;
mddev->delta_disks = - mddev->raid_disks / 2;
mddev->raid_disks += mddev->delta_disks;
- mddev->degraded = 0;
- /* make sure it will be not marked as dirty */
- mddev->resync_offset = MaxSector;
- mddev_clear_unsupported_flags(mddev, UNSUPPORTED_MDDEV_FLAGS);
- create_strip_zones(mddev, &priv_conf);
+ if (!create_strip_zones(mddev, &priv_conf)) {
+ /* make sure it will be not marked as dirty */
+ mddev->resync_offset = MaxSector;
+ mddev_clear_unsupported_flags(mddev, UNSUPPORTED_MDDEV_FLAGS);
+ }
+
return priv_conf;
}
static void *raid0_takeover_raid1(struct mddev *mddev)
{
struct r0conf *priv_conf;
int chunksect;
+ int old_chunk_sectors;
/* Check layout:
* - (N - 1) mirror drives must be already faulty
*/
if ((mddev->raid_disks - 1) != mddev->degraded) {
@@ -748,22 +751,29 @@ static void *raid0_takeover_raid1(struct mddev *mddev)
if ((chunksect << 9) < PAGE_SIZE)
/* array size does not allow a suitable chunk size */
return ERR_PTR(-EINVAL);
+ /* Save for rollback if create_strip_zones() fails */
+ old_chunk_sectors = mddev->chunk_sectors;
+
/* Set new parameters */
mddev->new_level = 0;
mddev->new_layout = 0;
mddev->new_chunk_sectors = chunksect;
mddev->chunk_sectors = chunksect;
mddev->delta_disks = 1 - mddev->raid_disks;
mddev->raid_disks = 1;
- /* make sure it will be not marked as dirty */
- mddev->resync_offset = MaxSector;
- mddev_clear_unsupported_flags(mddev, UNSUPPORTED_MDDEV_FLAGS);
- create_strip_zones(mddev, &priv_conf);
+ if (!create_strip_zones(mddev, &priv_conf)) {
+ /* make sure it will be not marked as dirty */
+ mddev->resync_offset = MaxSector;
+ mddev_clear_unsupported_flags(mddev, UNSUPPORTED_MDDEV_FLAGS);
+ } else {
+ mddev->chunk_sectors = old_chunk_sectors;
+ }
+
return priv_conf;
}
static void *raid0_takeover(struct mddev *mddev)
{
--
2.52.0