drm/panthor: Lazily allocate pages on mmap()

Defer pages allocation until their first access.

v2:
- Don't deal with FAULT_FLAG_INTERRUPTIBLE
- Make sure bo->backing.pages is never an ERR_PTR()
- Drop a useless vm_fault_t local var
- Fix comment in panthor_gem_fault()

v3:
- Collect R-bs

v4:
- No changes

v5:
- No changes

v6:
- Fix huge_fault handling

v7:
- No changes

Reviewed-by: Steven Price <steven.price@arm.com>
Reviewed-by: Liviu Dudau <liviu.dudau@arm.com>
Link: https://patch.msgid.link/20260401134854.2275433-8-boris.brezillon@collabora.com
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
This commit is contained in:
Boris Brezillon 2026-04-01 15:48:51 +02:00
parent 68cbf96b1e
commit f80c3886ed

View File

@ -604,15 +604,6 @@ static int panthor_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *v
if (is_cow_mapping(vma->vm_flags))
return -EINVAL;
dma_resv_lock(obj->resv, NULL);
ret = panthor_gem_backing_get_pages_locked(bo);
if (!ret)
ret = panthor_gem_prep_for_cpu_map_locked(bo);
dma_resv_unlock(obj->resv);
if (ret)
return ret;
vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP);
vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
if (should_map_wc(bo))
@ -653,35 +644,95 @@ static vm_fault_t insert_page(struct vm_fault *vmf, unsigned int order, struct p
return VM_FAULT_FALLBACK;
}
static vm_fault_t nonblocking_page_setup(struct vm_fault *vmf,
unsigned int order,
pgoff_t page_offset)
{
struct vm_area_struct *vma = vmf->vma;
struct panthor_gem_object *bo = to_panthor_bo(vma->vm_private_data);
vm_fault_t ret;
if (!dma_resv_trylock(bo->base.resv))
return VM_FAULT_RETRY;
if (bo->backing.pages)
ret = insert_page(vmf, order, bo->backing.pages[page_offset]);
else
ret = VM_FAULT_RETRY;
dma_resv_unlock(bo->base.resv);
return ret;
}
static vm_fault_t blocking_page_setup(struct vm_fault *vmf, unsigned int order,
struct panthor_gem_object *bo,
pgoff_t page_offset, bool mmap_lock_held)
{
vm_fault_t ret;
int err;
err = dma_resv_lock_interruptible(bo->base.resv, NULL);
if (err)
return mmap_lock_held ? VM_FAULT_NOPAGE : VM_FAULT_RETRY;
err = panthor_gem_backing_get_pages_locked(bo);
if (!err)
err = panthor_gem_prep_for_cpu_map_locked(bo);
if (err) {
ret = mmap_lock_held ? VM_FAULT_SIGBUS : VM_FAULT_RETRY;
} else {
struct page *page = bo->backing.pages[page_offset];
if (mmap_lock_held)
ret = insert_page(vmf, order, page);
else
ret = VM_FAULT_RETRY;
}
dma_resv_unlock(bo->base.resv);
return ret;
}
static vm_fault_t panthor_gem_any_fault(struct vm_fault *vmf, unsigned int order)
{
struct vm_area_struct *vma = vmf->vma;
struct drm_gem_object *obj = vma->vm_private_data;
struct panthor_gem_object *bo = to_panthor_bo(vma->vm_private_data);
loff_t num_pages = obj->size >> PAGE_SHIFT;
vm_fault_t ret;
loff_t num_pages = bo->base.size >> PAGE_SHIFT;
pgoff_t page_offset;
vm_fault_t ret;
if (order && order != PMD_ORDER)
return VM_FAULT_FALLBACK;
/* Offset to faulty address in the VMA. */
page_offset = vmf->pgoff - vma->vm_pgoff;
if (page_offset >= num_pages)
return VM_FAULT_SIGBUS;
dma_resv_lock(bo->base.resv, NULL);
ret = nonblocking_page_setup(vmf, order, page_offset);
if (ret != VM_FAULT_RETRY)
return ret;
if (page_offset >= num_pages ||
drm_WARN_ON_ONCE(obj->dev, !bo->backing.pages)) {
ret = VM_FAULT_SIGBUS;
goto out;
/* Check if we're allowed to retry. */
if (fault_flag_allow_retry_first(vmf->flags)) {
/* If we're allowed to retry but not wait here, return
* immediately, the wait will be done when the fault
* handler is called again, with the mmap_lock held.
*/
if (vmf->flags & FAULT_FLAG_RETRY_NOWAIT)
return VM_FAULT_RETRY;
/* Wait with the mmap lock released, if we're allowed to. */
drm_gem_object_get(&bo->base);
mmap_read_unlock(vmf->vma->vm_mm);
ret = blocking_page_setup(vmf, order, bo, page_offset, false);
drm_gem_object_put(&bo->base);
return ret;
}
ret = insert_page(vmf, order, bo->backing.pages[page_offset]);
out:
dma_resv_unlock(bo->base.resv);
return ret;
return blocking_page_setup(vmf, order, bo, page_offset, true);
}
static vm_fault_t panthor_gem_fault(struct vm_fault *vmf)
@ -689,31 +740,12 @@ static vm_fault_t panthor_gem_fault(struct vm_fault *vmf)
return panthor_gem_any_fault(vmf, 0);
}
static void panthor_gem_vm_open(struct vm_area_struct *vma)
{
struct panthor_gem_object *bo = to_panthor_bo(vma->vm_private_data);
drm_WARN_ON(bo->base.dev, drm_gem_is_imported(&bo->base));
dma_resv_lock(bo->base.resv, NULL);
/* We should have already pinned the pages when the buffer was first
* mmap'd, vm_open() just grabs an additional reference for the new
* mm the vma is getting copied into (ie. on fork()).
*/
drm_WARN_ON_ONCE(bo->base.dev, !bo->backing.pages);
dma_resv_unlock(bo->base.resv);
drm_gem_vm_open(vma);
}
static const struct vm_operations_struct panthor_gem_vm_ops = {
.fault = panthor_gem_fault,
#ifdef CONFIG_ARCH_SUPPORTS_PMD_PFNMAP
.huge_fault = panthor_gem_any_fault,
#endif
.open = panthor_gem_vm_open,
.open = drm_gem_vm_open,
.close = drm_gem_vm_close,
};