diff options
Diffstat (limited to 'fs/bio.c')
-rw-r--r-- | fs/bio.c | 36 |
1 files changed, 22 insertions, 14 deletions
@@ -788,6 +788,7 @@ struct bio *bio_copy_user_iov(struct request_queue *q, | |||
788 | int i, ret; | 788 | int i, ret; |
789 | int nr_pages = 0; | 789 | int nr_pages = 0; |
790 | unsigned int len = 0; | 790 | unsigned int len = 0; |
791 | unsigned int offset = map_data ? map_data->offset & ~PAGE_MASK : 0; | ||
791 | 792 | ||
792 | for (i = 0; i < iov_count; i++) { | 793 | for (i = 0; i < iov_count; i++) { |
793 | unsigned long uaddr; | 794 | unsigned long uaddr; |
@@ -814,35 +815,42 @@ struct bio *bio_copy_user_iov(struct request_queue *q, | |||
814 | bio->bi_rw |= (!write_to_vm << BIO_RW); | 815 | bio->bi_rw |= (!write_to_vm << BIO_RW); |
815 | 816 | ||
816 | ret = 0; | 817 | ret = 0; |
817 | i = 0; | 818 | |
819 | if (map_data) { | ||
820 | nr_pages = 1 << map_data->page_order; | ||
821 | i = map_data->offset / PAGE_SIZE; | ||
822 | } | ||
818 | while (len) { | 823 | while (len) { |
819 | unsigned int bytes; | 824 | unsigned int bytes = PAGE_SIZE; |
820 | 825 | ||
821 | if (map_data) | 826 | bytes -= offset; |
822 | bytes = 1U << (PAGE_SHIFT + map_data->page_order); | ||
823 | else | ||
824 | bytes = PAGE_SIZE; | ||
825 | 827 | ||
826 | if (bytes > len) | 828 | if (bytes > len) |
827 | bytes = len; | 829 | bytes = len; |
828 | 830 | ||
829 | if (map_data) { | 831 | if (map_data) { |
830 | if (i == map_data->nr_entries) { | 832 | if (i == map_data->nr_entries * nr_pages) { |
831 | ret = -ENOMEM; | 833 | ret = -ENOMEM; |
832 | break; | 834 | break; |
833 | } | 835 | } |
834 | page = map_data->pages[i++]; | 836 | |
835 | } else | 837 | page = map_data->pages[i / nr_pages]; |
838 | page += (i % nr_pages); | ||
839 | |||
840 | i++; | ||
841 | } else { | ||
836 | page = alloc_page(q->bounce_gfp | gfp_mask); | 842 | page = alloc_page(q->bounce_gfp | gfp_mask); |
837 | if (!page) { | 843 | if (!page) { |
838 | ret = -ENOMEM; | 844 | ret = -ENOMEM; |
839 | break; | 845 | break; |
846 | } | ||
840 | } | 847 | } |
841 | 848 | ||
842 | if (bio_add_pc_page(q, bio, page, bytes, 0) < bytes) | 849 | if (bio_add_pc_page(q, bio, page, bytes, offset) < bytes) |
843 | break; | 850 | break; |
844 | 851 | ||
845 | len -= bytes; | 852 | len -= bytes; |
853 | offset = 0; | ||
846 | } | 854 | } |
847 | 855 | ||
848 | if (ret) | 856 | if (ret) |
@@ -851,7 +859,7 @@ struct bio *bio_copy_user_iov(struct request_queue *q, | |||
851 | /* | 859 | /* |
852 | * success | 860 | * success |
853 | */ | 861 | */ |
854 | if (!write_to_vm) { | 862 | if (!write_to_vm && (!map_data || !map_data->null_mapped)) { |
855 | ret = __bio_copy_iov(bio, bio->bi_io_vec, iov, iov_count, 0, 0); | 863 | ret = __bio_copy_iov(bio, bio->bi_io_vec, iov, iov_count, 0, 0); |
856 | if (ret) | 864 | if (ret) |
857 | goto cleanup; | 865 | goto cleanup; |