[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <176169810481.1424854.17462269885414881579.stgit@frogsfrogsfrogs>
Date: Tue, 28 Oct 2025 17:46:31 -0700
From: "Darrick J. Wong" <djwong@...nel.org>
To: djwong@...nel.org, miklos@...redi.hu
Cc: joannelkoong@...il.com, bernd@...ernd.com, neal@...pa.dev,
linux-ext4@...r.kernel.org, linux-fsdevel@...r.kernel.org
Subject: [PATCH 06/31] fuse: flush events and send FUSE_SYNCFS and
FUSE_DESTROY on unmount
From: Darrick J. Wong <djwong@...nel.org>
At unmount time, there are a few things that we need to ask the fuse
server to do.
First, we need to flush queued events to userspace to give the fuse
server a chance to process the events. This is how we make sure that
the server processes FUSE_RELEASE events before the connection goes
down.
Second, to ensure that all those metadata updates are persisted to disk
before tell the fuse server to destroy itself, send FUSE_SYNCFS after
waiting for the queued events.
Finally, we need to send FUSE_DESTROY to the fuse server so that it
closes the filesystem and the device fds before unmount returns. That
way, a script that does something like "umount /dev/sda ; e2fsck -fn
/dev/sda" will not fail the e2fsck because the fd closure races with
e2fsck startup. Obviously, we need to wait for FUSE_SYNCFS.
This is a major behavior change and who knows what might break existing
code, so we hide it behind iomap mode.
Signed-off-by: "Darrick J. Wong" <djwong@...nel.org>
---
fs/fuse/fuse_i.h | 8 ++++++++
fs/fuse/file_iomap.c | 29 +++++++++++++++++++++++++++++
fs/fuse/inode.c | 9 +++++++--
3 files changed, 44 insertions(+), 2 deletions(-)
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 274de907257d94..839d4f2ada4656 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1430,6 +1430,9 @@ int fuse_init_fs_context_submount(struct fs_context *fsc);
*/
void fuse_conn_destroy(struct fuse_mount *fm);
+/* Send the FUSE_DESTROY command. */
+void fuse_send_destroy(struct fuse_mount *fm);
+
/* Drop the connection and free the fuse mount */
void fuse_mount_destroy(struct fuse_mount *fm);
@@ -1711,9 +1714,14 @@ static inline bool fuse_has_iomap(const struct inode *inode)
}
extern const struct fuse_backing_ops fuse_iomap_backing_ops;
+
+void fuse_iomap_mount(struct fuse_mount *fm);
+void fuse_iomap_unmount(struct fuse_mount *fm);
#else
# define fuse_iomap_enabled(...) (false)
# define fuse_has_iomap(...) (false)
+# define fuse_iomap_mount(...) ((void)0)
+# define fuse_iomap_unmount(...) ((void)0)
#endif
#endif /* _FS_FUSE_I_H */
diff --git a/fs/fuse/file_iomap.c b/fs/fuse/file_iomap.c
index e4fea3bdc0c2ce..1b9e1bf2f799a3 100644
--- a/fs/fuse/file_iomap.c
+++ b/fs/fuse/file_iomap.c
@@ -606,3 +606,32 @@ const struct fuse_backing_ops fuse_iomap_backing_ops = {
.may_close = fuse_iomap_may_close,
.post_open = fuse_iomap_post_open,
};
+
+void fuse_iomap_mount(struct fuse_mount *fm)
+{
+ struct fuse_conn *fc = fm->fc;
+
+ /*
+ * Enable syncfs for iomap fuse servers so that we can send a final
+ * flush at unmount time. This also means that we can support
+ * freeze/thaw properly.
+ */
+ fc->sync_fs = true;
+}
+
+void fuse_iomap_unmount(struct fuse_mount *fm)
+{
+ struct fuse_conn *fc = fm->fc;
+
+ /*
+ * Flush all pending commands, then issue a syncfs, flush the syncfs,
+ * and send a destroy command. This gives the fuse server a chance to
+ * process all the pending releases, write the last bits of metadata
+ * changes to disk, and close the iomap block devices before we return
+ * from the umount call.
+ */
+ fuse_flush_requests_and_wait(fc);
+ sync_filesystem(fm->sb);
+ fuse_flush_requests_and_wait(fc);
+ fuse_send_destroy(fm);
+}
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index eec711302a4a13..271356fa3be3ea 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -632,7 +632,7 @@ static void fuse_umount_begin(struct super_block *sb)
retire_super(sb);
}
-static void fuse_send_destroy(struct fuse_mount *fm)
+void fuse_send_destroy(struct fuse_mount *fm)
{
if (fm->fc->conn_init) {
FUSE_ARGS(args);
@@ -1471,6 +1471,9 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
init_server_timeout(fc, timeout);
+ if (fc->iomap)
+ fuse_iomap_mount(fm);
+
fm->sb->s_bdi->ra_pages =
min(fm->sb->s_bdi->ra_pages, ra_pages);
fc->minor = arg->minor;
@@ -2106,7 +2109,9 @@ void fuse_conn_destroy(struct fuse_mount *fm)
{
struct fuse_conn *fc = fm->fc;
- if (fc->destroy) {
+ if (fc->iomap) {
+ fuse_iomap_unmount(fm);
+ } else if (fc->destroy) {
/*
* Flush all pending requests (most of which will be
* FUSE_RELEASE) before sending FUSE_DESTROY, because the fuse
Powered by blists - more mailing lists