mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-04-24 14:07:52 -04:00
btrfs: qgroup: iterate qgroups without memory allocation for qgroup_reserve()
[ Upstream commit 686c4a5a42635e0d2889e3eb461c554fd0b616b4 ] Qgroup heavily relies on ulist to go through all the involved qgroups, but since we're using ulist inside fs_info->qgroup_lock spinlock, this means we're doing a lot of GFP_ATOMIC allocations. This patch reduces the GFP_ATOMIC usage for qgroup_reserve() by eliminating the memory allocation completely. This is done by moving the needed memory to btrfs_qgroup::iterator list_head, so that we can put all the involved qgroup into a on-stack list, thus eliminating the need to allocate memory while holding spinlock. The only cost is the slightly higher memory usage, but considering the reduce GFP_ATOMIC during a hot path, it should still be acceptable. Function qgroup_reserve() is the perfect start point for this conversion. Reviewed-by: Boris Burkov <boris@bur.io> Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> Stable-dep-of: b321a52cce06 ("btrfs: free qgroup pertrans reserve on transaction abort") Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
e93bcaebda
commit
1c9a5c4950
2 changed files with 38 additions and 32 deletions
|
@ -208,6 +208,7 @@ static struct btrfs_qgroup *add_qgroup_rb(struct btrfs_fs_info *fs_info,
|
||||||
INIT_LIST_HEAD(&qgroup->groups);
|
INIT_LIST_HEAD(&qgroup->groups);
|
||||||
INIT_LIST_HEAD(&qgroup->members);
|
INIT_LIST_HEAD(&qgroup->members);
|
||||||
INIT_LIST_HEAD(&qgroup->dirty);
|
INIT_LIST_HEAD(&qgroup->dirty);
|
||||||
|
INIT_LIST_HEAD(&qgroup->iterator);
|
||||||
|
|
||||||
rb_link_node(&qgroup->node, parent, p);
|
rb_link_node(&qgroup->node, parent, p);
|
||||||
rb_insert_color(&qgroup->node, &fs_info->qgroup_tree);
|
rb_insert_color(&qgroup->node, &fs_info->qgroup_tree);
|
||||||
|
@ -1342,6 +1343,24 @@ static void qgroup_dirty(struct btrfs_fs_info *fs_info,
|
||||||
list_add(&qgroup->dirty, &fs_info->dirty_qgroups);
|
list_add(&qgroup->dirty, &fs_info->dirty_qgroups);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qgroup_iterator_add(struct list_head *head, struct btrfs_qgroup *qgroup)
|
||||||
|
{
|
||||||
|
if (!list_empty(&qgroup->iterator))
|
||||||
|
return;
|
||||||
|
|
||||||
|
list_add_tail(&qgroup->iterator, head);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qgroup_iterator_clean(struct list_head *head)
|
||||||
|
{
|
||||||
|
while (!list_empty(head)) {
|
||||||
|
struct btrfs_qgroup *qgroup;
|
||||||
|
|
||||||
|
qgroup = list_first_entry(head, struct btrfs_qgroup, iterator);
|
||||||
|
list_del_init(&qgroup->iterator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The easy accounting, we're updating qgroup relationship whose child qgroup
|
* The easy accounting, we're updating qgroup relationship whose child qgroup
|
||||||
* only has exclusive extents.
|
* only has exclusive extents.
|
||||||
|
@ -3125,8 +3144,7 @@ static int qgroup_reserve(struct btrfs_root *root, u64 num_bytes, bool enforce,
|
||||||
struct btrfs_fs_info *fs_info = root->fs_info;
|
struct btrfs_fs_info *fs_info = root->fs_info;
|
||||||
u64 ref_root = root->root_key.objectid;
|
u64 ref_root = root->root_key.objectid;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct ulist_node *unode;
|
LIST_HEAD(qgroup_list);
|
||||||
struct ulist_iterator uiter;
|
|
||||||
|
|
||||||
if (!is_fstree(ref_root))
|
if (!is_fstree(ref_root))
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -3146,49 +3164,28 @@ static int qgroup_reserve(struct btrfs_root *root, u64 num_bytes, bool enforce,
|
||||||
if (!qgroup)
|
if (!qgroup)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
qgroup_iterator_add(&qgroup_list, qgroup);
|
||||||
* in a first step, we check all affected qgroups if any limits would
|
list_for_each_entry(qgroup, &qgroup_list, iterator) {
|
||||||
* be exceeded
|
|
||||||
*/
|
|
||||||
ulist_reinit(fs_info->qgroup_ulist);
|
|
||||||
ret = ulist_add(fs_info->qgroup_ulist, qgroup->qgroupid,
|
|
||||||
qgroup_to_aux(qgroup), GFP_ATOMIC);
|
|
||||||
if (ret < 0)
|
|
||||||
goto out;
|
|
||||||
ULIST_ITER_INIT(&uiter);
|
|
||||||
while ((unode = ulist_next(fs_info->qgroup_ulist, &uiter))) {
|
|
||||||
struct btrfs_qgroup *qg;
|
|
||||||
struct btrfs_qgroup_list *glist;
|
struct btrfs_qgroup_list *glist;
|
||||||
|
|
||||||
qg = unode_aux_to_qgroup(unode);
|
if (enforce && !qgroup_check_limits(qgroup, num_bytes)) {
|
||||||
|
|
||||||
if (enforce && !qgroup_check_limits(qg, num_bytes)) {
|
|
||||||
ret = -EDQUOT;
|
ret = -EDQUOT;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each_entry(glist, &qg->groups, next_group) {
|
list_for_each_entry(glist, &qgroup->groups, next_group)
|
||||||
ret = ulist_add(fs_info->qgroup_ulist,
|
qgroup_iterator_add(&qgroup_list, glist->group);
|
||||||
glist->group->qgroupid,
|
|
||||||
qgroup_to_aux(glist->group), GFP_ATOMIC);
|
|
||||||
if (ret < 0)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
/*
|
/*
|
||||||
* no limits exceeded, now record the reservation into all qgroups
|
* no limits exceeded, now record the reservation into all qgroups
|
||||||
*/
|
*/
|
||||||
ULIST_ITER_INIT(&uiter);
|
list_for_each_entry(qgroup, &qgroup_list, iterator)
|
||||||
while ((unode = ulist_next(fs_info->qgroup_ulist, &uiter))) {
|
qgroup_rsv_add(fs_info, qgroup, num_bytes, type);
|
||||||
struct btrfs_qgroup *qg;
|
|
||||||
|
|
||||||
qg = unode_aux_to_qgroup(unode);
|
|
||||||
|
|
||||||
qgroup_rsv_add(fs_info, qg, num_bytes, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
qgroup_iterator_clean(&qgroup_list);
|
||||||
spin_unlock(&fs_info->qgroup_lock);
|
spin_unlock(&fs_info->qgroup_lock);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -220,6 +220,15 @@ struct btrfs_qgroup {
|
||||||
struct list_head groups; /* groups this group is member of */
|
struct list_head groups; /* groups this group is member of */
|
||||||
struct list_head members; /* groups that are members of this group */
|
struct list_head members; /* groups that are members of this group */
|
||||||
struct list_head dirty; /* dirty groups */
|
struct list_head dirty; /* dirty groups */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For qgroup iteration usage.
|
||||||
|
*
|
||||||
|
* The iteration list should always be empty until qgroup_iterator_add()
|
||||||
|
* is called. And should be reset to empty after the iteration is
|
||||||
|
* finished.
|
||||||
|
*/
|
||||||
|
struct list_head iterator;
|
||||||
struct rb_node node; /* tree of qgroups */
|
struct rb_node node; /* tree of qgroups */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue