Loading fs/splice.c +36 −99 Original line number Original line Diff line number Diff line Loading @@ -1434,106 +1434,32 @@ static long do_splice(struct file *in, loff_t __user *off_in, return -EINVAL; return -EINVAL; } } /* static int get_iovec_page_array(struct iov_iter *from, * Map an iov into an array of pages and offset/length tupples. With the struct page **pages, * partial_page structure, we can map several non-contiguous ranges into struct partial_page *partial, * 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, unsigned int pipe_buffers) unsigned int pipe_buffers) { { int buffers = 0, error = 0; int buffers = 0; while (iov_iter_count(from)) { while (nr_vecs) { ssize_t copied; unsigned long off, npages; size_t start; struct iovec entry; void __user *base; copied = iov_iter_get_pages(from, pages + buffers, ~0UL, size_t len; pipe_buffers - buffers, &start); int i; if (copied <= 0) return buffers ? buffers : copied; error = -EFAULT; if (copy_from_user(&entry, iov, sizeof(entry))) iov_iter_advance(from, copied); break; while (copied) { int size = min_t(int, copied, PAGE_SIZE - start); base = entry.iov_base; partial[buffers].offset = start; len = entry.iov_len; partial[buffers].len = size; copied -= size; /* start = 0; * 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; buffers++; 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 buffers; return error; } } static int pipe_to_user(struct pipe_inode_info *pipe, struct pipe_buffer *buf, static int pipe_to_user(struct pipe_inode_info *pipe, struct pipe_buffer *buf, Loading Loading @@ -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 * as splice-from-memory, where the regular splice is splice-from-file (or * to file). In both cases the output is a pipe, naturally. * 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) unsigned long nr_segs, unsigned int flags) { { struct pipe_inode_info *pipe; 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 page *pages[PIPE_DEF_BUFFERS]; struct partial_page partial[PIPE_DEF_BUFFERS]; struct partial_page partial[PIPE_DEF_BUFFERS]; struct splice_pipe_desc spd = { struct splice_pipe_desc spd = { Loading @@ -1607,11 +1536,18 @@ static long vmsplice_to_pipe(struct file *file, const struct iovec __user *iov, if (!pipe) if (!pipe) return -EBADF; return -EBADF; if (splice_grow_spd(pipe, &spd)) ret = import_iovec(WRITE, uiov, nr_segs, ARRAY_SIZE(iovstack), &iov, &from); if (ret < 0) return ret; if (splice_grow_spd(pipe, &spd)) { kfree(iov); return -ENOMEM; return -ENOMEM; } spd.nr_pages = get_iovec_page_array(iov, nr_segs, spd.pages, spd.nr_pages = get_iovec_page_array(&from, spd.pages, spd.partial, false, spd.partial, spd.nr_pages_max); spd.nr_pages_max); if (spd.nr_pages <= 0) if (spd.nr_pages <= 0) ret = spd.nr_pages; ret = spd.nr_pages; Loading @@ -1619,6 +1555,7 @@ static long vmsplice_to_pipe(struct file *file, const struct iovec __user *iov, ret = splice_to_pipe(pipe, &spd); ret = splice_to_pipe(pipe, &spd); splice_shrink_spd(&spd); splice_shrink_spd(&spd); kfree(iov); return ret; return ret; } } Loading Loading
fs/splice.c +36 −99 Original line number Original line Diff line number Diff line Loading @@ -1434,106 +1434,32 @@ static long do_splice(struct file *in, loff_t __user *off_in, return -EINVAL; return -EINVAL; } } /* static int get_iovec_page_array(struct iov_iter *from, * Map an iov into an array of pages and offset/length tupples. With the struct page **pages, * partial_page structure, we can map several non-contiguous ranges into struct partial_page *partial, * 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, unsigned int pipe_buffers) unsigned int pipe_buffers) { { int buffers = 0, error = 0; int buffers = 0; while (iov_iter_count(from)) { while (nr_vecs) { ssize_t copied; unsigned long off, npages; size_t start; struct iovec entry; void __user *base; copied = iov_iter_get_pages(from, pages + buffers, ~0UL, size_t len; pipe_buffers - buffers, &start); int i; if (copied <= 0) return buffers ? buffers : copied; error = -EFAULT; if (copy_from_user(&entry, iov, sizeof(entry))) iov_iter_advance(from, copied); break; while (copied) { int size = min_t(int, copied, PAGE_SIZE - start); base = entry.iov_base; partial[buffers].offset = start; len = entry.iov_len; partial[buffers].len = size; copied -= size; /* start = 0; * 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; buffers++; 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 buffers; return error; } } static int pipe_to_user(struct pipe_inode_info *pipe, struct pipe_buffer *buf, static int pipe_to_user(struct pipe_inode_info *pipe, struct pipe_buffer *buf, Loading Loading @@ -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 * as splice-from-memory, where the regular splice is splice-from-file (or * to file). In both cases the output is a pipe, naturally. * 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) unsigned long nr_segs, unsigned int flags) { { struct pipe_inode_info *pipe; 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 page *pages[PIPE_DEF_BUFFERS]; struct partial_page partial[PIPE_DEF_BUFFERS]; struct partial_page partial[PIPE_DEF_BUFFERS]; struct splice_pipe_desc spd = { struct splice_pipe_desc spd = { Loading @@ -1607,11 +1536,18 @@ static long vmsplice_to_pipe(struct file *file, const struct iovec __user *iov, if (!pipe) if (!pipe) return -EBADF; return -EBADF; if (splice_grow_spd(pipe, &spd)) ret = import_iovec(WRITE, uiov, nr_segs, ARRAY_SIZE(iovstack), &iov, &from); if (ret < 0) return ret; if (splice_grow_spd(pipe, &spd)) { kfree(iov); return -ENOMEM; return -ENOMEM; } spd.nr_pages = get_iovec_page_array(iov, nr_segs, spd.pages, spd.nr_pages = get_iovec_page_array(&from, spd.pages, spd.partial, false, spd.partial, spd.nr_pages_max); spd.nr_pages_max); if (spd.nr_pages <= 0) if (spd.nr_pages <= 0) ret = spd.nr_pages; ret = spd.nr_pages; Loading @@ -1619,6 +1555,7 @@ static long vmsplice_to_pipe(struct file *file, const struct iovec __user *iov, ret = splice_to_pipe(pipe, &spd); ret = splice_to_pipe(pipe, &spd); splice_shrink_spd(&spd); splice_shrink_spd(&spd); kfree(iov); return ret; return ret; } } Loading