Merge tag 'fixes-for-5.3-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/mtd...
[linux-2.6-microblaze.git] / fs / btrfs / qgroup.c
index 3e6ffbb..f8a3c1b 100644 (file)
@@ -2614,6 +2614,7 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
        int ret = 0;
        int i;
        u64 *i_qgroups;
+       bool committing = false;
        struct btrfs_fs_info *fs_info = trans->fs_info;
        struct btrfs_root *quota_root;
        struct btrfs_qgroup *srcgroup;
@@ -2621,7 +2622,25 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
        u32 level_size = 0;
        u64 nums;
 
-       mutex_lock(&fs_info->qgroup_ioctl_lock);
+       /*
+        * There are only two callers of this function.
+        *
+        * One in create_subvol() in the ioctl context, which needs to hold
+        * the qgroup_ioctl_lock.
+        *
+        * The other one in create_pending_snapshot() where no other qgroup
+        * code can modify the fs as they all need to either start a new trans
+        * or hold a trans handler, thus we don't need to hold
+        * qgroup_ioctl_lock.
+        * This would avoid long and complex lock chain and make lockdep happy.
+        */
+       spin_lock(&fs_info->trans_lock);
+       if (trans->transaction->state == TRANS_STATE_COMMIT_DOING)
+               committing = true;
+       spin_unlock(&fs_info->trans_lock);
+
+       if (!committing)
+               mutex_lock(&fs_info->qgroup_ioctl_lock);
        if (!test_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags))
                goto out;
 
@@ -2785,7 +2804,8 @@ int btrfs_qgroup_inherit(struct btrfs_trans_handle *trans, u64 srcid,
 unlock:
        spin_unlock(&fs_info->qgroup_lock);
 out:
-       mutex_unlock(&fs_info->qgroup_ioctl_lock);
+       if (!committing)
+               mutex_unlock(&fs_info->qgroup_ioctl_lock);
        return ret;
 }