Commit 5ca73468 authored by Amir Goldstein's avatar Amir Goldstein Committed by Miklos Szeredi
Browse files

fuse: implement splice read/write passthrough



This allows passing fstests generic/249 and generic/591.

Signed-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 57e1176e
Loading
Loading
Loading
Loading
+27 −2
Original line number Diff line number Diff line
@@ -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,
+6 −0
Original line number Diff line number Diff line
@@ -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 */
+45 −0
Original line number Diff line number Diff line
@@ -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))