diff mbox series

[v13,04/10] fuse: Introduce synchronous read and write for passthrough

Message ID 20230519125705.598234-5-amir73il@gmail.com (mailing list archive)
State New, archived
Headers show
Series fuse: Add support for passthrough read/write | expand

Commit Message

Amir Goldstein May 19, 2023, 12:56 p.m. UTC
All the read and write operations performed on fuse_files which have the
passthrough feature enabled are forwarded to the associated backing file
via VFS.

Sending the request directly to the backing file avoids the userspace
round-trip that, because of possible context switches and additional
operations might reduce the overall performance, especially in those
cases where caching doesn't help, for example in reads at random offsets.

Verifying if a fuse_file has a backing file associated with it
can be done by checking the validity of its passthrough_filp pointer.
This pointer is not NULL only if passthrough has been successfully
enabled via the appropriate ioctl().

When a read/write operation is requested for a FUSE file with passthrough
enabled, a new equivalent VFS request is generated, which instead targets
the backing file.

The VFS layer performs additional checks that allow for safer operations
but may cause the operation to fail if the process accessing the FUSE
file does not have access to the backing file.

This change only implements synchronous requests in passthrough,
returning an error in the case of asynchronous operations, yet covering
the majority of the use cases.

Signed-off-by: Alessio Balsini <balsini@android.com>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
 fs/fuse/file.c        |  8 ++++++--
 fs/fuse/fuse_i.h      |  3 +++
 fs/fuse/passthrough.c | 47 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 56 insertions(+), 2 deletions(-)
diff mbox series

Patch

diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 96a46a5aa892..24d37681ddcd 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -1682,7 +1682,9 @@  static ssize_t fuse_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
 	if (FUSE_IS_DAX(inode))
 		return fuse_dax_read_iter(iocb, to);
 
-	if (!(ff->open_flags & FOPEN_DIRECT_IO))
+	if (ff->passthrough)
+		return fuse_passthrough_read_iter(iocb, to);
+	else if (!(ff->open_flags & FOPEN_DIRECT_IO))
 		return fuse_cache_read_iter(iocb, to);
 	else
 		return fuse_direct_read_iter(iocb, to);
@@ -1700,7 +1702,9 @@  static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
 	if (FUSE_IS_DAX(inode))
 		return fuse_dax_write_iter(iocb, from);
 
-	if (!(ff->open_flags & FOPEN_DIRECT_IO))
+	if (ff->passthrough)
+		return fuse_passthrough_write_iter(iocb, from);
+	else if (!(ff->open_flags & FOPEN_DIRECT_IO))
 		return fuse_cache_write_iter(iocb, from);
 	else
 		return fuse_direct_write_iter(iocb, from);
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 9ad1cc37a5c4..ff09fcd840df 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -1352,4 +1352,7 @@  int fuse_passthrough_setup(struct fuse_conn *fc, struct fuse_file *ff,
 void fuse_passthrough_put(struct fuse_passthrough *passthrough);
 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);
+
 #endif /* _FS_FUSE_I_H */
diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c
index 8d090ae252f2..9d81f3982c96 100644
--- a/fs/fuse/passthrough.c
+++ b/fs/fuse/passthrough.c
@@ -4,6 +4,53 @@ 
 
 #include <linux/file.h>
 #include <linux/idr.h>
+#include <linux/uio.h>
+
+#define FUSE_IOCB_MASK                                                  \
+	(IOCB_APPEND | IOCB_DSYNC | IOCB_HIPRI | IOCB_NOWAIT | IOCB_SYNC)
+
+ssize_t fuse_passthrough_read_iter(struct kiocb *iocb_fuse,
+				   struct iov_iter *iter)
+{
+	struct file *fuse_filp = iocb_fuse->ki_filp;
+	struct fuse_file *ff = fuse_filp->private_data;
+	struct file *passthrough_filp = ff->passthrough->filp;
+	ssize_t ret;
+	rwf_t rwf;
+
+	if (!iov_iter_count(iter))
+		return 0;
+
+	rwf = iocb_to_rw_flags(iocb_fuse->ki_flags, FUSE_IOCB_MASK);
+	ret = vfs_iter_read(passthrough_filp, iter, &iocb_fuse->ki_pos, rwf);
+
+	return ret;
+}
+
+ssize_t fuse_passthrough_write_iter(struct kiocb *iocb_fuse,
+				    struct iov_iter *iter)
+{
+	struct file *fuse_filp = iocb_fuse->ki_filp;
+	struct fuse_file *ff = fuse_filp->private_data;
+	struct inode *fuse_inode = file_inode(fuse_filp);
+	struct file *passthrough_filp = ff->passthrough->filp;
+	ssize_t ret;
+	rwf_t rwf;
+
+	if (!iov_iter_count(iter))
+		return 0;
+
+	inode_lock(fuse_inode);
+
+	file_start_write(passthrough_filp);
+	rwf = iocb_to_rw_flags(iocb_fuse->ki_flags, FUSE_IOCB_MASK);
+	ret = vfs_iter_write(passthrough_filp, iter, &iocb_fuse->ki_pos, rwf);
+	file_end_write(passthrough_filp);
+
+	inode_unlock(fuse_inode);
+
+	return ret;
+}
 
 /*
  * Returns passthrough_fh id that can be passed with FOPEN_PASSTHROUGH