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
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?
© 2016 - 2026 Red Hat, Inc.