[PATCH 08/10] drm/i915/gtt: Recursive ppgtt clear for gen8

Chris Wilson chris at chris-wilson.co.uk
Sat Jul 6 19:16:49 UTC 2019


With an explicit level, we can refactor the separate clear functions
as a simple recursive function. The additional knowledge of the level
allows us to spot when we can free an entire subtree at once.

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
 drivers/gpu/drm/i915/i915_gem_gtt.c | 120 +++++++++++-----------------
 1 file changed, 47 insertions(+), 73 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 5b96a744539c..ee3fef237bdf 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -929,95 +929,69 @@ static void gen8_ppgtt_cleanup(struct i915_address_space *vm)
 	free_scratch(vm);
 }
 
-/* Removes entries from a single page table, releasing it if it's empty.
- * Caller can use the return value to update higher-level entries.
- */
-static void gen8_ppgtt_clear_pt(const struct i915_address_space *vm,
-				struct i915_page_table *pt,
-				u64 start, u64 length)
+static void __gen8_ppgtt_clear(struct i915_address_space *vm,
+			       struct i915_page_directory * const pd,
+			       u64 *start, u64 end, int lvl)
 {
-	const unsigned int num_entries = gen8_pte_count(start, length);
-	gen8_pte_t *vaddr;
+	struct i915_page_scratch * const scratch = &vm->scratch[lvl];
+	unsigned int idx, len;
 
-	vaddr = kmap_atomic_px(pt);
-	memset64(vaddr + gen8_pte_index(start),
-		 vm->scratch[0].encode,
-		 num_entries);
-	kunmap_atomic(vaddr);
+	len = gen8_pd_range(*start, end, lvl, &idx);
+	GEM_BUG_ON(len > atomic_read(px_used(pd)));
 
-	GEM_BUG_ON(num_entries > atomic_read(&pt->used));
+	if (!lvl) {
+		u64 *vaddr;
 
-	atomic_sub(num_entries, &pt->used);
-}
+		vaddr = kmap_atomic_px(pd);
+		memset64(vaddr + idx, vm->scratch[0].encode, len);
+		kunmap_atomic(vaddr);
 
-static void gen8_ppgtt_clear_pd(struct i915_address_space *vm,
-				struct i915_page_directory *pd,
-				u64 start, u64 length)
-{
-	struct i915_page_table *pt;
-	u32 pde;
+		atomic_sub(len, px_used(pd));
+		*start += len << gen8_pd_shift(lvl);
+		return;
+	}
 
-	gen8_for_each_pde(pt, pd, start, length, pde) {
-		GEM_BUG_ON(px_base(pt) == px_base(&vm->scratch[1]));
+	do {
+		struct i915_page_table *pt = pd->entry[idx];
 
-		atomic_inc(&pt->used);
-		gen8_ppgtt_clear_pt(vm, pt, start, length);
-		if (release_pd_entry(pd, pde, pt, &vm->scratch[1]))
+		GEM_BUG_ON(pd->entry[idx] == scratch);
+
+		if (atomic_fetch_inc(&pt->used) >> pd_lvl(1) &&
+		    gen8_pd_index(*start, lvl - 1) == 0 &&
+		    (end - *start) >> gen8_pd_shift(lvl)) {
+			clear_pd_entry(pd, idx, scratch);
+			__gen8_ppgtt_cleanup(vm, as_pd(pt), I915_PDES, lvl - 1);
+			*start += gen8_pd_size(lvl);
+			continue;
+		}
+
+		__gen8_ppgtt_clear(vm, as_pd(pt), start, end, lvl - 1);
+		if (release_pd_entry(pd, idx, pt, scratch))
 			free_px(vm, pt);
-	}
+	} while (idx++, --len);
 }
 
-/* Removes entries from a single page dir pointer, releasing it if it's empty.
- * Caller can use the return value to update higher-level entries
- */
-static void gen8_ppgtt_clear_pdp(struct i915_address_space *vm,
-				 struct i915_page_directory * const pdp,
-				 u64 start, u64 length)
+static void gen8_ppgtt_clear(struct i915_address_space *vm,
+			     u64 start, u64 length)
 {
-	struct i915_page_directory *pd;
-	unsigned int pdpe;
-
-	gen8_for_each_pdpe(pd, pdp, start, length, pdpe) {
-		GEM_BUG_ON(px_base(pd) == px_base(&vm->scratch[2]));
-
-		atomic_inc(px_used(pd));
-		gen8_ppgtt_clear_pd(vm, pd, start, length);
-		if (release_pd_entry(pdp, pdpe, &pd->pt, &vm->scratch[2]))
-			free_px(vm, pd);
-	}
+	__gen8_ppgtt_clear(vm, i915_vm_to_ppgtt(vm)->pd,
+			   &start, start + length, vm->top);
 }
 
