Skip to content

Commit f35710d

Browse files
fdmananasmb49
authored andcommitted
btrfs: fix lockdep splat when enabling and disabling qgroups
BugLink: https://bugs.launchpad.net/bugs/1943484 commit a855fbe upstream When running test case btrfs/017 from fstests, lockdep reported the following splat: [ 1297.067385] ====================================================== [ 1297.067708] WARNING: possible circular locking dependency detected [ 1297.068022] 5.10.0-rc4-btrfs-next-73 #1 Not tainted [ 1297.068322] ------------------------------------------------------ [ 1297.068629] btrfs/189080 is trying to acquire lock: [ 1297.068929] ffff9f2725731690 (sb_internal#2){.+.+}-{0:0}, at: btrfs_quota_enable+0xaf/0xa70 [btrfs] [ 1297.069274] but task is already holding lock: [ 1297.069868] ffff9f2702b61a08 (&fs_info->qgroup_ioctl_lock){+.+.}-{3:3}, at: btrfs_quota_enable+0x3b/0xa70 [btrfs] [ 1297.070219] which lock already depends on the new lock. [ 1297.071131] the existing dependency chain (in reverse order) is: [ 1297.071721] -> #1 (&fs_info->qgroup_ioctl_lock){+.+.}-{3:3}: [ 1297.072375] lock_acquire+0xd8/0x490 [ 1297.072710] __mutex_lock+0xa3/0xb30 [ 1297.073061] btrfs_qgroup_inherit+0x59/0x6a0 [btrfs] [ 1297.073421] create_subvol+0x194/0x990 [btrfs] [ 1297.073780] btrfs_mksubvol+0x3fb/0x4a0 [btrfs] [ 1297.074133] __btrfs_ioctl_snap_create+0x119/0x1a0 [btrfs] [ 1297.074498] btrfs_ioctl_snap_create+0x58/0x80 [btrfs] [ 1297.074872] btrfs_ioctl+0x1a90/0x36f0 [btrfs] [ 1297.075245] __x64_sys_ioctl+0x83/0xb0 [ 1297.075617] do_syscall_64+0x33/0x80 [ 1297.075993] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 1297.076380] -> #0 (sb_internal#2){.+.+}-{0:0}: [ 1297.077166] check_prev_add+0x91/0xc60 [ 1297.077572] __lock_acquire+0x1740/0x3110 [ 1297.077984] lock_acquire+0xd8/0x490 [ 1297.078411] start_transaction+0x3c5/0x760 [btrfs] [ 1297.078853] btrfs_quota_enable+0xaf/0xa70 [btrfs] [ 1297.079323] btrfs_ioctl+0x2c60/0x36f0 [btrfs] [ 1297.079789] __x64_sys_ioctl+0x83/0xb0 [ 1297.080232] do_syscall_64+0x33/0x80 [ 1297.080680] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 1297.081139] other info that might help us debug this: [ 1297.082536] Possible unsafe locking scenario: [ 1297.083510] CPU0 CPU1 [ 1297.084005] ---- ---- [ 1297.084500] lock(&fs_info->qgroup_ioctl_lock); [ 1297.084994] lock(sb_internal#2); [ 1297.085485] lock(&fs_info->qgroup_ioctl_lock); [ 1297.085974] lock(sb_internal#2); [ 1297.086454] *** DEADLOCK *** [ 1297.087880] 3 locks held by btrfs/189080: [ 1297.088324] #0: ffff9f2725731470 (sb_writers#14){.+.+}-{0:0}, at: btrfs_ioctl+0xa73/0x36f0 [btrfs] [ 1297.088799] #1: ffff9f2702b60cc0 (&fs_info->subvol_sem){++++}-{3:3}, at: btrfs_ioctl+0x1f4d/0x36f0 [btrfs] [ 1297.089284] #2: ffff9f2702b61a08 (&fs_info->qgroup_ioctl_lock){+.+.}-{3:3}, at: btrfs_quota_enable+0x3b/0xa70 [btrfs] [ 1297.089771] stack backtrace: [ 1297.090662] CPU: 5 PID: 189080 Comm: btrfs Not tainted 5.10.0-rc4-btrfs-next-73 #1 [ 1297.091132] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.13.0-0-gf21b5a4aeb02-prebuilt.qemu.org 04/01/2014 [ 1297.092123] Call Trace: [ 1297.092629] dump_stack+0x8d/0xb5 [ 1297.093115] check_noncircular+0xff/0x110 [ 1297.093596] check_prev_add+0x91/0xc60 [ 1297.094076] ? kvm_clock_read+0x14/0x30 [ 1297.094553] ? kvm_sched_clock_read+0x5/0x10 [ 1297.095029] __lock_acquire+0x1740/0x3110 [ 1297.095510] lock_acquire+0xd8/0x490 [ 1297.095993] ? btrfs_quota_enable+0xaf/0xa70 [btrfs] [ 1297.096476] start_transaction+0x3c5/0x760 [btrfs] [ 1297.096962] ? btrfs_quota_enable+0xaf/0xa70 [btrfs] [ 1297.097451] btrfs_quota_enable+0xaf/0xa70 [btrfs] [ 1297.097941] ? btrfs_ioctl+0x1f4d/0x36f0 [btrfs] [ 1297.098429] btrfs_ioctl+0x2c60/0x36f0 [btrfs] [ 1297.098904] ? do_user_addr_fault+0x20c/0x430 [ 1297.099382] ? kvm_clock_read+0x14/0x30 [ 1297.099854] ? kvm_sched_clock_read+0x5/0x10 [ 1297.100328] ? sched_clock+0x5/0x10 [ 1297.100801] ? sched_clock_cpu+0x12/0x180 [ 1297.101272] ? __x64_sys_ioctl+0x83/0xb0 [ 1297.101739] __x64_sys_ioctl+0x83/0xb0 [ 1297.102207] do_syscall_64+0x33/0x80 [ 1297.102673] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 1297.103148] RIP: 0033:0x7f773ff65d87 This is because during the quota enable ioctl we lock first the mutex qgroup_ioctl_lock and then start a transaction, and starting a transaction acquires a fs freeze semaphore (at the VFS level). However, every other code path, except for the quota disable ioctl path, we do the opposite: we start a transaction and then lock the mutex. So fix this by making the quota enable and disable paths to start the transaction without having the mutex locked, and then, after starting the transaction, lock the mutex and check if some other task already enabled or disabled the quotas, bailing with success if that was the case. Signed-off-by: Filipe Manana <fdmanana@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Anand Jain <anand.jain@oracle.com> Conflicts: fs/btrfs/qgroup.c Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Kamal Mostafa <kamal@canonical.com> Signed-off-by: Luke Nowakowski-Krijger <luke.nowakowskikrijger@canonical.com>
1 parent c7796c3 commit f35710d

