mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 08:38:45 +02:00 
			
		
		
		
	fuse: implement splice read/write passthrough
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>
This commit is contained in:
		
							parent
							
								
									57e1176e60
								
							
						
					
					
						commit
						5ca7346861
					
				
					 3 changed files with 78 additions and 2 deletions
				
			
		|  | @ -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); | 		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) | static void fuse_writepage_free(struct fuse_writepage_args *wpa) | ||||||
| { | { | ||||||
| 	struct fuse_args_pages *ap = &wpa->ia.ap; | 	struct fuse_args_pages *ap = &wpa->ia.ap; | ||||||
|  | @ -3303,8 +3328,8 @@ static const struct file_operations fuse_file_operations = { | ||||||
| 	.lock		= fuse_file_lock, | 	.lock		= fuse_file_lock, | ||||||
| 	.get_unmapped_area = thp_get_unmapped_area, | 	.get_unmapped_area = thp_get_unmapped_area, | ||||||
| 	.flock		= fuse_file_flock, | 	.flock		= fuse_file_flock, | ||||||
| 	.splice_read	= filemap_splice_read, | 	.splice_read	= fuse_splice_read, | ||||||
| 	.splice_write	= iter_file_splice_write, | 	.splice_write	= fuse_splice_write, | ||||||
| 	.unlocked_ioctl	= fuse_file_ioctl, | 	.unlocked_ioctl	= fuse_file_ioctl, | ||||||
| 	.compat_ioctl	= fuse_file_compat_ioctl, | 	.compat_ioctl	= fuse_file_compat_ioctl, | ||||||
| 	.poll		= fuse_file_poll, | 	.poll		= fuse_file_poll, | ||||||
|  |  | ||||||
|  | @ -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_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_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 */ | #endif /* _FS_FUSE_I_H */ | ||||||
|  |  | ||||||
|  | @ -9,6 +9,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <linux/file.h> | #include <linux/file.h> | ||||||
| #include <linux/backing-file.h> | #include <linux/backing-file.h> | ||||||
|  | #include <linux/splice.h> | ||||||
| 
 | 
 | ||||||
| static void fuse_file_accessed(struct file *file) | static void fuse_file_accessed(struct file *file) | ||||||
| { | { | ||||||
|  | @ -79,6 +80,50 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb, | ||||||
| 	return ret; | 	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) | struct fuse_backing *fuse_backing_get(struct fuse_backing *fb) | ||||||
| { | { | ||||||
| 	if (fb && refcount_inc_not_zero(&fb->count)) | 	if (fb && refcount_inc_not_zero(&fb->count)) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue
	
	 Amir Goldstein
						Amir Goldstein