[PATCH v3 12/16] accel/thames: Install memory mappings populated and dirty

From: Tomeu Vizoso

Date: Fri Oct 23 2026 - 02:45:41 EST


The GEM DMA mmap helper maps the whole buffer up front, but with pages
that fault on their first write, since the A53 in the J722S has no
hardware dirty-bit management. The GEM shmem helper maps by page fault,
one at each page's first touch and another at its first write, which
its write-notify needs. Neither helper can install the pages writable
and dirty, so the driver sets the protection itself: for contiguous
buffers it calls dma_mmap_pages() with it, and for the others it
installs the pages with remap_pfn_range().

This saves 20% of the total time in the first invoke of a model on the
J722S.

Assisted-by: LLM
Signed-off-by: Tomeu Vizoso <tomeu@xxxxxxxxxxxxxxx>
---
drivers/accel/thames/thames_gem.c | 76 +++++++++++++++++++++++++++++++++++++--
1 file changed, 73 insertions(+), 3 deletions(-)

diff --git a/drivers/accel/thames/thames_gem.c b/drivers/accel/thames/thames_gem.c
index 5a4dc497768f..9f55169c6370 100644
--- a/drivers/accel/thames/thames_gem.c
+++ b/drivers/accel/thames/thames_gem.c
@@ -808,12 +808,82 @@ static void thames_gem_shmem_bo_free(struct drm_gem_object *obj)
drm_gem_shmem_free(&bo->shmem);
}

+/*
+ * Installed dirty and writable: the A53s have no hardware dirty-bit management,
+ * so every first write would fault.
+ */
+static int thames_gem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+ struct drm_gem_dma_object *dma_obj = to_drm_gem_dma_obj(obj);
+ pgprot_t prot;
+ int ret;
+
+ vma->vm_pgoff -= drm_vma_node_start(&obj->vma_node);
+ vm_flags_mod(vma, VM_DONTDUMP | VM_DONTEXPAND, VM_PFNMAP);
+
+ prot = vm_get_page_prot(vma->vm_flags);
+ if (vma->vm_flags & VM_WRITE)
+ prot = pte_pgprot(pte_mkdirty(pte_mkwrite_novma(pfn_pte(0, prot))));
+ vma->vm_page_prot = prot;
+
+ ret = dma_mmap_pages(drm_dev_dma_dev(obj->dev), vma, vma->vm_end - vma->vm_start,
+ virt_to_page(dma_obj->vaddr));
+ if (ret)
+ drm_gem_vm_close(vma);
+
+ return ret;
+}
+
+/*
+ * Every page installed now, dirty and writable: they are pinned for the
+ * object's life, and the helper would take two faults on each.
+ */
+static int thames_gem_shmem_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
+{
+ struct thames_gem_object *bo = to_thames_bo(obj);
+ unsigned long addr = vma->vm_start;
+ struct scatterlist *sg;
+ pgprot_t prot;
+ unsigned int i;
+ int ret;
+
+ if (vma_is_cow_mapping(vma))
+ return -EINVAL;
+
+ vm_flags_set(vma, VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP);
+
+ prot = vm_get_page_prot(vma->vm_flags);
+ if (vma->vm_flags & VM_WRITE)
+ prot = pte_pgprot(pte_mkdirty(pte_mkwrite_novma(pfn_pte(0, prot))));
+ vma->vm_page_prot = prot;
+
+ for_each_sgtable_sg(bo->shmem.sgt, sg, i) {
+ unsigned long len = min_t(unsigned long, sg->length, vma->vm_end - addr);
+
+ ret = remap_pfn_range(vma, addr, page_to_pfn(sg_page(sg)), len, prot);
+ if (ret)
+ return ret;
+
+ addr += len;
+ if (addr == vma->vm_end)
+ break;
+ }
+
+ return 0;
+}
+
+/* Only the GEM reference: the pages are pinned for the object's life. */
+static const struct vm_operations_struct thames_gem_shmem_vm_ops = {
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
static const struct drm_gem_object_funcs thames_gem_funcs = {
.free = thames_gem_bo_free,
.print_info = drm_gem_dma_object_print_info,
.get_sg_table = drm_gem_dma_object_get_sg_table,
.vmap = drm_gem_dma_object_vmap,
- .mmap = drm_gem_dma_object_mmap,
+ .mmap = thames_gem_mmap,
.vm_ops = &drm_gem_dma_vm_ops,
};

@@ -825,8 +895,8 @@ static const struct drm_gem_object_funcs thames_gem_shmem_funcs = {
.get_sg_table = drm_gem_shmem_object_get_sg_table,
.vmap = drm_gem_shmem_object_vmap,
.vunmap = drm_gem_shmem_object_vunmap,
- .mmap = drm_gem_shmem_object_mmap,
- .vm_ops = &drm_gem_shmem_vm_ops,
+ .mmap = thames_gem_shmem_mmap,
+ .vm_ops = &thames_gem_shmem_vm_ops,
};

int thames_ioctl_bo_create(struct drm_device *ddev, void *data, struct drm_file *file)

--
2.54.0