bio_map: avoid splitting abd pages across bios
If we encounter a split page, we add two iovecs to the bio, one for the fragment of the buffer on each side of the split. In order to do this safely, we must be sure that we always have room for both fragments. Its possible for a linear abd to have multiple pages, in which case we want to add the "left" fragment, then a run of proper 4K pages. then then "right" fragment. In this way we can keep whole pages together as much possible. This change handles both cases by noticing a split page. If we don't have at least two iovecs remaining in the bio, then we abort outright (allowing the caller to allocate a new bio and try again). We add the "left" fragment, and note how big we expect the right fragment to be. Then we load in as many full pages as are available. When we reach the last iovec, we close out the bio by taking as uch as is necessary to restore alignment. Signed-off-by: Rob Norris <rob.norris@klarasystems.com> (cherry picked from commit 173cafcc3d8b6c94c61844c705d7a410f412a18e)
This commit is contained in:
parent
98bcc390e8
commit
1bd475d6c6
|
@ -1034,16 +1034,43 @@ abd_nr_pages_off(abd_t *abd, unsigned int size, size_t off)
|
||||||
static unsigned int
|
static unsigned int
|
||||||
bio_map(struct bio *bio, void *buf_ptr, unsigned int bio_size)
|
bio_map(struct bio *bio, void *buf_ptr, unsigned int bio_size)
|
||||||
{
|
{
|
||||||
unsigned int offset, size, i;
|
unsigned int offset, size;
|
||||||
struct page *page;
|
struct page *page;
|
||||||
|
|
||||||
offset = offset_in_page(buf_ptr);
|
offset = offset_in_page(buf_ptr);
|
||||||
for (i = 0; i < bio->bi_max_vecs; i++) {
|
|
||||||
size = PAGE_SIZE - offset;
|
|
||||||
|
|
||||||
|
boolean_t is_split = B_FALSE;
|
||||||
|
unsigned int split_rem = 0;
|
||||||
|
for (int i = bio->bi_vcnt; i < bio->bi_max_vecs; i++) {
|
||||||
if (bio_size <= 0)
|
if (bio_size <= 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (is_split &&
|
||||||
|
(bio_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.
|
||||||
|
*/
|
||||||
|
size = split_rem;
|
||||||
|
is_split = B_FALSE;
|
||||||
|
}
|
||||||
|
else if (offset != 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 */
|
||||||
|
size = PAGE_SIZE - offset;
|
||||||
|
|
||||||
|
is_split = B_TRUE;
|
||||||
|
split_rem = offset;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
size = PAGE_SIZE;
|
||||||
|
|
||||||
if (size > bio_size)
|
if (size > bio_size)
|
||||||
size = bio_size;
|
size = bio_size;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue