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