From: Luis Henriques <luis@igalia.com>
To: Jun Wu <quark@lihdd.net>
Cc: fuse-devel@lists.linux.dev, miklos@szeredi.hu, Jun Wu <quark@meta.com>
Subject: Re: [PATCH v2] fuse: invalidate readdir cache on epoch bump
Date: Fri, 15 May 2026 09:53:27 +0100 [thread overview]
Message-ID: <8733ztt5o8.fsf@igalia.com> (raw)
In-Reply-To: <20260515001412.1270472-1-quark@lihdd.net> (Jun Wu's message of "Thu, 14 May 2026 17:14:12 -0700")
On Thu, May 14 2026, Jun Wu wrote:
> From: Jun Wu <quark@meta.com>
>
> FUSE_NOTIFY_INC_EPOCH invalidates dentries, but does not invalidate cached
> readdir results. A process with cwd inside a FUSE mount can therefore
> observe stale readdir(".") output after an epoch bump.
>
> Fix this by recording epoch in the readdir cache and checking it on reuse.
>
> Minimal reproducer:
>
> - mount a tiny FUSE fs with an empty root directory
> - on opendir, enable fi->cache_readdir and fi->keep_cache
> - chdir into the mount and call readdir(".") to populate readdir cache
> - make the FUSE server report one file in the root directory
> - send only FUSE_NOTIFY_INC_EPOCH
> - call readdir(".") again; before this change it stays stale, after this
> change it sees the new file
>
> Fixes: 2396356a945b ("fuse: add more control over cache invalidation behaviour")
> Signed-off-by: Jun Wu <quark@meta.com>
Nice catch! The fix looks good to me, so feel free to add my
Reviewed-by: Luis Henriques <luis@igalia.com>
Cheers,
--
Luís
> ---
> Changes in v2:
> - Addresses Joanne Koong's suggestions:
> - Drop the FUSE_EPOCH_READDIR init flag.
> - Add Fixes tag.
> - Rebase onto current mainline.
> ---
> fs/fuse/dev.c | 6 +++---
> fs/fuse/fuse_i.h | 3 +++
> fs/fuse/readdir.c | 5 ++++-
> 3 files changed, 10 insertions(+), 4 deletions(-)
>
> diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
> index 5dda7080f4a9..35e04b064f37 100644
> --- a/fs/fuse/dev.c
> +++ b/fs/fuse/dev.c
> @@ -2038,9 +2038,9 @@ static int fuse_notify_resend(struct fuse_conn *fc)
> }
>
> /*
> - * Increments the fuse connection epoch. This will result of dentries from
> - * previous epochs to be invalidated. Additionally, if inval_wq is set, a work
> - * queue is scheduled to trigger the invalidation.
> + * Increments the fuse connection epoch. This will cause dentries and
> + * readdir caches from previous epochs to be invalidated. Additionally,
> + * if inval_wq is set, a work queue is scheduled to trigger the invalidation.
> */
> static int fuse_notify_inc_epoch(struct fuse_conn *fc)
> {
> diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
> index 17423d4e3cfa..aa8d6dfbbcc7 100644
> --- a/fs/fuse/fuse_i.h
> +++ b/fs/fuse/fuse_i.h
> @@ -191,6 +191,9 @@ struct fuse_inode {
> /* iversion of directory when cache was started */
> u64 iversion;
>
> + /* epoch of fc when cache was started */
> + int epoch;
> +
> /* protects above fields */
> spinlock_t lock;
> } rdc;
> diff --git a/fs/fuse/readdir.c b/fs/fuse/readdir.c
> index db5ae8ec1030..b18d971e2bb4 100644
> --- a/fs/fuse/readdir.c
> +++ b/fs/fuse/readdir.c
> @@ -442,6 +442,7 @@ static void fuse_rdc_reset(struct inode *inode)
> fi->rdc.version++;
> fi->rdc.size = 0;
> fi->rdc.pos = 0;
> + fi->rdc.epoch = 0;
> }
>
> #define UNCACHED 1
> @@ -483,6 +484,7 @@ static int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
> if (!ctx->pos && !fi->rdc.size) {
> fi->rdc.mtime = inode_get_mtime(inode);
> fi->rdc.iversion = inode_query_iversion(inode);
> + fi->rdc.epoch = atomic_read(&fc->epoch);
> }
> spin_unlock(&fi->rdc.lock);
> return UNCACHED;
> @@ -496,7 +498,8 @@ static int fuse_readdir_cached(struct file *file, struct dir_context *ctx)
> struct timespec64 mtime = inode_get_mtime(inode);
>
> if (inode_peek_iversion(inode) != fi->rdc.iversion ||
> - !timespec64_equal(&fi->rdc.mtime, &mtime)) {
> + !timespec64_equal(&fi->rdc.mtime, &mtime) ||
> + fi->rdc.epoch != atomic_read(&fc->epoch)) {
> fuse_rdc_reset(inode);
> goto retry_locked;
> }
> --
> 2.54.0
>
next prev parent reply other threads:[~2026-05-15 8:53 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-31 22:39 [PATCH] fuse: invalidate readdir cache on epoch bump Jun Wu
2026-05-12 19:58 ` Joanne Koong
2026-05-14 23:09 ` [PATCH v2] " Jun Wu
2026-05-15 0:14 ` Jun Wu
2026-05-15 8:53 ` Luis Henriques [this message]
2026-05-15 15:36 ` Joanne Koong
2026-05-15 16:26 ` Luis Henriques
[not found] ` <CAFfKmRW_Dqtb_DwAgK5Oq5kTAnGY6bmRpYP-Gw2+PVBtU4YVtQ@mail.gmail.com>
2026-05-15 20:25 ` Luis Henriques
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=8733ztt5o8.fsf@igalia.com \
--to=luis@igalia.com \
--cc=fuse-devel@lists.linux.dev \
--cc=miklos@szeredi.hu \
--cc=quark@lihdd.net \
--cc=quark@meta.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.