]> git.hungrycats.org Git - linux/commitdiff
btrfs: roll back sprout setup after device add failure
authorGuanghui Yang <3497809730@qq.com>
Mon, 10 Aug 2026 23:32:27 +0000 (09:02 +0930)
committerDavid Sterba <dsterba@suse.com>
Mon, 14 Sep 2026 11:23:36 +0000 (13:23 +0200)
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")
Assisted-by: Codex:gpt-5
Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Guanghui Yang <3497809730@qq.com>
[ Fix a conflict with per-profile available space, revert sprout before
  updating per-profile available space estimation. ]
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/volumes.c

index 7fb0bf742a2969747bebb6dd0fda2dc41230b0f7..949e40baff3343ca4663f960392321f539102afd 100644 (file)
@@ -2783,6 +2783,41 @@ 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.
  */
@@ -3134,6 +3169,8 @@ error_sysfs:
                                    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);