The device_list_mutex and scrub_lock creates a nested locks in
btrfs_scrub_dev().

During lock the order is device_list_mutex and then scrub_lock, and during
unlock, the order is device_list_mutex and then scrub_lock.
Fix this to the lock order of scrub_lock and then device_list_mutex.

Signed-off-by: Anand Jain <anand.j...@oracle.com>
---
v1->v2: change the order of lock acquire first scrub_lock and then
        device_list_mutex, which matches with the order of unlock.
        The extra line which are now in the scrub_lock are ok to be
        under the scrub_lock.
 fs/btrfs/scrub.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index 902819d3cf41..a9d6fc3b01d4 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -3813,28 +3813,29 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 
devid, u64 start,
                return -EINVAL;
        }
 
-
+       mutex_lock(&fs_info->scrub_lock);
        mutex_lock(&fs_info->fs_devices->device_list_mutex);
        dev = btrfs_find_device(fs_info, devid, NULL, NULL);
        if (!dev || (test_bit(BTRFS_DEV_STATE_MISSING, &dev->dev_state) &&
                     !is_dev_replace)) {
                mutex_unlock(&fs_info->fs_devices->device_list_mutex);
+               mutex_unlock(&fs_info->scrub_lock);
                return -ENODEV;
        }
 
        if (!is_dev_replace && !readonly &&
            !test_bit(BTRFS_DEV_STATE_WRITEABLE, &dev->dev_state)) {
                mutex_unlock(&fs_info->fs_devices->device_list_mutex);
+               mutex_unlock(&fs_info->scrub_lock);
                btrfs_err_in_rcu(fs_info, "scrub: device %s is not writable",
                                rcu_str_deref(dev->name));
                return -EROFS;
        }
 
-       mutex_lock(&fs_info->scrub_lock);
        if (!test_bit(BTRFS_DEV_STATE_IN_FS_METADATA, &dev->dev_state) ||
            test_bit(BTRFS_DEV_STATE_REPLACE_TGT, &dev->dev_state)) {
-               mutex_unlock(&fs_info->scrub_lock);
                mutex_unlock(&fs_info->fs_devices->device_list_mutex);
+               mutex_unlock(&fs_info->scrub_lock);
                return -EIO;
        }
 
@@ -3843,23 +3844,23 @@ int btrfs_scrub_dev(struct btrfs_fs_info *fs_info, u64 
devid, u64 start,
            (!is_dev_replace &&
             btrfs_dev_replace_is_ongoing(&fs_info->dev_replace))) {
                btrfs_dev_replace_read_unlock(&fs_info->dev_replace);
-               mutex_unlock(&fs_info->scrub_lock);
                mutex_unlock(&fs_info->fs_devices->device_list_mutex);
+               mutex_unlock(&fs_info->scrub_lock);
                return -EINPROGRESS;
        }
        btrfs_dev_replace_read_unlock(&fs_info->dev_replace);
 
        ret = scrub_workers_get(fs_info, is_dev_replace);
        if (ret) {
-               mutex_unlock(&fs_info->scrub_lock);
                mutex_unlock(&fs_info->fs_devices->device_list_mutex);
+               mutex_unlock(&fs_info->scrub_lock);
                return ret;
        }
 
        sctx = scrub_setup_ctx(dev, is_dev_replace);
        if (IS_ERR(sctx)) {
-               mutex_unlock(&fs_info->scrub_lock);
                mutex_unlock(&fs_info->fs_devices->device_list_mutex);
+               mutex_unlock(&fs_info->scrub_lock);
                scrub_workers_put(fs_info);
                return PTR_ERR(sctx);
        }
-- 
1.8.3.1

Reply via email to