From: Darrick J. Wong <djwong@xxxxxxxxxx> 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@xxxxxxxxxx> --- fs/fuse/fuse_i.h | 7 +++++++ fs/fuse/file_iomap.c | 29 +++++++++++++++++++++++++++++ fs/fuse/inode.c | 9 +++++++-- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index f4834a02d16c98..6a155bdd389af6 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1364,6 +1364,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); @@ -1646,11 +1649,15 @@ static inline bool fuse_has_iomap(const struct inode *inode) int fuse_iomap_backing_open(struct fuse_conn *fc, struct fuse_backing *fb); int fuse_iomap_backing_close(struct fuse_conn *fc, struct fuse_backing *fb); +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_backing_open(...) (-EOPNOTSUPP) # define fuse_iomap_backing_close(...) (-EOPNOTSUPP) +# 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 154c99399f48d2..6e0e222da3046c 100644 --- a/fs/fuse/file_iomap.c +++ b/fs/fuse/file_iomap.c @@ -593,3 +593,32 @@ int fuse_iomap_backing_close(struct fuse_conn *fc, struct fuse_backing *fb) /* We only support closing iomap block devices at unmount */ return -EBUSY; } + +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, syncfs, flush that, 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. The caller already flushed previously pending requests, so we + * only need the flush to wait for syncfs. + */ + sync_filesystem(fm->sb); + fuse_flush_requests_and_wait(fc, secs_to_jiffies(60)); + fuse_send_destroy(fm); +} diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 1f3f91981410aa..3274ee1c31b62b 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -621,7 +621,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); @@ -1457,6 +1457,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; @@ -2055,7 +2058,9 @@ void fuse_conn_destroy(struct fuse_mount *fm) struct fuse_conn *fc = fm->fc; fuse_flush_requests_and_wait(fc, secs_to_jiffies(30)); - if (fc->destroy) + if (fc->iomap) + fuse_iomap_unmount(fm); + else if (fc->destroy) fuse_send_destroy(fm); fuse_abort_conn(fc);