abd_bio_map_off: avoid splitting scatter pages across bios

This is the same change as the previous commit, but for scatter abds.

Its less clear if this change is needed. Since scatter abds are only
ever added a page at time, both sides of the split should always be
added in consecutive segments.

Intuitively though, it may be possible for a partially-filled bio to be
used, or a bio with an odd number of iovecs, and that then could lead to
a misaligned bio. While I've not been able to reproduce this at all, it
seems to make sense to protect against it.

Signed-off-by: Rob Norris <rob.norris@klarasystems.com>
(cherry picked from commit cbdf21fd1a32a5e696a22cad497d9211221fa309)
This commit is contained in:
Rob Norris 2023-07-21 16:35:21 +10:00 committed by Geoff Amey
parent 1bd475d6c6
commit 74e8091130
1 changed files with 33 additions and 2 deletions

View File

@ -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);