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 | 5 +++++ fs/fuse/file_iomap.c | 23 +++++++++++++++++++++++ fs/fuse/inode.c | 6 ++++-- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 12c462a29fe0c4..850c187434a61a 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1380,6 +1380,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); @@ -1639,6 +1642,7 @@ int fuse_iomap_conn_alloc(struct fuse_conn *fc); void fuse_iomap_conn_put(struct fuse_conn *fc); int fuse_iomap_dev_add(struct fuse_conn *fc, const struct fuse_backing_map *map); +void fuse_iomap_conn_destroy(struct fuse_mount *fm); #else # define fuse_iomap_enabled(...) (false) # define fuse_has_iomap(...) (false) @@ -1646,6 +1650,7 @@ int fuse_iomap_dev_add(struct fuse_conn *fc, const struct fuse_backing_map *map) # define fuse_iomap_conn_alloc(...) (0) # define fuse_iomap_conn_put(...) ((void)0) # define fuse_iomap_dev_add(...) (-ENOSYS) +# define fuse_iomap_conn_destroy(...) ((void)0) #endif #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/file_iomap.c b/fs/fuse/file_iomap.c index 535429023d37e7..4724d5678112db 100644 --- a/fs/fuse/file_iomap.c +++ b/fs/fuse/file_iomap.c @@ -540,6 +540,12 @@ bool fuse_iomap_fill_super(struct fuse_mount *fm) } } + /* + * 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; return true; } @@ -585,3 +591,20 @@ int fuse_iomap_dev_add(struct fuse_conn *fc, const struct fuse_backing_map *map) out: return res; } + +void fuse_iomap_conn_destroy(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(fc, 60 * HZ); + fuse_send_destroy(fm); +} diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 8266f30bc8a954..8b12284bced7e6 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -618,7 +618,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); @@ -2064,7 +2064,9 @@ void fuse_conn_destroy(struct fuse_mount *fm) struct fuse_conn *fc = fm->fc; fuse_flush_requests(fc, 30 * HZ); - if (fc->destroy) + if (fc->iomap) + fuse_iomap_conn_destroy(fm); + else if (fc->destroy) fuse_send_destroy(fm); fuse_abort_conn(fc);