For this callback, btrfs will: - Go degraded if the fs can still maintain RW operations And of course mark the target device as missing. - Shutdown if the fs can not maintain RW operations I know the shutdown can be a little overkilled, if one has a RAID1 metadata and RAID0 data, in that case one can still read data with 50% rate to got some good data. But it can also be as bad as the only device went missing for a single device btrfs. So here we go safe other than sorry when handling missing device. And the remove_bdev callback will be hidden behind experimental features for now, the reasons are: - There are not enough btrfs specific bdev removal test cases The existing test cases are all removing the only device, thus only exercises the shutdown branch. - Not yet determined what's the expected behavior Although the current auto-degrade behavior is no worse than the old behavior, it may not always be what the end users want. Before there is a concrete solution, better hide the new feature from end users. Signed-off-by: Qu Wenruo <wqu@xxxxxxxx> --- fs/btrfs/super.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/volumes.c | 2 ++ fs/btrfs/volumes.h | 5 +++++ 3 files changed, 62 insertions(+) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 5a07330fb3a6..3fba3d6309a2 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2412,6 +2412,58 @@ static long btrfs_free_cached_objects(struct super_block *sb, struct shrink_cont return 0; } +#ifdef CONFIG_BTRFS_EXPERIMENTAL +static void btrfs_remove_bdev(struct super_block *sb, struct block_device *bdev, + bool surprise) +{ + struct btrfs_fs_info *fs_info = btrfs_sb(sb); + struct btrfs_device *device; + struct btrfs_dev_lookup_args lookup_args = { .devt = bdev->bd_dev }; + bool can_rw; + int ret; + + if (!surprise) { + ret = btrfs_sync_fs(sb, 1); + if (ret) + btrfs_warn(fs_info, + "filesystem failed to sync in preparation for device loss: %d", + ret); + } + + mutex_lock(&fs_info->fs_devices->device_list_mutex); + device = btrfs_find_device(fs_info->fs_devices, &lookup_args); + if (!device) { + btrfs_warn(fs_info, "unable to find btrfs device for block device '%pg'", + bdev); + mutex_unlock(&fs_info->fs_devices->device_list_mutex); + return; + } + set_bit(BTRFS_DEV_STATE_MISSING, &device->dev_state); + device->fs_devices->missing_devices++; + if (test_and_clear_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state)) { + list_del_init(&device->dev_alloc_list); + device->fs_devices->rw_devices--; + } + can_rw = btrfs_check_rw_degradable(fs_info, device); + mutex_unlock(&fs_info->fs_devices->device_list_mutex); + /* + * Now device is considered missing, btrfs_device_name() won't give a + * meaningful result anymore. + */ + if (!can_rw) { + btrfs_warn(fs_info, + "btrfs device id %llu has gone missing, can not maintain read-write", + device->devid); + btrfs_force_shutdown(fs_info); + return; + } + btrfs_warn(fs_info, + "btrfs device id %llu has gone missing, continue as degraded", + device->devid); + btrfs_set_opt(fs_info->mount_opt, DEGRADED); +} +#endif + static const struct super_operations btrfs_super_ops = { .drop_inode = btrfs_drop_inode, .evict_inode = btrfs_evict_inode, @@ -2427,6 +2479,9 @@ static const struct super_operations btrfs_super_ops = { .unfreeze_fs = btrfs_unfreeze, .nr_cached_objects = btrfs_nr_cached_objects, .free_cached_objects = btrfs_free_cached_objects, +#ifdef CONFIG_BTRFS_EXPERIMENTAL + .remove_bdev = btrfs_remove_bdev, +#endif }; static const struct file_operations btrfs_ctl_fops = { diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 8ea1a69808a3..8feac0129bdd 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -6794,6 +6794,8 @@ static bool dev_args_match_fs_devices(const struct btrfs_dev_lookup_args *args, static bool dev_args_match_device(const struct btrfs_dev_lookup_args *args, const struct btrfs_device *device) { + if (args->devt) + return device->devt == args->devt; if (args->missing) { if (test_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &device->dev_state) && !device->bdev) diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h index 6acb154ccf87..71e570f8337d 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -663,6 +663,11 @@ struct btrfs_dev_lookup_args { u64 devid; u8 *uuid; u8 *fsid; + /* + * If devt is specified, all other members will be ignored as it is + * enough to uniquely locate a device. + */ + dev_t devt; bool missing; }; -- 2.49.0