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
[not found] <20260331223927.763741-1-quark@lihdd.net>
2026-05-12 19:58 ` [PATCH] fuse: invalidate readdir cache on epoch bump 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
2026-06-11 12:07 ` Miklos Szeredi
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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox