On 4/26/25 02:08, Joanne Koong wrote: > 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> > Reviewed-by: Jeff Layton <jlayton@xxxxxxxxxx> Reviewed-by: Bernd Schubert <bschubert@xxxxxxx> > --- > fs/fuse/file.c | 55 +++++++++++++++++++++++++++++++++++++------------- > 1 file changed, 41 insertions(+), 14 deletions(-) > > diff --git a/fs/fuse/file.c b/fs/fuse/file.c > index 9a31f2a516b9..61eaec1c993b 100644 > --- a/fs/fuse/file.c > +++ b/fs/fuse/file.c > @@ -1490,7 +1490,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, > @@ -1502,23 +1503,49 @@ 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++) { > - 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); > + /* > + * 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; > + unsigned int offset; > + unsigned int len; > + > + WARN_ON(!pages[i]); > + folio = page_folio(pages[i]); > + > + 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); >