summaryrefslogtreecommitdiffstats
path: root/fs/btrfs/scrub.c
diff options
context:
space:
mode:
authorDavid Sterba <dsterba@suse.com>2018-12-04 10:11:56 -0500
committerDavid Sterba <dsterba@suse.com>2018-12-17 08:51:48 -0500
commit0e94c4f45d14cf89d1f40c91b0a8517e791672a7 (patch)
tree8066c321195c5c78093005ab56f566fda6bd069a /fs/btrfs/scrub.c
parent92f7ba434f51e8e9317f1d166105889aa230abd2 (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.c30
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
3943out_free_ctx:
3944 scrub_free_ctx(sctx);
3945
3946 return ret;
3941} 3947}
3942 3948
3943void btrfs_scrub_pause(struct btrfs_fs_info *fs_info) 3949void btrfs_scrub_pause(struct btrfs_fs_info *fs_info)