* [PATCH v3 1/6] btrfs: qgroup: iterate qgroups without memory allocation for qgroup_reserve()
2023-09-02 0:13 [PATCH v3 0/6] btrfs: qgroup: remove GFP_ATOMIC usage for ulist Qu Wenruo
@ 2023-09-02 0:13 ` Qu Wenruo
2023-09-02 0:13 ` [PATCH v3 2/6] btrfs: qgroup: use qgroup_iterator facility for btrfs_qgroup_free_refroot() Qu Wenruo
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Qu Wenruo @ 2023-09-02 0:13 UTC (permalink / raw)
To: linux-btrfs; +Cc: Boris Burkov
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 allocation.
This patch would reduce 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
eliminate the need to allocate memory 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>
---
fs/btrfs/qgroup.c | 68 +++++++++++++++++++++++------------------------
fs/btrfs/qgroup.h | 9 +++++++
2 files changed, 42 insertions(+), 35 deletions(-)
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 74244b4bb0e9..de34e2aef710 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -216,6 +216,7 @@ static struct btrfs_qgroup *add_qgroup_rb(struct btrfs_fs_info *fs_info,
INIT_LIST_HEAD(&qgroup->groups);
INIT_LIST_HEAD(&qgroup->members);
INIT_LIST_HEAD(&qgroup->dirty);
+ INIT_LIST_HEAD(&qgroup->iterator);
rb_link_node(&qgroup->node, parent, p);
rb_insert_color(&qgroup->node, &fs_info->qgroup_tree);
@@ -1367,6 +1368,25 @@ static void qgroup_dirty(struct btrfs_fs_info *fs_info,
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
* only has exclusive extents.
@@ -3154,12 +3174,11 @@ static bool qgroup_check_limits(const struct btrfs_qgroup *qg, u64 num_bytes)
static int qgroup_reserve(struct btrfs_root *root, u64 num_bytes, bool enforce,
enum btrfs_qgroup_rsv_type type)
{
- struct btrfs_qgroup *qgroup;
+ struct btrfs_qgroup *cur;
struct btrfs_fs_info *fs_info = root->fs_info;
u64 ref_root = root->root_key.objectid;
int ret = 0;
- struct ulist_node *unode;
- struct ulist_iterator uiter;
+ LIST_HEAD(qgroup_list);
if (!is_fstree(ref_root))
return 0;
@@ -3175,53 +3194,32 @@ static int qgroup_reserve(struct btrfs_root *root, u64 num_bytes, bool enforce,
if (!fs_info->quota_root)
goto out;
- qgroup = find_qgroup_rb(fs_info, ref_root);
- if (!qgroup)
+ cur = find_qgroup_rb(fs_info, ref_root);
+ if (!cur)
goto out;
- /*
- * in a first step, we check all affected qgroups if any limits would
- * 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;
+ qgroup_iterator_add(&qgroup_list, cur);
+ list_for_each_entry(cur, &qgroup_list, iterator) {
struct btrfs_qgroup_list *glist;
- qg = unode_aux_to_qgroup(unode);
-
- if (enforce && !qgroup_check_limits(qg, num_bytes)) {
+ if (enforce && !qgroup_check_limits(cur, num_bytes)) {
ret = -EDQUOT;
goto out;
}
- list_for_each_entry(glist, &qg->groups, next_group) {
- ret = ulist_add(fs_info->qgroup_ulist,
- glist->group->qgroupid,
- qgroup_to_aux(glist->group), GFP_ATOMIC);
- if (ret < 0)
- goto out;
- }
+ list_for_each_entry(glist, &cur->groups, next_group)
+ qgroup_iterator_add(&qgroup_list, glist->group);
}
+
ret = 0;
/*
* no limits exceeded, now record the reservation into all qgroups
*/
- ULIST_ITER_INIT(&uiter);
- while ((unode = ulist_next(fs_info->qgroup_ulist, &uiter))) {
- struct btrfs_qgroup *qg;
-
- qg = unode_aux_to_qgroup(unode);
-
- qgroup_rsv_add(fs_info, qg, num_bytes, type);
- }
+ list_for_each_entry(cur, &qgroup_list, iterator)
+ qgroup_rsv_add(fs_info, cur, num_bytes, type);
out:
+ qgroup_iterator_clean(&qgroup_list);
spin_unlock(&fs_info->qgroup_lock);
return ret;
}
diff --git a/fs/btrfs/qgroup.h b/fs/btrfs/qgroup.h
index 7bffa10589d6..5dc0583622c3 100644
--- a/fs/btrfs/qgroup.h
+++ b/fs/btrfs/qgroup.h
@@ -220,6 +220,15 @@ struct btrfs_qgroup {
struct list_head groups; /* groups this group is member of */
struct list_head members; /* groups that are members of this group */
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 */
/*
--
2.41.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 2/6] btrfs: qgroup: use qgroup_iterator facility for btrfs_qgroup_free_refroot()
2023-09-02 0:13 [PATCH v3 0/6] btrfs: qgroup: remove GFP_ATOMIC usage for ulist Qu Wenruo
2023-09-02 0:13 ` [PATCH v3 1/6] btrfs: qgroup: iterate qgroups without memory allocation for qgroup_reserve() Qu Wenruo
@ 2023-09-02 0:13 ` Qu Wenruo
2023-09-02 0:13 ` [PATCH v3 3/6] btrfs: qgroup: use qgroup_iterator facility for qgroup_convert_meta() Qu Wenruo
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Qu Wenruo @ 2023-09-02 0:13 UTC (permalink / raw)
To: linux-btrfs; +Cc: Boris Burkov
With the new qgroup_iterator_add() and qgroup_iterator_clean(), we can
get rid of the ulist and its GFP_ATOMIC memory allocation.
Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
fs/btrfs/qgroup.c | 37 +++++++++++--------------------------
1 file changed, 11 insertions(+), 26 deletions(-)
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index de34e2aef710..c2a1173d9f00 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -3237,10 +3237,8 @@ void btrfs_qgroup_free_refroot(struct btrfs_fs_info *fs_info,
u64 ref_root, u64 num_bytes,
enum btrfs_qgroup_rsv_type type)
{
- struct btrfs_qgroup *qgroup;
- struct ulist_node *unode;
- struct ulist_iterator uiter;
- int ret = 0;
+ struct btrfs_qgroup *cur;
+ LIST_HEAD(qgroup_list);
if (!is_fstree(ref_root))
return;
@@ -3257,8 +3255,8 @@ void btrfs_qgroup_free_refroot(struct btrfs_fs_info *fs_info,
if (!fs_info->quota_root)
goto out;
- qgroup = find_qgroup_rb(fs_info, ref_root);
- if (!qgroup)
+ cur = find_qgroup_rb(fs_info, ref_root);
+ if (!cur)
goto out;
if (num_bytes == (u64)-1)
@@ -3266,32 +3264,19 @@ void btrfs_qgroup_free_refroot(struct btrfs_fs_info *fs_info,
* We're freeing all pertrans rsv, get reserved value from
* level 0 qgroup as real num_bytes to free.
*/
- num_bytes = qgroup->rsv.values[type];
+ num_bytes = cur->rsv.values[type];
- 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;
+ qgroup_iterator_add(&qgroup_list, cur);
+ list_for_each_entry(cur, &qgroup_list, iterator) {
struct btrfs_qgroup_list *glist;
- qg = unode_aux_to_qgroup(unode);
-
- qgroup_rsv_release(fs_info, qg, num_bytes, type);
-
- list_for_each_entry(glist, &qg->groups, next_group) {
- ret = ulist_add(fs_info->qgroup_ulist,
- glist->group->qgroupid,
- qgroup_to_aux(glist->group), GFP_ATOMIC);
- if (ret < 0)
- goto out;
+ qgroup_rsv_release(fs_info, cur, num_bytes, type);
+ list_for_each_entry(glist, &cur->groups, next_group) {
+ qgroup_iterator_add(&qgroup_list, glist->group);
}
}
-
out:
+ qgroup_iterator_clean(&qgroup_list);
spin_unlock(&fs_info->qgroup_lock);
}
--
2.41.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 3/6] btrfs: qgroup: use qgroup_iterator facility for qgroup_convert_meta()
2023-09-02 0:13 [PATCH v3 0/6] btrfs: qgroup: remove GFP_ATOMIC usage for ulist Qu Wenruo
2023-09-02 0:13 ` [PATCH v3 1/6] btrfs: qgroup: iterate qgroups without memory allocation for qgroup_reserve() Qu Wenruo
2023-09-02 0:13 ` [PATCH v3 2/6] btrfs: qgroup: use qgroup_iterator facility for btrfs_qgroup_free_refroot() Qu Wenruo
@ 2023-09-02 0:13 ` Qu Wenruo
2023-09-02 0:13 ` [PATCH v3 4/6] btrfs: qgroup: use qgroup_iterator facility for __qgroup_excl_accounting() Qu Wenruo
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Qu Wenruo @ 2023-09-02 0:13 UTC (permalink / raw)
To: linux-btrfs; +Cc: Boris Burkov
With the new qgroup_iterator_add() and qgroup_iterator_clean(), we can
get rid of the ulist and its GFP_ATOMIC memory allocation.
Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
fs/btrfs/qgroup.c | 38 +++++++++++++-------------------------
1 file changed, 13 insertions(+), 25 deletions(-)
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index c2a1173d9f00..aa8e9e7be4f8 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -4119,10 +4119,8 @@ void __btrfs_qgroup_free_meta(struct btrfs_root *root, int num_bytes,
static void qgroup_convert_meta(struct btrfs_fs_info *fs_info, u64 ref_root,
int num_bytes)
{
- struct btrfs_qgroup *qgroup;
- struct ulist_node *unode;
- struct ulist_iterator uiter;
- int ret = 0;
+ struct btrfs_qgroup *cur;
+ LIST_HEAD(qgroup_list);
if (num_bytes == 0)
return;
@@ -4130,34 +4128,24 @@ static void qgroup_convert_meta(struct btrfs_fs_info *fs_info, u64 ref_root,
return;
spin_lock(&fs_info->qgroup_lock);
- qgroup = find_qgroup_rb(fs_info, ref_root);
- if (!qgroup)
+ cur = find_qgroup_rb(fs_info, ref_root);
+ if (!cur)
goto out;
- 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;
+
+ qgroup_iterator_add(&qgroup_list, cur);
+ list_for_each_entry(cur, &qgroup_list, iterator) {
struct btrfs_qgroup_list *glist;
- qg = unode_aux_to_qgroup(unode);
-
- qgroup_rsv_release(fs_info, qg, num_bytes,
+ qgroup_rsv_release(fs_info, cur, num_bytes,
BTRFS_QGROUP_RSV_META_PREALLOC);
- qgroup_rsv_add(fs_info, qg, num_bytes,
+ qgroup_rsv_add(fs_info, cur, num_bytes,
BTRFS_QGROUP_RSV_META_PERTRANS);
- list_for_each_entry(glist, &qg->groups, next_group) {
- ret = ulist_add(fs_info->qgroup_ulist,
- glist->group->qgroupid,
- qgroup_to_aux(glist->group), GFP_ATOMIC);
- if (ret < 0)
- goto out;
- }
+
+ list_for_each_entry(glist, &cur->groups, next_group)
+ qgroup_iterator_add(&qgroup_list, glist->group);
}
out:
+ qgroup_iterator_clean(&qgroup_list);
spin_unlock(&fs_info->qgroup_lock);
}
--
2.41.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 4/6] btrfs: qgroup: use qgroup_iterator facility for __qgroup_excl_accounting()
2023-09-02 0:13 [PATCH v3 0/6] btrfs: qgroup: remove GFP_ATOMIC usage for ulist Qu Wenruo
` (2 preceding siblings ...)
2023-09-02 0:13 ` [PATCH v3 3/6] btrfs: qgroup: use qgroup_iterator facility for qgroup_convert_meta() Qu Wenruo
@ 2023-09-02 0:13 ` Qu Wenruo
2023-09-02 0:13 ` [PATCH v3 5/6] btrfs: qgroup: use qgroup_iterator facility to replace tmp ulist of qgroup_update_refcnt() Qu Wenruo
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Qu Wenruo @ 2023-09-02 0:13 UTC (permalink / raw)
To: linux-btrfs; +Cc: Boris Burkov
With the new qgroup_iterator_add() and qgroup_iterator_clean(), we can
get rid of the ulist and its GFP_ATOMIC memory allocation.
Furthermore we can merge the code handling the initial and parent
qgroups into one loop, and drop the @tmp ulist parameter for involved
call sites.
Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
fs/btrfs/qgroup.c | 81 ++++++++++-------------------------------------
1 file changed, 17 insertions(+), 64 deletions(-)
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index aa8e9e7be4f8..3d768045aa5c 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -1401,14 +1401,12 @@ static void qgroup_iterator_clean(struct list_head *head)
*
* Caller should hold fs_info->qgroup_lock.
*/
-static int __qgroup_excl_accounting(struct btrfs_fs_info *fs_info,
- struct ulist *tmp, u64 ref_root,
+static int __qgroup_excl_accounting(struct btrfs_fs_info *fs_info, u64 ref_root,
struct btrfs_qgroup *src, int sign)
{
struct btrfs_qgroup *qgroup;
- struct btrfs_qgroup_list *glist;
- struct ulist_node *unode;
- struct ulist_iterator uiter;
+ struct btrfs_qgroup *cur;
+ LIST_HEAD(qgroup_list);
u64 num_bytes = src->excl;
int ret = 0;
@@ -1416,53 +1414,30 @@ static int __qgroup_excl_accounting(struct btrfs_fs_info *fs_info,
if (!qgroup)
goto out;
- qgroup->rfer += sign * num_bytes;
- qgroup->rfer_cmpr += sign * num_bytes;
+ qgroup_iterator_add(&qgroup_list, qgroup);
+ list_for_each_entry(cur, &qgroup_list, iterator) {
+ struct btrfs_qgroup_list *glist;
- WARN_ON(sign < 0 && qgroup->excl < num_bytes);
- qgroup->excl += sign * num_bytes;
- qgroup->excl_cmpr += sign * num_bytes;
-
- if (sign > 0)
- qgroup_rsv_add_by_qgroup(fs_info, qgroup, src);
- else
- qgroup_rsv_release_by_qgroup(fs_info, qgroup, src);
-
- qgroup_dirty(fs_info, qgroup);
-
- /* Get all of the parent groups that contain this qgroup */
- list_for_each_entry(glist, &qgroup->groups, next_group) {
- ret = ulist_add(tmp, glist->group->qgroupid,
- qgroup_to_aux(glist->group), GFP_ATOMIC);
- if (ret < 0)
- goto out;
- }
-
- /* Iterate all of the parents and adjust their reference counts */
- ULIST_ITER_INIT(&uiter);
- while ((unode = ulist_next(tmp, &uiter))) {
- qgroup = unode_aux_to_qgroup(unode);
qgroup->rfer += sign * num_bytes;
qgroup->rfer_cmpr += sign * num_bytes;
+
WARN_ON(sign < 0 && qgroup->excl < num_bytes);
qgroup->excl += sign * num_bytes;
+ qgroup->excl_cmpr += sign * num_bytes;
+
if (sign > 0)
qgroup_rsv_add_by_qgroup(fs_info, qgroup, src);
else
qgroup_rsv_release_by_qgroup(fs_info, qgroup, src);
- qgroup->excl_cmpr += sign * num_bytes;
qgroup_dirty(fs_info, qgroup);
- /* Add any parents of the parents */
- list_for_each_entry(glist, &qgroup->groups, next_group) {
- ret = ulist_add(tmp, glist->group->qgroupid,
- qgroup_to_aux(glist->group), GFP_ATOMIC);
- if (ret < 0)
- goto out;
- }
+ /* Append parent qgroups to @qgroup_list. */
+ list_for_each_entry(glist, &qgroup->groups, next_group)
+ qgroup_iterator_add(&qgroup_list, glist->group);
}
ret = 0;
out:
+ qgroup_iterator_clean(&qgroup_list);
return ret;
}
@@ -1479,8 +1454,7 @@ static int __qgroup_excl_accounting(struct btrfs_fs_info *fs_info,
* Return < 0 for other error.
*/
static int quick_update_accounting(struct btrfs_fs_info *fs_info,
- struct ulist *tmp, u64 src, u64 dst,
- int sign)
+ u64 src, u64 dst, int sign)
{
struct btrfs_qgroup *qgroup;
int ret = 1;
@@ -1491,8 +1465,7 @@ static int quick_update_accounting(struct btrfs_fs_info *fs_info,
goto out;
if (qgroup->excl == qgroup->rfer) {
ret = 0;
- err = __qgroup_excl_accounting(fs_info, tmp, dst,
- qgroup, sign);
+ err = __qgroup_excl_accounting(fs_info, dst, qgroup, sign);
if (err < 0) {
ret = err;
goto out;
@@ -1511,21 +1484,12 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src,
struct btrfs_qgroup *parent;
struct btrfs_qgroup *member;
struct btrfs_qgroup_list *list;
- struct ulist *tmp;
- unsigned int nofs_flag;
int ret = 0;
/* Check the level of src and dst first */
if (btrfs_qgroup_level(src) >= btrfs_qgroup_level(dst))
return -EINVAL;
- /* We hold a transaction handle open, must do a NOFS allocation. */
- nofs_flag = memalloc_nofs_save();
- tmp = ulist_alloc(GFP_KERNEL);
- memalloc_nofs_restore(nofs_flag);
- if (!tmp)
- return -ENOMEM;
-
mutex_lock(&fs_info->qgroup_ioctl_lock);
if (!fs_info->quota_root) {
ret = -ENOTCONN;
@@ -1562,11 +1526,10 @@ int btrfs_add_qgroup_relation(struct btrfs_trans_handle *trans, u64 src,
spin_unlock(&fs_info->qgroup_lock);
goto out;
}
- ret = quick_update_accounting(fs_info, tmp, src, dst, 1);
+ ret = quick_update_accounting(fs_info, src, dst, 1);
spin_unlock(&fs_info->qgroup_lock);
out:
mutex_unlock(&fs_info->qgroup_ioctl_lock);
- ulist_free(tmp);
return ret;
}
@@ -1577,19 +1540,10 @@ static int __del_qgroup_relation(struct btrfs_trans_handle *trans, u64 src,
struct btrfs_qgroup *parent;
struct btrfs_qgroup *member;
struct btrfs_qgroup_list *list;
- struct ulist *tmp;
bool found = false;
- unsigned int nofs_flag;
int ret = 0;
int ret2;
- /* We hold a transaction handle open, must do a NOFS allocation. */
- nofs_flag = memalloc_nofs_save();
- tmp = ulist_alloc(GFP_KERNEL);
- memalloc_nofs_restore(nofs_flag);
- if (!tmp)
- return -ENOMEM;
-
if (!fs_info->quota_root) {
ret = -ENOTCONN;
goto out;
@@ -1627,11 +1581,10 @@ static int __del_qgroup_relation(struct btrfs_trans_handle *trans, u64 src,
if (found) {
spin_lock(&fs_info->qgroup_lock);
del_relation_rb(fs_info, src, dst);
- ret = quick_update_accounting(fs_info, tmp, src, dst, -1);
+ ret = quick_update_accounting(fs_info, src, dst, -1);
spin_unlock(&fs_info->qgroup_lock);
}
out:
- ulist_free(tmp);
return ret;
}
--
2.41.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 5/6] btrfs: qgroup: use qgroup_iterator facility to replace tmp ulist of qgroup_update_refcnt()
2023-09-02 0:13 [PATCH v3 0/6] btrfs: qgroup: remove GFP_ATOMIC usage for ulist Qu Wenruo
` (3 preceding siblings ...)
2023-09-02 0:13 ` [PATCH v3 4/6] btrfs: qgroup: use qgroup_iterator facility for __qgroup_excl_accounting() Qu Wenruo
@ 2023-09-02 0:13 ` Qu Wenruo
2023-09-02 0:13 ` [PATCH v3 6/6] btrfs: qgroup: use qgroup_iterator_nested facility to replace qgroups " Qu Wenruo
2023-09-06 15:55 ` [PATCH v3 0/6] btrfs: qgroup: remove GFP_ATOMIC usage for ulist David Sterba
6 siblings, 0 replies; 9+ messages in thread
From: Qu Wenruo @ 2023-09-02 0:13 UTC (permalink / raw)
To: linux-btrfs; +Cc: Boris Burkov
For function qgroup_update_refcnt(), we use @tmp list to iterate all the
involved qgroups of a subvolume.
It's a perfect match for qgroup_iterator facility, as that @tmp ulist
has a very limited lifespan (just inside the while() loop).
By migrating to qgroup_iterator, we can get rid of the GFP_ATOMIC memory
allocation and no more error handling needed.
Reviewed-by: Boris Burkov <boris@bur.io>
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
fs/btrfs/qgroup.c | 37 +++++++++++--------------------------
1 file changed, 11 insertions(+), 26 deletions(-)
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index 3d768045aa5c..f24feb8f2065 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -2454,13 +2454,11 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans,
* Walk all of the roots that points to the bytenr and adjust their refcnts.
*/
static int qgroup_update_refcnt(struct btrfs_fs_info *fs_info,
- struct ulist *roots, struct ulist *tmp,
- struct ulist *qgroups, u64 seq, int update_old)
+ struct ulist *roots, struct ulist *qgroups,
+ u64 seq, int update_old)
{
struct ulist_node *unode;
struct ulist_iterator uiter;
- struct ulist_node *tmp_unode;
- struct ulist_iterator tmp_uiter;
struct btrfs_qgroup *qg;
int ret = 0;
@@ -2468,40 +2466,35 @@ static int qgroup_update_refcnt(struct btrfs_fs_info *fs_info,
return 0;
ULIST_ITER_INIT(&uiter);
while ((unode = ulist_next(roots, &uiter))) {
+ LIST_HEAD(tmp);
+
qg = find_qgroup_rb(fs_info, unode->val);
if (!qg)
continue;
- ulist_reinit(tmp);
ret = ulist_add(qgroups, qg->qgroupid, qgroup_to_aux(qg),
GFP_ATOMIC);
if (ret < 0)
return ret;
- ret = ulist_add(tmp, qg->qgroupid, qgroup_to_aux(qg), GFP_ATOMIC);
- if (ret < 0)
- return ret;
- ULIST_ITER_INIT(&tmp_uiter);
- while ((tmp_unode = ulist_next(tmp, &tmp_uiter))) {
+ qgroup_iterator_add(&tmp, qg);
+ list_for_each_entry(qg, &tmp, iterator) {
struct btrfs_qgroup_list *glist;
- qg = unode_aux_to_qgroup(tmp_unode);
if (update_old)
btrfs_qgroup_update_old_refcnt(qg, seq, 1);
else
btrfs_qgroup_update_new_refcnt(qg, seq, 1);
+
list_for_each_entry(glist, &qg->groups, next_group) {
ret = ulist_add(qgroups, glist->group->qgroupid,
qgroup_to_aux(glist->group),
GFP_ATOMIC);
if (ret < 0)
return ret;
- ret = ulist_add(tmp, glist->group->qgroupid,
- qgroup_to_aux(glist->group),
- GFP_ATOMIC);
- if (ret < 0)
- return ret;
+ qgroup_iterator_add(&tmp, glist->group);
}
}
+ qgroup_iterator_clean(&tmp);
}
return 0;
}
@@ -2666,7 +2659,6 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
{
struct btrfs_fs_info *fs_info = trans->fs_info;
struct ulist *qgroups = NULL;
- struct ulist *tmp = NULL;
u64 seq;
u64 nr_new_roots = 0;
u64 nr_old_roots = 0;
@@ -2705,12 +2697,6 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
ret = -ENOMEM;
goto out_free;
}
- tmp = ulist_alloc(GFP_NOFS);
- if (!tmp) {
- ret = -ENOMEM;
- goto out_free;
- }
-
mutex_lock(&fs_info->qgroup_rescan_lock);
if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
if (fs_info->qgroup_rescan_progress.objectid <= bytenr) {
@@ -2725,13 +2711,13 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
seq = fs_info->qgroup_seq;
/* Update old refcnts using old_roots */
- ret = qgroup_update_refcnt(fs_info, old_roots, tmp, qgroups, seq,
+ ret = qgroup_update_refcnt(fs_info, old_roots, qgroups, seq,
UPDATE_OLD);
if (ret < 0)
goto out;
/* Update new refcnts using new_roots */
- ret = qgroup_update_refcnt(fs_info, new_roots, tmp, qgroups, seq,
+ ret = qgroup_update_refcnt(fs_info, new_roots, qgroups, seq,
UPDATE_NEW);
if (ret < 0)
goto out;
@@ -2746,7 +2732,6 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
out:
spin_unlock(&fs_info->qgroup_lock);
out_free:
- ulist_free(tmp);
ulist_free(qgroups);
ulist_free(old_roots);
ulist_free(new_roots);
--
2.41.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v3 6/6] btrfs: qgroup: use qgroup_iterator_nested facility to replace qgroups ulist of qgroup_update_refcnt()
2023-09-02 0:13 [PATCH v3 0/6] btrfs: qgroup: remove GFP_ATOMIC usage for ulist Qu Wenruo
` (4 preceding siblings ...)
2023-09-02 0:13 ` [PATCH v3 5/6] btrfs: qgroup: use qgroup_iterator facility to replace tmp ulist of qgroup_update_refcnt() Qu Wenruo
@ 2023-09-02 0:13 ` Qu Wenruo
2023-09-06 15:55 ` [PATCH v3 0/6] btrfs: qgroup: remove GFP_ATOMIC usage for ulist David Sterba
6 siblings, 0 replies; 9+ messages in thread
From: Qu Wenruo @ 2023-09-02 0:13 UTC (permalink / raw)
To: linux-btrfs
The ulist @qgroups is utilized to record all involved qgroups from both
old and new roots inside btrfs_qgroup_account_extent().
Due to the fact that qgroup_update_refcnt() itself is already utilizing
qgroup_iterator, here we have to introduce another list_head,
btrfs_qgroup::nested_iterator, allowing nested iteartion.
Signed-off-by: Qu Wenruo <wqu@suse.com>
---
fs/btrfs/qgroup.c | 84 ++++++++++++++++++++++-------------------------
fs/btrfs/qgroup.h | 18 ++++++++++
2 files changed, 58 insertions(+), 44 deletions(-)
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
index f24feb8f2065..f8881ea0f444 100644
--- a/fs/btrfs/qgroup.c
+++ b/fs/btrfs/qgroup.c
@@ -217,6 +217,7 @@ static struct btrfs_qgroup *add_qgroup_rb(struct btrfs_fs_info *fs_info,
INIT_LIST_HEAD(&qgroup->members);
INIT_LIST_HEAD(&qgroup->dirty);
INIT_LIST_HEAD(&qgroup->iterator);
+ INIT_LIST_HEAD(&qgroup->nested_iterator);
rb_link_node(&qgroup->node, parent, p);
rb_insert_color(&qgroup->node, &fs_info->qgroup_tree);
@@ -2448,22 +2449,42 @@ int btrfs_qgroup_trace_subtree(struct btrfs_trans_handle *trans,
return ret;
}
+static void qgroup_iterator_nested_add(struct list_head *head,
+ struct btrfs_qgroup *qgroup)
+{
+ if (!list_empty(&qgroup->nested_iterator))
+ return;
+
+ list_add_tail(&qgroup->nested_iterator, head);
+}
+
+static void qgroup_iterator_nested_clean(struct list_head *head)
+{
+
+ while (!list_empty(head)) {
+ struct btrfs_qgroup *qgroup;
+
+ qgroup = list_first_entry(head, struct btrfs_qgroup,
+ nested_iterator);
+ list_del_init(&qgroup->nested_iterator);
+ }
+}
+
#define UPDATE_NEW 0
#define UPDATE_OLD 1
/*
* Walk all of the roots that points to the bytenr and adjust their refcnts.
*/
-static int qgroup_update_refcnt(struct btrfs_fs_info *fs_info,
- struct ulist *roots, struct ulist *qgroups,
- u64 seq, int update_old)
+static void qgroup_update_refcnt(struct btrfs_fs_info *fs_info,
+ struct ulist *roots, struct list_head *qgroups,
+ u64 seq, int update_old)
{
struct ulist_node *unode;
struct ulist_iterator uiter;
struct btrfs_qgroup *qg;
- int ret = 0;
if (!roots)
- return 0;
+ return;
ULIST_ITER_INIT(&uiter);
while ((unode = ulist_next(roots, &uiter))) {
LIST_HEAD(tmp);
@@ -2472,10 +2493,7 @@ static int qgroup_update_refcnt(struct btrfs_fs_info *fs_info,
if (!qg)
continue;
- ret = ulist_add(qgroups, qg->qgroupid, qgroup_to_aux(qg),
- GFP_ATOMIC);
- if (ret < 0)
- return ret;
+ qgroup_iterator_nested_add(qgroups, qg);
qgroup_iterator_add(&tmp, qg);
list_for_each_entry(qg, &tmp, iterator) {
struct btrfs_qgroup_list *glist;
@@ -2486,17 +2504,12 @@ static int qgroup_update_refcnt(struct btrfs_fs_info *fs_info,
btrfs_qgroup_update_new_refcnt(qg, seq, 1);
list_for_each_entry(glist, &qg->groups, next_group) {
- ret = ulist_add(qgroups, glist->group->qgroupid,
- qgroup_to_aux(glist->group),
- GFP_ATOMIC);
- if (ret < 0)
- return ret;
+ qgroup_iterator_nested_add(qgroups, glist->group);
qgroup_iterator_add(&tmp, glist->group);
}
}
qgroup_iterator_clean(&tmp);
}
- return 0;
}
/*
@@ -2535,22 +2548,18 @@ static int qgroup_update_refcnt(struct btrfs_fs_info *fs_info,
* But this time we don't need to consider other things, the codes and logic
* is easy to understand now.
*/
-static int qgroup_update_counters(struct btrfs_fs_info *fs_info,
- struct ulist *qgroups,
- u64 nr_old_roots,
- u64 nr_new_roots,
- u64 num_bytes, u64 seq)
+static void qgroup_update_counters(struct btrfs_fs_info *fs_info,
+ struct list_head *qgroups,
+ u64 nr_old_roots,
+ u64 nr_new_roots,
+ u64 num_bytes, u64 seq)
{
- struct ulist_node *unode;
- struct ulist_iterator uiter;
struct btrfs_qgroup *qg;
- u64 cur_new_count, cur_old_count;
- ULIST_ITER_INIT(&uiter);
- while ((unode = ulist_next(qgroups, &uiter))) {
+ list_for_each_entry(qg, qgroups, nested_iterator) {
+ u64 cur_new_count, cur_old_count;
bool dirty = false;
- qg = unode_aux_to_qgroup(unode);
cur_old_count = btrfs_qgroup_get_old_refcnt(qg, seq);
cur_new_count = btrfs_qgroup_get_new_refcnt(qg, seq);
@@ -2621,7 +2630,6 @@ static int qgroup_update_counters(struct btrfs_fs_info *fs_info,
if (dirty)
qgroup_dirty(fs_info, qg);
}
- return 0;
}
/*
@@ -2658,7 +2666,7 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
struct ulist *new_roots)
{
struct btrfs_fs_info *fs_info = trans->fs_info;
- struct ulist *qgroups = NULL;
+ LIST_HEAD(qgroups);
u64 seq;
u64 nr_new_roots = 0;
u64 nr_old_roots = 0;
@@ -2692,11 +2700,6 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
trace_btrfs_qgroup_account_extent(fs_info, trans->transid, bytenr,
num_bytes, nr_old_roots, nr_new_roots);
- qgroups = ulist_alloc(GFP_NOFS);
- if (!qgroups) {
- ret = -ENOMEM;
- goto out_free;
- }
mutex_lock(&fs_info->qgroup_rescan_lock);
if (fs_info->qgroup_flags & BTRFS_QGROUP_STATUS_FLAG_RESCAN) {
if (fs_info->qgroup_rescan_progress.objectid <= bytenr) {
@@ -2711,28 +2714,21 @@ int btrfs_qgroup_account_extent(struct btrfs_trans_handle *trans, u64 bytenr,
seq = fs_info->qgroup_seq;
/* Update old refcnts using old_roots */
- ret = qgroup_update_refcnt(fs_info, old_roots, qgroups, seq,
- UPDATE_OLD);
- if (ret < 0)
- goto out;
+ qgroup_update_refcnt(fs_info, old_roots, &qgroups, seq, UPDATE_OLD);
/* Update new refcnts using new_roots */
- ret = qgroup_update_refcnt(fs_info, new_roots, qgroups, seq,
- UPDATE_NEW);
- if (ret < 0)
- goto out;
+ qgroup_update_refcnt(fs_info, new_roots, &qgroups, seq, UPDATE_NEW);
- qgroup_update_counters(fs_info, qgroups, nr_old_roots, nr_new_roots,
+ qgroup_update_counters(fs_info, &qgroups, nr_old_roots, nr_new_roots,
num_bytes, seq);
/*
* Bump qgroup_seq to avoid seq overlap
*/
fs_info->qgroup_seq += max(nr_old_roots, nr_new_roots) + 1;
-out:
spin_unlock(&fs_info->qgroup_lock);
out_free:
- ulist_free(qgroups);
+ qgroup_iterator_nested_clean(&qgroups);
ulist_free(old_roots);
ulist_free(new_roots);
return ret;
diff --git a/fs/btrfs/qgroup.h b/fs/btrfs/qgroup.h
index 5dc0583622c3..fb0afc6b0de8 100644
--- a/fs/btrfs/qgroup.h
+++ b/fs/btrfs/qgroup.h
@@ -229,6 +229,24 @@ struct btrfs_qgroup {
* And should be reset to empty after the iteration is finished.
*/
struct list_head iterator;
+
+ /*
+ * For nested iterator usage.
+ *
+ * Here we support at most one level of nested iterator call like:
+ *
+ * LIST_HEAD(all_qgroups);
+ * {
+ * LIST_HEAD(local_qgroups);
+ * qgroup_iterator_add(local_qgroups, qg);
+ * qgroup_iterator_nested_add(all_qgroups, qg);
+ * do_some_work(local_qgroups);
+ * qgroup_iterator_clean(local_qgroups);
+ * }
+ * do_some_work(all_qgroups);
+ * qgroup_iterator_nested_clean(all_qgroups);
+ */
+ struct list_head nested_iterator;
struct rb_node node; /* tree of qgroups */
/*
--
2.41.0
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v3 0/6] btrfs: qgroup: remove GFP_ATOMIC usage for ulist
2023-09-02 0:13 [PATCH v3 0/6] btrfs: qgroup: remove GFP_ATOMIC usage for ulist Qu Wenruo
` (5 preceding siblings ...)
2023-09-02 0:13 ` [PATCH v3 6/6] btrfs: qgroup: use qgroup_iterator_nested facility to replace qgroups " Qu Wenruo
@ 2023-09-06 15:55 ` David Sterba
2023-09-06 16:07 ` David Sterba
6 siblings, 1 reply; 9+ messages in thread
From: David Sterba @ 2023-09-06 15:55 UTC (permalink / raw)
To: Qu Wenruo; +Cc: linux-btrfs
On Sat, Sep 02, 2023 at 08:13:51AM +0800, Qu Wenruo wrote:
> [CHANGELOG]
> v3:
> - Remove the no longer utilized memalloc_nofs_save() calls
> - Remove the "@" prefix of variables in the subject line
> - Rename iterator2 to nested_iterator and enhance the comments of it
>
> v2:
> - Remove the final GFP_ATOMIC ulist usage
> This is done by introducing a new list_head, btrfs_qgroup::iterator2,
> allowing us to do nested iteration.
>
> This extra nested facility is needed as even if we move the qgroups
> collection into one dedicated function, we're reusing the list for
> iteration which can lead to unnecessary re-visit of the same qgroup.
>
> Thus we have to support one level of nested iteration.
>
> - Add reviewed by tags from Boris
>
> [REPO]
> https://github.com/adam900710/linux/tree/qgroup_mutex
>
> Yep, the branch name shows my previous failed attempt to solve the
> problem.
>
> [PROBLEM]
> There are quite some GFP_ATOMIC usage for btrfs qgroups, most of them
> are for ulists.
>
> Those ulists are just used as a temporary memory to trace the involved
> qgroups.
>
> [ENHANCEMENT]
> This patchset would address the problem by adding a new list_head called
> iterator for btrfs_qgroup.
>
> And all call sites but one of ulist allocation can be migrated to use
> the new qgroup_iterator facility to iterate qgroups without any memory
> allocation.
>
> There is a special call site in btrfs_qgroup_account_extent() that it
> wants to do nested qgroups iteration, thus a nested version is
> introduced for this particular call site.
>
> And BTW since we can skip quite some memory allocation failure handling
> (since there is no memory allocation), we also save some lines of code.
>
> Qu Wenruo (6):
> btrfs: qgroup: iterate qgroups without memory allocation for
> qgroup_reserve()
> btrfs: qgroup: use qgroup_iterator facility for
> btrfs_qgroup_free_refroot()
> btrfs: qgroup: use qgroup_iterator facility for qgroup_convert_meta()
> btrfs: qgroup: use qgroup_iterator facility for
> __qgroup_excl_accounting()
> btrfs: qgroup: use qgroup_iterator facility to replace tmp ulist of
> qgroup_update_refcnt()
> btrfs: qgroup: use qgroup_iterator_nested facility to replace qgroups
> ulist of qgroup_update_refcnt()
Added to misc-next, with some minor fixups, thanks. Getting rid of the
allocations and error handling is great. I think qgroup_to_aux() is now
unused and maybe there's more related to the ulists that can be cleaned
up or removed.
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH v3 0/6] btrfs: qgroup: remove GFP_ATOMIC usage for ulist
2023-09-06 15:55 ` [PATCH v3 0/6] btrfs: qgroup: remove GFP_ATOMIC usage for ulist David Sterba
@ 2023-09-06 16:07 ` David Sterba
0 siblings, 0 replies; 9+ messages in thread
From: David Sterba @ 2023-09-06 16:07 UTC (permalink / raw)
To: David Sterba; +Cc: Qu Wenruo, linux-btrfs
On Wed, Sep 06, 2023 at 05:55:42PM +0200, David Sterba wrote:
> On Sat, Sep 02, 2023 at 08:13:51AM +0800, Qu Wenruo wrote:
> > [CHANGELOG]
> > v3:
> > - Remove the no longer utilized memalloc_nofs_save() calls
> > - Remove the "@" prefix of variables in the subject line
> > - Rename iterator2 to nested_iterator and enhance the comments of it
> >
> > v2:
> > - Remove the final GFP_ATOMIC ulist usage
> > This is done by introducing a new list_head, btrfs_qgroup::iterator2,
> > allowing us to do nested iteration.
> >
> > This extra nested facility is needed as even if we move the qgroups
> > collection into one dedicated function, we're reusing the list for
> > iteration which can lead to unnecessary re-visit of the same qgroup.
> >
> > Thus we have to support one level of nested iteration.
> >
> > - Add reviewed by tags from Boris
> >
> > [REPO]
> > https://github.com/adam900710/linux/tree/qgroup_mutex
> >
> > Yep, the branch name shows my previous failed attempt to solve the
> > problem.
> >
> > [PROBLEM]
> > There are quite some GFP_ATOMIC usage for btrfs qgroups, most of them
> > are for ulists.
> >
> > Those ulists are just used as a temporary memory to trace the involved
> > qgroups.
> >
> > [ENHANCEMENT]
> > This patchset would address the problem by adding a new list_head called
> > iterator for btrfs_qgroup.
> >
> > And all call sites but one of ulist allocation can be migrated to use
> > the new qgroup_iterator facility to iterate qgroups without any memory
> > allocation.
> >
> > There is a special call site in btrfs_qgroup_account_extent() that it
> > wants to do nested qgroups iteration, thus a nested version is
> > introduced for this particular call site.
> >
> > And BTW since we can skip quite some memory allocation failure handling
> > (since there is no memory allocation), we also save some lines of code.
> >
> > Qu Wenruo (6):
> > btrfs: qgroup: iterate qgroups without memory allocation for
> > qgroup_reserve()
> > btrfs: qgroup: use qgroup_iterator facility for
> > btrfs_qgroup_free_refroot()
> > btrfs: qgroup: use qgroup_iterator facility for qgroup_convert_meta()
> > btrfs: qgroup: use qgroup_iterator facility for
> > __qgroup_excl_accounting()
> > btrfs: qgroup: use qgroup_iterator facility to replace tmp ulist of
> > qgroup_update_refcnt()
> > btrfs: qgroup: use qgroup_iterator_nested facility to replace qgroups
> > ulist of qgroup_update_refcnt()
>
> Added to misc-next, with some minor fixups, thanks. Getting rid of the
> allocations and error handling is great. I think qgroup_to_aux() is now
> unused and maybe there's more related to the ulists that can be cleaned
> up or removed.
What's a bit unfortunate that now the size of btrfs_qgroup grows from
256 bytes to 288, which does not fit the 256 byte slab and goes to 512.
I don't see how the size could be reduced so we may eventually utilize
the remaining space for embedding other structures or caching some
values.
^ permalink raw reply [flat|nested] 9+ messages in thread