mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-05 10:04:12 +08:00
btrfs: qgroup: fix deadlock between rescan worker and remove qgroup
The commite804861bd4
("btrfs: fix deadlock between quota disable and qgroup rescan worker") by Kawasaki resolves deadlock between quota disable and qgroup rescan worker. But also there is a deadlock case like it. It's about enabling or disabling quota and creating or removing qgroup. It can be reproduced in simple script below. for i in {1..100} do btrfs quota enable /mnt & btrfs qgroup create 1/0 /mnt & btrfs qgroup destroy 1/0 /mnt & btrfs quota disable /mnt & done Here's why the deadlock happens: 1) The quota rescan task is running. 2) Task A calls btrfs_quota_disable(), locks the qgroup_ioctl_lock mutex, and then calls btrfs_qgroup_wait_for_completion(), to wait for the quota rescan task to complete. 3) Task B calls btrfs_remove_qgroup() and it blocks when trying to lock the qgroup_ioctl_lock mutex, because it's being held by task A. At that point task B is holding a transaction handle for the current transaction. 4) The quota rescan task calls btrfs_commit_transaction(). This results in it waiting for all other tasks to release their handles on the transaction, but task B is blocked on the qgroup_ioctl_lock mutex while holding a handle on the transaction, and that mutex is being held by task A, which is waiting for the quota rescan task to complete, resulting in a deadlock between these 3 tasks. To resolve this issue, the thread disabling quota should unlock qgroup_ioctl_lock before waiting rescan completion. Move btrfs_qgroup_wait_for_completion() after unlock of qgroup_ioctl_lock. Fixes:e804861bd4
("btrfs: fix deadlock between quota disable and qgroup rescan worker") CC: stable@vger.kernel.org # 5.4+ Reviewed-by: Filipe Manana <fdmanana@suse.com> Reviewed-by: Shin'ichiro Kawasaki <shinichiro.kawasaki@wdc.com> Signed-off-by: Sidong Yang <realwakka@gmail.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
parent
5fd76bf31c
commit
d4aef1e122
@ -1196,6 +1196,14 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
|
|||||||
if (!fs_info->quota_root)
|
if (!fs_info->quota_root)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unlock the qgroup_ioctl_lock mutex before waiting for the rescan worker to
|
||||||
|
* complete. Otherwise we can deadlock because btrfs_remove_qgroup() needs
|
||||||
|
* to lock that mutex while holding a transaction handle and the rescan
|
||||||
|
* worker needs to commit a transaction.
|
||||||
|
*/
|
||||||
|
mutex_unlock(&fs_info->qgroup_ioctl_lock);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Request qgroup rescan worker to complete and wait for it. This wait
|
* Request qgroup rescan worker to complete and wait for it. This wait
|
||||||
* must be done before transaction start for quota disable since it may
|
* must be done before transaction start for quota disable since it may
|
||||||
@ -1203,7 +1211,6 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
|
|||||||
*/
|
*/
|
||||||
clear_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
|
clear_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
|
||||||
btrfs_qgroup_wait_for_completion(fs_info, false);
|
btrfs_qgroup_wait_for_completion(fs_info, false);
|
||||||
mutex_unlock(&fs_info->qgroup_ioctl_lock);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 1 For the root item
|
* 1 For the root item
|
||||||
|
Loading…
Reference in New Issue
Block a user