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 | |
parent | f67da30c1d5fc9e341bc8121708874bfd7b31e45 (diff) |
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')
-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 @@ | |||
1 | #include <linux/export.h> | 1 | #include <linux/export.h> |
2 | #include <linux/uio.h> | 2 | #include <linux/uio.h> |
3 | #include <linux/pagemap.h> | 3 | #include <linux/pagemap.h> |
4 | #include <linux/slab.h> | ||
5 | #include <linux/vmalloc.h> | ||
4 | 6 | ||
5 | size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, | 7 | size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, |
6 | struct iov_iter *i) | 8 | struct iov_iter *i) |
@@ -263,6 +265,44 @@ ssize_t iov_iter_get_pages(struct iov_iter *i, | |||
263 | } | 265 | } |
264 | EXPORT_SYMBOL(iov_iter_get_pages); | 266 | EXPORT_SYMBOL(iov_iter_get_pages); |
265 | 267 | ||
268 | ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, | ||
269 | struct page ***pages, size_t maxsize, | ||
270 | size_t *start) | ||
271 | { | ||
272 | size_t offset = i->iov_offset; | ||
273 | const struct iovec *iov = i->iov; | ||
274 | size_t len; | ||
275 | unsigned long addr; | ||
276 | void *p; | ||
277 | int n; | ||
278 | int res; | ||
279 | |||
280 | len = iov->iov_len - offset; | ||
281 | if (len > i->count) | ||
282 | len = i->count; | ||
283 | if (len > maxsize) | ||
284 | len = maxsize; | ||
285 | addr = (unsigned long)iov->iov_base + offset; | ||
286 | len += *start = addr & (PAGE_SIZE - 1); | ||
287 | addr &= ~(PAGE_SIZE - 1); | ||
288 | n = (len + PAGE_SIZE - 1) / PAGE_SIZE; | ||
289 | |||
290 | p = kmalloc(n * sizeof(struct page *), GFP_KERNEL); | ||
291 | if (!p) | ||
292 | p = vmalloc(n * sizeof(struct page *)); | ||
293 | if (!p) | ||
294 | return -ENOMEM; | ||
295 | |||
296 | res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, p); | ||
297 | if (unlikely(res < 0)) { | ||
298 | kvfree(p); | ||
299 | return res; | ||
300 | } | ||
301 | *pages = p; | ||
302 | return (res == n ? len : res * PAGE_SIZE) - *start; | ||
303 | } | ||
304 | EXPORT_SYMBOL(iov_iter_get_pages_alloc); | ||
305 | |||
266 | int iov_iter_npages(const struct iov_iter *i, int maxpages) | 306 | int iov_iter_npages(const struct iov_iter *i, int maxpages) |
267 | { | 307 | { |
268 | size_t offset = i->iov_offset; | 308 | size_t offset = i->iov_offset; |