Skip to content

Commit e4e535b

Browse files
Ming Leiaxboe
authored andcommitted
iov_iter: don't require contiguous pages in iov_iter_extract_bvec_pages
The iov_iter_extract_pages interface allows to return physically discontiguous pages, as long as all but the first and last page in the array are page aligned and page size. Rewrite iov_iter_extract_bvec_pages to take advantage of that instead of only returning ranges of physically contiguous pages. Signed-off-by: Ming Lei <[email protected]> [hch: minor cleanups, new commit log] Signed-off-by: Christoph Hellwig <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Jens Axboe <[email protected]>
1 parent f1be178 commit e4e535b

File tree

1 file changed

+45
-22
lines changed

1 file changed

+45
-22
lines changed

lib/iov_iter.c

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1677,44 +1677,67 @@ static ssize_t iov_iter_extract_xarray_pages(struct iov_iter *i,
16771677
}
16781678

16791679
/*
1680-
* Extract a list of contiguous pages from an ITER_BVEC iterator. This does
1681-
* not get references on the pages, nor does it get a pin on them.
1680+
* Extract a list of virtually contiguous pages from an ITER_BVEC iterator.
1681+
* This does not get references on the pages, nor does it get a pin on them.
16821682
*/
16831683
static ssize_t iov_iter_extract_bvec_pages(struct iov_iter *i,
16841684
struct page ***pages, size_t maxsize,
16851685
unsigned int maxpages,
16861686
iov_iter_extraction_t extraction_flags,
16871687
size_t *offset0)
16881688
{
1689-
struct page **p, *page;
1690-
size_t skip = i->iov_offset, offset, size;
1691-
int k;
1689+
size_t skip = i->iov_offset, size = 0;
1690+
struct bvec_iter bi;
1691+
int k = 0;
16921692

1693-
for (;;) {
1694-
if (i->nr_segs == 0)
1695-
return 0;
1696-
size = min(maxsize, i->bvec->bv_len - skip);
1697-
if (size)
1698-
break;
1693+
if (i->nr_segs == 0)
1694+
return 0;
1695+
1696+
if (i->iov_offset == i->bvec->bv_len) {
16991697
i->iov_offset = 0;
17001698
i->nr_segs--;
17011699
i->bvec++;
17021700
skip = 0;
17031701
}
1702+
bi.bi_size = maxsize + skip;
1703+
bi.bi_bvec_done = skip;
1704+
1705+
maxpages = want_pages_array(pages, maxsize, skip, maxpages);
1706+
1707+
while (bi.bi_size && bi.bi_idx < i->nr_segs) {
1708+
struct bio_vec bv = bvec_iter_bvec(i->bvec, bi);
1709+
1710+
/*
1711+
* The iov_iter_extract_pages interface only allows an offset
1712+
* into the first page. Break out of the loop if we see an
1713+
* offset into subsequent pages, the caller will have to call
1714+
* iov_iter_extract_pages again for the reminder.
1715+
*/
1716+
if (k) {
1717+
if (bv.bv_offset)
1718+
break;
1719+
} else {
1720+
*offset0 = bv.bv_offset;
1721+
}
17041722

1705-
skip += i->bvec->bv_offset;
1706-
page = i->bvec->bv_page + skip / PAGE_SIZE;
1707-
offset = skip % PAGE_SIZE;
1708-
*offset0 = offset;
1723+
(*pages)[k++] = bv.bv_page;
1724+
size += bv.bv_len;
17091725

1710-
maxpages = want_pages_array(pages, size, offset, maxpages);
1711-
if (!maxpages)
1712-
return -ENOMEM;
1713-
p = *pages;
1714-
for (k = 0; k < maxpages; k++)
1715-
p[k] = page + k;
1726+
if (k >= maxpages)
1727+
break;
1728+
1729+
/*
1730+
* We are done when the end of the bvec doesn't align to a page
1731+
* boundary as that would create a hole in the returned space.
1732+
* The caller will handle this with another call to
1733+
* iov_iter_extract_pages.
1734+
*/
1735+
if (bv.bv_offset + bv.bv_len != PAGE_SIZE)
1736+
break;
1737+
1738+
bvec_iter_advance_single(i->bvec, &bi, bv.bv_len);
1739+
}
17161740

1717-
size = min_t(size_t, size, maxpages * PAGE_SIZE - offset);
17181741
iov_iter_advance(i, size);
17191742
return size;
17201743
}

0 commit comments

Comments
 (0)