@@ -332,6 +332,44 @@ static int del_relation_rb(struct btrfs_fs_info *fs_info,
return -ENOENT;
}
+static int qgroup_collect_parents(struct btrfs_qgroup *qgroup,
+ struct ulist *ul)
+{
+ struct ulist_iterator uiter;
+ struct ulist_node *unode;
+ struct btrfs_qgroup_list *glist;
+ struct btrfs_qgroup *qg;
+ bool err_free = false;
+ int ret = 0;
+
+ if (!ul) {
+ ul = ulist_alloc(GFP_KERNEL);
+ err_free = true;
+ } else {
+ ulist_reinit(ul);
+ }
+
+ ret = ulist_add(ul, qgroup->qgroupid,
+ qgroup_to_aux(qgroup), GFP_ATOMIC);
+ if (ret < 0)
+ goto out;
+ ULIST_ITER_INIT(&uiter);
+ while ((unode = ulist_next(ul, &uiter))) {
+ qg = unode_aux_to_qgroup(unode);
+ list_for_each_entry(glist, &qg->groups, next_group) {
+ ret = ulist_add(ul, glist->group->qgroupid,
+ qgroup_to_aux(glist->group), GFP_ATOMIC);
+ if (ret < 0)
+ goto out;
+ }
+ }
+ ret = 0;
+out:
+ if (ret && err_free)
+ ulist_free(ul);
+ return ret;
+}
+
#ifdef CONFIG_BTRFS_FS_RUN_SANITY_TESTS
int btrfs_verify_qgroup_counts(struct btrfs_fs_info *fs_info, u64 qgroupid,
u64 rfer, u64 excl)
@@ -4472,3 +4510,50 @@ void btrfs_qgroup_destroy_extent_records(struct btrfs_transaction *trans)
kfree(entry);
}
}
+
+int btrfs_record_simple_quota_delta(struct btrfs_fs_info *fs_info,
+ struct btrfs_simple_quota_delta *delta)
+{
+ int ret;
+ struct ulist *ul = fs_info->qgroup_ulist;
+ struct btrfs_qgroup *qgroup;
+ struct ulist_iterator uiter;
+ struct ulist_node *unode;
+ struct btrfs_qgroup *qg;
+ bool drop_rsv = false;
+ u64 root = delta->root;
+ u64 num_bytes = delta->num_bytes;
+ int sign = delta->is_inc ? 1 : -1;
+
+ if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE)
+ return 0;
+
+ if (!is_fstree(root))
+ return 0;
+
+ spin_lock(&fs_info->qgroup_lock);
+ qgroup = find_qgroup_rb(fs_info, root);
+ if (!qgroup) {
+ ret = -ENOENT;
+ goto out;
+ }
+ ret = qgroup_collect_parents(qgroup, ul);
+ if (ret)
+ goto out;
+
+ ULIST_ITER_INIT(&uiter);
+ while ((unode = ulist_next(ul, &uiter))) {
+ qg = unode_aux_to_qgroup(unode);
+ qg->excl += num_bytes * sign;
+ qg->rfer += num_bytes * sign;
+ if (delta->is_inc && delta->is_data)
+ drop_rsv = true;
+ qgroup_dirty(fs_info, qg);
+ }
+
+out:
+ spin_unlock(&fs_info->qgroup_lock);
+ if (!ret && drop_rsv)
+ btrfs_qgroup_free_refroot(fs_info, root, num_bytes, BTRFS_QGROUP_RSV_DATA);
+ return ret;
+}
@@ -235,6 +235,13 @@ struct btrfs_qgroup {
struct kobject kobj;
};
+struct btrfs_simple_quota_delta {
+ u64 root; /* The fstree root this delta counts against */
+ u64 num_bytes; /* The number of bytes in the extent being counted */
+ bool is_inc; /* Whether we are using or freeing the extent */
+ bool is_data; /* Whether the extent is data or metadata */
+};
+
static inline u64 btrfs_qgroup_subvolid(u64 qgroupid)
{
return (qgroupid & ((1ULL << BTRFS_QGROUP_LEVEL_SHIFT) - 1));
@@ -447,5 +454,6 @@ int btrfs_qgroup_trace_subtree_after_cow(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct extent_buffer *eb);
void btrfs_qgroup_destroy_extent_records(struct btrfs_transaction *trans);
bool btrfs_check_quota_leak(struct btrfs_fs_info *fs_info);
-
+int btrfs_record_simple_quota_delta(struct btrfs_fs_info *fs_info,
+ struct btrfs_simple_quota_delta *delta);
#endif
Rather than re-computing shared/exclusive ownership based on backrefs and walking roots for implicit backrefs, simple quotas does an increment when creating an extent and a decrement when deleting it. Add the API for the extent item code to use to track those events. Also add a helper function to make collecting parent qgroups in a ulist easier for functions like this. Signed-off-by: Boris Burkov <boris@bur.io> --- fs/btrfs/qgroup.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/qgroup.h | 10 +++++- 2 files changed, 94 insertions(+), 1 deletion(-)