diff options
author | David Sterba <dsterba@suse.com> | 2018-12-04 10:11:56 -0500 |
---|---|---|
committer | David Sterba <dsterba@suse.com> | 2018-12-17 08:51:48 -0500 |
commit | 0e94c4f45d14cf89d1f40c91b0a8517e791672a7 (patch) | |
tree | 8066c321195c5c78093005ab56f566fda6bd069a /fs/btrfs/scrub.c | |
parent | 92f7ba434f51e8e9317f1d166105889aa230abd2 (diff) |
btrfs: scrub: move scrub_setup_ctx allocation out of device_list_mutex
The scrub context is allocated with GFP_KERNEL and called from
btrfs_scrub_dev under the fs_info::device_list_mutex. This is not safe
regarding reclaim that could try to flush filesystem data in order to
get the memory. And the device_list_mutex is held during superblock
commit, so this would cause a lockup.
Move the alocation and initialization before any changes that require
the mutex.
Signed-off-by: David Sterba <dsterba@suse.com>
Diffstat (limited to 'fs/btrfs/scrub.c')
-rw-r--r-- | fs/btrfs/scrub.c | 30 |
1 files changed, 18 insertions, 12 deletions
diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c index edad1a40f797..367ab0911c01 100644 --- a/fs/btrfs/scrub.c +++ b/fs/btrfs/scrub.c | |||
@@ -3835,13 +3835,18 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, | |||
3835 | return -EINVAL; | 3835 | return -EINVAL; |
3836 | } | 3836 | } |
3837 | 3837 | ||
3838 | /* Allocate outside of device_list_mutex */ | ||
3839 | sctx = scrub_setup_ctx(fs_info, is_dev_replace); | ||
3840 | if (IS_ERR(sctx)) | ||
3841 | return PTR_ERR(sctx); | ||
3838 | 3842 | ||
3839 | mutex_lock(&fs_info->fs_devices->device_list_mutex); | 3843 | mutex_lock(&fs_info->fs_devices->device_list_mutex); |
3840 | dev = btrfs_find_device(fs_info, devid, NULL, NULL); | 3844 | dev = btrfs_find_device(fs_info, devid, NULL, NULL); |
3841 | if (!dev || (test_bit(BTRFS_DEV_STATE_MISSING, &dev->dev_state) && | 3845 | if (!dev || (test_bit(BTRFS_DEV_STATE_MISSING, &dev->dev_state) && |
3842 | !is_dev_replace)) { | 3846 | !is_dev_replace)) { |
3843 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); | 3847 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); |
3844 | return -ENODEV; | 3848 | ret = -ENODEV; |
3849 | goto out_free_ctx; | ||
3845 | } | 3850 | } |
3846 | 3851 | ||
3847 | if (!is_dev_replace && !readonly && | 3852 | if (!is_dev_replace && !readonly && |
@@ -3849,7 +3854,8 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, | |||
3849 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); | 3854 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); |
3850 | btrfs_err_in_rcu(fs_info, "scrub: device %s is not writable", | 3855 | btrfs_err_in_rcu(fs_info, "scrub: device %s is not writable", |
3851 | rcu_str_deref(dev->name)); | 3856 | rcu_str_deref(dev->name)); |
3852 | return -EROFS; | 3857 | ret = -EROFS; |
3858 | goto out_free_ctx; | ||
3853 | } | 3859 | } |
3854 | 3860 | ||
3855 | mutex_lock(&fs_info->scrub_lock); | 3861 | mutex_lock(&fs_info->scrub_lock); |
@@ -3857,7 +3863,8 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, | |||
3857 | test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &dev->dev_state)) { | 3863 | test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &dev->dev_state)) { |
3858 | mutex_unlock(&fs_info->scrub_lock); | 3864 | mutex_unlock(&fs_info->scrub_lock); |
3859 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); | 3865 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); |
3860 | return -EIO; | 3866 | ret = -EIO; |
3867 | goto out_free_ctx; | ||
3861 | } | 3868 | } |
3862 | 3869 | ||
3863 | down_read(&fs_info->dev_replace.rwsem); | 3870 | down_read(&fs_info->dev_replace.rwsem); |
@@ -3867,7 +3874,8 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, | |||
3867 | up_read(&fs_info->dev_replace.rwsem); | 3874 | up_read(&fs_info->dev_replace.rwsem); |
3868 | mutex_unlock(&fs_info->scrub_lock); | 3875 | mutex_unlock(&fs_info->scrub_lock); |
3869 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); | 3876 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); |
3870 | return -EINPROGRESS; | 3877 | ret = -EINPROGRESS; |
3878 | goto out_free_ctx; | ||
3871 | } | 3879 | } |
3872 | up_read(&fs_info->dev_replace.rwsem); | 3880 | up_read(&fs_info->dev_replace.rwsem); |
3873 | 3881 | ||
@@ -3875,16 +3883,9 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, | |||
3875 | if (ret) { | 3883 | if (ret) { |
3876 | mutex_unlock(&fs_info->scrub_lock); | 3884 | mutex_unlock(&fs_info->scrub_lock); |
3877 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); | 3885 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); |
3878 | return ret; | 3886 | goto out_free_ctx; |
3879 | } | 3887 | } |
3880 | 3888 | ||
3881 | sctx = scrub_setup_ctx(fs_info, is_dev_replace); | ||
3882 | if (IS_ERR(sctx)) { | ||
3883 | mutex_unlock(&fs_info->scrub_lock); | ||
3884 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); | ||
3885 | scrub_workers_put(fs_info); | ||
3886 | return PTR_ERR(sctx); | ||
3887 | } | ||
3888 | sctx->readonly = readonly; | 3889 | sctx->readonly = readonly; |
3889 | dev->scrub_ctx = sctx; | 3890 | dev->scrub_ctx = sctx; |
3890 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); | 3891 | mutex_unlock(&fs_info->fs_devices->device_list_mutex); |
@@ -3938,6 +3939,11 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 devid, u64 start, | |||
3938 | scrub_put_ctx(sctx); | 3939 | scrub_put_ctx(sctx); |
3939 | 3940 | ||
3940 | return ret; | 3941 | return ret; |
3942 | |||
3943 | out_free_ctx: | ||
3944 | scrub_free_ctx(sctx); | ||
3945 | |||
3946 | return ret; | ||
3941 | } | 3947 | } |
3942 | 3948 | ||
3943 | void btrfs_scrub_pause(struct btrfs_fs_info *fs_info) | 3949 | void btrfs_scrub_pause(struct btrfs_fs_info *fs_info) |