From faa24ca969392e19f650239b90123eee5b717d0d Mon Sep 17 00:00:00 2001
From: Harish Kasiviswanathan <Harish.Kasiviswanathan@amd.com>
Date: Wed, 19 Apr 2017 16:54:16 -0400
Subject: [PATCH 4/4] drm/amdgpu: Support page table update via CPU

Change-Id: I5fe1009de222e60c406b36d7a4271ac15d80e286
Signed-off-by: Harish Kasiviswanathan <Harish.Kasiviswanathan@amd.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 113 ++++++++++++++++++++++++++++++++-
 1 file changed, 110 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
index 44d6d34..e969c60 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
@@ -79,6 +79,12 @@ struct amdgpu_pte_update_params {
 		     uint64_t flags);
 	/* indicate update pt or its shadow */
 	bool shadow;
+	/* The next three are used during VM update by CPU */
+	bool update_by_cpu;
+	/* DMA addresses to use for mapping */
+	dma_addr_t *pages_addr;
+	/* Kernel pointer of PD/PT BO that needs to be updated */
+	void *kptr;
 };
 
 /* Helper to disable partial resident texture feature from a fence callback */
@@ -919,12 +925,14 @@ static uint64_t amdgpu_vm_map_gart(const dma_addr_t *pages_addr, uint64_t addr)
  * amdgpu_vm_cpu_set_ptes - helper to update page tables via CPU
  *
  * @params: see amdgpu_pte_update_params definition
- * @pe: addr of the page entry
+ * @pe: kmap addr of the page entry
  * @addr: dst addr to write into pe
  * @count: number of page entries to update
  * @incr: increase next addr by incr bytes
  * @flags: hw access flags
  *
+ * Used for updating System Memory not GART mapped to same device,
+ * VRAM memory and Page directory entries.
  */
 static void amdgpu_vm_cpu_set_ptes(struct amdgpu_pte_update_params *params,
 				   uint64_t pe, uint64_t addr,
@@ -932,12 +940,17 @@ static void amdgpu_vm_cpu_set_ptes(struct amdgpu_pte_update_params *params,
 				   uint64_t flags)
 {
 	unsigned int i;
+	uint64_t value;
 
 	for (i = 0; i < count; i++) {
+		value = params->pages_addr ?
+			amdgpu_vm_map_gart(params->pages_addr, addr) :
+			addr;
 		amdgpu_gart_set_pte_pde(params->adev, (void *)pe,
-					i, addr, flags);
+					i, value, flags);
 		addr += incr;
 	}
+
 	mb();
 	amdgpu_gart_flush_gpu_tlb(params->adev, 0);
 }
@@ -985,7 +998,8 @@ static int amdgpu_vm_update_level(struct amdgpu_device *adev,
 
 		r = amdgpu_bo_kmap(parent->bo, (void **)&pd_addr);
 		if (r)
-			dev_warn(adev->dev, "Page table update using CPU failed. Fallback to SDMA\n");
+			dev_warn(adev->dev,
+				 "Page table update using CPU failed. Fallback to SDMA\n");
 		else {
 			/* Wait for BO to be free. SDMA could be clearing it */
 			amdgpu_sync_create(&sync);
@@ -1180,6 +1194,59 @@ static struct amdgpu_bo *amdgpu_vm_get_pt(struct amdgpu_pte_update_params *p,
 }
 
 /**
+ * amdgpu_vm_update_ptes_cpu - Update the page tables in the range
+ *  start - @end using CPU.
+ * See amdgpu_vm_update_ptes for parameter description.
+ *
+ */
+static int amdgpu_vm_update_ptes_cpu(struct amdgpu_pte_update_params *params,
+				     uint64_t start, uint64_t end,
+				     uint64_t dst, uint64_t flags)
+{
+	struct amdgpu_device *adev = params->adev;
+	const uint64_t mask = AMDGPU_VM_PTE_COUNT(adev) - 1;
+	void *pe_ptr;
+	uint64_t addr;
+	struct amdgpu_bo *pt;
+	unsigned int nptes;
+	int r;
+
+	/* initialize the variables */
+	addr = start;
+
+	/* walk over the address space and update the page tables */
+	while (addr < end) {
+		pt = amdgpu_vm_get_pt(params, addr);
+		if (!pt) {
+			pr_err("PT not found, aborting update_ptes\n");
+			return -EINVAL;
+		}
+
+		WARN_ON(params->shadow);
+
+		r = amdgpu_bo_kmap(pt, &pe_ptr);
+		if (r)
+			return r;
+
+		pe_ptr += (addr & mask) * 8;
+
+		if ((addr & ~mask) == (end & ~mask))
+			nptes = end - addr;
+		else
+			nptes = AMDGPU_VM_PTE_COUNT(adev) - (addr & mask);
+
+		params->func(params, (uint64_t)pe_ptr, dst, nptes,
+			     AMDGPU_GPU_PAGE_SIZE, flags);
+
+		amdgpu_bo_kunmap(pt);
+		addr += nptes;
+		dst += nptes * AMDGPU_GPU_PAGE_SIZE;
+	}
+
+	return 0;
+}
+
+/**
  * amdgpu_vm_update_ptes - make sure that page tables are valid
  *
  * @params: see amdgpu_pte_update_params definition
@@ -1205,6 +1272,13 @@ static int amdgpu_vm_update_ptes(struct amdgpu_pte_update_params *params,
 	unsigned nptes; /* next number of ptes to be updated */
 	uint64_t next_pe_start;
 
+	if (params->update_by_cpu == true)
+		/* Return CPU based update status. If failed, retry using
+		 * SDMA will be done
+		 */
+		return amdgpu_vm_update_ptes_cpu(params, start, end,
+						  dst, flags);
+
 	/* initialize the variables */
 	addr = start;
 	pt = amdgpu_vm_get_pt(params, addr);
@@ -1391,6 +1465,39 @@ static int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev,
 	params.vm = vm;
 	params.src = src;
 
+	if (vm->is_vm_update_mode_cpu) {
+		struct amdgpu_sync sync;
+
+		amdgpu_sync_create(&sync);
+		amdgpu_sync_resv(adev, &sync, vm->root.bo->tbo.resv,
+				 AMDGPU_FENCE_OWNER_VM);
+		amdgpu_sync_wait(&sync);
+		amdgpu_sync_free(&sync);
+
+		params.func = amdgpu_vm_cpu_set_ptes;
+
+		/* params.src is used as flag to indicate system Memory */
+		if (pages_addr)
+			params.src = ~0;
+
+		params.pages_addr = pages_addr;
+		params.update_by_cpu = true;
+
+		params.shadow = false;
+		r = amdgpu_vm_frag_ptes(&params, start, last + 1, addr, flags);
+		if (!r)
+			return r;
+
+		dev_warn(adev->dev,
+			 "CPU update of VM failed. Fallback to SDMA\n");
+
+		/* Reset params for SDMA fallback path */
+		params.update_by_cpu = false;
+		params.pages_addr = NULL;
+		params.kptr = NULL;
+		params.func = NULL;
+	}
+
 	ring = container_of(vm->entity.sched, struct amdgpu_ring, sched);
 
 	/* sync to everything on unmapping */
-- 
1.9.1

