]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
btrfs: qgroup: update all parent qgroups when doing quick inherit
authorQu Wenruo <wqu@suse.com>
Thu, 4 Dec 2025 04:08:23 +0000 (14:38 +1030)
committerDavid Sterba <dsterba@suse.com>
Tue, 16 Dec 2025 21:53:14 +0000 (22:53 +0100)
[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>
fs/btrfs/qgroup.c

index 904d2a05e63ab1865b768ce95e82111e39779df4..206587820fec097838a053404074608937ebde10 100644 (file)
@@ -3208,7 +3208,10 @@ static int qgroup_snapshot_quick_inherit(struct btrfs_fs_info *fs_info,
 {
        struct btrfs_qgroup *src;
        struct btrfs_qgroup *parent;
+       struct btrfs_qgroup *qgroup;
        struct btrfs_qgroup_list *list;
+       LIST_HEAD(qgroup_list);
+       const u32 nodesize = fs_info->nodesize;
        int nr_parents = 0;
 
        if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_FULL)
@@ -3248,8 +3251,19 @@ static int qgroup_snapshot_quick_inherit(struct btrfs_fs_info *fs_info,
        if (parent->excl != parent->rfer)
                return 1;
 
-       parent->excl += fs_info->nodesize;
-       parent->rfer += fs_info->nodesize;
+       qgroup_iterator_add(&qgroup_list, parent);
+       list_for_each_entry(qgroup, &qgroup_list, iterator) {
+               qgroup->rfer += nodesize;
+               qgroup->rfer_cmpr += nodesize;
+               qgroup->excl += nodesize;
+               qgroup->excl_cmpr += nodesize;
+               qgroup_dirty(fs_info, qgroup);
+
+               /* Append parent qgroups to @qgroup_list. */
+               list_for_each_entry(list, &qgroup->groups, next_group)
+                       qgroup_iterator_add(&qgroup_list, list->group);
+       }
+       qgroup_iterator_clean(&qgroup_list);
        return 0;
 }