File tree

2 files changed

+52
-9
lines changed

2 files changed

+52
-9
lines changed

fs/btrfs/ctree.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,10 @@ struct btrfs_fs_info {
827827
*/
828828
struct ulist *qgroup_ulist;
829829

830-
/* protect user change for quota operations */
830+
/*
831+
* Protect user change for quota operations. If a transaction is needed,
832+
* it must be started before locking this lock.
833+
*/
831834
struct mutex qgroup_ioctl_lock;
832835

833836
/* list of dirty qgroups to be written at next commit */

fs/btrfs/qgroup.c

Lines changed: 48 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -886,19 +886,35 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info)
886886
struct btrfs_key found_key;
887887
struct btrfs_qgroup *qgroup = NULL;
888888
struct btrfs_trans_handle *trans = NULL;
889+
struct ulist *ulist = NULL;
889890
int ret = 0;
890891
int slot;
891892

892893
mutex_lock(&fs_info->qgroup_ioctl_lock);
893894
if (fs_info->quota_root)
894895
goto out;
895896

896-
fs_info->qgroup_ulist = ulist_alloc(GFP_KERNEL);
897-
if (!fs_info->qgroup_ulist) {
897+
ulist = ulist_alloc(GFP_KERNEL);
898+
if (!ulist) {
898899
ret = -ENOMEM;
899900
goto out;
900901
}
901902

903+
/*
904+
* Unlock qgroup_ioctl_lock before starting the transaction. This is to
905+
* avoid lock acquisition inversion problems (reported by lockdep) between
906+
* qgroup_ioctl_lock and the vfs freeze semaphores, acquired when we
907+
* start a transaction.
908+
* After we started the transaction lock qgroup_ioctl_lock again and
909+
* check if someone else created the quota root in the meanwhile. If so,
910+
* just return success and release the transaction handle.
911+
*
912+
* Also we don't need to worry about someone else calling
913+
* btrfs_sysfs_add_qgroups() after we unlock and getting an error because
914+
* that function returns 0 (success) when the sysfs entries already exist.
915+
*/
916+
mutex_unlock(&fs_info->qgroup_ioctl_lock);
917+
902918
/*
903919
* 1 for quota root item
904920
* 1 for BTRFS_QGROUP_STATUS item
@@ -908,12 +924,20 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info)
908924
* would be a lot of overkill.
909925
*/
910926
trans = btrfs_start_transaction(tree_root, 2);
927+
928+
mutex_lock(&fs_info->qgroup_ioctl_lock);
911929
if (IS_ERR(trans)) {
912930
ret = PTR_ERR(trans);
913931
trans = NULL;
914932
goto out;
915933
}
916934

935+
if (fs_info->quota_root)
936+
goto out;
937+
938+
fs_info->qgroup_ulist = ulist;
939+
ulist = NULL;
940+
917941
/*
918942
* initially create the quota tree
919943
*/
@@ -1046,10 +1070,13 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info)
10461070
if (ret) {
10471071
ulist_free(fs_info->qgroup_ulist);
10481072
fs_info->qgroup_ulist = NULL;
1049-
if (trans)
1050-
btrfs_end_transaction(trans);
10511073
}
10521074
mutex_unlock(&fs_info->qgroup_ioctl_lock);
1075+
if (ret && trans)
1076+
btrfs_end_transaction(trans);
1077+
else if (trans)
1078+
ret = btrfs_end_transaction(trans);
1079+
ulist_free(ulist);
10531080
return ret;
10541081
}
10551082

