Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3] writeback, memcg: skip foreign dirty tracking for bdev inodes
@ 2026-09-09  3:43 Julian Sun
  2026-09-09 11:03 ` Jan Kara
  0 siblings, 1 reply; 7+ messages in thread
From: Julian Sun @ 2026-09-09  3:43 UTC (permalink / raw)
  To: linux-mm, cgroups
  Cc: hannes, mhocko, roman.gushchin, shakeel.butt, muchun.song, akpm,
	axboe, tj, jack

The expectation in wbc_detach_inode() that "concurrent write sharing of an
inode is expected to be very rare" does not hold for bdev inodes. On ext4,
metadata from many memcgs shares the same bdev inode, so dirty throttling
in those memcgs can repeatedly trigger foreign flushes of the owner wb.
These flushes can also write ordinary file data, reducing overwrite
coalescing under continuous buffered overwrites. The resulting extra I/O
leaves less device bandwidth for other workloads.

Skip foreign dirty tracking for bdev inodes. Ordinary writeback and
foreign dirty tracking for non-bdev inodes are unchanged.

In a synthetic ext4 test with cgroup v2 (QEMU/KVM, 4 vCPUs, 8 GiB RAM),
device writes are capped at 200 MiB/s. fio repeatedly overwrites a 256 MiB
file in the root wb while four memory- and I/O-limited memcgs generate
metadata and trigger foreign flushes through dirty throttling. A concurrent
fio performs 16 GiB of sequential direct writes on the same filesystem.
Across five pairs with 9 GiB of buffered overwrites per run, mean results
are:

                                      Baseline    Skip bdev
  Overwritten-file device writes       5.60 GiB     1.70 GiB (-69.6%)
  Foreground completion time           104.2 s      89.1 s   (-14.4%)
  Foreground bandwidth                 157.3 MiB/s 183.8 MiB/s
  Foreground p99 completion latency     1434 ms      480 ms

The tradeoff is that memcgs with dirty bdev folios lose this way of
requesting writeback to relieve dirty pressure. In a separate uncapped ext4
test, a memcg with memory.high=256 MiB creates 60,000 files paced at 4,000
files/s, each with a distinct 128-byte xattr, and makes occasional buffered
data writes. The xattrs use external 4 KiB blocks. Across two pairs,
completion is 31-33% slower including final sync, while device writes
remain about 524 MiB.

With 64-byte xattrs stored within the inode, the same paced workload
showed no substantial completion-time regression, including at
memory.high=16 or 32 MiB where the baseline triggered dirty throttling
and foreign flushes.

I believe this external-xattr-heavy workload under memory pressure is
relatively uncommon in practice, and expect the benefits to outweigh the
regression. I therefore consider this an acceptable workaround.

Core fio commands for the overwrite test:

    fio --name=hotspot --filename="$dir/hot.data" --rw=write \
        --bs=64K --ioengine=psync --direct=0 --thread=1 \
        --size=256M --io_size=9216M --rate=64M \
        --refill_buffers=1 --invalidate=0 --eta=never \
        --output-format=json --output="$out/target.json" &
    hot_pid=$!

    sleep 8
    fio --name=foreground --filename="$dir/foreground.data" \
        --rw=write --bs=1M --ioengine=libaio --iodepth=32 \
        --direct=1 --thread=1 --invalidate=0 --size=4096M \
        --io_size=16384M --eta=never --output-format=json \
        --output="$out/foreground.json"
    wait "$hot_pid"

In the log below, base is the original kernel and skip includes this
patch. hot_GiB is device writes for the repeatedly overwritten file,
including final sync; the other performance columns describe foreground
fio.

Full five-pair log:
nohup: ignoring input
[2026-09-05 17:52:10] RESULTS=/tmp/cgwb-20260905-175145; WARNING: reformats /dev/nvme5n1; 5 pair(s), limit=200 MiB/s
[2026-09-05 17:52:10] round-001-base: booting
[2026-09-05 17:52:20] round-001-base: running workload
[2026-09-05 17:55:36] round-001-skip: booting
[2026-09-05 17:55:45] round-001-skip: running workload
[2026-09-05 17:58:57] ROUND 1: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
[2026-09-05 17:58:57]          base    104.574   156.67    156.67    203.82   1434.45     5.862    0
[2026-09-05 17:58:57]          skip     89.125   183.83    183.83    173.62    480.25     1.656    0
[2026-09-05 17:58:57] skip vs base: bandwidth +17.33%, time -14.77%, hotspot IO -71.75%
[2026-09-05 17:58:57] round-002-skip: booting
[2026-09-05 17:59:06] round-002-skip: running workload
[2026-09-05 18:02:18] round-002-base: booting
[2026-09-05 18:02:27] round-002-base: running workload
[2026-09-05 18:05:42] ROUND 2: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
[2026-09-05 18:05:42]          base    103.533   158.25    158.25    201.75   1434.45     5.493    0
[2026-09-05 18:05:42]          skip     89.124   183.83    183.83    173.65    480.25     1.730    0
[2026-09-05 18:05:42] skip vs base: bandwidth +16.17%, time -13.92%, hotspot IO -68.50%
[2026-09-05 18:05:42] round-003-base: booting
[2026-09-05 18:05:52] round-003-base: running workload
[2026-09-05 18:09:06] round-003-skip: booting
[2026-09-05 18:09:15] round-003-skip: running workload
[2026-09-05 18:12:28] ROUND 3: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
[2026-09-05 18:12:28]          base    104.648   156.56    156.56    203.96   1434.45     5.334    1
[2026-09-05 18:12:28]          skip     89.243   183.59    183.59    173.92    480.25     1.824    0
[2026-09-05 18:12:28] skip vs base: bandwidth +17.26%, time -14.72%, hotspot IO -65.80%
[2026-09-05 18:12:28] WARN: runtime diagnostics present; inspect each data/dmesg before using this pair.
[2026-09-05 18:12:28] round-004-skip: booting
[2026-09-05 18:12:37] round-004-skip: running workload
[2026-09-05 18:15:50] round-004-base: booting
[2026-09-05 18:16:00] round-004-base: running workload
[2026-09-05 18:19:15] ROUND 4: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
[2026-09-05 18:19:15]          base    103.565   158.20    158.20    201.84   1434.45     5.497    0
[2026-09-05 18:19:15]          skip     89.124   183.83    183.83    173.57    480.25     1.648    0
[2026-09-05 18:19:15] skip vs base: bandwidth +16.20%, time -13.94%, hotspot IO -70.01%
[2026-09-05 18:19:15] round-005-base: booting
[2026-09-05 18:19:24] round-005-base: running workload
[2026-09-05 18:22:41] round-005-skip: booting
[2026-09-05 18:22:50] round-005-skip: running workload
[2026-09-05 18:26:03] ROUND 5: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
[2026-09-05 18:26:03]          base    104.567   156.68    156.68    203.78   1434.45     5.830    1
[2026-09-05 18:26:03]          skip     89.035   184.02    184.02    173.47    480.25     1.648    0
[2026-09-05 18:26:03] skip vs base: bandwidth +17.44%, time -14.85%, hotspot IO -71.72%
[2026-09-05 18:26:03] WARN: runtime diagnostics present; inspect each data/dmesg before using this pair.
[2026-09-05 18:26:03] DONE: 5 pair(s); all test VMs stopped

Fixes: 97b27821b485 ("writeback, memcg: Implement foreign dirty flushing")
Signed-off-by: Julian Sun <sunjunchao@bytedance.com>
---
 mm/memcontrol.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 1271d390b617..6e8f7ff7d44a 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -3898,6 +3898,15 @@ void mem_cgroup_track_foreign_dirty_slowpath(struct folio *folio,
 	u64 oldest_at = now;
 	int oldest = -1;
 	int i;
+	struct address_space *mapping = folio_mapping(folio);
+	struct inode *bdev_inode = mapping ? mapping->host : NULL;
+
+	/*
+	 * Bdev inodes are usually shared by many memcgs so foreign
+	 * tracking leads to frequent flushes which is counterproductive.
+	 */
+	if (bdev_inode && sb_is_blkdev_sb(bdev_inode->i_sb))
+		return;
 
 	trace_track_foreign_dirty(folio, wb);
 
-- 
2.39.5



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

* Re: [PATCH v3] writeback, memcg: skip foreign dirty tracking for bdev inodes
  2026-09-09  3:43 [PATCH v3] writeback, memcg: skip foreign dirty tracking for bdev inodes Julian Sun
@ 2026-09-09 11:03 ` Jan Kara
  2026-09-09 13:18   ` [External] " Julian Sun
  2026-09-09 18:34   ` Tejun Heo
  0 siblings, 2 replies; 7+ messages in thread