-static void gen8_ppgtt_clear_3lvl(struct i915_address_space *vm,
-				  u64 start, u64 length)
+static void gen8_ppgtt_clear_pd(struct i915_address_space *vm,
+				struct i915_page_directory *pd,
+				u64 start, u64 length)
 {
-	gen8_ppgtt_clear_pdp(vm, i915_vm_to_ppgtt(vm)->pd, start, length);
+	__gen8_ppgtt_clear(vm, pd, &start, start + length, 1);
 }
 
-/* Removes entries from a single pml4.
- * This is the top-level structure in 4-level page tables used on gen8+.
- * Empty entries are always scratch pml4e.
- */
-static void gen8_ppgtt_clear_4lvl(struct i915_address_space *vm,
-				  u64 start, u64 length)
+static void gen8_ppgtt_clear_pdp(struct i915_address_space *vm,
+				 struct i915_page_directory * const pdp,
+				 u64 start, u64 length)
 {
-	struct i915_ppgtt *ppgtt = i915_vm_to_ppgtt(vm);
-	struct i915_page_directory * const pml4 = ppgtt->pd;
-	struct i915_page_directory *pdp;
-	unsigned int pml4e;
-
-	GEM_BUG_ON(!i915_vm_is_4lvl(vm));
-
-	gen8_for_each_pml4e(pdp, pml4, start, length, pml4e) {
-		GEM_BUG_ON(px_base(pdp) == px_base(&vm->scratch[3]));
-
-		atomic_inc(px_used(pdp));
-		gen8_ppgtt_clear_pdp(vm, pdp, start, length);
-		if (release_pd_entry(pml4, pml4e, &pdp->pt, &vm->scratch[3]))
-			free_px(vm, pdp);
-	}
+	__gen8_ppgtt_clear(vm, pdp, &start, start + length, 2);
 }
 
-
 static int gen8_ppgtt_alloc_pd(struct i915_address_space *vm,
 			       struct i915_page_directory *pd,
 			       u64 start, u64 length)
@@ -1180,7 +1154,7 @@ static int gen8_ppgtt_alloc_4lvl(struct i915_address_space *vm,
 	if (release_pd_entry(pml4, pml4e, &pdp->pt, &vm->scratch[3]))
 		free_px(vm, pdp);
 unwind:
-	gen8_ppgtt_clear_4lvl(vm, from, start - from);
+	gen8_ppgtt_clear(vm, from, start - from);
 out:
 	if (alloc)
 		free_px(vm, alloc);
@@ -1613,7 +1587,6 @@ static struct i915_ppgtt *gen8_ppgtt_create(struct drm_i915_private *i915)
 	if (i915_vm_is_4lvl(&ppgtt->vm)) {
 		ppgtt->vm.allocate_va_range = gen8_ppgtt_alloc_4lvl;
 		ppgtt->vm.insert_entries = gen8_ppgtt_insert_4lvl;
-		ppgtt->vm.clear_range = gen8_ppgtt_clear_4lvl;
 	} else {
 		if (intel_vgpu_active(i915)) {
 			err = gen8_preallocate_top_level_pdp(ppgtt);
@@ -1623,9 +1596,10 @@ static struct i915_ppgtt *gen8_ppgtt_create(struct drm_i915_private *i915)
 
 		ppgtt->vm.allocate_va_range = gen8_ppgtt_alloc_3lvl;
 		ppgtt->vm.insert_entries = gen8_ppgtt_insert_3lvl;
-		ppgtt->vm.clear_range = gen8_ppgtt_clear_3lvl;
 	}
 
+	ppgtt->vm.clear_range = gen8_ppgtt_clear;
+
 	if (intel_vgpu_active(i915))
 		gen8_ppgtt_notify_vgt(ppgtt, true);
 
-- 
2.20.1



More information about the Intel-gfx-trybot mailing list