Thomas pointed out that i915 is using apply_page_range instead of
vm_insert_pfn_prot to circumvent the PAT lookup and generally speed up
the page fault handling.

I've thought I give it a try and measure how much this can improve
things and it turned that mapping a 1GiB buffer is now more than 4x times
faster than before.

Signed-off-by: Christian König <christian.koe...@amd.com>
---
 drivers/gpu/drm/ttm/ttm_bo_vm.c | 130 ++++++++++++++++----------------
 1 file changed, 64 insertions(+), 66 deletions(-)

diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
index a194db83421d..93764b166678 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
@@ -160,6 +160,38 @@ vm_fault_t ttm_bo_vm_reserve(struct ttm_buffer_object *bo,
 }
 EXPORT_SYMBOL(ttm_bo_vm_reserve);
 
+/* State bag for calls to ttm_bo_vm_apply_cb */
+struct ttm_bo_vm_bag {
+       struct mm_struct                *mm;
+       struct ttm_buffer_object        *bo;
+       struct ttm_tt                   *ttm;
+       unsigned long                   page_offset;
+       pgprot_t                        prot;
+};
+
+/* Callback to fill in a specific PTE */
+static int ttm_bo_vm_apply_cb(pte_t *pte, unsigned long addr, void *data)
+{
+        struct ttm_bo_vm_bag *bag = data;
+       struct ttm_buffer_object *bo = bag->bo;
+       unsigned long pfn;
+
+       if (bo->resource->bus.is_iomem) {
+               pfn = ttm_bo_io_mem_pfn(bo, bag->page_offset);
+       } else {
+               struct page *page = bag->ttm->pages[bag->page_offset];
+
+               if (unlikely(!page))
+                       return -ENOMEM;
+               pfn = page_to_pfn(page);
+       }
+
+        /* Special PTE are not associated with any struct page */
+        set_pte_at(bag->mm, addr, pte, pte_mkspecial(pfn_pte(pfn, bag->prot)));
+       bag->page_offset++;
+       return 0;
+}
+
 /**
  * ttm_bo_vm_fault_reserved - TTM fault helper
  * @vmf: The struct vm_fault given as argument to the fault callback
@@ -183,101 +215,67 @@ vm_fault_t ttm_bo_vm_fault_reserved(struct vm_fault *vmf,
                                    pgoff_t num_prefault)
 {
        struct vm_area_struct *vma = vmf->vma;
-       struct ttm_buffer_object *bo = vma->vm_private_data;
-       struct ttm_device *bdev = bo->bdev;
-       unsigned long page_offset;
-       unsigned long page_last;
-       unsigned long pfn;
-       struct ttm_tt *ttm = NULL;
-       struct page *page;
+       struct ttm_bo_vm_bag bag = {
+               .mm = vma->vm_mm,
+               .bo = vma->vm_private_data
+       };
+       unsigned long size;
+       vm_fault_t ret;
        int err;
-       pgoff_t i;
-       vm_fault_t ret = VM_FAULT_NOPAGE;
-       unsigned long address = vmf->address;
 
        /*
         * Wait for buffer data in transit, due to a pipelined
         * move.
         */
-       ret = ttm_bo_vm_fault_idle(bo, vmf);
+       ret = ttm_bo_vm_fault_idle(bag.bo, vmf);
        if (unlikely(ret != 0))
                return ret;
 
-       err = ttm_mem_io_reserve(bdev, bo->resource);
+       err = ttm_mem_io_reserve(bag.bo->bdev, bag.bo->resource);
        if (unlikely(err != 0))
                return VM_FAULT_SIGBUS;
 
-       page_offset = ((address - vma->vm_start) >> PAGE_SHIFT) +
-               vma->vm_pgoff - drm_vma_node_start(&bo->base.vma_node);
-       page_last = vma_pages(vma) + vma->vm_pgoff -
-               drm_vma_node_start(&bo->base.vma_node);
-
-       if (unlikely(page_offset >= PFN_UP(bo->base.size)))
+       bag.page_offset = ((vmf->address - vma->vm_start) >> PAGE_SHIFT) +
+               vma->vm_pgoff - drm_vma_node_start(&bag.bo->base.vma_node);
+       if (unlikely(bag.page_offset >= PFN_UP(bag.bo->base.size)))
                return VM_FAULT_SIGBUS;
 
-       prot = ttm_io_prot(bo, bo->resource, prot);
-       if (!bo->resource->bus.is_iomem) {
+       prot = ttm_io_prot(bag.bo, bag.bo->resource, prot);
+       if (!bag.bo->resource->bus.is_iomem) {
                struct ttm_operation_ctx ctx = {
                        .interruptible = true,
                        .no_wait_gpu = false,
                        .force_alloc = true
                };
 
-               ttm = bo->ttm;
-               err = ttm_bo_populate(bo, &ctx);
-               if (err) {
-                       if (err == -EINTR || err == -ERESTARTSYS ||
-                           err == -EAGAIN)
-                               return VM_FAULT_NOPAGE;
-
-                       pr_debug("TTM fault hit %pe.\n", ERR_PTR(err));
-                       return VM_FAULT_SIGBUS;
-               }
+               bag.ttm = bag.bo->ttm;
+               err = ttm_bo_populate(bag.bo, &ctx);
+               if (err)
+                       goto error;
        } else {
                /* Iomem should not be marked encrypted */
                prot = pgprot_decrypted(prot);
        }
+       bag.prot = prot;
 
-       /*
-        * Speculatively prefault a number of pages. Only error on
-        * first page.
-        */
-       for (i = 0; i < num_prefault; ++i) {
-               if (bo->resource->bus.is_iomem) {
-                       pfn = ttm_bo_io_mem_pfn(bo, page_offset);
-               } else {
-                       page = ttm->pages[page_offset];
-                       if (unlikely(!page && i == 0)) {
-                               return VM_FAULT_OOM;
-                       } else if (unlikely(!page)) {
-                               break;
-                       }
-                       pfn = page_to_pfn(page);
-               }
+       /* Speculatively prefault a number of pages. */
+       size = min(num_prefault << PAGE_SHIFT, vma->vm_end - vmf->address);
+       err = apply_to_page_range(vma->vm_mm, vmf->address, size,
+                                 ttm_bo_vm_apply_cb, &bag);
 
-               /*
-                * Note that the value of @prot at this point may differ from
-                * the value of @vma->vm_page_prot in the caching- and
-                * encryption bits. This is because the exact location of the
-                * data may not be known at mmap() time and may also change
-                * at arbitrary times while the data is mmap'ed.
-                * See vmf_insert_pfn_prot() for a discussion.
-                */
-               ret = vmf_insert_pfn_prot(vma, address, pfn, prot);
+error:
+       if (err == -EINTR || err == -ERESTARTSYS || err == -EAGAIN)
+               return VM_FAULT_NOPAGE;
 
-               /* Never error on prefaulted PTEs */
-               if (unlikely((ret & VM_FAULT_ERROR))) {
-                       if (i == 0)
-                               return VM_FAULT_NOPAGE;
-                       else
-                               break;
-               }
+       if (err == -ENOMEM)
+               return VM_FAULT_OOM;
 
-               address += PAGE_SIZE;
-               if (unlikely(++page_offset >= page_last))
-                       break;
+       if (err) {
+               pr_debug("TTM fault hit %pe.\n", ERR_PTR(err));
+               return VM_FAULT_SIGBUS;
        }
-       return ret;
+
+       return VM_FAULT_NOPAGE;
 }
 EXPORT_SYMBOL(ttm_bo_vm_fault_reserved);
 
-- 
2.43.0

Reply via email to