All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 0/3] Parallelizing filesystem writeback
       [not found] <CGME20250212104507epcas5p18722b26bd022151979418508eae99065@epcas5p1.samsung.com>
@ 2025-02-12 10:36 ` Kundan Kumar
  2025-02-12 10:36   ` [RFC 1/3] writeback: add parallel writeback infrastructure Kundan Kumar
                     ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Kundan Kumar @ 2025-02-12 10:36 UTC (permalink / raw)
  To: david, mcgrof, jack, hch
  Cc: linux-fsdevel, linux-xfs, anuj20.g, axboe, clm, willy, gost.dev,
	vishak.g, amir73il, brauner, Kundan Kumar

This RFC is an attempt to share a POC for our LSFMM'25[0] proposal.
We initially prepared it based on the percpu writeback context design.
But based on feedback received initially, we moved towards a more
FS-geometry aware design. The patches are still clumsy, and there is
softlockup issue while running fstests as well(mentioned below).
Yet, we would like to gather feedback which will help in ironing
out the infra.

Kernel tree:
https://github.com/SamsungDS/linux/tree/priv/pw/rfc

Design
==========================
- A new "wb_ctx" structure is introduced that represents a writeback
  context/thread.
  A new "wb_ctx_list" field is introduced in bdi_writeback that describes
  a wb_ctx array, which gets initialized during wb_init.

- Writeback handling is modified to use "wb_ctx_list[0]" writeback
  context, so it continues to remain single threaded.

- In xfs, during AG initialization, we try to assign a different writeback
  context to each AG from the wb_ctx_list.

- During writeback, a "get_wb_ctx" handler is introduced in
  super_operations to fetch the writeback context from the inode, which
  gets used for writeback handling. Currently this handler is implemented
  only by xfs, so writeback handling is parallelized (per AG) for xfs
  only. It can be easily extended to other filesystems.

Discussion
==========================
- Design for API that would be used for deciding the number of writeback
  contexts by FS (suggested by Dave).

TBDs
==========================
- Need to explore cgroup related writeback handling.

- Add a list_lock field in struct wb_ctx for protecting pctx_b_* lists.
  Currently there is a "bdi_writeback->list_lock" for protecting per
  writeback context list.

- Ability to create writeback contexts dynamically. Currently, NR_WB_CTX
  number of writeback contexts are present in bdi_writeback and get
  initialized during wb_init.

Testing
==========================
- Encountered a softlockup issue while running fstests (test generic/051).
  Investigating the same.

[0] https://lore.kernel.org/all/20250129102627.161448-1-kundan.kumar@samsung.com/


Kundan Kumar (3):
  writeback: add parallel writeback infrastructure
  fs: modify writeback infra to work with wb_ctx
  xfs: use the parallel writeback infra per AG

 fs/fs-writeback.c                | 507 ++++++++++++++++++++-----------
 fs/xfs/libxfs/xfs_ag.c           |  16 +
 fs/xfs/libxfs/xfs_ag.h           |   1 +
 fs/xfs/xfs_super.c               |  20 ++
 include/linux/backing-dev-defs.h |  67 +++-
 include/linux/backing-dev.h      |  34 ++-
 include/linux/fs.h               |   1 +
 mm/backing-dev.c                 |  68 +++--
 mm/page-writeback.c              |  82 ++---
 9 files changed, 551 insertions(+), 245 deletions(-)


base-commit: 57d357a7663c4025c68ae07974b679d84e2d5d1a
-- 
2.25.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC 1/3] writeback: add parallel writeback infrastructure
  2025-02-12 10:36 ` [RFC 0/3] Parallelizing filesystem writeback Kundan Kumar
@ 2025-02-12 10:36   ` Kundan Kumar
  2025-02-12 10:36   ` [RFC 2/3] fs: modify writeback infra to work with wb_ctx Kundan Kumar
  2025-02-12 10:36   ` [RFC 3/3] xfs: use the parallel writeback infra per AG Kundan Kumar
  2 siblings, 0 replies; 7+ messages in thread
From: Kundan Kumar @ 2025-02-12 10:36 UTC (permalink / raw)
  To: david, mcgrof, jack, hch
  Cc: linux-fsdevel, linux-xfs, anuj20.g, axboe, clm, willy, gost.dev,
	vishak.g, amir73il, brauner, Kundan Kumar

This patch introduces infrastructure for parallel writeback.

- Writeback context list and index:
  wb_ctx_list: An array that represents the NR_WB_CTX writeback
  contexts.
  wb_idx: An index in wb_ctx_list used to manage the assignment of
  writeback contexts to file-systems.

- Inode lists:
  Each writeback context has its own separate inode lists
  corresponding to b_*.

  b_dirty -> pctx_b_dirty
  b_io -> pctx_b_io
  b_dirty_time -> pctx_b_dirty_time
  b_more_io -> pctx_b_more_io

- Per-writeback context work:
  pctx_dwork to handle multiple worker threads for per-writeback
  context operations concurrently.

- Helper functions:
  A set of helper functions, ctx_b_*_list(), are introduced to
  retrieve the list associated with a specific writeback context.

Signed-off-by: Kundan Kumar <kundan.kumar@samsung.com>
Signed-off-by: Anuj Gupta <anuj20.g@samsung.com>
---
 include/linux/backing-dev-defs.h | 61 ++++++++++++++++++++++++++++++++
 mm/backing-dev.c                 | 21 ++++++++++-
 2 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/include/linux/backing-dev-defs.h b/include/linux/backing-dev-defs.h
index 2ad261082bba..df627783e879 100644
--- a/include/linux/backing-dev-defs.h
+++ b/include/linux/backing-dev-defs.h
@@ -37,6 +37,7 @@ enum wb_stat_item {
 };
 
 #define WB_STAT_BATCH (8*(1+ilog2(nr_cpu_ids)))
+#define NR_WB_CTX 8
 
 /*
  * why some writeback work was initiated
@@ -80,6 +81,31 @@ struct wb_completion {
 #define DEFINE_WB_COMPLETION(cmpl, bdi)	\
 	struct wb_completion cmpl = WB_COMPLETION_INIT(bdi)
 
+struct wb_ctx {
+	struct delayed_work pctx_dwork;
+	struct list_head pctx_b_dirty;
+	struct list_head pctx_b_io;
+	struct list_head pctx_b_more_io;
+	struct list_head pctx_b_dirty_time;
+	struct bdi_writeback *b_wb;
+	unsigned long last_old_flush;	/* last old data flush */
+	unsigned long state;
+	unsigned long bw_time_stamp;	/* last time write bw is updated */
+	unsigned long dirtied_stamp;
+	unsigned long written_stamp;	/* pages written at bw_time_stamp */
+	unsigned long write_bandwidth;	/* the estimated write bandwidth */
+	unsigned long avg_write_bandwidth; /* further smoothed write bw, > 0 */
+
+	/*
+	 * The base dirty throttle rate, re-calculated on every 200ms.
+	 * All the bdi tasks' dirty rate will be curbed under it.
+	 * @dirty_ratelimit tracks the estimated @balanced_dirty_ratelimit
+	 * in small steps and is much more smooth/stable than the latter.
+	 */
+	unsigned long dirty_ratelimit;
+	unsigned long balanced_dirty_ratelimit;
+};
+
 /*
  * Each wb (bdi_writeback) can perform writeback operations, is measured
  * and throttled, independently.  Without cgroup writeback, each bdi
@@ -143,6 +169,8 @@ struct bdi_writeback {
 
 	struct list_head bdi_node;	/* anchored at bdi->wb_list */
 
+	int wb_idx;
+	struct wb_ctx wb_ctx_list[NR_WB_CTX];
 #ifdef CONFIG_CGROUP_WRITEBACK
 	struct percpu_ref refcnt;	/* used only for !root wb's */
 	struct fprop_local_percpu memcg_completions;
@@ -208,6 +236,39 @@ struct wb_lock_cookie {
 	unsigned long flags;
 };
 
+static struct wb_ctx *ctx_wb_struct(struct bdi_writeback *wb, int ctx_id)
+{
+	return &wb->wb_ctx_list[ctx_id];
+}
+
+static inline struct list_head *ctx_b_dirty_list(struct bdi_writeback *wb, int ctx_id)
+{
+	struct wb_ctx *p_wb = ctx_wb_struct(wb, ctx_id);
+
+	return &p_wb->pctx_b_dirty;
+}
+
+static inline struct list_head *ctx_b_dirty_time_list(struct bdi_writeback *wb, int ctx_id)
+{
+	struct wb_ctx *p_wb = ctx_wb_struct(wb, ctx_id);
+
+	return &p_wb->pctx_b_dirty_time;
+}
+
+static inline struct list_head *ctx_b_io_list(struct bdi_writeback *wb, int ctx_id)
+{
+	struct wb_ctx *p_wb = ctx_wb_struct(wb, ctx_id);
+
+	return &p_wb->pctx_b_io;
+}
+
+static inline struct list_head *ctx_b_more_io_list(struct bdi_writeback *wb, int ctx_id)
+{
+	struct wb_ctx *p_wb = ctx_wb_struct(wb, ctx_id);
+
+	return &p_wb->pctx_b_more_io;
+}
+
 #ifdef CONFIG_CGROUP_WRITEBACK
 
 /**
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index e61bbb1bd622..fc072e9fe42c 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -515,7 +515,8 @@ static void wb_update_bandwidth_workfn(struct work_struct *work)
 static int wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi,
 		   gfp_t gfp)
 {
-	int err;
+	int i, err;
+	struct wb_ctx *p_wb_ctx;
 
 	memset(wb, 0, sizeof(*wb));
 
@@ -533,12 +534,30 @@ static int wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi,
 	wb->dirty_ratelimit = INIT_BW;
 	wb->write_bandwidth = INIT_BW;
 	wb->avg_write_bandwidth = INIT_BW;
+	wb->wb_idx = 0;
 
 	spin_lock_init(&wb->work_lock);
 	INIT_LIST_HEAD(&wb->work_list);
 	INIT_DELAYED_WORK(&wb->dwork, wb_workfn);
 	INIT_DELAYED_WORK(&wb->bw_dwork, wb_update_bandwidth_workfn);
 
+	for (i = 0; i < NR_WB_CTX; i++) {
+		p_wb_ctx = &wb->wb_ctx_list[i];
+		p_wb_ctx->b_wb = wb;
+		p_wb_ctx->last_old_flush = jiffies;
+		p_wb_ctx->bw_time_stamp = jiffies;
+		p_wb_ctx->balanced_dirty_ratelimit = INIT_BW;
+		p_wb_ctx->dirty_ratelimit = INIT_BW;
+		p_wb_ctx->write_bandwidth = INIT_BW;
+		p_wb_ctx->avg_write_bandwidth = INIT_BW;
+
+		INIT_LIST_HEAD(ctx_b_dirty_list(wb, i));
+		INIT_LIST_HEAD(ctx_b_dirty_time_list(wb, i));
+		INIT_LIST_HEAD(ctx_b_io_list(wb, i));
+		INIT_LIST_HEAD(ctx_b_more_io_list(wb, i));
+
+		INIT_DELAYED_WORK(&p_wb_ctx->pctx_dwork, wb_workfn);
+	}
 	err = fprop_local_init_percpu(&wb->completions, gfp);
 	if (err)
 		return err;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC 2/3] fs: modify writeback infra to work with wb_ctx
  2025-02-12 10:36 ` [RFC 0/3] Parallelizing filesystem writeback Kundan Kumar
  2025-02-12 10:36   ` [RFC 1/3] writeback: add parallel writeback infrastructure Kundan Kumar
@ 2025-02-12 10:36   ` Kundan Kumar
  2025-02-13  5:20     ` kernel test robot
                       ` (2 more replies)
  2025-02-12 10:36   ` [RFC 3/3] xfs: use the parallel writeback infra per AG Kundan Kumar
  2 siblings, 3 replies; 7+ messages in thread
From: Kundan Kumar @ 2025-02-12 10:36 UTC (permalink / raw)
  To: david, mcgrof, jack, hch
  Cc: linux-fsdevel, linux-xfs, anuj20.g, axboe, clm, willy, gost.dev,
	vishak.g, amir73il, brauner, Kundan Kumar

The current writeback handling uses bdi_writeback structure
(single worker thread). Modify it so that it starts using the wb_ctx
structure (parallel writeback thread). The writeback always gets
scheduled on the first wb_ctx.

Changes introduced in this patch:

- __mark_inode_dirty:
  This function is modified to add an inode to a particular writeback
  context (wb_ctx) and a corresponding work (pctx_dwork) is scheduled.

- wakeup functions:
  wb_wakeup now wakes up all contexts.
  wb_ctx_wakeup wakes up the specific wb_ctx work.
  wb_wakeup_delayed has been changed to wb_ctx_wakeup_delayed.

- When the writeback work starts, it fetches the inode from the
  b_dirty list and begins processing it. A sample base writeback
  flow is as follows:

  wb_workfn()
    wb_do_writeback()
      wb_writeback()
        queue_io()
          move_expired_inodes()
        writeback_sb_inodes()
        __writeback_single_inode()
        do_writepages()
          xfs_vm_writepages
            iomap_writepages()
              xfs_map_blocks()
            iomap_submit_ioend()
              submit_bio()

  These functions have been modified to accept a writeback context
  (wb_ctx) parameter, allowing them to work on inode (b*) lists in a
  particular writeback context.

- __wakeup_flusher_threads_bdi is modified to start flusher threads for
  each wb_ctx.

- wakeup_dirtytime_writeback is modifed to wake up threads for each wb_ctx

- These writeback flags are made per-context (wb_ctx) specific:
  WB_has_dirty_io
  WB_writeback_running
  WB_start_all

- write bandwidth and average write bandwidth are calculated per writeback
  context.

- FS sync call eventually reaches bdi_split_work_to_wbs, which is modified
  to start threads for all writeback contexts.

Signed-off-by: Kundan Kumar <kundan.kumar@samsung.com>
---
 fs/fs-writeback.c                | 489 ++++++++++++++++++++-----------
 include/linux/backing-dev-defs.h |   6 +-
 include/linux/backing-dev.h      |  34 ++-
 mm/backing-dev.c                 |  49 ++--
 mm/page-writeback.c              |  82 +++---
 5 files changed, 419 insertions(+), 241 deletions(-)

diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index 3cd99e2dc6ac..8f7dd5d10085 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -82,31 +82,39 @@ static inline struct inode *wb_inode(struct list_head *head)
 
 EXPORT_TRACEPOINT_SYMBOL_GPL(wbc_writepage);
 
-static bool wb_io_lists_populated(struct bdi_writeback *wb)
+static bool wb_ctx_io_lists_populated(struct bdi_writeback *wb,
+				      struct wb_ctx *p_wb_ctx)
 {
-	if (wb_has_dirty_io(wb)) {
+	if (wb_ctx_has_dirty_io(p_wb_ctx)) {
 		return false;
 	} else {
-		set_bit(WB_has_dirty_io, &wb->state);
-		WARN_ON_ONCE(!wb->avg_write_bandwidth);
-		atomic_long_add(wb->avg_write_bandwidth,
+		set_bit(WB_has_dirty_io, &p_wb_ctx->state);
+		WARN_ON_ONCE(!p_wb_ctx->avg_write_bandwidth);
+		atomic_long_add(p_wb_ctx->avg_write_bandwidth,
 				&wb->bdi->tot_write_bandwidth);
 		return true;
 	}
 }
 
-static void wb_io_lists_depopulated(struct bdi_writeback *wb)
+static void wb_ctx_io_lists_depopulated(struct bdi_writeback *wb,
+					struct wb_ctx *p_wb_ctx)
 {
-	if (wb_has_dirty_io(wb) && list_empty(&wb->b_dirty) &&
-	    list_empty(&wb->b_io) && list_empty(&wb->b_more_io)) {
-		clear_bit(WB_has_dirty_io, &wb->state);
-		WARN_ON_ONCE(atomic_long_sub_return(wb->avg_write_bandwidth,
+	struct list_head *pctx_b_more_io, *pctx_b_io, *pctx_b_dirty;
+
+	pctx_b_io = &p_wb_ctx->pctx_b_io;
+	pctx_b_more_io = &p_wb_ctx->pctx_b_more_io;
+	pctx_b_dirty = &p_wb_ctx->pctx_b_dirty;
+
+	if (wb_ctx_has_dirty_io(p_wb_ctx) && list_empty(pctx_b_dirty) &&
+	    list_empty(pctx_b_io) && list_empty(pctx_b_more_io)) {
+		clear_bit(WB_has_dirty_io, &p_wb_ctx->state);
+		WARN_ON_ONCE(atomic_long_sub_return(p_wb_ctx->avg_write_bandwidth,
 					&wb->bdi->tot_write_bandwidth) < 0);
 	}
 }
 
 /**
- * inode_io_list_move_locked - move an inode onto a bdi_writeback IO list
+ * inode_io_list_move_locked_ctx - move an inode onto a wb_ctx IO list
  * @inode: inode to be moved
  * @wb: target bdi_writeback
  * @head: one of @wb->b_{dirty|io|more_io|dirty_time}
@@ -115,10 +123,14 @@ static void wb_io_lists_depopulated(struct bdi_writeback *wb)
  * Returns %true if @inode is the first occupant of the !dirty_time IO
  * lists; otherwise, %false.
  */
-static bool inode_io_list_move_locked(struct inode *inode,
-				      struct bdi_writeback *wb,
-				      struct list_head *head)
+static bool inode_io_list_move_locked_ctx(struct inode *inode,
+					  struct bdi_writeback *wb,
+					  struct list_head *head,
+					  struct wb_ctx *p_wb_ctx)
 {
+	struct list_head *pctx_b_dirty_time = &p_wb_ctx->pctx_b_dirty_time;
+
+
 	assert_spin_locked(&wb->list_lock);
 	assert_spin_locked(&inode->i_lock);
 	WARN_ON_ONCE(inode->i_state & I_FREEING);
@@ -126,18 +138,31 @@ static bool inode_io_list_move_locked(struct inode *inode,
 	list_move(&inode->i_io_list, head);
 
 	/* dirty_time doesn't count as dirty_io until expiration */
-	if (head != &wb->b_dirty_time)
-		return wb_io_lists_populated(wb);
+	if (head != pctx_b_dirty_time)
+		return wb_ctx_io_lists_populated(wb, p_wb_ctx);
 
-	wb_io_lists_depopulated(wb);
+	wb_ctx_io_lists_depopulated(wb, p_wb_ctx);
 	return false;
 }
 
 static void wb_wakeup(struct bdi_writeback *wb)
+{
+	for (int i = 0; i < NR_WB_CTX; i++) {
+		struct wb_ctx *p_wb_ctx = ctx_wb_struct(wb, i);
+
+		spin_lock_irq(&wb->work_lock);
+		if (test_bit(WB_registered, &wb->state))
+			mod_delayed_work(bdi_wq, &p_wb_ctx->pctx_dwork, 0);
+		spin_unlock_irq(&wb->work_lock);
+	}
+}
+
+static void wb_ctx_wakeup(struct bdi_writeback *wb,
+			  struct wb_ctx *p_wb_ctx)
 {
 	spin_lock_irq(&wb->work_lock);
 	if (test_bit(WB_registered, &wb->state))
-		mod_delayed_work(bdi_wq, &wb->dwork, 0);
+		mod_delayed_work(bdi_wq, &p_wb_ctx->pctx_dwork, 0);
 	spin_unlock_irq(&wb->work_lock);
 }
 
@@ -155,14 +180,15 @@ static void wb_wakeup(struct bdi_writeback *wb)
  * We have to be careful not to postpone flush work if it is scheduled for
  * earlier. Thus we use queue_delayed_work().
  */
-static void wb_wakeup_delayed(struct bdi_writeback *wb)
+static void wb_ctx_wakeup_delayed(struct bdi_writeback *wb,
+				  struct wb_ctx *p_wb_ctx)
 {
 	unsigned long timeout;
 
 	timeout = msecs_to_jiffies(dirty_writeback_interval * 10);
 	spin_lock_irq(&wb->work_lock);
 	if (test_bit(WB_registered, &wb->state))
-		queue_delayed_work(bdi_wq, &wb->dwork, timeout);
+		queue_delayed_work(bdi_wq, &p_wb_ctx->pctx_dwork, timeout);
 	spin_unlock_irq(&wb->work_lock);
 }
 
@@ -181,9 +207,12 @@ static void finish_writeback_work(struct wb_writeback_work *work)
 	}
 }
 