@@ -1062,19 +1089,29 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
10621089
mutex_lock(&fs_info->qgroup_ioctl_lock);
10631090
if (!fs_info->quota_root)
10641091
goto out;
1092+
mutex_unlock(&fs_info->qgroup_ioctl_lock);
10651093

10661094
/*
10671095
* 1 For the root item
10681096
*
10691097
* We should also reserve enough items for the quota tree deletion in
10701098
* btrfs_clean_quota_tree but this is not done.
1099+
*
1100+
* Also, we must always start a transaction without holding the mutex
1101+
* qgroup_ioctl_lock, see btrfs_quota_enable().
10711102
*/
10721103
trans = btrfs_start_transaction(fs_info->tree_root, 1);
1104+
1105+
mutex_lock(&fs_info->qgroup_ioctl_lock);
10731106
if (IS_ERR(trans)) {
10741107
ret = PTR_ERR(trans);
1108+
trans = NULL;
10751109
goto out;
10761110
}
10771111

1112+
if (!fs_info->quota_root)
1113+
goto out;
1114+
10781115
clear_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
10791116
btrfs_qgroup_wait_for_completion(fs_info, false);
10801117
spin_lock(&fs_info->qgroup_lock);
@@ -1088,13 +1125,13 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
10881125
ret = btrfs_clean_quota_tree(trans, quota_root);
10891126
if (ret) {
10901127
btrfs_abort_transaction(trans, ret);
1091-
goto end_trans;
1128+
goto out;
10921129
}
10931130

10941131
ret = btrfs_del_root(trans, &quota_root->root_key);
10951132
if (ret) {
10961133
btrfs_abort_transaction(trans, ret);
1097-
goto end_trans;
1134+
goto out;
10981135
}
10991136

11001137
list_del(&quota_root->dirty_list);
@@ -1108,10 +1145,13 @@ int btrfs_quota_disable(struct btrfs_fs_info *fs_info)
11081145
free_extent_buffer(quota_root->commit_root);
11091146
kfree(quota_root);
11101147

1111-
end_trans:
1112-
ret = btrfs_end_transaction(trans);
11131148
out:
11141149
mutex_unlock(&fs_info->qgroup_ioctl_lock);
1150+
if (ret && trans)
1151+
btrfs_end_transaction(trans);
1152+
else if (trans)
1153+
ret = btrfs_end_transaction(trans);
1154+
11151155
return ret;
11161156
}
11171157

0 commit comments

Comments
 (0)