Skip to content

Commit

Permalink
block: Add bio_copy_data()
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Kent Overstreet committed Mar 23, 2013
1 parent b783863 commit 16ac3d6
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 0 deletions.
70 changes: 70 additions & 0 deletions fs/bio.c
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,76 @@ void bio_advance(struct bio *bio, unsigned bytes)
}
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_vec *iovecs;
struct sg_iovec *sgvecs;
Expand Down
2 changes: 2 additions & 0 deletions include/linux/bio.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ static inline void bio_flush_dcache_pages(struct bio *bi)
}
#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 *,
unsigned long, unsigned int, int, gfp_t);
extern struct bio *bio_copy_user_iov(struct request_queue *,
Expand Down

0 comments on commit 16ac3d6

Please sign in to comment.