Re: [PATCH v3 2/4] mm: kick writeback flusher for IOCB_DONTCACHE with targeted dirty tracking

Jeff Layton
2026-04-27T10:44:23Z

On Mon, 2026-04-27 at 04:01 +0530, Ritesh Harjani wrote:
> Jeff Layton <jlayton@kernel.org> writes:
>
> > The IOCB_DONTCACHE writeback path in generic_write_sync() calls
> > filemap_flush_range() on every write, submitting writeback inline in
> > the writer's context. Perf lock contention profiling shows the
> > performance problem is not lock contention but the writeback submission
> > work itself — walking the page tree and submitting I/O blocks the writer
> > for milliseconds, inflating p99.9 latency from 23ms (buffered) to 93ms
> > (dontcache).
> >
> > Replace the inline filemap_flush_range() call with a flusher kick that
> > drains dirty pages in the background. This moves writeback submission
> > completely off the writer's hot path.
> >
> > To avoid flushing unrelated buffered dirty data, add a dedicated
> > WB_start_dontcache bit and wb_check_start_dontcache() handler that uses
> > the new NR_DONTCACHE_DIRTY counter to determine how many pages to write
> > back. The flusher writes back that many pages from the oldest dirty
> > inodes (not restricted to dontcache-specific inodes). This helps
> > preserve I/O batching while limiting the scope of expedited writeback.
> >
>
> Yup, so, we wakeup the writeback flusher, which will write those many
> "number" of dirty pages. Those dirty pages written by writeback, can be
> of any type though, can be DONTCACHE or normal (non-dontcache) dirty
> pages. IIUC, writeback doesn't distinguish between them while writing.
>

Correct. This was the approach that Jan and HCH suggested in the
responses to the last posting.

>
> IMO, what we could also include in the commit msg is why is this above
> approach taken? IIUC, that is because, by writing NR_DONTCACHE_DIRTY
> pages, it still reduces the page cache pressure and still reduces the
> amount of work that the reclaim has to do, even though some of those
> pages maybe non-dontcache pages, in case if there was a parallel
> buffered write in the system.
>

Good suggestion. I'll add that.

>
> Also should the following change be documented somewhere? Like in Man
> page maybe? i.e.
> Earlier RWF_DONTCACHE writes made sure that those dirty pages are
> immediately submitted for writeback and completion would release those
> pages. But now, in certain cases when there is a mixed buffered write in
> the system, those dontcache dirty pages might be written back after a
> delay (whenever the next time writeback kicks in).
> However for RWF_DONTCACHE reads, it should not affect anything.
>

Looks like DONTCACHE is documented in the preadv/writev manpage. Here's
the current blurb about writes:

Additionally, any range dirtied by a write operation with RWF_DONT‐
CACHE set will get kicked off for writeback. This is similar to
calling sync_file_range(2) with SYNC_FILE_RANGE_WRITE to start
writeback on the given range. RWF_DONTCACHE is a hint, or best ef‐
fort, where no hard guarantees are given on the state of the page
cache once the operation completes.

I don't think this verbiage is invalid after this change. Kicking off
writeback is still just a hint, like it was before. We could mention
about how that I/O can compete with regular buffered I/O, but it seems
a bit like we're adding info that will just be confusing for users.

> > Like WB_start_all, the WB_start_dontcache bit coalesces multiple
> > DONTCACHE writes into a single flusher wakeup without per-write
> > allocations.
> >
> > Also add WB_REASON_DONTCACHE as a new writeback reason for tracing
> > visibility, and target the correct cgroup writeback domain via
> > unlocked_inode_to_wb_begin().
> >
> > dontcache-bench results on dual-socket Xeon Gold 6138 (80 CPUs, 256 GB
> > RAM, Samsung MZ1LB1T9HALS 1.7 TB NVMe, local XFS, io_uring, file size
> > ~503 GB, compared to a v6.19-ish baseline):
> >
>
> Can we please also test parallel buffered writes and dontcache writes?
> Since this patch series definitely affects that.
>
> BTW - adding these numbers in the commit msg itself is much helpful.
>

To be clear, this only affects DONTCACHE, not normal buffered writes,
but I guess you're referring to the fact that DONTCACHE and buffered
writes can compete now.

Can you clarify specifically what you'd like me to test here? Are you
saying you want me to test parallel and buffered writes together at the
same time (i.e. make them compete?).

I should be able to do that for the local benchmarks, but nfsd's iomode
settings are global and that won't be possible there.

> > Single-client sequential write (MB/s):
> > baseline patched change
> > buffered 1449.8 1440.1 -0.7%
> > dontcache 1347.9 1461.5 +8.4%
> > direct 1450.0 1440.1 -0.7%
> >
> > Single-client sequential write latency (us):
> > baseline patched change
> > dontcache p50 3031.0 10551.3 +248.1%
> > dontcache p99 74973.2 21626.9 -71.2%
> > dontcache p99.9 85459.0 23199.7 -72.9%
> >
> > Single-client random write (MB/s):
> > baseline patched change
> > dontcache 284.2 295.4 +3.9%
> >
> > Single-client random write p99.9 latency (us):
> > baseline patched change
> > dontcache 2277.4 872.4 -61.7%
> >
> > Multi-writer aggregate throughput (MB/s):
>
> Can you please help describe this test scenario if possible.. In above
> you mentioned we are writing file_size as 2x RAM_SIZE. But your
> multi-client tests says something else..
>
> local num_clients=4
> + mem_kb=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
> + client_size="$(( mem_kb / 1024 / num_clients ))M"
>
> Also the multi-writer case is spawning parallel fio jobs, and then
> parsing and aggregating the bandwidth results instead of using fio to
> spawn multiple parallel threads... which is ok, but a bit wierd.
> Why not let fio do the aggregate bandwidth, and latency calculation
> instead?
>

That's what I get for asking Claude to roll a testsuite. I'm not that
well-versed in fio, but that makes sense. I'll have a look at reworking
it along those lines.

> > baseline patched change
> > buffered 1619.5 1611.2 -0.5%
> > dontcache 1281.1 1629.4 +27.2%
> > direct 1545.4 1609.4 +4.1%
> >
> > Mixed-mode noisy neighbor (dontcache writer + buffered readers):
> > baseline patched change
> > writer (MB/s) 1297.6 1471.1 +13.4%
> > readers avg (MB/s) 855.0 462.4 -45.9%
> >
> > nfsd-io-bench results on same hardware (XFS on NVMe, NFSv3 via fio
> > NFS engine with libnfs, 1024 NFSD threads, pool_mode=pernode,
> > file size ~502 GB, compared to v6.19-ish baseline):
> >
> > Single-client sequential write (MB/s):
> > baseline patched change
> > buffered 4844.2 4653.4 -3.9%
> > dontcache 3028.3 3723.1 +22.9%
> > direct 957.6 987.8 +3.2%
> >
> > Single-client sequential write p99.9 latency (us):
> > baseline patched change
> > dontcache 759169.0 175112.2 -76.9%
> >
> > Single-client random write (MB/s):
> > baseline patched change
> > dontcache 590.0 1561.0 +164.6%
> >
> > Multi-writer aggregate throughput (MB/s):
> > baseline patched change
> > buffered 9636.3 9422.9 -2.2%
> > dontcache 1894.9 9442.6 +398.3%
> > direct 809.6 975.1 +20.4%
> >
> > Noisy neighbor (dontcache writer + random readers):
> > baseline patched change
> > writer (MB/s) 1854.5 4063.6 +119.1%
> > readers avg (MB/s) 131.2 101.6 -22.5%
> >
> > The NFS results show even larger improvements than the local benchmarks.
> > Multi-writer dontcache throughput improves nearly 5x, matching buffered
> > I/O. Dirty page footprint drops 85-95% in sequential workloads vs.
> > buffered.
> >
>
> Nice :)
> Some explaination here of why 5x improvement with NFS compared to local
> filesystems please?
> (I am not much aware of NFS side, but a possible reasoning would help)
>

I suspect that it's because of the "scattered" nature of nfsd writes.
When the client sends a write to nfsd, we wake a nfsd thread to service
it. So, if there are a lot of writes operating in parallel, they all
get done in the context of different tasks.

My hunch is that this I/O pattern (writing to same file from a bunch of
different threads), particularly suffers from the DONTCACHE inline
write behavior. The threads all end up competing to submit jobs to the
queue and that causes the performance to fall off sharply.

Thanks for the review!
--
Jeff Layton <jlayton@kernel.org>