btrfs: check squota parent usage on membership change

We could have detected the quick inherit bug more directly if we had
an extra warning about squota hierarchy consistency while modifying the
hierarchy. In squotas, the parent usage always simply adds up to the sum of
its children, so we can just check for that when changing membership and
detect more accounting bugs.

Reviewed-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: Boris Burkov <boris@bur.io>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Boris Burkov
2025-12-01 15:33:49 -08:00
committed by David Sterba
parent a5eb902436
commit 9c46bcda5f

View File

@@ -346,6 +346,42 @@ int btrfs_verify_qgroup_counts(const struct btrfs_fs_info *fs_info, u64 qgroupid
}
#endif
static bool squota_check_parent_usage(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *parent)
{
u64 excl_sum = 0;
u64 rfer_sum = 0;
u64 excl_cmpr_sum = 0;
u64 rfer_cmpr_sum = 0;
struct btrfs_qgroup_list *glist;
int nr_members = 0;
bool mismatch;
if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE)
return false;
if (btrfs_qgroup_level(parent->qgroupid) == 0)
return false;
/* Eligible parent qgroup. Squota; level > 0; empty members list. */
list_for_each_entry(glist, &parent->members, next_member) {
excl_sum += glist->member->excl;
rfer_sum += glist->member->rfer;
excl_cmpr_sum += glist->member->excl_cmpr;
rfer_cmpr_sum += glist->member->rfer_cmpr;
nr_members++;
}
mismatch = (parent->excl != excl_sum || parent->rfer != rfer_sum ||
parent->excl_cmpr != excl_cmpr_sum || parent->rfer_cmpr != excl_cmpr_sum);
WARN(mismatch,
"parent squota qgroup %hu/%llu has mismatched usage from its %d members. "
"%llu %llu %llu %llu vs %llu %llu %llu %llu\n",
btrfs_qgroup_level(parent->qgroupid),
btrfs_qgroup_subvolid(parent->qgroupid), nr_members, parent->excl,
parent->rfer, parent->excl_cmpr, parent->rfer_cmpr, excl_sum,
rfer_sum, excl_cmpr_sum, rfer_cmpr_sum);
return mismatch;
}
__printf(2, 3)
static void qgroup_mark_inconsistent(struct btrfs_fs_info *fs_info, const char *fmt, ...)
{
@@ -1562,6 +1598,7 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src, u64 dst
goto out;
}
ret = quick_update_accounting(fs_info, src, dst, 1);
squota_check_parent_usage(fs_info, parent);
spin_unlock(&fs_info->qgroup_lock);
out:
kfree(prealloc);
@@ -1618,6 +1655,8 @@ static int __del_qgroup_relation(struct btrfs_trans_handle *trans, u64 src,
spin_lock(&fs_info->qgroup_lock);
del_relation_rb(fs_info, src, dst);
ret = quick_update_accounting(fs_info, src, dst, -1);
ASSERT(parent);
squota_check_parent_usage(fs_info, parent);
spin_unlock(&fs_info->qgroup_lock);
}
out: