splice: switch get_iovec_page_array() to iov_iter
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Родитель
e7c3c64624
Коммит
db85a9eb2e
133
fs/splice.c
133
fs/splice.c
|
@ -1434,106 +1434,32 @@ static long do_splice(struct file *in, loff_t __user *off_in,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map an iov into an array of pages and offset/length tupples. With the
|
||||
* partial_page structure, we can map several non-contiguous ranges into
|
||||
* our ones pages[] map instead of splitting that operation into pieces.
|
||||
* Could easily be exported as a generic helper for other users, in which
|
||||
* case one would probably want to add a 'max_nr_pages' parameter as well.
|
||||
*/
|
||||
static int get_iovec_page_array(const struct iovec __user *iov,
|
||||
unsigned int nr_vecs, struct page **pages,
|
||||
struct partial_page *partial, bool aligned,
|
||||
static int get_iovec_page_array(struct iov_iter *from,
|
||||
struct page **pages,
|
||||
struct partial_page *partial,
|
||||
unsigned int pipe_buffers)
|
||||
{
|
||||
int buffers = 0, error = 0;
|
||||
int buffers = 0;
|
||||
while (iov_iter_count(from)) {
|
||||
ssize_t copied;
|
||||
size_t start;
|
||||
|
||||
while (nr_vecs) {
|
||||
unsigned long off, npages;
|
||||
struct iovec entry;
|
||||
void __user *base;
|
||||
size_t len;
|
||||
int i;
|
||||
copied = iov_iter_get_pages(from, pages + buffers, ~0UL,
|
||||
pipe_buffers - buffers, &start);
|
||||
if (copied <= 0)
|
||||
return buffers ? buffers : copied;
|
||||
|
||||
error = -EFAULT;
|
||||
if (copy_from_user(&entry, iov, sizeof(entry)))
|
||||
break;
|
||||
|
||||
base = entry.iov_base;
|
||||
len = entry.iov_len;
|
||||
|
||||
/*
|
||||
* Sanity check this iovec. 0 read succeeds.
|
||||
*/
|
||||
error = 0;
|
||||
if (unlikely(!len))
|
||||
break;
|
||||
error = -EFAULT;
|
||||
if (!access_ok(VERIFY_READ, base, len))
|
||||
break;
|
||||
|
||||
/*
|
||||
* Get this base offset and number of pages, then map
|
||||
* in the user pages.
|
||||
*/
|
||||
off = (unsigned long) base & ~PAGE_MASK;
|
||||
|
||||
/*
|
||||
* If asked for alignment, the offset must be zero and the
|
||||
* length a multiple of the PAGE_SIZE.
|
||||
*/
|
||||
error = -EINVAL;
|
||||
if (aligned && (off || len & ~PAGE_MASK))
|
||||
break;
|
||||
|
||||
npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
if (npages > pipe_buffers - buffers)
|
||||
npages = pipe_buffers - buffers;
|
||||
|
||||
error = get_user_pages_fast((unsigned long)base, npages,
|
||||
0, &pages[buffers]);
|
||||
|
||||
if (unlikely(error <= 0))
|
||||
break;
|
||||
|
||||
/*
|
||||
* Fill this contiguous range into the partial page map.
|
||||
*/
|
||||
for (i = 0; i < error; i++) {
|
||||
const int plen = min_t(size_t, len, PAGE_SIZE - off);
|
||||
|
||||
partial[buffers].offset = off;
|
||||
partial[buffers].len = plen;
|
||||
|
||||
off = 0;
|
||||
len -= plen;
|
||||
iov_iter_advance(from, copied);
|
||||
while (copied) {
|
||||
int size = min_t(int, copied, PAGE_SIZE - start);
|
||||
partial[buffers].offset = start;
|
||||
partial[buffers].len = size;
|
||||
copied -= size;
|
||||
start = 0;
|
||||
buffers++;
|
||||
}
|
||||
|
||||
/*
|
||||
* We didn't complete this iov, stop here since it probably
|
||||
* means we have to move some of this into a pipe to
|
||||
* be able to continue.
|
||||
*/
|
||||
if (len)
|
||||
break;
|
||||
|
||||
/*
|
||||
* Don't continue if we mapped fewer pages than we asked for,
|
||||
* or if we mapped the max number of pages that we have
|
||||
* room for.
|
||||
*/
|
||||
if (error < npages || buffers == pipe_buffers)
|
||||
break;
|
||||
|
||||
nr_vecs--;
|
||||
iov++;
|
||||
}
|
||||
|
||||
if (buffers)
|
||||
return buffers;
|
||||
|
||||
return error;
|
||||
return buffers;
|
||||
}
|
||||
|
||||
static int pipe_to_user(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
|
||||
|
@ -1587,10 +1513,13 @@ static long vmsplice_to_user(struct file *file, const struct iovec __user *uiov,
|
|||
* as splice-from-memory, where the regular splice is splice-from-file (or
|
||||
* to file). In both cases the output is a pipe, naturally.
|
||||
*/
|
||||
static long vmsplice_to_pipe(struct file *file, const struct iovec __user *iov,
|
||||
static long vmsplice_to_pipe(struct file *file, const struct iovec __user *uiov,
|
||||
unsigned long nr_segs, unsigned int flags)
|
||||
{
|
||||
struct pipe_inode_info *pipe;
|
||||
struct iovec iovstack[UIO_FASTIOV];
|
||||
struct iovec *iov = iovstack;
|
||||
struct iov_iter from;
|
||||
struct page *pages[PIPE_DEF_BUFFERS];
|
||||
struct partial_page partial[PIPE_DEF_BUFFERS];
|
||||
struct splice_pipe_desc spd = {
|
||||
|
@ -1607,11 +1536,18 @@ static long vmsplice_to_pipe(struct file *file, const struct iovec __user *iov,
|
|||
if (!pipe)
|
||||
return -EBADF;
|
||||
|
||||
if (splice_grow_spd(pipe, &spd))
|
||||
return -ENOMEM;
|
||||
ret = import_iovec(WRITE, uiov, nr_segs,
|
||||
ARRAY_SIZE(iovstack), &iov, &from);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
spd.nr_pages = get_iovec_page_array(iov, nr_segs, spd.pages,
|
||||
spd.partial, false,
|
||||
if (splice_grow_spd(pipe, &spd)) {
|
||||
kfree(iov);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
spd.nr_pages = get_iovec_page_array(&from, spd.pages,
|
||||
spd.partial,
|
||||
spd.nr_pages_max);
|
||||
if (spd.nr_pages <= 0)
|
||||
ret = spd.nr_pages;
|
||||
|
@ -1619,6 +1555,7 @@ static long vmsplice_to_pipe(struct file *file, const struct iovec __user *iov,
|
|||
ret = splice_to_pipe(pipe, &spd);
|
||||
|
||||
splice_shrink_spd(&spd);
|
||||
kfree(iov);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче