Skip to content

Commit 0f2b809

Browse files
josefbacikkdave
authored andcommitted
btrfs: take the cleaner_mutex earlier in qgroup disable
One of my CI runs popped the following lockdep splat ====================================================== WARNING: possible circular locking dependency detected 6.9.0-rc4+ #1 Not tainted ------------------------------------------------------ btrfs/471533 is trying to acquire lock: ffff92ba46980850 (&fs_info->cleaner_mutex){+.+.}-{3:3}, at: btrfs_quota_disable+0x54/0x4c0 but task is already holding lock: ffff92ba46980bd0 (&fs_info->subvol_sem){++++}-{3:3}, at: btrfs_ioctl+0x1c8f/0x2600 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #2 (&fs_info->subvol_sem){++++}-{3:3}: down_read+0x42/0x170 btrfs_rename+0x607/0xb00 btrfs_rename2+0x2e/0x70 vfs_rename+0xaf8/0xfc0 do_renameat2+0x586/0x600 __x64_sys_rename+0x43/0x50 do_syscall_64+0x95/0x180 entry_SYSCALL_64_after_hwframe+0x76/0x7e -> #1 (&sb->s_type->i_mutex_key#16){++++}-{3:3}: down_write+0x3f/0xc0 btrfs_inode_lock+0x40/0x70 prealloc_file_extent_cluster+0x1b0/0x370 relocate_file_extent_cluster+0xb2/0x720 relocate_data_extent+0x107/0x160 relocate_block_group+0x442/0x550 btrfs_relocate_block_group+0x2cb/0x4b0 btrfs_relocate_chunk+0x50/0x1b0 btrfs_balance+0x92f/0x13d0 btrfs_ioctl+0x1abf/0x2600 __x64_sys_ioctl+0x97/0xd0 do_syscall_64+0x95/0x180 entry_SYSCALL_64_after_hwframe+0x76/0x7e -> #0 (&fs_info->cleaner_mutex){+.+.}-{3:3}: __lock_acquire+0x13e7/0x2180 lock_acquire+0xcb/0x2e0 __mutex_lock+0xbe/0xc00 btrfs_quota_disable+0x54/0x4c0 btrfs_ioctl+0x206b/0x2600 __x64_sys_ioctl+0x97/0xd0 do_syscall_64+0x95/0x180 entry_SYSCALL_64_after_hwframe+0x76/0x7e other info that might help us debug this: Chain exists of: &fs_info->cleaner_mutex --> &sb->s_type->i_mutex_key#16 --> &fs_info->subvol_sem Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(&fs_info->subvol_sem); lock(&sb->s_type->i_mutex_key#16); lock(&fs_info->subvol_sem); lock(&fs_info->cleaner_mutex); *** DEADLOCK *** 2 locks held by btrfs/471533: #0: ffff92ba4319e420 (sb_writers#14){.+.+}-{0:0}, at: btrfs_ioctl+0x3b5/0x2600 #1: ffff92ba46980bd0 (&fs_info->subvol_sem){++++}-{3:3}, at: btrfs_ioctl+0x1c8f/0x2600 stack backtrace: CPU: 1 PID: 471533 Comm: btrfs Kdump: loaded Not tainted 6.9.0-rc4+ #1 Call Trace: <TASK> dump_stack_lvl+0x77/0xb0 check_noncircular+0x148/0x160 ? lock_acquire+0xcb/0x2e0 __lock_acquire+0x13e7/0x2180 lock_acquire+0xcb/0x2e0 ? btrfs_quota_disable+0x54/0x4c0 ? lock_is_held_type+0x9a/0x110 __mutex_lock+0xbe/0xc00 ? btrfs_quota_disable+0x54/0x4c0 ? srso_return_thunk+0x5/0x5f ? lock_acquire+0xcb/0x2e0 ? btrfs_quota_disable+0x54/0x4c0 ? btrfs_quota_disable+0x54/0x4c0 btrfs_quota_disable+0x54/0x4c0 btrfs_ioctl+0x206b/0x2600 ? srso_return_thunk+0x5/0x5f ? __do_sys_statfs+0x61/0x70 __x64_sys_ioctl+0x97/0xd0 do_syscall_64+0x95/0x180 ? srso_return_thunk+0x5/0x5f ? reacquire_held_locks+0xd1/0x1f0 ? do_user_addr_fault+0x307/0x8a0 ? srso_return_thunk+0x5/0x5f ? lock_acquire+0xcb/0x2e0 ? srso_return_thunk+0x5/0x5f ? srso_return_thunk+0x5/0x5f ? find_held_lock+0x2b/0x80 ? srso_return_thunk+0x5/0x5f ? lock_release+0xca/0x2a0 ? srso_return_thunk+0x5/0x5f ? do_user_addr_fault+0x35c/0x8a0 ? srso_return_thunk+0x5/0x5f ? trace_hardirqs_off+0x4b/0xc0 ? srso_return_thunk+0x5/0x5f ? lockdep_hardirqs_on_prepare+0xde/0x190 ? srso_return_thunk+0x5/0x5f This happens because when we call rename we already have the inode mutex held, and then we acquire the subvol_sem if we are a subvolume. This makes the dependency inode lock -> subvol sem When we're running data relocation we will preallocate space for the data relocation inode, and we always run the relocation under the ->cleaner_mutex. This now creates the dependency of cleaner_mutex -> inode lock (from the prealloc) -> subvol_sem Qgroup delete is doing this in the opposite order, it is acquiring the subvol_sem and then it is acquiring the cleaner_mutex, which results in this lockdep splat. This deadlock can't happen in reality, because we won't ever rename the data reloc inode, nor is the data reloc inode a subvolume. However this is fairly easy to fix, simply take the cleaner mutex in the case where we are disabling qgroups before we take the subvol_sem. This resolves the lockdep splat. Reviewed-by: Filipe Manana <[email protected]> Signed-off-by: Josef Bacik <[email protected]> Reviewed-by: David Sterba <[email protected]> Signed-off-by: David Sterba <[email protected]>
1 parent 9af503d commit 0f2b809

