Linux Kernel Selftest development
 help / color / mirror / Atom feed
* [PATCH 0/5] fuse: report a request refused for a live nodeid as stale
@ 2026-08-27 23:37 Aaron Paterson
  2026-08-27 23:37 ` [PATCH 1/5] selftests/fuse: ignore the built acl cache test Aaron Paterson
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Aaron Paterson @ 2026-08-27 23:37 UTC (permalink / raw)
  To: Miklos Szeredi, Stefan Hajnoczi, Vivek Goyal, German Maglione,
	Shuah Khan
  Cc: Eugenio Pérez, fuse-devel, linux-fsdevel, linux-kselftest,
	virtualization, linux-kernel, Aaron Paterson

A FUSE request that names a nodeid and nothing else is only sent for an
inode the client has looked up and holds a reference to, and the server
owes the client that inode until it is sent FUSE_FORGET. When a server
answers such a request with ENOENT it is describing a handle it was
obliged to honour, not a file that has gone away, and the client passes
that ENOENT to the caller unchanged: a file that never stopped existing
is reported missing.

This series reports that case as stale instead, so the VFS retries the
lookup rather than handing a spurious ENOENT to userspace. The path
opens say EOPENSTALE, which is what an open says when the cached
information it started from has gone stale and which path_openat()
resolves into ECHILD under LOOKUP_RCU or ESTALE otherwise; everything
else says ESTALE, which retry_estale() answers by repeating the lookup
once under LOOKUP_REVAL. NFS reports its own stale opens the same way.

The conversion is written twice, once in fs/fuse for any transport and
once in fs/fuse/virtio_fs.c at reply completion, and each one fixes the
symptom without the other. They are sent together because they were
found together; if only one is wanted, patch 3 is the general one and
patch 5 is the virtio-specific one, and either can be dropped.

Why it matters, and how it was found: a server that releases an inode
as soon as a rename displaces the name it was looked up by will refuse
opens for inodes the client still holds. On such a server, roughly one
open in eight during a rename race is refused while stat continues to
describe the file. That reaches real programs. git's t5318 fails 52 to
53 subtests per round against such a mount and 0 against local disk on
the same machine in the same alternating run, and the mechanism is
visible underneath it: .git/index opens ending ENOENT are 49 of 116 on
the affected mount against 9 of 157 on local disk, where those 9 are
the legitimate ones a fresh repository makes before an index exists.
The user-visible failure is a destroyed git index.

Measurements. All four arms are one kernel source, 6.18.5, with one
config, built from trees that differ only in whether these patches are
applied, run against the same server with the same probe for the same
duration:

  arm  fs/fuse  virtio_fs  opens    refused
   A   stock    stock      323076   45201 (14.0%)
   B   stock    patched     54378   0
   C   patched  stock      323776   0
   D   patched  patched    315114   0

Every refusal on arm A is the contradiction: open said ENOENT while
stat on the same path still described the file. Arms C and A carry
almost identical open counts against the same server, so the pair
differing only in fs/fuse is 45201 refusals against none. Arms B and C
are why the two conversions are sent together but either can be
dropped: each removes the symptom without the other.

For the record, the same fault measured 12.9% on a 6.8 distro kernel
three weeks earlier, so nothing between 6.8 and 6.18 addressed it, which
is consistent with these patches applying to current mainline with only
hunk-offset movement.

The first two patches are selftest housekeeping that patch 4 needs: a
build artifact that was not ignored, and a rename of the libfuse3
pkg-config variables so a second test can share the same guard. Patch 4
is the test itself, which mounts a libfuse3 server that refuses one
request for a live nodeid and asserts the caller does not see ENOENT.
It skips where libfuse3 or fusermount3 is unavailable.

checkpatch is clean apart from two warnings I believe are false
positives, and I would rather name them than leave them to be
rediscovered: MAINTAINERS already carries
F: tools/testing/selftests/filesystems/fuse/ under FUSE FILESYSTEM
[CORE], so the new selftest needs no MAINTAINERS change; and the
char *fuse_argv[] in the test cannot be static const, because
struct fuse_args carries a plain char **argv that libfuse may modify.

Aaron Paterson (5):
  selftests/fuse: ignore the built acl cache test
  selftests/fuse: name the libfuse3 flags for the library
  fuse: report a request refused for a live nodeid as stale
  selftests/fuse: cover a request refused for a live nodeid
  virtiofs: report a request refused for a live nodeid as stale

 fs/fuse/dir.c                                 |  10 +
 fs/fuse/file.c                                |  11 +
 fs/fuse/fuse_i.h                              |  19 +
 fs/fuse/inode.c                               |   2 +-
 fs/fuse/virtio_fs.c                           |  62 +++
 .../selftests/filesystems/fuse/.gitignore     |   2 +
 .../selftests/filesystems/fuse/Makefile       |  16 +-
 .../filesystems/fuse/fuse_estale_test.c       | 450 ++++++++++++++++++
 8 files changed, 565 insertions(+), 7 deletions(-)
 create mode 100644 tools/testing/selftests/filesystems/fuse/fuse_estale_test.c

-- 
2.55.0.553.g4ad8c266be



^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-08-28  2:02 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-27 23:37 [PATCH 0/5] fuse: report a request refused for a live nodeid as stale Aaron Paterson
2026-08-27 23:37 ` [PATCH 1/5] selftests/fuse: ignore the built acl cache test Aaron Paterson
2026-08-27 23:37 ` [PATCH 2/5] selftests/fuse: name the libfuse3 flags for the library Aaron Paterson
2026-08-27 23:37 ` [PATCH 3/5] fuse: report a request refused for a live nodeid as stale Aaron Paterson
2026-08-27 23:38 ` [PATCH 4/5] selftests/fuse: cover a request refused for a live nodeid Aaron Paterson
2026-08-27 23:38 ` [PATCH 5/5] virtiofs: report a request refused for a live nodeid as stale Aaron Paterson
2026-08-28  2:02 ` [PATCH 0/5] fuse: report a request refused for a live nodeid Aaron Paterson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox