From: Pedro Falcato <pfalcato@suse.de>
To: Salvatore Dipietro <dipiets@amazon.com>
Cc: jack@suse.cz, abuehaze@amazon.com, agruenba@redhat.com,
akpm@linux-foundation.org, alisaidi@amazon.com,
almaz.alexandrovich@paragon-software.com, apopple@nvidia.com,
blakgeof@amazon.com, brauner@kernel.org, byungchul@sk.com,
cem@kernel.org, david@kernel.org, dipietro.salvatore@gmail.com,
dipiets@amazon.it, djwong@kernel.org, dlemoal@kernel.org,
exfat@lists.linux.dev, fuse-devel@lists.linux.dev,
gfs2@lists.linux.dev, gourry@gourry.net, hyc.lee@gmail.com,
joshua.hahnjy@gmail.com, jth@kernel.org, liam@infradead.org,
linkinjeon@kernel.org, linux-fsdevel@vger.kernel.org,
linux-kernel@vger.kernel.org, linux-mm@kvack.org,
linux-xfs@vger.kernel.org, ljs@kernel.org,
matthew.brost@intel.com, mhocko@suse.com, miklos@szeredi.hu,
naohiro.aota@wdc.com, ntfs3@lists.linux.dev,
ntfs@lists.linux.dev, rakie.kim@sk.com, ritesh.list@gmail.com,
rppt@kernel.org, rvvandan@amazon.com, ryan.roberts@arm.com,
sj1557.seo@samsung.com, surenb@google.com, vbabka@kernel.org,
viro@zeniv.linux.org.uk, willy@infradead.org,
ying.huang@linux.alibaba.com, yuezhang.mo@sony.com,
ziy@nvidia.com, kirill@shutemov.name, dgc@kernel.org
Subject: Re: [PATCH 0/2] mm, iomap: account dirty at page granularity for per-block dirty folios
Date: Fri, 13 Nov 2026 19:13:07 +0000 [thread overview]
Message-ID: <avdg_Q08T-ilyNmK@pedro-suse.tail5790ac.ts.net> (raw)
In-Reply-To: <20261113155015.516481-1-dipiets@amazon.com>
On Fri, Nov 13, 2026 at 03:50:15PM +0000, Salvatore Dipietro wrote:
> Hi all,
>
> Thanks Jan, Pedro, Dave, David and Kiryl for the feedback.
>
> Following Dave's question about how PostgreSQL gets large folios on
> files it writes 8 KiB at a time, we looked deeper and found a
> reproducer that needs only fio and a single disk.
>
> What happens:
>
> 1. A buffered sequential read (VACUUM, a backup, a scan) builds large
> folios through readahead. They stay large in the page cache until
> they are evicted.
> 2. The first small write into one of these clean folios charges the
> whole folio as dirty: a 4 KiB write into a 256 KiB folio charges
> 256 KiB, while writeback only writes the dirty 4 KiB.
> 3. With random writes, most writes land in a different clean folio,
> so the dirty count reaches the limits with little real data.
> balance_dirty_pages() then throttles the writers, and they end up
> paced by how many folios writeback can clean per second rather
> than by the bytes to write.
>
> Reproducer: XFS on one EBS gp3 volume (3000 IOPS), m8g.8xlarge
> (32 CPUs, 128 GiB), default read_ahead_kb=128. fio's 1 MiB reads grow
> the readahead window up to the device's max_sectors_kb (256 KiB), so
> the read builds 256 KiB folios.
>
> ; create.fio: 16 x 3 GiB files, direct I/O so nothing is cached
> [global]
> directory=/mnt/xfs/fio
> filename_format=f.$jobnum
> size=3072m
> numjobs=16
> group_reporting=1
> [create]
> rw=write
> bs=1m
> direct=1
> ioengine=libaio
> iodepth=16
>
> ; load.fio: buffered sequential read; builds the large folios
> [global]
> directory=/mnt/xfs/fio
> filename_format=f.$jobnum
> size=3072m
> numjobs=16
> group_reporting=1
> [load]
> rw=read
> bs=1m
> direct=0
> ioengine=psync
>
> ; randwrite.fio: random 4 KiB buffered writes, 32 x 312 = ~10k IOPS
> ; invalidate=0 keeps the cached folios when fio opens the files
> [randwrite]
> directory=/mnt/xfs/fio
> filename=f.0:f.1:f.2:f.3:f.4:f.5:f.6:f.7:f.8:f.9:f.10:f.11:f.12:f.13:f.14:f.15
> rw=randwrite
> bs=4k
> direct=0
> ioengine=psync
> invalidate=0
> numjobs=32
> rate_iops=312
> time_based=1
> runtime=120
> group_reporting=1
>
> To reproduce it, run (as root):
>
> fio create.fio
> sync; echo 3 > /proc/sys/vm/drop_caches
> fio load.fio # skip this step for the control run
> fio randwrite.fio
>
> fio asks for ~10k write IOPS, more than the volume's 3000, so dirty
> data builds up in both runs. In the control run (no load step) each
> 4 KiB write creates a 4 KiB folio: after 120 s only 3.3G is dirty,
> below the 12.2G background threshold, and nothing is throttled. After
> the load step the same writes land in 256 KiB folios, and each write
> into a clean folio is charged 256 KiB: the dirty count passes the
> background threshold after 6 s and writers are throttled from 13 s.
>
> Results on v7.4-rc2, one run per kernel, each on a fresh instance.
> Writer sleep is the total balance_dirty_pages() pause time over the
> 32 writers during the 120 s write phase:
>
> write p99 / max writer max folio
> IOPS lat ms sleep s dirty size
> baseline, control 9,984 0.01 / 0.30 0 3.3G -
> baseline 4,061 60 / 160 179 23.5G 256K
> v1 (this series) 9,984 0.01 / 0.67 0 3.6G 256K
> max order cap 64K 7,071 50 / 280 73 22.3G 64K
> max order cap 16K 9,984 0.02 / 0.05 0 11.9G 16K
> split on write 9,984 0.02 / 0.17 0 3.6G 256K
> evict on write 9,984 0.03 / 0.19 0 3.3G 256K
> POSIX_FADV_RANDOM 9,984 0.02 / 0.05 0 3.3G 4K
>
> The control run gave 9,984 IOPS and max latency up to 0.30 ms on every
> kernel.
>
> - max order cap: the global cap in mapping_max_folio_order() that Jan
> suggested trying. At 64 KiB a 4 KiB write still charges 16x and is
> throttled. At 16 KiB this run is not throttled, but dirty reached
> 11.9G, just below the 12.2G background threshold. The cap that
> avoids throttling depends on the write size, and it removes large
> folios for every file, including ones that never see small writes.
> - split on write: when a buffered write (iomap opts in, not for
> zeroing or unshare) finds a clean, uptodate, unmapped folio at least
> 4x the write size, folio_split() it down to the write's order before
> dirtying it. The rest of the folio stays cached in large folios.
> On the concerns Jan raised about splitting [1]: split on write only
> splits clean, uptodate, unmapped folios, so it needs no new
> filesystem state beyond an opt-in flag in iomap, and if
> folio_split() fails the write simply proceeds as today; evict on
> write avoids splitting altogether.
> - evict on write: same trigger, but mapping_evict_folio() the folio
> instead of splitting it (~40 lines). This discards the folio's
> other cached data; nothing reads it back in this test, so the read
> cost does not show here.
> - POSIX_FADV_RANDOM (Dave's question): fio's fadvise_hint=random on
> the load job. It avoids the problem without a kernel change, but
> only where the application opts in: the hint is per open file, so
> every process that reads a file must issue it, and a reader that
> doesn't (a backup or copy tool, for example) still builds large
> folios. Every application that reads files sequentially and then
> rewrites them in place would have to adopt it, and keeps the
> regression until it does. It also turns readahead off for those
> files, so their sequential reads lose readahead.
Holy heck, did an LLM write this?
>
> Split and evict on write keep whole-folio accounting and act at write
> time, so they also cover large folios already in the page cache,
> whoever built them, without each application having to opt in. Both
> only touch clean, uptodate, unmapped folios at least 4x the write
> size, and only on iomap buffered writes. Split keeps the rest of the
> folio cached at the cost of the split; evict is simpler but drops it.
> I'd appreciate your opinion on these options, or any other direction
> you think is more appropriate.
I think you'll face strong resistance to any of these options.
--
Pedro
next prev parent reply other threads:[~2026-11-13 19:13 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-27 11:27 Salvatore Dipietro
2026-10-27 11:27 ` [PATCH 1/2] mm: add infrastructure for per-dirty-page accounting Salvatore Dipietro
2026-10-27 11:27 ` [PATCH 2/2] mm, iomap: account dirty at page granularity for per-block dirty folios Salvatore Dipietro
2026-10-27 12:22 ` [PATCH 0/2] " David Hildenbrand (Arm)
2026-10-27 13:45 ` Kiryl Shutsemau
2026-10-27 13:59 ` Pedro Falcato
2026-11-03 11:47 ` Jan Kara
2026-11-13 15:50 ` Salvatore Dipietro
2026-11-13 19:13 ` Pedro Falcato [this message]
2026-10-27 23:10 ` [syzbot ci] " syzbot ci
2026-10-28 21:59 ` [PATCH 0/2] " Dave Chinner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=avdg_Q08T-ilyNmK@pedro-suse.tail5790ac.ts.net \
--to=pfalcato@suse.de \
--cc=abuehaze@amazon.com \
--cc=agruenba@redhat.com \
--cc=akpm@linux-foundation.org \
--cc=alisaidi@amazon.com \
--cc=almaz.alexandrovich@paragon-software.com \
--cc=apopple@nvidia.com \
--cc=blakgeof@amazon.com \
--cc=brauner@kernel.org \
--cc=byungchul@sk.com \
--cc=cem@kernel.org \
--cc=david@kernel.org \
--cc=dgc@kernel.org \
--cc=dipietro.salvatore@gmail.com \
--cc=dipiets@amazon.com \
--cc=dipiets@amazon.it \
--cc=djwong@kernel.org \
--cc=dlemoal@kernel.org \
--cc=exfat@lists.linux.dev \
--cc=fuse-devel@lists.linux.dev \
--cc=gfs2@lists.linux.dev \
--cc=gourry@gourry.net \
--cc=hyc.lee@gmail.com \
--cc=jack@suse.cz \
--cc=joshua.hahnjy@gmail.com \
--cc=jth@kernel.org \
--cc=kirill@shutemov.name \
--cc=liam@infradead.org \
--cc=linkinjeon@kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linux-xfs@vger.kernel.org \
--cc=ljs@kernel.org \
--cc=matthew.brost@intel.com \
--cc=mhocko@suse.com \
--cc=miklos@szeredi.hu \
--cc=naohiro.aota@wdc.com \
--cc=ntfs3@lists.linux.dev \
--cc=ntfs@lists.linux.dev \
--cc=rakie.kim@sk.com \
--cc=ritesh.list@gmail.com \
--cc=rppt@kernel.org \
--cc=rvvandan@amazon.com \
--cc=ryan.roberts@arm.com \
--cc=sj1557.seo@samsung.com \
--cc=surenb@google.com \
--cc=vbabka@kernel.org \
--cc=viro@zeniv.linux.org.uk \
--cc=willy@infradead.org \
--cc=ying.huang@linux.alibaba.com \
--cc=yuezhang.mo@sony.com \
--cc=ziy@nvidia.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®