diff --git a/module/os/linux/zfs/abd_os.c b/module/os/linux/zfs/abd_os.c index 5b39394de7..2a31111bb3 100644 --- a/module/os/linux/zfs/abd_os.c +++ b/module/os/linux/zfs/abd_os.c @@ -1143,7 +1143,9 @@ abd_bio_map_off(struct bio *bio, abd_t *abd, abd_iter_init(&aiter, abd); abd_iter_advance(&aiter, off); - for (int i = 0; i < bio->bi_max_vecs; i++) { + boolean_t is_split = B_FALSE; + unsigned int split_rem = 0; + for (int i = bio->bi_vcnt; i < bio->bi_max_vecs; i++) { struct page *pg; size_t len, sgoff, pgoff; struct scatterlist *sg; @@ -1154,7 +1156,36 @@ abd_bio_map_off(struct bio *bio, abd_t *abd, sg = aiter.iter_sg; sgoff = aiter.iter_offset; pgoff = sgoff & (PAGESIZE - 1); - len = MIN(io_size, PAGESIZE - pgoff); + + if (is_split && + (io_size <= split_rem || ((i + 1) == bio->bi_max_vecs))) { + /* + * Last segment and we're split, so we have to add just + * enough to restore alignment. + */ + len = split_rem; + is_split = B_FALSE; + } + else if (pgoff != 0) { + /* + * This is a split page, so we need to ensure that we + * have room for the tail within this bio. + */ + if ((i + 1) == bio->bi_max_vecs) + break; + + /* Take up to the end of the page */ + len = PAGE_SIZE - pgoff; + + is_split = B_TRUE; + split_rem = pgoff; + } + else + len = PAGE_SIZE; + + if (len > io_size) + len = io_size; + ASSERT(len > 0); pg = nth_page(sg_page(sg), sgoff >> PAGE_SHIFT);