diff options
Diffstat (limited to 'fs/btrfs/qgroup.c')
| -rw-r--r-- | fs/btrfs/qgroup.c | 24 | 
1 files changed, 23 insertions, 1 deletions
| diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c index 6c037f1252b7..8928275823a1 100644 --- a/fs/btrfs/qgroup.c +++ b/fs/btrfs/qgroup.c @@ -940,6 +940,14 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info)  	int ret = 0;  	int slot; +	/* +	 * We need to have subvol_sem write locked, to prevent races between +	 * concurrent tasks trying to enable quotas, because we will unlock +	 * and relock qgroup_ioctl_lock before setting fs_info->quota_root +	 * and before setting BTRFS_FS_QUOTA_ENABLED. +	 */ +	lockdep_assert_held_write(&fs_info->subvol_sem); +  	mutex_lock(&fs_info->qgroup_ioctl_lock);  	if (fs_info->quota_root)  		goto out; @@ -1117,8 +1125,19 @@ out_add_root:  		goto out_free_path;  	} +	mutex_unlock(&fs_info->qgroup_ioctl_lock); +	/* +	 * Commit the transaction while not holding qgroup_ioctl_lock, to avoid +	 * a deadlock with tasks concurrently doing other qgroup operations, such +	 * adding/removing qgroups or adding/deleting qgroup relations for example, +	 * because all qgroup operations first start or join a transaction and then +	 * lock the qgroup_ioctl_lock mutex. +	 * We are safe from a concurrent task trying to enable quotas, by calling +	 * this function, since we are serialized by fs_info->subvol_sem. +	 */  	ret = btrfs_commit_transaction(trans);  	trans = NULL; +	mutex_lock(&fs_info->qgroup_ioctl_lock);  	if (ret)  		goto out_free_path; @@ -3142,6 +3161,7 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans,  			      struct btrfs_path *path)  {  	struct btrfs_fs_info *fs_info = trans->fs_info; +	struct btrfs_root *extent_root;  	struct btrfs_key found;  	struct extent_buffer *scratch_leaf = NULL;  	struct ulist *roots = NULL; @@ -3151,7 +3171,9 @@ static int qgroup_rescan_leaf(struct btrfs_trans_handle *trans,  	int ret;  	mutex_lock(&fs_info->qgroup_rescan_lock); -	ret = btrfs_search_slot_for_read(fs_info->extent_root, +	extent_root = btrfs_extent_root(fs_info, +				fs_info->qgroup_rescan_progress.objectid); +	ret = btrfs_search_slot_for_read(extent_root,  					 &fs_info->qgroup_rescan_progress,  					 path, 1, 0); |