From 3cfce590fe18c2ed77d191dbb7086542c446880f Mon Sep 17 00:00:00 2001 From: Shai Barack Date: Thu, 10 Sep 2026 18:56:14 +0000 Subject: [PATCH] fuse: Forward vfs_fadvise to backing_file in passthrough mode When an application invokes posix_fadvise(..., POSIX_FADV_DONTNEED) or other fadvise advice on a FUSE passthrough file descriptor, the VFS currently only executes generic_fadvise() on the upper FUSE inode mapping. Because FUSE passthrough does not forward fadvise operations to the underlying backing file, pages residing in the lower filesystem's pagecache remain resident in memory. During media-heavy workloads (such as image thumbnail generation or streaming), single-access file data accumulates in the inactive pagecache of the lower filesystem. Under memory pressure, the kernel retains these stale media pages and instead evicts active executable pages from running processes, leading to severe direct reclaim latency spikes. Architecturally, Linux VFS supports filesystem-specific fadvise delegation via the .fadvise hook in struct file_operations. Implement fuse_file_fadvise() in fs/fuse/file.c and register it in fuse_file_operations. When passthrough is active on a fuse_file, fuse_file_fadvise() forwards the vfs_fadvise() call to the lower backing_file under the opened credentials, allowing POSIX_FADV_DONTNEED to cleanly invalidate the physical backing filesystem pagecache. Signed-off-by: Shai Barack Cc: Miklos Szeredi Cc: Amir Goldstein Cc: Bernd Schubert Cc: Sandeep Dhavale Cc: Akilesh Kailash Cc: Suren Baghdasaryan Cc: fuse-devel@lists.linux.dev Cc: linux-fsdevel@vger.kernel.org Cc: linux-kernel@vger.kernel.org Reviewed-by: T.J. Mercier Reviewed-by: Amir Goldstein Signed-off-by: Miklos Szeredi --- fs/fuse/file.c | 11 +++++++++++ fs/fuse/fuse_i.h | 1 + fs/fuse/passthrough.c | 13 +++++++++++++ 3 files changed, 25 insertions(+) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 0ef46f6e0267..125abfeb4053 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -3103,6 +3103,16 @@ static ssize_t fuse_copy_file_range(struct file *src_file, loff_t src_off, return ret; } +static int fuse_file_fadvise(struct file *file, loff_t offset, loff_t len, int advice) +{ + struct fuse_file *ff = file->private_data; + + if (fuse_is_passthrough(ff)) + return fuse_passthrough_fadvise(ff, offset, len, advice); + + return generic_fadvise(file, offset, len, advice); +} + static const struct file_operations fuse_file_operations = { .llseek = fuse_file_llseek, .read_iter = fuse_file_read_iter, @@ -3123,6 +3133,7 @@ static const struct file_operations fuse_file_operations = { .fallocate = fuse_file_fallocate, .copy_file_range = fuse_copy_file_range, .setlease = generic_setlease, + .fadvise = fuse_file_fadvise, }; static const struct address_space_operations fuse_file_aops = { diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 8546855386b5..893ddc33049a 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1316,6 +1316,7 @@ ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe, struct file *out, loff_t *ppos, size_t len, unsigned int flags); ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma); +int fuse_passthrough_fadvise(struct fuse_file *ff, loff_t offset, loff_t len, int advice); #ifdef CONFIG_SYSCTL extern int fuse_sysctl_register(void); diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index b43d3e0f7081..12aaaf0a2bbb 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -148,6 +148,19 @@ ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma) return backing_file_mmap(backing_file, vma, &ctx); } +int fuse_passthrough_fadvise(struct fuse_file *ff, loff_t offset, loff_t len, int advice) +{ + struct file *backing_file = fuse_file_passthrough(ff); + const struct cred *old_cred; + int ret; + + old_cred = override_creds(ff->cred); + ret = vfs_fadvise(backing_file, offset, len, advice); + revert_creds(old_cred); + + return ret; +} + /* * Setup passthrough to a backing file. *