Optimize processing folios larger than one page size for the direct io case. If contiguous pages are part of the same folio, collate the processing instead of processing each page in the folio separately. Signed-off-by: Joanne Koong <joannelkoong@xxxxxxxxx> --- fs/fuse/file.c | 52 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 13 deletions(-) diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 971624557810..bbc862c1b3fa 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1484,7 +1484,8 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii, } while (nbytes < *nbytesp && nr_pages < max_pages) { - unsigned nfolios, i; + struct folio *prev_folio = NULL; + unsigned npages, i; size_t start; ret = iov_iter_extract_pages(ii, &pages, @@ -1496,23 +1497,48 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii, nbytes += ret; - nfolios = DIV_ROUND_UP(ret + start, PAGE_SIZE); + npages = DIV_ROUND_UP(ret + start, PAGE_SIZE); - for (i = 0; i < nfolios; i++) { + /* + * We must check each extracted page. We can't assume every page + * in a large folio is used. For example, userspace may mmap() a + * file PROT_WRITE, MAP_PRIVATE, and then store to the middle of + * a large folio, in which case the extracted pages could be + * + * folio A page 0 + * folio A page 1 + * folio B page 0 + * folio A page 3 + * + * where folio A belongs to the file and folio B is an anonymous + * COW page. + */ + for (i = 0; i < npages && ret; i++) { struct folio *folio = page_folio(pages[i]); - unsigned int offset = start + - (folio_page_idx(folio, pages[i]) << PAGE_SHIFT); - unsigned int len = min_t(unsigned int, ret, PAGE_SIZE - start); + unsigned int offset; + unsigned int len; + + WARN_ON(!folio); + + len = min_t(unsigned int, ret, PAGE_SIZE - start); + + if (folio == prev_folio && pages[i] != pages[i - 1]) { + WARN_ON(ap->folios[ap->num_folios - 1] != folio); + ap->descs[ap->num_folios - 1].length += len; + WARN_ON(ap->descs[ap->num_folios - 1].length > folio_size(folio)); + } else { + offset = start + (folio_page_idx(folio, pages[i]) << PAGE_SHIFT); + ap->descs[ap->num_folios].offset = offset; + ap->descs[ap->num_folios].length = len; + ap->folios[ap->num_folios] = folio; + start = 0; + ap->num_folios++; + prev_folio = folio; + } - ap->descs[ap->num_folios].offset = offset; - ap->descs[ap->num_folios].length = len; - ap->folios[ap->num_folios] = folio; - start = 0; ret -= len; - ap->num_folios++; } - - nr_pages += nfolios; + nr_pages += npages; } kfree(pages); -- 2.43.5