[PATCH 3/3] btrfs: roll back sprout setup after device add failure

From: Guanghui Yang

Date: Tue Jul 14 2026 - 02:13:17 EST


btrfs_init_new_device() calls btrfs_setup_sprout() before creating the
first writable chunks for a seed filesystem. That moves the seed devices
out of fs_info->fs_devices, clears the seeding state and installs a new
fsid for the sprout filesystem.

If a later step fails, the error path removes the new device but leaves
fs_info->fs_devices in the partially initialized sprout state. The mounted
filesystem can then be left with no open devices after the failed device
add.

Add the inverse of btrfs_setup_sprout() and use it from the error path so
the mounted seed filesystem is restored before the temporary seed_devices
copy is released.

Fixes: 2b82032c34ec ("Btrfs: Seed device support")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Guanghui Yang <3497809730@xxxxxx>
---
fs/btrfs/volumes.c | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)

diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index a14f186f5b07..9aba2762025b 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -2772,6 +2772,42 @@ static void btrfs_setup_sprout(struct btrfs_fs_info *fs_info,
btrfs_set_super_flags(disk_super, super_flags);
}

+static void btrfs_rollback_sprout(struct btrfs_fs_info *fs_info,
+ struct btrfs_fs_devices *seed_devices)
+{
+ struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
+ struct btrfs_super_block *disk_super = fs_info->super_copy;
+ struct btrfs_device *device;
+ u64 super_flags;
+
+ lockdep_assert_held(&uuid_mutex);
+ lockdep_assert_held(&fs_devices->device_list_mutex);
+
+ list_del_init(&seed_devices->seed_list);
+ list_splice_init_rcu(&seed_devices->devices, &fs_devices->devices,
+ synchronize_rcu);
+ list_for_each_entry(device, &fs_devices->devices, dev_list)
+ device->fs_devices = fs_devices;
+
+ fs_devices->seeding = true;
+ fs_devices->num_devices = seed_devices->num_devices;
+ fs_devices->open_devices = seed_devices->open_devices;
+ fs_devices->missing_devices = seed_devices->missing_devices;
+ fs_devices->rotating = seed_devices->rotating;
+ fs_devices->latest_dev = seed_devices->latest_dev;
+
+ memcpy(fs_devices->fsid, seed_devices->fsid, BTRFS_FSID_SIZE);
+ memcpy(fs_devices->metadata_uuid, seed_devices->metadata_uuid,
+ BTRFS_FSID_SIZE);
+ memcpy(disk_super->fsid, seed_devices->fsid, BTRFS_FSID_SIZE);
+
+ super_flags = btrfs_super_flags(disk_super) | BTRFS_SUPER_FLAG_SEEDING;
+ btrfs_set_super_flags(disk_super, super_flags);
+
+ seed_devices->opened = 0;
+ free_fs_devices(seed_devices);
+}
+
/*
* Store the expected generation for seed devices in device items.
*/
@@ -3088,6 +3124,8 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
orig_super_total_bytes);
btrfs_set_super_num_devices(fs_info->super_copy,
orig_super_num_devices);
+ if (seeding_dev)
+ btrfs_rollback_sprout(fs_info, seed_devices);
btrfs_update_per_profile_avail(fs_info);
mutex_unlock(&fs_info->chunk_mutex);
mutex_unlock(&fs_info->fs_devices->device_list_mutex);
--
2.52.0.windows.1