diff mbox series

[06/17] block: simplify bio_map_kern

Message ID 20250422142628.1553523-7-hch@lst.de
State New
Headers show
Series None | expand

Commit Message

Christoph Hellwig April 22, 2025, 2:26 p.m. UTC
Split bio_map_kern into a simple version that can use bio_add_virt_nofail
for kernel direct mapping addresses and a more complex bio_map_vmalloc
with the logic to chunk up and map vmalloc ranges using the
bio_add_vmalloc helper.

Signed-off-by: Christoph Hellwig <hch@lst.de>
---
 block/blk-map.c | 74 +++++++++++++++++++------------------------------
 1 file changed, 29 insertions(+), 45 deletions(-)
diff mbox series

Patch

diff --git a/block/blk-map.c b/block/blk-map.c
index ca6b55ac0da1..7742d3cb0499 100644
--- a/block/blk-map.c
+++ b/block/blk-map.c
@@ -317,65 +317,47 @@  static void bio_map_kern_endio(struct bio *bio)
 	kfree(bio);
 }
 
-/**
- *	bio_map_kern	-	map kernel address into bio
- *	@data: pointer to buffer to map
- *	@len: length in bytes
- *	@op: bio/request operation
- *	@gfp_mask: allocation flags for bio allocation
- *
- *	Map the kernel address into a bio suitable for io to a block
- *	device. Returns an error pointer in case of error.
- */
-static struct bio *bio_map_kern(void *data, unsigned int len,
-		enum req_op op, gfp_t gfp_mask)
+static struct bio *bio_map_virt(void *data, unsigned int len, enum req_op op,
+		gfp_t gfp_mask)
 {
-	unsigned long kaddr = (unsigned long)data;
-	unsigned long end = (kaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
-	unsigned long start = kaddr >> PAGE_SHIFT;
-	const int nr_pages = end - start;
-	bool is_vmalloc = is_vmalloc_addr(data);
-	struct page *page;
-	int offset, i;
 	struct bio *bio;
 
-	bio = bio_kmalloc(nr_pages, gfp_mask);
+	bio = bio_kmalloc(1, gfp_mask);
 	if (!bio)
 		return ERR_PTR(-ENOMEM);
-	bio_init(bio, NULL, bio->bi_inline_vecs, nr_pages, op);
-
-	if (is_vmalloc) {
-		flush_kernel_vmap_range(data, len);
-		bio->bi_private = data;
-	}
-
-	offset = offset_in_page(kaddr);
-	for (i = 0; i < nr_pages; i++) {
-		unsigned int bytes = PAGE_SIZE - offset;
+	bio_init(bio, NULL, bio->bi_inline_vecs, 1, op);
+	bio_add_virt_nofail(bio, data, len);
+	bio->bi_end_io = bio_map_kern_endio;
+	return bio;
+}
 
-		if (len <= 0)
-			break;
+static struct bio *bio_map_vmalloc(void *data, unsigned int len, enum req_op op,
+		gfp_t gfp_mask)
+{
+	unsigned int nr_vecs = bio_vmalloc_max_vecs(data, len);
+	unsigned int added;
+	struct bio *bio;
 
-		if (bytes > len)
-			bytes = len;
+	bio = bio_kmalloc(nr_vecs, gfp_mask);
+	if (!bio)
+		return ERR_PTR(-ENOMEM);
+	bio_init(bio, NULL, bio->bi_inline_vecs, nr_vecs, op);
+	bio->bi_private = data;
+	bio->bi_end_io = bio_map_kern_endio;
 
-		if (!is_vmalloc)
-			page = virt_to_page(data);
-		else
-			page = vmalloc_to_page(data);
-		if (bio_add_page(bio, page, bytes, offset) < bytes) {
+	do {
+		added = bio_add_vmalloc(bio, data, len);
+		if (!added) {
 			/* we don't support partial mappings */
 			bio_uninit(bio);
 			kfree(bio);
 			return ERR_PTR(-EINVAL);
 		}
 
-		data += bytes;
-		len -= bytes;
-		offset = 0;
-	}
+		data += added;
+		len -= added;
+	} while (len);
 
-	bio->bi_end_io = bio_map_kern_endio;
 	return bio;
 }
 
@@ -713,8 +695,10 @@  int blk_rq_map_kern(struct request *rq, void *kbuf, unsigned int len,
 	if (!blk_rq_aligned(rq->q, addr, len) || object_is_on_stack(kbuf) ||
 	    blk_queue_may_bounce(rq->q))
 		bio = bio_copy_kern(kbuf, len, req_op(rq), gfp_mask);
+	else if (is_vmalloc_addr(kbuf))
+		bio = bio_map_vmalloc(kbuf, len, req_op(rq), gfp_mask);
 	else
-		bio = bio_map_kern(kbuf, len, req_op(rq), gfp_mask);
+		bio = bio_map_virt(kbuf, len, req_op(rq), gfp_mask);
 
 	if (IS_ERR(bio))
 		return PTR_ERR(bio);