From 96cf043ad1da57d3b72566f39b1c20094752db61 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 21 Mar 2025 14:13:57 +0100 Subject: [PATCH] block: add a bio_add_vmalloc helper 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 --- block/bio.c | 21 +++++++++++++++++++++ include/linux/bio.h | 17 +++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/block/bio.c b/block/bio.c index 3761600f3e04..3f7946f0d410 100644 --- a/block/bio.c +++ b/block/bio.c @@ -1058,6 +1058,27 @@ 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. + */ +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; + 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 0678b67162ee..8eeec0a4ca6d 100644 --- a/include/linux/bio.h +++ b/include/linux/bio.h @@ -434,6 +434,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 bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter); void bio_iov_bvec_set(struct bio *bio, const struct iov_iter *iter); void __bio_release_pages(struct bio *bio, bool mark_dirty); -- 2.50.1