]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
fuse: fix direct io folio offset and length calculation
authorJoanne Koong <joannelkoong@gmail.com>
Wed, 11 Dec 2024 20:55:56 +0000 (12:55 -0800)
committerMiklos Szeredi <mszeredi@redhat.com>
Thu, 12 Dec 2024 08:27:42 +0000 (09:27 +0100)
For the direct io case, the pages from userspace may be part of a huge
folio, even if all folios in the page cache for fuse are small.

Fix the logic for calculating the offset and length of the folio for
the direct io case, which currently incorrectly assumes that all folios
encountered are one page size.

Fixes: 3b97c3652d91 ("fuse: convert direct io to use folios")
Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
Reviewed-by: Jingbo Xu <jefflexu@linux.alibaba.com>
Reviewed-by: Bernd Schubert <bschubert@ddn.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/file.c

index 88d0946b5bc98705e0d895bc798aa4d9df080c3c..15b08d6a57398fd14e06878fde2f8245d835c2f3 100644 (file)
@@ -1557,18 +1557,22 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii,
 
                nbytes += ret;
 
-               ret += start;
-               /* Currently, all folios in FUSE are one page */
-               nfolios = DIV_ROUND_UP(ret, PAGE_SIZE);
-
-               ap->descs[ap->num_folios].offset = start;
-               fuse_folio_descs_length_init(ap->descs, ap->num_folios, nfolios);
-               for (i = 0; i < nfolios; i++)
-                       ap->folios[i + ap->num_folios] = page_folio(pages[i]);
-
-               ap->num_folios += nfolios;
-               ap->descs[ap->num_folios - 1].length -=
-                       (PAGE_SIZE - ret) & (PAGE_SIZE - 1);
+               nfolios = 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);
+
+                       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;
        }
        kfree(pages);