For this callback, btrfs will: - Go degraded if the fs can still maintain RW operations - 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. Signed-off-by: Qu Wenruo <wqu@xxxxxxxx> --- fs/btrfs/super.c | 34 ++++++++++++++++++++++++++++++++++ fs/btrfs/volumes.c | 2 ++ fs/btrfs/volumes.h | 5 +++++ 3 files changed, 41 insertions(+) diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index 82ce0625b2f0..f82f9be41321 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2397,6 +2397,39 @@ static long btrfs_free_cached_objects(struct super_block *sb, struct shrink_cont return 0; } +static void btrfs_shutdown_bdev(struct super_block *sb, struct block_device *bdev) +{ + struct btrfs_fs_info *fs_info = btrfs_sb(sb); + struct btrfs_device *device; + struct btrfs_dev_lookup_args lookup_args = { .devt = bdev->bd_dev }; + + mutex_lock(&fs_info->fs_devices->device_list_mutex); + device = btrfs_find_device(fs_info->fs_devices, &lookup_args); + mutex_unlock(&fs_info->fs_devices->device_list_mutex); + if (!device) { + btrfs_warn(fs_info, "unable to find btrfs device for block device '%pg'", + bdev); + 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--; + } + if (!btrfs_check_rw_degradable(fs_info, device)) { + btrfs_warn_in_rcu(fs_info, + "btrfs device id %llu path %s has gone missing, can not maintain read-write", + device->devid, btrfs_dev_name(device)); + btrfs_shutdown(fs_info); + return; + } + btrfs_warn_in_rcu(fs_info, + "btrfs device id %llu path %s has gone missing, continue degraded", + device->devid, btrfs_dev_name(device)); + btrfs_set_opt(fs_info->mount_opt, DEGRADED); +} + static const struct super_operations btrfs_super_ops = { .drop_inode = btrfs_drop_inode, .evict_inode = btrfs_evict_inode, @@ -2412,6 +2445,7 @@ 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, + .shutdown_bdev = btrfs_shutdown_bdev, }; static const struct file_operations btrfs_ctl_fops = { diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 541d17ca5dcf..5639df71ef91 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c @@ -6792,6 +6792,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 afa71d315c46..2c5e3ebc5a2b 100644 --- a/fs/btrfs/volumes.h +++ b/fs/btrfs/volumes.h @@ -652,6 +652,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