|
@@ -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)
|
|
|
{
|