From: Jan Kara @ 2026-09-09 11:03 UTC (permalink / raw)
  To: Julian Sun
  Cc: linux-mm, cgroups, hannes, mhocko, roman.gushchin, shakeel.butt,
	muchun.song, akpm, axboe, tj, jack

On Wed 09-09-26 11:43:43, Julian Sun wrote:
> The expectation in wbc_detach_inode() that "concurrent write sharing of an
> inode is expected to be very rare" does not hold for bdev inodes. On ext4,
> metadata from many memcgs shares the same bdev inode, so dirty throttling
> in those memcgs can repeatedly trigger foreign flushes of the owner wb.
> These flushes can also write ordinary file data, reducing overwrite
> coalescing under continuous buffered overwrites. The resulting extra I/O
> leaves less device bandwidth for other workloads.

Hum. Are you aware of [1]? The guy is from the same company so I was
assuming you two are working together on the problem :). 

[1] https://lore.kernel.org/all/cover.1788836830.git.yinxin.x@bytedance.com

> Skip foreign dirty tracking for bdev inodes. Ordinary writeback and
> foreign dirty tracking for non-bdev inodes are unchanged.

Frankly, I'm not yet convinced this is worth the risk of regressions. The
gains are nice but both the winning and the loosing loads are synthetic and
none of them is like "this is definitely what most people are going to
hit".

I think what [1] is proposing is more in the direction where we might go
(although the second patch in its current form isn't acceptable to me
either). But what I miss from both your approaches is the verification that
the change actually helps your *production* workloads. Unless it is an
obvious fix (like was the case of patch 1 in the series [1]) I'm very
reluctant to approve any additional heuristics unless it is proven they
actually help production loads. These heuristics are never going to be
perfect so you can always come up with a synthetic load breaking it in one
way or another. The art is in tuning them so they work for the practical
cases :)

								Honza

