[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250825122931.13037-2-wangyufei@vivo.com>
Date: Mon, 25 Aug 2025 20:29:30 +0800
From: wangyufei <wangyufei@...o.com>
To: Andrew Morton <akpm@...ux-foundation.org>,
David Hildenbrand <david@...hat.com>,
Lorenzo Stoakes <lorenzo.stoakes@...cle.com>,
"Liam R. Howlett" <Liam.Howlett@...cle.com>,
Vlastimil Babka <vbabka@...e.cz>,
Mike Rapoport <rppt@...nel.org>,
Suren Baghdasaryan <surenb@...gle.com>,
Michal Hocko <mhocko@...e.com>,
"Matthew Wilcox (Oracle)" <willy@...radead.org>,
Stephen Rothwell <sfr@...b.auug.org.au>,
wangyufei <wangyufei@...o.com>,
linux-kernel@...r.kernel.org (open list),
linux-mm@...ck.org (open list:MEMORY MANAGEMENT - MISC),
linux-fsdevel@...r.kernel.org (open list:PAGE CACHE)
Cc: kundan.kumar@...sung.com,
anuj20.g@...sung.com,
hch@....de,
bernd@...ernd.com,
djwong@...nel.org,
jack@...e.cz,
linux-kernel@...r.kernel.org,
linux-mm@...ck.org,
linux-fsdevel@...r.kernel.org,
opensource.kernel@...o.com
Subject: [RFC 1/1] writeback: add sysfs to config the number of writeback contexts
The number of writeback contexts is set to the number of CPUs by
default. To test the impact of the number of writeback contexts
on the writeback performance of filesystems, we introduce a sysfs
interface 'nwritebacks' for adjusting bdi->wb_ctx_arr in runtime.
However, only increasing the bdi->wb_ctx_arr is supported; support
for reducing it is still under development.
Signed-off-by: wangyufei <wangyufei@...o.com>
---
include/linux/backing-dev.h | 3 ++
mm/backing-dev.c | 59 +++++++++++++++++++++++++++++++++++
mm/page-writeback.c | 61 +++++++++++++++++++++++++++++++++++++
3 files changed, 123 insertions(+)
diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h
index 30a812fbd..c59578c25 100644
--- a/include/linux/backing-dev.h
+++ b/include/linux/backing-dev.h
@@ -112,6 +112,7 @@ int bdi_set_max_ratio_no_scale(struct backing_dev_info *bdi, unsigned int max_ra
int bdi_set_min_bytes(struct backing_dev_info *bdi, u64 min_bytes);
int bdi_set_max_bytes(struct backing_dev_info *bdi, u64 max_bytes);
int bdi_set_strict_limit(struct backing_dev_info *bdi, unsigned int strict_limit);
+int bdi_set_nwritebacks(struct backing_dev_info *bdi, int nwritebacks);
/*
* Flags in backing_dev_info::capability
@@ -128,6 +129,8 @@ int bdi_set_strict_limit(struct backing_dev_info *bdi, unsigned int strict_limit
extern struct backing_dev_info noop_backing_dev_info;
int bdi_init(struct backing_dev_info *bdi);
+int bdi_wb_ctx_init(struct backing_dev_info *bdi, struct bdi_writeback_ctx *bdi_wb_ctx);
+void bdi_wb_ctx_exit(struct backing_dev_info *bdi, struct bdi_writeback_ctx *bdi_wb_ctx);
/**
* writeback_in_progress - determine whether there is writeback in progress
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index a5b44dd79..44b24c1e4 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -469,6 +469,34 @@ static ssize_t strict_limit_show(struct device *dev,
}
static DEVICE_ATTR_RW(strict_limit);
+static ssize_t nwritebacks_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct backing_dev_info *bdi = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%d\n", bdi->nr_wb_ctx);
+}
+
+static ssize_t nwritebacks_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct backing_dev_info *bdi = dev_get_drvdata(dev);
+ int nr;
+ ssize_t ret;
+
+ ret = kstrtoint(buf, 10, &nr);
+ if (ret < 0)
+ return ret;
+
+ ret = bdi_set_nwritebacks(bdi, nr);
+ if (!ret)
+ ret = count;
+
+ return ret;
+}
+static DEVICE_ATTR_RW(nwritebacks);
+
static struct attribute *bdi_dev_attrs[] = {
&dev_attr_read_ahead_kb.attr,
&dev_attr_min_ratio.attr,
@@ -479,6 +507,7 @@ static struct attribute *bdi_dev_attrs[] = {
&dev_attr_max_bytes.attr,
&dev_attr_stable_pages_required.attr,
&dev_attr_strict_limit.attr,
+ &dev_attr_nwritebacks.attr,
NULL,
};
ATTRIBUTE_GROUPS(bdi_dev);
@@ -1004,6 +1033,22 @@ static int __init cgwb_init(void)
}
subsys_initcall(cgwb_init);
+int bdi_wb_ctx_init(struct backing_dev_info *bdi, struct bdi_writeback_ctx *bdi_wb_ctx)
+{
+ int ret;
+
+ INIT_RADIX_TREE(&bdi_wb_ctx->cgwb_tree, GFP_ATOMIC);
+ mutex_init(&bdi->cgwb_release_mutex);
+ init_rwsem(&bdi_wb_ctx->wb_switch_rwsem);
+
+ ret = wb_init(&bdi_wb_ctx->wb, bdi_wb_ctx, bdi, GFP_KERNEL);
+ if (!ret) {
+ bdi_wb_ctx->wb.memcg_css = &root_mem_cgroup->css;
+ bdi_wb_ctx->wb.blkcg_css = blkcg_root_css;
+ }
+ return ret;
+}
+
#else /* CONFIG_CGROUP_WRITEBACK */
static int cgwb_bdi_init(struct backing_dev_info *bdi)
@@ -1292,3 +1337,17 @@ const char *bdi_dev_name(struct backing_dev_info *bdi)
return bdi->dev_name;
}
EXPORT_SYMBOL_GPL(bdi_dev_name);
+
+int bdi_wb_ctx_init(struct backing_dev_info *bdi, struct bdi_writeback_ctx *bdi_wb_ctx)
+{
+ return wb_init(&bdi_wb_ctx->wb, bdi_wb_ctx, bdi, GFP_KERNEL);
+}
+
+void bdi_wb_ctx_exit(struct backing_dev_info *bdi, struct bdi_writeback_ctx *bdi_wb_ctx)
+{
+ wb_shutdown(&bdi_wb_ctx->wb);
+ cgwb_bdi_unregister(bdi, bdi_wb_ctx);
+
+ WARN_ON_ONCE(test_bit(WB_registered, &bdi_wb_ctx->wb.state));
+ wb_exit(&bdi_wb_ctx->wb);
+}
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 6f283a777..87c77004b 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -740,6 +740,59 @@ static int __bdi_set_max_ratio(struct backing_dev_info *bdi, unsigned int max_ra
return ret;
}
+static int __bdi_set_wb_ctx(struct backing_dev_info *bdi, int nwritebacks)
+{
+ struct bdi_writeback_ctx **new_ctx_arr, **old_ctx_arr;
+ int i, ret;
+
+ new_ctx_arr = kcalloc(nwritebacks, sizeof(struct bdi_writeback_ctx *), GFP_KERNEL);
+ if (!new_ctx_arr)
+ return -ENOMEM;
+
+ for (i = 0; i < min(bdi->nr_wb_ctx, nwritebacks); i++)
+ new_ctx_arr[i] = bdi->wb_ctx_arr[i];
+
+ for (i = bdi->nr_wb_ctx; i < nwritebacks; i++) {
+ new_ctx_arr[i] = (struct bdi_writeback_ctx *)
+ kzalloc(sizeof(struct bdi_writeback_ctx), GFP_KERNEL);
+ if (!new_ctx_arr[i]) {
+ pr_err("Failed to allocate %d", i);
+ while (--i >= bdi->nr_wb_ctx)
+ kfree(new_ctx_arr[i]);
+ kfree(new_ctx_arr);
+ return -ENOMEM;
+ }
+ INIT_LIST_HEAD(&new_ctx_arr[i]->wb_list);
+ init_waitqueue_head(&new_ctx_arr[i]->wb_waitq);
+ }
+
+ for (i = bdi->nr_wb_ctx; i < nwritebacks; i++) {
+ ret = bdi_wb_ctx_init(bdi, new_ctx_arr[i]);
+ if (ret) {
+ while (--i >= bdi->nr_wb_ctx) {
+ bdi_wb_ctx_exit(bdi, new_ctx_arr[i]);
+ kfree(new_ctx_arr[i]);
+ }
+ kfree(new_ctx_arr);
+ return ret;
+ }
+ list_add_tail_rcu(&new_ctx_arr[i]->wb.bdi_node, &new_ctx_arr[i]->wb_list);
+ set_bit(WB_registered, &new_ctx_arr[i]->wb.state);
+ }
+
+ // Make sure the initialization is done before assignment
+ smp_wmb();
+
+ old_ctx_arr = bdi->wb_ctx_arr;
+ spin_lock_bh(&bdi_lock);
+ bdi->wb_ctx_arr = new_ctx_arr;
+ bdi->nr_wb_ctx = nwritebacks;
+ spin_unlock_bh(&bdi_lock);
+
+ kfree(old_ctx_arr);
+ return 0;
+}
+
int bdi_set_min_ratio_no_scale(struct backing_dev_info *bdi, unsigned int min_ratio)
{
return __bdi_set_min_ratio(bdi, min_ratio);
@@ -818,6 +871,14 @@ int bdi_set_strict_limit(struct backing_dev_info *bdi, unsigned int strict_limit
return 0;
}
+int bdi_set_nwritebacks(struct backing_dev_info *bdi, int nwritebacks)
+{
+ if (nwritebacks < bdi->nr_wb_ctx)
+ return -EINVAL;
+
+ return __bdi_set_wb_ctx(bdi, nwritebacks);
+}
+
static unsigned long dirty_freerun_ceiling(unsigned long thresh,
unsigned long bg_thresh)
{
--
2.39.0
Powered by blists - more mailing lists