|
@@ -507,9 +507,116 @@ ssize_t generic_file_splice_read(struct file *in, loff_t *ppos,
|
|
|
|
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
-
|
|
|
|
EXPORT_SYMBOL(generic_file_splice_read);
|
|
EXPORT_SYMBOL(generic_file_splice_read);
|
|
|
|
|
|
|
|
+static const struct pipe_buf_operations default_pipe_buf_ops = {
|
|
|
|
+ .can_merge = 0,
|
|
|
|
+ .map = generic_pipe_buf_map,
|
|
|
|
+ .unmap = generic_pipe_buf_unmap,
|
|
|
|
+ .confirm = generic_pipe_buf_confirm,
|
|
|
|
+ .release = generic_pipe_buf_release,
|
|
|
|
+ .steal = generic_pipe_buf_steal,
|
|
|
|
+ .get = generic_pipe_buf_get,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static ssize_t kernel_readv(struct file *file, const struct iovec *vec,
|
|
|
|
+ unsigned long vlen, loff_t offset)
|
|
|
|
+{
|
|
|
|
+ mm_segment_t old_fs;
|
|
|
|
+ loff_t pos = offset;
|
|
|
|
+ ssize_t res;
|
|
|
|
+
|
|
|
|
+ old_fs = get_fs();
|
|
|
|
+ set_fs(get_ds());
|
|
|
|
+ /* The cast to a user pointer is valid due to the set_fs() */
|
|
|
|
+ res = vfs_readv(file, (const struct iovec __user *)vec, vlen, &pos);
|
|
|
|
+ set_fs(old_fs);
|
|
|
|
+
|
|
|
|
+ return res;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+ssize_t default_file_splice_read(struct file *in, loff_t *ppos,
|
|
|
|
+ struct pipe_inode_info *pipe, size_t len,
|
|
|
|
+ unsigned int flags)
|
|
|
|
+{
|
|
|
|
+ unsigned int nr_pages;
|
|
|
|
+ unsigned int nr_freed;
|
|
|
|
+ size_t offset;
|
|
|
|
+ struct page *pages[PIPE_BUFFERS];
|
|
|
|
+ struct partial_page partial[PIPE_BUFFERS];
|
|
|
|
+ struct iovec vec[PIPE_BUFFERS];
|
|
|
|
+ pgoff_t index;
|
|
|
|
+ ssize_t res;
|
|
|
|
+ size_t this_len;
|
|
|
|
+ int error;
|
|
|
|
+ int i;
|
|
|
|
+ struct splice_pipe_desc spd = {
|
|
|
|
+ .pages = pages,
|
|
|
|
+ .partial = partial,
|
|
|
|
+ .flags = flags,
|
|
|
|
+ .ops = &default_pipe_buf_ops,
|
|
|
|
+ .spd_release = spd_release_page,
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ index = *ppos >> PAGE_CACHE_SHIFT;
|
|
|
|
+ offset = *ppos & ~PAGE_CACHE_MASK;
|
|
|
|
+ nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
|
|
|
+
|
|
|
|
+ for (i = 0; i < nr_pages && i < PIPE_BUFFERS && len; i++) {
|
|
|
|
+ struct page *page;
|
|
|
|
+
|
|
|
|
+ page = alloc_page(GFP_HIGHUSER);
|
|
|
|
+ error = -ENOMEM;
|
|
|
|
+ if (!page)
|
|
|
|
+ goto err;
|
|
|
|
+
|
|
|
|
+ this_len = min_t(size_t, len, PAGE_CACHE_SIZE - offset);
|
|
|
|
+ vec[i].iov_base = (void __user *) kmap(page);
|
|
|
|
+ vec[i].iov_len = this_len;
|
|
|
|
+ pages[i] = page;
|
|
|
|
+ spd.nr_pages++;
|
|
|
|
+ len -= this_len;
|
|
|
|
+ offset = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ res = kernel_readv(in, vec, spd.nr_pages, *ppos);
|
|
|
|
+ if (res < 0)
|
|
|
|
+ goto err;
|
|
|
|
+
|
|
|
|
+ error = 0;
|
|
|
|
+ if (!res)
|
|
|
|
+ goto err;
|
|
|
|
+
|
|
|
|
+ nr_freed = 0;
|
|
|
|
+ for (i = 0; i < spd.nr_pages; i++) {
|
|
|
|
+ kunmap(pages[i]);
|
|
|
|
+ this_len = min_t(size_t, vec[i].iov_len, res);
|
|
|
|
+ partial[i].offset = 0;
|
|
|
|
+ partial[i].len = this_len;
|
|
|
|
+ if (!this_len) {
|
|
|
|
+ __free_page(pages[i]);
|
|
|
|
+ pages[i] = NULL;
|
|
|
|
+ nr_freed++;
|
|
|
|
+ }
|
|
|
|
+ res -= this_len;
|
|
|
|
+ }
|
|
|
|
+ spd.nr_pages -= nr_freed;
|
|
|
|
+
|
|
|
|
+ res = splice_to_pipe(pipe, &spd);
|
|
|
|
+ if (res > 0)
|
|
|
|
+ *ppos += res;
|
|
|
|
+
|
|
|
|
+ return res;
|
|
|
|
+
|
|
|
|
+err:
|
|
|
|
+ for (i = 0; i < spd.nr_pages; i++) {
|
|
|
|
+ kunmap(pages[i]);
|
|
|
|
+ __free_page(pages[i]);
|
|
|
|
+ }
|
|
|
|
+ return error;
|
|
|
|
+}
|
|
|
|
+EXPORT_SYMBOL(default_file_splice_read);
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos'
|
|
* Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos'
|
|
* using sendpage(). Return the number of bytes sent.
|
|
* using sendpage(). Return the number of bytes sent.
|
|
@@ -933,11 +1040,10 @@ static long do_splice_to(struct file *in, loff_t *ppos,
|
|
struct pipe_inode_info *pipe, size_t len,
|
|
struct pipe_inode_info *pipe, size_t len,
|
|
unsigned int flags)
|
|
unsigned int flags)
|
|
{
|
|
{
|
|
|
|
+ ssize_t (*splice_read)(struct file *, loff_t *,
|
|
|
|
+ struct pipe_inode_info *, size_t, unsigned int);
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
- if (unlikely(!in->f_op || !in->f_op->splice_read))
|
|
|
|
- return -EINVAL;
|
|
|
|
-
|
|
|
|
if (unlikely(!(in->f_mode & FMODE_READ)))
|
|
if (unlikely(!(in->f_mode & FMODE_READ)))
|
|
return -EBADF;
|
|
return -EBADF;
|
|
|
|
|
|
@@ -945,7 +1051,11 @@ static long do_splice_to(struct file *in, loff_t *ppos,
|
|
if (unlikely(ret < 0))
|
|
if (unlikely(ret < 0))
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
- return in->f_op->splice_read(in, ppos, pipe, len, flags);
|
|
|
|
|
|
+ splice_read = in->f_op->splice_read;
|
|
|
|
+ if (!splice_read)
|
|
|
|
+ splice_read = default_file_splice_read;
|
|
|
|
+
|
|
|
|
+ return splice_read(in, ppos, pipe, len, flags);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|