[RFC PATCH 3/9] fuse: add fuse connection generation

From: Alexander Mikhalitsyn
Date: Mon Feb 20 2023 - 14:38:47 EST


We will use connection generation to detect stale inodes
from the "old" fuse daemon and invalidate/revalidate them.

There is no functional changes.

Cc: Miklos Szeredi <mszeredi@xxxxxxxxxx>
Cc: Al Viro <viro@xxxxxxxxxxxxxxxxxx>
Cc: Amir Goldstein <amir73il@xxxxxxxxx>
Cc: Stéphane Graber <stgraber@xxxxxxxxxx>
Cc: Seth Forshee <sforshee@xxxxxxxxxx>
Cc: Christian Brauner <brauner@xxxxxxxxxx>
Cc: Andrei Vagin <avagin@xxxxxxxxx>
Cc: Pavel Tikhomirov <ptikhomirov@xxxxxxxxxxxxx>
Cc: linux-fsdevel@xxxxxxxxxxxxxxx
Cc: linux-kernel@xxxxxxxxxxxxxxx
Cc: criu@xxxxxxxxxx
Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@xxxxxxxxxxxxx>
---
fs/fuse/file.c | 1 +
fs/fuse/fuse_i.h | 29 +++++++++++++++++++++++++++++
fs/fuse/inode.c | 2 ++
3 files changed, 32 insertions(+)

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 3eb28aad5674..6d89d4dd4c55 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -78,6 +78,7 @@ struct fuse_file *fuse_file_alloc(struct fuse_mount *fm)
init_waitqueue_head(&ff->poll_wait);

ff->kh = atomic64_inc_return(&fm->fc->khctr);
+ ff->conn_gen = READ_ONCE(fm->fc->conn_gen);

return ff;
}
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index d5fc2d89ff1c..ccd7c145de94 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -161,6 +161,9 @@ struct fuse_inode {
*/
struct fuse_inode_dax *dax;
#endif
+
+ /** Fuse connection (fuse_conn) generation when inode was allocated */
+ u32 conn_gen;
};

/** FUSE inode state bits */
@@ -232,6 +235,9 @@ struct fuse_file {

/** Has flock been performed on this file? */
bool flock:1;
+
+ /** Fuse connection (fuse_conn) generation when file was allocated */
+ u32 conn_gen;
};

/** One input argument of a request */
@@ -847,6 +853,18 @@ struct fuse_conn {

/* New writepages go into this bucket */
struct fuse_sync_bucket __rcu *curr_bucket;
+
+ /**
+ * Connection generation.
+ * Used to determine if inodes/files were created with an "old"
+ * fuse connection and have to be invalidated. So, all requests
+ * related to these inodes should fail with -EIO.
+ *
+ * CHECKME: do we really need conn_gen for struct fuse_file?
+ * Right now it's only needed for fuse_file_put(), where we have
+ * no access to the inode in some cases.
+ */
+ u32 conn_gen;
};

/*
@@ -910,6 +928,17 @@ static inline u64 fuse_get_attr_version(const struct fuse_conn *fc)
return atomic64_read(&fc->attr_version);
}

+static inline bool fuse_stale_ff(const struct fuse_file *ff)
+{
+ return unlikely(READ_ONCE(ff->fm->fc->conn_gen) != ff->conn_gen);
+}
+
+static inline bool fuse_stale_inode_conn(const struct inode *inode)
+{
+ return unlikely(READ_ONCE(get_fuse_conn(inode)->conn_gen) !=
+ get_fuse_inode(inode)->conn_gen);
+}
+
static inline bool fuse_stale_inode(const struct inode *inode, int generation,
struct fuse_attr *attr)
{
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 097b7049057e..c604434611d9 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -77,6 +77,7 @@ static struct inode *fuse_alloc_inode(struct super_block *sb)
fi->attr_version = 0;
fi->orig_ino = 0;
fi->state = 0;
+ fi->conn_gen = READ_ONCE(get_fuse_conn_super(sb)->conn_gen);
mutex_init(&fi->mutex);
spin_lock_init(&fi->lock);
fi->forget = fuse_alloc_forget();
@@ -841,6 +842,7 @@ void fuse_conn_init(struct fuse_conn *fc, struct fuse_mount *fm,
fc->user_ns = get_user_ns(user_ns);
fc->max_pages = FUSE_DEFAULT_MAX_PAGES_PER_REQ;
fc->max_pages_limit = FUSE_MAX_MAX_PAGES;
+ fc->conn_gen = 1;

INIT_LIST_HEAD(&fc->mounts);
list_add(&fm->fc_entry, &fc->mounts);
--
2.34.1