[PATCH v2 04/16] writeback: affine inode to a writeback ctx within a bdi

Kundan Kumar
2025-10-14T12:10:38Z

Affine inode to a writeback context. This helps in minimizing the
filesytem fragmentation due to inode being processed by different
threads.

To support parallel writeback, wire up a new superblock operation
get_inode_wb_ctx(). Filesystems can override this callback and select
desired writeback context for a inode. FS can use the wb context based
on its geometry and also use 64 bit inode numbers.

If a filesystem doesn't implement this callback, it defaults to
DEFALT_WB_CTX = 0, maintaining its original behavior.

An example implementation for XFS is provided, where XFS selects the
writeback context based on its Allocation Group number.

Signed-off-by: Anuj Gupta <anuj20.g@samsung.com>
Signed-off-by: Kundan Kumar <kundan.kumar@samsung.com>
---
fs/fs-writeback.c | 3 ++-
fs/xfs/xfs_super.c | 13 +++++++++++++
include/linux/backing-dev.h | 5 ++++-
include/linux/fs.h | 1 +
4 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 0715a7617391..56c048e22f72 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -265,7 +265,8 @@ void __inode_attach_wb(struct inode *inode, struct folio *folio)
{
struct backing_dev_info *bdi = inode_to_bdi(inode);
struct bdi_writeback *wb = NULL;
- struct bdi_writeback_ctx *bdi_writeback_ctx = bdi->wb_ctx[0];
+ struct bdi_writeback_ctx *bdi_writeback_ctx =
+ fetch_bdi_writeback_ctx(inode);

if (inode_cgwb_enabled(inode)) {
struct cgroup_subsys_state *memcg_css;
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index bb0a82635a77..b3ec9141d902 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -53,6 +53,7 @@
#include <linux/magic.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
+#include <linux/backing-dev.h>

static const struct super_operations xfs_super_operations;

@@ -1294,6 +1295,17 @@ xfs_fs_show_stats(
return 0;
}

+static struct bdi_writeback_ctx *
+xfs_get_inode_wb_ctx(
+ struct inode *inode)
+{
+ struct xfs_inode *ip = XFS_I(inode);
+ struct backing_dev_info *bdi = inode_to_bdi(inode);
+ xfs_agino_t agno = XFS_INO_TO_AGNO(ip->i_mount, ip->i_ino);
+
+ return bdi->wb_ctx[agno % bdi->nr_wb_ctx];
+}
+
static const struct super_operations xfs_super_operations = {
.alloc_inode = xfs_fs_alloc_inode,
.destroy_inode = xfs_fs_destroy_inode,
@@ -1310,6 +1322,7 @@ static const struct super_operations xfs_super_operations = {
.free_cached_objects = xfs_fs_free_cached_objects,
.shutdown = xfs_fs_shutdown,
.show_stats = xfs_fs_show_stats,
+ .get_inode_wb_ctx = xfs_get_inode_wb_ctx,
};

static int
diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h
index 951ab5497500..59bbb69d300c 100644
--- a/include/linux/backing-dev.h
+++ b/include/linux/backing-dev.h
@@ -148,6 +148,7 @@ static inline bool mapping_can_writeback(struct address_space *mapping)
return inode_to_bdi(mapping->host)->capabilities & BDI_CAP_WRITEBACK;
}

+#define DEFAULT_WB_CTX 0
#define for_each_bdi_wb_ctx(bdi, wbctx) \
for (int __i = 0; __i < (bdi)->nr_wb_ctx \
&& ((wbctx) = (bdi)->wb_ctx[__i]) != NULL; __i++)
@@ -157,7 +158,9 @@ fetch_bdi_writeback_ctx(struct inode *inode)
{
struct backing_dev_info *bdi = inode_to_bdi(inode);

- return bdi->wb_ctx[0];
+ if (inode->i_sb->s_op->get_inode_wb_ctx)
+ return inode->i_sb->s_op->get_inode_wb_ctx(inode);
+ return bdi->wb_ctx[DEFAULT_WB_CTX];
}

#ifdef CONFIG_CGROUP_WRITEBACK
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 754fec84f350..5199b0d49fa5 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2379,6 +2379,7 @@ struct super_operations {
*/
int (*remove_bdev)(struct super_block *sb, struct block_device *bdev);
void (*shutdown)(struct super_block *sb);
+ struct bdi_writeback_ctx *(*get_inode_wb_ctx)(struct inode *inode);
};

/*
--
2.25.1