diff options
| -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 | ||
