DF-4413 / fix.diff
diff --git a/sys/dev/drm/radeon/radeon_vm.c b/sys/dev/drm/radeon/radeon_vm.c --- a/sys/dev/drm/radeon/radeon_vm.c +++ b/sys/dev/drm/radeon/radeon_vm.c @@ -548,11 +548,30 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev, /* drop mutex to allocate and clear page table */ mutex_unlock(&vm->mutex); r = radeon_bo_create(rdev, RADEON_VM_PTE_COUNT * 8, RADEON_GPU_PAGE_SIZE, true, RADEON_GEM_DOMAIN_VRAM, 0, NULL, NULL, &pt); if (r) - return r; + goto error_rollback; r = radeon_vm_clear_bo(rdev, pt); if (r) { radeon_bo_unref(&pt); - return r; + goto error_rollback; } @@ -575,6 +594,21 @@ int radeon_vm_bo_set_addr(struct radeon_device *rdev, mutex_unlock(&vm->mutex); return 0; +error_rollback: + /* + * The mapping was committed to vm->va / vm->cleared BEFORE the + * page-table allocation loop. Leaving it there with NULL page + * tables makes the next radeon_vm_bo_update() walk dereference + * vm->page_tables[pt_idx].bo == NULL in radeon_vm_update_ptes() + * and panics the kernel. Take the mapping back out instead. + * vm->mutex is NOT held here (dropped above), so re-acquire it. + */ + mutex_lock(&vm->mutex); + if (bo_va->it.start || bo_va->it.last) { + interval_tree_remove(&bo_va->it, &vm->va); + lockmgr(&vm->status_lock, LK_EXCLUSIVE); + bo_va->it.start = 0; + bo_va->it.last = 0; + list_del_init(&bo_va->vm_status); + lockmgr(&vm->status_lock, LK_RELEASE); + } + mutex_unlock(&vm->mutex); + return r; + error_unreserve: radeon_bo_unreserve(bo_va->bo); return r; |