[PATCH 2/2] block: allow different-pgmap pages as separate bvecs in bio_add_page

Naman Jain posted 2 patches 14 hours ago
[PATCH 2/2] block: allow different-pgmap pages as separate bvecs in bio_add_page
Posted by Naman Jain 14 hours ago
bio_add_page() and bio_integrity_add_page() reject pages from a
different dev_pagemap entirely, returning 0 even when the page could
be added as a new bvec entry. The pgmap check was intended only to
prevent merging into the same bvec segment, not to block the page
from being added at all.

This causes callers to unnecessarily start a new bio when a buffer
spans pages from two different pgmaps, even though the bio has room
for another bvec.

Fix both functions by moving the zone_device_pages_have_same_pgmap()
check into the merge conditional. Pages from different pgmaps now
skip the merge attempt and fall through to be added as new separate
bvec entries.

This is safe because biovec_phys_mergeable() now also checks for
pgmap mismatches, preventing the downstream merge, DMA mapping, and
request coalescing paths from combining segments across pgmaps.

Fixes: 49580e690755 ("block: add check when merging zone device pages")
Cc: stable@vger.kernel.org
Signed-off-by: Naman Jain <namjain@linux.microsoft.com>
---
 block/bio-integrity.c | 6 ++----
 block/bio.c           | 6 ++----
 2 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/block/bio-integrity.c b/block/bio-integrity.c
index e79eaf0477943..3462697331890 100644
--- a/block/bio-integrity.c
+++ b/block/bio-integrity.c
@@ -231,10 +231,8 @@ int bio_integrity_add_page(struct bio *bio, struct page *page,
 	if (bip->bip_vcnt > 0) {
 		struct bio_vec *bv = &bip->bip_vec[bip->bip_vcnt - 1];
 
-		if (!zone_device_pages_have_same_pgmap(bv->bv_page, page))
-			return 0;
-
-		if (bvec_try_merge_hw_page(q, bv, page, len, offset)) {
+		if (zone_device_pages_have_same_pgmap(bv->bv_page, page) &&
+		    bvec_try_merge_hw_page(q, bv, page, len, offset)) {
 			bip->bip_iter.bi_size += len;
 			return len;
 		}
diff --git a/block/bio.c b/block/bio.c
index 77067fa346d35..7715e59e68613 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1034,10 +1034,8 @@ int bio_add_page(struct bio *bio, struct page *page,
 	if (bio->bi_vcnt > 0) {
 		struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1];
 
-		if (!zone_device_pages_have_same_pgmap(bv->bv_page, page))
-			return 0;
-
-		if (bvec_try_merge_page(bv, page, len, offset)) {
+		if (zone_device_pages_have_same_pgmap(bv->bv_page, page) &&
+		    bvec_try_merge_page(bv, page, len, offset)) {
 			bio->bi_iter.bi_size += len;
 			return len;
 		}
-- 
2.43.0
Re: [PATCH 2/2] block: allow different-pgmap pages as separate bvecs in bio_add_page
Posted by Christoph Hellwig 8 hours ago
On Wed, Apr 01, 2026 at 08:23:29AM +0000, Naman Jain wrote:
> bio_add_page() and bio_integrity_add_page() reject pages from a
> different dev_pagemap entirely, returning 0 even when the page could
> be added as a new bvec entry. The pgmap check was intended only to
> prevent merging into the same bvec segment, not to block the page
> from being added at all.
> 
> This causes callers to unnecessarily start a new bio when a buffer
> spans pages from two different pgmaps, even though the bio has room
> for another bvec.

This is not unnecessary.  A single dma mapping operation can only
map a single target pgmap.  The old SG API works around this by
doing multiple mapping operation underneath, but compared to that
just having multiple bios is much easier and more efficient.

What is your use case here?