diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2014-11-27 14:14:31 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2014-11-27 18:44:12 -0500 |
commit | 1b17f1f2e56a091deb99a068a6f1e82b5bb76b09 (patch) | |
tree | 064b5d30603cb316e6dd2ca820ae09c33cd4a7a8 /mm/iov_iter.c | |
parent | e5393fae3b49e80179f04afdc0916fcb6846ef17 (diff) |
iov_iter.c: convert iov_iter_get_pages_alloc() to iterate_all_kinds
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'mm/iov_iter.c')
-rw-r--r-- | mm/iov_iter.c | 107 |
1 files changed, 45 insertions, 62 deletions
diff --git a/mm/iov_iter.c b/mm/iov_iter.c index 75e29ef59b0e..3214b9b493ae 100644 --- a/mm/iov_iter.c +++ b/mm/iov_iter.c | |||
@@ -428,43 +428,6 @@ void iov_iter_init(struct iov_iter *i, int direction, | |||
428 | } | 428 | } |
429 | EXPORT_SYMBOL(iov_iter_init); | 429 | EXPORT_SYMBOL(iov_iter_init); |
430 | 430 | ||
431 | static ssize_t get_pages_alloc_iovec(struct iov_iter *i, | ||
432 | struct page ***pages, size_t maxsize, | ||
433 | size_t *start) | ||
434 | { | ||
435 | size_t offset = i->iov_offset; | ||
436 | const struct iovec *iov = i->iov; | ||
437 | size_t len; | ||
438 | unsigned long addr; | ||
439 | void *p; | ||
440 | int n; | ||
441 | int res; | ||
442 | |||
443 | len = iov->iov_len - offset; | ||
444 | if (len > i->count) | ||
445 | len = i->count; | ||
446 | if (len > maxsize) | ||
447 | len = maxsize; | ||
448 | addr = (unsigned long)iov->iov_base + offset; | ||
449 | len += *start = addr & (PAGE_SIZE - 1); | ||
450 | addr &= ~(PAGE_SIZE - 1); | ||
451 | n = (len + PAGE_SIZE - 1) / PAGE_SIZE; | ||
452 | |||
453 | p = kmalloc(n * sizeof(struct page *), GFP_KERNEL); | ||
454 | if (!p) | ||
455 | p = vmalloc(n * sizeof(struct page *)); | ||
456 | if (!p) | ||
457 | return -ENOMEM; | ||
458 | |||
459 | res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, p); | ||
460 | if (unlikely(res < 0)) { | ||
461 | kvfree(p); | ||
462 | return res; | ||
463 | } | ||
464 | *pages = p; | ||
465 | return (res == n ? len : res * PAGE_SIZE) - *start; | ||
466 | } | ||
467 | |||
468 | static void memcpy_from_page(char *to, struct page *page, size_t offset, size_t len) | 431 | static void memcpy_from_page(char *to, struct page *page, size_t offset, size_t len) |
469 | { | 432 | { |
470 | char *from = kmap_atomic(page); | 433 | char *from = kmap_atomic(page); |
@@ -622,27 +585,6 @@ static size_t zero_bvec(size_t bytes, struct iov_iter *i) | |||
622 | return wanted - bytes; | 585 | return wanted - bytes; |
623 | } | 586 | } |
624 | 587 | ||
625 | static ssize_t get_pages_alloc_bvec(struct iov_iter *i, | ||
626 | struct page ***pages, size_t maxsize, | ||
627 | size_t *start) | ||
628 | { | ||
629 | const struct bio_vec *bvec = i->bvec; | ||
630 | size_t len = bvec->bv_len - i->iov_offset; | ||
631 | if (len > i->count) | ||
632 | len = i->count; | ||
633 | if (len > maxsize) | ||
634 | len = maxsize; | ||
635 | *start = bvec->bv_offset + i->iov_offset; | ||
636 | |||
637 | *pages = kmalloc(sizeof(struct page *), GFP_KERNEL); | ||
638 | if (!*pages) | ||
639 | return -ENOMEM; | ||
640 | |||
641 | get_page(**pages = bvec->bv_page); | ||
642 | |||
643 | return len; | ||
644 | } | ||
645 | |||
646 | size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, | 588 | size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes, |
647 | struct iov_iter *i) | 589 | struct iov_iter *i) |
648 | { | 590 | { |
@@ -777,14 +719,55 @@ ssize_t iov_iter_get_pages(struct iov_iter *i, | |||
777 | } | 719 | } |
778 | EXPORT_SYMBOL(iov_iter_get_pages); | 720 | EXPORT_SYMBOL(iov_iter_get_pages); |
779 | 721 | ||
722 | static struct page **get_pages_array(size_t n) | ||
723 | { | ||
724 | struct page **p = kmalloc(n * sizeof(struct page *), GFP_KERNEL); | ||
725 | if (!p) | ||
726 | p = vmalloc(n * sizeof(struct page *)); | ||
727 | return p; | ||
728 | } | ||
729 | |||
780 | ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, | 730 | ssize_t iov_iter_get_pages_alloc(struct iov_iter *i, |
781 | struct page ***pages, size_t maxsize, | 731 | struct page ***pages, size_t maxsize, |
782 | size_t *start) | 732 | size_t *start) |
783 | { | 733 | { |
784 | if (i->type & ITER_BVEC) | 734 | struct page **p; |
785 | return get_pages_alloc_bvec(i, pages, maxsize, start); | 735 | |
786 | else | 736 | if (maxsize > i->count) |
787 | return get_pages_alloc_iovec(i, pages, maxsize, start); | 737 | maxsize = i->count; |
738 | |||
739 | if (!maxsize) | ||
740 | return 0; | ||
741 | |||
742 | iterate_all_kinds(i, maxsize, v, ({ | ||
743 | unsigned long addr = (unsigned long)v.iov_base; | ||
744 | size_t len = v.iov_len + (*start = addr & (PAGE_SIZE - 1)); | ||
745 | int n; | ||
746 | int res; | ||
747 | |||
748 | addr &= ~(PAGE_SIZE - 1); | ||
749 | n = DIV_ROUND_UP(len, PAGE_SIZE); | ||
750 | p = get_pages_array(n); | ||
751 | if (!p) | ||
752 | return -ENOMEM; | ||
753 | res = get_user_pages_fast(addr, n, (i->type & WRITE) != WRITE, p); | ||
754 | if (unlikely(res < 0)) { | ||
755 | kvfree(p); | ||
756 | return res; | ||
757 | } | ||
758 | *pages = p; | ||
759 | return (res == n ? len : res * PAGE_SIZE) - *start; | ||
760 | 0;}),({ | ||
761 | /* can't be more than PAGE_SIZE */ | ||
762 | *start = v.bv_offset; | ||
763 | *pages = p = get_pages_array(1); | ||
764 | if (!p) | ||
765 | return -ENOMEM; | ||
766 | get_page(*p = v.bv_page); | ||
767 | return v.bv_len; | ||
768 | }) | ||
769 | ) | ||
770 | return 0; | ||
788 | } | 771 | } |
789 | EXPORT_SYMBOL(iov_iter_get_pages_alloc); | 772 | EXPORT_SYMBOL(iov_iter_get_pages_alloc); |
790 | 773 | ||