diff options
author | Kent Overstreet <koverstreet@google.com> | 2012-09-10 16:57:51 -0400 |
---|---|---|
committer | Kent Overstreet <koverstreet@google.com> | 2013-03-23 17:15:37 -0400 |
commit | 16ac3d63e74f3d6e34e42d6e523b6a61de0020f0 (patch) | |
tree | 08bfffad2ccf0b67d704588ff14f9e5227ed0e7d /fs/bio.c | |
parent | b783863f68c26c5411c50002f98a047a40b94e8e (diff) |
block: Add bio_copy_data()
This gets open coded quite a bit and it's tricky to get right, so make a
generic version and convert some existing users over to it instead.
Signed-off-by: Kent Overstreet <koverstreet@google.com>
CC: Jens Axboe <axboe@kernel.dk>
Diffstat (limited to 'fs/bio.c')
-rw-r--r-- | fs/bio.c | 70 |
1 files changed, 70 insertions, 0 deletions
@@ -829,6 +829,76 @@ void bio_advance(struct bio *bio, unsigned bytes) | |||
829 | } | 829 | } |
830 | EXPORT_SYMBOL(bio_advance); | 830 | EXPORT_SYMBOL(bio_advance); |
831 | 831 | ||
832 | /** | ||
833 | * bio_copy_data - copy contents of data buffers from one chain of bios to | ||
834 | * another | ||
835 | * @src: source bio list | ||
836 | * @dst: destination bio list | ||
837 | * | ||
838 | * If @src and @dst are single bios, bi_next must be NULL - otherwise, treats | ||
839 | * @src and @dst as linked lists of bios. | ||
840 | * | ||
841 | * Stops when it reaches the end of either @src or @dst - that is, copies | ||
842 | * min(src->bi_size, dst->bi_size) bytes (or the equivalent for lists of bios). | ||
843 | */ | ||
844 | void bio_copy_data(struct bio *dst, struct bio *src) | ||
845 | { | ||
846 | struct bio_vec *src_bv, *dst_bv; | ||
847 | unsigned src_offset, dst_offset, bytes; | ||
848 | void *src_p, *dst_p; | ||
849 | |||
850 | src_bv = bio_iovec(src); | ||
851 | dst_bv = bio_iovec(dst); | ||
852 | |||
853 | src_offset = src_bv->bv_offset; | ||
854 | dst_offset = dst_bv->bv_offset; | ||
855 | |||
856 | while (1) { | ||
857 | if (src_offset == src_bv->bv_offset + src_bv->bv_len) { | ||
858 | src_bv++; | ||
859 | if (src_bv == bio_iovec_idx(src, src->bi_vcnt)) { | ||
860 | src = src->bi_next; | ||
861 | if (!src) | ||
862 | break; | ||
863 | |||
864 | src_bv = bio_iovec(src); | ||
865 | } | ||
866 | |||
867 | src_offset = src_bv->bv_offset; | ||
868 | } | ||
869 | |||
870 | if (dst_offset == dst_bv->bv_offset + dst_bv->bv_len) { | ||
871 | dst_bv++; | ||
872 | if (dst_bv == bio_iovec_idx(dst, dst->bi_vcnt)) { | ||
873 | dst = dst->bi_next; | ||
874 | if (!dst) | ||
875 | break; | ||
876 | |||
877 | dst_bv = bio_iovec(dst); | ||
878 | } | ||
879 | |||
880 | dst_offset = dst_bv->bv_offset; | ||
881 | } | ||
882 | |||
883 | bytes = min(dst_bv->bv_offset + dst_bv->bv_len - dst_offset, | ||
884 | src_bv->bv_offset + src_bv->bv_len - src_offset); | ||
885 | |||
886 | src_p = kmap_atomic(src_bv->bv_page); | ||
887 | dst_p = kmap_atomic(dst_bv->bv_page); | ||
888 | |||
889 | memcpy(dst_p + dst_bv->bv_offset, | ||
890 | src_p + src_bv->bv_offset, | ||
891 | bytes); | ||
892 | |||
893 | kunmap_atomic(dst_p); | ||
894 | kunmap_atomic(src_p); | ||
895 | |||
896 | src_offset += bytes; | ||
897 | dst_offset += bytes; | ||
898 | } | ||
899 | } | ||
900 | EXPORT_SYMBOL(bio_copy_data); | ||
901 | |||
832 | struct bio_map_data { | 902 | struct bio_map_data { |
833 | struct bio_vec *iovecs; | 903 | struct bio_vec *iovecs; |
834 | struct sg_iovec *sgvecs; | 904 | struct sg_iovec *sgvecs; |