drm/amdkfd: avoid unmap dma address when svm_ranges are split
authorAlex Sierra <alex.sierra@amd.com>
Thu, 27 Jul 2023 20:32:37 +0000 (15:32 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 7 Aug 2023 20:36:44 +0000 (16:36 -0400)
DMA address reference within svm_ranges should be unmapped only after
the memory has been released from the system. In case of range
splitting, the DMA address information should be copied to the
corresponding range after this has split. But leaving dma mapping
intact.

Signed-off-by: Alex Sierra <alex.sierra@amd.com>
Reviewed-by: Felix Kuehling <Felix.Kuehling@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/amdkfd/kfd_migrate.c
drivers/gpu/drm/amd/amdkfd/kfd_svm.c
drivers/gpu/drm/amd/amdkfd/kfd_svm.h

index 709ac88..7d82c7d 100644 (file)
@@ -461,7 +461,6 @@ svm_migrate_vma_to_vram(struct kfd_node *node, struct svm_range *prange,
                                    0, node->id, trigger);
 
        svm_range_dma_unmap(adev->dev, scratch, 0, npages);
-       svm_range_free_dma_mappings(prange);
 
 out_free:
        kvfree(buf);
@@ -543,10 +542,12 @@ svm_migrate_ram_to_vram(struct svm_range *prange, uint32_t best_loc,
                addr = next;
        }
 
-       if (cpages)
+       if (cpages) {
                prange->actual_loc = best_loc;
-       else
+               svm_range_free_dma_mappings(prange, true);
+       } else {
                svm_range_vram_node_free(prange);
+       }
 
        return r < 0 ? r : 0;
 }
index 01c7de2..243ef74 100644 (file)
@@ -243,7 +243,7 @@ void svm_range_dma_unmap(struct device *dev, dma_addr_t *dma_addr,
        }
 }
 
-void svm_range_free_dma_mappings(struct svm_range *prange)
+void svm_range_free_dma_mappings(struct svm_range *prange, bool unmap_dma)
 {
        struct kfd_process_device *pdd;
        dma_addr_t *dma_addr;
@@ -264,13 +264,14 @@ void svm_range_free_dma_mappings(struct svm_range *prange)
                        continue;
                }
                dev = &pdd->dev->adev->pdev->dev;
-               svm_range_dma_unmap(dev, dma_addr, 0, prange->npages);
+               if (unmap_dma)
+                       svm_range_dma_unmap(dev, dma_addr, 0, prange->npages);
                kvfree(dma_addr);
                prange->dma_addr[gpuidx] = NULL;
        }
 }
 
-static void svm_range_free(struct svm_range *prange, bool update_mem_usage)
+static void svm_range_free(struct svm_range *prange, bool do_unmap)
 {
        uint64_t size = (prange->last - prange->start + 1) << PAGE_SHIFT;
        struct kfd_process *p = container_of(prange->svms, struct kfd_process, svms);
@@ -279,9 +280,9 @@ static void svm_range_free(struct svm_range *prange, bool update_mem_usage)
                 prange->start, prange->last);
 
        svm_range_vram_node_free(prange);
-       svm_range_free_dma_mappings(prange);
+       svm_range_free_dma_mappings(prange, do_unmap);
 
-       if (update_mem_usage && !p->xnack_enabled) {
+       if (do_unmap && !p->xnack_enabled) {
                pr_debug("unreserve prange 0x%p size: 0x%llx\n", prange, size);
                amdgpu_amdkfd_unreserve_mem_limit(NULL, size,
                                        KFD_IOC_ALLOC_MEM_FLAGS_USERPTR, 0);
@@ -853,6 +854,37 @@ static void svm_range_debug_dump(struct svm_range_list *svms)
        }
 }
 
+static void *
+svm_range_copy_array(void *psrc, size_t size, uint64_t num_elements,
+                    uint64_t offset)
+{
+       unsigned char *dst;
+
+       dst = kvmalloc_array(num_elements, size, GFP_KERNEL);
+       if (!dst)
+               return NULL;
+       memcpy(dst, (unsigned char *)psrc + offset, num_elements * size);
+
+       return (void *)dst;
+}
+
+static int
+svm_range_copy_dma_addrs(struct svm_range *dst, struct svm_range *src)
+{
+       int i;
+
+       for (i = 0; i < MAX_GPU_INSTANCE; i++) {
+               if (!src->dma_addr[i])
+                       continue;
+               dst->dma_addr[i] = svm_range_copy_array(src->dma_addr[i],
+                                       sizeof(*src->dma_addr[i]), src->npages, 0);
+               if (!dst->dma_addr[i])
+                       return -ENOMEM;
+       }
+
+       return 0;
+}
+
 static int
 svm_range_split_array(void *ppnew, void *ppold, size_t size,
                      uint64_t old_start, uint64_t old_n,
@@ -867,22 +899,16 @@ svm_range_split_array(void *ppnew, void *ppold, size_t size,
        if (!pold)
                return 0;
 
-       new = kvmalloc_array(new_n, size, GFP_KERNEL);
+       d = (new_start - old_start) * size;
+       new = svm_range_copy_array(pold, size, new_n, d);
        if (!new)
                return -ENOMEM;
-
-       d = (new_start - old_start) * size;
-       memcpy(new, pold + d, new_n * size);
-
-       old = kvmalloc_array(old_n, size, GFP_KERNEL);
+       d = (new_start == old_start) ? new_n * size : 0;
+       old = svm_range_copy_array(pold, size, old_n, d);
        if (!old) {
                kvfree(new);
                return -ENOMEM;
        }
-
-       d = (new_start == old_start) ? new_n * size : 0;
-       memcpy(old, pold + d, old_n * size);
-
        kvfree(pold);
        *(void **)ppold = old;
        *(void **)ppnew = new;
@@ -1928,7 +1954,10 @@ static struct svm_range *svm_range_clone(struct svm_range *old)
        new = svm_range_new(old->svms, old->start, old->last, false);
        if (!new)
                return NULL;
-
+       if (svm_range_copy_dma_addrs(new, old)) {
+               svm_range_free(new, false);
+               return NULL;
+       }
        if (old->svm_bo) {
                new->ttm_res = old->ttm_res;
                new->offset = old->offset;
index 21b1451..9e668ee 100644 (file)
@@ -183,7 +183,7 @@ void svm_range_add_list_work(struct svm_range_list *svms,
 void schedule_deferred_list_work(struct svm_range_list *svms);
 void svm_range_dma_unmap(struct device *dev, dma_addr_t *dma_addr,
                         unsigned long offset, unsigned long npages);
-void svm_range_free_dma_mappings(struct svm_range *prange);
+void svm_range_free_dma_mappings(struct svm_range *prange, bool unmap_dma);
 int svm_range_get_info(struct kfd_process *p, uint32_t *num_svm_ranges,
                       uint64_t *svm_priv_data_size);
 int kfd_criu_checkpoint_svm(struct kfd_process *p,