> 
> In a synthetic ext4 test with cgroup v2 (QEMU/KVM, 4 vCPUs, 8 GiB RAM),
> device writes are capped at 200 MiB/s. fio repeatedly overwrites a 256 MiB
> file in the root wb while four memory- and I/O-limited memcgs generate
> metadata and trigger foreign flushes through dirty throttling. A concurrent
> fio performs 16 GiB of sequential direct writes on the same filesystem.
> Across five pairs with 9 GiB of buffered overwrites per run, mean results
> are:
> 
>                                       Baseline    Skip bdev
>   Overwritten-file device writes       5.60 GiB     1.70 GiB (-69.6%)
>   Foreground completion time           104.2 s      89.1 s   (-14.4%)
>   Foreground bandwidth                 157.3 MiB/s 183.8 MiB/s
>   Foreground p99 completion latency     1434 ms      480 ms
> 
> The tradeoff is that memcgs with dirty bdev folios lose this way of
> requesting writeback to relieve dirty pressure. In a separate uncapped ext4
> test, a memcg with memory.high=256 MiB creates 60,000 files paced at 4,000
> files/s, each with a distinct 128-byte xattr, and makes occasional buffered
> data writes. The xattrs use external 4 KiB blocks. Across two pairs,
> completion is 31-33% slower including final sync, while device writes
> remain about 524 MiB.
> 
> With 64-byte xattrs stored within the inode, the same paced workload
> showed no substantial completion-time regression, including at
> memory.high=16 or 32 MiB where the baseline triggered dirty throttling
> and foreign flushes.
> 
> I believe this external-xattr-heavy workload under memory pressure is
> relatively uncommon in practice, and expect the benefits to outweigh the
> regression. I therefore consider this an acceptable workaround.
> 
> Core fio commands for the overwrite test:
> 
>     fio --name=hotspot --filename="$dir/hot.data" --rw=write \
>         --bs=64K --ioengine=psync --direct=0 --thread=1 \
>         --size=256M --io_size=9216M --rate=64M \
>         --refill_buffers=1 --invalidate=0 --eta=never \
>         --output-format=json --output="$out/target.json" &
>     hot_pid=$!
> 
>     sleep 8
>     fio --name=foreground --filename="$dir/foreground.data" \
>         --rw=write --bs=1M --ioengine=libaio --iodepth=32 \
>         --direct=1 --thread=1 --invalidate=0 --size=4096M \
>         --io_size=16384M --eta=never --output-format=json \
>         --output="$out/foreground.json"
>     wait "$hot_pid"
> 
> In the log below, base is the original kernel and skip includes this
> patch. hot_GiB is device writes for the repeatedly overwritten file,
> including final sync; the other performance columns describe foreground
> fio.
> 
> Full five-pair log:
> nohup: ignoring input
> [2026-09-05 17:52:10] RESULTS=/tmp/cgwb-20260905-175145; WARNING: reformats /dev/nvme5n1; 5 pair(s), limit=200 MiB/s
> [2026-09-05 17:52:10] round-001-base: booting
> [2026-09-05 17:52:20] round-001-base: running workload
> [2026-09-05 17:55:36] round-001-skip: booting
> [2026-09-05 17:55:45] round-001-skip: running workload
> [2026-09-05 17:58:57] ROUND 1: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
> [2026-09-05 17:58:57]          base    104.574   156.67    156.67    203.82   1434.45     5.862    0
> [2026-09-05 17:58:57]          skip     89.125   183.83    183.83    173.62    480.25     1.656    0
> [2026-09-05 17:58:57] skip vs base: bandwidth +17.33%, time -14.77%, hotspot IO -71.75%
> [2026-09-05 17:58:57] round-002-skip: booting
> [2026-09-05 17:59:06] round-002-skip: running workload
> [2026-09-05 18:02:18] round-002-base: booting
> [2026-09-05 18:02:27] round-002-base: running workload
> [2026-09-05 18:05:42] ROUND 2: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
> [2026-09-05 18:05:42]          base    103.533   158.25    158.25    201.75   1434.45     5.493    0
> [2026-09-05 18:05:42]          skip     89.124   183.83    183.83    173.65    480.25     1.730    0
> [2026-09-05 18:05:42] skip vs base: bandwidth +16.17%, time -13.92%, hotspot IO -68.50%
> [2026-09-05 18:05:42] round-003-base: booting
> [2026-09-05 18:05:52] round-003-base: running workload
> [2026-09-05 18:09:06] round-003-skip: booting
> [2026-09-05 18:09:15] round-003-skip: running workload
> [2026-09-05 18:12:28] ROUND 3: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
> [2026-09-05 18:12:28]          base    104.648   156.56    156.56    203.96   1434.45     5.334    1
> [2026-09-05 18:12:28]          skip     89.243   183.59    183.59    173.92    480.25     1.824    0
> [2026-09-05 18:12:28] skip vs base: bandwidth +17.26%, time -14.72%, hotspot IO -65.80%
> [2026-09-05 18:12:28] WARN: runtime diagnostics present; inspect each data/dmesg before using this pair.
> [2026-09-05 18:12:28] round-004-skip: booting
> [2026-09-05 18:12:37] round-004-skip: running workload
> [2026-09-05 18:15:50] round-004-base: booting
> [2026-09-05 18:16:00] round-004-base: running workload
> [2026-09-05 18:19:15] ROUND 4: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
> [2026-09-05 18:19:15]          base    103.565   158.20    158.20    201.84   1434.45     5.497    0
> [2026-09-05 18:19:15]          skip     89.124   183.83    183.83    173.57    480.25     1.648    0
> [2026-09-05 18:19:15] skip vs base: bandwidth +16.20%, time -13.94%, hotspot IO -70.01%
> [2026-09-05 18:19:15] round-005-base: booting
> [2026-09-05 18:19:24] round-005-base: running workload
> [2026-09-05 18:22:41] round-005-skip: booting
> [2026-09-05 18:22:50] round-005-skip: running workload
> [2026-09-05 18:26:03] ROUND 5: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
> [2026-09-05 18:26:03]          base    104.567   156.68    156.68    203.78   1434.45     5.830    1
> [2026-09-05 18:26:03]          skip     89.035   184.02    184.02    173.47    480.25     1.648    0
> [2026-09-05 18:26:03] skip vs base: bandwidth +17.44%, time -14.85%, hotspot IO -71.72%
> [2026-09-05 18:26:03] WARN: runtime diagnostics present; inspect each data/dmesg before using this pair.
> [2026-09-05 18:26:03] DONE: 5 pair(s); all test VMs stopped
> 
> Fixes: 97b27821b485 ("writeback, memcg: Implement foreign dirty flushing")
> Signed-off-by: Julian Sun <sunjunchao@bytedance.com>
> ---
>  mm/memcontrol.c | 9 +++++++++
>  1 file changed, 9 insertions(+)
> 
> diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> index 1271d390b617..6e8f7ff7d44a 100644
> --- a/mm/memcontrol.c
> +++ b/mm/memcontrol.c
> @@ -3898,6 +3898,15 @@ void mem_cgroup_track_foreign_dirty_slowpath(struct folio *folio,
>  	u64 oldest_at = now;
>  	int oldest = -1;
>  	int i;
> +	struct address_space *mapping = folio_mapping(folio);
> +	struct inode *bdev_inode = mapping ? mapping->host : NULL;
> +
> +	/*
> +	 * Bdev inodes are usually shared by many memcgs so foreign
> +	 * tracking leads to frequent flushes which is counterproductive.
> +	 */
> +	if (bdev_inode && sb_is_blkdev_sb(bdev_inode->i_sb))
> +		return;
>  
>  	trace_track_foreign_dirty(folio, wb);
>  
> -- 
> 2.39.5
> 
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR


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

* Re: [External] Re: [PATCH v3] writeback, memcg: skip foreign dirty tracking for bdev inodes
  2026-09-09 11:03 ` Jan Kara
@ 2026-09-09 13:18   ` Julian Sun
  2026-09-11 11:20     ` Jan Kara
  2026-09-09 18:34   ` Tejun Heo
  1 sibling, 1 reply; 7+ messages in thread
From: Julian Sun @ 2026-09-09 13:18 UTC (permalink / raw)
  To: Jan Kara
  Cc: linux-mm, cgroups, hannes, mhocko, roman.gushchin, shakeel.butt,
	muchun.song, akpm, axboe, tj

On Wed, Sep 9, 2026 at 7:13 PM Jan Kara <jack@suse.cz> wrote:
>
> On Wed 09-09-26 11:43:43, Julian Sun wrote:
> > The expectation in wbc_detach_inode() that "concurrent write sharing of an
> > inode is expected to be very rare" does not hold for bdev inodes. On ext4,
> > metadata from many memcgs shares the same bdev inode, so dirty throttling
> > in those memcgs can repeatedly trigger foreign flushes of the owner wb.
> > These flushes can also write ordinary file data, reducing overwrite
> > coalescing under continuous buffered overwrites. The resulting extra I/O
> > leaves less device bandwidth for other workloads.
>
> Hum. Are you aware of [1]? The guy is from the same company so I was
> assuming you two are working together on the problem :).

