amdgpu_trap_alloc() first acquires reservation_ww_class_mutex via
drm_exec, and then calls amdgpu_bo_create_kernel() while still holding
that lock. amdgpu_bo_create_kernel() internally calls
amdgpu_bo_create_reserved() which tries to acquire the same
reservation_ww_class_mutex again — causing a recursive lock and
triggering a lockdep deadlock warning on Navi44:
WARNING: possible recursive locking detected
6.19.0-kfd-root #38 Not tainted
--------------------------------------------
kworker/0:2/959 is trying to acquire lock:
ffff8bb17995d9e8 (reservation_ww_class_mutex){+.+.}-{4:4}, at:
amdgpu_bo_create_reserved+0xd2/0x2a0 [amdgpu]
but task is already holding lock:
ffffcc23c3397ba0 (reservation_ww_class_mutex){+.+.}-{4:4}, at:
amdgpu_trap_alloc+0xac/0x230 [amdgpu]
Possible unsafe locking scenario:
CPU0
----
lock(reservation_ww_class_mutex);
lock(reservation_ww_class_mutex);
*** DEADLOCK ***
Call Trace:
amdgpu_bo_create_kernel+0x1a/0x80 [amdgpu]
amdgpu_trap_alloc+0x18d/0x230 [amdgpu]
amdgpu_driver_open_kms+0x2f1/0x390 [amdgpu]
drm_file_alloc+0x20a/0x2d0 [drm]
drm_client_init+0x75/0x100 [drm]
amdgpu_amdkfd_drm_client_create+0x53/0x90 [amdgpu]
amdgpu_pci_probe+0x463/0x630 [amdgpu]
Fix this by creating the TMA buffer object before drm_exec is
initialized, so the lock is not held during BO creation. Then lock
the TMA BO together with the TBA BO inside the drm_exec section as
required for the subsequent map operations.
v2 (Christian):
- Use amdgpu_bo_create() instead of amdgpu_bo_create_kernel() for the
per-VM TMA BO; eviction is handled by the VM's BO list via
amdgpu_vm_bo_add(), no pinning needed.
Fixes: c50a45b2e98d ("drm/amdgpu: Add cwsr functions")
Cc: Lijo Lazar <[email protected]>
Cc: Christian König <[email protected]>
Cc: Alex Deucher <[email protected]>
Signed-off-by: Srinivasan Shanmugam <[email protected]>
---
drivers/gpu/drm/amd/amdgpu/amdgpu_trap.c | 74 ++++++++++++++++++------
1 file changed, 56 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_trap.c
b/drivers/gpu/drm/amd/amdgpu/amdgpu_trap.c
index 31f653ec3fb1..d2ef8e2d108d 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_trap.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_trap.c
@@ -377,6 +377,7 @@ static int amdgpu_trap_unmap_region(struct amdgpu_device
*adev,
int amdgpu_trap_alloc(struct amdgpu_device *adev, struct amdgpu_vm *vm,
struct amdgpu_trap_obj **trap_obj)
{
+ struct amdgpu_bo_param bp;
struct amdgpu_trap_obj *cwsr;
struct amdgpu_bo *bo;
struct drm_exec exec;
@@ -390,46 +391,81 @@ int amdgpu_trap_alloc(struct amdgpu_device *adev, struct
amdgpu_vm *vm,
if (!cwsr)
return -ENOMEM;
+ /*
+ * Create TMA BO as an unpinned kernel BO. amdgpu_bo_create_kernel()
+ * is for pinned FW BOs only — it internally calls amdgpu_bo_reserve()
+ * which acquires reservation_ww_class_mutex and conflicts with
drm_exec.
+ * Use amdgpu_bo_create() instead, similar to VM page table allocation
+ * in amdgpu_vm_pt.c. The BO is added to the VM's BO list via
+ * amdgpu_vm_bo_add() in amdgpu_trap_map_region(), which ensures it is
+ * validated before GPU access — no pinning needed.
+ */
+ memset(&bp, 0, sizeof(bp));
+ bp.size = AMDGPU_TRAP_TMA_MAX_SIZE;
+ bp.byte_align = PAGE_SIZE;
+ bp.domain = AMDGPU_GEM_DOMAIN_GTT;
+ bp.flags = AMDGPU_GEM_CREATE_CPU_GTT_USWC |
+ AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED;
+ bp.type = ttm_bo_type_kernel;
+ bp.bo_ptr_size = sizeof(struct amdgpu_bo);
+
+ r = amdgpu_bo_create(adev, &bp, &cwsr->tma_bo);
+ if (r)
+ goto err;
+
bo = adev->trap_info->isa_bo;
drm_exec_init(&exec, DRM_EXEC_INTERRUPTIBLE_WAIT, 0);
drm_exec_until_all_locked(&exec) {
r = amdgpu_vm_lock_pd(vm, &exec, 0);
if (likely(!r))
r = drm_exec_lock_obj(&exec, &bo->tbo.base);
+ if (likely(!r))
+ r = drm_exec_lock_obj(&exec, &cwsr->tma_bo->tbo.base);
drm_exec_retry_on_contention(&exec);
if (unlikely(r)) {
dev_err(adev->dev,
"failed to reserve for CWSR allocs: err=%d\n",
r);
- goto err;
+ goto err_exec;
}
}
- r = amdgpu_bo_create_kernel(adev, AMDGPU_TRAP_TMA_MAX_SIZE, PAGE_SIZE,
- AMDGPU_GEM_DOMAIN_GTT, &cwsr->tma_bo, NULL,
- &cwsr->tma_cpu_addr);
- if (r)
- goto err;
-
r = amdgpu_trap_map_region(adev, vm, cwsr, AMDGPU_TRAP_TMA);
if (r)
- goto err;
+ goto err_exec;
r = amdgpu_trap_map_region(adev, vm, cwsr, AMDGPU_TRAP_TBA);
if (r) {
amdgpu_trap_unmap_region(adev, cwsr, AMDGPU_TRAP_TMA);
- goto err;
+ goto err_exec;
}
-err:
+err_exec:
drm_exec_fini(&exec);
- if (r) {
- amdgpu_bo_free_kernel(&cwsr->tma_bo, NULL, NULL);
- kfree(cwsr);
- *trap_obj = NULL;
- } else {
- *trap_obj = cwsr;
- }
+ if (r)
+ goto err_bo;
+
+ /* Get CPU mapping and zero TMA memory */
+ r = amdgpu_bo_reserve(cwsr->tma_bo, false);
+ if (r)
+ goto err_unmap;
+ r = amdgpu_bo_kmap(cwsr->tma_bo, &cwsr->tma_cpu_addr);
+ amdgpu_bo_unreserve(cwsr->tma_bo);
+ if (r)
+ goto err_unmap;
+
+ memset(cwsr->tma_cpu_addr, 0, AMDGPU_TRAP_TMA_MAX_SIZE);
+
+ *trap_obj = cwsr;
+ return 0;
+err_unmap:
+ amdgpu_trap_unmap_region(adev, cwsr, AMDGPU_TRAP_TBA);
+ amdgpu_trap_unmap_region(adev, cwsr, AMDGPU_TRAP_TMA);
+err_bo:
+ amdgpu_bo_unref(&cwsr->tma_bo);
+err:
+ kfree(cwsr);
+ *trap_obj = NULL;
return r;
}
@@ -500,7 +536,9 @@ void amdgpu_trap_free(struct amdgpu_device *adev, struct
amdgpu_vm *vm,
amdgpu_trap_unmap_region(adev, *trap_obj, AMDGPU_TRAP_TMA);
err:
drm_exec_fini(&exec);
- amdgpu_bo_free_kernel(&(*trap_obj)->tma_bo, NULL, NULL);
+ /* TMA BO not pinned — release CPU mapping and free */
+ amdgpu_bo_kunmap((*trap_obj)->tma_bo);
+ amdgpu_bo_unref(&(*trap_obj)->tma_bo);
kfree(*trap_obj);
*trap_obj = NULL;
}
--
2.34.1