drm_gem_put_pages(), which we rely on for returning BO pages to shmem,
assume per-folio refcounting and not per-page. If we call
shmem_read_mapping_page() per-page, we break this assumption and leak
pages every time we get a huge page allocated.

v2:
- Rework the logic for() loop to better match the folio-granular
  allocation scheme

Cc: Loïc Molinari <[email protected]>
Fixes: c12e9fcb5a5a ("drm/panfrost: Introduce huge tmpfs mountpoint option")
Signed-off-by: Boris Brezillon <[email protected]>
---
 drivers/gpu/drm/panfrost/panfrost_mmu.c | 40 +++++++++++++++----------
 1 file changed, 25 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c 
b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index 02ccc05e23bb..3f8e7eced1c0 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -586,12 +586,12 @@ addr_to_mapping(struct panfrost_device *pfdev, int as, 
u64 addr)
 static int panfrost_mmu_map_fault_addr(struct panfrost_device *pfdev, int as,
                                       u64 addr)
 {
-       int ret, i;
+       int ret;
        struct panfrost_gem_mapping *bomapping;
        struct panfrost_gem_object *bo;
        struct address_space *mapping;
        struct drm_gem_object *obj;
-       pgoff_t page_offset;
+       pgoff_t page_offset, nr_pages;
        struct sg_table *sgt;
        struct page **pages;
 
@@ -612,6 +612,7 @@ static int panfrost_mmu_map_fault_addr(struct 
panfrost_device *pfdev, int as,
        addr &= ~((u64)SZ_2M - 1);
        page_offset = addr >> PAGE_SHIFT;
        page_offset -= bomapping->mmnode.start;
+       nr_pages = bo->base.base.size >> PAGE_SHIFT;
 
        obj = &bo->base.base;
 
@@ -625,8 +626,7 @@ static int panfrost_mmu_map_fault_addr(struct 
panfrost_device *pfdev, int as,
                        goto err_unlock;
                }
 
-               pages = kvmalloc_array(bo->base.base.size >> PAGE_SHIFT,
-                                      sizeof(struct page *), GFP_KERNEL | 
__GFP_ZERO);
+               pages = kvmalloc_array(nr_pages, sizeof(struct page *), 
GFP_KERNEL | __GFP_ZERO);
                if (!pages) {
                        kvfree(bo->sgts);
                        bo->sgts = NULL;
@@ -648,20 +648,30 @@ static int panfrost_mmu_map_fault_addr(struct 
panfrost_device *pfdev, int as,
        mapping = bo->base.base.filp->f_mapping;
        mapping_set_unevictable(mapping);
 
-       for (i = page_offset; i < page_offset + NUM_FAULT_PAGES; i++) {
-               /* Can happen if the last fault only partially filled this
-                * section of the pages array before failing. In that case
-                * we skip already filled pages.
-                */
-               if (pages[i])
-                       continue;
+       for (pgoff_t pg = page_offset; pg < page_offset + NUM_FAULT_PAGES;) {
+               bool already_owned = false;
+               struct folio *folio;
 
-               pages[i] = shmem_read_mapping_page(mapping, i);
-               if (IS_ERR(pages[i])) {
-                       ret = PTR_ERR(pages[i]);
-                       pages[i] = NULL;
+               folio = shmem_read_folio(mapping, pg);
+               if (IS_ERR(folio)) {
+                       ret = PTR_ERR(folio);
                        goto err_unlock;
                }
+
+               pg &= ~(folio_nr_pages(folio) - 1);
+               for (u32 i = 0; i < folio_nr_pages(folio) && pg < nr_pages; 
i++) {
+                       if (pages[pg])
+                               already_owned = true;
+
+                       pages[pg++] = folio_page(folio, i);
+               }
+
+               /* We always fill the page array at a folio granularity so
+                * there's no valid reason for a folio range to be partially
+                * populated.
+                */
+               if (drm_WARN_ON(&pfdev->base, already_owned))
+                       folio_put(folio);
        }
 
        ret = sg_alloc_table_from_pages(sgt, pages + page_offset,
-- 
2.52.0

Reply via email to