Add a helper to add a vmalloc region to a bio, abstracting away the
vmalloc addresses from the underlying pages.  Also add a helper to
calculate how many segments need to be allocated for a vmalloc region.

Signed-off-by: Christoph Hellwig <h...@lst.de>
---
 block/bio.c         | 27 +++++++++++++++++++++++++++
 include/linux/bio.h | 17 +++++++++++++++++
 2 files changed, 44 insertions(+)

diff --git a/block/bio.c b/block/bio.c
index a6a867a432cf..3cc93bbdeeb9 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1058,6 +1058,33 @@ bool bio_add_folio(struct bio *bio, struct folio *folio, 
size_t len,
 }
 EXPORT_SYMBOL(bio_add_folio);
 
+/**
+ * bio_add_vmalloc - add a vmalloc region to a bio
+ * @bio: destination bio
+ * @vaddr: virtual address to add
+ * @len: total length of the data to add
+ *
+ * Add the data at @vaddr to @bio and return how much was added.  This can an
+ * usually is less than the amount originally asked.  Returns 0 if no data 
could
+ * be added to the bio.
+ *
+ * This helper calls flush_kernel_vmap_range() for the range added.  For reads,
+ * the caller still needs to manually call invalidate_kernel_vmap_range() in
+ * the completion handler.
+ */
+unsigned int bio_add_vmalloc(struct bio *bio, void *vaddr, unsigned len)
+{
+       unsigned int offset = offset_in_page(vaddr);
+
+       len = min(len, PAGE_SIZE - offset);
+       if (bio_add_page(bio, vmalloc_to_page(vaddr), len, offset) < len)
+               return 0;
+       if (op_is_write(bio_op(bio)))
+               flush_kernel_vmap_range(vaddr, len);
+       return len;
+}
+EXPORT_SYMBOL_GPL(bio_add_vmalloc);
+
 void __bio_release_pages(struct bio *bio, bool mark_dirty)
 {
        struct folio_iter fi;
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 17a10220c57d..c4069422fd0a 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -433,6 +433,23 @@ static inline void bio_add_virt_nofail(struct bio *bio, 
void *vaddr,
        __bio_add_page(bio, virt_to_page(vaddr), len, offset_in_page(vaddr));
 }
 
+/**
+ * bio_vmalloc_max_vecs - number of segments needed to map vmalloc data
+ * @vaddr: address to map
+ * @len: length to map
+ *
+ * Calculate how many bio segments need to be allocated to map the vmalloc/vmap
+ * range in [@addr:@len].  This could be an overestimation if the vmalloc area
+ * is backed by large folios.
+ */
+static inline unsigned int bio_vmalloc_max_vecs(void *vaddr, unsigned int len)
+{
+       return DIV_ROUND_UP(offset_in_page(vaddr) + len, PAGE_SIZE);
+}
+
+unsigned int __must_check bio_add_vmalloc(struct bio *bio, void *vaddr,
+               unsigned len);
+
 int submit_bio_wait(struct bio *bio);
 int bdev_rw_virt(struct block_device *bdev, sector_t sector, void *data,
                size_t len, enum req_op op);
-- 
2.47.2


Reply via email to