diff options
Diffstat (limited to 'fs/bio.c')
-rw-r--r-- | fs/bio.c | 90 |
1 files changed, 90 insertions, 0 deletions
@@ -937,6 +937,95 @@ struct bio *bio_map_kern(struct request_queue *q, void *data, unsigned int len, | |||
937 | return ERR_PTR(-EINVAL); | 937 | return ERR_PTR(-EINVAL); |
938 | } | 938 | } |
939 | 939 | ||
940 | static void bio_copy_kern_endio(struct bio *bio, int err) | ||
941 | { | ||
942 | struct bio_vec *bvec; | ||
943 | const int read = bio_data_dir(bio) == READ; | ||
944 | char *p = bio->bi_private; | ||
945 | int i; | ||
946 | |||
947 | __bio_for_each_segment(bvec, bio, i, 0) { | ||
948 | char *addr = page_address(bvec->bv_page); | ||
949 | |||
950 | if (read && !err) | ||
951 | memcpy(p, addr, bvec->bv_len); | ||
952 | |||
953 | __free_page(bvec->bv_page); | ||
954 | p += bvec->bv_len; | ||
955 | } | ||
956 | |||
957 | bio_put(bio); | ||
958 | } | ||
959 | |||
960 | /** | ||
961 | * bio_copy_kern - copy kernel address into bio | ||
962 | * @q: the struct request_queue for the bio | ||
963 | * @data: pointer to buffer to copy | ||
964 | * @len: length in bytes | ||
965 | * @gfp_mask: allocation flags for bio and page allocation | ||
966 | * | ||
967 | * copy the kernel address into a bio suitable for io to a block | ||
968 | * device. Returns an error pointer in case of error. | ||
969 | */ | ||
970 | struct bio *bio_copy_kern(struct request_queue *q, void *data, unsigned int len, | ||
971 | gfp_t gfp_mask, int reading) | ||
972 | { | ||
973 | unsigned long kaddr = (unsigned long)data; | ||
974 | unsigned long end = (kaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
975 | unsigned long start = kaddr >> PAGE_SHIFT; | ||
976 | const int nr_pages = end - start; | ||
977 | struct bio *bio; | ||
978 | struct bio_vec *bvec; | ||
979 | int i, ret; | ||
980 | |||
981 | bio = bio_alloc(gfp_mask, nr_pages); | ||
982 | if (!bio) | ||
983 | return ERR_PTR(-ENOMEM); | ||
984 | |||
985 | while (len) { | ||
986 | struct page *page; | ||
987 | unsigned int bytes = PAGE_SIZE; | ||
988 | |||
989 | if (bytes > len) | ||
990 | bytes = len; | ||
991 | |||
992 | page = alloc_page(q->bounce_gfp | gfp_mask); | ||
993 | if (!page) { | ||
994 | ret = -ENOMEM; | ||
995 | goto cleanup; | ||
996 | } | ||
997 | |||
998 | if (bio_add_pc_page(q, bio, page, bytes, 0) < bytes) { | ||
999 | ret = -EINVAL; | ||
1000 | goto cleanup; | ||
1001 | } | ||
1002 | |||
1003 | len -= bytes; | ||
1004 | } | ||
1005 | |||
1006 | if (!reading) { | ||
1007 | void *p = data; | ||
1008 | |||
1009 | bio_for_each_segment(bvec, bio, i) { | ||
1010 | char *addr = page_address(bvec->bv_page); | ||
1011 | |||
1012 | memcpy(addr, p, bvec->bv_len); | ||
1013 | p += bvec->bv_len; | ||
1014 | } | ||
1015 | } | ||
1016 | |||
1017 | bio->bi_private = data; | ||
1018 | bio->bi_end_io = bio_copy_kern_endio; | ||
1019 | return bio; | ||
1020 | cleanup: | ||
1021 | bio_for_each_segment(bvec, bio, i) | ||
1022 | __free_page(bvec->bv_page); | ||
1023 | |||
1024 | bio_put(bio); | ||
1025 | |||
1026 | return ERR_PTR(ret); | ||
1027 | } | ||
1028 | |||
940 | /* | 1029 | /* |
941 | * bio_set_pages_dirty() and bio_check_pages_dirty() are support functions | 1030 | * bio_set_pages_dirty() and bio_check_pages_dirty() are support functions |
942 | * for performing direct-IO in BIOs. | 1031 | * for performing direct-IO in BIOs. |
@@ -1273,6 +1362,7 @@ EXPORT_SYMBOL(bio_get_nr_vecs); | |||
1273 | EXPORT_SYMBOL(bio_map_user); | 1362 | EXPORT_SYMBOL(bio_map_user); |
1274 | EXPORT_SYMBOL(bio_unmap_user); | 1363 | EXPORT_SYMBOL(bio_unmap_user); |
1275 | EXPORT_SYMBOL(bio_map_kern); | 1364 | EXPORT_SYMBOL(bio_map_kern); |
1365 | EXPORT_SYMBOL(bio_copy_kern); | ||
1276 | EXPORT_SYMBOL(bio_pair_release); | 1366 | EXPORT_SYMBOL(bio_pair_release); |
1277 | EXPORT_SYMBOL(bio_split); | 1367 | EXPORT_SYMBOL(bio_split); |
1278 | EXPORT_SYMBOL(bio_split_pool); | 1368 | EXPORT_SYMBOL(bio_split_pool); |