]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - mm/iov_iter.c
new helper: iov_iter_get_pages_alloc()
[mirror_ubuntu-artful-kernel.git] / mm / iov_iter.c
index 0b677f8f9bad8576ad154c2707677fa32cd980d7..a5c691c1a283c62466ad4596d27035329a0d7949 100644 (file)
@@ -1,6 +1,8 @@
 #include <linux/export.h>
 #include <linux/uio.h>
 #include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
 
 size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
                         struct iov_iter *i)
@@ -263,6 +265,44 @@ ssize_t iov_iter_get_pages(struct iov_iter *i,
 }
 EXPORT_SYMBOL(iov_iter_get_pages);
 
+ssize_t iov_iter_get_pages_alloc(struct iov_iter *i,
+                  struct page ***pages, size_t maxsize,
+                  size_t *start)
+{
+       size_t offset = i->iov_offset;
+       const struct iovec *iov = i->iov;
+       size_t len;
+       unsigned long addr;
+       void *p;
+       int n;
+       int res;
+
+       len = iov->iov_len - offset;
+       if (len > i->count)
+               len = i->count;
+       if (len > maxsize)
+               len = maxsize;
+       addr = (unsigned long)iov->iov_base + offset;
+       len += *start = addr & (PAGE_SIZE - 1);
+       addr &= ~(PAGE_SIZE - 1);
+       n = (len + PAGE_SIZE - 1) / PAGE_SIZE;
+       
+       p = kmalloc(n * sizeof(struct page *), GFP_KERNEL);
+       if (!p)
+               p = vmalloc(n * sizeof(struct page *));
+       if (!p)
+               return -ENOMEM;
+
+       res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, p);
+       if (unlikely(res < 0)) {
+               kvfree(p);
+               return res;
+       }
+       *pages = p;
+       return (res == n ? len : res * PAGE_SIZE) - *start;
+}
+EXPORT_SYMBOL(iov_iter_get_pages_alloc);
+
 int iov_iter_npages(const struct iov_iter *i, int maxpages)
 {
        size_t offset = i->iov_offset;