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>
This commit is contained in:
Kent Overstreet 2012-09-10 13:57:51 -07:00
parent b783863f68
commit 16ac3d63e7
2 changed files with 72 additions and 0 deletions

View File

@ -829,6 +829,76 @@ void bio_advance(struct bio *bio, unsigned bytes)
} }
EXPORT_SYMBOL(bio_advance); EXPORT_SYMBOL(bio_advance);
/**
* bio_copy_data - copy contents of data buffers from one chain of bios to
* another
* @src: source bio list
* @dst: destination bio list
*
* If @src and @dst are single bios, bi_next must be NULL - otherwise, treats
* @src and @dst as linked lists of bios.
*
* Stops when it reaches the end of either @src or @dst - that is, copies
* min(src->bi_size, dst->bi_size) bytes (or the equivalent for lists of bios).
*/
void bio_copy_data(struct bio *dst, struct bio *src)
{
struct bio_vec *src_bv, *dst_bv;
unsigned src_offset, dst_offset, bytes;
void *src_p, *dst_p;
src_bv = bio_iovec(src);
dst_bv = bio_iovec(dst);
src_offset = src_bv->bv_offset;
dst_offset = dst_bv->bv_offset;
while (1) {
if (src_offset == src_bv->bv_offset + src_bv->bv_len) {
src_bv++;
if (src_bv == bio_iovec_idx(src, src->bi_vcnt)) {
src = src->bi_next;
if (!src)
break;
src_bv = bio_iovec(src);
}
src_offset = src_bv->bv_offset;
}
if (dst_offset == dst_bv->bv_offset + dst_bv->bv_len) {
dst_bv++;
if (dst_bv == bio_iovec_idx(dst, dst->bi_vcnt)) {
dst = dst->bi_next;
if (!dst)
break;
dst_bv = bio_iovec(dst);
}
dst_offset = dst_bv->bv_offset;
}
bytes = min(dst_bv->bv_offset + dst_bv->bv_len - dst_offset,
src_bv->bv_offset + src_bv->bv_len - src_offset);
src_p = kmap_atomic(src_bv->bv_page);
dst_p = kmap_atomic(dst_bv->bv_page);
memcpy(dst_p + dst_bv->bv_offset,
src_p + src_bv->bv_offset,
bytes);
kunmap_atomic(dst_p);
kunmap_atomic(src_p);
src_offset += bytes;
dst_offset += bytes;
}
}
EXPORT_SYMBOL(bio_copy_data);
struct bio_map_data { struct bio_map_data {
struct bio_vec *iovecs; struct bio_vec *iovecs;
struct sg_iovec *sgvecs; struct sg_iovec *sgvecs;

View File

@ -286,6 +286,8 @@ static inline void bio_flush_dcache_pages(struct bio *bi)
} }
#endif #endif
extern void bio_copy_data(struct bio *dst, struct bio *src);
extern struct bio *bio_copy_user(struct request_queue *, struct rq_map_data *, extern struct bio *bio_copy_user(struct request_queue *, struct rq_map_data *,
unsigned long, unsigned int, int, gfp_t); unsigned long, unsigned int, int, gfp_t);
extern struct bio *bio_copy_user_iov(struct request_queue *, extern struct bio *bio_copy_user_iov(struct request_queue *,