diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index e68604ae3ced..e3bcb927429f 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -1005,48 +1005,170 @@ static ssize_t pipe_get_pages_alloc(struct iov_iter *i,
 	return n;
 }
 
-ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
-		   struct page ***pages, size_t maxsize,
-		   size_t *start)
+/**
+ * iov_iter_pvec_size - find length of page aligned iovecs in iov_iter
+ * @i: iov_iter to in which to find the size
+ * @maxsize: maximum size to return
+ * @npages: return pointer for how many pages this I/O will span
+ *
+ * Some filesystems can stitch together multiple iovecs into a single page
+ * vector when both the previous tail and current base are page aligned. This
+ * function determines how much of the remaining iov_iter can be stuffed into
+ * a single pagevec, up to the provided maxsize value.
+ *
+ * It also calculates how many pages this I/O will span and returns that value
+ * in npages.
+ */
+static size_t iov_iter_pvec_size(const struct iov_iter *i, size_t maxsize,
+				 int *npages)
 {
-	struct page **p;
+	size_t size = min(iov_iter_count(i), maxsize);
+	size_t pv_size = 0;
+	size_t start = 0;
+	bool contig = false, first = true;
 
-	if (maxsize > i->count)
-		maxsize = i->count;
+	if (!size)
+		return 0;
 
+	/* Pipes are naturally aligned for this */
 	if (unlikely(i->type & ITER_PIPE))
-		return pipe_get_pages_alloc(i, pages, maxsize, start);
+		return size;
+
+	/*
+	 * An iov can be page vectored when the current base and previous
+	 * tail are both page aligned. Note that we don't require that the
+	 * initial base in the first iovec also be page aligned.
+	 */
+	iterate_all_kinds(i, size, v,
+		({
+		 if (first || (contig && PAGE_ALIGNED(v.iov_base))) {
+			if (first)
+				start = ((unsigned long)v.iov_base & (PAGE_SIZE - 1));
+			pv_size += v.iov_len;
+			first = false;
+			contig = PAGE_ALIGNED(v.iov_base + v.iov_len);
+		 }; 0;
+		 }),
+		({
+		 if (first || (contig && v.bv_offset == 0)) {
+			if (first)
+				start = v.bv_offset;
+			pv_size += v.bv_len;
+			first = false;
+			contig = PAGE_ALIGNED(v.bv_offset + v.bv_len);
+		 }
+		 }),
+		({
+		 if (first || (contig && PAGE_ALIGNED(v.iov_base))) {
+			if (first)
+				start = ((unsigned long)v.iov_base & (PAGE_SIZE - 1));
+			pv_size += v.iov_len;
+			first = false;
+			contig = PAGE_ALIGNED(v.iov_base + v.iov_len);
+		 }
+		 }))
+	*npages = DIV_ROUND_UP(pv_size + start, PAGE_SIZE);
+	return pv_size;
+}
+
+ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
+		   struct page ***ppages, size_t maxsize,
+		   size_t *pstart)
+{
+	struct page **p, **pc;
+	size_t start = 0;
+	ssize_t len = 0;
+	int npages = 0, res = 0;
+	bool first = true;
+
+	if (unlikely(i->type & ITER_PIPE))
+		return pipe_get_pages_alloc(i, ppages, maxsize, pstart);
+
+	maxsize = iov_iter_pvec_size(i, maxsize, &npages);
+	p = get_pages_array(npages);
+	if (!p)
+		return -ENOMEM;
+
+	pc = p;
 	iterate_all_kinds(i, maxsize, v, ({
 		unsigned long addr = (unsigned long)v.iov_base;
-		size_t len = v.iov_len + (*start = addr & (PAGE_SIZE - 1));
+		size_t slen = v.iov_len;
+		size_t sstart = 0;
 		int n;
-		int res;
 
-		addr &= ~(PAGE_SIZE - 1);
-		n = DIV_ROUND_UP(len, PAGE_SIZE);
-		p = get_pages_array(n);
-		if (!p)
-			return -ENOMEM;
-		res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, p);
-		if (unlikely(res < 0)) {
-			kvfree(p);
-			return res;
+		if (first) {
+			start = addr & (PAGE_SIZE - 1);
+			slen += start;
+			sstart = start;
+			first = false;
 		}
-		*pages = p;
-		return (res == n ? len : res * PAGE_SIZE) - *start;
+
+		n = DIV_ROUND_UP(slen, PAGE_SIZE);
+		if (pc + n > p + npages) {
+			/*
+			 * Eek! Something changed between when we initially
+			 * measured for the page array and now. Maybe a
+			 * userland memory scribble? We haven't overrun
+			 * anything at this point, so we can safely just
+			 * return what we have, if we have gotten anything.
+			 * If this is the first pass, then just return EFAULT.
+			 */
+			if (first)
+				res = -EFAULT;
+			goto out;
+		}
+		addr &= ~(PAGE_SIZE - 1);
+		res = get_user_pages_fast(addr, n,
+					  (i->type & WRITE) != WRITE, pc);
+		if (unlikely(res < 0))
+			goto out;
+		len += (res == n ? slen : res * PAGE_SIZE) - sstart;
+		pc += res;
 	0;}),({
-		/* can't be more than PAGE_SIZE */
-		*start = v.bv_offset;
-		*pages = p = get_pages_array(1);
-		if (!p)
-			return -ENOMEM;
-		get_page(*p = v.bv_page);
-		return v.bv_len;
+		/* bio_vecs are limited to a single page each */
+		if (first) {
+			start = v.bv_offset;
+			first = false;
+		}
+		get_page(*pc = v.bv_page);
+		len += v.bv_len;
+		++pc;
+		if (pc > p + npages) {
+			/*
+			 * Should we return an error here?  This should never
+			 * happen as kernel callers had better not muck with
+			 * the array while we're iterating over it.
+			 *
+			 * At this point, we haven't overrun anything so we
+			 * can just return what we have gotten so far. Still,
+			 * it looks like something is wrong, so pop a warning
+			 * here.
+			 */
+			WARN_ONCE(true, "%s: array overrun (%p > %p + %d)\n",
+					__func__, pc, p, npages);
+			goto out;
+		}
+
+		BUG_ON(pc > p + npages);
 	}),({
-		return -EFAULT;
+		/* FIXME: should we handle this case? */
+		res = -EFAULT;
+		goto out;
 	})
 	)
-	return 0;
+out:
+	if (unlikely(res < 0)) {
+		struct page **i;
+
+		for (i = p; i < pc; i++)
+			put_page(*i);
+		kvfree(p);
+		return res;
+	}
+
+	*ppages = p;
+	*pstart = start;
+	return len;
 }
 EXPORT_SYMBOL(iov_iter_get_pages_alloc);
 
