]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
drm/amdgpu: add graceful VM fault handling v3
[linux.git] / drivers / gpu / drm / amd / amdgpu / amdgpu_vm.c
index 24c3c05e2fb7d70121b03f7ec111de018c67e96a..8327469f5722116dce821e463b5a08a9f4ee2ff4 100644 (file)
@@ -302,7 +302,7 @@ static void amdgpu_vm_bo_base_init(struct amdgpu_vm_bo_base *base,
        base->next = bo->vm_bo;
        bo->vm_bo = base;
 
-       if (bo->tbo.resv != vm->root.base.bo->tbo.resv)
+       if (bo->tbo.base.resv != vm->root.base.bo->tbo.base.resv)
                return;
 
        vm->bulk_moveable = false;
@@ -583,7 +583,7 @@ void amdgpu_vm_del_from_lru_notify(struct ttm_buffer_object *bo)
        for (bo_base = abo->vm_bo; bo_base; bo_base = bo_base->next) {
                struct amdgpu_vm *vm = bo_base->vm;
 
-               if (abo->tbo.resv == vm->root.base.bo->tbo.resv)
+               if (abo->tbo.base.resv == vm->root.base.bo->tbo.base.resv)
                        vm->bulk_moveable = false;
        }
 
@@ -695,6 +695,7 @@ bool amdgpu_vm_ready(struct amdgpu_vm *vm)
  * @adev: amdgpu_device pointer
  * @vm: VM to clear BO from
  * @bo: BO to clear
+ * @direct: use a direct update
  *
  * Root PD needs to be reserved when calling this.
  *
@@ -703,7 +704,8 @@ bool amdgpu_vm_ready(struct amdgpu_vm *vm)
  */
 static int amdgpu_vm_clear_bo(struct amdgpu_device *adev,
                              struct amdgpu_vm *vm,
-                             struct amdgpu_bo *bo)
+                             struct amdgpu_bo *bo,
+                             bool direct)
 {
        struct ttm_operation_ctx ctx = { true, false };
        unsigned level = adev->vm_manager.root_level;
@@ -762,6 +764,7 @@ static int amdgpu_vm_clear_bo(struct amdgpu_device *adev,
        memset(&params, 0, sizeof(params));
        params.adev = adev;
        params.vm = vm;
+       params.direct = direct;
 
        r = vm->update_funcs->prepare(&params, AMDGPU_FENCE_OWNER_KFD, NULL);
        if (r)
@@ -818,7 +821,8 @@ static int amdgpu_vm_clear_bo(struct amdgpu_device *adev,
  * @bp: resulting BO allocation parameters
  */
 static void amdgpu_vm_bo_param(struct amdgpu_device *adev, struct amdgpu_vm *vm,
-                              int level, struct amdgpu_bo_param *bp)
+                              int level, bool direct,
+                              struct amdgpu_bo_param *bp)
 {
        memset(bp, 0, sizeof(*bp));
 
@@ -833,8 +837,9 @@ static void amdgpu_vm_bo_param(struct amdgpu_device *adev, struct amdgpu_vm *vm,
        else if (!vm->root.base.bo || vm->root.base.bo->shadow)
                bp->flags |= AMDGPU_GEM_CREATE_SHADOW;
        bp->type = ttm_bo_type_kernel;
+       bp->no_wait_gpu = direct;
        if (vm->root.base.bo)
-               bp->resv = vm->root.base.bo->tbo.resv;
+               bp->resv = vm->root.base.bo->tbo.base.resv;
 }
 
 /**
@@ -852,7 +857,8 @@ static void amdgpu_vm_bo_param(struct amdgpu_device *adev, struct amdgpu_vm *vm,
  */
 static int amdgpu_vm_alloc_pts(struct amdgpu_device *adev,
                               struct amdgpu_vm *vm,
-                              struct amdgpu_vm_pt_cursor *cursor)
+                              struct amdgpu_vm_pt_cursor *cursor,
+                              bool direct)
 {
        struct amdgpu_vm_pt *entry = cursor->entry;
        struct amdgpu_bo_param bp;
@@ -873,7 +879,7 @@ static int amdgpu_vm_alloc_pts(struct amdgpu_device *adev,
        if (entry->base.bo)
                return 0;
 
-       amdgpu_vm_bo_param(adev, vm, cursor->level, &bp);
+       amdgpu_vm_bo_param(adev, vm, cursor->level, direct, &bp);
 
        r = amdgpu_bo_create(adev, &bp, &pt);
        if (r)
@@ -885,7 +891,7 @@ static int amdgpu_vm_alloc_pts(struct amdgpu_device *adev,
        pt->parent = amdgpu_bo_ref(cursor->parent->base.bo);
        amdgpu_vm_bo_base_init(&entry->base, vm, pt);
 
-       r = amdgpu_vm_clear_bo(adev, vm, pt);
+       r = amdgpu_vm_clear_bo(adev, vm, pt, direct);
        if (r)
                goto error_free_pt;
 
@@ -1036,10 +1042,8 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, bool need_
                id->oa_base != job->oa_base ||
                id->oa_size != job->oa_size);
        bool vm_flush_needed = job->vm_needs_flush;
-       bool pasid_mapping_needed = id->pasid != job->pasid ||
-               !id->pasid_mapping ||
-               !dma_fence_is_signaled(id->pasid_mapping);
        struct dma_fence *fence = NULL;
+       bool pasid_mapping_needed;
        unsigned patch_offset = 0;
        int r;
 
@@ -1049,6 +1053,12 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, bool need_
                pasid_mapping_needed = true;
        }
 
+       mutex_lock(&id_mgr->lock);
+       if (id->pasid != job->pasid || !id->pasid_mapping ||
+           !dma_fence_is_signaled(id->pasid_mapping))
+               pasid_mapping_needed = true;
+       mutex_unlock(&id_mgr->lock);
+
        gds_switch_needed &= !!ring->funcs->emit_gds_switch;
        vm_flush_needed &= !!ring->funcs->emit_vm_flush  &&
                        job->vm_pd_addr != AMDGPU_BO_INVALID_OFFSET;
@@ -1088,9 +1098,11 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, bool need_
        }
 
        if (pasid_mapping_needed) {
+               mutex_lock(&id_mgr->lock);
                id->pasid = job->pasid;
                dma_fence_put(id->pasid_mapping);
                id->pasid_mapping = dma_fence_get(fence);
+               mutex_unlock(&id_mgr->lock);
        }
        dma_fence_put(fence);
 
@@ -1221,18 +1233,19 @@ static void amdgpu_vm_invalidate_pds(struct amdgpu_device *adev,
 }
 
 /*
- * amdgpu_vm_update_directories - make sure that all directories are valid
+ * amdgpu_vm_update_pdes - make sure that all directories are valid
  *
  * @adev: amdgpu_device pointer
  * @vm: requested vm
+ * @direct: submit directly to the paging queue
  *
  * Makes sure all directories are up to date.
  *
  * Returns:
  * 0 for success, error for failure.
  */
-int amdgpu_vm_update_directories(struct amdgpu_device *adev,
-                                struct amdgpu_vm *vm)
+int amdgpu_vm_update_pdes(struct amdgpu_device *adev,
+                         struct amdgpu_vm *vm, bool direct)
 {
        struct amdgpu_vm_update_params params;
        int r;
@@ -1243,6 +1256,7 @@ int amdgpu_vm_update_directories(struct amdgpu_device *adev,
        memset(&params, 0, sizeof(params));
        params.adev = adev;
        params.vm = vm;
+       params.direct = direct;
 
        r = vm->update_funcs->prepare(&params, AMDGPU_FENCE_OWNER_VM, NULL);
        if (r)
@@ -1393,7 +1407,8 @@ static int amdgpu_vm_update_ptes(struct amdgpu_vm_update_params *params,
                uint64_t incr, entry_end, pe_start;
                struct amdgpu_bo *pt;
 
-               r = amdgpu_vm_alloc_pts(params->adev, params->vm, &cursor);
+               r = amdgpu_vm_alloc_pts(params->adev, params->vm, &cursor,
+                                       params->direct);
                if (r)
                        return r;
 
@@ -1484,13 +1499,14 @@ static int amdgpu_vm_update_ptes(struct amdgpu_vm_update_params *params,
  * amdgpu_vm_bo_update_mapping - update a mapping in the vm page table
  *
  * @adev: amdgpu_device pointer
- * @exclusive: fence we need to sync to
- * @pages_addr: DMA addresses to use for mapping
  * @vm: requested vm
+ * @direct: direct submission in a page fault
+ * @exclusive: fence we need to sync to
  * @start: start of mapped range
  * @last: last mapped entry
  * @flags: flags for the entries
  * @addr: addr to set the area to
+ * @pages_addr: DMA addresses to use for mapping
  * @fence: optional resulting fence
  *
  * Fill in the page table entries between @start and @last.
@@ -1499,11 +1515,11 @@ static int amdgpu_vm_update_ptes(struct amdgpu_vm_update_params *params,
  * 0 for success, -EINVAL for failure.
  */
 static int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev,
+                                      struct amdgpu_vm *vm, bool direct,
                                       struct dma_fence *exclusive,
-                                      dma_addr_t *pages_addr,
-                                      struct amdgpu_vm *vm,
                                       uint64_t start, uint64_t last,
                                       uint64_t flags, uint64_t addr,
+                                      dma_addr_t *pages_addr,
                                       struct dma_fence **fence)
 {
        struct amdgpu_vm_update_params params;
@@ -1513,6 +1529,7 @@ static int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev,
        memset(&params, 0, sizeof(params));
        params.adev = adev;
        params.vm = vm;
+       params.direct = direct;
        params.pages_addr = pages_addr;
 
        /* sync to everything except eviction fences on unmapping */
@@ -1571,27 +1588,8 @@ static int amdgpu_vm_bo_split_mapping(struct amdgpu_device *adev,
        if (!(mapping->flags & AMDGPU_PTE_WRITEABLE))
                flags &= ~AMDGPU_PTE_WRITEABLE;
 
-       flags &= ~AMDGPU_PTE_EXECUTABLE;
-       flags |= mapping->flags & AMDGPU_PTE_EXECUTABLE;
-
-       if (adev->asic_type == CHIP_NAVI10) {
-               flags &= ~AMDGPU_PTE_MTYPE_NV10_MASK;
-               flags |= (mapping->flags & AMDGPU_PTE_MTYPE_NV10_MASK);
-       } else {
-               flags &= ~AMDGPU_PTE_MTYPE_VG10_MASK;
-               flags |= (mapping->flags & AMDGPU_PTE_MTYPE_VG10_MASK);
-       }
-
-       if ((mapping->flags & AMDGPU_PTE_PRT) &&
-           (adev->asic_type >= CHIP_VEGA10)) {
-               flags |= AMDGPU_PTE_PRT;
-               if (adev->asic_type >= CHIP_NAVI10) {
-                       flags |= AMDGPU_PTE_SNOOPED;
-                       flags |= AMDGPU_PTE_LOG;
-                       flags |= AMDGPU_PTE_SYSTEM;
-               }
-               flags &= ~AMDGPU_PTE_VALID;
-       }
+       /* Apply ASIC specific mapping flags */
+       amdgpu_gmc_get_vm_pte(adev, mapping, &flags);
 
        trace_amdgpu_vm_bo_update(mapping);
 
@@ -1644,9 +1642,9 @@ static int amdgpu_vm_bo_split_mapping(struct amdgpu_device *adev,
                }
 
                last = min((uint64_t)mapping->last, start + max_entries - 1);
-               r = amdgpu_vm_bo_update_mapping(adev, exclusive, dma_addr, vm,
+               r = amdgpu_vm_bo_update_mapping(adev, vm, false, exclusive,
                                                start, last, flags, addr,
-                                               fence);
+                                               dma_addr, fence);
                if (r)
                        return r;
 
@@ -1702,7 +1700,7 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev,
                        ttm = container_of(bo->tbo.ttm, struct ttm_dma_tt, ttm);
                        pages_addr = ttm->dma_address;
                }
-               exclusive = reservation_object_get_excl(bo->tbo.resv);
+               exclusive = bo->tbo.moving;
        }
 
        if (bo) {
@@ -1712,7 +1710,7 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev,
                flags = 0x0;
        }
 
-       if (clear || (bo && bo->tbo.resv == vm->root.base.bo->tbo.resv))
+       if (clear || (bo && bo->tbo.base.resv == vm->root.base.bo->tbo.base.resv))
                last_update = &vm->last_update;
        else
                last_update = &bo_va->last_pt_update;
@@ -1743,7 +1741,7 @@ int amdgpu_vm_bo_update(struct amdgpu_device *adev,
         * the evicted list so that it gets validated again on the
         * next command submission.
         */
-       if (bo && bo->tbo.resv == vm->root.base.bo->tbo.resv) {
+       if (bo && bo->tbo.base.resv == vm->root.base.bo->tbo.base.resv) {
                uint32_t mem_type = bo->tbo.mem.mem_type;
 
                if (!(bo->preferred_domains & amdgpu_mem_type_to_domain(mem_type)))
@@ -1879,18 +1877,18 @@ static void amdgpu_vm_free_mapping(struct amdgpu_device *adev,
  */
 static void amdgpu_vm_prt_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)
 {
-       struct reservation_object *resv = vm->root.base.bo->tbo.resv;
+       struct dma_resv *resv = vm->root.base.bo->tbo.base.resv;
        struct dma_fence *excl, **shared;
        unsigned i, shared_count;
        int r;
 
-       r = reservation_object_get_fences_rcu(resv, &excl,
+       r = dma_resv_get_fences_rcu(resv, &excl,
                                              &shared_count, &shared);
        if (r) {
                /* Not enough memory to grab the fence list, as last resort
                 * block for all the fences to complete.
                 */
-               reservation_object_wait_timeout_rcu(resv, true, false,
+               dma_resv_wait_timeout_rcu(resv, true, false,
                                                    MAX_SCHEDULE_TIMEOUT);
                return;
        }
@@ -1940,9 +1938,9 @@ int amdgpu_vm_clear_freed(struct amdgpu_device *adev,
                    mapping->start < AMDGPU_GMC_HOLE_START)
                        init_pte_value = AMDGPU_PTE_DEFAULT_ATC;
 
-               r = amdgpu_vm_bo_update_mapping(adev, NULL, NULL, vm,
+               r = amdgpu_vm_bo_update_mapping(adev, vm, false, NULL,
                                                mapping->start, mapping->last,
-                                               init_pte_value, 0, &f);
+                                               init_pte_value, 0, NULL, &f);
                amdgpu_vm_free_mapping(adev, vm, mapping, f);
                if (r) {
                        dma_fence_put(f);
@@ -1978,7 +1976,7 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev,
                           struct amdgpu_vm *vm)
 {
        struct amdgpu_bo_va *bo_va, *tmp;
-       struct reservation_object *resv;
+       struct dma_resv *resv;
        bool clear;
        int r;
 
@@ -1993,11 +1991,11 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev,
        while (!list_empty(&vm->invalidated)) {
                bo_va = list_first_entry(&vm->invalidated, struct amdgpu_bo_va,
                                         base.vm_status);
-               resv = bo_va->base.bo->tbo.resv;
+               resv = bo_va->base.bo->tbo.base.resv;
                spin_unlock(&vm->invalidated_lock);
 
                /* Try to reserve the BO to avoid clearing its ptes */
-               if (!amdgpu_vm_debug && reservation_object_trylock(resv))
+               if (!amdgpu_vm_debug && dma_resv_trylock(resv))
                        clear = false;
                /* Somebody else is using the BO right now */
                else
@@ -2008,7 +2006,7 @@ int amdgpu_vm_handle_moved(struct amdgpu_device *adev,
                        return r;
 
                if (!clear)
-                       reservation_object_unlock(resv);
+                       dma_resv_unlock(resv);
                spin_lock(&vm->invalidated_lock);
        }
        spin_unlock(&vm->invalidated_lock);
@@ -2084,7 +2082,7 @@ static void amdgpu_vm_bo_insert_map(struct amdgpu_device *adev,
        if (mapping->flags & AMDGPU_PTE_PRT)
                amdgpu_vm_prt_get(adev);
 
-       if (bo && bo->tbo.resv == vm->root.base.bo->tbo.resv &&
+       if (bo && bo->tbo.base.resv == vm->root.base.bo->tbo.base.resv &&
            !bo_va->base.moved) {
                list_move(&bo_va->base.vm_status, &vm->moved);
        }
@@ -2416,7 +2414,8 @@ void amdgpu_vm_bo_trace_cs(struct amdgpu_vm *vm, struct ww_acquire_ctx *ticket)
                        struct amdgpu_bo *bo;
 
                        bo = mapping->bo_va->base.bo;
-                       if (READ_ONCE(bo->tbo.resv->lock.ctx) != ticket)
+                       if (dma_resv_locking_ctx(bo->tbo.base.resv) !=
+                           ticket)
                                continue;
                }
 
@@ -2443,7 +2442,7 @@ void amdgpu_vm_bo_rmv(struct amdgpu_device *adev,
        struct amdgpu_vm_bo_base **base;
 
        if (bo) {
-               if (bo->tbo.resv == vm->root.base.bo->tbo.resv)
+               if (bo->tbo.base.resv == vm->root.base.bo->tbo.base.resv)
                        vm->bulk_moveable = false;
 
                for (base = &bo_va->base.bo->vm_bo; *base;
@@ -2507,7 +2506,7 @@ void amdgpu_vm_bo_invalidate(struct amdgpu_device *adev,
        for (bo_base = bo->vm_bo; bo_base; bo_base = bo_base->next) {
                struct amdgpu_vm *vm = bo_base->vm;
 
-               if (evicted && bo->tbo.resv == vm->root.base.bo->tbo.resv) {
+               if (evicted && bo->tbo.base.resv == vm->root.base.bo->tbo.base.resv) {
                        amdgpu_vm_bo_evicted(bo_base);
                        continue;
                }
@@ -2518,7 +2517,7 @@ void amdgpu_vm_bo_invalidate(struct amdgpu_device *adev,
 
                if (bo->tbo.type == ttm_bo_type_kernel)
                        amdgpu_vm_bo_relocated(bo_base);
-               else if (bo->tbo.resv == vm->root.base.bo->tbo.resv)
+               else if (bo->tbo.base.resv == vm->root.base.bo->tbo.base.resv)
                        amdgpu_vm_bo_moved(bo_base);
                else
                        amdgpu_vm_bo_invalidated(bo_base);
@@ -2648,7 +2647,7 @@ void amdgpu_vm_adjust_size(struct amdgpu_device *adev, uint32_t min_vm_size,
  */
 long amdgpu_vm_wait_idle(struct amdgpu_vm *vm, long timeout)
 {
-       return reservation_object_wait_timeout_rcu(vm->root.base.bo->tbo.resv,
+       return dma_resv_wait_timeout_rcu(vm->root.base.bo->tbo.base.resv,
                                                   true, true, timeout);
 }
 
@@ -2683,12 +2682,17 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm,
        spin_lock_init(&vm->invalidated_lock);
        INIT_LIST_HEAD(&vm->freed);
 
-       /* create scheduler entity for page table updates */
-       r = drm_sched_entity_init(&vm->entity, adev->vm_manager.vm_pte_rqs,
+       /* create scheduler entities for page table updates */
+       r = drm_sched_entity_init(&vm->direct, adev->vm_manager.vm_pte_rqs,
                                  adev->vm_manager.vm_pte_num_rqs, NULL);
        if (r)
                return r;
 
+       r = drm_sched_entity_init(&vm->delayed, adev->vm_manager.vm_pte_rqs,
+                                 adev->vm_manager.vm_pte_num_rqs, NULL);
+       if (r)
+               goto error_free_direct;
+
        vm->pte_support_ats = false;
 
        if (vm_context == AMDGPU_VM_CONTEXT_COMPUTE) {
@@ -2712,24 +2716,24 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm,
                vm->update_funcs = &amdgpu_vm_sdma_funcs;
        vm->last_update = NULL;
 
-       amdgpu_vm_bo_param(adev, vm, adev->vm_manager.root_level, &bp);
+       amdgpu_vm_bo_param(adev, vm, adev->vm_manager.root_level, false, &bp);
        if (vm_context == AMDGPU_VM_CONTEXT_COMPUTE)
                bp.flags &= ~AMDGPU_GEM_CREATE_SHADOW;
        r = amdgpu_bo_create(adev, &bp, &root);
        if (r)
-               goto error_free_sched_entity;
+               goto error_free_delayed;
 
        r = amdgpu_bo_reserve(root, true);
        if (r)
                goto error_free_root;
 
-       r = reservation_object_reserve_shared(root->tbo.resv, 1);
+       r = dma_resv_reserve_shared(root->tbo.base.resv, 1);
        if (r)
                goto error_unreserve;
 
        amdgpu_vm_bo_base_init(&vm->root.base, vm, root);
 
-       r = amdgpu_vm_clear_bo(adev, vm, root);
+       r = amdgpu_vm_clear_bo(adev, vm, root, false);
        if (r)
                goto error_unreserve;
 
@@ -2760,8 +2764,11 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm,
        amdgpu_bo_unref(&vm->root.base.bo);
        vm->root.base.bo = NULL;
 
-error_free_sched_entity:
-       drm_sched_entity_destroy(&vm->entity);
+error_free_delayed:
+       drm_sched_entity_destroy(&vm->delayed);
+
+error_free_direct:
+       drm_sched_entity_destroy(&vm->direct);
 
        return r;
 }
@@ -2849,7 +2856,7 @@ int amdgpu_vm_make_compute(struct amdgpu_device *adev, struct amdgpu_vm *vm, uns
         */
        if (pte_support_ats != vm->pte_support_ats) {
                vm->pte_support_ats = pte_support_ats;
-               r = amdgpu_vm_clear_bo(adev, vm, vm->root.base.bo);
+               r = amdgpu_vm_clear_bo(adev, vm, vm->root.base.bo, false);
                if (r)
                        goto free_idr;
        }
@@ -2862,6 +2869,13 @@ int amdgpu_vm_make_compute(struct amdgpu_device *adev, struct amdgpu_vm *vm, uns
        WARN_ONCE((vm->use_cpu_for_update && !amdgpu_gmc_vram_full_visible(&adev->gmc)),
                  "CPU update of VM recommended only for large BAR system\n");
 
+       if (vm->use_cpu_for_update)
+               vm->update_funcs = &amdgpu_vm_cpu_funcs;
+       else
+               vm->update_funcs = &amdgpu_vm_sdma_funcs;
+       dma_fence_put(vm->last_update);
+       vm->last_update = NULL;
+
        if (vm->pasid) {
                unsigned long flags;
 
@@ -2931,19 +2945,28 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)
        struct amdgpu_bo_va_mapping *mapping, *tmp;
        bool prt_fini_needed = !!adev->gmc.gmc_funcs->set_prt;
        struct amdgpu_bo *root;
-       int i, r;
+       int i;
 
        amdgpu_amdkfd_gpuvm_destroy_cb(adev, vm);
 
+       root = amdgpu_bo_ref(vm->root.base.bo);
+       amdgpu_bo_reserve(root, true);
        if (vm->pasid) {
                unsigned long flags;
 
                spin_lock_irqsave(&adev->vm_manager.pasid_lock, flags);
                idr_remove(&adev->vm_manager.pasid_idr, vm->pasid);
                spin_unlock_irqrestore(&adev->vm_manager.pasid_lock, flags);
+               vm->pasid = 0;
        }
 
-       drm_sched_entity_destroy(&vm->entity);
+       amdgpu_vm_free_pts(adev, vm, NULL);
+       amdgpu_bo_unreserve(root);
+       amdgpu_bo_unref(&root);
+       WARN_ON(vm->root.base.bo);
+
+       drm_sched_entity_destroy(&vm->direct);
+       drm_sched_entity_destroy(&vm->delayed);
 
        if (!RB_EMPTY_ROOT(&vm->va.rb_root)) {
                dev_err(adev->dev, "still active bo inside vm\n");
@@ -2966,16 +2989,6 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm)
                amdgpu_vm_free_mapping(adev, vm, mapping, NULL);
        }
 
-       root = amdgpu_bo_ref(vm->root.base.bo);
-       r = amdgpu_bo_reserve(root, true);
-       if (r) {
-               dev_err(adev->dev, "Leaking page tables because BO reservation failed\n");
-       } else {
-               amdgpu_vm_free_pts(adev, vm, NULL);
-               amdgpu_bo_unreserve(root);
-       }
-       amdgpu_bo_unref(&root);
-       WARN_ON(vm->root.base.bo);
        dma_fence_put(vm->last_update);
        for (i = 0; i < AMDGPU_MAX_VMHUBS; i++)
                amdgpu_vmid_free_reserved(adev, vm, i);
@@ -3060,12 +3073,12 @@ int amdgpu_vm_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
        switch (args->in.op) {
        case AMDGPU_VM_OP_RESERVE_VMID:
                /* current, we only have requirement to reserve vmid from gfxhub */
-               r = amdgpu_vmid_alloc_reserved(adev, &fpriv->vm, AMDGPU_GFXHUB);
+               r = amdgpu_vmid_alloc_reserved(adev, &fpriv->vm, AMDGPU_GFXHUB_0);
                if (r)
                        return r;
                break;
        case AMDGPU_VM_OP_UNRESERVE_VMID:
-               amdgpu_vmid_free_reserved(adev, &fpriv->vm, AMDGPU_GFXHUB);
+               amdgpu_vmid_free_reserved(adev, &fpriv->vm, AMDGPU_GFXHUB_0);
                break;
        default:
                return -EINVAL;
@@ -3113,3 +3126,76 @@ void amdgpu_vm_set_task_info(struct amdgpu_vm *vm)
                }
        }
 }
+
+/**
+ * amdgpu_vm_handle_fault - graceful handling of VM faults.
+ * @adev: amdgpu device pointer
+ * @pasid: PASID of the VM
+ * @addr: Address of the fault
+ *
+ * Try to gracefully handle a VM fault. Return true if the fault was handled and
+ * shouldn't be reported any more.
+ */
+bool amdgpu_vm_handle_fault(struct amdgpu_device *adev, unsigned int pasid,
+                           uint64_t addr)
+{
+       struct amdgpu_bo *root;
+       uint64_t value, flags;
+       struct amdgpu_vm *vm;
+       long r;
+
+       spin_lock(&adev->vm_manager.pasid_lock);
+       vm = idr_find(&adev->vm_manager.pasid_idr, pasid);
+       if (vm)
+               root = amdgpu_bo_ref(vm->root.base.bo);
+       else
+               root = NULL;
+       spin_unlock(&adev->vm_manager.pasid_lock);
+
+       if (!root)
+               return false;
+
+       r = amdgpu_bo_reserve(root, true);
+       if (r)
+               goto error_unref;
+
+       /* Double check that the VM still exists */
+       spin_lock(&adev->vm_manager.pasid_lock);
+       vm = idr_find(&adev->vm_manager.pasid_idr, pasid);
+       if (vm && vm->root.base.bo != root)
+               vm = NULL;
+       spin_unlock(&adev->vm_manager.pasid_lock);
+       if (!vm)
+               goto error_unlock;
+
+       addr /= AMDGPU_GPU_PAGE_SIZE;
+       flags = AMDGPU_PTE_VALID | AMDGPU_PTE_SNOOPED |
+               AMDGPU_PTE_SYSTEM;
+
+       if (amdgpu_vm_fault_stop == AMDGPU_VM_FAULT_STOP_NEVER) {
+               /* Redirect the access to the dummy page */
+               value = adev->dummy_page_addr;
+               flags |= AMDGPU_PTE_EXECUTABLE | AMDGPU_PTE_READABLE |
+                       AMDGPU_PTE_WRITEABLE;
+       } else {
+               /* Let the hw retry silently on the PTE */
+               value = 0;
+       }
+
+       r = amdgpu_vm_bo_update_mapping(adev, vm, true, NULL, addr, addr + 1,
+                                       flags, value, NULL, NULL);
+       if (r)
+               goto error_unlock;
+
+       r = amdgpu_vm_update_pdes(adev, vm, true);
+
+error_unlock:
+       amdgpu_bo_unreserve(root);
+       if (r < 0)
+               DRM_ERROR("Can't handle page fault (%ld)\n", r);
+
+error_unref:
+       amdgpu_bo_unref(&root);
+
+       return false;
+}