diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2014-03-21 04:58:33 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2014-05-06 17:32:53 -0400 |
commit | 91f79c43d1b54d7154b118860d81b39bad07dfff (patch) | |
tree | a5b142ba57fdabf835476b6dbca24288a78f0c53 /mm/iov_iter.c | |
parent | f67da30c1d5fc9e341bc8121708874bfd7b31e45 (diff) | |
download | linux-stable-91f79c43d1b54d7154b118860d81b39bad07dfff.tar.gz linux-stable-91f79c43d1b54d7154b118860d81b39bad07dfff.tar.bz2 linux-stable-91f79c43d1b54d7154b118860d81b39bad07dfff.zip |
new helper: iov_iter_get_pages_alloc()
same as iov_iter_get_pages(), except that pages array is allocated
(kmalloc if possible, vmalloc if that fails) and left for caller to
free. Lustre and NFS ->direct_IO() switched to it.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'mm/iov_iter.c')
-rw-r--r-- | mm/iov_iter.c | 40 |
1 files changed, 40 insertions, 0 deletions
diff --git a/mm/iov_iter.c b/mm/iov_iter.c index 0b677f8f9bad..a5c691c1a283 100644 --- a/mm/iov_iter.c +++ b/mm/iov_iter.c @@ -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; |