[PATCH v5 2/2] fuse: add new function to invalidate cache for all inodes

From: Luis Henriques
Date: Sun Feb 16 2025 - 11:52:20 EST


Currently userspace is able to notify the kernel to invalidate the cache
for an inode. This means that, if all the inodes in a filesystem need to
be invalidated, then userspace needs to iterate through all of them and do
this kernel notification separately.

This patch adds a new option that allows userspace to invalidate all the
inodes with a single notification operation. In addition to invalidate
all the inodes, it also shrinks the sb dcache.

Signed-off-by: Luis Henriques <luis@xxxxxxxxxx>
---
fs/fuse/inode.c | 33 +++++++++++++++++++++++++++++++++
include/uapi/linux/fuse.h | 3 +++
2 files changed, 36 insertions(+)

diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index e9db2cb8c150..01a4dc5677ae 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -547,6 +547,36 @@ struct inode *fuse_ilookup(struct fuse_conn *fc, u64 nodeid,
return NULL;
}

+static int fuse_reverse_inval_all(struct fuse_conn *fc)
+{
+ struct fuse_mount *fm;
+ struct inode *inode;
+
+ inode = fuse_ilookup(fc, FUSE_ROOT_ID, &fm);
+ if (!inode || !fm)
+ return -ENOENT;
+
+ /* Remove all possible active references to cached inodes */
+ shrink_dcache_sb(fm->sb);
+
+ /* Remove all unreferenced inodes from cache */
+ invalidate_inodes(fm->sb);
+
+ return 0;
+}
+
+/*
+ * Notify to invalidate inodes cache. It can be called with @nodeid set to
+ * either:
+ *
+ * - An inode number - Any pending writebacks within the rage [@offset @len]
+ * will be triggered and the inode will be validated. To invalidate the whole
+ * cache @offset has to be set to '0' and @len needs to be <= '0'; if @offset
+ * is negative, only the inode attributes are invalidated.
+ *
+ * - FUSE_INVAL_ALL_INODES - All the inodes in the superblock are invalidated
+ * and the whole dcache is shrinked.
+ */
int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid,
loff_t offset, loff_t len)
{
@@ -555,6 +585,9 @@ int fuse_reverse_inval_inode(struct fuse_conn *fc, u64 nodeid,
pgoff_t pg_start;
pgoff_t pg_end;

+ if (nodeid == FUSE_INVAL_ALL_INODES)
+ return fuse_reverse_inval_all(fc);
+
inode = fuse_ilookup(fc, nodeid, NULL);
if (!inode)
return -ENOENT;
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 5e0eb41d967e..e5852b63f99f 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -669,6 +669,9 @@ enum fuse_notify_code {
FUSE_NOTIFY_CODE_MAX,
};

+/* The nodeid to request to invalidate all inodes */
+#define FUSE_INVAL_ALL_INODES 0
+
/* The read buffer is required to be at least 8k, but may be much larger */
#define FUSE_MIN_READ_BUFFER 8192