Enabling FUSE passthrough for mmap-ed operations not only affects performance, but has also been shown as mandatory for the correct functioning of FUSE passthrough. yanwu noticed [1] that a FUSE file with passthrough enabled may suffer data inconsistencies if the same file is also accessed with mmap. What happens is that read/write operations are directly applied to the backing file's page cache, while mmap-ed operations are affecting the FUSE file's page cache. Extend the FUSE passthrough implementation to also handle memory-mapped FUSE file, to both fix the cache inconsistencies and extend the passthrough performance benefits to mmap-ed operations. [1] https://lore.kernel.org/lkml/20210119110654.11817-1-wu-yan@xxxxxxx/ Signed-off-by: Alessio Balsini <balsini@xxxxxxxxxxx> Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- fs/fuse/file.c | 3 +++ fs/fuse/fuse_i.h | 1 + fs/fuse/passthrough.c | 27 +++++++++++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 24d37681ddcd..80e20bae569f 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2521,6 +2521,9 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) if (FUSE_IS_DAX(file_inode(file))) return fuse_dax_mmap(file, vma); + if (ff->passthrough) + return fuse_passthrough_mmap(file, vma); + if (ff->open_flags & FOPEN_DIRECT_IO) { /* Can't provide the coherency needed for MAP_SHARED */ if (vma->vm_flags & VM_MAYSHARE) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 61a3968cfc8f..238a43349298 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1356,5 +1356,6 @@ void fuse_passthrough_free(struct fuse_passthrough *passthrough); ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *to); ssize_t fuse_passthrough_write_iter(struct kiocb *iocb, struct iov_iter *from); +ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma); #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index 06c6926aa85a..10b370bcc423 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -129,6 +129,33 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb_fuse, return ret; } +ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma) +{ + int ret; + const struct cred *old_cred; + struct fuse_file *ff = file->private_data; + struct file *passthrough_filp = ff->passthrough->filp; + + if (!passthrough_filp->f_op->mmap) + return -ENODEV; + + if (WARN_ON(file != vma->vm_file)) + return -EIO; + + vma->vm_file = get_file(passthrough_filp); + + old_cred = override_creds(ff->passthrough->cred); + ret = call_mmap(vma->vm_file, vma); + revert_creds(old_cred); + + if (ret) + fput(passthrough_filp); + else + fput(file); + + return ret; +} + /* * Returns passthrough_fh id that can be passed with FOPEN_PASSTHROUGH * open response and needs to be released with fuse_passthrough_close(). -- 2.34.1