-static void wb_queue_work(struct bdi_writeback *wb,
-			  struct wb_writeback_work *work)
+static void wb_ctx_queue_work(struct bdi_writeback *wb,
+			      struct wb_writeback_work *work,
+			      int ctx_id)
 {
+	struct wb_ctx *p_wb_ctx = ctx_wb_struct(wb, ctx_id);
+
 	trace_writeback_queue(wb, work);
 
 	if (work->done)
@@ -193,7 +222,7 @@ static void wb_queue_work(struct bdi_writeback *wb,
 
 	if (test_bit(WB_registered, &wb->state)) {
 		list_add_tail(&work->list, &wb->work_list);
-		mod_delayed_work(bdi_wq, &wb->dwork, 0);
+		mod_delayed_work(bdi_wq, &p_wb_ctx->pctx_dwork, 0);
 	} else
 		finish_writeback_work(work);
 
@@ -299,6 +328,22 @@ void __inode_attach_wb(struct inode *inode, struct folio *folio)
  * Remove the inode from wb's io lists and if necessarily put onto b_attached
  * list.  Only inodes attached to cgwb's are kept on this list.
  */
+static void inode_cgwb_move_to_attached_ctx(struct inode *inode,
+					    struct bdi_writeback *wb,
+					    struct wb_ctx *p_wb_ctx)
+{
+	assert_spin_locked(&wb->list_lock);
+	assert_spin_locked(&inode->i_lock);
+	WARN_ON_ONCE(inode->i_state & I_FREEING);
+
+	inode->i_state &= ~I_SYNC_QUEUED;
+	if (wb != &wb->bdi->wb)
+		list_move(&inode->i_io_list, &wb->b_attached);
+	else
+		list_del_init(&inode->i_io_list);
+	wb_ctx_io_lists_depopulated(wb, p_wb_ctx);
+}
+
 static void inode_cgwb_move_to_attached(struct inode *inode,
 					struct bdi_writeback *wb)
 {
@@ -311,7 +356,12 @@ static void inode_cgwb_move_to_attached(struct inode *inode,
 		list_move(&inode->i_io_list, &wb->b_attached);
 	else
 		list_del_init(&inode->i_io_list);
-	wb_io_lists_depopulated(wb);
+
+	for (int i = 0; i < NR_WB_CTX; i++) {
+		struct wb_ctx *p_wb_ctx = ctx_wb_struct(wb, i);
+
+		wb_ctx_io_lists_depopulated(wb, p_wb_ctx);
+	}
 }
 
 /**
@@ -454,13 +504,21 @@ static bool inode_do_switch_wbs(struct inode *inode,
 
 		if (inode->i_state & I_DIRTY_ALL) {
 			struct inode *pos;
-
-			list_for_each_entry(pos, &new_wb->b_dirty, i_io_list)
-				if (time_after_eq(inode->dirtied_when,
-						  pos->dirtied_when))
-					break;
-			inode_io_list_move_locked(inode, new_wb,
-						  pos->i_io_list.prev);
+			for (int i = 0; i < NR_WB_CTX; i++) {
+				struct wb_ctx *p_wb_ctx = ctx_wb_struct(new_wb,
+									i);
+				struct list_head *pctx_b_dirty =
+						ctx_b_dirty_list(new_wb, i);
+
+				list_for_each_entry(pos, pctx_b_dirty,
+						    i_io_list)
+					if (time_after_eq(inode->dirtied_when,
+							  pos->dirtied_when))
+						break;
+				inode_io_list_move_locked_ctx(inode, new_wb,
+							      pos->i_io_list.prev,
+							      p_wb_ctx);
+			}
 		} else {
 			inode_cgwb_move_to_attached(inode, new_wb);
 		}
@@ -696,8 +754,11 @@ bool cleanup_offline_cgwb(struct bdi_writeback *wb)
 	 * accounted for.
 	 */
 	restart = isw_prepare_wbs_switch(isw, &wb->b_attached, &nr);
-	if (!restart)
-		restart = isw_prepare_wbs_switch(isw, &wb->b_dirty_time, &nr);
+	/*
+	 * TODO : Modify it to work with parallel writeback
+	 * if (!restart)
+	 * restart = isw_prepare_wbs_switch(isw, &wb->b_dirty_time, &nr);
+	 */
 	spin_unlock(&wb->list_lock);
 
 	/* no attached inodes? bail out */
@@ -1010,53 +1071,59 @@ static void bdi_split_work_to_wbs(struct backing_dev_info *bdi,
 		struct wb_writeback_work *work;
 		long nr_pages;
 
-		if (last_wb) {
-			wb_put(last_wb);
-			last_wb = NULL;
-		}
+		for (int i = 0; i < NR_WB_CTX; i++) {
+			struct wb_ctx *p_wb_ctx = ctx_wb_struct(wb, i);
+			struct list_head *pctx_b_dirty_time =
+						ctx_b_dirty_time_list(wb, i);
 
-		/* SYNC_ALL writes out I_DIRTY_TIME too */
-		if (!wb_has_dirty_io(wb) &&
-		    (base_work->sync_mode == WB_SYNC_NONE ||
-		     list_empty(&wb->b_dirty_time)))
-			continue;
-		if (skip_if_busy && writeback_in_progress(wb))
-			continue;
+			if (last_wb) {
+				wb_put(last_wb);
+				last_wb = NULL;
+			}
 
-		nr_pages = wb_split_bdi_pages(wb, base_work->nr_pages);
+			/* SYNC_ALL writes out I_DIRTY_TIME too */
+			if (!wb_ctx_has_dirty_io(p_wb_ctx) &&
+			    (base_work->sync_mode == WB_SYNC_NONE ||
+			     list_empty(pctx_b_dirty_time)))
+				continue;
+			if (skip_if_busy && writeback_ctx_in_progress(p_wb_ctx))
+				continue;
 
-		work = kmalloc(sizeof(*work), GFP_ATOMIC);
-		if (work) {
-			*work = *base_work;
-			work->nr_pages = nr_pages;
-			work->auto_free = 1;
-			wb_queue_work(wb, work);
-			continue;
-		}
+			nr_pages = wb_split_bdi_pages(wb, base_work->nr_pages);
 
-		/*
-		 * If wb_tryget fails, the wb has been shutdown, skip it.
-		 *
-		 * Pin @wb so that it stays on @bdi->wb_list.  This allows
-		 * continuing iteration from @wb after dropping and
-		 * regrabbing rcu read lock.
-		 */
-		if (!wb_tryget(wb))
-			continue;
+			work = kmalloc(sizeof(*work), GFP_ATOMIC);
+			if (work) {
+				*work = *base_work;
+				work->nr_pages = nr_pages;
+				work->auto_free = 1;
+				wb_ctx_queue_work(wb, work, i);
+				continue;
+			}
 
-		/* alloc failed, execute synchronously using on-stack fallback */
-		work = &fallback_work;
-		*work = *base_work;
-		work->nr_pages = nr_pages;
-		work->auto_free = 0;
-		work->done = &fallback_work_done;
+			/*
+			 * If wb_tryget fails, the wb has been shutdown, skip it.
+			 *
+			 * Pin @wb so that it stays on @bdi->wb_list.  This allows
+			 * continuing iteration from @wb after dropping and
+			 * regrabbing rcu read lock.
+			 */
+			if (!wb_tryget(wb))
+				continue;
 
-		wb_queue_work(wb, work);
-		last_wb = wb;
+			/* alloc failed, execute synchronously using on-stack fallback */
+			work = &fallback_work;
+			*work = *base_work;
+			work->nr_pages = nr_pages;
+			work->auto_free = 0;
+			work->done = &fallback_work_done;
 
-		rcu_read_unlock();
-		wb_wait_for_completion(&fallback_work_done);
-		goto restart;
+			wb_ctx_queue_work(wb, work, i);
+			last_wb = wb;
+
+			rcu_read_unlock();
+			wb_wait_for_completion(&fallback_work_done);
+			goto restart;
+		}
 	}
 	rcu_read_unlock();
 
@@ -1131,7 +1198,8 @@ int cgroup_writeback_by_id(u64 bdi_id, int memcg_id,
 		work->reason = reason;
 		work->done = done;
 		work->auto_free = 1;
-		wb_queue_work(wb, work);
+		/* Schedule on first wb_ctx */
+		wb_ctx_queue_work(wb, work, 0);
 		ret = 0;
 	} else {
 		ret = -ENOMEM;
@@ -1261,9 +1329,11 @@ static unsigned long get_nr_dirty_pages(void)
 		get_nr_dirty_inodes();
 }
 
-static void wb_start_writeback(struct bdi_writeback *wb, enum wb_reason reason)
+static void wb_ctx_start_writeback(struct bdi_writeback *wb,
+				   enum wb_reason reason,
+				   struct wb_ctx *p_wb_ctx)
 {
-	if (!wb_has_dirty_io(wb))
+	if (!wb_ctx_has_dirty_io(p_wb_ctx))
 		return;
 
 	/*
@@ -1274,12 +1344,12 @@ static void wb_start_writeback(struct bdi_writeback *wb, enum wb_reason reason)
 	 * that work. Ensure that we only allow one of them pending and
 	 * inflight at the time.
 	 */
-	if (test_bit(WB_start_all, &wb->state) ||
-	    test_and_set_bit(WB_start_all, &wb->state))
+	if (test_bit(WB_start_all, &p_wb_ctx->state) ||
+	    test_and_set_bit(WB_start_all, &p_wb_ctx->state))
 		return;
 
 	wb->start_all_reason = reason;
-	wb_wakeup(wb);
+	wb_ctx_wakeup(wb, p_wb_ctx);
 }
 
 /**
@@ -1308,13 +1378,19 @@ void wb_start_background_writeback(struct bdi_writeback *wb)
 void inode_io_list_del(struct inode *inode)
 {
 	struct bdi_writeback *wb;
+	int i;
 
 	wb = inode_to_wb_and_lock_list(inode);
 	spin_lock(&inode->i_lock);
 
 	inode->i_state &= ~I_SYNC_QUEUED;
 	list_del_init(&inode->i_io_list);
-	wb_io_lists_depopulated(wb);
+
+	for (i = 0; i < NR_WB_CTX; i++) {
+		struct wb_ctx *p_wb_ctx = ctx_wb_struct(wb, i);
+
+		wb_ctx_io_lists_depopulated(wb, p_wb_ctx);
+	}
 
 	spin_unlock(&inode->i_lock);
 	spin_unlock(&wb->list_lock);
@@ -1366,8 +1442,12 @@ void sb_clear_inode_writeback(struct inode *inode)
  * the case then the inode must have been redirtied while it was being written
  * out and we don't reset its dirtied_when.
  */
-static void redirty_tail_locked(struct inode *inode, struct bdi_writeback *wb)
+static void redirty_tail_locked_ctx(struct inode *inode,
+				    struct bdi_writeback *wb,
+				    struct wb_ctx *p_wb_ctx)
 {
+	struct list_head *pctx_b_dirty = &p_wb_ctx->pctx_b_dirty;
+
 	assert_spin_locked(&inode->i_lock);
 
 	inode->i_state &= ~I_SYNC_QUEUED;
@@ -1378,32 +1458,36 @@ static void redirty_tail_locked(struct inode *inode, struct bdi_writeback *wb)
 	 */
 	if (inode->i_state & I_FREEING) {
 		list_del_init(&inode->i_io_list);
-		wb_io_lists_depopulated(wb);
+		wb_ctx_io_lists_depopulated(wb, p_wb_ctx);
 		return;
 	}
-	if (!list_empty(&wb->b_dirty)) {
+	if (!list_empty(pctx_b_dirty)) {
 		struct inode *tail;
 
-		tail = wb_inode(wb->b_dirty.next);
+		tail = wb_inode(pctx_b_dirty->next);
 		if (time_before(inode->dirtied_when, tail->dirtied_when))
 			inode->dirtied_when = jiffies;
 	}
-	inode_io_list_move_locked(inode, wb, &wb->b_dirty);
+	inode_io_list_move_locked_ctx(inode, wb, pctx_b_dirty, p_wb_ctx);
 }
 
-static void redirty_tail(struct inode *inode, struct bdi_writeback *wb)
+static void redirty_tail_ctx(struct inode *inode, struct bdi_writeback *wb,
+			     struct wb_ctx *p_wb_ctx)
 {
 	spin_lock(&inode->i_lock);
-	redirty_tail_locked(inode, wb);
+	redirty_tail_locked_ctx(inode, wb, p_wb_ctx);
 	spin_unlock(&inode->i_lock);
 }
 
 /*
  * requeue inode for re-scanning after bdi->b_io list is exhausted.
  */
-static void requeue_io(struct inode *inode, struct bdi_writeback *wb)
+static void requeue_io_ctx(struct inode *inode, struct bdi_writeback *wb,
+			   struct wb_ctx *p_wb_ctx)
 {
-	inode_io_list_move_locked(inode, wb, &wb->b_more_io);
+	struct list_head *pctx_b_more_io = &p_wb_ctx->pctx_b_more_io;
+
+	inode_io_list_move_locked_ctx(inode, wb, pctx_b_more_io, p_wb_ctx);
 }
 
 static void inode_sync_complete(struct inode *inode)
@@ -1498,21 +1582,30 @@ static int move_expired_inodes(struct list_head *delaying_queue,
  *                                           |
  *                                           +--> dequeue for IO
  */
-static void queue_io(struct bdi_writeback *wb, struct wb_writeback_work *work,
-		     unsigned long dirtied_before)
+static void queue_io_ctx(struct bdi_writeback *wb,
+			 struct wb_writeback_work *work,
+			 unsigned long dirtied_before,
+			 struct wb_ctx *p_wb_ctx)
 {
 	int moved;
 	unsigned long time_expire_jif = dirtied_before;
+	struct list_head *pctx_b_more_io, *pctx_b_io, *pctx_b_dirty,
+			 *pctx_b_dirty_time;
+
+	pctx_b_io = &p_wb_ctx->pctx_b_io;
+	pctx_b_more_io = &p_wb_ctx->pctx_b_more_io;
+	pctx_b_dirty = &p_wb_ctx->pctx_b_dirty;
+	pctx_b_dirty_time = &p_wb_ctx->pctx_b_dirty_time;
 
 	assert_spin_locked(&wb->list_lock);
-	list_splice_init(&wb->b_more_io, &wb->b_io);
-	moved = move_expired_inodes(&wb->b_dirty, &wb->b_io, dirtied_before);
+	list_splice_init(pctx_b_more_io, pctx_b_io);
+	moved = move_expired_inodes(pctx_b_dirty, pctx_b_io, dirtied_before);
 	if (!work->for_sync)
 		time_expire_jif = jiffies - dirtytime_expire_interval * HZ;
-	moved += move_expired_inodes(&wb->b_dirty_time, &wb->b_io,
+	moved += move_expired_inodes(pctx_b_dirty_time, pctx_b_io,
 				     time_expire_jif);
 	if (moved)
-		wb_io_lists_populated(wb);
+		wb_ctx_io_lists_populated(wb, p_wb_ctx);
 	trace_writeback_queue_io(wb, work, dirtied_before, moved);
 }
 
@@ -1588,10 +1681,12 @@ static void inode_sleep_on_writeback(struct inode *inode)
  * processes all inodes in writeback lists and requeueing inodes behind flusher
  * thread's back can have unexpected consequences.
  */
-static void requeue_inode(struct inode *inode, struct bdi_writeback *wb,
-			  struct writeback_control *wbc,
-			  unsigned long dirtied_before)
+static void requeue_inode_ctx(struct inode *inode, struct bdi_writeback *wb,
+			      struct writeback_control *wbc,
+			      unsigned long dirtied_before,
+			      struct wb_ctx *p_wb_ctx)
 {
+	struct list_head *pctx_b_dirty_time = &p_wb_ctx->pctx_b_dirty_time;
 	if (inode->i_state & I_FREEING)
 		return;
 
@@ -1612,9 +1707,9 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb,
 		 * filesystems so handle that gracefully.
 		 */
 		if (inode->i_state & I_DIRTY_ALL)
-			redirty_tail_locked(inode, wb);
+			redirty_tail_locked_ctx(inode, wb, p_wb_ctx);
 		else
-			inode_cgwb_move_to_attached(inode, wb);
+			inode_cgwb_move_to_attached_ctx(inode, wb, p_wb_ctx);
 		return;
 	}
 
@@ -1626,7 +1721,7 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb,
 		if (wbc->nr_to_write <= 0 &&
 		    !inode_dirtied_after(inode, dirtied_before)) {
 			/* Slice used up. Queue for next turn. */
-			requeue_io(inode, wb);
+			requeue_io_ctx(inode, wb, p_wb_ctx);
 		} else {
 			/*
 			 * Writeback blocked by something other than
@@ -1635,7 +1730,7 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb,
 			 * retrying writeback of the dirty page/inode
 			 * that cannot be performed immediately.
 			 */
-			redirty_tail_locked(inode, wb);
+			redirty_tail_locked_ctx(inode, wb, p_wb_ctx);
 		}
 	} else if (inode->i_state & I_DIRTY) {
 		/*
@@ -1643,14 +1738,15 @@ static void requeue_inode(struct inode *inode, struct bdi_writeback *wb,
 		 * such as delayed allocation during submission or metadata
 		 * updates after data IO completion.
 		 */
-		redirty_tail_locked(inode, wb);
+		redirty_tail_locked_ctx(inode, wb, p_wb_ctx);
 	} else if (inode->i_state & I_DIRTY_TIME) {
 		inode->dirtied_when = jiffies;
-		inode_io_list_move_locked(inode, wb, &wb->b_dirty_time);
+		inode_io_list_move_locked_ctx(inode, wb, pctx_b_dirty_time,
+					      p_wb_ctx);
 		inode->i_state &= ~I_SYNC_QUEUED;
 	} else {
 		/* The inode is clean. Remove from writeback lists. */
-		inode_cgwb_move_to_attached(inode, wb);
+		inode_cgwb_move_to_attached_ctx(inode, wb, p_wb_ctx);
 	}
 }
 
@@ -1817,13 +1913,19 @@ static int writeback_single_inode(struct inode *inode,
 		if (!(inode->i_state & I_DIRTY_ALL))
 			inode_cgwb_move_to_attached(inode, wb);
 		else if (!(inode->i_state & I_SYNC_QUEUED)) {
+			/* Move to first wb_ctx */
+			struct wb_ctx *p_wb_ctx = ctx_wb_struct(wb, 0);
+			struct list_head *pctx_b_dirty_time =
+					ctx_b_dirty_time_list(wb, 0);
+
 			if ((inode->i_state & I_DIRTY))
-				redirty_tail_locked(inode, wb);
+				redirty_tail_locked_ctx(inode, wb, p_wb_ctx);
 			else if (inode->i_state & I_DIRTY_TIME) {
 				inode->dirtied_when = jiffies;
-				inode_io_list_move_locked(inode,
-							  wb,
-							  &wb->b_dirty_time);
+				inode_io_list_move_locked_ctx(inode,
+							      wb,
+							      pctx_b_dirty_time,
+							      p_wb_ctx);
 			}
 		}
 	}
@@ -1836,7 +1938,8 @@ static int writeback_single_inode(struct inode *inode,
 }
 
 static long writeback_chunk_size(struct bdi_writeback *wb,
-				 struct wb_writeback_work *work)
+				 struct wb_writeback_work *work,
+				 struct wb_ctx *p_wb_ctx)
 {
 	long pages;
 
@@ -1856,7 +1959,7 @@ static long writeback_chunk_size(struct bdi_writeback *wb,
 	if (work->sync_mode == WB_SYNC_ALL || work->tagged_writepages)
 		pages = LONG_MAX;
 	else {
-		pages = min(wb->avg_write_bandwidth / 2,
+		pages = min(p_wb_ctx->avg_write_bandwidth / 2,
 			    global_wb_domain.dirty_limit / DIRTY_SCOPE);
 		pages = min(pages, work->nr_pages);
 		pages = round_down(pages + MIN_WRITEBACK_PAGES,
@@ -1875,9 +1978,10 @@ static long writeback_chunk_size(struct bdi_writeback *wb,
  * unlock and relock that for each inode it ends up doing
  * IO for.
  */
-static long writeback_sb_inodes(struct super_block *sb,
-				struct bdi_writeback *wb,
-				struct wb_writeback_work *work)
+static long writeback_sb_inodes_ctx(struct super_block *sb,
+				    struct bdi_writeback *wb,
+				    struct wb_writeback_work *work,
+				    struct wb_ctx *p_wb_ctx)
 {
 	struct writeback_control wbc = {
 		.sync_mode		= work->sync_mode,
@@ -1893,13 +1997,14 @@ static long writeback_sb_inodes(struct super_block *sb,
 	long write_chunk;
 	long total_wrote = 0;  /* count both pages and inodes */
 	unsigned long dirtied_before = jiffies;
+	struct list_head *pctx_b_io = &p_wb_ctx->pctx_b_io;
 
 	if (work->for_kupdate)
 		dirtied_before = jiffies -
 			msecs_to_jiffies(dirty_expire_interval * 10);
 
-	while (!list_empty(&wb->b_io)) {
-		struct inode *inode = wb_inode(wb->b_io.prev);
+	while (!list_empty(pctx_b_io)) {
+		struct inode *inode = wb_inode(pctx_b_io->prev);
 		struct bdi_writeback *tmp_wb;
 		long wrote;
 
@@ -1910,7 +2015,7 @@ static long writeback_sb_inodes(struct super_block *sb,
 				 * superblock, move all inodes not belonging
 				 * to it back onto the dirty list.
 				 */
-				redirty_tail(inode, wb);
+				redirty_tail_ctx(inode, wb, p_wb_ctx);
 				continue;
 			}
 
@@ -1929,7 +2034,7 @@ static long writeback_sb_inodes(struct super_block *sb,
 		 */
 		spin_lock(&inode->i_lock);
 		if (inode->i_state & (I_NEW | I_FREEING | I_WILL_FREE)) {
-			redirty_tail_locked(inode, wb);
+			redirty_tail_locked_ctx(inode, wb, p_wb_ctx);
 			spin_unlock(&inode->i_lock);
 			continue;
 		}
@@ -1943,7 +2048,7 @@ static long writeback_sb_inodes(struct super_block *sb,
 			 * We'll have another go at writing back this inode
 			 * when we completed a full scan of b_io.
 			 */
-			requeue_io(inode, wb);
+			requeue_io_ctx(inode, wb, p_wb_ctx);
 			spin_unlock(&inode->i_lock);
 			trace_writeback_sb_inodes_requeue(inode);
 			continue;
@@ -1965,7 +2070,7 @@ static long writeback_sb_inodes(struct super_block *sb,
 		inode->i_state |= I_SYNC;
 		wbc_attach_and_unlock_inode(&wbc, inode);
 
-		write_chunk = writeback_chunk_size(wb, work);
+		write_chunk = writeback_chunk_size(wb, work, p_wb_ctx);
 		wbc.nr_to_write = write_chunk;
 		wbc.pages_skipped = 0;
 
@@ -2002,7 +2107,7 @@ static long writeback_sb_inodes(struct super_block *sb,
 		spin_lock(&inode->i_lock);
 		if (!(inode->i_state & I_DIRTY_ALL))
 			total_wrote++;
-		requeue_inode(inode, tmp_wb, &wbc, dirtied_before);
+		requeue_inode_ctx(inode, tmp_wb, &wbc, dirtied_before, p_wb_ctx);
 		inode_sync_complete(inode);
 		spin_unlock(&inode->i_lock);
 
@@ -2025,14 +2130,16 @@ static long writeback_sb_inodes(struct super_block *sb,
 	return total_wrote;
 }
 
-static long __writeback_inodes_wb(struct bdi_writeback *wb,
-				  struct wb_writeback_work *work)
+static long __writeback_inodes_wb_ctx(struct bdi_writeback *wb,
+				      struct wb_writeback_work *work,
+				      struct wb_ctx *p_wb_ctx)
 {
 	unsigned long start_time = jiffies;
 	long wrote = 0;
+	struct list_head *pctx_b_io = &p_wb_ctx->pctx_b_io;
 
-	while (!list_empty(&wb->b_io)) {
-		struct inode *inode = wb_inode(wb->b_io.prev);
+	while (!list_empty(pctx_b_io)) {
+		struct inode *inode = wb_inode(pctx_b_io->prev);
 		struct super_block *sb = inode->i_sb;
 
 		if (!super_trylock_shared(sb)) {
@@ -2041,10 +2148,10 @@ static long __writeback_inodes_wb(struct bdi_writeback *wb,
 			 * s_umount being grabbed by someone else. Don't use
 			 * requeue_io() to avoid busy retrying the inode/sb.
 			 */
-			redirty_tail(inode, wb);
+			redirty_tail_ctx(inode, wb, p_wb_ctx);
 			continue;
 		}
-		wrote += writeback_sb_inodes(sb, wb, work);
+		wrote += writeback_sb_inodes_ctx(sb, wb, work, p_wb_ctx);
 		up_read(&sb->s_umount);
 
 		/* refer to the same tests at the end of writeback_sb_inodes */
@@ -2059,8 +2166,8 @@ static long __writeback_inodes_wb(struct bdi_writeback *wb,
 	return wrote;
 }
 
-static long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages,
-				enum wb_reason reason)
+static long writeback_inodes_wb_ctx(struct bdi_writeback *wb, long nr_pages,
+				enum wb_reason reason, struct wb_ctx *p_wb_ctx)
 {
 	struct wb_writeback_work work = {
 		.nr_pages	= nr_pages,
@@ -2069,12 +2176,13 @@ static long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages,
 		.reason		= reason,
 	};
 	struct blk_plug plug;
+	struct list_head *pctx_b_io = &p_wb_ctx->pctx_b_io;
 
 	blk_start_plug(&plug);
 	spin_lock(&wb->list_lock);
-	if (list_empty(&wb->b_io))
-		queue_io(wb, &work, jiffies);
-	__writeback_inodes_wb(wb, &work);
+	if (list_empty(pctx_b_io))
+		queue_io_ctx(wb, &work, jiffies, p_wb_ctx);
+	__writeback_inodes_wb_ctx(wb, &work, p_wb_ctx);
 	spin_unlock(&wb->list_lock);
 	blk_finish_plug(&plug);
 
@@ -2096,8 +2204,9 @@ static long writeback_inodes_wb(struct bdi_writeback *wb, long nr_pages,
  * dirtied_before takes precedence over nr_to_write.  So we'll only write back
  * all dirty pages if they are all attached to "old" mappings.
  */
-static long wb_writeback(struct bdi_writeback *wb,
-			 struct wb_writeback_work *work)
+static long wb_ctx_writeback(struct bdi_writeback *wb,
+			     struct wb_writeback_work *work,
+			     struct wb_ctx *p_wb_ctx)
 {
 	long nr_pages = work->nr_pages;
 	unsigned long dirtied_before = jiffies;
@@ -2105,6 +2214,10 @@ static long wb_writeback(struct bdi_writeback *wb,
 	long progress;
 	struct blk_plug plug;
 	bool queued = false;
+	struct list_head *pctx_b_io, *pctx_b_more_io;
+
+	pctx_b_io = &p_wb_ctx->pctx_b_io;
+	pctx_b_more_io = &p_wb_ctx->pctx_b_more_io;
 
 	blk_start_plug(&plug);
 	for (;;) {
@@ -2135,7 +2248,7 @@ static long wb_writeback(struct bdi_writeback *wb,
 		spin_lock(&wb->list_lock);
 
 		trace_writeback_start(wb, work);
-		if (list_empty(&wb->b_io)) {
+		if (list_empty(pctx_b_io)) {
 			/*
 			 * Kupdate and background works are special and we want
 			 * to include all inodes that need writing. Livelock
@@ -2149,13 +2262,13 @@ static long wb_writeback(struct bdi_writeback *wb,
 			} else if (work->for_background)
 				dirtied_before = jiffies;
 
-			queue_io(wb, work, dirtied_before);
+			queue_io_ctx(wb, work, dirtied_before, p_wb_ctx);
 			queued = true;
 		}
 		if (work->sb)
-			progress = writeback_sb_inodes(work->sb, wb, work);
+			progress = writeback_sb_inodes_ctx(work->sb, wb, work, p_wb_ctx);
 		else
-			progress = __writeback_inodes_wb(wb, work);
+			progress = __writeback_inodes_wb_ctx(wb, work, p_wb_ctx);
 		trace_writeback_written(wb, work);
 
 		/*
@@ -2174,7 +2287,7 @@ static long wb_writeback(struct bdi_writeback *wb,
 		/*
 		 * No more inodes for IO, bail
 		 */
-		if (list_empty(&wb->b_more_io)) {
+		if (list_empty(pctx_b_more_io)) {
 			spin_unlock(&wb->list_lock);
 			break;
 		}
@@ -2185,7 +2298,7 @@ static long wb_writeback(struct bdi_writeback *wb,
 		 * we'll just busyloop.
 		 */
 		trace_writeback_wait(wb, work);
-		inode = wb_inode(wb->b_more_io.prev);
+		inode = wb_inode(pctx_b_more_io->prev);
 		spin_lock(&inode->i_lock);
 		spin_unlock(&wb->list_lock);
 		/* This function drops i_lock... */
@@ -2213,7 +2326,8 @@ static struct wb_writeback_work *get_next_work_item(struct bdi_writeback *wb)
 	return work;
 }
 
-static long wb_check_background_flush(struct bdi_writeback *wb)
+static long wb_ctx_check_background_flush(struct bdi_writeback *wb,
+					  struct wb_ctx *p_wb_ctx)
 {
 	if (wb_over_bg_thresh(wb)) {
 
@@ -2225,13 +2339,14 @@ static long wb_check_background_flush(struct bdi_writeback *wb)
 			.reason		= WB_REASON_BACKGROUND,
 		};
 
-		return wb_writeback(wb, &work);
+		return wb_ctx_writeback(wb, &work, p_wb_ctx);
 	}
 
 	return 0;
 }
 
-static long wb_check_old_data_flush(struct bdi_writeback *wb)
+static long wb_ctx_check_old_data_flush(struct bdi_writeback *wb,
+					struct wb_ctx *p_wb_ctx)
 {
 	unsigned long expired;
 	long nr_pages;
@@ -2242,12 +2357,12 @@ static long wb_check_old_data_flush(struct bdi_writeback *wb)
 	if (!dirty_writeback_interval)
 		return 0;
 
-	expired = wb->last_old_flush +
+	expired = p_wb_ctx->last_old_flush +
 			msecs_to_jiffies(dirty_writeback_interval * 10);
 	if (time_before(jiffies, expired))
 		return 0;
 
-	wb->last_old_flush = jiffies;
+	p_wb_ctx->last_old_flush = jiffies;
 	nr_pages = get_nr_dirty_pages();
 
 	if (nr_pages) {
@@ -2259,17 +2374,18 @@ static long wb_check_old_data_flush(struct bdi_writeback *wb)
 			.reason		= WB_REASON_PERIODIC,
 		};
 
-		return wb_writeback(wb, &work);
+		return wb_ctx_writeback(wb, &work, p_wb_ctx);
 	}
 
 	return 0;
 }
 
-static long wb_check_start_all(struct bdi_writeback *wb)
+static long wb_ctx_check_start_all(struct bdi_writeback *wb,
+				   struct wb_ctx *p_wb_ctx)
 {
 	long nr_pages;
 
-	if (!test_bit(WB_start_all, &wb->state))
+	if (!test_bit(WB_start_all, &p_wb_ctx->state))
 		return 0;
 
 	nr_pages = get_nr_dirty_pages();
@@ -2281,40 +2397,40 @@ static long wb_check_start_all(struct bdi_writeback *wb)
 			.reason		= wb->start_all_reason,
 		};
 
-		nr_pages = wb_writeback(wb, &work);
+		nr_pages = wb_ctx_writeback(wb, &work, p_wb_ctx);
 	}
 
-	clear_bit(WB_start_all, &wb->state);
+	clear_bit(WB_start_all, &p_wb_ctx->state);
 	return nr_pages;
 }
 
-
 /*
  * Retrieve work items and do the writeback they describe
  */
-static long wb_do_writeback(struct bdi_writeback *wb)
+static long wb_ctx_do_writeback(struct bdi_writeback *wb,
+				struct wb_ctx *p_wb_ctx)
 {
 	struct wb_writeback_work *work;
 	long wrote = 0;
 
-	set_bit(WB_writeback_running, &wb->state);
+	set_bit(WB_writeback_running, &p_wb_ctx->state);
 	while ((work = get_next_work_item(wb)) != NULL) {
 		trace_writeback_exec(wb, work);
-		wrote += wb_writeback(wb, work);
+		wrote += wb_ctx_writeback(wb, work, p_wb_ctx);
 		finish_writeback_work(work);
 	}
 
 	/*
 	 * Check for a flush-everything request
 	 */
-	wrote += wb_check_start_all(wb);
+	wrote += wb_ctx_check_start_all(wb, p_wb_ctx);
 
 	/*
 	 * Check for periodic writeback, kupdated() style
 	 */
-	wrote += wb_check_old_data_flush(wb);
-	wrote += wb_check_background_flush(wb);
-	clear_bit(WB_writeback_running, &wb->state);
+	wrote += wb_ctx_check_old_data_flush(wb, p_wb_ctx);
+	wrote += wb_ctx_check_background_flush(wb, p_wb_ctx);
+	clear_bit(WB_writeback_running, &p_wb_ctx->state);
 
 	return wrote;
 }
@@ -2323,10 +2439,11 @@ static long wb_do_writeback(struct bdi_writeback *wb)
  * Handle writeback of dirty data for the device backed by this bdi. Also
  * reschedules periodically and does kupdated style flushing.
  */
-void wb_workfn(struct work_struct *work)
+void wb_ctx_workfn(struct work_struct *work)
 {
-	struct bdi_writeback *wb = container_of(to_delayed_work(work),
-						struct bdi_writeback, dwork);
+	struct wb_ctx *p_wb_ctx = container_of(to_delayed_work(work),
+					struct wb_ctx, pctx_dwork);
+	struct bdi_writeback *wb = p_wb_ctx->b_wb;
 	long pages_written;
 
 	set_worker_desc("flush-%s", bdi_dev_name(wb->bdi));
@@ -2340,7 +2457,7 @@ void wb_workfn(struct work_struct *work)
 		 * rescuer as work_list needs to be drained.
 		 */
 		do {
-			pages_written = wb_do_writeback(wb);
+			pages_written = wb_ctx_do_writeback(wb, p_wb_ctx);
 			trace_writeback_pages_written(pages_written);
 		} while (!list_empty(&wb->work_list));
 	} else {
@@ -2349,15 +2466,16 @@ void wb_workfn(struct work_struct *work)
 		 * the emergency worker.  Don't hog it.  Hopefully, 1024 is
 		 * enough for efficient IO.
 		 */
-		pages_written = writeback_inodes_wb(wb, 1024,
-						    WB_REASON_FORKER_THREAD);
+		pages_written = writeback_inodes_wb_ctx(wb, 1024,
+						WB_REASON_FORKER_THREAD,
+						p_wb_ctx);
 		trace_writeback_pages_written(pages_written);
 	}
 
 	if (!list_empty(&wb->work_list))
-		wb_wakeup(wb);
-	else if (wb_has_dirty_io(wb) && dirty_writeback_interval)
-		wb_wakeup_delayed(wb);
+		wb_ctx_wakeup(wb, p_wb_ctx);
+	else if (wb_ctx_has_dirty_io(p_wb_ctx) && dirty_writeback_interval)
+		wb_ctx_wakeup_delayed(wb, p_wb_ctx);
 }
 
 /*
@@ -2371,8 +2489,13 @@ static void __wakeup_flusher_threads_bdi(struct backing_dev_info *bdi,
 	if (!bdi_has_dirty_io(bdi))
 		return;
 
-	list_for_each_entry_rcu(wb, &bdi->wb_list, bdi_node)
-		wb_start_writeback(wb, reason);
+	list_for_each_entry_rcu(wb, &bdi->wb_list, bdi_node) {
+		for (int i = 0; i < NR_WB_CTX; i++) {
+			struct wb_ctx *p_wb_ctx = ctx_wb_struct(wb, i);
+
+			wb_ctx_start_writeback(wb, reason, p_wb_ctx);
+		}
+	}
 }
 
 void wakeup_flusher_threads_bdi(struct backing_dev_info *bdi,
@@ -2427,9 +2550,16 @@ static void wakeup_dirtytime_writeback(struct work_struct *w)
 	list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) {
 		struct bdi_writeback *wb;
 
-		list_for_each_entry_rcu(wb, &bdi->wb_list, bdi_node)
-			if (!list_empty(&wb->b_dirty_time))
-				wb_wakeup(wb);
+		list_for_each_entry_rcu(wb, &bdi->wb_list, bdi_node) {
+			for (int i = 0; i < NR_WB_CTX; i++) {
+				struct wb_ctx *p_wb_ctx = ctx_wb_struct(wb, i);
+				struct list_head *pcpu_dirty_time_list =
+						ctx_b_dirty_time_list(wb, i);
+
+				if (!list_empty(pcpu_dirty_time_list))
+					wb_ctx_wakeup(wb, p_wb_ctx);
+			}
+		}
 	}
 	rcu_read_unlock();
 	schedule_delayed_work(&dirtytime_work, dirtytime_expire_interval * HZ);
@@ -2581,6 +2711,8 @@ void __mark_inode_dirty(struct inode *inode, int flags)
 		 * reposition it (that would break b_dirty time-ordering).
 		 */
 		if (!was_dirty) {
+			/* Schedule writeback on first wb_ctx */
+			struct wb_ctx *p_wb_ctx = ctx_wb_struct(wb, 0);
 			struct list_head *dirty_list;
 			bool wakeup_bdi = false;
 
@@ -2588,13 +2720,16 @@ void __mark_inode_dirty(struct inode *inode, int flags)
 			if (dirtytime)
 				inode->dirtied_time_when = jiffies;
 
+
 			if (inode->i_state & I_DIRTY)
-				dirty_list = &wb->b_dirty;
+				dirty_list = ctx_b_dirty_list(wb, 0);
 			else
-				dirty_list = &wb->b_dirty_time;
+				dirty_list = ctx_b_dirty_time_list(wb,
+								   0);
 
-			wakeup_bdi = inode_io_list_move_locked(inode, wb,
-							       dirty_list);
+			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);
@@ -2608,7 +2743,7 @@ void __mark_inode_dirty(struct inode *inode, int flags)
 			 */
 			if (wakeup_bdi &&
 			    (wb->bdi->capabilities & BDI_CAP_WRITEBACK))
-				wb_wakeup_delayed(wb);
+				wb_ctx_wakeup_delayed(wb, p_wb_ctx);
 			return;
 		}
 	}
diff --git a/include/linux/backing-dev-defs.h b/include/linux/backing-dev-defs.h
index df627783e879..d7e9df5562f4 100644
--- a/include/linux/backing-dev-defs.h
+++ b/include/linux/backing-dev-defs.h
@@ -134,15 +134,12 @@ struct bdi_writeback {
 	unsigned long state;		/* Always use atomic bitops on this */
 	unsigned long last_old_flush;	/* last old data flush */
 
-	struct list_head b_dirty;	/* dirty inodes */
-	struct list_head b_io;		/* parked for writeback */
-	struct list_head b_more_io;	/* parked for more writeback */
-	struct list_head b_dirty_time;	/* time stamps are dirty */
 	spinlock_t list_lock;		/* protects the b_* lists */
 
 	atomic_t writeback_inodes;	/* number of inodes under writeback */
 	struct percpu_counter stat[NR_WB_STAT_ITEMS];
 
+	/* TODO : Remove these duplicated fields */
 	unsigned long bw_time_stamp;	/* last time write bw is updated */
 	unsigned long dirtied_stamp;
 	unsigned long written_stamp;	/* pages written at bw_time_stamp */
@@ -164,7 +161,6 @@ struct bdi_writeback {
 
 	spinlock_t work_lock;		/* protects work_list & dwork scheduling */
 	struct list_head work_list;
-	struct delayed_work dwork;	/* work item used for writeback */
 	struct delayed_work bw_dwork;	/* work item used for bandwidth estimate */
 
 	struct list_head bdi_node;	/* anchored at bdi->wb_list */
diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h
index 8e7af9a03b41..c2c2fa5dbb44 100644
--- a/include/linux/backing-dev.h
+++ b/include/linux/backing-dev.h
@@ -37,7 +37,7 @@ void bdi_unregister(struct backing_dev_info *bdi);
 struct backing_dev_info *bdi_alloc(int node_id);
 
 void wb_start_background_writeback(struct bdi_writeback *wb);
-void wb_workfn(struct work_struct *work);
+void wb_ctx_workfn(struct work_struct *work);
 
 void wb_wait_for_completion(struct wb_completion *done);
 
@@ -48,7 +48,21 @@ extern struct workqueue_struct *bdi_wq;
 
 static inline bool wb_has_dirty_io(struct bdi_writeback *wb)
 {
-	return test_bit(WB_has_dirty_io, &wb->state);
+	bool state;
+	struct wb_ctx *p_wb_ctx;
+
+	for (int i = 0; i < NR_WB_CTX; i++) {
+		p_wb_ctx = ctx_wb_struct(wb, i);
+		state =  test_bit(WB_has_dirty_io, &p_wb_ctx->state);
+		if (state)
+			return state;
+	}
+	return false;
+}
+
+static inline bool wb_ctx_has_dirty_io(struct wb_ctx *p_wb_ctx)
+{
+	return test_bit(WB_has_dirty_io, &p_wb_ctx->state);
 }
 
 static inline bool bdi_has_dirty_io(struct backing_dev_info *bdi)
@@ -138,7 +152,21 @@ int bdi_init(struct backing_dev_info *bdi);
  */
 static inline bool writeback_in_progress(struct bdi_writeback *wb)
 {
-	return test_bit(WB_writeback_running, &wb->state);
+	bool state;
+	struct wb_ctx *p_wb_ctx;
+
+	for (int i = 0; i < NR_WB_CTX; i++) {
+		p_wb_ctx = ctx_wb_struct(wb, i);
+		state =  test_bit(WB_writeback_running, &p_wb_ctx->state);
+		if (state)
+			return state;
+	}
+	return false;
+}
+
+static inline bool writeback_ctx_in_progress(struct wb_ctx *p_wb_ctx)
+{
+	return test_bit(WB_writeback_running, &p_wb_ctx->state);
 }
 
 struct backing_dev_info *inode_to_bdi(struct inode *inode);
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index fc072e9fe42c..1711759f4879 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -65,15 +65,24 @@ static void collect_wb_stats(struct wb_stats *stats,
 	struct inode *inode;
 
 	spin_lock(&wb->list_lock);
-	list_for_each_entry(inode, &wb->b_dirty, i_io_list)
-		stats->nr_dirty++;
-	list_for_each_entry(inode, &wb->b_io, i_io_list)
-		stats->nr_io++;
-	list_for_each_entry(inode, &wb->b_more_io, i_io_list)
-		stats->nr_more_io++;
-	list_for_each_entry(inode, &wb->b_dirty_time, i_io_list)
-		if (inode->i_state & I_DIRTY_TIME)
-			stats->nr_dirty_time++;
+
+	for (int i = 0; i < NR_WB_CTX; i++) {
+		struct list_head *pctx_b_dirty = ctx_b_dirty_list(wb, i);
+		struct list_head *pctx_b_io = ctx_b_io_list(wb, i);
+		struct list_head *pctx_b_more_io = ctx_b_more_io_list(wb, i);
+		struct list_head *pctx_b_dirty_time =
+					ctx_b_dirty_time_list(wb, i);
+
+		list_for_each_entry(inode, pctx_b_dirty, i_io_list)
+			stats->nr_dirty++;
+		list_for_each_entry(inode, pctx_b_io, i_io_list)
+			stats->nr_io++;
+		list_for_each_entry(inode, pctx_b_more_io, i_io_list)
+			stats->nr_more_io++;
+		list_for_each_entry(inode, pctx_b_dirty_time, i_io_list)
+			if (inode->i_state & I_DIRTY_TIME)
+				stats->nr_dirty_time++;
+	}
 	spin_unlock(&wb->list_lock);
 
 	stats->nr_writeback += wb_stat(wb, WB_WRITEBACK);
@@ -522,10 +531,6 @@ static int wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi,
 
 	wb->bdi = bdi;
 	wb->last_old_flush = jiffies;
-	INIT_LIST_HEAD(&wb->b_dirty);
-	INIT_LIST_HEAD(&wb->b_io);
-	INIT_LIST_HEAD(&wb->b_more_io);
-	INIT_LIST_HEAD(&wb->b_dirty_time);
 	spin_lock_init(&wb->list_lock);
 
 	atomic_set(&wb->writeback_inodes, 0);
@@ -538,7 +543,6 @@ static int wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi,
 
 	spin_lock_init(&wb->work_lock);
 	INIT_LIST_HEAD(&wb->work_list);
-	INIT_DELAYED_WORK(&wb->dwork, wb_workfn);
 	INIT_DELAYED_WORK(&wb->bw_dwork, wb_update_bandwidth_workfn);
 
 	for (i = 0; i < NR_WB_CTX; i++) {
@@ -556,8 +560,9 @@ static int wb_init(struct bdi_writeback *wb, struct backing_dev_info *bdi,
 		INIT_LIST_HEAD(ctx_b_io_list(wb, i));
 		INIT_LIST_HEAD(ctx_b_more_io_list(wb, i));
 
-		INIT_DELAYED_WORK(&p_wb_ctx->pctx_dwork, wb_workfn);
+		INIT_DELAYED_WORK(&p_wb_ctx->pctx_dwork, wb_ctx_workfn);
 	}
+
 	err = fprop_local_init_percpu(&wb->completions, gfp);
 	if (err)
 		return err;
@@ -590,15 +595,23 @@ static void wb_shutdown(struct bdi_writeback *wb)
 	 * tells wb_workfn() that @wb is dying and its work_list needs to
 	 * be drained no matter what.
 	 */
-	mod_delayed_work(bdi_wq, &wb->dwork, 0);
-	flush_delayed_work(&wb->dwork);
+	for (int i = 0; i < NR_WB_CTX; i++) {
+		struct wb_ctx *p_wb_ctx = ctx_wb_struct(wb, i);
+
+		mod_delayed_work(bdi_wq, &p_wb_ctx->pctx_dwork, 0);
+		flush_delayed_work(&p_wb_ctx->pctx_dwork);
+	}
 	WARN_ON(!list_empty(&wb->work_list));
 	flush_delayed_work(&wb->bw_dwork);
 }
 
 static void wb_exit(struct bdi_writeback *wb)
 {
-	WARN_ON(delayed_work_pending(&wb->dwork));
+	for (int i = 0; i < NR_WB_CTX; i++) {
+		struct wb_ctx *p_wb_ctx = ctx_wb_struct(wb, i);
+
+		WARN_ON(delayed_work_pending(&p_wb_ctx->pctx_dwork));
+	}
 	percpu_counter_destroy_many(wb->stat, NR_WB_STAT_ITEMS);
 	fprop_local_destroy_percpu(&wb->completions);
 }
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index eb55ece39c56..fa68facaa651 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -1255,54 +1255,60 @@ static void wb_position_ratio(struct dirty_throttle_control *dtc)
 	dtc->pos_ratio = pos_ratio;
 }
 
-static void wb_update_write_bandwidth(struct bdi_writeback *wb,
+static void wb_pctx_update_write_bandwidth(struct bdi_writeback *wb,
 				      unsigned long elapsed,
 				      unsigned long written)
 {
 	const unsigned long period = roundup_pow_of_two(3 * HZ);
-	unsigned long avg = wb->avg_write_bandwidth;
-	unsigned long old = wb->write_bandwidth;
 	u64 bw;
+	int i;
 
-	/*
-	 * bw = written * HZ / elapsed
-	 *
-	 *                   bw * elapsed + write_bandwidth * (period - elapsed)
-	 * write_bandwidth = ---------------------------------------------------
-	 *                                          period
-	 *
-	 * @written may have decreased due to folio_redirty_for_writepage().
-	 * Avoid underflowing @bw calculation.
-	 */
-	bw = written - min(written, wb->written_stamp);
-	bw *= HZ;
-	if (unlikely(elapsed > period)) {
-		bw = div64_ul(bw, elapsed);
-		avg = bw;
-		goto out;
-	}
-	bw += (u64)wb->write_bandwidth * (period - elapsed);
-	bw >>= ilog2(period);
+	for (i = 0; i < NR_WB_CTX; i++) {
+		unsigned long avg, old;
+		struct wb_ctx *p_wb_ctx = ctx_wb_struct(wb, i);
 
-	/*
-	 * one more level of smoothing, for filtering out sudden spikes
-	 */
-	if (avg > old && old >= (unsigned long)bw)
-		avg -= (avg - old) >> 3;
+		avg = p_wb_ctx->avg_write_bandwidth;
+		old = p_wb_ctx->write_bandwidth;
+		/*
+		 * bw = written * HZ / elapsed
+		 *
+		 *                   bw * elapsed + write_bandwidth * (period - elapsed)
+		 * write_bandwidth = ---------------------------------------------------
+		 *                                          period
+		 *
+		 * @written may have decreased due to folio_redirty_for_writepage().
+		 * Avoid underflowing @bw calculation.
+		 */
+		bw = written - min(written, wb->written_stamp);
+		bw *= HZ;
+		if (unlikely(elapsed > period)) {
+			bw = div64_ul(bw, elapsed);
+			avg = bw;
+			goto out;
+		}
+		bw += (u64)p_wb_ctx->write_bandwidth * (period - elapsed);
+		bw >>= ilog2(period);
 
-	if (avg < old && old <= (unsigned long)bw)
-		avg += (old - avg) >> 3;
+		/*
+		 * one more level of smoothing, for filtering out sudden spikes
+		 */
+		if (avg > old && old >= (unsigned long)bw)
+			avg -= (avg - old) >> 3;
 
+		if (avg < old && old <= (unsigned long)bw)
+			avg += (old - avg) >> 3;
 out:
-	/* keep avg > 0 to guarantee that tot > 0 if there are dirty wbs */
-	avg = max(avg, 1LU);
-	if (wb_has_dirty_io(wb)) {
-		long delta = avg - wb->avg_write_bandwidth;
-		WARN_ON_ONCE(atomic_long_add_return(delta,
-					&wb->bdi->tot_write_bandwidth) <= 0);
+		/* keep avg > 0 to guarantee that tot > 0 if there are dirty wbs */
+		avg = max(avg, 1LU);
+		if (wb_ctx_has_dirty_io(p_wb_ctx)) {
+			long delta = avg - p_wb_ctx->avg_write_bandwidth;
+
+			WARN_ON_ONCE(atomic_long_add_return(delta,
+						&wb->bdi->tot_write_bandwidth) <= 0);
+		}
+		p_wb_ctx->write_bandwidth = bw;
+		WRITE_ONCE(p_wb_ctx->avg_write_bandwidth, avg);
 	}
-	wb->write_bandwidth = bw;
-	WRITE_ONCE(wb->avg_write_bandwidth, avg);
 }
 
 static void update_dirty_limit(struct dirty_throttle_control *dtc)
@@ -1545,7 +1551,7 @@ static void __wb_update_bandwidth(struct dirty_throttle_control *gdtc,
 			wb_update_dirty_ratelimit(mdtc, dirtied, elapsed);
 		}
 	}
-	wb_update_write_bandwidth(wb, elapsed, written);
+	wb_pctx_update_write_bandwidth(wb, elapsed, written);
 
 	wb->dirtied_stamp = dirtied;
 	wb->written_stamp = written;
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC 3/3] xfs: use the parallel writeback infra per AG
  2025-02-12 10:36 ` [RFC 0/3] Parallelizing filesystem writeback Kundan Kumar
  2025-02-12 10:36   ` [RFC 1/3] writeback: add parallel writeback infrastructure Kundan Kumar
  2025-02-12 10:36   ` [RFC 2/3] fs: modify writeback infra to work with wb_ctx Kundan Kumar
@ 2025-02-12 10:36   ` Kundan Kumar
  2 siblings, 0 replies; 7+ messages in thread
From: Kundan Kumar @ 2025-02-12 10:36 UTC (permalink / raw)
  To: david, mcgrof, jack, hch
  Cc: linux-fsdevel, linux-xfs, anuj20.g, axboe, clm, willy, gost.dev,
	vishak.g, amir73il, brauner, Kundan Kumar

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@samsung.com>
Signed-off-by: Anuj Gupta <anuj20.g@samsung.com>
---
 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


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [RFC 2/3] fs: modify writeback infra to work with wb_ctx
  2025-02-12 10:36   ` [RFC 2/3] fs: modify writeback infra to work with wb_ctx Kundan Kumar
@ 2025-02-13  5:20     ` kernel test robot
  2025-02-13  5:30     ` kernel test robot
  2025-02-13  6:15     ` kernel test robot
  2 siblings, 0 replies; 7+ messages in thread
From: kernel test robot @ 2025-02-13  5:20 UTC (permalink / raw)
  To: Kundan Kumar; +Cc: oe-kbuild-all

Hi Kundan,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build errors:

[auto build test ERROR on 57d357a7663c4025c68ae07974b679d84e2d5d1a]

url:    https://github.com/intel-lab-lkp/linux/commits/Kundan-Kumar/writeback-add-parallel-writeback-infrastructure/20250212-211021
base:   57d357a7663c4025c68ae07974b679d84e2d5d1a
patch link:    https://lore.kernel.org/r/20250212103634.448437-3-kundan.kumar%40samsung.com
patch subject: [RFC 2/3] fs: modify writeback infra to work with wb_ctx
config: s390-randconfig-001-20250213 (https://download.01.org/0day-ci/archive/20250213/202502131333.sRLxAfsk-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250213/202502131333.sRLxAfsk-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202502131333.sRLxAfsk-lkp@intel.com/

All error/warnings (new ones prefixed by >>):

   fs/fs-writeback.c: In function 'inode_cgwb_move_to_attached':
>> fs/fs-writeback.c:1272:9: error: implicit declaration of function 'wb_io_lists_depopulated'; did you mean 'wb_ctx_io_lists_depopulated'? [-Wimplicit-function-declaration]
    1272 |         wb_io_lists_depopulated(wb);
         |         ^~~~~~~~~~~~~~~~~~~~~~~
         |         wb_ctx_io_lists_depopulated
   fs/fs-writeback.c: In function 'bdi_split_work_to_wbs':
>> fs/fs-writeback.c:1309:17: error: implicit declaration of function 'wb_queue_work'; did you mean 'queue_work'? [-Wimplicit-function-declaration]
    1309 |                 wb_queue_work(&bdi->wb, base_work);
         |                 ^~~~~~~~~~~~~
         |                 queue_work
   fs/fs-writeback.c: In function 'requeue_inode_ctx':
>> fs/fs-writeback.c:1712:25: error: implicit declaration of function 'inode_cgwb_move_to_attached_ctx'; did you mean 'inode_cgwb_move_to_attached'? [-Wimplicit-function-declaration]
    1712 |                         inode_cgwb_move_to_attached_ctx(inode, wb, p_wb_ctx);
         |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         |                         inode_cgwb_move_to_attached
   fs/fs-writeback.c: At top level:
>> fs/fs-writeback.c:210:13: warning: 'wb_ctx_queue_work' defined but not used [-Wunused-function]
     210 | static void wb_ctx_queue_work(struct bdi_writeback *wb,
         |             ^~~~~~~~~~~~~~~~~


vim +1272 fs/fs-writeback.c

7fc5854f8c6efa Tejun Heo      2017-12-12  1262  
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1263  static void inode_cgwb_move_to_attached(struct inode *inode,
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1264  					struct bdi_writeback *wb)
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1265  {
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1266  	assert_spin_locked(&wb->list_lock);
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1267  	assert_spin_locked(&inode->i_lock);
a9438b44bc7015 Jan Kara       2022-12-12  1268  	WARN_ON_ONCE(inode->i_state & I_FREEING);
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1269  
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1270  	inode->i_state &= ~I_SYNC_QUEUED;
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1271  	list_del_init(&inode->i_io_list);
f3b6a6df38aa51 Roman Gushchin 2021-06-28 @1272  	wb_io_lists_depopulated(wb);
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1273  }
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1274  
87e1d789bf55b1 Tejun Heo      2015-05-28  1275  static struct bdi_writeback *
87e1d789bf55b1 Tejun Heo      2015-05-28  1276  locked_inode_to_wb_and_lock_list(struct inode *inode)
87e1d789bf55b1 Tejun Heo      2015-05-28  1277  	__releases(&inode->i_lock)
87e1d789bf55b1 Tejun Heo      2015-05-28  1278  	__acquires(&wb->list_lock)
87e1d789bf55b1 Tejun Heo      2015-05-28  1279  {
87e1d789bf55b1 Tejun Heo      2015-05-28  1280  	struct bdi_writeback *wb = inode_to_wb(inode);
87e1d789bf55b1 Tejun Heo      2015-05-28  1281  
87e1d789bf55b1 Tejun Heo      2015-05-28  1282  	spin_unlock(&inode->i_lock);
87e1d789bf55b1 Tejun Heo      2015-05-28  1283  	spin_lock(&wb->list_lock);
87e1d789bf55b1 Tejun Heo      2015-05-28  1284  	return wb;
87e1d789bf55b1 Tejun Heo      2015-05-28  1285  }
87e1d789bf55b1 Tejun Heo      2015-05-28  1286  
87e1d789bf55b1 Tejun Heo      2015-05-28  1287  static struct bdi_writeback *inode_to_wb_and_lock_list(struct inode *inode)
87e1d789bf55b1 Tejun Heo      2015-05-28  1288  	__acquires(&wb->list_lock)
87e1d789bf55b1 Tejun Heo      2015-05-28  1289  {
87e1d789bf55b1 Tejun Heo      2015-05-28  1290  	struct bdi_writeback *wb = inode_to_wb(inode);
87e1d789bf55b1 Tejun Heo      2015-05-28  1291  
87e1d789bf55b1 Tejun Heo      2015-05-28  1292  	spin_lock(&wb->list_lock);
87e1d789bf55b1 Tejun Heo      2015-05-28  1293  	return wb;
87e1d789bf55b1 Tejun Heo      2015-05-28  1294  }
87e1d789bf55b1 Tejun Heo      2015-05-28  1295  
f2b65121607631 Tejun Heo      2015-05-22  1296  static long wb_split_bdi_pages(struct bdi_writeback *wb, long nr_pages)
f2b65121607631 Tejun Heo      2015-05-22  1297  {
f2b65121607631 Tejun Heo      2015-05-22  1298  	return nr_pages;
f2b65121607631 Tejun Heo      2015-05-22  1299  }
f2b65121607631 Tejun Heo      2015-05-22  1300  
db125360409fc3 Tejun Heo      2015-05-22  1301  static void bdi_split_work_to_wbs(struct backing_dev_info *bdi,
db125360409fc3 Tejun Heo      2015-05-22  1302  				  struct wb_writeback_work *base_work,
db125360409fc3 Tejun Heo      2015-05-22  1303  				  bool skip_if_busy)
db125360409fc3 Tejun Heo      2015-05-22  1304  {
db125360409fc3 Tejun Heo      2015-05-22  1305  	might_sleep();
db125360409fc3 Tejun Heo      2015-05-22  1306  
006a0973ed020a Tejun Heo      2015-08-25  1307  	if (!skip_if_busy || !writeback_in_progress(&bdi->wb)) {
db125360409fc3 Tejun Heo      2015-05-22  1308  		base_work->auto_free = 0;
db125360409fc3 Tejun Heo      2015-05-22 @1309  		wb_queue_work(&bdi->wb, base_work);
db125360409fc3 Tejun Heo      2015-05-22  1310  	}
db125360409fc3 Tejun Heo      2015-05-22  1311  }
db125360409fc3 Tejun Heo      2015-05-22  1312  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC 2/3] fs: modify writeback infra to work with wb_ctx
  2025-02-12 10:36   ` [RFC 2/3] fs: modify writeback infra to work with wb_ctx Kundan Kumar
  2025-02-13  5:20     ` kernel test robot
@ 2025-02-13  5:30     ` kernel test robot
  2025-02-13  6:15     ` kernel test robot
  2 siblings, 0 replies; 7+ messages in thread
From: kernel test robot @ 2025-02-13  5:30 UTC (permalink / raw)
  To: Kundan Kumar; +Cc: llvm, oe-kbuild-all

Hi Kundan,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build errors:

[auto build test ERROR on 57d357a7663c4025c68ae07974b679d84e2d5d1a]

url:    https://github.com/intel-lab-lkp/linux/commits/Kundan-Kumar/writeback-add-parallel-writeback-infrastructure/20250212-211021
base:   57d357a7663c4025c68ae07974b679d84e2d5d1a
patch link:    https://lore.kernel.org/r/20250212103634.448437-3-kundan.kumar%40samsung.com
patch subject: [RFC 2/3] fs: modify writeback infra to work with wb_ctx
config: s390-randconfig-002-20250213 (https://download.01.org/0day-ci/archive/20250213/202502131305.iPwTCMke-lkp@intel.com/config)
compiler: clang version 21.0.0git (https://github.com/llvm/llvm-project 6807164500e9920638e2ab0cdb4bf8321d24f8eb)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250213/202502131305.iPwTCMke-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202502131305.iPwTCMke-lkp@intel.com/

All errors (new ones prefixed by >>):

>> fs/fs-writeback.c:1272:2: error: call to undeclared function 'wb_io_lists_depopulated'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    1272 |         wb_io_lists_depopulated(wb);
         |         ^
   fs/fs-writeback.c:1272:2: note: did you mean 'wb_ctx_io_lists_depopulated'?
   fs/fs-writeback.c:99:13: note: 'wb_ctx_io_lists_depopulated' declared here
      99 | static void wb_ctx_io_lists_depopulated(struct bdi_writeback *wb,
         |             ^
>> fs/fs-writeback.c:1309:3: error: call to undeclared function 'wb_queue_work'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    1309 |                 wb_queue_work(&bdi->wb, base_work);
         |                 ^
   fs/fs-writeback.c:1309:3: note: did you mean 'queue_work'?
   include/linux/workqueue.h:659:20: note: 'queue_work' declared here
     659 | static inline bool queue_work(struct workqueue_struct *wq,
         |                    ^
>> fs/fs-writeback.c:1712:4: error: call to undeclared function 'inode_cgwb_move_to_attached_ctx'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    1712 |                         inode_cgwb_move_to_attached_ctx(inode, wb, p_wb_ctx);
         |                         ^
   fs/fs-writeback.c:1712:4: note: did you mean 'inode_cgwb_move_to_attached'?
   fs/fs-writeback.c:1263:13: note: 'inode_cgwb_move_to_attached' declared here
    1263 | static void inode_cgwb_move_to_attached(struct inode *inode,
         |             ^
   fs/fs-writeback.c:1749:3: error: call to undeclared function 'inode_cgwb_move_to_attached_ctx'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
    1749 |                 inode_cgwb_move_to_attached_ctx(inode, wb, p_wb_ctx);
         |                 ^
   4 errors generated.


vim +/wb_io_lists_depopulated +1272 fs/fs-writeback.c

7fc5854f8c6efa Tejun Heo      2017-12-12  1262  
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1263  static void inode_cgwb_move_to_attached(struct inode *inode,
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1264  					struct bdi_writeback *wb)
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1265  {
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1266  	assert_spin_locked(&wb->list_lock);
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1267  	assert_spin_locked(&inode->i_lock);
a9438b44bc7015 Jan Kara       2022-12-12  1268  	WARN_ON_ONCE(inode->i_state & I_FREEING);
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1269  
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1270  	inode->i_state &= ~I_SYNC_QUEUED;
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1271  	list_del_init(&inode->i_io_list);
f3b6a6df38aa51 Roman Gushchin 2021-06-28 @1272  	wb_io_lists_depopulated(wb);
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1273  }
f3b6a6df38aa51 Roman Gushchin 2021-06-28  1274  
87e1d789bf55b1 Tejun Heo      2015-05-28  1275  static struct bdi_writeback *
87e1d789bf55b1 Tejun Heo      2015-05-28  1276  locked_inode_to_wb_and_lock_list(struct inode *inode)
87e1d789bf55b1 Tejun Heo      2015-05-28  1277  	__releases(&inode->i_lock)
87e1d789bf55b1 Tejun Heo      2015-05-28  1278  	__acquires(&wb->list_lock)
87e1d789bf55b1 Tejun Heo      2015-05-28  1279  {
87e1d789bf55b1 Tejun Heo      2015-05-28  1280  	struct bdi_writeback *wb = inode_to_wb(inode);
87e1d789bf55b1 Tejun Heo      2015-05-28  1281  
87e1d789bf55b1 Tejun Heo      2015-05-28  1282  	spin_unlock(&inode->i_lock);
87e1d789bf55b1 Tejun Heo      2015-05-28  1283  	spin_lock(&wb->list_lock);
87e1d789bf55b1 Tejun Heo      2015-05-28  1284  	return wb;
87e1d789bf55b1 Tejun Heo      2015-05-28  1285  }
87e1d789bf55b1 Tejun Heo      2015-05-28  1286  
87e1d789bf55b1 Tejun Heo      2015-05-28  1287  static struct bdi_writeback *inode_to_wb_and_lock_list(struct inode *inode)
87e1d789bf55b1 Tejun Heo      2015-05-28  1288  	__acquires(&wb->list_lock)
87e1d789bf55b1 Tejun Heo      2015-05-28  1289  {
87e1d789bf55b1 Tejun Heo      2015-05-28  1290  	struct bdi_writeback *wb = inode_to_wb(inode);
87e1d789bf55b1 Tejun Heo      2015-05-28  1291  
87e1d789bf55b1 Tejun Heo      2015-05-28  1292  	spin_lock(&wb->list_lock);
87e1d789bf55b1 Tejun Heo      2015-05-28  1293  	return wb;
87e1d789bf55b1 Tejun Heo      2015-05-28  1294  }
87e1d789bf55b1 Tejun Heo      2015-05-28  1295  
f2b65121607631 Tejun Heo      2015-05-22  1296  static long wb_split_bdi_pages(struct bdi_writeback *wb, long nr_pages)
f2b65121607631 Tejun Heo      2015-05-22  1297  {
f2b65121607631 Tejun Heo      2015-05-22  1298  	return nr_pages;
f2b65121607631 Tejun Heo      2015-05-22  1299  }
f2b65121607631 Tejun Heo      2015-05-22  1300  
db125360409fc3 Tejun Heo      2015-05-22  1301  static void bdi_split_work_to_wbs(struct backing_dev_info *bdi,
db125360409fc3 Tejun Heo      2015-05-22  1302  				  struct wb_writeback_work *base_work,
db125360409fc3 Tejun Heo      2015-05-22  1303  				  bool skip_if_busy)
db125360409fc3 Tejun Heo      2015-05-22  1304  {
db125360409fc3 Tejun Heo      2015-05-22  1305  	might_sleep();
db125360409fc3 Tejun Heo      2015-05-22  1306  
006a0973ed020a Tejun Heo      2015-08-25  1307  	if (!skip_if_busy || !writeback_in_progress(&bdi->wb)) {
db125360409fc3 Tejun Heo      2015-05-22  1308  		base_work->auto_free = 0;
db125360409fc3 Tejun Heo      2015-05-22 @1309  		wb_queue_work(&bdi->wb, base_work);
db125360409fc3 Tejun Heo      2015-05-22  1310  	}
db125360409fc3 Tejun Heo      2015-05-22  1311  }
db125360409fc3 Tejun Heo      2015-05-22  1312  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC 2/3] fs: modify writeback infra to work with wb_ctx
  2025-02-12 10:36   ` [RFC 2/3] fs: modify writeback infra to work with wb_ctx Kundan Kumar
  2025-02-13  5:20     ` kernel test robot
  2025-02-13  5:30     ` kernel test robot
@ 2025-02-13  6:15     ` kernel test robot
  2 siblings, 0 replies; 7+ messages in thread
From: kernel test robot @ 2025-02-13  6:15 UTC (permalink / raw)
  To: Kundan Kumar; +Cc: oe-kbuild-all

Hi Kundan,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build warnings:

[auto build test WARNING on 57d357a7663c4025c68ae07974b679d84e2d5d1a]

url:    https://github.com/intel-lab-lkp/linux/commits/Kundan-Kumar/writeback-add-parallel-writeback-infrastructure/20250212-211021
base:   57d357a7663c4025c68ae07974b679d84e2d5d1a
patch link:    https://lore.kernel.org/r/20250212103634.448437-3-kundan.kumar%40samsung.com
patch subject: [RFC 2/3] fs: modify writeback infra to work with wb_ctx
config: arc-randconfig-002-20250213 (https://download.01.org/0day-ci/archive/20250213/202502131351.uG7U2prq-lkp@intel.com/config)
compiler: arc-elf-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250213/202502131351.uG7U2prq-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202502131351.uG7U2prq-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> fs/fs-writeback.c:130: warning: Function parameter or struct member 'p_wb_ctx' not described in 'inode_io_list_move_locked_ctx'
>> fs/fs-writeback.c:334: warning: Function parameter or struct member 'p_wb_ctx' not described in 'inode_cgwb_move_to_attached_ctx'
>> fs/fs-writeback.c:334: warning: expecting prototype for inode_cgwb_move_to_attached(). Prototype was for inode_cgwb_move_to_attached_ctx() instead


vim +130 fs/fs-writeback.c

d6c10f1fc8626d Tejun Heo    2015-05-22  115  
d6c10f1fc8626d Tejun Heo    2015-05-22  116  /**
4f84ead3d4293c Kundan Kumar 2025-02-12  117   * inode_io_list_move_locked_ctx - move an inode onto a wb_ctx IO list
d6c10f1fc8626d Tejun Heo    2015-05-22  118   * @inode: inode to be moved
d6c10f1fc8626d Tejun Heo    2015-05-22  119   * @wb: target bdi_writeback
bbbc3c1cfaf690 Wang Long    2017-12-05  120   * @head: one of @wb->b_{dirty|io|more_io|dirty_time}
d6c10f1fc8626d Tejun Heo    2015-05-22  121   *
c7f5408493aeb0 Dave Chinner 2015-03-04  122   * Move @inode->i_io_list to @list of @wb and set %WB_has_dirty_io.
d6c10f1fc8626d Tejun Heo    2015-05-22  123   * Returns %true if @inode is the first occupant of the !dirty_time IO
d6c10f1fc8626d Tejun Heo    2015-05-22  124   * lists; otherwise, %false.
d6c10f1fc8626d Tejun Heo    2015-05-22  125   */
4f84ead3d4293c Kundan Kumar 2025-02-12  126  static bool inode_io_list_move_locked_ctx(struct inode *inode,
d6c10f1fc8626d Tejun Heo    2015-05-22  127  					  struct bdi_writeback *wb,
4f84ead3d4293c Kundan Kumar 2025-02-12  128  					  struct list_head *head,
4f84ead3d4293c Kundan Kumar 2025-02-12  129  					  struct wb_ctx *p_wb_ctx)
d6c10f1fc8626d Tejun Heo    2015-05-22 @130  {
4f84ead3d4293c Kundan Kumar 2025-02-12  131  	struct list_head *pctx_b_dirty_time = &p_wb_ctx->pctx_b_dirty_time;
4f84ead3d4293c Kundan Kumar 2025-02-12  132  
4f84ead3d4293c Kundan Kumar 2025-02-12  133  
d6c10f1fc8626d Tejun Heo    2015-05-22  134  	assert_spin_locked(&wb->list_lock);
10e14073107dd0 Jchao Sun    2022-05-24  135  	assert_spin_locked(&inode->i_lock);
a9438b44bc7015 Jan Kara     2022-12-12  136  	WARN_ON_ONCE(inode->i_state & I_FREEING);
d6c10f1fc8626d Tejun Heo    2015-05-22  137  
c7f5408493aeb0 Dave Chinner 2015-03-04  138  	list_move(&inode->i_io_list, head);
d6c10f1fc8626d Tejun Heo    2015-05-22  139  
d6c10f1fc8626d Tejun Heo    2015-05-22  140  	/* dirty_time doesn't count as dirty_io until expiration */
4f84ead3d4293c Kundan Kumar 2025-02-12  141  	if (head != pctx_b_dirty_time)
4f84ead3d4293c Kundan Kumar 2025-02-12  142  		return wb_ctx_io_lists_populated(wb, p_wb_ctx);
d6c10f1fc8626d Tejun Heo    2015-05-22  143  
4f84ead3d4293c Kundan Kumar 2025-02-12  144  	wb_ctx_io_lists_depopulated(wb, p_wb_ctx);
d6c10f1fc8626d Tejun Heo    2015-05-22  145  	return false;
d6c10f1fc8626d Tejun Heo    2015-05-22  146  }
d6c10f1fc8626d Tejun Heo    2015-05-22  147  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2025-02-13  6:15 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <CGME20250212104507epcas5p18722b26bd022151979418508eae99065@epcas5p1.samsung.com>
2025-02-12 10:36 ` [RFC 0/3] Parallelizing filesystem writeback Kundan Kumar
2025-02-12 10:36   ` [RFC 1/3] writeback: add parallel writeback infrastructure Kundan Kumar
2025-02-12 10:36   ` [RFC 2/3] fs: modify writeback infra to work with wb_ctx Kundan Kumar
2025-02-13  5:20     ` kernel test robot
2025-02-13  5:30     ` kernel test robot
2025-02-13  6:15     ` kernel test robot
2025-02-12 10:36   ` [RFC 3/3] xfs: use the parallel writeback infra per AG Kundan Kumar

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.