summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKent Overstreet <kmo@daterainc.com>2013-11-26 15:52:25 -0800
committerKent Overstreet <kmo@daterainc.com>2014-06-17 18:25:54 -0700
commit75b1f0d07da6c0dbd823cd23b11bc697fdf09e41 (patch)
tree9fd98396dd6bc6d28777868526a6b1020b2cc551
parent94bea102bae9172364eb7bdf9e7a9785bcddfae0 (diff)
block: iov_count_pages()
Signed-off-by: Kent Overstreet <kmo@daterainc.com>
-rw-r--r--block/bio.c42
-rw-r--r--include/linux/uio.h2
-rw-r--r--lib/iovec.c30
3 files changed, 38 insertions, 36 deletions
diff --git a/block/bio.c b/block/bio.c
index 8a5d8239ecb8..bdb3896d2b58 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1088,23 +1088,9 @@ struct bio *bio_copy_user_iov(struct request_queue *q,
unsigned int len;
unsigned int offset = map_data ? map_data->offset & ~PAGE_MASK : 0;
- for (i = 0; i < iter->nr_segs; i++) {
- unsigned long uaddr;
- unsigned long end;
- unsigned long start;
-
- uaddr = (unsigned long) iter->iov[i].iov_base;
- end = (uaddr + iter->iov[i].iov_len + PAGE_SIZE - 1) >> PAGE_SHIFT;
- start = uaddr >> PAGE_SHIFT;
-
- /*
- * Overflow, abort
- */
- if (end < start)
- return ERR_PTR(-EINVAL);
-
- nr_pages += end - start;
- }
+ nr_pages = iov_count_pages(iter, 0);
+ if (nr_pages < 0)
+ return ERR_PTR(nr_pages);
if (offset)
nr_pages++;
@@ -1234,25 +1220,9 @@ static struct bio *__bio_map_user_iov(struct request_queue *q,
struct iov_iter i;
struct iovec iov;
- iov_for_each(iov, i, *iter) {
- unsigned long uaddr = (unsigned long) iov.iov_base;
- unsigned long len = iov.iov_len;
- unsigned long end = (uaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
- unsigned long start = uaddr >> PAGE_SHIFT;
-
- /*
- * Overflow, abort
- */
- if (end < start)
- return ERR_PTR(-EINVAL);
-
- nr_pages += end - start;
- /*
- * buffer must be aligned to at least hardsector size for now
- */
- if (uaddr & queue_dma_alignment(q))
- return ERR_PTR(-EINVAL);
- }
+ nr_pages = iov_count_pages(iter, queue_dma_alignment(q));
+ if (nr_pages < 0)
+ return ERR_PTR(nr_pages);
if (!nr_pages)
return ERR_PTR(-EINVAL);
diff --git a/include/linux/uio.h b/include/linux/uio.h
index e2231e47cec1..c08110705118 100644
--- a/include/linux/uio.h
+++ b/include/linux/uio.h
@@ -71,6 +71,8 @@ static inline struct iovec iov_iter_iovec(const struct iov_iter *iter)
unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to);
+int iov_count_pages(const struct iov_iter *iter, unsigned align);
+
size_t iov_iter_copy_from_user_atomic(struct page *page,
struct iov_iter *i, unsigned long offset, size_t bytes);
void iov_iter_advance(struct iov_iter *i, size_t bytes);
diff --git a/lib/iovec.c b/lib/iovec.c
index 454baa88bf27..fa2c050dc137 100644
--- a/lib/iovec.c
+++ b/lib/iovec.c
@@ -1,5 +1,7 @@
#include <linux/uaccess.h>
#include <linux/export.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
#include <linux/uio.h>
/*
@@ -51,3 +53,31 @@ int memcpy_toiovec(struct iovec *iov, unsigned char *kdata, int len)
return 0;
}
EXPORT_SYMBOL(memcpy_toiovec);
+
+int iov_count_pages(const struct iov_iter *iter, unsigned align)
+{
+ struct iov_iter i = *iter;
+ int nr_pages = 0;
+
+ while (iov_iter_count(&i)) {
+ unsigned long uaddr = (unsigned long) i.iov->iov_base +
+ i.iov_offset;
+ unsigned long len = i.iov->iov_len - i.iov_offset;
+
+ if ((uaddr & align) || (len & align))
+ return -EINVAL;
+
+ /*
+ * Overflow, abort
+ */
+ if (uaddr + len < uaddr)
+ return -EINVAL;
+
+ nr_pages += DIV_ROUND_UP(len + offset_in_page(uaddr),
+ PAGE_SIZE);
+ iov_iter_advance(&i, len);
+ }
+
+ return nr_pages;
+}
+EXPORT_SYMBOL(iov_count_pages);