From e05a54573df7df3f3260d870d1d477eed8c181ba Mon Sep 17 00:00:00 2001 From: Felix Kuehling Date: Mon, 8 Jun 2026 14:54:20 -0400 Subject: [PATCH] drm/amdgpu: Pin page tables in NPA VMs With latest POR, remote importers aren't guaranteed to support instruction replay-on-fault. Therefore, we cannot support unpinned exports or unpinned page-tables in the exporter mappings into NPA space. Signed-off-by: Felix Kuehling Reviewed-by: Mukul Joshi Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c | 4 +-- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 40 ++++++++++++++++++++++ drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h | 3 ++ drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c | 25 +++++++++++++- 4 files changed, 69 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c index a3eb95a4911b..94d34ed60ae2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ualink.c @@ -3444,8 +3444,8 @@ int amdgpu_ualink_manager_start(struct amdgpu_device *adev) if (r) goto out; - /* For using CPU for page table updates. */ - r = amdgpu_vm_make_compute(adev, &adev->ualink.npa_vm); + /* For pinning page tables and using CPU for page table updates. */ + r = amdgpu_vm_make_npa(adev, &adev->ualink.npa_vm); if (r) goto uninit_vm; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 2cc18da28f12..36f6dcd063fe 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -2752,6 +2752,46 @@ unreserve_bo: return r; } +/** + * amdgpu_vm_make_npa - Turn a GFX VM into an NPA VM + * + * @adev: amdgpu_device pointer + * @vm: requested vm + * + * This only works on GFX VMs that don't have any BOs added and no + * page tables allocated yet. + * + * Changes the following VM parameters: + * - use_cpu_for_update + * - pins page tables + * - initializes PTEs to no-retry encoding + * + * Reinitializes the page directory to reflect the changed ATS + * setting. + * + * Returns: + * 0 for success, -errno for errors. + */ +int amdgpu_vm_make_npa(struct amdgpu_device *adev, struct amdgpu_vm *vm) +{ + int r = amdgpu_vm_make_compute(adev, vm); + + if (r) + return r; + vm->is_npa = true; + r = amdgpu_bo_reserve(vm->root.bo, false); + if (r) + return r; + r = amdgpu_bo_pin(vm->root.bo, AMDGPU_GEM_DOMAIN_VRAM); + amdgpu_bo_unreserve(vm->root.bo); + if (r) + return r; + + vm->is_npa = true; + + return 0; +} + static int amdgpu_vm_stats_is_zero(struct amdgpu_vm *vm) { for (int i = 0; i < __AMDGPU_PL_NUM; ++i) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index 7a0ee19a12b1..0f6634b74974 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -369,6 +369,8 @@ struct amdgpu_vm { struct ttm_lru_bulk_move lru_bulk_move; /* Flag to indicate if VM is used for compute */ bool is_compute_context; + /* Flag to indicate that page tables are for NPA mappings */ + bool is_npa; /* Flag to indicate if VM needs a TLB fence (KFD or KGD) */ bool need_tlb_fence; @@ -429,6 +431,7 @@ void amdgpu_vm_manager_fini(struct amdgpu_device *adev); long amdgpu_vm_wait_idle(struct amdgpu_vm *vm, long timeout); int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm, int32_t xcp_id); int amdgpu_vm_make_compute(struct amdgpu_device *adev, struct amdgpu_vm *vm); +int amdgpu_vm_make_npa(struct amdgpu_device *adev, struct amdgpu_vm *vm); void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm); int amdgpu_vm_lock_pd(struct amdgpu_vm *vm, struct drm_exec *exec, unsigned int num_fences); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c index b85b70a1a428..b9831a5f85d8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_pt.c @@ -445,6 +445,7 @@ int amdgpu_vm_pt_create(struct amdgpu_device *adev, struct amdgpu_vm *vm, { struct amdgpu_bo_param bp; unsigned int num_entries; + int r; memset(&bp, 0, sizeof(bp)); @@ -477,7 +478,24 @@ int amdgpu_vm_pt_create(struct amdgpu_device *adev, struct amdgpu_vm *vm, if (vm->root.bo) bp.resv = vm->root.bo->tbo.base.resv; - return amdgpu_bo_create_vm(adev, &bp, vmbo); + r = amdgpu_bo_create_vm(adev, &bp, vmbo); + if (r) + return r; + + /* Assumes that reservation is shared with the VM root and that the + * reservation is locked + */ + if (vm->root.bo && vm->is_npa) { + struct amdgpu_bo *pt_bo = &(*vmbo)->bo; + + r = amdgpu_bo_pin(pt_bo, AMDGPU_GEM_DOMAIN_VRAM); + if (r) { + amdgpu_bo_unref(&pt_bo); + return r; + } + } + + return 0; } /** @@ -527,6 +545,8 @@ static int amdgpu_vm_pt_alloc(struct amdgpu_device *adev, return 0; error_free_pt: + if (vm->is_npa) + amdgpu_bo_unpin(pt_bo); amdgpu_bo_unref(&pt_bo); return r; } @@ -541,6 +561,9 @@ static void amdgpu_vm_pt_free(struct amdgpu_vm_bo_base *entry) if (!entry->bo) return; + if (entry->vm->is_npa) + amdgpu_bo_unpin(entry->bo); + amdgpu_vm_update_stats(entry, entry->bo->tbo.resource, -1); entry->bo->vm_bo = NULL; ttm_bo_set_bulk_move(&entry->bo->tbo, NULL);