The bdi_writeback now provides NR_WB_CTX number of writeback contexts stored in the wb_ctx_list. This allows each allocation group in xfs to utilize a distinct writeback context. To implement this, the following changes have been made: - Adjust xfs to assign a unique writeback context to each allocation group. - Include a super_operations function pointer within super_operations to retrieve the writeback context associated with an inode. Currently it is implemented by xfs. It can be extended to other FS in future. Signed-off-by: Kundan Kumar <kundan.kumar@xxxxxxxxxxx> Signed-off-by: Anuj Gupta <anuj20.g@xxxxxxxxxxx> --- fs/fs-writeback.c | 34 ++++++++++++++++++++++------------ fs/xfs/libxfs/xfs_ag.c | 16 ++++++++++++++++ fs/xfs/libxfs/xfs_ag.h | 1 + fs/xfs/xfs_super.c | 20 ++++++++++++++++++++ include/linux/fs.h | 1 + 5 files changed, 60 insertions(+), 12 deletions(-) diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 8f7dd5d10085..75a6f5c50ee7 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -2712,7 +2712,7 @@ void __mark_inode_dirty(struct inode *inode, int flags) */ if (!was_dirty) { /* Schedule writeback on first wb_ctx */ - struct wb_ctx *p_wb_ctx = ctx_wb_struct(wb, 0); + struct wb_ctx *p_wb_ctx; struct list_head *dirty_list; bool wakeup_bdi = false; @@ -2720,17 +2720,27 @@ void __mark_inode_dirty(struct inode *inode, int flags) if (dirtytime) inode->dirtied_time_when = jiffies; - - if (inode->i_state & I_DIRTY) - dirty_list = ctx_b_dirty_list(wb, 0); - else - dirty_list = ctx_b_dirty_time_list(wb, - 0); - - wakeup_bdi = inode_io_list_move_locked_ctx(inode, wb, - dirty_list, - p_wb_ctx); - + if (!sb->s_op->get_wb_ctx) { + p_wb_ctx = ctx_wb_struct(wb, 0); + + if (inode->i_state & I_DIRTY) + dirty_list = ctx_b_dirty_list(wb, 0); + else + dirty_list = ctx_b_dirty_time_list(wb, + 0); + wakeup_bdi = inode_io_list_move_locked_ctx(inode, wb, + dirty_list, + p_wb_ctx); + } else { + p_wb_ctx = sb->s_op->get_wb_ctx(inode); + if (inode->i_state & I_DIRTY) + dirty_list = &p_wb_ctx->pctx_b_dirty; + else + dirty_list = &p_wb_ctx->pctx_b_dirty_time; + wakeup_bdi = inode_io_list_move_locked_ctx(inode, wb, + dirty_list, + p_wb_ctx); + } spin_unlock(&wb->list_lock); spin_unlock(&inode->i_lock); trace_writeback_dirty_inode_enqueue(inode); diff --git a/fs/xfs/libxfs/xfs_ag.c b/fs/xfs/libxfs/xfs_ag.c index b59cb461e096..5d6551e5522e 100644 --- a/fs/xfs/libxfs/xfs_ag.c +++ b/fs/xfs/libxfs/xfs_ag.c @@ -214,6 +214,11 @@ xfs_update_last_ag_size( return 0; } +static struct backing_dev_info *xfs_mp_to_bdi(struct xfs_mount *mp) +{ + return mp->m_super->s_bdi; +} + static int xfs_perag_alloc( struct xfs_mount *mp, @@ -221,12 +226,22 @@ xfs_perag_alloc( xfs_agnumber_t agcount, xfs_rfsblock_t dblocks) { + struct backing_dev_info *bdi = xfs_mp_to_bdi(mp); + struct bdi_writeback *wb = &bdi->wb; + struct wb_ctx *p_wb_ctx; struct xfs_perag *pag; int error; pag = kzalloc(sizeof(*pag), GFP_KERNEL); if (!pag) return -ENOMEM; + spin_lock(&wb->list_lock); + if (wb->wb_idx >= NR_WB_CTX) + wb->wb_idx = 0; + p_wb_ctx = &wb->wb_ctx_list[wb->wb_idx]; + wb->wb_idx++; + spin_unlock(&wb->list_lock); + pag->pag_wb_ctx = p_wb_ctx; #ifdef __KERNEL__ /* Place kernel structure only init below this point. */ @@ -261,6 +276,7 @@ xfs_perag_alloc( return error; } + int xfs_initialize_perag( struct xfs_mount *mp, diff --git a/fs/xfs/libxfs/xfs_ag.h b/fs/xfs/libxfs/xfs_ag.h index 1f24cfa27321..5c25850e94dd 100644 --- a/fs/xfs/libxfs/xfs_ag.h +++ b/fs/xfs/libxfs/xfs_ag.h @@ -89,6 +89,7 @@ struct xfs_perag { /* background prealloc block trimming */ struct delayed_work pag_blockgc_work; + struct wb_ctx *pag_wb_ctx; #endif /* __KERNEL__ */ }; diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index d92d7a07ea89..7416e1ad45e9 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -1210,6 +1210,25 @@ xfs_fs_shutdown( xfs_force_shutdown(XFS_M(sb), SHUTDOWN_DEVICE_REMOVED); } +static struct wb_ctx * + xfs_get_wb_ctx( + struct inode *inode) +{ + struct xfs_inode *xfs_inode = XFS_I(inode); + struct xfs_perag *pag = xfs_perag_get(xfs_inode->i_mount, + XFS_INO_TO_AGNO(xfs_inode->i_mount, xfs_inode->i_ino)); + struct wb_ctx *p_wb_ctx; + + if (!pag) { + /* There had better still be a perag structure! */ + ASSERT(0); + return NULL; + } + p_wb_ctx = pag->pag_wb_ctx; + xfs_perag_put(pag); + return p_wb_ctx; +} + static const struct super_operations xfs_super_operations = { .alloc_inode = xfs_fs_alloc_inode, .destroy_inode = xfs_fs_destroy_inode, @@ -1224,6 +1243,7 @@ static const struct super_operations xfs_super_operations = { .nr_cached_objects = xfs_fs_nr_cached_objects, .free_cached_objects = xfs_fs_free_cached_objects, .shutdown = xfs_fs_shutdown, + .get_wb_ctx = xfs_get_wb_ctx, }; static int diff --git a/include/linux/fs.h b/include/linux/fs.h index be3ad155ec9f..4f655daeb97f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2307,6 +2307,7 @@ struct super_operations { long (*free_cached_objects)(struct super_block *, struct shrink_control *); void (*shutdown)(struct super_block *sb); + struct wb_ctx *(*get_wb_ctx)(struct inode *); }; /* -- 2.25.1