Skip to content

Commit 7be4788

Browse files
Naman Jainkawasaki
authored andcommitted
block: relax pgmap check in bio_add_page for compatible zone device pages
bio_add_page() and bio_integrity_add_page() reject pages from different dev_pagemaps entirely, returning 0 even when those pages have compatible DMA mapping requirements. This forces callers to start a new bio when buffers span pgmap boundaries, even though the pages could safely coexist as separate bvec entries. This matters for guests where memory is registered through devm_memremap_pages() with MEMORY_DEVICE_GENERIC in multiple calls, creating separate dev_pagemaps for each chunk. When a direct I/O buffer spans two such chunks, bio_add_page() rejects the second page, forcing an unnecessary bio split or I/O failure. Introduce zone_device_pages_compatible() in blk.h to check whether two pages can coexist in the same bio as separate bvec entries. The block DMA iterator (blk_dma_map_iter_start) caches the P2PDMA mapping state from the first segment and applies it to all others, so P2PDMA pages from different pgmaps must not be mixed, and neither must P2PDMA and non-P2PDMA pages. All other combinations (MEMORY_DEVICE_GENERIC pages from different pgmaps, or MEMORY_DEVICE_GENERIC with normal RAM) use the same dma_map_phys path and are safe. Replace the blanket zone_device_pages_have_same_pgmap() rejection with zone_device_pages_compatible(), while keeping zone_device_pages_have_same_pgmap() as a merge guard. Pages from different pgmaps can be added as separate bvec entries but must not be coalesced into the same segment, as that would make it impossible to recover the correct pgmap via page_pgmap(). Fixes: 49580e6 ("block: add check when merging zone device pages") Cc: [email protected] Signed-off-by: Naman Jain <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]>
1 parent 2a1dab7 commit 7be4788

3 files changed

Lines changed: 25 additions & 6 deletions

File tree

block/bio-integrity.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,10 +231,10 @@ int bio_integrity_add_page(struct bio *bio, struct page *page,
231231
if (bip->bip_vcnt > 0) {
232232
struct bio_vec *bv = &bip->bip_vec[bip->bip_vcnt - 1];
233233

234-
if (!zone_device_pages_have_same_pgmap(bv->bv_page, page))
234+
if (!zone_device_pages_compatible(bv->bv_page, page))
235235
return 0;
236-
237-
if (bvec_try_merge_hw_page(q, bv, page, len, offset)) {
236+
if (zone_device_pages_have_same_pgmap(bv->bv_page, page) &&
237+
bvec_try_merge_hw_page(q, bv, page, len, offset)) {
238238
bip->bip_iter.bi_size += len;
239239
return len;
240240
}

block/bio.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,10 +1048,10 @@ int bio_add_page(struct bio *bio, struct page *page,
10481048
if (bio->bi_vcnt > 0) {
10491049
struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1];
10501050

1051-
if (!zone_device_pages_have_same_pgmap(bv->bv_page, page))
1051+
if (!zone_device_pages_compatible(bv->bv_page, page))
10521052
return 0;
1053-
1054-
if (bvec_try_merge_page(bv, page, len, offset)) {
1053+
if (zone_device_pages_have_same_pgmap(bv->bv_page, page) &&
1054+
bvec_try_merge_page(bv, page, len, offset)) {
10551055
bio->bi_iter.bi_size += len;
10561056
return len;
10571057
}

block/blk.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,25 @@ static inline bool biovec_phys_mergeable(struct request_queue *q,
136136
return true;
137137
}
138138

139+
/*
140+
* Check if two pages from potentially different zone device pgmaps can
141+
* coexist as separate bvec entries in the same bio.
142+
*
143+
* The block DMA iterator (blk_dma_map_iter_start) caches the P2PDMA mapping
144+
* state from the first segment and applies it to all subsequent segments, so
145+
* P2PDMA pages from different pgmaps must not be mixed in the same bio.
146+
*
147+
* Other zone device types (FS_DAX, GENERIC) use the same dma_map_phys() path
148+
* as normal RAM. PRIVATE and COHERENT pages never appear in bios.
149+
*/
150+
static inline bool zone_device_pages_compatible(const struct page *a,
151+
const struct page *b)
152+
{
153+
if (is_pci_p2pdma_page(a) || is_pci_p2pdma_page(b))
154+
return zone_device_pages_have_same_pgmap(a, b);
155+
return true;
156+
}
157+
139158
static inline bool __bvec_gap_to_prev(const struct queue_limits *lim,
140159
struct bio_vec *bprv, unsigned int offset)
141160
{

0 commit comments

Comments
 (0)