Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

btrfs: reset device back to allocation state when removing

When closing a device, btrfs_close_one_device() first allocates a new
device, copies the device to close's name, replaces it in the dev_list
with the copy and then finally frees it.

This involves two memory allocation, which can potentially fail. As this
code path is tricky to unwind, the allocation failures where handled by
BUG_ON()s.

But this copying isn't strictly needed, all that is needed is resetting
the device in question to it's state it had after the allocation.

Signed-off-by: Johannes Thumshirn <jthumshirn@suse.de>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>

authored by

Johannes Thumshirn and committed by
David Sterba
321f69f8 3fff3975

+12 -17
+12 -17
fs/btrfs/volumes.c
··· 1066 1066 static void btrfs_close_one_device(struct btrfs_device *device) 1067 1067 { 1068 1068 struct btrfs_fs_devices *fs_devices = device->fs_devices; 1069 - struct btrfs_device *new_device; 1070 - struct rcu_string *name; 1071 1069 1072 1070 if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state) && 1073 1071 device->devid != BTRFS_DEV_REPLACE_DEVID) { ··· 1077 1079 fs_devices->missing_devices--; 1078 1080 1079 1081 btrfs_close_bdev(device); 1080 - if (device->bdev) 1082 + if (device->bdev) { 1081 1083 fs_devices->open_devices--; 1082 - 1083 - new_device = btrfs_alloc_device(NULL, &device->devid, 1084 - device->uuid); 1085 - BUG_ON(IS_ERR(new_device)); /* -ENOMEM */ 1086 - 1087 - /* Safe because we are under uuid_mutex */ 1088 - if (device->name) { 1089 - name = rcu_string_strdup(device->name->str, GFP_NOFS); 1090 - BUG_ON(!name); /* -ENOMEM */ 1091 - rcu_assign_pointer(new_device->name, name); 1084 + device->bdev = NULL; 1092 1085 } 1086 + clear_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state); 1093 1087 1094 - list_replace_rcu(&device->dev_list, &new_device->dev_list); 1095 - new_device->fs_devices = device->fs_devices; 1088 + device->fs_info = NULL; 1089 + atomic_set(&device->dev_stats_ccnt, 0); 1090 + extent_io_tree_release(&device->alloc_state); 1096 1091 1097 - synchronize_rcu(); 1098 - btrfs_free_device(device); 1092 + /* Verify the device is back in a pristine state */ 1093 + ASSERT(!test_bit(BTRFS_DEV_STATE_FLUSH_SENT, &device->dev_state)); 1094 + ASSERT(!test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &device->dev_state)); 1095 + ASSERT(list_empty(&device->dev_alloc_list)); 1096 + ASSERT(list_empty(&device->post_commit_list)); 1097 + ASSERT(atomic_read(&device->reada_in_flight) == 0); 1099 1098 } 1100 1099 1101 1100 static int close_fs_devices(struct btrfs_fs_devices *fs_devices)