->splice_write() via ->write_iter()
iter_file_splice_write() - a ->splice_write() instance that gathers the pipe buffers, builds a bio_vec-based iov_iter covering those and feeds it to ->write_iter(). A bunch of simple cases coverted to that... [AV: fixed the braino spotted by Cyrill] Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
62a8067a7f
commit
8d0207652c
16 changed files with 156 additions and 56 deletions
|
@ -1583,7 +1583,7 @@ const struct file_operations def_blk_fops = {
|
||||||
.compat_ioctl = compat_blkdev_ioctl,
|
.compat_ioctl = compat_blkdev_ioctl,
|
||||||
#endif
|
#endif
|
||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = iter_file_splice_write,
|
||||||
};
|
};
|
||||||
|
|
||||||
int ioctl_by_bdev(struct block_device *bdev, unsigned cmd, unsigned long arg)
|
int ioctl_by_bdev(struct block_device *bdev, unsigned cmd, unsigned long arg)
|
||||||
|
|
|
@ -77,7 +77,7 @@ const struct file_operations exofs_file_operations = {
|
||||||
.fsync = exofs_file_fsync,
|
.fsync = exofs_file_fsync,
|
||||||
.flush = exofs_flush,
|
.flush = exofs_flush,
|
||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = iter_file_splice_write,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct inode_operations exofs_file_inode_operations = {
|
const struct inode_operations exofs_file_inode_operations = {
|
||||||
|
|
|
@ -75,7 +75,7 @@ const struct file_operations ext2_file_operations = {
|
||||||
.release = ext2_release_file,
|
.release = ext2_release_file,
|
||||||
.fsync = ext2_fsync,
|
.fsync = ext2_fsync,
|
||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = iter_file_splice_write,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_EXT2_FS_XIP
|
#ifdef CONFIG_EXT2_FS_XIP
|
||||||
|
|
|
@ -63,7 +63,7 @@ const struct file_operations ext3_file_operations = {
|
||||||
.release = ext3_release_file,
|
.release = ext3_release_file,
|
||||||
.fsync = ext3_sync_file,
|
.fsync = ext3_sync_file,
|
||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = iter_file_splice_write,
|
||||||
};
|
};
|
||||||
|
|
||||||
const struct inode_operations ext3_file_inode_operations = {
|
const struct inode_operations ext3_file_inode_operations = {
|
||||||
|
|
|
@ -599,7 +599,7 @@ const struct file_operations ext4_file_operations = {
|
||||||
.release = ext4_release_file,
|
.release = ext4_release_file,
|
||||||
.fsync = ext4_sync_file,
|
.fsync = ext4_sync_file,
|
||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = iter_file_splice_write,
|
||||||
.fallocate = ext4_fallocate,
|
.fallocate = ext4_fallocate,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -692,5 +692,5 @@ const struct file_operations f2fs_file_operations = {
|
||||||
.compat_ioctl = f2fs_compat_ioctl,
|
.compat_ioctl = f2fs_compat_ioctl,
|
||||||
#endif
|
#endif
|
||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = iter_file_splice_write,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1068,7 +1068,7 @@ const struct file_operations gfs2_file_fops = {
|
||||||
.lock = gfs2_lock,
|
.lock = gfs2_lock,
|
||||||
.flock = gfs2_flock,
|
.flock = gfs2_flock,
|
||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = iter_file_splice_write,
|
||||||
.setlease = gfs2_setlease,
|
.setlease = gfs2_setlease,
|
||||||
.fallocate = gfs2_fallocate,
|
.fallocate = gfs2_fallocate,
|
||||||
};
|
};
|
||||||
|
@ -1098,7 +1098,7 @@ const struct file_operations gfs2_file_fops_nolock = {
|
||||||
.release = gfs2_release,
|
.release = gfs2_release,
|
||||||
.fsync = gfs2_fsync,
|
.fsync = gfs2_fsync,
|
||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = iter_file_splice_write,
|
||||||
.setlease = generic_setlease,
|
.setlease = generic_setlease,
|
||||||
.fallocate = gfs2_fallocate,
|
.fallocate = gfs2_fallocate,
|
||||||
};
|
};
|
||||||
|
|
|
@ -157,7 +157,7 @@ const struct file_operations jfs_file_operations = {
|
||||||
.write_iter = generic_file_write_iter,
|
.write_iter = generic_file_write_iter,
|
||||||
.mmap = generic_file_mmap,
|
.mmap = generic_file_mmap,
|
||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = iter_file_splice_write,
|
||||||
.fsync = jfs_fsync,
|
.fsync = jfs_fsync,
|
||||||
.release = jfs_release,
|
.release = jfs_release,
|
||||||
.unlocked_ioctl = jfs_ioctl,
|
.unlocked_ioctl = jfs_ioctl,
|
||||||
|
|
|
@ -38,7 +38,7 @@ const struct file_operations ramfs_file_operations = {
|
||||||
.mmap = generic_file_mmap,
|
.mmap = generic_file_mmap,
|
||||||
.fsync = noop_fsync,
|
.fsync = noop_fsync,
|
||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = iter_file_splice_write,
|
||||||
.llseek = generic_file_llseek,
|
.llseek = generic_file_llseek,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ const struct file_operations ramfs_file_operations = {
|
||||||
.write_iter = generic_file_write_iter,
|
.write_iter = generic_file_write_iter,
|
||||||
.fsync = noop_fsync,
|
.fsync = noop_fsync,
|
||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = iter_file_splice_write,
|
||||||
.llseek = generic_file_llseek,
|
.llseek = generic_file_llseek,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -248,7 +248,7 @@ const struct file_operations reiserfs_file_operations = {
|
||||||
.read_iter = generic_file_read_iter,
|
.read_iter = generic_file_read_iter,
|
||||||
.write_iter = generic_file_write_iter,
|
.write_iter = generic_file_write_iter,
|
||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = iter_file_splice_write,
|
||||||
.llseek = generic_file_llseek,
|
.llseek = generic_file_llseek,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
140
fs/splice.c
140
fs/splice.c
|
@ -32,6 +32,7 @@
|
||||||
#include <linux/gfp.h>
|
#include <linux/gfp.h>
|
||||||
#include <linux/socket.h>
|
#include <linux/socket.h>
|
||||||
#include <linux/compat.h>
|
#include <linux/compat.h>
|
||||||
|
#include <linux/aio.h>
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1052,6 +1053,145 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
|
||||||
|
|
||||||
EXPORT_SYMBOL(generic_file_splice_write);
|
EXPORT_SYMBOL(generic_file_splice_write);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* iter_file_splice_write - splice data from a pipe to a file
|
||||||
|
* @pipe: pipe info
|
||||||
|
* @out: file to write to
|
||||||
|
* @ppos: position in @out
|
||||||
|
* @len: number of bytes to splice
|
||||||
|
* @flags: splice modifier flags
|
||||||
|
*
|
||||||
|
* Description:
|
||||||
|
* Will either move or copy pages (determined by @flags options) from
|
||||||
|
* the given pipe inode to the given file.
|
||||||
|
* This one is ->write_iter-based.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ssize_t
|
||||||
|
iter_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
|
||||||
|
loff_t *ppos, size_t len, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct splice_desc sd = {
|
||||||
|
.total_len = len,
|
||||||
|
.flags = flags,
|
||||||
|
.pos = *ppos,
|
||||||
|
.u.file = out,
|
||||||
|
};
|
||||||
|
int nbufs = pipe->buffers;
|
||||||
|
struct bio_vec *array = kcalloc(nbufs, sizeof(struct bio_vec),
|
||||||
|
GFP_KERNEL);
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
if (unlikely(!array))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
pipe_lock(pipe);
|
||||||
|
|
||||||
|
splice_from_pipe_begin(&sd);
|
||||||
|
while (sd.total_len) {
|
||||||
|
struct iov_iter from;
|
||||||
|
struct kiocb kiocb;
|
||||||
|
size_t left;
|
||||||
|
int n, idx;
|
||||||
|
|
||||||
|
ret = splice_from_pipe_next(pipe, &sd);
|
||||||
|
if (ret <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (unlikely(nbufs < pipe->buffers)) {
|
||||||
|
kfree(array);
|
||||||
|
nbufs = pipe->buffers;
|
||||||
|
array = kcalloc(nbufs, sizeof(struct bio_vec),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!array) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build the vector */
|
||||||
|
left = sd.total_len;
|
||||||
|
for (n = 0, idx = pipe->curbuf; left && n < pipe->nrbufs; n++, idx++) {
|
||||||
|
struct pipe_buffer *buf = pipe->bufs + idx;
|
||||||
|
size_t this_len = buf->len;
|
||||||
|
|
||||||
|
if (this_len > left)
|
||||||
|
this_len = left;
|
||||||
|
|
||||||
|
if (idx == pipe->buffers - 1)
|
||||||
|
idx = -1;
|
||||||
|
|
||||||
|
ret = buf->ops->confirm(pipe, buf);
|
||||||
|
if (unlikely(ret)) {
|
||||||
|
if (ret == -ENODATA)
|
||||||
|
ret = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
array[n].bv_page = buf->page;
|
||||||
|
array[n].bv_len = this_len;
|
||||||
|
array[n].bv_offset = buf->offset;
|
||||||
|
left -= this_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ... iov_iter */
|
||||||
|
from.type = ITER_BVEC | WRITE;
|
||||||
|
from.bvec = array;
|
||||||
|
from.nr_segs = n;
|
||||||
|
from.count = sd.total_len - left;
|
||||||
|
from.iov_offset = 0;
|
||||||
|
|
||||||
|
/* ... and iocb */
|
||||||
|
init_sync_kiocb(&kiocb, out);
|
||||||
|
kiocb.ki_pos = sd.pos;
|
||||||
|
kiocb.ki_nbytes = sd.total_len - left;
|
||||||
|
|
||||||
|
/* now, send it */
|
||||||
|
ret = out->f_op->write_iter(&kiocb, &from);
|
||||||
|
if (-EIOCBQUEUED == ret)
|
||||||
|
ret = wait_on_sync_kiocb(&kiocb);
|
||||||
|
|
||||||
|
if (ret <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
sd.num_spliced += ret;
|
||||||
|
sd.total_len -= ret;
|
||||||
|
*ppos = sd.pos = kiocb.ki_pos;
|
||||||
|
|
||||||
|
/* dismiss the fully eaten buffers, adjust the partial one */
|
||||||
|
while (ret) {
|
||||||
|
struct pipe_buffer *buf = pipe->bufs + pipe->curbuf;
|
||||||
|
if (ret >= buf->len) {
|
||||||
|
const struct pipe_buf_operations *ops = buf->ops;
|
||||||
|
ret -= buf->len;
|
||||||
|
buf->len = 0;
|
||||||
|
buf->ops = NULL;
|
||||||
|
ops->release(pipe, buf);
|
||||||
|
pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1);
|
||||||
|
pipe->nrbufs--;
|
||||||
|
if (pipe->files)
|
||||||
|
sd.need_wakeup = true;
|
||||||
|
} else {
|
||||||
|
buf->offset += ret;
|
||||||
|
buf->len -= ret;
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
done:
|
||||||
|
kfree(array);
|
||||||
|
splice_from_pipe_end(pipe, &sd);
|
||||||
|
|
||||||
|
pipe_unlock(pipe);
|
||||||
|
|
||||||
|
if (sd.num_spliced)
|
||||||
|
ret = sd.num_spliced;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPORT_SYMBOL(iter_file_splice_write);
|
||||||
|
|
||||||
static int write_pipe_buf(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
|
static int write_pipe_buf(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
|
||||||
struct splice_desc *sd)
|
struct splice_desc *sd)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1585,7 +1585,7 @@ const struct file_operations ubifs_file_operations = {
|
||||||
.fsync = ubifs_fsync,
|
.fsync = ubifs_fsync,
|
||||||
.unlocked_ioctl = ubifs_ioctl,
|
.unlocked_ioctl = ubifs_ioctl,
|
||||||
.splice_read = generic_file_splice_read,
|
.splice_read = generic_file_splice_read,
|
||||||
.splice_write = generic_file_splice_write,
|
.splice_write = iter_file_splice_write,
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
.compat_ioctl = ubifs_compat_ioctl,
|
.compat_ioctl = ubifs_compat_ioctl,
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -342,47 +342,6 @@ xfs_file_splice_read(
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* xfs_file_splice_write() does not use xfs_rw_ilock() because
|
|
||||||
* generic_file_splice_write() takes the i_mutex itself. This, in theory,
|
|
||||||
* couuld cause lock inversions between the aio_write path and the splice path
|
|
||||||
* if someone is doing concurrent splice(2) based writes and write(2) based
|
|
||||||
* writes to the same inode. The only real way to fix this is to re-implement
|
|
||||||
* the generic code here with correct locking orders.
|
|
||||||
*/
|
|
||||||
STATIC ssize_t
|
|
||||||
xfs_file_splice_write(
|
|
||||||
struct pipe_inode_info *pipe,
|
|
||||||
struct file *outfilp,
|
|
||||||
loff_t *ppos,
|
|
||||||
size_t count,
|
|
||||||
unsigned int flags)
|
|
||||||
{
|
|
||||||
struct inode *inode = outfilp->f_mapping->host;
|
|
||||||
struct xfs_inode *ip = XFS_I(inode);
|
|
||||||
int ioflags = 0;
|
|
||||||
ssize_t ret;
|
|
||||||
|
|
||||||
XFS_STATS_INC(xs_write_calls);
|
|
||||||
|
|
||||||
if (outfilp->f_mode & FMODE_NOCMTIME)
|
|
||||||
ioflags |= IO_INVIS;
|
|
||||||
|
|
||||||
if (XFS_FORCED_SHUTDOWN(ip->i_mount))
|
|
||||||
return -EIO;
|
|
||||||
|
|
||||||
xfs_ilock(ip, XFS_IOLOCK_EXCL);
|
|
||||||
|
|
||||||
trace_xfs_file_splice_write(ip, count, *ppos, ioflags);
|
|
||||||
|
|
||||||
ret = generic_file_splice_write(pipe, outfilp, ppos, count, flags);
|
|
||||||
if (ret > 0)
|
|
||||||
XFS_STATS_ADD(xs_write_bytes, ret);
|
|
||||||
|
|
||||||
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This routine is called to handle zeroing any space in the last block of the
|
* This routine is called to handle zeroing any space in the last block of the
|
||||||
* file that is beyond the EOF. We do this since the size is being increased
|
* file that is beyond the EOF. We do this since the size is being increased
|
||||||
|
@ -1442,7 +1401,7 @@ const struct file_operations xfs_file_operations = {
|
||||||
.read_iter = xfs_file_read_iter,
|
.read_iter = xfs_file_read_iter,
|
||||||
.write_iter = xfs_file_write_iter,
|
.write_iter = xfs_file_write_iter,
|
||||||
.splice_read = xfs_file_splice_read,
|
.splice_read = xfs_file_splice_read,
|
||||||
.splice_write = xfs_file_splice_write,
|
.splice_write = iter_file_splice_write,
|
||||||
.unlocked_ioctl = xfs_file_ioctl,
|
.unlocked_ioctl = xfs_file_ioctl,
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
.compat_ioctl = xfs_file_compat_ioctl,
|
.compat_ioctl = xfs_file_compat_ioctl,
|
||||||
|
|
|
@ -1060,7 +1060,6 @@ DEFINE_RW_EVENT(xfs_file_read);
|
||||||
DEFINE_RW_EVENT(xfs_file_buffered_write);
|
DEFINE_RW_EVENT(xfs_file_buffered_write);
|
||||||
DEFINE_RW_EVENT(xfs_file_direct_write);
|
DEFINE_RW_EVENT(xfs_file_direct_write);
|
||||||
DEFINE_RW_EVENT(xfs_file_splice_read);
|
DEFINE_RW_EVENT(xfs_file_splice_read);
|
||||||
DEFINE_RW_EVENT(xfs_file_splice_write);
|
|
||||||
|
|
||||||
DECLARE_EVENT_CLASS(xfs_page_class,
|
DECLARE_EVENT_CLASS(xfs_page_class,
|
||||||
TP_PROTO(struct inode *inode, struct page *page, unsigned long off,
|
TP_PROTO(struct inode *inode, struct page *page, unsigned long off,
|
||||||
|
|
|
@ -2434,6 +2434,8 @@ extern ssize_t default_file_splice_read(struct file *, loff_t *,
|
||||||
struct pipe_inode_info *, size_t, unsigned int);
|
struct pipe_inode_info *, size_t, unsigned int);
|
||||||
extern ssize_t generic_file_splice_write(struct pipe_inode_info *,
|
extern ssize_t generic_file_splice_write(struct pipe_inode_info *,
|
||||||
struct file *, loff_t *, size_t, unsigned int);
|
struct file *, loff_t *, size_t, unsigned int);
|
||||||
|
extern ssize_t iter_file_splice_write(struct pipe_inode_info *,
|
||||||
|
struct file *, loff_t *, size_t, unsigned int);
|
||||||
extern ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe,
|
extern ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe,
|
||||||
struct file *out, loff_t *, size_t len, unsigned int flags);
|
struct file *out, loff_t *, size_t len, unsigned int flags);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue