Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

block: open code bio_add_page and fix handling of mismatching P2P ranges

bio_add_page fails to add data to the bio when mixing P2P with non-P2P
ranges, or ranges that map to different P2P providers. In that case
it will trigger that WARN_ON and return an error up the chain instead of
simply starting a new bio as intended. Fix this by open coding
bio_add_page and handling this case explicitly. While doing so, stop
merging physical contiguous data that belongs to multiple folios. While
this merge could lead to more efficient bio packing in some case,
dropping will allow to remove handling of this corner case in other
places and make the code more robust.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Anuj Gupta <anuj20.g@samsung.com>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Tested-by: Anuj Gupta <anuj20.g@samsung.com>
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Christoph Hellwig and committed by
Jens Axboe
12da89e8 4d77007d

+13 -24
+13 -24
block/bio.c
··· 1216 1216 * For a multi-segment *iter, this function only adds pages from the next 1217 1217 * non-empty segment of the iov iterator. 1218 1218 */ 1219 - static int __bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter) 1219 + static ssize_t __bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter) 1220 1220 { 1221 1221 iov_iter_extraction_t extraction_flags = 0; 1222 1222 unsigned short nr_pages = bio->bi_max_vecs - bio->bi_vcnt; ··· 1226 1226 ssize_t size; 1227 1227 unsigned int i = 0; 1228 1228 size_t offset, left, len; 1229 - int ret = 0; 1230 1229 1231 1230 /* 1232 1231 * Move page array up in the allocated memory for the bio vecs as far as ··· 1246 1247 1247 1248 nr_pages = DIV_ROUND_UP(offset + size, PAGE_SIZE); 1248 1249 for (left = size; left > 0; left -= len) { 1249 - unsigned int old_vcnt = bio->bi_vcnt; 1250 1250 unsigned int nr_to_add; 1251 1251 1252 - len = get_contig_folio_len(&pages[i], &nr_to_add, left, offset); 1253 - if (!bio_add_page(bio, pages[i], len, offset)) { 1254 - WARN_ON_ONCE(1); 1255 - ret = -EINVAL; 1256 - goto out; 1252 + if (bio->bi_vcnt > 0) { 1253 + struct bio_vec *prev = &bio->bi_io_vec[bio->bi_vcnt - 1]; 1254 + 1255 + if (!zone_device_pages_have_same_pgmap(prev->bv_page, 1256 + pages[i])) 1257 + break; 1257 1258 } 1258 1259 1259 - if (bio_flagged(bio, BIO_PAGE_PINNED)) { 1260 - /* 1261 - * We're adding another fragment of a page that already 1262 - * was part of the last segment. Undo our pin as the 1263 - * page was pinned when an earlier fragment of it was 1264 - * added to the bio and __bio_release_pages expects a 1265 - * single pin per page. 1266 - */ 1267 - if (offset && bio->bi_vcnt == old_vcnt) 1268 - unpin_user_folio(page_folio(pages[i]), 1); 1269 - } 1260 + len = get_contig_folio_len(&pages[i], &nr_to_add, left, offset); 1261 + __bio_add_page(bio, pages[i], len, offset); 1270 1262 i += nr_to_add; 1271 1263 offset = 0; 1272 1264 } 1273 1265 1274 1266 iov_iter_revert(iter, left); 1275 - out: 1276 1267 while (i < nr_pages) 1277 1268 bio_release_page(bio, pages[i++]); 1278 - 1279 - return ret; 1269 + return size - left; 1280 1270 } 1281 1271 1282 1272 /* ··· 1325 1337 int bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter, 1326 1338 unsigned len_align_mask) 1327 1339 { 1328 - int ret = 0; 1340 + ssize_t ret; 1329 1341 1330 1342 if (WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED))) 1331 1343 return -EIO; ··· 1338 1350 1339 1351 if (iov_iter_extract_will_pin(iter)) 1340 1352 bio_set_flag(bio, BIO_PAGE_PINNED); 1353 + 1341 1354 do { 1342 1355 ret = __bio_iov_iter_get_pages(bio, iter); 1343 - } while (!ret && iov_iter_count(iter) && !bio_full(bio, 0)); 1356 + } while (ret > 0 && iov_iter_count(iter) && !bio_full(bio, 0)); 1344 1357 1345 1358 if (bio->bi_vcnt) 1346 1359 return bio_iov_iter_align_down(bio, iter, len_align_mask);