* [PATCH 2/3] block: simplify minsize alignment in bio_iov_iter_bounce_write
2026-07-14 13:11 bio bounce buffering fixes Christoph Hellwig
2026-07-14 13:11 ` [PATCH 1/3] block: handle huge zero folios in bio_free_folios Christoph Hellwig
@ 2026-07-14 13:11 ` Christoph Hellwig
2026-07-14 13:12 ` [PATCH 3/3] block,iov_iter: move bio_iov_iter_align_down into iov_iter_extract_bvecs Christoph Hellwig
2 siblings, 0 replies; 7+ messages in thread
From: Christoph Hellwig @ 2026-07-14 13:11 UTC (permalink / raw)
To: Jens Axboe
Cc: Al Viro, Andrew Morton, Keith Busch, Qu Wenruo, Neptune,
linux-block, linux-fsdevel
Don't allow partial copies to create unaligned length, which the file
systems can't handle, and remove thee need for the separate
bio_iov_iter_align_down() pass.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
block/bio.c | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/block/bio.c b/block/bio.c
index 23f739d5321d..7f707ecf328e 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1399,7 +1399,12 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter,
* bytes as @this_len is queued but only advanced
* less than that.
* Need to compensate that for the revert.
+ *
+ * Round down the amount copied to the minimum size /
+ * aligned required by the file system so that we don't
+ * create unaligned bios.
*/
+ copied = rounddown(copied, minsize);
iov_iter_revert(iter, bio->bi_iter.bi_size - this_len +
copied);
bio_free_folios(bio);
@@ -1410,7 +1415,7 @@ static int bio_iov_iter_bounce_write(struct bio *bio, struct iov_iter *iter,
if (!bio->bi_iter.bi_size)
return -ENOMEM;
- return bio_iov_iter_align_down(bio, iter, minsize - 1);
+ return 0;
}
static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter,
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 3/3] block,iov_iter: move bio_iov_iter_align_down into iov_iter_extract_bvecs
2026-07-14 13:11 bio bounce buffering fixes Christoph Hellwig
2026-07-14 13:11 ` [PATCH 1/3] block: handle huge zero folios in bio_free_folios Christoph Hellwig
2026-07-14 13:11 ` [PATCH 2/3] block: simplify minsize alignment in bio_iov_iter_bounce_write Christoph Hellwig
@ 2026-07-14 13:12 ` Christoph Hellwig
2026-07-14 14:26 ` Keith Busch
2 siblings, 1 reply; 7+ messages in thread
From: Christoph Hellwig @ 2026-07-14 13:12 UTC (permalink / raw)
To: Jens Axboe
Cc: Al Viro, Andrew Morton, Keith Busch, Qu Wenruo, Neptune,
linux-block, linux-fsdevel
Add a len_align_mask argument to iov_iter_extract_bvecs() and align the
extracted length in this core routine. This ensures that the returned
bvec array is directly usable by the caller, and thus fixes two bugs in
bio_iov_iter_bounce_read() where the bounce bvec size is not updated to
the adjusted value, and the bounce folio is not freed when we can't form
a valid bio at all.
Fixes: e7b8b3c5b2a6 ("block: align down bounces bios")
Reported-by: Neptune <z1281552865@gmail.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
block/bio.c | 44 +++++---------------------------------------
include/linux/uio.h | 1 +
lib/iov_iter.c | 37 +++++++++++++++++++++++++++++++++++--
3 files changed, 41 insertions(+), 41 deletions(-)
diff --git a/block/bio.c b/block/bio.c
index 7f707ecf328e..659678cd0843 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1185,41 +1185,6 @@ void bio_iov_bvec_set(struct bio *bio, const struct iov_iter *iter)
bio_set_flag(bio, BIO_CLONED);
}
-/*
- * Aligns the bio size to the len_align_mask, releasing excessive bio vecs that
- * __bio_iov_iter_get_pages may have inserted, and reverts the trimmed length
- * for the next iteration.
- */
-static int bio_iov_iter_align_down(struct bio *bio, struct iov_iter *iter,
- unsigned len_align_mask)
-{
- size_t nbytes = bio->bi_iter.bi_size & len_align_mask;
-
- if (!nbytes)
- return 0;
-
- iov_iter_revert(iter, nbytes);
- bio->bi_iter.bi_size -= nbytes;
- do {
- struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1];
-
- if (nbytes < bv->bv_len) {
- bv->bv_len -= nbytes;
- break;
- }
-
- if (bio_flagged(bio, BIO_PAGE_PINNED))
- unpin_user_page(bv->bv_page);
-
- bio->bi_vcnt--;
- nbytes -= bv->bv_len;
- } while (nbytes);
-
- if (!bio->bi_vcnt)
- return -EFAULT;
- return 0;
-}
-
#ifdef CONFIG_DEBUG_KERNEL
static inline bool bio_iov_bvec_aligned(const struct bio *bio,
unsigned mem_align_mask)
@@ -1305,7 +1270,7 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec,
BIO_MAX_SIZE - bio->bi_iter.bi_size,
&bio->bi_vcnt, bio->bi_max_vecs,
- mem_align_mask, flags);
+ mem_align_mask, len_align_mask, flags);
if (ret <= 0) {
/*
* A misaligned vector fails the whole I/O. Release any
@@ -1326,7 +1291,7 @@ int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter,
if (is_pci_p2pdma_page(bio->bi_io_vec->bv_page))
bio->bi_opf |= REQ_NOMERGE;
- return bio_iov_iter_align_down(bio, iter, len_align_mask);
+ return 0;
}
static struct folio *folio_alloc_greedy(gfp_t gfp, size_t *size,
@@ -1432,7 +1397,8 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter,
ssize_t ret;
ret = iov_iter_extract_bvecs(iter, bio->bi_io_vec + 1, len,
- &bio->bi_vcnt, bio->bi_max_vecs - 1, 0, 0);
+ &bio->bi_vcnt, bio->bi_max_vecs - 1, 0,
+ minsize - 1, 0);
if (ret <= 0) {
if (!bio->bi_vcnt) {
folio_put(folio);
@@ -1453,7 +1419,7 @@ static int bio_iov_iter_bounce_read(struct bio *bio, struct iov_iter *iter,
bvec_set_folio(&bio->bi_io_vec[0], folio, bio->bi_iter.bi_size, 0);
if (iov_iter_extract_will_pin(iter))
bio_set_flag(bio, BIO_PAGE_PINNED);
- return bio_iov_iter_align_down(bio, iter, minsize - 1);
+ return 0;
}
/**
diff --git a/include/linux/uio.h b/include/linux/uio.h
index fe2e985d74d2..fb1a161fa765 100644
--- a/include/linux/uio.h
+++ b/include/linux/uio.h
@@ -399,6 +399,7 @@ ssize_t iov_iter_extract_pages(struct iov_iter *i, struct page ***pages,
ssize_t iov_iter_extract_bvecs(struct iov_iter *iter, struct bio_vec *bv,
size_t max_size, unsigned short *nr_vecs,
unsigned short max_vecs, unsigned mem_align_mask,
+ unsigned len_align_mask,
iov_iter_extraction_t extraction_flags);
/**
diff --git a/lib/iov_iter.c b/lib/iov_iter.c
index 34a52e9ba9e1..bb6084672971 100644
--- a/lib/iov_iter.c
+++ b/lib/iov_iter.c
@@ -1897,6 +1897,36 @@ static unsigned int get_contig_folio_len(struct page **pages,
#define PAGE_PTRS_PER_BVEC (sizeof(struct bio_vec) / sizeof(struct page *))
+static ssize_t bvec_iter_align_down(struct iov_iter *iter, struct bio_vec *bvec,
+ size_t size, unsigned short *nr_vecs, unsigned len_align_mask)
+{
+ size_t nbytes = size & len_align_mask;
+
+ if (!nbytes)
+ return size;
+
+ iov_iter_revert(iter, nbytes);
+ size -= nbytes;
+ do {
+ struct bio_vec *bv = &bvec[*nr_vecs - 1];
+
+ if (nbytes < bv->bv_len) {
+ bv->bv_len -= nbytes;
+ break;
+ }
+
+ if (iov_iter_extract_will_pin(iter))
+ unpin_user_page(bv->bv_page);
+
+ (*nr_vecs)--;
+ nbytes -= bv->bv_len;
+ } while (nbytes);
+
+ if (*nr_vecs == 0)
+ return -EFAULT;
+ return size;
+}
+
/**
* iov_iter_extract_bvecs - Extract bvecs from an iterator
* @iter: the iterator to extract from
@@ -1906,6 +1936,7 @@ static unsigned int get_contig_folio_len(struct page **pages,
* @max_vecs: maximum vectors in @bv, including those filled before calling
* @mem_align_mask: reject with -EINVAL if the source address or
* length is not aligned to this mask
+ * @len_align_mask: the mask to align the total size to, 0 for any length
* @extraction_flags: flags to qualify request
*
* Like iov_iter_extract_pages(), but returns physically contiguous ranges
@@ -1918,7 +1949,7 @@ static unsigned int get_contig_folio_len(struct page **pages,
ssize_t iov_iter_extract_bvecs(struct iov_iter *iter, struct bio_vec *bv,
size_t max_size, unsigned short *nr_vecs,
unsigned short max_vecs, unsigned mem_align_mask,
- iov_iter_extraction_t extraction_flags)
+ unsigned len_align_mask, iov_iter_extraction_t extraction_flags)
{
unsigned long start = (unsigned long)iter_iov_addr(iter);
unsigned short entries_left = max_vecs - *nr_vecs;
@@ -1960,11 +1991,13 @@ ssize_t iov_iter_extract_bvecs(struct iov_iter *iter, struct bio_vec *bv,
offset = 0;
}
+ size -= left;
iov_iter_revert(iter, left);
if (iov_iter_extract_will_pin(iter)) {
while (i < nr_pages)
unpin_user_page(pages[i++]);
}
- return size - left;
+
+ return bvec_iter_align_down(iter, bv, size, nr_vecs, len_align_mask);
}
EXPORT_SYMBOL_GPL(iov_iter_extract_bvecs);
--
2.53.0
^ permalink raw reply related [flat|nested] 7+ messages in thread