[PATCH v2 14/16] writeback: segregated allocation and free of writeback contexts

Kundan Kumar
2025-10-14T12:11:31Z

The independent functions of alloc and free will be used while changing
the number of writeback contexts.

Signed-off-by: Kundan Kumar <kundan.kumar@samsung.com>
Signed-off-by: Anuj Gupta <anuj20.g@samsung.com>
---
mm/backing-dev.c | 72 ++++++++++++++++++++++++++++++++----------------
1 file changed, 49 insertions(+), 23 deletions(-)

diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index 0a3204a3a3a3..2a8f3b683b2d 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -1038,8 +1038,46 @@ static void cgwb_remove_from_bdi_list(struct bdi_writeback *wb)

#endif /* CONFIG_CGROUP_WRITEBACK */

+static struct bdi_writeback_ctx **wb_ctx_alloc(struct backing_dev_info *bdi,
+ int num_ctxs)
+{
+ struct bdi_writeback_ctx **wb_ctx;
+
+ wb_ctx = kcalloc(num_ctxs, sizeof(struct bdi_writeback_ctx *),
+ GFP_KERNEL);
+ if (!wb_ctx)
+ return NULL;
+
+ for (int i = 0; i < num_ctxs; i++) {
+ wb_ctx[i] = (struct bdi_writeback_ctx *)
+ kzalloc(sizeof(struct bdi_writeback_ctx), GFP_KERNEL);
+ if (!wb_ctx[i]) {
+ pr_err("Failed to allocate %d", i);
+ while (--i >= 0)
+ kfree(wb_ctx[i]);
+ kfree(wb_ctx);
+ return NULL;
+ }
+ INIT_LIST_HEAD(&wb_ctx[i]->wb_list);
+ init_waitqueue_head(&wb_ctx[i]->wb_waitq);
+ }
+ return wb_ctx;
+}
+
+static void wb_ctx_free(struct backing_dev_info *bdi)
+{
+ struct bdi_writeback_ctx *bdi_wb_ctx;
+
+ for_each_bdi_wb_ctx(bdi, bdi_wb_ctx) {
+ kfree(bdi_wb_ctx);
+ }
+ kfree(bdi->wb_ctx);
+}
+
int bdi_init(struct backing_dev_info *bdi)
{
+ int ret;
+
bdi->dev = NULL;

kref_init(&bdi->refcnt);
@@ -1047,48 +1085,36 @@ int bdi_init(struct backing_dev_info *bdi)
bdi->max_ratio = 100 * BDI_RATIO_SCALE;
bdi->max_prop_frac = FPROP_FRAC_BASE;

+ INIT_LIST_HEAD(&bdi->bdi_list);
+
/*
* User can configure nr_wb_ctx using the newly introduced sysfs knob.
* echo N > /sys/class/bdi/<maj>:<min>/nwritebacks
* Filesystem can also increase same during mount.
*/
bdi->nr_wb_ctx = 1;
- bdi->wb_ctx = kcalloc(bdi->nr_wb_ctx,
- sizeof(struct bdi_writeback_ctx *),
- GFP_KERNEL);
- INIT_LIST_HEAD(&bdi->bdi_list);
- for (int i = 0; i < bdi->nr_wb_ctx; i++) {
- bdi->wb_ctx[i] = (struct bdi_writeback_ctx *)
- kzalloc(sizeof(struct bdi_writeback_ctx), GFP_KERNEL);
- if (!bdi->wb_ctx[i]) {
- pr_err("Failed to allocate %d", i);
- while (--i >= 0)
- kfree(bdi->wb_ctx[i]);
- kfree(bdi->wb_ctx);
- return -ENOMEM;
- }
- INIT_LIST_HEAD(&bdi->wb_ctx[i]->wb_list);
- init_waitqueue_head(&bdi->wb_ctx[i]->wb_waitq);
- }
+
+ bdi->wb_ctx = wb_ctx_alloc(bdi, bdi->nr_wb_ctx);
+ if (!bdi->wb_ctx)
+ return -ENOMEM;
+
bdi->last_bdp_sleep = jiffies;

- return cgwb_bdi_init(bdi);
+ ret = cgwb_bdi_init(bdi);
+ if (ret)
+ wb_ctx_free(bdi);
+ return ret;
}

struct backing_dev_info *bdi_alloc(int node_id)
{
struct backing_dev_info *bdi;
- struct bdi_writeback_ctx *bdi_wb_ctx;

bdi = kzalloc_node(sizeof(*bdi), GFP_KERNEL, node_id);
if (!bdi)
return NULL;

if (bdi_init(bdi)) {
- for_each_bdi_wb_ctx(bdi, bdi_wb_ctx) {
- kfree(bdi_wb_ctx);
- }
- kfree(bdi->wb_ctx);
kfree(bdi);
return NULL;
}
--
2.25.1