File tree

2 files changed

+38
-16
lines changed

2 files changed

+38
-16
lines changed

fs/btrfs/ioctl.c

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3758,23 +3758,50 @@ static long btrfs_ioctl_quota_ctl(struct file *file, void __user *arg)
37583758
goto drop_write;
37593759
}
37603760

3761-
down_write(&fs_info->subvol_sem);
3762-
37633761
switch (sa->cmd) {
37643762
case BTRFS_QUOTA_CTL_ENABLE:
37653763
case BTRFS_QUOTA_CTL_ENABLE_SIMPLE_QUOTA:
3764+
down_write(&fs_info->subvol_sem);
37663765
ret = btrfs_quota_enable(fs_info, sa);
3766+
up_write(&fs_info->subvol_sem);
37673767
break;
37683768
case BTRFS_QUOTA_CTL_DISABLE:
3769+
/*
3770+
* Lock the cleaner mutex to prevent races with concurrent
3771+
* relocation, because relocation may be building backrefs for
3772+
* blocks of the quota root while we are deleting the root. This
3773+
* is like dropping fs roots of deleted snapshots/subvolumes, we
3774+
* need the same protection.
3775+
*
3776+
* This also prevents races between concurrent tasks trying to
3777+
* disable quotas, because we will unlock and relock
3778+
* qgroup_ioctl_lock across BTRFS_FS_QUOTA_ENABLED changes.
3779+
*
3780+
* We take this here because we have the dependency of
3781+
*
3782+
* inode_lock -> subvol_sem
3783+
*
3784+
* because of rename. With relocation we can prealloc extents,
3785+
* so that makes the dependency chain
3786+
*
3787+
* cleaner_mutex -> inode_lock -> subvol_sem
3788+
*
3789+
* so we must take the cleaner_mutex here before we take the
3790+
* subvol_sem. The deadlock can't actually happen, but this
3791+
* quiets lockdep.
3792+
*/
3793+
mutex_lock(&fs_info->cleaner_mutex);
3794+
down_write(&fs_info->subvol_sem);
37693795
ret = btrfs_quota_disable(fs_info);
3796+
up_write(&fs_info->subvol_sem);
3797+
mutex_unlock(&fs_info->cleaner_mutex);
37703798
break;
37713799
default:
37723800
ret = -EINVAL;
37733801
break;
37743802
}
37753803

37763804
kfree(sa);
3777-
up_write(&fs_info->subvol_sem);
37783805
drop_write:
37793806
mnt_drop_write_file(file);
37803807
return ret;

fs/btrfs/qgroup.c

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,16 +1342,10 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
13421342
lockdep_assert_held_write(&fs_info->subvol_sem);
13431343

13441344
/*
1345-
* Lock the cleaner mutex to prevent races with concurrent relocation,
1346-
* because relocation may be building backrefs for blocks of the quota
1347-
* root while we are deleting the root. This is like dropping fs roots
1348-
* of deleted snapshots/subvolumes, we need the same protection.
1349-
*
1350-
* This also prevents races between concurrent tasks trying to disable
1351-
* quotas, because we will unlock and relock qgroup_ioctl_lock across
1352-
* BTRFS_FS_QUOTA_ENABLED changes.
1345+
* Relocation will mess with backrefs, so make sure we have the
1346+
* cleaner_mutex held to protect us from relocate.
13531347
*/
1354-
mutex_lock(&fs_info->cleaner_mutex);
1348+
lockdep_assert_held(&fs_info->cleaner_mutex);
13551349

13561350
mutex_lock(&fs_info->qgroup_ioctl_lock);
13571351
if (!fs_info->quota_root)
@@ -1373,9 +1367,13 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
13731367
clear_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
13741368
btrfs_qgroup_wait_for_completion(fs_info, false);
13751369

1370+
/*
1371+
* We have nothing held here and no trans handle, just return the error
1372+
* if there is one.
1373+
*/
13761374
ret = flush_reservations(fs_info);
13771375
if (ret)
1378-
goto out_unlock_cleaner;
1376+
return ret;
13791377

13801378
/*
13811379
* 1 For the root item
@@ -1439,9 +1437,6 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
14391437
btrfs_end_transaction(trans);
14401438
else if (trans)
14411439
ret = btrfs_commit_transaction(trans);
1442-
out_unlock_cleaner:
1443-
mutex_unlock(&fs_info->cleaner_mutex);
1444-
14451440
return ret;
14461441
}
14471442

0 commit comments

Comments
 (0)