]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
fuse: implement splice read/write passthrough
authorAmir Goldstein <amir73il@gmail.com>
Fri, 13 Oct 2023 10:26:03 +0000 (13:26 +0300)
committerMiklos Szeredi <mszeredi@redhat.com>
Tue, 5 Mar 2024 12:40:42 +0000 (13:40 +0100)
This allows passing fstests generic/249 and generic/591.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/file.c
fs/fuse/fuse_i.h
fs/fuse/passthrough.c

index 903bb71aac6dc38259f6bbcfeec26b423011fe5c..3216dcf3ace044ae9a312e7d8fdb539d21b7b62f 100644 (file)
@@ -1723,6 +1723,31 @@ static ssize_t fuse_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
                return fuse_cache_write_iter(iocb, from);
 }
 
+static ssize_t fuse_splice_read(struct file *in, loff_t *ppos,
+                               struct pipe_inode_info *pipe, size_t len,
+                               unsigned int flags)
+{
+       struct fuse_file *ff = in->private_data;
+
+       /* FOPEN_DIRECT_IO overrides FOPEN_PASSTHROUGH */
+       if (fuse_file_passthrough(ff) && !(ff->open_flags & FOPEN_DIRECT_IO))
+               return fuse_passthrough_splice_read(in, ppos, pipe, len, flags);
+       else
+               return filemap_splice_read(in, ppos, pipe, len, flags);
+}
+
+static ssize_t fuse_splice_write(struct pipe_inode_info *pipe, struct file *out,
+                                loff_t *ppos, size_t len, unsigned int flags)
+{
+       struct fuse_file *ff = out->private_data;
+
+       /* FOPEN_DIRECT_IO overrides FOPEN_PASSTHROUGH */
+       if (fuse_file_passthrough(ff) && !(ff->open_flags & FOPEN_DIRECT_IO))
+               return fuse_passthrough_splice_write(pipe, out, ppos, len, flags);
+       else
+               return iter_file_splice_write(pipe, out, ppos, len, flags);
+}
+
 static void fuse_writepage_free(struct fuse_writepage_args *wpa)
 {
        struct fuse_args_pages *ap = &wpa->ia.ap;
@@ -3303,8 +3328,8 @@ static const struct file_operations fuse_file_operations = {
        .lock           = fuse_file_lock,
        .get_unmapped_area = thp_get_unmapped_area,
        .flock          = fuse_file_flock,
-       .splice_read    = filemap_splice_read,
-       .splice_write   = iter_file_splice_write,
+       .splice_read    = fuse_splice_read,
+       .splice_write   = fuse_splice_write,
        .unlocked_ioctl = fuse_file_ioctl,
        .compat_ioctl   = fuse_file_compat_ioctl,
        .poll           = fuse_file_poll,
index 6cc7d0752f9fd7bef70e73a7b96de45a90c1ee2c..53ac73739788981cc3241d7b8cf4dfc5a94f37d6 100644 (file)
@@ -1468,5 +1468,11 @@ static inline struct file *fuse_file_passthrough(struct fuse_file *ff)
 
 ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *iter);
 ssize_t fuse_passthrough_write_iter(struct kiocb *iocb, struct iov_iter *iter);
+ssize_t fuse_passthrough_splice_read(struct file *in, loff_t *ppos,
+                                    struct pipe_inode_info *pipe,
+                                    size_t len, unsigned int flags);
+ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe,
+                                     struct file *out, loff_t *ppos,
+                                     size_t len, unsigned int flags);
 
 #endif /* _FS_FUSE_I_H */
index 0e5d316bdad35fe8765f242571c9616e084d57b0..2b119c592f028a5713b8e64f024d1168c45ffde8 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <linux/file.h>
 #include <linux/backing-file.h>
+#include <linux/splice.h>
 
 static void fuse_file_accessed(struct file *file)
 {
@@ -79,6 +80,50 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb,
        return ret;
 }
 
+ssize_t fuse_passthrough_splice_read(struct file *in, loff_t *ppos,
+                                    struct pipe_inode_info *pipe,
+                                    size_t len, unsigned int flags)
+{
+       struct fuse_file *ff = in->private_data;
+       struct file *backing_file = fuse_file_passthrough(ff);
+       struct backing_file_ctx ctx = {
+               .cred = ff->cred,
+               .user_file = in,
+               .accessed = fuse_file_accessed,
+       };
+
+       pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__,
+                backing_file, ppos ? *ppos : 0, len, flags);
+
+       return backing_file_splice_read(backing_file, ppos, pipe, len, flags,
+                                       &ctx);
+}
+
+ssize_t fuse_passthrough_splice_write(struct pipe_inode_info *pipe,
+                                     struct file *out, loff_t *ppos,
+                                     size_t len, unsigned int flags)
+{
+       struct fuse_file *ff = out->private_data;
+       struct file *backing_file = fuse_file_passthrough(ff);
+       struct inode *inode = file_inode(out);
+       ssize_t ret;
+       struct backing_file_ctx ctx = {
+               .cred = ff->cred,
+               .user_file = out,
+               .end_write = fuse_file_modified,
+       };
+
+       pr_debug("%s: backing_file=0x%p, pos=%lld, len=%zu, flags=0x%x\n", __func__,
+                backing_file, ppos ? *ppos : 0, len, flags);
+
+       inode_lock(inode);
+       ret = backing_file_splice_write(pipe, backing_file, ppos, len, flags,
+                                       &ctx);
+       inode_unlock(inode);
+
+       return ret;
+}
+
 struct fuse_backing *fuse_backing_get(struct fuse_backing *fb)
 {
        if (fb && refcount_inc_not_zero(&fb->count))