Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit db85a9eb authored by Al Viro's avatar Al Viro
Browse files

splice: switch get_iovec_page_array() to iov_iter



Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent e7c3c646
Loading
Loading
Loading
Loading
+36 −99
Original line number Original line Diff line number Diff line
@@ -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,
@@ -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 = {
@@ -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;
@@ -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;
}
}