summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmir Goldstein <amir73il@gmail.com>2024-02-09 17:16:49 +0200
committerMiklos Szeredi <mszeredi@redhat.com>2024-03-05 13:40:42 +0100
commitfda0b98ef0a6a2e3fe328b869d53002c8c82001b (patch)
tree973681ab67e9bda57e471b6e8a6933f8e44cfc12
parent5ca73468612d8e0767614992da8decc7f9f48926 (diff)
fuse: implement passthrough for mmap
An mmap request for a file open in passthrough mode, maps the memory directly to the backing file. An mmap of a file in direct io mode, usually uses cached mmap and puts the inode in caching io mode, which denies new passthrough opens of that inode, because caching io mode is conflicting with passthrough io mode. For the same reason, trying to mmap a direct io file, while there is a passthrough file open on the same inode will fail with -ENODEV. An mmap of a file in direct io mode, also needs to wait for parallel dio writes in-progress to complete. If a passthrough file is opened, while an mmap of another direct io file is waiting for parallel dio writes to complete, the wait is aborted and mmap fails with -ENODEV. A FUSE server that uses passthrough and direct io opens on the same inode that may also be mmaped, is advised to provide a backing fd also for the files that are open in direct io mode (i.e. use the flags combination FOPEN_DIRECT_IO | FOPEN_PASSTHROUGH), so that mmap will always use the backing file, even if read/write do not passthrough. Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/fuse/file.c13
-rw-r--r--fs/fuse/fuse_i.h1
-rw-r--r--fs/fuse/passthrough.c16
3 files changed, 27 insertions, 3 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 3216dcf3ace0..3ce158f70927 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -2555,14 +2555,21 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
{
struct fuse_file *ff = file->private_data;
struct fuse_conn *fc = ff->fm->fc;
+ struct inode *inode = file_inode(file);
int rc;
/* DAX mmap is superior to direct_io mmap */
- if (FUSE_IS_DAX(file_inode(file)))
+ if (FUSE_IS_DAX(inode))
return fuse_dax_mmap(file, vma);
- /* TODO: implement mmap to backing file */
+ /*
+ * If inode is in passthrough io mode, because it has some file open
+ * in passthrough mode, either mmap to backing file or fail mmap,
+ * because mixing cached mmap and passthrough io mode is not allowed.
+ */
if (fuse_file_passthrough(ff))
+ return fuse_passthrough_mmap(file, vma);
+ else if (fuse_inode_backing(get_fuse_inode(inode)))
return -ENODEV;
/*
@@ -2589,7 +2596,7 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma)
* Also waits for parallel dio writers to go into serial mode
* (exclusive instead of shared lock).
*/
- rc = fuse_file_cached_io_start(file_inode(file), ff);
+ rc = fuse_file_cached_io_start(inode, ff);
if (rc)
return rc;
}
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 53ac73739788..7bd3552b1e80 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1474,5 +1474,6 @@ ssize_t fuse_passthrough_splice_read(struct file *in, loff_t *ppos,
ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe,
struct file *out, loff_t *ppos,
size_t len, unsigned int flags);
+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 2b119c592f02..1567f0323858 100644
--- a/fs/fuse/passthrough.c
+++ b/fs/fuse/passthrough.c
@@ -124,6 +124,22 @@ ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe,
return ret;
}
+ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct fuse_file *ff = file->private_data;
+ struct file *backing_file = fuse_file_passthrough(ff);
+ struct backing_file_ctx ctx = {
+ .cred = ff->cred,
+ .user_file = file,
+ .accessed = fuse_file_accessed,
+ };
+
+ pr_debug("%s: backing_file=0x%p, start=%lu, end=%lu\n", __func__,
+ backing_file, vma->vm_start, vma->vm_end);
+
+ return backing_file_mmap(backing_file, vma, &ctx);
+}
+
struct fuse_backing *fuse_backing_get(struct fuse_backing *fb)
{
if (fb && refcount_inc_not_zero(&fb->count))