}
#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, ...)
{
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);
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: