[PATCH v2] fuse: allow larger read requests by setting bdi->io_pages

From: Jim Harris

Date: Mon Jun 22 2026 - 20:43:00 EST


A FUSE server that advertises a large max_pages and max_write (e.g.
max_pages=256, max_write=1MB) cannot currently obtain matching
FUSE_READ request sizes from the kernel. Buffered sequential writes
arrive at the server at the negotiated max_write size, but a large
buffered read() is split into several smaller FUSE_READ requests.

For a buffered read, filemap_get_pages() -> page_cache_sync_ra() sizes
the read against ractl_max_pages():

max_pages = ractl->ra->ra_pages;
if (req_size > max_pages && bdi->io_pages > max_pages)
max_pages = min(req_size, bdi->io_pages);

fuse leaves bdi->io_pages at the default VM_READAHEAD_PAGES (128KB), so
a 1MB read() (req_size = 256 pages) is clamped to the readahead window
(128KB, or 256KB for POSIX_FADV_SEQUENTIAL), producing four 256KB
FUSE_READ round-trips instead of one.

Set bdi->io_pages to fc->max_pages after feature negotiation. As the
code above shows, io_pages only raises the limit when the request size
already exceeds the readahead window, so it enlarges explicitly
requested reads without enlarging the speculative readahead window.
This avoids increasing speculative page-cache readahead on behalf of
an unprivileged server. NFS does the same, setting io_pages from
rpages while leaving ra_pages at the default.

fc->max_pages is already bounded by fc->max_pages_limit (and, for
virtio-fs, by the virtqueue descriptor count), so io_pages inherits
the same bound.

Suggested-by: Joanne Koong <joannelkoong@xxxxxxxxx>
Signed-off-by: Jim Harris <jim.harris@xxxxxxxxxx>
Assisted-by: Cursor:claude-opus-4.8
---
Changes since v1 [1]:
- Set bdi->io_pages instead of bdi->ra_pages, per Joanne's review.
Raising ra_pages would also enlarge the speculative readahead
window for all workloads (including small sequential reads),
increasing page-cache readahead on behalf of the server. Setting
io_pages enlarges only explicitly requested reads larger than the
readahead window, leaving speculative readahead unchanged.

[1] https://lore.kernel.org/linux-fsdevel/20260602204430.18427-1-jim.harris@xxxxxxxxxx/

Notes on AI assistance:

The mm/readahead analysis (confirming ractl_max_pages() consults
io_pages only when the request exceeds the readahead window) and the
bulk of this changelog were drafted with an AI coding assistant (see
Assisted-by trailer). The one-line code change was reviewed by me.
The motivating performance observation was made by me on a real
virtio-fs workload, and verification was performed by me.

fs/fuse/inode.c | 1 +
1 file changed, 1 insertion(+)

diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index deddfffb037f..c8373e537d2b 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -1495,6 +1495,7 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,

fm->sb->s_bdi->ra_pages =
min(fm->sb->s_bdi->ra_pages, ra_pages);
+ fm->sb->s_bdi->io_pages = fc->max_pages;
fc->minor = arg->minor;
fc->max_write = arg->minor < 5 ? 4096 : arg->max_write;
fc->max_write = max_t(unsigned, 4096, fc->max_write);
--
2.43.0