* [PATCH v3] fuse: back uncached readdir buffers with pages
@ 2026-05-19 0:47 Matthew R. Ochs
2026-05-19 10:04 ` Miklos Szeredi
0 siblings, 1 reply; 6+ messages in thread
From: Matthew R. Ochs @ 2026-05-19 0:47 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: Bernd Schubert, linux-fsdevel, linux-kernel, stable
Commit dabb90391028 ("fuse: increase readdir buffer size") changed
fuse_readdir_uncached() to size its temporary buffer from ctx->count.
This is useful for overlayfs and other in-kernel callers that use
INT_MAX to indicate an unlimited directory read.
The buffer is capped by fc->max_pages converted to bytes with PAGE_SIZE.
However, fc->max_pages is a page-count limit, not a byte-sized payload
limit. READDIR is a read-side operation, so include fc->max_read in the
cap. Also keep fc->max_write in the cap: it is the daemon-advertised
byte-sized payload limit relevant to virtiofs in the failing
configuration, while fc->max_read can remain effectively unlimited there.
The larger buffer is also currently supplied as a kvec output argument.
For virtiofs, kvec arguments are copied through req->argbuf, which is
allocated with kmalloc(..., GFP_ATOMIC). A large readdir buffer can
therefore require a multi-megabyte contiguous atomic allocation and fail
with -ENOMEM.
This was observed with a 64K-page guest on a 4K-page host, using an
overlayfs mount whose lower directory is on virtiofs. Reading a merged
directory through overlayfs failed with:
ls: reading directory '<path>': Cannot allocate memory
Avoid the oversized request and the large bounce-buffer allocation by
capping the requested byte size by fc->max_pages, fc->max_read, and
fc->max_write, then backing the uncached readdir output with pages and
setting out_pages. The virtiofs transport can then pass the pages as
scatter-gather entries instead of copying the output through argbuf.
Map the pages with vm_map_ram() only while parsing the returned dirents,
so the existing parser can continue to operate on a linear kernel mapping.
Fixes: dabb90391028 ("fuse: increase readdir buffer size")
Cc: stable@vger.kernel.org
Signed-off-by: Matthew R. Ochs <mochs@nvidia.com>
---
v3:
- Cap the requested byte size by fc->max_read in addition to fc->max_pages
and fc->max_write.
- Use clamp_t(size_t, ...) for the readdir buffer size calculation.
- Use __free(kvfree) for the temporary page pointer array.
- Use release_pages() for pages allocated by alloc_pages_bulk().
- Handle partial alloc_pages_bulk() success by shrinking the request size.
- Verified with --overlay-rwdir across 4K/64K host and guest page sizes.
- Link to v2: https://lore.kernel.org/all/20260428233028.2747981-1-mochs@nvidia.com/
v2:
- Reworked uncached readdir to use output pages and out_pages, per Miklos.
- Cap the requested byte size by both fc->max_pages and fc->max_write.
- Map pages with vm_map_ram() only while parsing returned dirents.
- Verified with --overlay-rwdir across 4K/64K host and guest page sizes.
- Link to v1: https://lore.kernel.org/all/20260428021304.2338592-1-mochs@nvidia.com/
fs/fuse/readdir.c | 64 +++++++++++++++++++++++++++++++++++++++--------
1 file changed, 54 insertions(+), 10 deletions(-)
diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c
index db5ae8ec1030..8116688fe5b2 100644
--- a/fs/fuse/readdir.c
+++ b/fs/fuse/readdir.c
@@ -12,6 +12,7 @@
#include <linux/posix_acl.h>
#include <linux/pagemap.h>
#include <linux/highmem.h>
+#include <linux/vmalloc.h>
static bool fuse_use_readdirplus(struct inode *dir, struct dir_context *ctx)
{
@@ -343,17 +344,48 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx)
struct fuse_mount *fm = get_fuse_mount(inode);
struct fuse_conn *fc = fm->fc;
struct fuse_io_args ia = {};
- struct fuse_args *args = &ia.ap.args;
+ struct fuse_args_pages *ap = &ia.ap;
+ struct fuse_args *args = &ap->args;
+ struct page **pages __free(kvfree) = NULL;
void *buf;
- size_t bufsize = clamp((unsigned int) ctx->count, PAGE_SIZE, fc->max_pages << PAGE_SHIFT);
+ size_t max_bufsize = min3((size_t)fc->max_pages << PAGE_SHIFT,
+ (size_t)fc->max_read,
+ (size_t)fc->max_write);
+ size_t bufsize = clamp_t(size_t, ctx->count, PAGE_SIZE, max_bufsize);
+ unsigned int nr_pages = DIV_ROUND_UP(bufsize, PAGE_SIZE);
u64 attr_version = 0, evict_ctr = 0;
bool locked;
+ unsigned int nr_alloc;
+ unsigned int i;
- buf = kvmalloc(bufsize, GFP_KERNEL);
- if (!buf)
+ pages = kvcalloc(nr_pages, sizeof(*pages), GFP_KERNEL);
+ if (!pages)
return -ENOMEM;
- args->out_args[0].value = buf;
+ nr_alloc = alloc_pages_bulk(GFP_KERNEL, nr_pages, pages);
+ if (!nr_alloc) {
+ res = -ENOMEM;
+ goto out;
+ }
+ if (nr_alloc < nr_pages) {
+ nr_pages = nr_alloc;
+ bufsize = (size_t)nr_pages << PAGE_SHIFT;
+ }
+
+ ap->folios = fuse_folios_alloc(nr_pages, GFP_KERNEL, &ap->descs);
+ if (!ap->folios) {
+ res = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < nr_pages; i++) {
+ ap->folios[i] = page_folio(pages[i]);
+ ap->descs[i].length = min_t(size_t,
+ bufsize - (size_t)i * PAGE_SIZE,
+ PAGE_SIZE);
+ }
+ ap->num_folios = nr_pages;
+ args->out_pages = true;
plus = fuse_use_readdirplus(inode, ctx);
if (plus) {
@@ -372,16 +404,28 @@ static int fuse_readdir_uncached(struct file *file, struct dir_context *ctx)
if (ff->open_flags & FOPEN_CACHE_DIR)
fuse_readdir_cache_end(file, ctx->pos);
- } else if (plus) {
- res = parse_dirplusfile(buf, res, file, ctx, attr_version,
- evict_ctr);
} else {
- res = parse_dirfile(buf, res, file, ctx);
+ buf = vm_map_ram(pages, nr_pages, -1);
+ if (!buf) {
+ res = -ENOMEM;
+ } else {
+ if (plus)
+ res = parse_dirplusfile(buf, res, file, ctx,
+ attr_version,
+ evict_ctr);
+ else
+ res = parse_dirfile(buf, res, file, ctx);
+
+ vm_unmap_ram(buf, nr_pages);
+ }
}
}
- kvfree(buf);
fuse_invalidate_atime(inode);
+
+out:
+ kfree(ap->folios);
+ release_pages(pages, nr_alloc);
return res;
}
--
2.50.1
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] fuse: back uncached readdir buffers with pages
2026-05-19 0:47 [PATCH v3] fuse: back uncached readdir buffers with pages Matthew R. Ochs
@ 2026-05-19 10:04 ` Miklos Szeredi
2026-05-19 15:34 ` Matt Ochs
0 siblings, 1 reply; 6+ messages in thread
From: Miklos Szeredi @ 2026-05-19 10:04 UTC (permalink / raw)
To: Matthew R. Ochs; +Cc: Bernd Schubert, linux-fsdevel, linux-kernel, stable
On Tue, 19 May 2026 at 02:47, Matthew R. Ochs <mochs@nvidia.com> wrote:
> This was observed with a 64K-page guest on a 4K-page host, using an
> overlayfs mount whose lower directory is on virtiofs. Reading a merged
> directory through overlayfs failed with:
>
> ls: reading directory '<path>': Cannot allocate memory
IDGI, the patch makes FUSE_READDIR supply an array of folios.
Virtiofs shouldn't need to allocate a large argbuf after that.
What am I missing?
Thanks,
Miklos
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] fuse: back uncached readdir buffers with pages
2026-05-19 10:04 ` Miklos Szeredi
@ 2026-05-19 15:34 ` Matt Ochs
2026-05-19 15:41 ` Miklos Szeredi
0 siblings, 1 reply; 6+ messages in thread
From: Matt Ochs @ 2026-05-19 15:34 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: Bernd Schubert, linux-fsdevel, linux-kernel, stable
> On May 19, 2026, at 05:04, Miklos Szeredi <miklos@szeredi.hu> wrote:
>
> On Tue, 19 May 2026 at 02:47, Matthew R. Ochs <mochs@nvidia.com> wrote:
>
>> This was observed with a 64K-page guest on a 4K-page host, using an
>> overlayfs mount whose lower directory is on virtiofs. Reading a merged
>> directory through overlayfs failed with:
>>
>> ls: reading directory '<path>': Cannot allocate memory
>
> IDGI, the patch makes FUSE_READDIR supply an array of folios.
> Virtiofs shouldn't need to allocate a large argbuf after that.
>
> What am I missing?
You're right. After switching READDIR to out_pages, the large READDIR
reply payload should no longer be copied through req->argbuf. My commit
message conflated that with the separate request-size issue.
What I saw while testing the page-backed version was that the READDIR
size was still derived from fc->max_pages using the guest PAGE_SIZE. On
the failing 4K host / 64K guest setup that produced:
PAGE_SIZE=65536 max_pages=124 max_read=UINT_MAX max_write=1048576
bufsize=8126464 nr_pages=124
With out_pages but without the byte-size cap, fuse_simple_request()
still returned -ENOMEM. Capping the request to 1048576 bytes / 16 pages
made the same test pass.
So the out_pages change fixes the large reply-payload argbuf problem.
The fc->max_read/fc->max_write cap is addressing the separate issue that
fc->max_pages is only a page-count limit and can translate to an
oversized READDIR byte count when the client PAGE_SIZE differs.
I can rework the commit message to make that distinction clear.
-matt
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] fuse: back uncached readdir buffers with pages
2026-05-19 15:34 ` Matt Ochs
@ 2026-05-19 15:41 ` Miklos Szeredi
2026-05-22 1:32 ` Matt Ochs
0 siblings, 1 reply; 6+ messages in thread
From: Miklos Szeredi @ 2026-05-19 15:41 UTC (permalink / raw)
To: Matt Ochs; +Cc: Bernd Schubert, linux-fsdevel, linux-kernel, stable
On Tue, 19 May 2026 at 17:34, Matt Ochs <mochs@nvidia.com> wrote:
> With out_pages but without the byte-size cap, fuse_simple_request()
> still returned -ENOMEM. Capping the request to 1048576 bytes / 16 pages
> made the same test pass.
Can you tell why is it failing now?
> I can rework the commit message to make that distinction clear.
Please remove the request size cap from this patch. We need to
discuss fixing that properly: limiting by max_write, while might seem
to make the failure go away, is conceptually wrong.
Thanks,
Miklos
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] fuse: back uncached readdir buffers with pages
2026-05-19 15:41 ` Miklos Szeredi
@ 2026-05-22 1:32 ` Matt Ochs
2026-06-10 12:22 ` Miklos Szeredi
0 siblings, 1 reply; 6+ messages in thread
From: Matt Ochs @ 2026-05-22 1:32 UTC (permalink / raw)
To: Miklos Szeredi; +Cc: Bernd Schubert, linux-fsdevel, linux-kernel, stable
Hi Miklos,
> On May 19, 2026, at 10:41, Miklos Szeredi <miklos@szeredi.hu> wrote:
>
> On Tue, 19 May 2026 at 17:34, Matt Ochs <mochs@nvidia.com> wrote:
>
>> With out_pages but without the byte-size cap, fuse_simple_request()
>> still returned -ENOMEM. Capping the request to 1048576 bytes / 16 pages
>> made the same test pass.
>
> Can you tell why is it failing now?
I tracked down where the remaining -ENOMEM comes from.
For the failing READDIR request, kernel-side instrumentation shows:
READDIR out_size=8126464 num_folios=124 total_sgs=127
copy_args_to_argbuf: num_in=1 num_out=0 len=40
virtqueue_add_sgs: ok
completion: out_error=-12 out_len=16
So the output is page backed, the argbuf only contains the 40-byte input
argument, and the virtqueue submission succeeds. The -ENOMEM is coming back
from virtiofsd.
The remaining failure is the virtiofsd READDIR/READDIRPLUS range check.
Virtiofsd advertises:
max_write = MAX_BUFFER_SIZE
max_pages = ceil(MAX_BUFFER_SIZE / host_page_size)
and then rejects READDIR/READDIRPLUS if the requested size is larger than
MAX_BUFFER_SIZE.
On the failing setup the host is 4K and the guest is 64K. The guest ends up
with fc->max_pages=124 after the virtqueue-size cap, so uncached READDIR asks
for:
124 * 65536 = 8126464 bytes
That exceeds virtiofsd's MAX_BUFFER_SIZE of 1048576 bytes, so virtiofsd
returns ENOMEM before doing the directory read. On a 64K host this is masked
because virtiofsd advertises max_pages=16, keeping the guest request at 1MiB.
Given that, I agree the generic FUSE patch should not include the max_write
cap. I'll send a v4 that only backs uncached readdir with output pages.
For the remaining virtiofsd issue, does capping the local READDIR response
size in virtiofsd sound like the right direction? READDIR can return less
than requested, so treating MAX_BUFFER_SIZE as the maximum chunk to produce
seems preferable to rejecting an otherwise valid request.
-matt
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] fuse: back uncached readdir buffers with pages
2026-05-22 1:32 ` Matt Ochs
@ 2026-06-10 12:22 ` Miklos Szeredi
0 siblings, 0 replies; 6+ messages in thread
From: Miklos Szeredi @ 2026-06-10 12:22 UTC (permalink / raw)
To: Matt Ochs; +Cc: Bernd Schubert, linux-fsdevel, linux-kernel, stable
On Fri, 22 May 2026 at 03:32, Matt Ochs <mochs@nvidia.com> wrote:
> For the remaining virtiofsd issue, does capping the local READDIR response
> size in virtiofsd sound like the right direction? READDIR can return less
> than requested, so treating MAX_BUFFER_SIZE as the maximum chunk to produce
> seems preferable to rejecting an otherwise valid request.
Right. The server has every right to return a short count on a
READDIR request, so capping it at MAX_BUFFER_SIZE is fine.
Thanks,
Miklos
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-06-10 12:22 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-19 0:47 [PATCH v3] fuse: back uncached readdir buffers with pages Matthew R. Ochs
2026-05-19 10:04 ` Miklos Szeredi
2026-05-19 15:34 ` Matt Ochs
2026-05-19 15:41 ` Miklos Szeredi
2026-05-22 1:32 ` Matt Ochs
2026-06-10 12:22 ` Miklos Szeredi
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®