mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 19:51:31 -04:00
->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 <[email protected]>
This commit is contained in:
parent
62a8067a7f
commit
8d0207652c
16 changed files with 156 additions and 56 deletions
140
fs/splice.c
140
fs/splice.c
|
|
@ -32,6 +32,7 @@
|
|||
#include <linux/gfp.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/aio.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);
|
||||
|
||||
/**
|
||||
* 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,
|
||||
struct splice_desc *sd)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue