Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 16ac3d63 authored by Kent Overstreet's avatar Kent Overstreet
Browse files

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: default avatarKent Overstreet <koverstreet@google.com>
CC: Jens Axboe <axboe@kernel.dk>
parent b783863f
Loading
Loading
Loading
Loading
+70 −0
Original line number Diff line number Diff line
@@ -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;
+2 −0
Original line number Diff line number Diff line
@@ -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 *,