1d38ceaf9SAlex Deucher /* 2d38ceaf9SAlex Deucher * Copyright 2008 Advanced Micro Devices, Inc. 3d38ceaf9SAlex Deucher * Copyright 2008 Red Hat Inc. 4d38ceaf9SAlex Deucher * Copyright 2009 Jerome Glisse. 5d38ceaf9SAlex Deucher * 6d38ceaf9SAlex Deucher * Permission is hereby granted, free of charge, to any person obtaining a 7d38ceaf9SAlex Deucher * copy of this software and associated documentation files (the "Software"), 8d38ceaf9SAlex Deucher * to deal in the Software without restriction, including without limitation 9d38ceaf9SAlex Deucher * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10d38ceaf9SAlex Deucher * and/or sell copies of the Software, and to permit persons to whom the 11d38ceaf9SAlex Deucher * Software is furnished to do so, subject to the following conditions: 12d38ceaf9SAlex Deucher * 13d38ceaf9SAlex Deucher * The above copyright notice and this permission notice shall be included in 14d38ceaf9SAlex Deucher * all copies or substantial portions of the Software. 15d38ceaf9SAlex Deucher * 16d38ceaf9SAlex Deucher * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17d38ceaf9SAlex Deucher * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18d38ceaf9SAlex Deucher * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19d38ceaf9SAlex Deucher * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 20d38ceaf9SAlex Deucher * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21d38ceaf9SAlex Deucher * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22d38ceaf9SAlex Deucher * OTHER DEALINGS IN THE SOFTWARE. 23d38ceaf9SAlex Deucher * 24d38ceaf9SAlex Deucher * Authors: Dave Airlie 25d38ceaf9SAlex Deucher * Alex Deucher 26d38ceaf9SAlex Deucher * Jerome Glisse 27d38ceaf9SAlex Deucher */ 28d38ceaf9SAlex Deucher #include <drm/drmP.h> 29d38ceaf9SAlex Deucher #include <drm/amdgpu_drm.h> 30d38ceaf9SAlex Deucher #include "amdgpu.h" 31d38ceaf9SAlex Deucher #include "amdgpu_trace.h" 32d38ceaf9SAlex Deucher 33d38ceaf9SAlex Deucher /* 34d38ceaf9SAlex Deucher * GPUVM 35d38ceaf9SAlex Deucher * GPUVM is similar to the legacy gart on older asics, however 36d38ceaf9SAlex Deucher * rather than there being a single global gart table 37d38ceaf9SAlex Deucher * for the entire GPU, there are multiple VM page tables active 38d38ceaf9SAlex Deucher * at any given time. The VM page tables can contain a mix 39d38ceaf9SAlex Deucher * vram pages and system memory pages and system memory pages 40d38ceaf9SAlex Deucher * can be mapped as snooped (cached system pages) or unsnooped 41d38ceaf9SAlex Deucher * (uncached system pages). 42d38ceaf9SAlex Deucher * Each VM has an ID associated with it and there is a page table 43d38ceaf9SAlex Deucher * associated with each VMID. When execting a command buffer, 44d38ceaf9SAlex Deucher * the kernel tells the the ring what VMID to use for that command 45d38ceaf9SAlex Deucher * buffer. VMIDs are allocated dynamically as commands are submitted. 46d38ceaf9SAlex Deucher * The userspace drivers maintain their own address space and the kernel 47d38ceaf9SAlex Deucher * sets up their pages tables accordingly when they submit their 48d38ceaf9SAlex Deucher * command buffers and a VMID is assigned. 49d38ceaf9SAlex Deucher * Cayman/Trinity support up to 8 active VMs at any given time; 50d38ceaf9SAlex Deucher * SI supports 16. 51d38ceaf9SAlex Deucher */ 52d38ceaf9SAlex Deucher 534ff37a83SChristian König /* Special value that no flush is necessary */ 544ff37a83SChristian König #define AMDGPU_VM_NO_FLUSH (~0ll) 554ff37a83SChristian König 56d38ceaf9SAlex Deucher /** 57d38ceaf9SAlex Deucher * amdgpu_vm_num_pde - return the number of page directory entries 58d38ceaf9SAlex Deucher * 59d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 60d38ceaf9SAlex Deucher * 618843dbbbSChristian König * Calculate the number of page directory entries. 62d38ceaf9SAlex Deucher */ 63d38ceaf9SAlex Deucher static unsigned amdgpu_vm_num_pdes(struct amdgpu_device *adev) 64d38ceaf9SAlex Deucher { 65d38ceaf9SAlex Deucher return adev->vm_manager.max_pfn >> amdgpu_vm_block_size; 66d38ceaf9SAlex Deucher } 67d38ceaf9SAlex Deucher 68d38ceaf9SAlex Deucher /** 69d38ceaf9SAlex Deucher * amdgpu_vm_directory_size - returns the size of the page directory in bytes 70d38ceaf9SAlex Deucher * 71d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 72d38ceaf9SAlex Deucher * 738843dbbbSChristian König * Calculate the size of the page directory in bytes. 74d38ceaf9SAlex Deucher */ 75d38ceaf9SAlex Deucher static unsigned amdgpu_vm_directory_size(struct amdgpu_device *adev) 76d38ceaf9SAlex Deucher { 77d38ceaf9SAlex Deucher return AMDGPU_GPU_PAGE_ALIGN(amdgpu_vm_num_pdes(adev) * 8); 78d38ceaf9SAlex Deucher } 79d38ceaf9SAlex Deucher 80d38ceaf9SAlex Deucher /** 8156467ebfSChristian König * amdgpu_vm_get_pd_bo - add the VM PD to a validation list 82d38ceaf9SAlex Deucher * 83d38ceaf9SAlex Deucher * @vm: vm providing the BOs 843c0eea6cSChristian König * @validated: head of validation list 8556467ebfSChristian König * @entry: entry to add 86d38ceaf9SAlex Deucher * 87d38ceaf9SAlex Deucher * Add the page directory to the list of BOs to 8856467ebfSChristian König * validate for command submission. 89d38ceaf9SAlex Deucher */ 9056467ebfSChristian König void amdgpu_vm_get_pd_bo(struct amdgpu_vm *vm, 9156467ebfSChristian König struct list_head *validated, 9256467ebfSChristian König struct amdgpu_bo_list_entry *entry) 93d38ceaf9SAlex Deucher { 9456467ebfSChristian König entry->robj = vm->page_directory; 9556467ebfSChristian König entry->priority = 0; 9656467ebfSChristian König entry->tv.bo = &vm->page_directory->tbo; 9756467ebfSChristian König entry->tv.shared = true; 982f568dbdSChristian König entry->user_pages = NULL; 9956467ebfSChristian König list_add(&entry->tv.head, validated); 1003d5a08c1Smonk.liu } 101d38ceaf9SAlex Deucher 10256467ebfSChristian König /** 103ee1782c3SChristian König * amdgpu_vm_get_bos - add the vm BOs to a duplicates list 10456467ebfSChristian König * 10556467ebfSChristian König * @vm: vm providing the BOs 1063c0eea6cSChristian König * @duplicates: head of duplicates list 107d38ceaf9SAlex Deucher * 108ee1782c3SChristian König * Add the page directory to the BO duplicates list 109ee1782c3SChristian König * for command submission. 110d38ceaf9SAlex Deucher */ 111ee1782c3SChristian König void amdgpu_vm_get_pt_bos(struct amdgpu_vm *vm, struct list_head *duplicates) 112d38ceaf9SAlex Deucher { 113ee1782c3SChristian König unsigned i; 114d38ceaf9SAlex Deucher 115d38ceaf9SAlex Deucher /* add the vm page table to the list */ 116ee1782c3SChristian König for (i = 0; i <= vm->max_pde_used; ++i) { 117ee1782c3SChristian König struct amdgpu_bo_list_entry *entry = &vm->page_tables[i].entry; 118d38ceaf9SAlex Deucher 119ee1782c3SChristian König if (!entry->robj) 120d38ceaf9SAlex Deucher continue; 121d38ceaf9SAlex Deucher 122ee1782c3SChristian König list_add(&entry->tv.head, duplicates); 123d38ceaf9SAlex Deucher } 124eceb8a15SChristian König 125eceb8a15SChristian König } 126eceb8a15SChristian König 127eceb8a15SChristian König /** 128eceb8a15SChristian König * amdgpu_vm_move_pt_bos_in_lru - move the PT BOs to the LRU tail 129eceb8a15SChristian König * 130eceb8a15SChristian König * @adev: amdgpu device instance 131eceb8a15SChristian König * @vm: vm providing the BOs 132eceb8a15SChristian König * 133eceb8a15SChristian König * Move the PT BOs to the tail of the LRU. 134eceb8a15SChristian König */ 135eceb8a15SChristian König void amdgpu_vm_move_pt_bos_in_lru(struct amdgpu_device *adev, 136eceb8a15SChristian König struct amdgpu_vm *vm) 137eceb8a15SChristian König { 138eceb8a15SChristian König struct ttm_bo_global *glob = adev->mman.bdev.glob; 139eceb8a15SChristian König unsigned i; 140eceb8a15SChristian König 141eceb8a15SChristian König spin_lock(&glob->lru_lock); 142eceb8a15SChristian König for (i = 0; i <= vm->max_pde_used; ++i) { 143eceb8a15SChristian König struct amdgpu_bo_list_entry *entry = &vm->page_tables[i].entry; 144eceb8a15SChristian König 145eceb8a15SChristian König if (!entry->robj) 146eceb8a15SChristian König continue; 147eceb8a15SChristian König 148eceb8a15SChristian König ttm_bo_move_to_lru_tail(&entry->robj->tbo); 149eceb8a15SChristian König } 150eceb8a15SChristian König spin_unlock(&glob->lru_lock); 151d38ceaf9SAlex Deucher } 152d38ceaf9SAlex Deucher 153d38ceaf9SAlex Deucher /** 154d38ceaf9SAlex Deucher * amdgpu_vm_grab_id - allocate the next free VMID 155d38ceaf9SAlex Deucher * 156d38ceaf9SAlex Deucher * @vm: vm to allocate id for 1577f8a5290SChristian König * @ring: ring we want to submit job to 1587f8a5290SChristian König * @sync: sync object where we add dependencies 15994dd0a4aSChristian König * @fence: fence protecting ID from reuse 160d38ceaf9SAlex Deucher * 1617f8a5290SChristian König * Allocate an id for the vm, adding fences to the sync obj as necessary. 162d38ceaf9SAlex Deucher */ 1637f8a5290SChristian König int amdgpu_vm_grab_id(struct amdgpu_vm *vm, struct amdgpu_ring *ring, 1644ff37a83SChristian König struct amdgpu_sync *sync, struct fence *fence, 1654ff37a83SChristian König unsigned *vm_id, uint64_t *vm_pd_addr) 166d38ceaf9SAlex Deucher { 1674ff37a83SChristian König uint64_t pd_addr = amdgpu_bo_gpu_offset(vm->page_directory); 168d38ceaf9SAlex Deucher struct amdgpu_device *adev = ring->adev; 1694ff37a83SChristian König struct amdgpu_vm_id *id = &vm->ids[ring->idx]; 1704ff37a83SChristian König struct fence *updates = sync->last_vm_update; 171a9a78b32SChristian König int r; 172d38ceaf9SAlex Deucher 17394dd0a4aSChristian König mutex_lock(&adev->vm_manager.lock); 17494dd0a4aSChristian König 175d38ceaf9SAlex Deucher /* check if the id is still valid */ 1764ff37a83SChristian König if (id->mgr_id) { 1774ff37a83SChristian König struct fence *flushed = id->flushed_updates; 1784ff37a83SChristian König bool is_later; 1791c16c0a7SChristian König long owner; 1801c16c0a7SChristian König 1814ff37a83SChristian König if (!flushed) 1824ff37a83SChristian König is_later = true; 1834ff37a83SChristian König else if (!updates) 1844ff37a83SChristian König is_later = false; 1854ff37a83SChristian König else 1864ff37a83SChristian König is_later = fence_is_later(updates, flushed); 187a9a78b32SChristian König 1884ff37a83SChristian König owner = atomic_long_read(&id->mgr_id->owner); 1894ff37a83SChristian König if (!is_later && owner == (long)id && 1904ff37a83SChristian König pd_addr == id->pd_gpu_addr) { 1914ff37a83SChristian König 192a8bd1becSChristian König r = amdgpu_sync_fence(ring->adev, sync, 193a8bd1becSChristian König id->mgr_id->active); 194a8bd1becSChristian König if (r) { 195a8bd1becSChristian König mutex_unlock(&adev->vm_manager.lock); 196a8bd1becSChristian König return r; 197a8bd1becSChristian König } 198a8bd1becSChristian König 1994ff37a83SChristian König fence_put(id->mgr_id->active); 2004ff37a83SChristian König id->mgr_id->active = fence_get(fence); 2014ff37a83SChristian König 2024ff37a83SChristian König list_move_tail(&id->mgr_id->list, 2034ff37a83SChristian König &adev->vm_manager.ids_lru); 2044ff37a83SChristian König 2054ff37a83SChristian König *vm_id = id->mgr_id - adev->vm_manager.ids; 2064ff37a83SChristian König *vm_pd_addr = AMDGPU_VM_NO_FLUSH; 20722073fe7SChristian König trace_amdgpu_vm_grab_id(vm, ring->idx, *vm_id, 20822073fe7SChristian König *vm_pd_addr); 209a9a78b32SChristian König 21094dd0a4aSChristian König mutex_unlock(&adev->vm_manager.lock); 2117f8a5290SChristian König return 0; 21239ff8449SChristian König } 2131c16c0a7SChristian König } 214d38ceaf9SAlex Deucher 2154ff37a83SChristian König id->mgr_id = list_first_entry(&adev->vm_manager.ids_lru, 216a9a78b32SChristian König struct amdgpu_vm_manager_id, 217a9a78b32SChristian König list); 218d38ceaf9SAlex Deucher 2194ff37a83SChristian König r = amdgpu_sync_fence(ring->adev, sync, id->mgr_id->active); 220a9a78b32SChristian König if (!r) { 2214ff37a83SChristian König fence_put(id->mgr_id->active); 2224ff37a83SChristian König id->mgr_id->active = fence_get(fence); 2234ff37a83SChristian König 2244ff37a83SChristian König fence_put(id->flushed_updates); 2254ff37a83SChristian König id->flushed_updates = fence_get(updates); 2264ff37a83SChristian König 2274ff37a83SChristian König id->pd_gpu_addr = pd_addr; 2284ff37a83SChristian König 2294ff37a83SChristian König list_move_tail(&id->mgr_id->list, &adev->vm_manager.ids_lru); 2304ff37a83SChristian König atomic_long_set(&id->mgr_id->owner, (long)id); 2314ff37a83SChristian König 2324ff37a83SChristian König *vm_id = id->mgr_id - adev->vm_manager.ids; 2334ff37a83SChristian König *vm_pd_addr = pd_addr; 23422073fe7SChristian König trace_amdgpu_vm_grab_id(vm, ring->idx, *vm_id, *vm_pd_addr); 235d38ceaf9SAlex Deucher } 236d38ceaf9SAlex Deucher 23794dd0a4aSChristian König mutex_unlock(&adev->vm_manager.lock); 23894dd0a4aSChristian König return r; 239d38ceaf9SAlex Deucher } 240d38ceaf9SAlex Deucher 241d38ceaf9SAlex Deucher /** 242d38ceaf9SAlex Deucher * amdgpu_vm_flush - hardware flush the vm 243d38ceaf9SAlex Deucher * 244d38ceaf9SAlex Deucher * @ring: ring to use for flush 245cffadc83SChristian König * @vm_id: vmid number to use 2464ff37a83SChristian König * @pd_addr: address of the page directory 247d38ceaf9SAlex Deucher * 2484ff37a83SChristian König * Emit a VM flush when it is necessary. 249d38ceaf9SAlex Deucher */ 250d38ceaf9SAlex Deucher void amdgpu_vm_flush(struct amdgpu_ring *ring, 251cffadc83SChristian König unsigned vm_id, uint64_t pd_addr, 252cffadc83SChristian König uint32_t gds_base, uint32_t gds_size, 253cffadc83SChristian König uint32_t gws_base, uint32_t gws_size, 254cffadc83SChristian König uint32_t oa_base, uint32_t oa_size) 255d38ceaf9SAlex Deucher { 256971fe9a9SChristian König struct amdgpu_device *adev = ring->adev; 257971fe9a9SChristian König struct amdgpu_vm_manager_id *mgr_id = &adev->vm_manager.ids[vm_id]; 258d564a06eSChristian König bool gds_switch_needed = ring->funcs->emit_gds_switch && ( 259971fe9a9SChristian König mgr_id->gds_base != gds_base || 260971fe9a9SChristian König mgr_id->gds_size != gds_size || 261971fe9a9SChristian König mgr_id->gws_base != gws_base || 262971fe9a9SChristian König mgr_id->gws_size != gws_size || 263971fe9a9SChristian König mgr_id->oa_base != oa_base || 264d564a06eSChristian König mgr_id->oa_size != oa_size); 265971fe9a9SChristian König 266d564a06eSChristian König if (ring->funcs->emit_pipeline_sync && ( 267d564a06eSChristian König pd_addr != AMDGPU_VM_NO_FLUSH || gds_switch_needed)) 268d564a06eSChristian König amdgpu_ring_emit_pipeline_sync(ring); 269d564a06eSChristian König 270d564a06eSChristian König if (pd_addr != AMDGPU_VM_NO_FLUSH) { 271d564a06eSChristian König trace_amdgpu_vm_flush(pd_addr, ring->idx, vm_id); 272d564a06eSChristian König amdgpu_ring_emit_vm_flush(ring, vm_id, pd_addr); 273d564a06eSChristian König } 274d564a06eSChristian König 275d564a06eSChristian König if (gds_switch_needed) { 276971fe9a9SChristian König mgr_id->gds_base = gds_base; 277971fe9a9SChristian König mgr_id->gds_size = gds_size; 278971fe9a9SChristian König mgr_id->gws_base = gws_base; 279971fe9a9SChristian König mgr_id->gws_size = gws_size; 280971fe9a9SChristian König mgr_id->oa_base = oa_base; 281971fe9a9SChristian König mgr_id->oa_size = oa_size; 282cffadc83SChristian König amdgpu_ring_emit_gds_switch(ring, vm_id, 283cffadc83SChristian König gds_base, gds_size, 284cffadc83SChristian König gws_base, gws_size, 285cffadc83SChristian König oa_base, oa_size); 286d38ceaf9SAlex Deucher } 287971fe9a9SChristian König } 288971fe9a9SChristian König 289971fe9a9SChristian König /** 290971fe9a9SChristian König * amdgpu_vm_reset_id - reset VMID to zero 291971fe9a9SChristian König * 292971fe9a9SChristian König * @adev: amdgpu device structure 293971fe9a9SChristian König * @vm_id: vmid number to use 294971fe9a9SChristian König * 295971fe9a9SChristian König * Reset saved GDW, GWS and OA to force switch on next flush. 296971fe9a9SChristian König */ 297971fe9a9SChristian König void amdgpu_vm_reset_id(struct amdgpu_device *adev, unsigned vm_id) 298971fe9a9SChristian König { 299971fe9a9SChristian König struct amdgpu_vm_manager_id *mgr_id = &adev->vm_manager.ids[vm_id]; 300971fe9a9SChristian König 301971fe9a9SChristian König mgr_id->gds_base = 0; 302971fe9a9SChristian König mgr_id->gds_size = 0; 303971fe9a9SChristian König mgr_id->gws_base = 0; 304971fe9a9SChristian König mgr_id->gws_size = 0; 305971fe9a9SChristian König mgr_id->oa_base = 0; 306971fe9a9SChristian König mgr_id->oa_size = 0; 307971fe9a9SChristian König } 308d38ceaf9SAlex Deucher 309d38ceaf9SAlex Deucher /** 310d38ceaf9SAlex Deucher * amdgpu_vm_bo_find - find the bo_va for a specific vm & bo 311d38ceaf9SAlex Deucher * 312d38ceaf9SAlex Deucher * @vm: requested vm 313d38ceaf9SAlex Deucher * @bo: requested buffer object 314d38ceaf9SAlex Deucher * 3158843dbbbSChristian König * Find @bo inside the requested vm. 316d38ceaf9SAlex Deucher * Search inside the @bos vm list for the requested vm 317d38ceaf9SAlex Deucher * Returns the found bo_va or NULL if none is found 318d38ceaf9SAlex Deucher * 319d38ceaf9SAlex Deucher * Object has to be reserved! 320d38ceaf9SAlex Deucher */ 321d38ceaf9SAlex Deucher struct amdgpu_bo_va *amdgpu_vm_bo_find(struct amdgpu_vm *vm, 322d38ceaf9SAlex Deucher struct amdgpu_bo *bo) 323d38ceaf9SAlex Deucher { 324d38ceaf9SAlex Deucher struct amdgpu_bo_va *bo_va; 325d38ceaf9SAlex Deucher 326d38ceaf9SAlex Deucher list_for_each_entry(bo_va, &bo->va, bo_list) { 327d38ceaf9SAlex Deucher if (bo_va->vm == vm) { 328d38ceaf9SAlex Deucher return bo_va; 329d38ceaf9SAlex Deucher } 330d38ceaf9SAlex Deucher } 331d38ceaf9SAlex Deucher return NULL; 332d38ceaf9SAlex Deucher } 333d38ceaf9SAlex Deucher 334d38ceaf9SAlex Deucher /** 335d38ceaf9SAlex Deucher * amdgpu_vm_update_pages - helper to call the right asic function 336d38ceaf9SAlex Deucher * 337d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 3389ab21462SChristian König * @gtt: GART instance to use for mapping 3399ab21462SChristian König * @gtt_flags: GTT hw access flags 340d38ceaf9SAlex Deucher * @ib: indirect buffer to fill with commands 341d38ceaf9SAlex Deucher * @pe: addr of the page entry 342d38ceaf9SAlex Deucher * @addr: dst addr to write into pe 343d38ceaf9SAlex Deucher * @count: number of page entries to update 344d38ceaf9SAlex Deucher * @incr: increase next addr by incr bytes 345d38ceaf9SAlex Deucher * @flags: hw access flags 346d38ceaf9SAlex Deucher * 347d38ceaf9SAlex Deucher * Traces the parameters and calls the right asic functions 348d38ceaf9SAlex Deucher * to setup the page table using the DMA. 349d38ceaf9SAlex Deucher */ 350d38ceaf9SAlex Deucher static void amdgpu_vm_update_pages(struct amdgpu_device *adev, 3519ab21462SChristian König struct amdgpu_gart *gtt, 3529ab21462SChristian König uint32_t gtt_flags, 353d38ceaf9SAlex Deucher struct amdgpu_ib *ib, 354d38ceaf9SAlex Deucher uint64_t pe, uint64_t addr, 355d38ceaf9SAlex Deucher unsigned count, uint32_t incr, 3569ab21462SChristian König uint32_t flags) 357d38ceaf9SAlex Deucher { 358d38ceaf9SAlex Deucher trace_amdgpu_vm_set_page(pe, addr, count, incr, flags); 359d38ceaf9SAlex Deucher 3609ab21462SChristian König if ((gtt == &adev->gart) && (flags == gtt_flags)) { 3619ab21462SChristian König uint64_t src = gtt->table_addr + (addr >> 12) * 8; 362d38ceaf9SAlex Deucher amdgpu_vm_copy_pte(adev, ib, pe, src, count); 363d38ceaf9SAlex Deucher 3649ab21462SChristian König } else if (gtt) { 3659ab21462SChristian König dma_addr_t *pages_addr = gtt->pages_addr; 366b07c9d2aSChristian König amdgpu_vm_write_pte(adev, ib, pages_addr, pe, addr, 367b07c9d2aSChristian König count, incr, flags); 368b07c9d2aSChristian König 369b07c9d2aSChristian König } else if (count < 3) { 370b07c9d2aSChristian König amdgpu_vm_write_pte(adev, ib, NULL, pe, addr, 371d38ceaf9SAlex Deucher count, incr, flags); 372d38ceaf9SAlex Deucher 373d38ceaf9SAlex Deucher } else { 374d38ceaf9SAlex Deucher amdgpu_vm_set_pte_pde(adev, ib, pe, addr, 375d38ceaf9SAlex Deucher count, incr, flags); 376d38ceaf9SAlex Deucher } 377d38ceaf9SAlex Deucher } 378d38ceaf9SAlex Deucher 379d38ceaf9SAlex Deucher /** 380d38ceaf9SAlex Deucher * amdgpu_vm_clear_bo - initially clear the page dir/table 381d38ceaf9SAlex Deucher * 382d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 383d38ceaf9SAlex Deucher * @bo: bo to clear 384ef9f0a83SChunming Zhou * 385ef9f0a83SChunming Zhou * need to reserve bo first before calling it. 386d38ceaf9SAlex Deucher */ 387d38ceaf9SAlex Deucher static int amdgpu_vm_clear_bo(struct amdgpu_device *adev, 3882bd9ccfaSChristian König struct amdgpu_vm *vm, 389d38ceaf9SAlex Deucher struct amdgpu_bo *bo) 390d38ceaf9SAlex Deucher { 3912d55e45aSChristian König struct amdgpu_ring *ring; 3924af9f07cSChunming Zhou struct fence *fence = NULL; 393d71518b5SChristian König struct amdgpu_job *job; 394d38ceaf9SAlex Deucher unsigned entries; 395d38ceaf9SAlex Deucher uint64_t addr; 396d38ceaf9SAlex Deucher int r; 397d38ceaf9SAlex Deucher 3982d55e45aSChristian König ring = container_of(vm->entity.sched, struct amdgpu_ring, sched); 3992d55e45aSChristian König 400ca952613Smonk.liu r = reservation_object_reserve_shared(bo->tbo.resv); 401ca952613Smonk.liu if (r) 402ca952613Smonk.liu return r; 403ca952613Smonk.liu 404d38ceaf9SAlex Deucher r = ttm_bo_validate(&bo->tbo, &bo->placement, true, false); 405d38ceaf9SAlex Deucher if (r) 406ef9f0a83SChunming Zhou goto error; 407d38ceaf9SAlex Deucher 408d38ceaf9SAlex Deucher addr = amdgpu_bo_gpu_offset(bo); 409d38ceaf9SAlex Deucher entries = amdgpu_bo_size(bo) / 8; 410d38ceaf9SAlex Deucher 411d71518b5SChristian König r = amdgpu_job_alloc_with_ib(adev, 64, &job); 412d71518b5SChristian König if (r) 413ef9f0a83SChunming Zhou goto error; 414d38ceaf9SAlex Deucher 415d71518b5SChristian König amdgpu_vm_update_pages(adev, NULL, 0, &job->ibs[0], addr, 0, entries, 416d71518b5SChristian König 0, 0); 417d71518b5SChristian König amdgpu_ring_pad_ib(ring, &job->ibs[0]); 418d71518b5SChristian König 419d71518b5SChristian König WARN_ON(job->ibs[0].length_dw > 64); 4202bd9ccfaSChristian König r = amdgpu_job_submit(job, ring, &vm->entity, 4212bd9ccfaSChristian König AMDGPU_FENCE_OWNER_VM, &fence); 422d38ceaf9SAlex Deucher if (r) 423d38ceaf9SAlex Deucher goto error_free; 424d38ceaf9SAlex Deucher 4254af9f07cSChunming Zhou amdgpu_bo_fence(bo, fence, true); 426281b4223SChunming Zhou fence_put(fence); 427d5fc5e82SChunming Zhou return 0; 428ef9f0a83SChunming Zhou 429d38ceaf9SAlex Deucher error_free: 430d71518b5SChristian König amdgpu_job_free(job); 431d38ceaf9SAlex Deucher 432ef9f0a83SChunming Zhou error: 433d38ceaf9SAlex Deucher return r; 434d38ceaf9SAlex Deucher } 435d38ceaf9SAlex Deucher 436d38ceaf9SAlex Deucher /** 437b07c9d2aSChristian König * amdgpu_vm_map_gart - Resolve gart mapping of addr 438d38ceaf9SAlex Deucher * 439b07c9d2aSChristian König * @pages_addr: optional DMA address to use for lookup 440d38ceaf9SAlex Deucher * @addr: the unmapped addr 441d38ceaf9SAlex Deucher * 442d38ceaf9SAlex Deucher * Look up the physical address of the page that the pte resolves 443b07c9d2aSChristian König * to and return the pointer for the page table entry. 444d38ceaf9SAlex Deucher */ 445b07c9d2aSChristian König uint64_t amdgpu_vm_map_gart(const dma_addr_t *pages_addr, uint64_t addr) 446d38ceaf9SAlex Deucher { 447d38ceaf9SAlex Deucher uint64_t result; 448d38ceaf9SAlex Deucher 449b07c9d2aSChristian König if (pages_addr) { 450d38ceaf9SAlex Deucher /* page table offset */ 451b07c9d2aSChristian König result = pages_addr[addr >> PAGE_SHIFT]; 452d38ceaf9SAlex Deucher 453d38ceaf9SAlex Deucher /* in case cpu page size != gpu page size*/ 454d38ceaf9SAlex Deucher result |= addr & (~PAGE_MASK); 455d38ceaf9SAlex Deucher 456b07c9d2aSChristian König } else { 457b07c9d2aSChristian König /* No mapping required */ 458b07c9d2aSChristian König result = addr; 459b07c9d2aSChristian König } 460b07c9d2aSChristian König 461b07c9d2aSChristian König result &= 0xFFFFFFFFFFFFF000ULL; 462b07c9d2aSChristian König 463d38ceaf9SAlex Deucher return result; 464d38ceaf9SAlex Deucher } 465d38ceaf9SAlex Deucher 466d38ceaf9SAlex Deucher /** 467d38ceaf9SAlex Deucher * amdgpu_vm_update_pdes - make sure that page directory is valid 468d38ceaf9SAlex Deucher * 469d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 470d38ceaf9SAlex Deucher * @vm: requested vm 471d38ceaf9SAlex Deucher * @start: start of GPU address range 472d38ceaf9SAlex Deucher * @end: end of GPU address range 473d38ceaf9SAlex Deucher * 474d38ceaf9SAlex Deucher * Allocates new page tables if necessary 4758843dbbbSChristian König * and updates the page directory. 476d38ceaf9SAlex Deucher * Returns 0 for success, error for failure. 477d38ceaf9SAlex Deucher */ 478d38ceaf9SAlex Deucher int amdgpu_vm_update_page_directory(struct amdgpu_device *adev, 479d38ceaf9SAlex Deucher struct amdgpu_vm *vm) 480d38ceaf9SAlex Deucher { 4812d55e45aSChristian König struct amdgpu_ring *ring; 482d38ceaf9SAlex Deucher struct amdgpu_bo *pd = vm->page_directory; 483d38ceaf9SAlex Deucher uint64_t pd_addr = amdgpu_bo_gpu_offset(pd); 484d38ceaf9SAlex Deucher uint32_t incr = AMDGPU_VM_PTE_COUNT * 8; 485d38ceaf9SAlex Deucher uint64_t last_pde = ~0, last_pt = ~0; 486d38ceaf9SAlex Deucher unsigned count = 0, pt_idx, ndw; 487d71518b5SChristian König struct amdgpu_job *job; 488d5fc5e82SChunming Zhou struct amdgpu_ib *ib; 4894af9f07cSChunming Zhou struct fence *fence = NULL; 490d5fc5e82SChunming Zhou 491d38ceaf9SAlex Deucher int r; 492d38ceaf9SAlex Deucher 4932d55e45aSChristian König ring = container_of(vm->entity.sched, struct amdgpu_ring, sched); 4942d55e45aSChristian König 495d38ceaf9SAlex Deucher /* padding, etc. */ 496d38ceaf9SAlex Deucher ndw = 64; 497d38ceaf9SAlex Deucher 498d38ceaf9SAlex Deucher /* assume the worst case */ 499d38ceaf9SAlex Deucher ndw += vm->max_pde_used * 6; 500d38ceaf9SAlex Deucher 501d71518b5SChristian König r = amdgpu_job_alloc_with_ib(adev, ndw * 4, &job); 502d71518b5SChristian König if (r) 503d38ceaf9SAlex Deucher return r; 504d71518b5SChristian König 505d71518b5SChristian König ib = &job->ibs[0]; 506d38ceaf9SAlex Deucher 507d38ceaf9SAlex Deucher /* walk over the address space and update the page directory */ 508d38ceaf9SAlex Deucher for (pt_idx = 0; pt_idx <= vm->max_pde_used; ++pt_idx) { 509ee1782c3SChristian König struct amdgpu_bo *bo = vm->page_tables[pt_idx].entry.robj; 510d38ceaf9SAlex Deucher uint64_t pde, pt; 511d38ceaf9SAlex Deucher 512d38ceaf9SAlex Deucher if (bo == NULL) 513d38ceaf9SAlex Deucher continue; 514d38ceaf9SAlex Deucher 515d38ceaf9SAlex Deucher pt = amdgpu_bo_gpu_offset(bo); 516d38ceaf9SAlex Deucher if (vm->page_tables[pt_idx].addr == pt) 517d38ceaf9SAlex Deucher continue; 518d38ceaf9SAlex Deucher vm->page_tables[pt_idx].addr = pt; 519d38ceaf9SAlex Deucher 520d38ceaf9SAlex Deucher pde = pd_addr + pt_idx * 8; 521d38ceaf9SAlex Deucher if (((last_pde + 8 * count) != pde) || 522d38ceaf9SAlex Deucher ((last_pt + incr * count) != pt)) { 523d38ceaf9SAlex Deucher 524d38ceaf9SAlex Deucher if (count) { 5259ab21462SChristian König amdgpu_vm_update_pages(adev, NULL, 0, ib, 5269ab21462SChristian König last_pde, last_pt, 5279ab21462SChristian König count, incr, 5289ab21462SChristian König AMDGPU_PTE_VALID); 529d38ceaf9SAlex Deucher } 530d38ceaf9SAlex Deucher 531d38ceaf9SAlex Deucher count = 1; 532d38ceaf9SAlex Deucher last_pde = pde; 533d38ceaf9SAlex Deucher last_pt = pt; 534d38ceaf9SAlex Deucher } else { 535d38ceaf9SAlex Deucher ++count; 536d38ceaf9SAlex Deucher } 537d38ceaf9SAlex Deucher } 538d38ceaf9SAlex Deucher 539d38ceaf9SAlex Deucher if (count) 5409ab21462SChristian König amdgpu_vm_update_pages(adev, NULL, 0, ib, last_pde, last_pt, 5419ab21462SChristian König count, incr, AMDGPU_PTE_VALID); 542d38ceaf9SAlex Deucher 543d5fc5e82SChunming Zhou if (ib->length_dw != 0) { 5449e5d5309SChristian König amdgpu_ring_pad_ib(ring, ib); 545e86f9ceeSChristian König amdgpu_sync_resv(adev, &job->sync, pd->tbo.resv, 546e86f9ceeSChristian König AMDGPU_FENCE_OWNER_VM); 547d5fc5e82SChunming Zhou WARN_ON(ib->length_dw > ndw); 5482bd9ccfaSChristian König r = amdgpu_job_submit(job, ring, &vm->entity, 5492bd9ccfaSChristian König AMDGPU_FENCE_OWNER_VM, &fence); 550d5fc5e82SChunming Zhou if (r) 5514af9f07cSChunming Zhou goto error_free; 55205906decSBas Nieuwenhuizen 5534af9f07cSChunming Zhou amdgpu_bo_fence(pd, fence, true); 55405906decSBas Nieuwenhuizen fence_put(vm->page_directory_fence); 55505906decSBas Nieuwenhuizen vm->page_directory_fence = fence_get(fence); 556281b4223SChunming Zhou fence_put(fence); 557d5fc5e82SChunming Zhou 558d71518b5SChristian König } else { 559d71518b5SChristian König amdgpu_job_free(job); 560d5fc5e82SChunming Zhou } 561d38ceaf9SAlex Deucher 562d38ceaf9SAlex Deucher return 0; 563d5fc5e82SChunming Zhou 564d5fc5e82SChunming Zhou error_free: 565d71518b5SChristian König amdgpu_job_free(job); 5664af9f07cSChunming Zhou return r; 567d38ceaf9SAlex Deucher } 568d38ceaf9SAlex Deucher 569d38ceaf9SAlex Deucher /** 570d38ceaf9SAlex Deucher * amdgpu_vm_frag_ptes - add fragment information to PTEs 571d38ceaf9SAlex Deucher * 572d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 5739ab21462SChristian König * @gtt: GART instance to use for mapping 5749ab21462SChristian König * @gtt_flags: GTT hw mapping flags 575d38ceaf9SAlex Deucher * @ib: IB for the update 576d38ceaf9SAlex Deucher * @pe_start: first PTE to handle 577d38ceaf9SAlex Deucher * @pe_end: last PTE to handle 578d38ceaf9SAlex Deucher * @addr: addr those PTEs should point to 579d38ceaf9SAlex Deucher * @flags: hw mapping flags 580d38ceaf9SAlex Deucher */ 581d38ceaf9SAlex Deucher static void amdgpu_vm_frag_ptes(struct amdgpu_device *adev, 5829ab21462SChristian König struct amdgpu_gart *gtt, 5839ab21462SChristian König uint32_t gtt_flags, 584d38ceaf9SAlex Deucher struct amdgpu_ib *ib, 585d38ceaf9SAlex Deucher uint64_t pe_start, uint64_t pe_end, 5869ab21462SChristian König uint64_t addr, uint32_t flags) 587d38ceaf9SAlex Deucher { 588d38ceaf9SAlex Deucher /** 589d38ceaf9SAlex Deucher * The MC L1 TLB supports variable sized pages, based on a fragment 590d38ceaf9SAlex Deucher * field in the PTE. When this field is set to a non-zero value, page 591d38ceaf9SAlex Deucher * granularity is increased from 4KB to (1 << (12 + frag)). The PTE 592d38ceaf9SAlex Deucher * flags are considered valid for all PTEs within the fragment range 593d38ceaf9SAlex Deucher * and corresponding mappings are assumed to be physically contiguous. 594d38ceaf9SAlex Deucher * 595d38ceaf9SAlex Deucher * The L1 TLB can store a single PTE for the whole fragment, 596d38ceaf9SAlex Deucher * significantly increasing the space available for translation 597d38ceaf9SAlex Deucher * caching. This leads to large improvements in throughput when the 598d38ceaf9SAlex Deucher * TLB is under pressure. 599d38ceaf9SAlex Deucher * 600d38ceaf9SAlex Deucher * The L2 TLB distributes small and large fragments into two 601d38ceaf9SAlex Deucher * asymmetric partitions. The large fragment cache is significantly 602d38ceaf9SAlex Deucher * larger. Thus, we try to use large fragments wherever possible. 603d38ceaf9SAlex Deucher * Userspace can support this by aligning virtual base address and 604d38ceaf9SAlex Deucher * allocation size to the fragment size. 605d38ceaf9SAlex Deucher */ 606d38ceaf9SAlex Deucher 607d38ceaf9SAlex Deucher /* SI and newer are optimized for 64KB */ 608d38ceaf9SAlex Deucher uint64_t frag_flags = AMDGPU_PTE_FRAG_64KB; 609d38ceaf9SAlex Deucher uint64_t frag_align = 0x80; 610d38ceaf9SAlex Deucher 611d38ceaf9SAlex Deucher uint64_t frag_start = ALIGN(pe_start, frag_align); 612d38ceaf9SAlex Deucher uint64_t frag_end = pe_end & ~(frag_align - 1); 613d38ceaf9SAlex Deucher 614d38ceaf9SAlex Deucher unsigned count; 615d38ceaf9SAlex Deucher 61631f6c1feSChristian König /* Abort early if there isn't anything to do */ 61731f6c1feSChristian König if (pe_start == pe_end) 61831f6c1feSChristian König return; 61931f6c1feSChristian König 620d38ceaf9SAlex Deucher /* system pages are non continuously */ 6219ab21462SChristian König if (gtt || !(flags & AMDGPU_PTE_VALID) || (frag_start >= frag_end)) { 622d38ceaf9SAlex Deucher 623d38ceaf9SAlex Deucher count = (pe_end - pe_start) / 8; 6249ab21462SChristian König amdgpu_vm_update_pages(adev, gtt, gtt_flags, ib, pe_start, 6259ab21462SChristian König addr, count, AMDGPU_GPU_PAGE_SIZE, 6269ab21462SChristian König flags); 627d38ceaf9SAlex Deucher return; 628d38ceaf9SAlex Deucher } 629d38ceaf9SAlex Deucher 630d38ceaf9SAlex Deucher /* handle the 4K area at the beginning */ 631d38ceaf9SAlex Deucher if (pe_start != frag_start) { 632d38ceaf9SAlex Deucher count = (frag_start - pe_start) / 8; 6339ab21462SChristian König amdgpu_vm_update_pages(adev, NULL, 0, ib, pe_start, addr, 6349ab21462SChristian König count, AMDGPU_GPU_PAGE_SIZE, flags); 635d38ceaf9SAlex Deucher addr += AMDGPU_GPU_PAGE_SIZE * count; 636d38ceaf9SAlex Deucher } 637d38ceaf9SAlex Deucher 638d38ceaf9SAlex Deucher /* handle the area in the middle */ 639d38ceaf9SAlex Deucher count = (frag_end - frag_start) / 8; 6409ab21462SChristian König amdgpu_vm_update_pages(adev, NULL, 0, ib, frag_start, addr, count, 6419ab21462SChristian König AMDGPU_GPU_PAGE_SIZE, flags | frag_flags); 642d38ceaf9SAlex Deucher 643d38ceaf9SAlex Deucher /* handle the 4K area at the end */ 644d38ceaf9SAlex Deucher if (frag_end != pe_end) { 645d38ceaf9SAlex Deucher addr += AMDGPU_GPU_PAGE_SIZE * count; 646d38ceaf9SAlex Deucher count = (pe_end - frag_end) / 8; 6479ab21462SChristian König amdgpu_vm_update_pages(adev, NULL, 0, ib, frag_end, addr, 6489ab21462SChristian König count, AMDGPU_GPU_PAGE_SIZE, flags); 649d38ceaf9SAlex Deucher } 650d38ceaf9SAlex Deucher } 651d38ceaf9SAlex Deucher 652d38ceaf9SAlex Deucher /** 653d38ceaf9SAlex Deucher * amdgpu_vm_update_ptes - make sure that page tables are valid 654d38ceaf9SAlex Deucher * 655d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 6569ab21462SChristian König * @gtt: GART instance to use for mapping 6579ab21462SChristian König * @gtt_flags: GTT hw mapping flags 658d38ceaf9SAlex Deucher * @vm: requested vm 659d38ceaf9SAlex Deucher * @start: start of GPU address range 660d38ceaf9SAlex Deucher * @end: end of GPU address range 661d38ceaf9SAlex Deucher * @dst: destination address to map to 662d38ceaf9SAlex Deucher * @flags: mapping flags 663d38ceaf9SAlex Deucher * 6648843dbbbSChristian König * Update the page tables in the range @start - @end. 665d38ceaf9SAlex Deucher */ 666a1e08d3bSChristian König static void amdgpu_vm_update_ptes(struct amdgpu_device *adev, 6679ab21462SChristian König struct amdgpu_gart *gtt, 6689ab21462SChristian König uint32_t gtt_flags, 669d38ceaf9SAlex Deucher struct amdgpu_vm *vm, 670d38ceaf9SAlex Deucher struct amdgpu_ib *ib, 671d38ceaf9SAlex Deucher uint64_t start, uint64_t end, 6729ab21462SChristian König uint64_t dst, uint32_t flags) 673d38ceaf9SAlex Deucher { 67431f6c1feSChristian König const uint64_t mask = AMDGPU_VM_PTE_COUNT - 1; 67531f6c1feSChristian König 67631f6c1feSChristian König uint64_t last_pe_start = ~0, last_pe_end = ~0, last_dst = ~0; 677d38ceaf9SAlex Deucher uint64_t addr; 678d38ceaf9SAlex Deucher 679d38ceaf9SAlex Deucher /* walk over the address space and update the page tables */ 680d38ceaf9SAlex Deucher for (addr = start; addr < end; ) { 681d38ceaf9SAlex Deucher uint64_t pt_idx = addr >> amdgpu_vm_block_size; 682ee1782c3SChristian König struct amdgpu_bo *pt = vm->page_tables[pt_idx].entry.robj; 683d38ceaf9SAlex Deucher unsigned nptes; 68431f6c1feSChristian König uint64_t pe_start; 685d38ceaf9SAlex Deucher 686d38ceaf9SAlex Deucher if ((addr & ~mask) == (end & ~mask)) 687d38ceaf9SAlex Deucher nptes = end - addr; 688d38ceaf9SAlex Deucher else 689d38ceaf9SAlex Deucher nptes = AMDGPU_VM_PTE_COUNT - (addr & mask); 690d38ceaf9SAlex Deucher 69131f6c1feSChristian König pe_start = amdgpu_bo_gpu_offset(pt); 69231f6c1feSChristian König pe_start += (addr & mask) * 8; 693d38ceaf9SAlex Deucher 69431f6c1feSChristian König if (last_pe_end != pe_start) { 695d38ceaf9SAlex Deucher 6969ab21462SChristian König amdgpu_vm_frag_ptes(adev, gtt, gtt_flags, ib, 69731f6c1feSChristian König last_pe_start, last_pe_end, 6989ab21462SChristian König last_dst, flags); 699d38ceaf9SAlex Deucher 70031f6c1feSChristian König last_pe_start = pe_start; 70131f6c1feSChristian König last_pe_end = pe_start + 8 * nptes; 702d38ceaf9SAlex Deucher last_dst = dst; 703d38ceaf9SAlex Deucher } else { 70431f6c1feSChristian König last_pe_end += 8 * nptes; 705d38ceaf9SAlex Deucher } 706d38ceaf9SAlex Deucher 707d38ceaf9SAlex Deucher addr += nptes; 708d38ceaf9SAlex Deucher dst += nptes * AMDGPU_GPU_PAGE_SIZE; 709d38ceaf9SAlex Deucher } 710d38ceaf9SAlex Deucher 7119ab21462SChristian König amdgpu_vm_frag_ptes(adev, gtt, gtt_flags, ib, 71231f6c1feSChristian König last_pe_start, last_pe_end, 7139ab21462SChristian König last_dst, flags); 714d38ceaf9SAlex Deucher } 715d38ceaf9SAlex Deucher 716d38ceaf9SAlex Deucher /** 717d38ceaf9SAlex Deucher * amdgpu_vm_bo_update_mapping - update a mapping in the vm page table 718d38ceaf9SAlex Deucher * 719d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 7209ab21462SChristian König * @gtt: GART instance to use for mapping 721d38ceaf9SAlex Deucher * @gtt_flags: flags as they are used for GTT 722a14faa65SChristian König * @vm: requested vm 723a14faa65SChristian König * @start: start of mapped range 724a14faa65SChristian König * @last: last mapped entry 725a14faa65SChristian König * @flags: flags for the entries 726a14faa65SChristian König * @addr: addr to set the area to 727d38ceaf9SAlex Deucher * @fence: optional resulting fence 728d38ceaf9SAlex Deucher * 729a14faa65SChristian König * Fill in the page table entries between @start and @last. 730d38ceaf9SAlex Deucher * Returns 0 for success, -EINVAL for failure. 731d38ceaf9SAlex Deucher */ 732d38ceaf9SAlex Deucher static int amdgpu_vm_bo_update_mapping(struct amdgpu_device *adev, 7339ab21462SChristian König struct amdgpu_gart *gtt, 7349ab21462SChristian König uint32_t gtt_flags, 735d38ceaf9SAlex Deucher struct amdgpu_vm *vm, 736a14faa65SChristian König uint64_t start, uint64_t last, 737a14faa65SChristian König uint32_t flags, uint64_t addr, 738a14faa65SChristian König struct fence **fence) 739d38ceaf9SAlex Deucher { 7402d55e45aSChristian König struct amdgpu_ring *ring; 741a1e08d3bSChristian König void *owner = AMDGPU_FENCE_OWNER_VM; 742d38ceaf9SAlex Deucher unsigned nptes, ncmds, ndw; 743d71518b5SChristian König struct amdgpu_job *job; 744d5fc5e82SChunming Zhou struct amdgpu_ib *ib; 7454af9f07cSChunming Zhou struct fence *f = NULL; 746d38ceaf9SAlex Deucher int r; 747d38ceaf9SAlex Deucher 7482d55e45aSChristian König ring = container_of(vm->entity.sched, struct amdgpu_ring, sched); 7492d55e45aSChristian König 750a1e08d3bSChristian König /* sync to everything on unmapping */ 751a1e08d3bSChristian König if (!(flags & AMDGPU_PTE_VALID)) 752a1e08d3bSChristian König owner = AMDGPU_FENCE_OWNER_UNDEFINED; 753a1e08d3bSChristian König 754a14faa65SChristian König nptes = last - start + 1; 755d38ceaf9SAlex Deucher 756d38ceaf9SAlex Deucher /* 757d38ceaf9SAlex Deucher * reserve space for one command every (1 << BLOCK_SIZE) 758d38ceaf9SAlex Deucher * entries or 2k dwords (whatever is smaller) 759d38ceaf9SAlex Deucher */ 760d38ceaf9SAlex Deucher ncmds = (nptes >> min(amdgpu_vm_block_size, 11)) + 1; 761d38ceaf9SAlex Deucher 762d38ceaf9SAlex Deucher /* padding, etc. */ 763d38ceaf9SAlex Deucher ndw = 64; 764d38ceaf9SAlex Deucher 7659ab21462SChristian König if ((gtt == &adev->gart) && (flags == gtt_flags)) { 766d38ceaf9SAlex Deucher /* only copy commands needed */ 767d38ceaf9SAlex Deucher ndw += ncmds * 7; 768d38ceaf9SAlex Deucher 7699ab21462SChristian König } else if (gtt) { 770d38ceaf9SAlex Deucher /* header for write data commands */ 771d38ceaf9SAlex Deucher ndw += ncmds * 4; 772d38ceaf9SAlex Deucher 773d38ceaf9SAlex Deucher /* body of write data command */ 774d38ceaf9SAlex Deucher ndw += nptes * 2; 775d38ceaf9SAlex Deucher 776d38ceaf9SAlex Deucher } else { 777d38ceaf9SAlex Deucher /* set page commands needed */ 778d38ceaf9SAlex Deucher ndw += ncmds * 10; 779d38ceaf9SAlex Deucher 780d38ceaf9SAlex Deucher /* two extra commands for begin/end of fragment */ 781d38ceaf9SAlex Deucher ndw += 2 * 10; 782d38ceaf9SAlex Deucher } 783d38ceaf9SAlex Deucher 784d71518b5SChristian König r = amdgpu_job_alloc_with_ib(adev, ndw * 4, &job); 785d71518b5SChristian König if (r) 786d38ceaf9SAlex Deucher return r; 787d71518b5SChristian König 788d71518b5SChristian König ib = &job->ibs[0]; 789d5fc5e82SChunming Zhou 790e86f9ceeSChristian König r = amdgpu_sync_resv(adev, &job->sync, vm->page_directory->tbo.resv, 791a1e08d3bSChristian König owner); 792a1e08d3bSChristian König if (r) 793a1e08d3bSChristian König goto error_free; 794d38ceaf9SAlex Deucher 795a1e08d3bSChristian König r = reservation_object_reserve_shared(vm->page_directory->tbo.resv); 796a1e08d3bSChristian König if (r) 797a1e08d3bSChristian König goto error_free; 798a1e08d3bSChristian König 799a1e08d3bSChristian König amdgpu_vm_update_ptes(adev, gtt, gtt_flags, vm, ib, start, last + 1, 800a1e08d3bSChristian König addr, flags); 801d38ceaf9SAlex Deucher 8029e5d5309SChristian König amdgpu_ring_pad_ib(ring, ib); 803d5fc5e82SChunming Zhou WARN_ON(ib->length_dw > ndw); 8042bd9ccfaSChristian König r = amdgpu_job_submit(job, ring, &vm->entity, 8052bd9ccfaSChristian König AMDGPU_FENCE_OWNER_VM, &f); 806d5fc5e82SChunming Zhou if (r) 8074af9f07cSChunming Zhou goto error_free; 808d5fc5e82SChunming Zhou 809bf60efd3SChristian König amdgpu_bo_fence(vm->page_directory, f, true); 810d38ceaf9SAlex Deucher if (fence) { 811bb1e38a4SChunming Zhou fence_put(*fence); 8124af9f07cSChunming Zhou *fence = fence_get(f); 813d38ceaf9SAlex Deucher } 814281b4223SChunming Zhou fence_put(f); 815d38ceaf9SAlex Deucher return 0; 816d5fc5e82SChunming Zhou 817d5fc5e82SChunming Zhou error_free: 818d71518b5SChristian König amdgpu_job_free(job); 8194af9f07cSChunming Zhou return r; 820d38ceaf9SAlex Deucher } 821d38ceaf9SAlex Deucher 822d38ceaf9SAlex Deucher /** 823a14faa65SChristian König * amdgpu_vm_bo_split_mapping - split a mapping into smaller chunks 824a14faa65SChristian König * 825a14faa65SChristian König * @adev: amdgpu_device pointer 826a14faa65SChristian König * @gtt: GART instance to use for mapping 827a14faa65SChristian König * @vm: requested vm 828a14faa65SChristian König * @mapping: mapped range and flags to use for the update 829a14faa65SChristian König * @addr: addr to set the area to 830a14faa65SChristian König * @gtt_flags: flags as they are used for GTT 831a14faa65SChristian König * @fence: optional resulting fence 832a14faa65SChristian König * 833a14faa65SChristian König * Split the mapping into smaller chunks so that each update fits 834a14faa65SChristian König * into a SDMA IB. 835a14faa65SChristian König * Returns 0 for success, -EINVAL for failure. 836a14faa65SChristian König */ 837a14faa65SChristian König static int amdgpu_vm_bo_split_mapping(struct amdgpu_device *adev, 838a14faa65SChristian König struct amdgpu_gart *gtt, 839a14faa65SChristian König uint32_t gtt_flags, 840a14faa65SChristian König struct amdgpu_vm *vm, 841a14faa65SChristian König struct amdgpu_bo_va_mapping *mapping, 842a14faa65SChristian König uint64_t addr, struct fence **fence) 843a14faa65SChristian König { 844a14faa65SChristian König const uint64_t max_size = 64ULL * 1024ULL * 1024ULL / AMDGPU_GPU_PAGE_SIZE; 845a14faa65SChristian König 846a14faa65SChristian König uint64_t start = mapping->it.start; 847a14faa65SChristian König uint32_t flags = gtt_flags; 848a14faa65SChristian König int r; 849a14faa65SChristian König 850a14faa65SChristian König /* normally,bo_va->flags only contians READABLE and WIRTEABLE bit go here 851a14faa65SChristian König * but in case of something, we filter the flags in first place 852a14faa65SChristian König */ 853a14faa65SChristian König if (!(mapping->flags & AMDGPU_PTE_READABLE)) 854a14faa65SChristian König flags &= ~AMDGPU_PTE_READABLE; 855a14faa65SChristian König if (!(mapping->flags & AMDGPU_PTE_WRITEABLE)) 856a14faa65SChristian König flags &= ~AMDGPU_PTE_WRITEABLE; 857a14faa65SChristian König 858a14faa65SChristian König trace_amdgpu_vm_bo_update(mapping); 859a14faa65SChristian König 860a14faa65SChristian König addr += mapping->offset; 861a14faa65SChristian König 862a14faa65SChristian König if (!gtt || ((gtt == &adev->gart) && (flags == gtt_flags))) 863a14faa65SChristian König return amdgpu_vm_bo_update_mapping(adev, gtt, gtt_flags, vm, 864a14faa65SChristian König start, mapping->it.last, 865a14faa65SChristian König flags, addr, fence); 866a14faa65SChristian König 867a14faa65SChristian König while (start != mapping->it.last + 1) { 868a14faa65SChristian König uint64_t last; 869a14faa65SChristian König 870fb29b57cSFelix Kuehling last = min((uint64_t)mapping->it.last, start + max_size - 1); 871a14faa65SChristian König r = amdgpu_vm_bo_update_mapping(adev, gtt, gtt_flags, vm, 872a14faa65SChristian König start, last, flags, addr, 873a14faa65SChristian König fence); 874a14faa65SChristian König if (r) 875a14faa65SChristian König return r; 876a14faa65SChristian König 877a14faa65SChristian König start = last + 1; 878fb29b57cSFelix Kuehling addr += max_size * AMDGPU_GPU_PAGE_SIZE; 879a14faa65SChristian König } 880a14faa65SChristian König 881a14faa65SChristian König return 0; 882a14faa65SChristian König } 883a14faa65SChristian König 884a14faa65SChristian König /** 885d38ceaf9SAlex Deucher * amdgpu_vm_bo_update - update all BO mappings in the vm page table 886d38ceaf9SAlex Deucher * 887d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 888d38ceaf9SAlex Deucher * @bo_va: requested BO and VM object 889d38ceaf9SAlex Deucher * @mem: ttm mem 890d38ceaf9SAlex Deucher * 891d38ceaf9SAlex Deucher * Fill in the page table entries for @bo_va. 892d38ceaf9SAlex Deucher * Returns 0 for success, -EINVAL for failure. 893d38ceaf9SAlex Deucher * 894d38ceaf9SAlex Deucher * Object have to be reserved and mutex must be locked! 895d38ceaf9SAlex Deucher */ 896d38ceaf9SAlex Deucher int amdgpu_vm_bo_update(struct amdgpu_device *adev, 897d38ceaf9SAlex Deucher struct amdgpu_bo_va *bo_va, 898d38ceaf9SAlex Deucher struct ttm_mem_reg *mem) 899d38ceaf9SAlex Deucher { 900d38ceaf9SAlex Deucher struct amdgpu_vm *vm = bo_va->vm; 901d38ceaf9SAlex Deucher struct amdgpu_bo_va_mapping *mapping; 9029ab21462SChristian König struct amdgpu_gart *gtt = NULL; 903d38ceaf9SAlex Deucher uint32_t flags; 904d38ceaf9SAlex Deucher uint64_t addr; 905d38ceaf9SAlex Deucher int r; 906d38ceaf9SAlex Deucher 907d38ceaf9SAlex Deucher if (mem) { 908b7d698d7SChristian König addr = (u64)mem->start << PAGE_SHIFT; 9099ab21462SChristian König switch (mem->mem_type) { 9109ab21462SChristian König case TTM_PL_TT: 9119ab21462SChristian König gtt = &bo_va->bo->adev->gart; 9129ab21462SChristian König break; 9139ab21462SChristian König 9149ab21462SChristian König case TTM_PL_VRAM: 915d38ceaf9SAlex Deucher addr += adev->vm_manager.vram_base_offset; 9169ab21462SChristian König break; 9179ab21462SChristian König 9189ab21462SChristian König default: 9199ab21462SChristian König break; 9209ab21462SChristian König } 921d38ceaf9SAlex Deucher } else { 922d38ceaf9SAlex Deucher addr = 0; 923d38ceaf9SAlex Deucher } 924d38ceaf9SAlex Deucher 925d38ceaf9SAlex Deucher flags = amdgpu_ttm_tt_pte_flags(adev, bo_va->bo->tbo.ttm, mem); 926d38ceaf9SAlex Deucher 9277fc11959SChristian König spin_lock(&vm->status_lock); 9287fc11959SChristian König if (!list_empty(&bo_va->vm_status)) 9297fc11959SChristian König list_splice_init(&bo_va->valids, &bo_va->invalids); 9307fc11959SChristian König spin_unlock(&vm->status_lock); 9317fc11959SChristian König 9327fc11959SChristian König list_for_each_entry(mapping, &bo_va->invalids, list) { 933a14faa65SChristian König r = amdgpu_vm_bo_split_mapping(adev, gtt, flags, vm, mapping, addr, 9349ab21462SChristian König &bo_va->last_pt_update); 935d38ceaf9SAlex Deucher if (r) 936d38ceaf9SAlex Deucher return r; 937d38ceaf9SAlex Deucher } 938d38ceaf9SAlex Deucher 939d6c10f6bSChristian König if (trace_amdgpu_vm_bo_mapping_enabled()) { 940d6c10f6bSChristian König list_for_each_entry(mapping, &bo_va->valids, list) 941d6c10f6bSChristian König trace_amdgpu_vm_bo_mapping(mapping); 942d6c10f6bSChristian König 943d6c10f6bSChristian König list_for_each_entry(mapping, &bo_va->invalids, list) 944d6c10f6bSChristian König trace_amdgpu_vm_bo_mapping(mapping); 945d6c10f6bSChristian König } 946d6c10f6bSChristian König 947d38ceaf9SAlex Deucher spin_lock(&vm->status_lock); 9486d1d0ef7Smonk.liu list_splice_init(&bo_va->invalids, &bo_va->valids); 949d38ceaf9SAlex Deucher list_del_init(&bo_va->vm_status); 9507fc11959SChristian König if (!mem) 9517fc11959SChristian König list_add(&bo_va->vm_status, &vm->cleared); 952d38ceaf9SAlex Deucher spin_unlock(&vm->status_lock); 953d38ceaf9SAlex Deucher 954d38ceaf9SAlex Deucher return 0; 955d38ceaf9SAlex Deucher } 956d38ceaf9SAlex Deucher 957d38ceaf9SAlex Deucher /** 958d38ceaf9SAlex Deucher * amdgpu_vm_clear_freed - clear freed BOs in the PT 959d38ceaf9SAlex Deucher * 960d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 961d38ceaf9SAlex Deucher * @vm: requested vm 962d38ceaf9SAlex Deucher * 963d38ceaf9SAlex Deucher * Make sure all freed BOs are cleared in the PT. 964d38ceaf9SAlex Deucher * Returns 0 for success. 965d38ceaf9SAlex Deucher * 966d38ceaf9SAlex Deucher * PTs have to be reserved and mutex must be locked! 967d38ceaf9SAlex Deucher */ 968d38ceaf9SAlex Deucher int amdgpu_vm_clear_freed(struct amdgpu_device *adev, 969d38ceaf9SAlex Deucher struct amdgpu_vm *vm) 970d38ceaf9SAlex Deucher { 971d38ceaf9SAlex Deucher struct amdgpu_bo_va_mapping *mapping; 972d38ceaf9SAlex Deucher int r; 973d38ceaf9SAlex Deucher 974d38ceaf9SAlex Deucher while (!list_empty(&vm->freed)) { 975d38ceaf9SAlex Deucher mapping = list_first_entry(&vm->freed, 976d38ceaf9SAlex Deucher struct amdgpu_bo_va_mapping, list); 977d38ceaf9SAlex Deucher list_del(&mapping->list); 978*e17841b9SChristian König 979a14faa65SChristian König r = amdgpu_vm_bo_split_mapping(adev, NULL, 0, vm, mapping, 9809ab21462SChristian König 0, NULL); 981d38ceaf9SAlex Deucher kfree(mapping); 982d38ceaf9SAlex Deucher if (r) 983d38ceaf9SAlex Deucher return r; 984d38ceaf9SAlex Deucher 985d38ceaf9SAlex Deucher } 986d38ceaf9SAlex Deucher return 0; 987d38ceaf9SAlex Deucher 988d38ceaf9SAlex Deucher } 989d38ceaf9SAlex Deucher 990d38ceaf9SAlex Deucher /** 991d38ceaf9SAlex Deucher * amdgpu_vm_clear_invalids - clear invalidated BOs in the PT 992d38ceaf9SAlex Deucher * 993d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 994d38ceaf9SAlex Deucher * @vm: requested vm 995d38ceaf9SAlex Deucher * 996d38ceaf9SAlex Deucher * Make sure all invalidated BOs are cleared in the PT. 997d38ceaf9SAlex Deucher * Returns 0 for success. 998d38ceaf9SAlex Deucher * 999d38ceaf9SAlex Deucher * PTs have to be reserved and mutex must be locked! 1000d38ceaf9SAlex Deucher */ 1001d38ceaf9SAlex Deucher int amdgpu_vm_clear_invalids(struct amdgpu_device *adev, 1002cfe2c978Smonk.liu struct amdgpu_vm *vm, struct amdgpu_sync *sync) 1003d38ceaf9SAlex Deucher { 1004cfe2c978Smonk.liu struct amdgpu_bo_va *bo_va = NULL; 100591e1a520SChristian König int r = 0; 1006d38ceaf9SAlex Deucher 1007d38ceaf9SAlex Deucher spin_lock(&vm->status_lock); 1008d38ceaf9SAlex Deucher while (!list_empty(&vm->invalidated)) { 1009d38ceaf9SAlex Deucher bo_va = list_first_entry(&vm->invalidated, 1010d38ceaf9SAlex Deucher struct amdgpu_bo_va, vm_status); 1011d38ceaf9SAlex Deucher spin_unlock(&vm->status_lock); 101269b576a1SChunming Zhou mutex_lock(&bo_va->mutex); 1013d38ceaf9SAlex Deucher r = amdgpu_vm_bo_update(adev, bo_va, NULL); 101469b576a1SChunming Zhou mutex_unlock(&bo_va->mutex); 1015d38ceaf9SAlex Deucher if (r) 1016d38ceaf9SAlex Deucher return r; 1017d38ceaf9SAlex Deucher 1018d38ceaf9SAlex Deucher spin_lock(&vm->status_lock); 1019d38ceaf9SAlex Deucher } 1020d38ceaf9SAlex Deucher spin_unlock(&vm->status_lock); 1021d38ceaf9SAlex Deucher 1022cfe2c978Smonk.liu if (bo_va) 1023bb1e38a4SChunming Zhou r = amdgpu_sync_fence(adev, sync, bo_va->last_pt_update); 102491e1a520SChristian König 102591e1a520SChristian König return r; 1026d38ceaf9SAlex Deucher } 1027d38ceaf9SAlex Deucher 1028d38ceaf9SAlex Deucher /** 1029d38ceaf9SAlex Deucher * amdgpu_vm_bo_add - add a bo to a specific vm 1030d38ceaf9SAlex Deucher * 1031d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 1032d38ceaf9SAlex Deucher * @vm: requested vm 1033d38ceaf9SAlex Deucher * @bo: amdgpu buffer object 1034d38ceaf9SAlex Deucher * 10358843dbbbSChristian König * Add @bo into the requested vm. 1036d38ceaf9SAlex Deucher * Add @bo to the list of bos associated with the vm 1037d38ceaf9SAlex Deucher * Returns newly added bo_va or NULL for failure 1038d38ceaf9SAlex Deucher * 1039d38ceaf9SAlex Deucher * Object has to be reserved! 1040d38ceaf9SAlex Deucher */ 1041d38ceaf9SAlex Deucher struct amdgpu_bo_va *amdgpu_vm_bo_add(struct amdgpu_device *adev, 1042d38ceaf9SAlex Deucher struct amdgpu_vm *vm, 1043d38ceaf9SAlex Deucher struct amdgpu_bo *bo) 1044d38ceaf9SAlex Deucher { 1045d38ceaf9SAlex Deucher struct amdgpu_bo_va *bo_va; 1046d38ceaf9SAlex Deucher 1047d38ceaf9SAlex Deucher bo_va = kzalloc(sizeof(struct amdgpu_bo_va), GFP_KERNEL); 1048d38ceaf9SAlex Deucher if (bo_va == NULL) { 1049d38ceaf9SAlex Deucher return NULL; 1050d38ceaf9SAlex Deucher } 1051d38ceaf9SAlex Deucher bo_va->vm = vm; 1052d38ceaf9SAlex Deucher bo_va->bo = bo; 1053d38ceaf9SAlex Deucher bo_va->ref_count = 1; 1054d38ceaf9SAlex Deucher INIT_LIST_HEAD(&bo_va->bo_list); 10557fc11959SChristian König INIT_LIST_HEAD(&bo_va->valids); 10567fc11959SChristian König INIT_LIST_HEAD(&bo_va->invalids); 1057d38ceaf9SAlex Deucher INIT_LIST_HEAD(&bo_va->vm_status); 105869b576a1SChunming Zhou mutex_init(&bo_va->mutex); 1059d38ceaf9SAlex Deucher list_add_tail(&bo_va->bo_list, &bo->va); 1060d38ceaf9SAlex Deucher 1061d38ceaf9SAlex Deucher return bo_va; 1062d38ceaf9SAlex Deucher } 1063d38ceaf9SAlex Deucher 1064d38ceaf9SAlex Deucher /** 1065d38ceaf9SAlex Deucher * amdgpu_vm_bo_map - map bo inside a vm 1066d38ceaf9SAlex Deucher * 1067d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 1068d38ceaf9SAlex Deucher * @bo_va: bo_va to store the address 1069d38ceaf9SAlex Deucher * @saddr: where to map the BO 1070d38ceaf9SAlex Deucher * @offset: requested offset in the BO 1071d38ceaf9SAlex Deucher * @flags: attributes of pages (read/write/valid/etc.) 1072d38ceaf9SAlex Deucher * 1073d38ceaf9SAlex Deucher * Add a mapping of the BO at the specefied addr into the VM. 1074d38ceaf9SAlex Deucher * Returns 0 for success, error for failure. 1075d38ceaf9SAlex Deucher * 107649b02b18SChunming Zhou * Object has to be reserved and unreserved outside! 1077d38ceaf9SAlex Deucher */ 1078d38ceaf9SAlex Deucher int amdgpu_vm_bo_map(struct amdgpu_device *adev, 1079d38ceaf9SAlex Deucher struct amdgpu_bo_va *bo_va, 1080d38ceaf9SAlex Deucher uint64_t saddr, uint64_t offset, 1081d38ceaf9SAlex Deucher uint64_t size, uint32_t flags) 1082d38ceaf9SAlex Deucher { 1083d38ceaf9SAlex Deucher struct amdgpu_bo_va_mapping *mapping; 1084d38ceaf9SAlex Deucher struct amdgpu_vm *vm = bo_va->vm; 1085d38ceaf9SAlex Deucher struct interval_tree_node *it; 1086d38ceaf9SAlex Deucher unsigned last_pfn, pt_idx; 1087d38ceaf9SAlex Deucher uint64_t eaddr; 1088d38ceaf9SAlex Deucher int r; 1089d38ceaf9SAlex Deucher 10900be52de9SChristian König /* validate the parameters */ 10910be52de9SChristian König if (saddr & AMDGPU_GPU_PAGE_MASK || offset & AMDGPU_GPU_PAGE_MASK || 109249b02b18SChunming Zhou size == 0 || size & AMDGPU_GPU_PAGE_MASK) 10930be52de9SChristian König return -EINVAL; 10940be52de9SChristian König 1095d38ceaf9SAlex Deucher /* make sure object fit at this offset */ 1096005ae95eSFelix Kuehling eaddr = saddr + size - 1; 109749b02b18SChunming Zhou if ((saddr >= eaddr) || (offset + size > amdgpu_bo_size(bo_va->bo))) 1098d38ceaf9SAlex Deucher return -EINVAL; 1099d38ceaf9SAlex Deucher 1100d38ceaf9SAlex Deucher last_pfn = eaddr / AMDGPU_GPU_PAGE_SIZE; 1101005ae95eSFelix Kuehling if (last_pfn >= adev->vm_manager.max_pfn) { 1102005ae95eSFelix Kuehling dev_err(adev->dev, "va above limit (0x%08X >= 0x%08X)\n", 1103d38ceaf9SAlex Deucher last_pfn, adev->vm_manager.max_pfn); 1104d38ceaf9SAlex Deucher return -EINVAL; 1105d38ceaf9SAlex Deucher } 1106d38ceaf9SAlex Deucher 1107d38ceaf9SAlex Deucher saddr /= AMDGPU_GPU_PAGE_SIZE; 1108d38ceaf9SAlex Deucher eaddr /= AMDGPU_GPU_PAGE_SIZE; 1109d38ceaf9SAlex Deucher 1110c25867dfSChunming Zhou spin_lock(&vm->it_lock); 1111005ae95eSFelix Kuehling it = interval_tree_iter_first(&vm->va, saddr, eaddr); 1112c25867dfSChunming Zhou spin_unlock(&vm->it_lock); 1113d38ceaf9SAlex Deucher if (it) { 1114d38ceaf9SAlex Deucher struct amdgpu_bo_va_mapping *tmp; 1115d38ceaf9SAlex Deucher tmp = container_of(it, struct amdgpu_bo_va_mapping, it); 1116d38ceaf9SAlex Deucher /* bo and tmp overlap, invalid addr */ 1117d38ceaf9SAlex Deucher dev_err(adev->dev, "bo %p va 0x%010Lx-0x%010Lx conflict with " 1118d38ceaf9SAlex Deucher "0x%010lx-0x%010lx\n", bo_va->bo, saddr, eaddr, 1119d38ceaf9SAlex Deucher tmp->it.start, tmp->it.last + 1); 1120d38ceaf9SAlex Deucher r = -EINVAL; 1121f48b2659SChunming Zhou goto error; 1122d38ceaf9SAlex Deucher } 1123d38ceaf9SAlex Deucher 1124d38ceaf9SAlex Deucher mapping = kmalloc(sizeof(*mapping), GFP_KERNEL); 1125d38ceaf9SAlex Deucher if (!mapping) { 1126d38ceaf9SAlex Deucher r = -ENOMEM; 1127f48b2659SChunming Zhou goto error; 1128d38ceaf9SAlex Deucher } 1129d38ceaf9SAlex Deucher 1130d38ceaf9SAlex Deucher INIT_LIST_HEAD(&mapping->list); 1131d38ceaf9SAlex Deucher mapping->it.start = saddr; 1132005ae95eSFelix Kuehling mapping->it.last = eaddr; 1133d38ceaf9SAlex Deucher mapping->offset = offset; 1134d38ceaf9SAlex Deucher mapping->flags = flags; 1135d38ceaf9SAlex Deucher 113669b576a1SChunming Zhou mutex_lock(&bo_va->mutex); 11377fc11959SChristian König list_add(&mapping->list, &bo_va->invalids); 113869b576a1SChunming Zhou mutex_unlock(&bo_va->mutex); 1139c25867dfSChunming Zhou spin_lock(&vm->it_lock); 1140d38ceaf9SAlex Deucher interval_tree_insert(&mapping->it, &vm->va); 1141c25867dfSChunming Zhou spin_unlock(&vm->it_lock); 114293e3e438SChristian König trace_amdgpu_vm_bo_map(bo_va, mapping); 1143d38ceaf9SAlex Deucher 1144d38ceaf9SAlex Deucher /* Make sure the page tables are allocated */ 1145d38ceaf9SAlex Deucher saddr >>= amdgpu_vm_block_size; 1146d38ceaf9SAlex Deucher eaddr >>= amdgpu_vm_block_size; 1147d38ceaf9SAlex Deucher 1148d38ceaf9SAlex Deucher BUG_ON(eaddr >= amdgpu_vm_num_pdes(adev)); 1149d38ceaf9SAlex Deucher 1150d38ceaf9SAlex Deucher if (eaddr > vm->max_pde_used) 1151d38ceaf9SAlex Deucher vm->max_pde_used = eaddr; 1152d38ceaf9SAlex Deucher 1153d38ceaf9SAlex Deucher /* walk over the address space and allocate the page tables */ 1154d38ceaf9SAlex Deucher for (pt_idx = saddr; pt_idx <= eaddr; ++pt_idx) { 1155bf60efd3SChristian König struct reservation_object *resv = vm->page_directory->tbo.resv; 1156ee1782c3SChristian König struct amdgpu_bo_list_entry *entry; 1157d38ceaf9SAlex Deucher struct amdgpu_bo *pt; 1158d38ceaf9SAlex Deucher 1159ee1782c3SChristian König entry = &vm->page_tables[pt_idx].entry; 1160ee1782c3SChristian König if (entry->robj) 1161d38ceaf9SAlex Deucher continue; 1162d38ceaf9SAlex Deucher 1163d38ceaf9SAlex Deucher r = amdgpu_bo_create(adev, AMDGPU_VM_PTE_COUNT * 8, 1164d38ceaf9SAlex Deucher AMDGPU_GPU_PAGE_SIZE, true, 1165857d913dSAlex Deucher AMDGPU_GEM_DOMAIN_VRAM, 1166857d913dSAlex Deucher AMDGPU_GEM_CREATE_NO_CPU_ACCESS, 1167bf60efd3SChristian König NULL, resv, &pt); 116849b02b18SChunming Zhou if (r) 1169d38ceaf9SAlex Deucher goto error_free; 117049b02b18SChunming Zhou 117182b9c55bSChristian König /* Keep a reference to the page table to avoid freeing 117282b9c55bSChristian König * them up in the wrong order. 117382b9c55bSChristian König */ 117482b9c55bSChristian König pt->parent = amdgpu_bo_ref(vm->page_directory); 117582b9c55bSChristian König 11762bd9ccfaSChristian König r = amdgpu_vm_clear_bo(adev, vm, pt); 1177d38ceaf9SAlex Deucher if (r) { 1178d38ceaf9SAlex Deucher amdgpu_bo_unref(&pt); 1179d38ceaf9SAlex Deucher goto error_free; 1180d38ceaf9SAlex Deucher } 1181d38ceaf9SAlex Deucher 1182ee1782c3SChristian König entry->robj = pt; 1183ee1782c3SChristian König entry->priority = 0; 1184ee1782c3SChristian König entry->tv.bo = &entry->robj->tbo; 1185ee1782c3SChristian König entry->tv.shared = true; 11862f568dbdSChristian König entry->user_pages = NULL; 1187d38ceaf9SAlex Deucher vm->page_tables[pt_idx].addr = 0; 1188d38ceaf9SAlex Deucher } 1189d38ceaf9SAlex Deucher 1190d38ceaf9SAlex Deucher return 0; 1191d38ceaf9SAlex Deucher 1192d38ceaf9SAlex Deucher error_free: 1193d38ceaf9SAlex Deucher list_del(&mapping->list); 1194c25867dfSChunming Zhou spin_lock(&vm->it_lock); 1195d38ceaf9SAlex Deucher interval_tree_remove(&mapping->it, &vm->va); 1196c25867dfSChunming Zhou spin_unlock(&vm->it_lock); 119793e3e438SChristian König trace_amdgpu_vm_bo_unmap(bo_va, mapping); 1198d38ceaf9SAlex Deucher kfree(mapping); 1199d38ceaf9SAlex Deucher 1200f48b2659SChunming Zhou error: 1201d38ceaf9SAlex Deucher return r; 1202d38ceaf9SAlex Deucher } 1203d38ceaf9SAlex Deucher 1204d38ceaf9SAlex Deucher /** 1205d38ceaf9SAlex Deucher * amdgpu_vm_bo_unmap - remove bo mapping from vm 1206d38ceaf9SAlex Deucher * 1207d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 1208d38ceaf9SAlex Deucher * @bo_va: bo_va to remove the address from 1209d38ceaf9SAlex Deucher * @saddr: where to the BO is mapped 1210d38ceaf9SAlex Deucher * 1211d38ceaf9SAlex Deucher * Remove a mapping of the BO at the specefied addr from the VM. 1212d38ceaf9SAlex Deucher * Returns 0 for success, error for failure. 1213d38ceaf9SAlex Deucher * 121449b02b18SChunming Zhou * Object has to be reserved and unreserved outside! 1215d38ceaf9SAlex Deucher */ 1216d38ceaf9SAlex Deucher int amdgpu_vm_bo_unmap(struct amdgpu_device *adev, 1217d38ceaf9SAlex Deucher struct amdgpu_bo_va *bo_va, 1218d38ceaf9SAlex Deucher uint64_t saddr) 1219d38ceaf9SAlex Deucher { 1220d38ceaf9SAlex Deucher struct amdgpu_bo_va_mapping *mapping; 1221d38ceaf9SAlex Deucher struct amdgpu_vm *vm = bo_va->vm; 12227fc11959SChristian König bool valid = true; 1223d38ceaf9SAlex Deucher 12246c7fc503SChristian König saddr /= AMDGPU_GPU_PAGE_SIZE; 122569b576a1SChunming Zhou mutex_lock(&bo_va->mutex); 12267fc11959SChristian König list_for_each_entry(mapping, &bo_va->valids, list) { 1227d38ceaf9SAlex Deucher if (mapping->it.start == saddr) 1228d38ceaf9SAlex Deucher break; 1229d38ceaf9SAlex Deucher } 1230d38ceaf9SAlex Deucher 12317fc11959SChristian König if (&mapping->list == &bo_va->valids) { 12327fc11959SChristian König valid = false; 12337fc11959SChristian König 12347fc11959SChristian König list_for_each_entry(mapping, &bo_va->invalids, list) { 12357fc11959SChristian König if (mapping->it.start == saddr) 12367fc11959SChristian König break; 12377fc11959SChristian König } 12387fc11959SChristian König 123969b576a1SChunming Zhou if (&mapping->list == &bo_va->invalids) { 124069b576a1SChunming Zhou mutex_unlock(&bo_va->mutex); 1241d38ceaf9SAlex Deucher return -ENOENT; 1242d38ceaf9SAlex Deucher } 124369b576a1SChunming Zhou } 124469b576a1SChunming Zhou mutex_unlock(&bo_va->mutex); 1245d38ceaf9SAlex Deucher list_del(&mapping->list); 1246c25867dfSChunming Zhou spin_lock(&vm->it_lock); 1247d38ceaf9SAlex Deucher interval_tree_remove(&mapping->it, &vm->va); 1248c25867dfSChunming Zhou spin_unlock(&vm->it_lock); 124993e3e438SChristian König trace_amdgpu_vm_bo_unmap(bo_va, mapping); 1250d38ceaf9SAlex Deucher 1251*e17841b9SChristian König if (valid) 1252d38ceaf9SAlex Deucher list_add(&mapping->list, &vm->freed); 1253*e17841b9SChristian König else 1254d38ceaf9SAlex Deucher kfree(mapping); 1255d38ceaf9SAlex Deucher 1256d38ceaf9SAlex Deucher return 0; 1257d38ceaf9SAlex Deucher } 1258d38ceaf9SAlex Deucher 1259d38ceaf9SAlex Deucher /** 1260d38ceaf9SAlex Deucher * amdgpu_vm_bo_rmv - remove a bo to a specific vm 1261d38ceaf9SAlex Deucher * 1262d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 1263d38ceaf9SAlex Deucher * @bo_va: requested bo_va 1264d38ceaf9SAlex Deucher * 12658843dbbbSChristian König * Remove @bo_va->bo from the requested vm. 1266d38ceaf9SAlex Deucher * 1267d38ceaf9SAlex Deucher * Object have to be reserved! 1268d38ceaf9SAlex Deucher */ 1269d38ceaf9SAlex Deucher void amdgpu_vm_bo_rmv(struct amdgpu_device *adev, 1270d38ceaf9SAlex Deucher struct amdgpu_bo_va *bo_va) 1271d38ceaf9SAlex Deucher { 1272d38ceaf9SAlex Deucher struct amdgpu_bo_va_mapping *mapping, *next; 1273d38ceaf9SAlex Deucher struct amdgpu_vm *vm = bo_va->vm; 1274d38ceaf9SAlex Deucher 1275d38ceaf9SAlex Deucher list_del(&bo_va->bo_list); 1276d38ceaf9SAlex Deucher 1277d38ceaf9SAlex Deucher spin_lock(&vm->status_lock); 1278d38ceaf9SAlex Deucher list_del(&bo_va->vm_status); 1279d38ceaf9SAlex Deucher spin_unlock(&vm->status_lock); 1280d38ceaf9SAlex Deucher 12817fc11959SChristian König list_for_each_entry_safe(mapping, next, &bo_va->valids, list) { 1282d38ceaf9SAlex Deucher list_del(&mapping->list); 1283c25867dfSChunming Zhou spin_lock(&vm->it_lock); 1284d38ceaf9SAlex Deucher interval_tree_remove(&mapping->it, &vm->va); 1285c25867dfSChunming Zhou spin_unlock(&vm->it_lock); 128693e3e438SChristian König trace_amdgpu_vm_bo_unmap(bo_va, mapping); 1287d38ceaf9SAlex Deucher list_add(&mapping->list, &vm->freed); 12887fc11959SChristian König } 12897fc11959SChristian König list_for_each_entry_safe(mapping, next, &bo_va->invalids, list) { 12907fc11959SChristian König list_del(&mapping->list); 1291c25867dfSChunming Zhou spin_lock(&vm->it_lock); 12927fc11959SChristian König interval_tree_remove(&mapping->it, &vm->va); 1293c25867dfSChunming Zhou spin_unlock(&vm->it_lock); 1294d38ceaf9SAlex Deucher kfree(mapping); 1295d38ceaf9SAlex Deucher } 1296bb1e38a4SChunming Zhou fence_put(bo_va->last_pt_update); 129769b576a1SChunming Zhou mutex_destroy(&bo_va->mutex); 1298d38ceaf9SAlex Deucher kfree(bo_va); 1299d38ceaf9SAlex Deucher } 1300d38ceaf9SAlex Deucher 1301d38ceaf9SAlex Deucher /** 1302d38ceaf9SAlex Deucher * amdgpu_vm_bo_invalidate - mark the bo as invalid 1303d38ceaf9SAlex Deucher * 1304d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 1305d38ceaf9SAlex Deucher * @vm: requested vm 1306d38ceaf9SAlex Deucher * @bo: amdgpu buffer object 1307d38ceaf9SAlex Deucher * 13088843dbbbSChristian König * Mark @bo as invalid. 1309d38ceaf9SAlex Deucher */ 1310d38ceaf9SAlex Deucher void amdgpu_vm_bo_invalidate(struct amdgpu_device *adev, 1311d38ceaf9SAlex Deucher struct amdgpu_bo *bo) 1312d38ceaf9SAlex Deucher { 1313d38ceaf9SAlex Deucher struct amdgpu_bo_va *bo_va; 1314d38ceaf9SAlex Deucher 1315d38ceaf9SAlex Deucher list_for_each_entry(bo_va, &bo->va, bo_list) { 1316d38ceaf9SAlex Deucher spin_lock(&bo_va->vm->status_lock); 13177fc11959SChristian König if (list_empty(&bo_va->vm_status)) 1318d38ceaf9SAlex Deucher list_add(&bo_va->vm_status, &bo_va->vm->invalidated); 1319d38ceaf9SAlex Deucher spin_unlock(&bo_va->vm->status_lock); 1320d38ceaf9SAlex Deucher } 1321d38ceaf9SAlex Deucher } 1322d38ceaf9SAlex Deucher 1323d38ceaf9SAlex Deucher /** 1324d38ceaf9SAlex Deucher * amdgpu_vm_init - initialize a vm instance 1325d38ceaf9SAlex Deucher * 1326d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 1327d38ceaf9SAlex Deucher * @vm: requested vm 1328d38ceaf9SAlex Deucher * 13298843dbbbSChristian König * Init @vm fields. 1330d38ceaf9SAlex Deucher */ 1331d38ceaf9SAlex Deucher int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm) 1332d38ceaf9SAlex Deucher { 1333d38ceaf9SAlex Deucher const unsigned align = min(AMDGPU_VM_PTB_ALIGN_SIZE, 1334d38ceaf9SAlex Deucher AMDGPU_VM_PTE_COUNT * 8); 13359571e1d8SMichel Dänzer unsigned pd_size, pd_entries; 13362d55e45aSChristian König unsigned ring_instance; 13372d55e45aSChristian König struct amdgpu_ring *ring; 13382bd9ccfaSChristian König struct amd_sched_rq *rq; 1339d38ceaf9SAlex Deucher int i, r; 1340d38ceaf9SAlex Deucher 1341d38ceaf9SAlex Deucher for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { 13424ff37a83SChristian König vm->ids[i].mgr_id = NULL; 1343d38ceaf9SAlex Deucher vm->ids[i].flushed_updates = NULL; 1344d38ceaf9SAlex Deucher } 1345d38ceaf9SAlex Deucher vm->va = RB_ROOT; 1346d38ceaf9SAlex Deucher spin_lock_init(&vm->status_lock); 1347d38ceaf9SAlex Deucher INIT_LIST_HEAD(&vm->invalidated); 13487fc11959SChristian König INIT_LIST_HEAD(&vm->cleared); 1349d38ceaf9SAlex Deucher INIT_LIST_HEAD(&vm->freed); 1350c25867dfSChunming Zhou spin_lock_init(&vm->it_lock); 1351d38ceaf9SAlex Deucher pd_size = amdgpu_vm_directory_size(adev); 1352d38ceaf9SAlex Deucher pd_entries = amdgpu_vm_num_pdes(adev); 1353d38ceaf9SAlex Deucher 1354d38ceaf9SAlex Deucher /* allocate page table array */ 13559571e1d8SMichel Dänzer vm->page_tables = drm_calloc_large(pd_entries, sizeof(struct amdgpu_vm_pt)); 1356d38ceaf9SAlex Deucher if (vm->page_tables == NULL) { 1357d38ceaf9SAlex Deucher DRM_ERROR("Cannot allocate memory for page table array\n"); 1358d38ceaf9SAlex Deucher return -ENOMEM; 1359d38ceaf9SAlex Deucher } 1360d38ceaf9SAlex Deucher 13612bd9ccfaSChristian König /* create scheduler entity for page table updates */ 13622d55e45aSChristian König 13632d55e45aSChristian König ring_instance = atomic_inc_return(&adev->vm_manager.vm_pte_next_ring); 13642d55e45aSChristian König ring_instance %= adev->vm_manager.vm_pte_num_rings; 13652d55e45aSChristian König ring = adev->vm_manager.vm_pte_rings[ring_instance]; 13662bd9ccfaSChristian König rq = &ring->sched.sched_rq[AMD_SCHED_PRIORITY_KERNEL]; 13672bd9ccfaSChristian König r = amd_sched_entity_init(&ring->sched, &vm->entity, 13682bd9ccfaSChristian König rq, amdgpu_sched_jobs); 13692bd9ccfaSChristian König if (r) 13702bd9ccfaSChristian König return r; 13712bd9ccfaSChristian König 137205906decSBas Nieuwenhuizen vm->page_directory_fence = NULL; 137305906decSBas Nieuwenhuizen 1374d38ceaf9SAlex Deucher r = amdgpu_bo_create(adev, pd_size, align, true, 1375857d913dSAlex Deucher AMDGPU_GEM_DOMAIN_VRAM, 1376857d913dSAlex Deucher AMDGPU_GEM_CREATE_NO_CPU_ACCESS, 137772d7668bSChristian König NULL, NULL, &vm->page_directory); 1378d38ceaf9SAlex Deucher if (r) 13792bd9ccfaSChristian König goto error_free_sched_entity; 13802bd9ccfaSChristian König 1381ef9f0a83SChunming Zhou r = amdgpu_bo_reserve(vm->page_directory, false); 13822bd9ccfaSChristian König if (r) 13832bd9ccfaSChristian König goto error_free_page_directory; 13842bd9ccfaSChristian König 13852bd9ccfaSChristian König r = amdgpu_vm_clear_bo(adev, vm, vm->page_directory); 1386ef9f0a83SChunming Zhou amdgpu_bo_unreserve(vm->page_directory); 13872bd9ccfaSChristian König if (r) 13882bd9ccfaSChristian König goto error_free_page_directory; 1389d38ceaf9SAlex Deucher 1390d38ceaf9SAlex Deucher return 0; 13912bd9ccfaSChristian König 13922bd9ccfaSChristian König error_free_page_directory: 13932bd9ccfaSChristian König amdgpu_bo_unref(&vm->page_directory); 13942bd9ccfaSChristian König vm->page_directory = NULL; 13952bd9ccfaSChristian König 13962bd9ccfaSChristian König error_free_sched_entity: 13972bd9ccfaSChristian König amd_sched_entity_fini(&ring->sched, &vm->entity); 13982bd9ccfaSChristian König 13992bd9ccfaSChristian König return r; 1400d38ceaf9SAlex Deucher } 1401d38ceaf9SAlex Deucher 1402d38ceaf9SAlex Deucher /** 1403d38ceaf9SAlex Deucher * amdgpu_vm_fini - tear down a vm instance 1404d38ceaf9SAlex Deucher * 1405d38ceaf9SAlex Deucher * @adev: amdgpu_device pointer 1406d38ceaf9SAlex Deucher * @vm: requested vm 1407d38ceaf9SAlex Deucher * 14088843dbbbSChristian König * Tear down @vm. 1409d38ceaf9SAlex Deucher * Unbind the VM and remove all bos from the vm bo list 1410d38ceaf9SAlex Deucher */ 1411d38ceaf9SAlex Deucher void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm) 1412d38ceaf9SAlex Deucher { 1413d38ceaf9SAlex Deucher struct amdgpu_bo_va_mapping *mapping, *tmp; 1414d38ceaf9SAlex Deucher int i; 1415d38ceaf9SAlex Deucher 14162d55e45aSChristian König amd_sched_entity_fini(vm->entity.sched, &vm->entity); 14172bd9ccfaSChristian König 1418d38ceaf9SAlex Deucher if (!RB_EMPTY_ROOT(&vm->va)) { 1419d38ceaf9SAlex Deucher dev_err(adev->dev, "still active bo inside vm\n"); 1420d38ceaf9SAlex Deucher } 1421d38ceaf9SAlex Deucher rbtree_postorder_for_each_entry_safe(mapping, tmp, &vm->va, it.rb) { 1422d38ceaf9SAlex Deucher list_del(&mapping->list); 1423d38ceaf9SAlex Deucher interval_tree_remove(&mapping->it, &vm->va); 1424d38ceaf9SAlex Deucher kfree(mapping); 1425d38ceaf9SAlex Deucher } 1426d38ceaf9SAlex Deucher list_for_each_entry_safe(mapping, tmp, &vm->freed, list) { 1427d38ceaf9SAlex Deucher list_del(&mapping->list); 1428d38ceaf9SAlex Deucher kfree(mapping); 1429d38ceaf9SAlex Deucher } 1430d38ceaf9SAlex Deucher 1431d38ceaf9SAlex Deucher for (i = 0; i < amdgpu_vm_num_pdes(adev); i++) 1432ee1782c3SChristian König amdgpu_bo_unref(&vm->page_tables[i].entry.robj); 14339571e1d8SMichel Dänzer drm_free_large(vm->page_tables); 1434d38ceaf9SAlex Deucher 1435d38ceaf9SAlex Deucher amdgpu_bo_unref(&vm->page_directory); 143605906decSBas Nieuwenhuizen fence_put(vm->page_directory_fence); 1437d38ceaf9SAlex Deucher for (i = 0; i < AMDGPU_MAX_RINGS; ++i) { 14384ff37a83SChristian König struct amdgpu_vm_id *id = &vm->ids[i]; 14391c16c0a7SChristian König 14404ff37a83SChristian König if (id->mgr_id) 14414ff37a83SChristian König atomic_long_cmpxchg(&id->mgr_id->owner, 14424ff37a83SChristian König (long)id, 0); 14434ff37a83SChristian König fence_put(id->flushed_updates); 1444d38ceaf9SAlex Deucher } 1445d38ceaf9SAlex Deucher } 1446ea89f8c9SChristian König 1447ea89f8c9SChristian König /** 1448a9a78b32SChristian König * amdgpu_vm_manager_init - init the VM manager 1449a9a78b32SChristian König * 1450a9a78b32SChristian König * @adev: amdgpu_device pointer 1451a9a78b32SChristian König * 1452a9a78b32SChristian König * Initialize the VM manager structures 1453a9a78b32SChristian König */ 1454a9a78b32SChristian König void amdgpu_vm_manager_init(struct amdgpu_device *adev) 1455a9a78b32SChristian König { 1456a9a78b32SChristian König unsigned i; 1457a9a78b32SChristian König 1458a9a78b32SChristian König INIT_LIST_HEAD(&adev->vm_manager.ids_lru); 1459a9a78b32SChristian König 1460a9a78b32SChristian König /* skip over VMID 0, since it is the system VM */ 1461971fe9a9SChristian König for (i = 1; i < adev->vm_manager.num_ids; ++i) { 1462971fe9a9SChristian König amdgpu_vm_reset_id(adev, i); 1463a9a78b32SChristian König list_add_tail(&adev->vm_manager.ids[i].list, 1464a9a78b32SChristian König &adev->vm_manager.ids_lru); 1465971fe9a9SChristian König } 14662d55e45aSChristian König 14672d55e45aSChristian König atomic_set(&adev->vm_manager.vm_pte_next_ring, 0); 1468a9a78b32SChristian König } 1469a9a78b32SChristian König 1470a9a78b32SChristian König /** 1471ea89f8c9SChristian König * amdgpu_vm_manager_fini - cleanup VM manager 1472ea89f8c9SChristian König * 1473ea89f8c9SChristian König * @adev: amdgpu_device pointer 1474ea89f8c9SChristian König * 1475ea89f8c9SChristian König * Cleanup the VM manager and free resources. 1476ea89f8c9SChristian König */ 1477ea89f8c9SChristian König void amdgpu_vm_manager_fini(struct amdgpu_device *adev) 1478ea89f8c9SChristian König { 1479ea89f8c9SChristian König unsigned i; 1480ea89f8c9SChristian König 1481ea89f8c9SChristian König for (i = 0; i < AMDGPU_NUM_VM; ++i) 14821c16c0a7SChristian König fence_put(adev->vm_manager.ids[i].active); 1483ea89f8c9SChristian König } 1484