Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

btrfs: qgroup: update all parent qgroups when doing quick inherit

[BUG]
There is a bug that if a subvolume has multi-level parent qgroups, and
is able to do a quick inherit, only the direct parent qgroup got
updated:

mkfs.btrfs -f -O quota $dev
mount $dev $mnt
btrfs subv create $mnt/subv1
btrfs qgroup create 1/100 $mnt
btrfs qgroup create 2/100 $mnt
btrfs qgroup assign 1/100 2/100 $mnt
btrfs qgroup assign 0/256 1/100 $mnt
btrfs qgroup show -p --sync $mnt

Qgroupid Referenced Exclusive Parent Path
-------- ---------- --------- ------ ----
0/5 16.00KiB 16.00KiB - <toplevel>
0/256 16.00KiB 16.00KiB 1/100 subv1
1/100 16.00KiB 16.00KiB 2/100 2/100<1 member qgroup>
2/100 16.00KiB 16.00KiB - <0 member qgroups>

btrfs subv snap -i 1/100 $mnt/subv1 $mnt/snap1
btrfs qgroup show -p --sync $mnt

Qgroupid Referenced Exclusive Parent Path
-------- ---------- --------- ------ ----
0/5 16.00KiB 16.00KiB - <toplevel>
0/256 16.00KiB 16.00KiB 1/100 subv1
0/257 16.00KiB 16.00KiB 1/100 snap1
1/100 32.00KiB 32.00KiB 2/100 2/100<1 member qgroup>
2/100 16.00KiB 16.00KiB - <0 member qgroups>
# Note that 2/100 is not updated, and qgroup numbers are inconsistent

umount $mnt

[CAUSE]
If the snapshot source subvolume belongs to a parent qgroup, and the new
snapshot target is also added to the new same parent qgroup, we allow a
quick update without marking qgroup inconsistent.

But that quick update only update the parent qgroup, without checking if
there is any more parent qgroups.

[FIX]
Iterate through all parent qgroups during the quick inherit.

Reported-by: Boris Burkov <boris@bur.io>
Fixes: b20fe56cd285 ("btrfs: qgroup: allow quick inherit if snapshot is created and added to the same parent")
Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>

authored by

Qu Wenruo and committed by
David Sterba
68d4b3fa 7ee19a59

+16 -2
+16 -2
fs/btrfs/qgroup.c
··· 3208 3208 { 3209 3209 struct btrfs_qgroup *src; 3210 3210 struct btrfs_qgroup *parent; 3211 + struct btrfs_qgroup *qgroup; 3211 3212 struct btrfs_qgroup_list *list; 3213 + LIST_HEAD(qgroup_list); 3214 + const u32 nodesize = fs_info->nodesize; 3212 3215 int nr_parents = 0; 3213 3216 3214 3217 if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_FULL) ··· 3251 3248 if (parent->excl != parent->rfer) 3252 3249 return 1; 3253 3250 3254 - parent->excl += fs_info->nodesize; 3255 - parent->rfer += fs_info->nodesize; 3251 + qgroup_iterator_add(&qgroup_list, parent); 3252 + list_for_each_entry(qgroup, &qgroup_list, iterator) { 3253 + qgroup->rfer += nodesize; 3254 + qgroup->rfer_cmpr += nodesize; 3255 + qgroup->excl += nodesize; 3256 + qgroup->excl_cmpr += nodesize; 3257 + qgroup_dirty(fs_info, qgroup); 3258 + 3259 + /* Append parent qgroups to @qgroup_list. */ 3260 + list_for_each_entry(list, &qgroup->groups, next_group) 3261 + qgroup_iterator_add(&qgroup_list, list->group); 3262 + } 3263 + qgroup_iterator_clean(&qgroup_list); 3256 3264 return 0; 3257 3265 } 3258 3266