Yes, Xin and I are on the same team, and we are looking at different
ways to mitigate this issue.

We will continue trying to gather more evidence from production to
better understand its actual impact. The number of calls to
`balance_dirty_pages()` and the time spent in it might be useful
metrics for this purpose.

Jan, if we can show that this patch provides a measurable improvement
in production, would you consider accepting it? Or do you think we
should still look for a better approach? If you think there is a
better way to address this, we'd be very interested in your
suggestion.

>
> [1] https://lore.kernel.org/all/cover.1788836830.git.yinxin.x@bytedance.com
>
> > Skip foreign dirty tracking for bdev inodes. Ordinary writeback and
> > foreign dirty tracking for non-bdev inodes are unchanged.
>
> Frankly, I'm not yet convinced this is worth the risk of regressions. The
> gains are nice but both the winning and the loosing loads are synthetic and
> none of them is like "this is definitely what most people are going to
> hit".
>
> I think what [1] is proposing is more in the direction where we might go
> (although the second patch in its current form isn't acceptable to me
> either). But what I miss from both your approaches is the verification that
> the change actually helps your *production* workloads. Unless it is an
> obvious fix (like was the case of patch 1 in the series [1]) I'm very
> reluctant to approve any additional heuristics unless it is proven they
> actually help production loads. These heuristics are never going to be
> perfect so you can always come up with a synthetic load breaking it in one
> way or another. The art is in tuning them so they work for the practical
> cases :)
>
>                                                                 Honza
>
> >
> > In a synthetic ext4 test with cgroup v2 (QEMU/KVM, 4 vCPUs, 8 GiB RAM),
> > device writes are capped at 200 MiB/s. fio repeatedly overwrites a 256 MiB
> > file in the root wb while four memory- and I/O-limited memcgs generate
> > metadata and trigger foreign flushes through dirty throttling. A concurrent
> > fio performs 16 GiB of sequential direct writes on the same filesystem.
> > Across five pairs with 9 GiB of buffered overwrites per run, mean results
> > are:
> >
> >                                       Baseline    Skip bdev
> >   Overwritten-file device writes       5.60 GiB     1.70 GiB (-69.6%)
> >   Foreground completion time           104.2 s      89.1 s   (-14.4%)
> >   Foreground bandwidth                 157.3 MiB/s 183.8 MiB/s
> >   Foreground p99 completion latency     1434 ms      480 ms
> >
> > The tradeoff is that memcgs with dirty bdev folios lose this way of
> > requesting writeback to relieve dirty pressure. In a separate uncapped ext4
> > test, a memcg with memory.high=256 MiB creates 60,000 files paced at 4,000
> > files/s, each with a distinct 128-byte xattr, and makes occasional buffered
> > data writes. The xattrs use external 4 KiB blocks. Across two pairs,
> > completion is 31-33% slower including final sync, while device writes
> > remain about 524 MiB.
> >
> > With 64-byte xattrs stored within the inode, the same paced workload
> > showed no substantial completion-time regression, including at
> > memory.high=16 or 32 MiB where the baseline triggered dirty throttling
> > and foreign flushes.
> >
> > I believe this external-xattr-heavy workload under memory pressure is
> > relatively uncommon in practice, and expect the benefits to outweigh the
> > regression. I therefore consider this an acceptable workaround.
> >
> > Core fio commands for the overwrite test:
> >
> >     fio --name=hotspot --filename="$dir/hot.data" --rw=write \
> >         --bs=64K --ioengine=psync --direct=0 --thread=1 \
> >         --size=256M --io_size=9216M --rate=64M \
> >         --refill_buffers=1 --invalidate=0 --eta=never \
> >         --output-format=json --output="$out/target.json" &
> >     hot_pid=$!
> >
> >     sleep 8
> >     fio --name=foreground --filename="$dir/foreground.data" \
> >         --rw=write --bs=1M --ioengine=libaio --iodepth=32 \
> >         --direct=1 --thread=1 --invalidate=0 --size=4096M \
> >         --io_size=16384M --eta=never --output-format=json \
> >         --output="$out/foreground.json"
> >     wait "$hot_pid"
> >
> > In the log below, base is the original kernel and skip includes this
> > patch. hot_GiB is device writes for the repeatedly overwritten file,
> > including final sync; the other performance columns describe foreground
> > fio.
> >
> > Full five-pair log:
> > nohup: ignoring input
> > [2026-09-05 17:52:10] RESULTS=/tmp/cgwb-20260905-175145; WARNING: reformats /dev/nvme5n1; 5 pair(s), limit=200 MiB/s
> > [2026-09-05 17:52:10] round-001-base: booting
> > [2026-09-05 17:52:20] round-001-base: running workload
> > [2026-09-05 17:55:36] round-001-skip: booting
> > [2026-09-05 17:55:45] round-001-skip: running workload
> > [2026-09-05 17:58:57] ROUND 1: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
> > [2026-09-05 17:58:57]          base    104.574   156.67    156.67    203.82   1434.45     5.862    0
> > [2026-09-05 17:58:57]          skip     89.125   183.83    183.83    173.62    480.25     1.656    0
> > [2026-09-05 17:58:57] skip vs base: bandwidth +17.33%, time -14.77%, hotspot IO -71.75%
> > [2026-09-05 17:58:57] round-002-skip: booting
> > [2026-09-05 17:59:06] round-002-skip: running workload
> > [2026-09-05 18:02:18] round-002-base: booting
> > [2026-09-05 18:02:27] round-002-base: running workload
> > [2026-09-05 18:05:42] ROUND 2: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
> > [2026-09-05 18:05:42]          base    103.533   158.25    158.25    201.75   1434.45     5.493    0
> > [2026-09-05 18:05:42]          skip     89.124   183.83    183.83    173.65    480.25     1.730    0
> > [2026-09-05 18:05:42] skip vs base: bandwidth +16.17%, time -13.92%, hotspot IO -68.50%
> > [2026-09-05 18:05:42] round-003-base: booting
> > [2026-09-05 18:05:52] round-003-base: running workload
> > [2026-09-05 18:09:06] round-003-skip: booting
> > [2026-09-05 18:09:15] round-003-skip: running workload
> > [2026-09-05 18:12:28] ROUND 3: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
> > [2026-09-05 18:12:28]          base    104.648   156.56    156.56    203.96   1434.45     5.334    1
> > [2026-09-05 18:12:28]          skip     89.243   183.59    183.59    173.92    480.25     1.824    0
> > [2026-09-05 18:12:28] skip vs base: bandwidth +17.26%, time -14.72%, hotspot IO -65.80%
> > [2026-09-05 18:12:28] WARN: runtime diagnostics present; inspect each data/dmesg before using this pair.
> > [2026-09-05 18:12:28] round-004-skip: booting
> > [2026-09-05 18:12:37] round-004-skip: running workload
> > [2026-09-05 18:15:50] round-004-base: booting
> > [2026-09-05 18:16:00] round-004-base: running workload
> > [2026-09-05 18:19:15] ROUND 4: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
> > [2026-09-05 18:19:15]          base    103.565   158.20    158.20    201.84   1434.45     5.497    0
> > [2026-09-05 18:19:15]          skip     89.124   183.83    183.83    173.57    480.25     1.648    0
> > [2026-09-05 18:19:15] skip vs base: bandwidth +16.20%, time -13.94%, hotspot IO -70.01%
> > [2026-09-05 18:19:15] round-005-base: booting
> > [2026-09-05 18:19:24] round-005-base: running workload
> > [2026-09-05 18:22:41] round-005-skip: booting
> > [2026-09-05 18:22:50] round-005-skip: running workload
> > [2026-09-05 18:26:03] ROUND 5: kind     seconds    MiB/s      IOPS   mean_ms    p99_ms   hot_GiB WARN
> > [2026-09-05 18:26:03]          base    104.567   156.68    156.68    203.78   1434.45     5.830    1
> > [2026-09-05 18:26:03]          skip     89.035   184.02    184.02    173.47    480.25     1.648    0
> > [2026-09-05 18:26:03] skip vs base: bandwidth +17.44%, time -14.85%, hotspot IO -71.72%
> > [2026-09-05 18:26:03] WARN: runtime diagnostics present; inspect each data/dmesg before using this pair.
> > [2026-09-05 18:26:03] DONE: 5 pair(s); all test VMs stopped
> >
> > Fixes: 97b27821b485 ("writeback, memcg: Implement foreign dirty flushing")
> > Signed-off-by: Julian Sun <sunjunchao@bytedance.com>
> > ---
> >  mm/memcontrol.c | 9 +++++++++
> >  1 file changed, 9 insertions(+)
> >
> > diff --git a/mm/memcontrol.c b/mm/memcontrol.c
> > index 1271d390b617..6e8f7ff7d44a 100644
> > --- a/mm/memcontrol.c
> > +++ b/mm/memcontrol.c
> > @@ -3898,6 +3898,15 @@ void mem_cgroup_track_foreign_dirty_slowpath(struct folio *folio,
> >       u64 oldest_at = now;
> >       int oldest = -1;
> >       int i;
> > +     struct address_space *mapping = folio_mapping(folio);
> > +     struct inode *bdev_inode = mapping ? mapping->host : NULL;
> > +
> > +     /*
> > +      * Bdev inodes are usually shared by many memcgs so foreign
> > +      * tracking leads to frequent flushes which is counterproductive.
> > +      */
> > +     if (bdev_inode && sb_is_blkdev_sb(bdev_inode->i_sb))
> > +             return;
> >
> >       trace_track_foreign_dirty(folio, wb);
> >
> > --
> > 2.39.5
> >
> --
> Jan Kara <jack@suse.com>
> SUSE Labs, CR


Thanks,
-- 
Julian Sun <sunjunchao@bytedance.com>


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

* Re: [PATCH v3] writeback, memcg: skip foreign dirty tracking for bdev inodes
  2026-09-09 11:03 ` Jan Kara
  2026-09-09 13:18   ` [External] " Julian Sun
@ 2026-09-09 18:34   ` Tejun Heo
  1 sibling, 0 replies; 7+ messages in thread
From: Tejun Heo @ 2026-09-09 18:34 UTC (permalink / raw)
  To: Jan Kara
  Cc: Julian Sun, linux-mm, cgroups, hannes, mhocko, roman.gushchin,
	shakeel.butt, muchun.song, akpm, axboe

Hello,

On Wed, Sep 09, 2026 at 01:03:27PM +0200, Jan Kara wrote:
> On Wed 09-09-26 11:43:43, Julian Sun wrote:
> > The expectation in wbc_detach_inode() that "concurrent write sharing of an
> > inode is expected to be very rare" does not hold for bdev inodes. On ext4,
> > metadata from many memcgs shares the same bdev inode, so dirty throttling
> > in those memcgs can repeatedly trigger foreign flushes of the owner wb.
> > These flushes can also write ordinary file data, reducing overwrite
> > coalescing under continuous buffered overwrites. The resulting extra I/O
> > leaves less device bandwidth for other workloads.
> 
> Hum. Are you aware of [1]? The guy is from the same company so I was
> assuming you two are working together on the problem :). 
> 
> [1] https://lore.kernel.org/all/cover.1788836830.git.yinxin.x@bytedance.com
> 
> > Skip foreign dirty tracking for bdev inodes. Ordinary writeback and
> > foreign dirty tracking for non-bdev inodes are unchanged.
> 
> Frankly, I'm not yet convinced this is worth the risk of regressions. The
> gains are nice but both the winning and the loosing loads are synthetic and
> none of them is like "this is definitely what most people are going to
> hit".
> 
> I think what [1] is proposing is more in the direction where we might go
> (although the second patch in its current form isn't acceptable to me
> either). But what I miss from both your approaches is the verification that
> the change actually helps your *production* workloads. Unless it is an
> obvious fix (like was the case of patch 1 in the series [1]) I'm very
> reluctant to approve any additional heuristics unless it is proven they
> actually help production loads. These heuristics are never going to be
> perfect so you can always come up with a synthetic load breaking it in one
> way or another. The art is in tuning them so they work for the practical
> cases :)

The original design's assumption was that a single inode being actively
written into by multiple cgroups would be rare and rather silly and all we
need to do when that happens is being able to limp along. So, the trade-offs
were made in favor of lower overhead for the common cases where there can be
a huge number of active inodes. This mostly worked out for normal files.

Here, the situation is very different. The sharing isn't silly and there are
only a handful of these inodes. If this is an actual problem, I don't think
it can be solved by slewing the existing mechanism one way or another. Maybe
it will make some scenarios better but it will worsen others. The existing
mechanism simply doesn't have enough knoweldge to make sensible decisions.

Given that, if this is a real problem, I think the right solution likely is
tracking more states for these cases so that it can actually make sensible
decisions rather than trying to guess blindly.

Thanks.

-- 
tejun


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

* Re: [External] Re: [PATCH v3] writeback, memcg: skip foreign dirty tracking for bdev inodes
  2026-09-09 13:18   ` [External] " Julian Sun
@ 2026-09-11 11:20     ` Jan Kara
  2026-09-11 11:39       ` Julian Sun
  0 siblings, 1 reply; 7+ messages in thread
From: Jan Kara @ 2026-09-11 11:20 UTC (permalink / raw)
  To: Julian Sun
  Cc: Jan Kara, linux-mm, cgroups, hannes, mhocko, roman.gushchin,
	shakeel.butt, muchun.song, akpm, axboe, tj

On Wed 09-09-26 21:18:11, Julian Sun wrote:
> On Wed, Sep 9, 2026 at 7:13 PM Jan Kara <jack@suse.cz> wrote:
> >
> > On Wed 09-09-26 11:43:43, Julian Sun wrote:
> > > The expectation in wbc_detach_inode() that "concurrent write sharing of an
> > > inode is expected to be very rare" does not hold for bdev inodes. On ext4,
> > > metadata from many memcgs shares the same bdev inode, so dirty throttling
> > > in those memcgs can repeatedly trigger foreign flushes of the owner wb.
> > > These flushes can also write ordinary file data, reducing overwrite
> > > coalescing under continuous buffered overwrites. The resulting extra I/O
> > > leaves less device bandwidth for other workloads.
> >
> > Hum. Are you aware of [1]? The guy is from the same company so I was
> > assuming you two are working together on the problem :).
> 
> Yes, Xin and I are on the same team, and we are looking at different
> ways to mitigate this issue.

