From 328cc1ffc25a7e175c6b18da3e17563047ab3609 Mon Sep 17 00:00:00 2001 From: Pavel Reichl Date: Wed, 12 Jul 2023 15:58:37 +0200 Subject: [PATCH 1/2] fuse: add "expire only" mode to FUSE_NOTIFY_INVAL_ENTRY Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2188181 Tested: with xfstests and bz reproducer Upstream Status: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Pavel Reichl Add a flag to entry expiration that lets the filesystem expire a dentry without kicking it out from the cache immediately. This makes a difference for overmounted dentries, where plain invalidation would detach all submounts before dropping the dentry from the cache. If only expiry is set on the dentry, then any overmounts are left alone and until ->d_revalidate() is called. Note: ->d_revalidate() is not called for the case of following a submount, so invalidation will only be triggered for the non-overmounted case. The dentry could also be mounted in a different mount instance, in which case any submounts will still be detached. Suggested-by: Jakob Blomer Signed-off-by: Miklos Szeredi (cherry picked from commit 4f8d37020e1fd0bf6ee9381ba918135ef3712efd) --- fs/fuse/dev.c | 4 ++-- fs/fuse/dir.c | 6 ++++-- fs/fuse/fuse_i.h | 2 +- include/uapi/linux/fuse.h | 9 ++++++++- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 8cdb5ab3cb60..5ff068e9abd4 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -1500,7 +1500,7 @@ static int fuse_notify_inval_entry(struct fuse_conn *fc, unsigned int size, buf[outarg.namelen] = 0; down_read(&fc->killsb); - err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name); + err = fuse_reverse_inval_entry(fc, outarg.parent, 0, &name, outarg.flags); up_read(&fc->killsb); kfree(buf); return err; @@ -1548,7 +1548,7 @@ static int fuse_notify_delete(struct fuse_conn *fc, unsigned int size, buf[outarg.namelen] = 0; down_read(&fc->killsb); - err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name); + err = fuse_reverse_inval_entry(fc, outarg.parent, outarg.child, &name, 0); up_read(&fc->killsb); kfree(buf); return err; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index c6a54b891a3e..1e2b89ddd2c2 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1151,7 +1151,7 @@ int fuse_update_attributes(struct inode *inode, struct file *file) } int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, - u64 child_nodeid, struct qstr *name) + u64 child_nodeid, struct qstr *name, u32 flags) { int err = -ENOTDIR; struct inode *parent; @@ -1178,7 +1178,9 @@ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, goto unlock; fuse_dir_changed(parent); - fuse_invalidate_entry(entry); + if (!(flags & FUSE_EXPIRE_ONLY)) + d_invalidate(entry); + fuse_invalidate_entry_cache(entry); if (child_nodeid != 0 && d_really_is_positive(entry)) { inode_lock(d_inode(entry)); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 4b2d7290bf2a..6b9c1b9c6e81 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1176,7 +1176,7 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid, * then the dentry is unhashed (d_delete()). */ int fuse_reverse_inval_entry(struct fuse_conn *fc, u64 parent_nodeid, - u64 child_nodeid, struct qstr *name); + u64 child_nodeid, struct qstr *name, u32 flags); int fuse_do_open(struct fuse_mount *fm, u64 nodeid, struct file *file, bool isdir); diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 3f0ea63fec08..fc0744ebf3cc 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -193,6 +193,7 @@ * - add flags2 to fuse_init_in and fuse_init_out * - add FUSE_SECURITY_CTX init flag * - add security context to create, mkdir, symlink, and mknod requests + * - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry */ #ifndef _LINUX_FUSE_H @@ -483,6 +484,12 @@ struct fuse_file_lock { */ #define FUSE_SETXATTR_ACL_KILL_SGID (1 << 0) +/** + * notify_inval_entry flags + * FUSE_EXPIRE_ONLY + */ +#define FUSE_EXPIRE_ONLY (1 << 0) + enum fuse_opcode { FUSE_LOOKUP = 1, FUSE_FORGET = 2, /* no reply */ @@ -910,7 +917,7 @@ struct fuse_notify_inval_inode_out { struct fuse_notify_inval_entry_out { uint64_t parent; uint32_t namelen; - uint32_t padding; + uint32_t flags; }; struct fuse_notify_delete_out { From c4ea092a2ae07d222448bb19e1facd252683fada Mon Sep 17 00:00:00 2001 From: Pavel Reichl Date: Wed, 12 Jul 2023 16:17:22 +0200 Subject: [PATCH 2/2] fuse: add feature flag for expire-only Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2188181 Tested: with xfstests and bz reproducer Upstream Status: RHEL only Signed-off-by: Pavel Reichl Add an init flag idicating whether the FUSE_EXPIRE_ONLY flag of FUSE_NOTIFY_INVAL_ENTRY is effective. This is needed for backports of this feature, otherwise the server could just check the protocol version. This patch is not yet in upstream, original author is Miklos Szeredi. Fixes: 4f8d37020e1f ("fuse: add "expire only" mode to FUSE_NOTIFY_INVAL_ENTRY") Signed-off-by: Miklos Szeredi --- fs/fuse/inode.c | 2 +- include/uapi/linux/fuse.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index ac0c9f52dbbf..0a3b92838c24 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -1134,7 +1134,7 @@ void fuse_send_init(struct fuse_mount *fm) FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS | FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA | FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT | - FUSE_SECURITY_CTX; + FUSE_SECURITY_CTX | FUSE_HAS_EXPIRE_ONLY; #ifdef CONFIG_FUSE_DAX if (fm->fc->dax) flags |= FUSE_MAP_ALIGNMENT; diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index fc0744ebf3cc..8127513d58a2 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -194,6 +194,7 @@ * - add FUSE_SECURITY_CTX init flag * - add security context to create, mkdir, symlink, and mknod requests * - add FUSE_EXPIRE_ONLY flag to fuse_notify_inval_entry + * - add FUSE_HAS_EXPIRE_ONLY */ #ifndef _LINUX_FUSE_H @@ -352,6 +353,7 @@ struct fuse_file_lock { * FUSE_INIT_RESERVED: reserved, do not use * FUSE_SECURITY_CTX: add security context to create, mkdir, symlink, and * mknod + * FUSE_HAS_EXPIRE_ONLY: kernel supports expiry-only entry invalidation */ #define FUSE_ASYNC_READ (1 << 0) #define FUSE_POSIX_LOCKS (1 << 1) @@ -387,6 +389,7 @@ struct fuse_file_lock { #define FUSE_INIT_RESERVED (1 << 31) /* bits 32..63 get shifted down 32 bits into the flags2 field */ #define FUSE_SECURITY_CTX (1ULL << 32) +#define FUSE_HAS_EXPIRE_ONLY (1ULL << 35) /** * CUSE INIT request/reply flags