drm/i915/gem: Fix Virtual Memory mapping boundaries calculation
authorjsg <jsg@openbsd.org>
Thu, 15 Aug 2024 03:10:37 +0000 (03:10 +0000)
committerjsg <jsg@openbsd.org>
Thu, 15 Aug 2024 03:10:37 +0000 (03:10 +0000)
From Andi Shyti
4b09513ce93b3dcb590baaaff2ce96f2d098312d in linux-6.6.y/6.6.46
8bdd9ef7e9b1b2a73e394712b72b22055e0e26c3 in mainline linux

sys/dev/pci/drm/i915/gem/i915_gem_mman.c

index a5585e4..983f22f 100644 (file)
@@ -325,6 +325,41 @@ out:
        return i915_error_to_vmf_fault(err);
 }
 
+static void set_address_limits(struct vm_area_struct *area,
+                              struct i915_vma *vma,
+                              unsigned long obj_offset,
+                              unsigned long *start_vaddr,
+                              unsigned long *end_vaddr)
+{
+       unsigned long vm_start, vm_end, vma_size; /* user's memory parameters */
+       long start, end; /* memory boundaries */
+
+       /*
+        * Let's move into the ">> PAGE_SHIFT"
+        * domain to be sure not to lose bits
+        */
+       vm_start = area->vm_start >> PAGE_SHIFT;
+       vm_end = area->vm_end >> PAGE_SHIFT;
+       vma_size = vma->size >> PAGE_SHIFT;
+
+       /*
+        * Calculate the memory boundaries by considering the offset
+        * provided by the user during memory mapping and the offset
+        * provided for the partial mapping.
+        */
+       start = vm_start;
+       start -= obj_offset;
+       start += vma->gtt_view.partial.offset;
+       end = start + vma_size;
+
+       start = max_t(long, start, vm_start);
+       end = min_t(long, end, vm_end);
+
+       /* Let's move back into the "<< PAGE_SHIFT" domain */
+       *start_vaddr = (unsigned long)start << PAGE_SHIFT;
+       *end_vaddr = (unsigned long)end << PAGE_SHIFT;
+}
+
 static vm_fault_t vm_fault_gtt(struct vm_fault *vmf)
 {
 #define MIN_CHUNK_PAGES (SZ_1M >> PAGE_SHIFT)
@@ -337,14 +372,18 @@ static vm_fault_t vm_fault_gtt(struct vm_fault *vmf)
        struct i915_ggtt *ggtt = to_gt(i915)->ggtt;
        bool write = area->vm_flags & VM_WRITE;
        struct i915_gem_ww_ctx ww;
+       unsigned long obj_offset;
+       unsigned long start, end; /* memory boundaries */
        intel_wakeref_t wakeref;
        struct i915_vma *vma;
        pgoff_t page_offset;
+       unsigned long pfn;
        int srcu;
        int ret;
 
-       /* We don't use vmf->pgoff since that has the fake offset */
+       obj_offset = area->vm_pgoff - drm_vma_node_start(&mmo->vma_node);
        page_offset = (vmf->address - area->vm_start) >> PAGE_SHIFT;
+       page_offset += obj_offset;
 
        trace_i915_gem_object_fault(obj, page_offset, true, write);
 
@@ -437,12 +476,14 @@ retry:
        if (ret)
                goto err_unpin;
 
+       set_address_limits(area, vma, obj_offset, &start, &end);
+
+       pfn = (ggtt->gmadr.start + i915_ggtt_offset(vma)) >> PAGE_SHIFT;
+       pfn += (start - area->vm_start) >> PAGE_SHIFT;
+       pfn += obj_offset - vma->gtt_view.partial.offset;
+
        /* Finally, remap it using the new GTT offset */
-       ret = remap_io_mapping(area,
-                              area->vm_start + (vma->gtt_view.partial.offset << PAGE_SHIFT),
-                              (ggtt->gmadr.start + i915_ggtt_offset(vma)) >> PAGE_SHIFT,
-                              min_t(u64, vma->size, area->vm_end - area->vm_start),
-                              &ggtt->iomap);
+       ret = remap_io_mapping(area, start, pfn, end - start, &ggtt->iomap);
        if (ret)
                goto err_fence;
 
@@ -655,6 +696,41 @@ remap_io_mapping(pmap_t pm, vm_prot_t mapprot,
        return 0;
 }
 
+static void set_address_limits(struct vm_map_entry *entry,
+                              struct i915_vma *vma,
+                              unsigned long obj_offset,
+                              unsigned long *start_vaddr,
+                              unsigned long *end_vaddr)
+{
+       unsigned long vm_start, vm_end, vma_size; /* user's memory parameters */
+       long start, end; /* memory boundaries */
+
+       /*
+        * Let's move into the ">> PAGE_SHIFT"
+        * domain to be sure not to lose bits
+        */
+       vm_start = entry->start >> PAGE_SHIFT;
+       vm_end = entry->end >> PAGE_SHIFT;
+       vma_size = vma->size >> PAGE_SHIFT;
+
+       /*
+        * Calculate the memory boundaries by considering the offset
+        * provided by the user during memory mapping and the offset
+        * provided for the partial mapping.
+        */
+       start = vm_start;
+       start -= obj_offset;
+       start += vma->gtt_view.partial.offset;
+       end = start + vma_size;
+
+       start = max_t(long, start, vm_start);
+       end = min_t(long, end, vm_end);
+
+       /* Let's move back into the "<< PAGE_SHIFT" domain */
+       *start_vaddr = (unsigned long)start << PAGE_SHIFT;
+       *end_vaddr = (unsigned long)end << PAGE_SHIFT;
+}
+
 static int
 vm_fault_gtt(struct i915_mmap_offset *mmo, struct uvm_faultinfo *ufi,
     vaddr_t vaddr, vm_prot_t access_type)
@@ -668,13 +744,16 @@ vm_fault_gtt(struct i915_mmap_offset *mmo, struct uvm_faultinfo *ufi,
        struct i915_ggtt *ggtt = to_gt(i915)->ggtt;
        int write = !!(access_type & PROT_WRITE);
        struct i915_gem_ww_ctx ww;
+       unsigned long obj_offset;
+       unsigned long start, end; /* memory boundaries */
        intel_wakeref_t wakeref;
        struct i915_vma *vma;
        pgoff_t page_offset;
+       unsigned long pfn;
        int srcu;
        int ret;
 
-       /* We don't use vmf->pgoff since that has the fake offset */
+       obj_offset = entry->offset - drm_vma_node_start(&mmo->vma_node);
        page_offset = (vaddr - entry->start) >> PAGE_SHIFT;
 
        trace_i915_gem_object_fault(obj, page_offset, true, write);
@@ -768,11 +847,15 @@ retry:
        if (ret)
                goto err_unpin;
 
+       set_address_limits(entry, vma, obj_offset, &start, &end);
+
+       pfn = (ggtt->gmadr.start + i915_ggtt_offset(vma)) >> PAGE_SHIFT;
+       pfn += (start - entry->start) >> PAGE_SHIFT;
+       pfn += obj_offset - vma->gtt_view.partial.offset;
+
        /* Finally, remap it using the new GTT offset */
        ret = remap_io_mapping(ufi->orig_map->pmap, entry->protection,
-                              entry->start + (vma->gtt_view.partial.offset << PAGE_SHIFT),
-                              (ggtt->gmadr.start + i915_ggtt_offset(vma)) >> PAGE_SHIFT,
-                              min_t(u64, vma->size, entry->end - entry->start));
+                              start, pfn, end - start);
        if (ret)
                goto err_fence;