OK :).

> We will continue trying to gather more evidence from production to
> better understand its actual impact. The number of calls to
> `balance_dirty_pages()` and the time spent in it might be useful
> metrics for this purpose.
> 
> Jan, if we can show that this patch provides a measurable improvement
> in production, would you consider accepting it? Or do you think we
> should still look for a better approach? If you think there is a
> better way to address this, we'd be very interested in your
> suggestion.

I definitely acknowledge that there's a problem with foreign flushing due
to bdev inodes that needs to be fixed because as Tejun wrote, bdev inodes
break the fundamental assumptions of foreign flushes. But so far I haven't
seen a solution which I'd definitely support.

Having data from production to understand what exactly is the practical
problem will hopefully help us understand how the solution should look
like. Is it just that we trigger too many flushes and that slows down
things? Or is even one foreign flush of the whole wb due to bdev inode
making performance bad? Is it that we have many foreign pages and foreign
flushes fail to properly write them?  These are questions we need to answer
to decide how the solution should look like. And the solution could be
anything from just limiting number of foreign flushes for a wb to 1
(because there really isn't big point in more of them being queued) to more
involved foreign page tracking for bdev inodes so that flushes can be more
targetted.

								Honza
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR


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

* Re: [PATCH v3] writeback, memcg: skip foreign dirty tracking for bdev inodes
  2026-09-11 11:20     ` Jan Kara
@ 2026-09-11 11:39       ` Julian Sun
  2026-09-11 15:39         ` Jan Kara
  0 siblings, 1 reply; 7+ messages in thread
From: Julian Sun @ 2026-09-11 11:39 UTC (permalink / raw)
  To: Jan Kara
  Cc: linux-mm, cgroups, hannes, mhocko, roman.gushchin, shakeel.butt,
	muchun.song, akpm, axboe, tj

On 9/11/26 7:20 PM, Jan Kara wrote:
> On Wed 09-09-26 21:18:11, Julian Sun wrote:
>> On Wed, Sep 9, 2026 at 7:13 PM Jan Kara <jack@suse.cz> wrote:
>>>
>>> On Wed 09-09-26 11:43:43, Julian Sun wrote:
>>>> The expectation in wbc_detach_inode() that "concurrent write sharing of an
>>>> inode is expected to be very rare" does not hold for bdev inodes. On ext4,
>>>> metadata from many memcgs shares the same bdev inode, so dirty throttling
>>>> in those memcgs can repeatedly trigger foreign flushes of the owner wb.
>>>> These flushes can also write ordinary file data, reducing overwrite
>>>> coalescing under continuous buffered overwrites. The resulting extra I/O
>>>> leaves less device bandwidth for other workloads.
>>>
>>> Hum. Are you aware of [1]? The guy is from the same company so I was
>>> assuming you two are working together on the problem :).
>>
>> Yes, Xin and I are on the same team, and we are looking at different
>> ways to mitigate this issue.
> 
> OK :).
> 
>> We will continue trying to gather more evidence from production to
>> better understand its actual impact. The number of calls to
>> `balance_dirty_pages()` and the time spent in it might be useful
>> metrics for this purpose.
>>
>> Jan, if we can show that this patch provides a measurable improvement
>> in production, would you consider accepting it? Or do you think we
>> should still look for a better approach? If you think there is a
>> better way to address this, we'd be very interested in your
>> suggestion.
> 
> I definitely acknowledge that there's a problem with foreign flushing due
> to bdev inodes that needs to be fixed because as Tejun wrote, bdev inodes
> break the fundamental assumptions of foreign flushes. But so far I haven't
> seen a solution which I'd definitely support.
> 
> Having data from production to understand what exactly is the practical
> problem will hopefully help us understand how the solution should look
> like. Is it just that we trigger too many flushes and that slows down
> things? Or is even one foreign flush of the whole wb due to bdev inode
> making performance bad? Is it that we have many foreign pages and foreign
> flushes fail to properly write them?  These are questions we need to answer
> to decide how the solution should look like. And the solution could be
> anything from just limiting number of foreign flushes for a wb to 1
> (because there really isn't big point in more of them being queued) to more
> involved foreign page tracking for bdev inodes so that flushes can be more
> targetted.

Hi Jan,

Thanks for your feedback.

All the foreign flushes we've observed were triggered by bdev inodes.
What we've observed is that the bdev inode has only around 10 MB of
dirty pages, but it triggers writeback of around 40 GB of dirty pages
across the entire wb. The actual foreign pages account for less than
10 MB, yet flushing them triggers writeback of the whole wb, and this
happens repeatedly.

This appears to cause significant write amplification.

Limiting foreign flushes to one may not be sufficient in this case,
since even a single flush can trigger a large amount of unnecessary
writeback. I'm currently working on tracking foreign inodes so that
foreign flushes can target those inodes specifically.

The impact on production workloads is not easy to measure directly,
which is another challenge we're facing. We've had reports of slow
dirty page writeback from production workloads. Although we haven't
established the full causal chain yet, we suspect foreign flushes may
be contributing to the problem.
> 
> 								Honza

Thanks,
-- 
Julian Sun <sunjunchao@bytedance.com>


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

* Re: [PATCH v3] writeback, memcg: skip foreign dirty tracking for bdev inodes
  2026-09-11 11:39       ` Julian Sun
@ 2026-09-11 15:39         ` Jan Kara
  0 siblings, 0 replies; 7+ messages in thread
From: Jan Kara @ 2026-09-11 15:39 UTC (permalink / raw)
  To: Julian Sun
  Cc: Jan Kara, linux-mm, cgroups, hannes, mhocko, roman.gushchin,
	shakeel.butt, muchun.song, akpm, axboe, tj

On Fri 11-09-26 19:39:42, Julian Sun wrote:
> On 9/11/26 7:20 PM, Jan Kara wrote:
> > On Wed 09-09-26 21:18:11, Julian Sun wrote:
> > > On Wed, Sep 9, 2026 at 7:13 PM Jan Kara <jack@suse.cz> wrote:
> > > > 
> > > > On Wed 09-09-26 11:43:43, Julian Sun wrote:
> > > > > The expectation in wbc_detach_inode() that "concurrent write sharing of an
> > > > > inode is expected to be very rare" does not hold for bdev inodes. On ext4,
> > > > > metadata from many memcgs shares the same bdev inode, so dirty throttling
> > > > > in those memcgs can repeatedly trigger foreign flushes of the owner wb.
> > > > > These flushes can also write ordinary file data, reducing overwrite
> > > > > coalescing under continuous buffered overwrites. The resulting extra I/O
> > > > > leaves less device bandwidth for other workloads.
> > > > 
> > > > Hum. Are you aware of [1]? The guy is from the same company so I was
> > > > assuming you two are working together on the problem :).
> > > 
> > > Yes, Xin and I are on the same team, and we are looking at different
> > > ways to mitigate this issue.
> > 
> > OK :).
> > 
> > > We will continue trying to gather more evidence from production to
> > > better understand its actual impact. The number of calls to
> > > `balance_dirty_pages()` and the time spent in it might be useful
> > > metrics for this purpose.
> > > 
> > > Jan, if we can show that this patch provides a measurable improvement
> > > in production, would you consider accepting it? Or do you think we
> > > should still look for a better approach? If you think there is a
> > > better way to address this, we'd be very interested in your
> > > suggestion.
> > 
> > I definitely acknowledge that there's a problem with foreign flushing due
> > to bdev inodes that needs to be fixed because as Tejun wrote, bdev inodes
> > break the fundamental assumptions of foreign flushes. But so far I haven't
> > seen a solution which I'd definitely support.
> > 
> > Having data from production to understand what exactly is the practical
> > problem will hopefully help us understand how the solution should look
> > like. Is it just that we trigger too many flushes and that slows down
> > things? Or is even one foreign flush of the whole wb due to bdev inode
> > making performance bad? Is it that we have many foreign pages and foreign
> > flushes fail to properly write them?  These are questions we need to answer
> > to decide how the solution should look like. And the solution could be
> > anything from just limiting number of foreign flushes for a wb to 1
> > (because there really isn't big point in more of them being queued) to more
> > involved foreign page tracking for bdev inodes so that flushes can be more
> > targetted.
> 
> Hi Jan,
> 
> Thanks for your feedback.
> 
> All the foreign flushes we've observed were triggered by bdev inodes.
> What we've observed is that the bdev inode has only around 10 MB of
> dirty pages, but it triggers writeback of around 40 GB of dirty pages
> across the entire wb. The actual foreign pages account for less than
> 10 MB, yet flushing them triggers writeback of the whole wb, and this
> happens repeatedly.
> 
> This appears to cause significant write amplification.
> 
> Limiting foreign flushes to one may not be sufficient in this case,
> since even a single flush can trigger a large amount of unnecessary
> writeback. I'm currently working on tracking foreign inodes so that
> foreign flushes can target those inodes specifically.

OK, good, these are already some tangible data to use :). So in that case
I'd maybe just:

1) Avoid adding 'wb' as foreign just because it owns bdev inode where we
are dirtying a page.
2) Track that we have some foreign pages in this particular bdev inode.
3) In balance dirty pages queue flushes of bdev inodes where we have
foreign pages (besides queueing standard foreign flushes).

I didn't put too much thought into how to integrate this in an elegant way
with the current writeback infrastructure but something like this shouldn't
be that hard and it should help you with the foreign flush issues you
observe.

> The impact on production workloads is not easy to measure directly,
> which is another challenge we're facing. We've had reports of slow
> dirty page writeback from production workloads. Although we haven't
> established the full causal chain yet, we suspect foreign flushes may
> be contributing to the problem.

Just being able to measure that "with these patches we don't need to flush
40GB of unrelated data in foreign flushes in our production workloads" is
enough to convince me they bring some tangible benefit for your workload
:).

								Honza
-- 
Jan Kara <jack@suse.com>
SUSE Labs, CR


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

end of thread, other threads:[~2026-09-11 15:40 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-09  3:43 [PATCH v3] writeback, memcg: skip foreign dirty tracking for bdev inodes Julian Sun
2026-09-09 11:03 ` Jan Kara
2026-09-09 13:18   ` [External] " Julian Sun
2026-09-11 11:20     ` Jan Kara
2026-09-11 11:39       ` Julian Sun
2026-09-11 15:39         ` Jan Kara
2026-09-09 18:34   ` Tejun Heo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox