btrfs: detach failed sprout device from transaction update list
Subsystems (2)
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH v3 1/3 btrfs: detach failed sprout device from transaction update list
When creating the first metadata chunk for a sprout filesystem, create_chunk() adds the new device to the transaction dev_update_list through device->post_commit_list.
If the subsequent system chunk creation fails, btrfs_init_new_device() aborts the transaction and releases the device while post_commit_list is still linked. This triggers a warning in btrfs_free_device() and leaves the transaction list referencing freed memory.
Detach the device while holding chunk_mutex before releasing it.
--- fs/btrfs/volumes.c | 2 ++ 1 file changed, 2 insertions(+)
fs/btrfs/volumes.c +2 -0
@@ -2988,6 +2988,8 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_pathbtrfs_sysfs_remove_device (device);mutex_lock (&fs_info->fs_devices->device_list_mutex);mutex_lock (&fs_info->chunk_mutex); +if (!list_empty (&device->post_commit_list)) +list_del_init (&device->post_commit_list);list_del_rcu (&device->dev_list);list_del (&device->dev_alloc_list); fs_info->fs_devices->num_devices--;
Signature · 1 line
2.53.0
PATCH v3 2/3 btrfs: restore active device pointers after failed sprout
In reply to a message not in the archivebtrfs_init_new_device() switches latest_dev and possibly s_bdev from the seed device to the new sprout device before creating the first writable chunks.
If chunk creation or the subsequent sprout setup fails, the error path releases the new device without switching those pointers back. btrfs_show_devname() can then dereference the freed latest_dev and crash.
Restore the active device pointers to the latest seed device before removing and releasing the failed sprout device.
--- fs/btrfs/volumes.c | 3 +++ 1 file changed, 3 insertions(+)
fs/btrfs/volumes.c +3 -0
@@ -2987,6 +2987,9 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path error_sysfs:btrfs_sysfs_remove_device (device);mutex_lock (&fs_info->fs_devices->device_list_mutex); +if (seeding_dev) +btrfs_assign_next_active_device (device, + seed_devices->latest_dev);mutex_lock (&fs_info->chunk_mutex);if (!list_empty (&device->post_commit_list))list_del_init (&device->post_commit_list);
Signature · 1 line
2.53.0
PATCH v3 3/3 btrfs: roll back sprout setup after device add failure
In reply to a message not in the archivebtrfs_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.
--- fs/btrfs/volumes.c | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+)
fs/btrfs/volumes.c +38 -0
@@ -2699,6 +2699,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. */
@@ -3005,6 +3041,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);mutex_unlock (&fs_info->chunk_mutex);mutex_unlock (&fs_info->fs_devices->device_list_mutex); error_trans:
Signature · 1 line
2.53.0