The individual.needs_update loop in amdgpu_vm_handle_moved() unconditionally dereferenced bo_va->base.bo (to read its reservation object, ttm_tt and to test for a DMABuf import). However bo_va->base.bo can legitimately be NULL for PRT/sparse mappings - amdgpu_vm_bo_update() already handles a NULL bo via its "else if (!bo)" PRT path. When such a bo_va reaches the moved list (e.g. the userq eviction restore worker running amdgpu_vm_handle_moved() while a user queue is being torn down during a GPU reset), the NULL deref crashes the kernel:
BUG: kernel NULL pointer dereference, address: 0000000000000158 #PF: supervisor read access in kernel mode Oops: 0000 [#1] SMP NOPTI Workqueue: events amdgpu_userq_restore_worker [amdgpu] RIP: 0010:amdgpu_vm_handle_moved+0x17a/0x200 [amdgpu] Call Trace: <TASK> amdgpu_userq_vm_validate_and_restore_queue+0x2ce/0x920 [amdgpu] amdgpu_userq_restore_worker+0xce/0x210 [amdgpu] process_scheduled_works+0xa6/0x460 worker_thread+0x13c/0x290 kthread+0xfb/0x140 ret_from_fork+0x1b6/0x2b0 ret_from_fork_asm+0x1a/0x30 </TASK> The faulting instruction is "mov rax,[rdx+0x158]" with rdx (bo) == 0 and CR2 == 0x158, i.e. reading bo->tbo.base.resv off a NULL bo. Guard the BO-less case: skip the reservation dance and the DMABuf-import check when bo is NULL, and let amdgpu_vm_bo_update() take its existing PRT path. v2: Use local bo pointer consistently after NULL guard (no functional change) (Bob) Signed-off-by: Jesse Zhang <[email protected]> --- drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index aac8ace9d7a6..d8552d9dbe5a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1631,11 +1631,17 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, bo_va = list_first_entry(&vm->individual.needs_update, typeof(*bo_va), base.vm_status); bo = bo_va->base.bo; - resv = bo->tbo.base.resv; + resv = bo ? bo->tbo.base.resv : NULL; spin_unlock(&vm->individual_lock); + /* PRT/sparse mappings have no BO to reserve; just update the + * page tables (amdgpu_vm_bo_update() handles a NULL bo). + */ + if (!bo) { + clear = false; + unlock = false; /* Try to reserve the BO to avoid clearing its ptes */ - if (!adev->debug_vm && !amdgpu_ttm_tt_get_usermm(bo->tbo.ttm) && + } else if (!adev->debug_vm && !amdgpu_ttm_tt_get_usermm(bo->tbo.ttm) && dma_resv_trylock(resv)) { clear = false; unlock = true; @@ -1659,10 +1665,10 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev, /* Remember evicted DMABuf imports in compute VMs for later * validation */ - if (vm->is_compute_context && - drm_gem_is_imported(&bo_va->base.bo->tbo.base) && - (!bo_va->base.bo->tbo.resource || - bo_va->base.bo->tbo.resource->mem_type == TTM_PL_SYSTEM)) + if (bo && vm->is_compute_context && + drm_gem_is_imported(&bo->tbo.base) && + (!bo->tbo.resource || + bo->tbo.resource->mem_type == TTM_PL_SYSTEM)) amdgpu_vm_bo_evicted(&bo_va->base); spin_lock(&vm->individual_lock); -- 2.49.0
