/* * Copyright 2008 Advanced Micro Devices, Inc. * Copyright 2008 Red Hat Inc. * Copyright 2009 Jerome Glisse. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: Dave Airlie * Alex Deucher * Jerome Glisse */#include <linux/dma-fence-array.h>#include <linux/interval_tree_generic.h>#include <linux/idr.h>#include <drm/drmP.h>#include <drm/amdgpu_drm.h>#include "amdgpu.h"#include "amdgpu_trace.h" /* * GPUVM * GPUVM is similar to the legacy gart on older asics, however * rather than there being a single global gart table * for the entire GPU, there are multiple VM page tables active * at any given time. The VM page tables can contain a mix * vram pages and system memory pages and system memory pages * can be mapped as snooped (cached system pages) or unsnooped * (uncached system pages). * Each VM has an ID associated with it and there is a page table * associated with each VMID. When execting a command buffer, * the kernel tells the the ring what VMID to use for that command * buffer. VMIDs are allocated dynamically as commands are submitted. * The userspace drivers maintain their own address space and the kernel * sets up their pages tables accordingly when they submit their * command buffers and a VMID is assigned. * Cayman/Trinity support up to 8 active VMs at any given time; * SI supports 16. */ #define START(node) ((node)->start)#define LAST(node) ((node)->last) INTERVAL_TREE_DEFINE(struct amdgpu_bo_va_mapping, rb, uint64_t, __subtree_last, START, LAST, static, amdgpu_vm_it) #undef START#undef LAST /* Local structure. Encapsulate some VM table update parameters to reduce * the number of function parameters */struct amdgpu_pte_update_params { /* amdgpu device we do this update for */ struct amdgpu_device *adev; /* optional amdgpu_vm we do this update for */ struct amdgpu_vm *vm; /* address where to copy page table entries from */ uint64_t src; /* indirect buffer to fill with commands */ struct amdgpu_ib *ib; /* Function which actually does the update */ void (*func)(struct amdgpu_pte_update_params *params, uint64_t pe, uint64_t addr, unsigned count, uint32_t incr, uint64_t flags); /* The next two are used during VM update by CPU * DMA addresses to use for mapping * Kernel pointer of PD/PT BO that needs to be updated */ dma_addr_t *pages_addr; void *kptr;}; /* Helper to disable partial resident texture feature from a fence callback */struct amdgpu_prt_cb { struct amdgpu_device *adev; struct dma_fence_cb cb;}; /** * amdgpu_vm_level_shift - return the addr shift for each level * * @adev: amdgpu_device pointer * * Returns the number of bits the pfn needs to be right shifted for a level. */static unsigned amdgpu_vm_level_shift(struct amdgpu_device *adev, unsigned level){ unsigned shift = 0xff; switch (level) { case AMDGPU_VM_PDB2: case AMDGPU_VM_PDB1: case AMDGPU_VM_PDB0: shift = 9 * (AMDGPU_VM_PDB0 - level) + adev->vm_manager.block_size; break; case AMDGPU_VM_PTB: shift = 0; break; default: dev_err(adev->dev, "the level%d isn't supported.\n", level); } return shift;} /** * amdgpu_vm_num_entries - return the number of entries in a PD/PT * * @adev: amdgpu_device pointer * * Calculate the number of entries in a page directory or page table. */static unsigned amdgpu_vm_num_entries(struct amdgpu_device *adev, unsigned level){ unsigned shift = amdgpu_vm_level_shift(adev, adev->vm_manager.root_level); if (level == adev->vm_manager.root_level) /* For the root directory */ return round_up(adev->vm_manager.max_pfn, 1 << shift) >> shift; else if (level != AMDGPU_VM_PTB) /* Everything in between */ return 512; else /* For the page tables on the leaves */ return AMDGPU_VM_PTE_COUNT(adev);} /** * amdgpu_vm_bo_size - returns the size of the BOs in bytes * * @adev: amdgpu_device pointer * * Calculate the size of the BO for a page directory or page table in bytes. */static unsigned amdgpu_vm_bo_size(struct amdgpu_device *adev, unsigned level){ return AMDGPU_GPU_PAGE_ALIGN(amdgpu_vm_num_entries(adev, level) * 8);} /** * amdgpu_vm_get_pd_bo - add the VM PD to a validation list * * @vm: vm providing the BOs * @validated: head of validation list * @entry: entry to add * * Add the page directory to the list of BOs to * validate for command submission. */void amdgpu_vm_get_pd_bo(struct amdgpu_vm *vm, struct list_head *validated, struct amdgpu_bo_list_entry *entry){ entry->robj = vm->root.base.bo; entry->priority = 0; entry->tv.bo = &entry->robj->tbo; entry->tv.shared = true; entry->user_pages = NULL; list_add(&entry->tv.head, validated);} /** * amdgpu_vm_validate_pt_bos - validate the page table BOs * * @adev: amdgpu device pointer * @vm: vm providing the BOs * @validate: callback to do the validation * @param: parameter for the validation callback * * Validate the page table BOs on command submission if neccessary. */int amdgpu_vm_validate_pt_bos(struct amdgpu_device *adev, struct amdgpu_vm *vm, int (*validate)(void *p, struct amdgpu_bo *bo), void *param){ struct ttm_bo_global *glob = adev->mman.bdev.glob; int r; spin_lock(&vm->status_lock); while (!list_empty(&vm->evicted)) { struct amdgpu_vm_bo_base *bo_base; struct amdgpu_bo *bo; bo_base = list_first_entry(&vm->evicted, struct amdgpu_vm_bo_base, vm_status); spin_unlock(&vm->status_lock); bo = bo_base->bo; BUG_ON(!bo); if (bo->parent) { r = validate(param, bo); if (r) return r; spin_lock(&glob->lru_lock); ttm_bo_move_to_lru_tail(&bo->tbo); if (bo->shadow) ttm_bo_move_to_lru_tail(&bo->shadow->tbo); spin_unlock(&glob->lru_lock); } if (bo->tbo.type == ttm_bo_type_kernel && vm->use_cpu_for_update) { r = amdgpu_bo_kmap(bo, NULL); if (r) return r; } spin_lock(&vm->status_lock); if (bo->tbo.type != ttm_bo_type_kernel) list_move(&bo_base->vm_status, &vm->moved); else list_move(&bo_base->vm_status, &vm->relocated); } spin_unlock(&vm->status_lock); return 0;} /** * amdgpu_vm_ready - check VM is ready for updates * * @vm: VM to check * * Check if all VM PDs/PTs are ready for updates */bool amdgpu_vm_ready(struct amdgpu_vm *vm){ bool ready; spin_lock(&vm->status_lock); ready = list_empty(&vm->evicted); spin_unlock(&vm->status_lock); return ready;} /** * amdgpu_vm_alloc_levels - allocate the PD/PT levels * * @adev: amdgpu_device pointer * @vm: requested vm * @saddr: start of the address range * @eaddr: end of the address range * * Make sure the page directories and page tables are allocated */static int amdgpu_vm_alloc_levels(struct amdgpu_device *adev, struct amdgpu_vm *vm, struct amdgpu_vm_pt *parent, uint64_t saddr, uint64_t eaddr, unsigned level){ unsigned shift = amdgpu_vm_level_shift(adev, level); unsigned pt_idx, from, to; int r; u64 flags; uint64_t init_value = 0; if (!parent->entries) { unsigned num_entries = amdgpu_vm_num_entries(adev, level); parent->entries = kvmalloc_array(num_entries, sizeof(struct amdgpu_vm_pt), GFP_KERNEL | __GFP_ZERO); if (!parent->entries) return -ENOMEM; memset(parent->entries, 0 , sizeof(struct amdgpu_vm_pt)); } from = saddr >> shift; to = eaddr >> shift; if (from >= amdgpu_vm_num_entries(adev, level) || to >= amdgpu_vm_num_entries(adev, level)) return -EINVAL; ++level; saddr = saddr & ((1 << shift) - 1); eaddr = eaddr & ((1 << shift) - 1); flags = AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS | AMDGPU_GEM_CREATE_VRAM_CLEARED; if (vm->use_cpu_for_update) flags |= AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED; else flags |= (AMDGPU_GEM_CREATE_NO_CPU_ACCESS | AMDGPU_GEM_CREATE_SHADOW); if (vm->pte_support_ats) { init_value = AMDGPU_PTE_DEFAULT_ATC; if (level != AMDGPU_VM_PTB) init_value |= AMDGPU_PDE_PTE; } /* walk over the address space and allocate the page tables */ for (pt_idx = from; pt_idx <= to; ++pt_idx) { struct reservation_object *resv = vm->root.base.bo->tbo.resv; struct amdgpu_vm_pt *entry = &parent->entries[pt_idx]; struct amdgpu_bo *pt; if (!entry->base.bo) { r = amdgpu_bo_create(adev, amdgpu_vm_bo_size(adev, level), AMDGPU_GPU_PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, flags, NULL, resv, init_value, &pt); if (r) return r; if (vm->use_cpu_for_update) { r = amdgpu_bo_kmap(pt, NULL); if (r) { amdgpu_bo_unref(&pt); return r; } } /* Keep a reference to the root directory to avoid * freeing them up in the wrong order. */ pt->parent = amdgpu_bo_ref(parent->base.bo); entry->base.vm = vm; entry->base.bo = pt; list_add_tail(&entry->base.bo_list, &pt->va); spin_lock(&vm->status_lock); list_add(&entry->base.vm_status, &vm->relocated); spin_unlock(&vm->status_lock); } if (level < AMDGPU_VM_PTB) { uint64_t sub_saddr = (pt_idx == from) ? saddr : 0; uint64_t sub_eaddr = (pt_idx == to) ? eaddr : ((1 << shift) - 1); r = amdgpu_vm_alloc_levels(adev, vm, entry, sub_saddr, sub_eaddr, level); if (r) return r; } } return 0;} /** * amdgpu_vm_alloc_pts - Allocate page tables. * * @adev: amdgpu_device pointer * @vm: VM to allocate page tables for * @saddr: Start address which needs to be allocated * @size: Size from start address we need. * * Make sure the page tables are allocated. */int amdgpu_vm_alloc_pts(struct amdgpu_device *adev, struct amdgpu_vm *vm, uint64_t saddr, uint64_t size){ uint64_t last_pfn; uint64_t eaddr; /* validate the parameters */ if (saddr & AMDGPU_GPU_PAGE_MASK || size & AMDGPU_GPU_PAGE_MASK) return -EINVAL; eaddr = saddr + size - 1; last_pfn = eaddr / AMDGPU_GPU_PAGE_SIZE; if (last_pfn >= adev->vm_manager.max_pfn) { dev_err(adev->dev, "va above limit (0x%08llX >= 0x%08llX)\n", last_pfn, adev->vm_manager.max_pfn); return -EINVAL; } saddr /= AMDGPU_GPU_PAGE_SIZE; eaddr /= AMDGPU_GPU_PAGE_SIZE; return amdgpu_vm_alloc_levels(adev, vm, &vm->root, saddr, eaddr, adev->vm_manager.root_level);} /** * amdgpu_vm_check_compute_bug - check whether asic has compute vm bug * * @adev: amdgpu_device pointer */void amdgpu_vm_check_compute_bug(struct amdgpu_device *adev){ const struct amdgpu_ip_block *ip_block; bool has_compute_vm_bug; struct amdgpu_ring *ring; int i; has_compute_vm_bug = false; ip_block = amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_GFX); if (ip_block) { /* Compute has a VM bug for GFX version < 7. Compute has a VM bug for GFX 8 MEC firmware version < 673.*/ if (ip_block->version->major <= 7) has_compute_vm_bug = true; else if (ip_block->version->major == 8) if (adev->gfx.mec_fw_version < 673) has_compute_vm_bug = true; } for (i = 0; i < adev->num_rings; i++) { ring = adev->rings[i]; if (ring->funcs->type == AMDGPU_RING_TYPE_COMPUTE) /* only compute rings */ ring->has_compute_vm_bug = has_compute_vm_bug; else ring->has_compute_vm_bug = false; }} bool amdgpu_vm_need_pipeline_sync(struct amdgpu_ring *ring, struct amdgpu_job *job){ struct amdgpu_device *adev = ring->adev; unsigned vmhub = ring->funcs->vmhub; struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; struct amdgpu_vmid *id; bool gds_switch_needed; bool vm_flush_needed = job->vm_needs_flush || ring->has_compute_vm_bug; if (job->vmid == 0) return false; id = &id_mgr->ids[job->vmid]; gds_switch_needed = ring->funcs->emit_gds_switch && ( id->gds_base != job->gds_base || id->gds_size != job->gds_size || id->gws_base != job->gws_base || id->gws_size != job->gws_size || id->oa_base != job->oa_base || id->oa_size != job->oa_size); if (amdgpu_vmid_had_gpu_reset(adev, id)) return true; return vm_flush_needed || gds_switch_needed;} static bool amdgpu_vm_is_large_bar(struct amdgpu_device *adev){ return (adev->mc.real_vram_size == adev->mc.visible_vram_size);} /** * amdgpu_vm_flush - hardware flush the vm * * @ring: ring to use for flush * @vmid: vmid number to use * @pd_addr: address of the page directory * * Emit a VM flush when it is necessary. */int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, bool need_pipe_sync){ struct amdgpu_device *adev = ring->adev; unsigned vmhub = ring->funcs->vmhub; struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; struct amdgpu_vmid *id = &id_mgr->ids[job->vmid]; bool gds_switch_needed = ring->funcs->emit_gds_switch && ( id->gds_base != job->gds_base || id->gds_size != job->gds_size || id->gws_base != job->gws_base || id->gws_size != job->gws_size || id->oa_base != job->oa_base || id->oa_size != job->oa_size); bool vm_flush_needed = job->vm_needs_flush; unsigned patch_offset = 0; int r; if (amdgpu_vmid_had_gpu_reset(adev, id)) { gds_switch_needed = true; vm_flush_needed = true; }Lines 1–500 of 2574. Load more
/* * Copyright 2008 Advanced Micro Devices, Inc. * Copyright 2008 Red Hat Inc. * Copyright 2009 Jerome Glisse. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: Dave Airlie * Alex Deucher * Jerome Glisse */ #include <linux/dma-fence-array.h> #include <linux/interval_tree_generic.h> #include <linux/idr.h> #include <drm/drmP.h> #include <drm/amdgpu_drm.h> #include "amdgpu.h" #include "amdgpu_trace.h" /* * GPUVM * GPUVM is similar to the legacy gart on older asics, however * rather than there being a single global gart table * for the entire GPU, there are multiple VM page tables active * at any given time. The VM page tables can contain a mix * vram pages and system memory pages and system memory pages * can be mapped as snooped (cached system pages) or unsnooped * (uncached system pages). * Each VM has an ID associated with it and there is a page table * associated with each VMID. When execting a command buffer, * the kernel tells the the ring what VMID to use for that command * buffer. VMIDs are allocated dynamically as commands are submitted. * The userspace drivers maintain their own address space and the kernel * sets up their pages tables accordingly when they submit their * command buffers and a VMID is assigned. * Cayman/Trinity support up to 8 active VMs at any given time; * SI supports 16. */ #define START(node) ((node)->start) #define LAST(node) ((node)->last) INTERVAL_TREE_DEFINE(struct amdgpu_bo_va_mapping, rb, uint64_t, __subtree_last, START, LAST, static, amdgpu_vm_it) #undef START #undef LAST /* Local structure. Encapsulate some VM table update parameters to reduce * the number of function parameters */ struct amdgpu_pte_update_params { /* amdgpu device we do this update for */ struct amdgpu_device *adev; /* optional amdgpu_vm we do this update for */ struct amdgpu_vm *vm; /* address where to copy page table entries from */ uint64_t src; /* indirect buffer to fill with commands */ struct amdgpu_ib *ib; /* Function which actually does the update */ void (*func)(struct amdgpu_pte_update_params *params, uint64_t pe, uint64_t addr, unsigned count, uint32_t incr, uint64_t flags); /* The next two are used during VM update by CPU * DMA addresses to use for mapping * Kernel pointer of PD/PT BO that needs to be updated */ dma_addr_t *pages_addr; void *kptr; }; /* Helper to disable partial resident texture feature from a fence callback */ struct amdgpu_prt_cb { struct amdgpu_device *adev; struct dma_fence_cb cb; }; /** * amdgpu_vm_level_shift - return the addr shift for each level * * @adev: amdgpu_device pointer * * Returns the number of bits the pfn needs to be right shifted for a level. */ static unsigned amdgpu_vm_level_shift(struct amdgpu_device *adev, unsigned level) { unsigned shift = 0xff; switch (level) { case AMDGPU_VM_PDB2: case AMDGPU_VM_PDB1: case AMDGPU_VM_PDB0: shift = 9 * (AMDGPU_VM_PDB0 - level) + adev->vm_manager.block_size; break; case AMDGPU_VM_PTB: shift = 0; break; default: dev_err(adev->dev, "the level%d isn't supported.\n", level); } return shift; } /** * amdgpu_vm_num_entries - return the number of entries in a PD/PT * * @adev: amdgpu_device pointer * * Calculate the number of entries in a page directory or page table. */ static unsigned amdgpu_vm_num_entries(struct amdgpu_device *adev, unsigned level) { unsigned shift = amdgpu_vm_level_shift(adev, adev->vm_manager.root_level); if (level == adev->vm_manager.root_level) /* For the root directory */ return round_up(adev->vm_manager.max_pfn, 1 << shift) >> shift; else if (level != AMDGPU_VM_PTB) /* Everything in between */ return 512; else /* For the page tables on the leaves */ return AMDGPU_VM_PTE_COUNT(adev); } /** * amdgpu_vm_bo_size - returns the size of the BOs in bytes * * @adev: amdgpu_device pointer * * Calculate the size of the BO for a page directory or page table in bytes. */ static unsigned amdgpu_vm_bo_size(struct amdgpu_device *adev, unsigned level) { return AMDGPU_GPU_PAGE_ALIGN(amdgpu_vm_num_entries(adev, level) * 8); } /** * amdgpu_vm_get_pd_bo - add the VM PD to a validation list * * @vm: vm providing the BOs * @validated: head of validation list * @entry: entry to add * * Add the page directory to the list of BOs to * validate for command submission. */ void amdgpu_vm_get_pd_bo(struct amdgpu_vm *vm, struct list_head *validated, struct amdgpu_bo_list_entry *entry) { entry->robj = vm->root.base.bo; entry->priority = 0; entry->tv.bo = &entry->robj->tbo; entry->tv.shared = true; entry->user_pages = NULL; list_add(&entry->tv.head, validated); } /** * amdgpu_vm_validate_pt_bos - validate the page table BOs * * @adev: amdgpu device pointer * @vm: vm providing the BOs * @validate: callback to do the validation * @param: parameter for the validation callback * * Validate the page table BOs on command submission if neccessary. */ int amdgpu_vm_validate_pt_bos(struct amdgpu_device *adev, struct amdgpu_vm *vm, int (*validate)(void *p, struct amdgpu_bo *bo), void *param) { struct ttm_bo_global *glob = adev->mman.bdev.glob; int r; spin_lock(&vm->status_lock); while (!list_empty(&vm->evicted)) { struct amdgpu_vm_bo_base *bo_base; struct amdgpu_bo *bo; bo_base = list_first_entry(&vm->evicted, struct amdgpu_vm_bo_base, vm_status); spin_unlock(&vm->status_lock); bo = bo_base->bo; BUG_ON(!bo); if (bo->parent) { r = validate(param, bo); if (r) return r; spin_lock(&glob->lru_lock); ttm_bo_move_to_lru_tail(&bo->tbo); if (bo->shadow) ttm_bo_move_to_lru_tail(&bo->shadow->tbo); spin_unlock(&glob->lru_lock); } if (bo->tbo.type == ttm_bo_type_kernel && vm->use_cpu_for_update) { r = amdgpu_bo_kmap(bo, NULL); if (r) return r; } spin_lock(&vm->status_lock); if (bo->tbo.type != ttm_bo_type_kernel) list_move(&bo_base->vm_status, &vm->moved); else list_move(&bo_base->vm_status, &vm->relocated); } spin_unlock(&vm->status_lock); return 0; } /** * amdgpu_vm_ready - check VM is ready for updates * * @vm: VM to check * * Check if all VM PDs/PTs are ready for updates */ bool amdgpu_vm_ready(struct amdgpu_vm *vm) { bool ready; spin_lock(&vm->status_lock); ready = list_empty(&vm->evicted); spin_unlock(&vm->status_lock); return ready; } /** * amdgpu_vm_alloc_levels - allocate the PD/PT levels * * @adev: amdgpu_device pointer * @vm: requested vm * @saddr: start of the address range * @eaddr: end of the address range * * Make sure the page directories and page tables are allocated */ static int amdgpu_vm_alloc_levels(struct amdgpu_device *adev, struct amdgpu_vm *vm, struct amdgpu_vm_pt *parent, uint64_t saddr, uint64_t eaddr, unsigned level) { unsigned shift = amdgpu_vm_level_shift(adev, level); unsigned pt_idx, from, to; int r; u64 flags; uint64_t init_value = 0; if (!parent->entries) { unsigned num_entries = amdgpu_vm_num_entries(adev, level); parent->entries = kvmalloc_array(num_entries, sizeof(struct amdgpu_vm_pt), GFP_KERNEL | __GFP_ZERO); if (!parent->entries) return -ENOMEM; memset(parent->entries, 0 , sizeof(struct amdgpu_vm_pt)); } from = saddr >> shift; to = eaddr >> shift; if (from >= amdgpu_vm_num_entries(adev, level) || to >= amdgpu_vm_num_entries(adev, level)) return -EINVAL; ++level; saddr = saddr & ((1 << shift) - 1); eaddr = eaddr & ((1 << shift) - 1); flags = AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS | AMDGPU_GEM_CREATE_VRAM_CLEARED; if (vm->use_cpu_for_update) flags |= AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED; else flags |= (AMDGPU_GEM_CREATE_NO_CPU_ACCESS | AMDGPU_GEM_CREATE_SHADOW); if (vm->pte_support_ats) { init_value = AMDGPU_PTE_DEFAULT_ATC; if (level != AMDGPU_VM_PTB) init_value |= AMDGPU_PDE_PTE; } /* walk over the address space and allocate the page tables */ for (pt_idx = from; pt_idx <= to; ++pt_idx) { struct reservation_object *resv = vm->root.base.bo->tbo.resv; struct amdgpu_vm_pt *entry = &parent->entries[pt_idx]; struct amdgpu_bo *pt; if (!entry->base.bo) { r = amdgpu_bo_create(adev, amdgpu_vm_bo_size(adev, level), AMDGPU_GPU_PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_VRAM, flags, NULL, resv, init_value, &pt); if (r) return r; if (vm->use_cpu_for_update) { r = amdgpu_bo_kmap(pt, NULL); if (r) { amdgpu_bo_unref(&pt); return r; } } /* Keep a reference to the root directory to avoid * freeing them up in the wrong order. */ pt->parent = amdgpu_bo_ref(parent->base.bo); entry->base.vm = vm; entry->base.bo = pt; list_add_tail(&entry->base.bo_list, &pt->va); spin_lock(&vm->status_lock); list_add(&entry->base.vm_status, &vm->relocated); spin_unlock(&vm->status_lock); } if (level < AMDGPU_VM_PTB) { uint64_t sub_saddr = (pt_idx == from) ? saddr : 0; uint64_t sub_eaddr = (pt_idx == to) ? eaddr : ((1 << shift) - 1); r = amdgpu_vm_alloc_levels(adev, vm, entry, sub_saddr, sub_eaddr, level); if (r) return r; } } return 0; } /** * amdgpu_vm_alloc_pts - Allocate page tables. * * @adev: amdgpu_device pointer * @vm: VM to allocate page tables for * @saddr: Start address which needs to be allocated * @size: Size from start address we need. * * Make sure the page tables are allocated. */ int amdgpu_vm_alloc_pts(struct amdgpu_device *adev, struct amdgpu_vm *vm, uint64_t saddr, uint64_t size) { uint64_t last_pfn; uint64_t eaddr; /* validate the parameters */ if (saddr & AMDGPU_GPU_PAGE_MASK || size & AMDGPU_GPU_PAGE_MASK) return -EINVAL; eaddr = saddr + size - 1; last_pfn = eaddr / AMDGPU_GPU_PAGE_SIZE; if (last_pfn >= adev->vm_manager.max_pfn) { dev_err(adev->dev, "va above limit (0x%08llX >= 0x%08llX)\n", last_pfn, adev->vm_manager.max_pfn); return -EINVAL; } saddr /= AMDGPU_GPU_PAGE_SIZE; eaddr /= AMDGPU_GPU_PAGE_SIZE; return amdgpu_vm_alloc_levels(adev, vm, &vm->root, saddr, eaddr, adev->vm_manager.root_level); } /** * amdgpu_vm_check_compute_bug - check whether asic has compute vm bug * * @adev: amdgpu_device pointer */ void amdgpu_vm_check_compute_bug(struct amdgpu_device *adev) { const struct amdgpu_ip_block *ip_block; bool has_compute_vm_bug; struct amdgpu_ring *ring; int i; has_compute_vm_bug = false; ip_block = amdgpu_device_ip_get_ip_block(adev, AMD_IP_BLOCK_TYPE_GFX); if (ip_block) { /* Compute has a VM bug for GFX version < 7. Compute has a VM bug for GFX 8 MEC firmware version < 673.*/ if (ip_block->version->major <= 7) has_compute_vm_bug = true; else if (ip_block->version->major == 8) if (adev->gfx.mec_fw_version < 673) has_compute_vm_bug = true; } for (i = 0; i < adev->num_rings; i++) { ring = adev->rings[i]; if (ring->funcs->type == AMDGPU_RING_TYPE_COMPUTE) /* only compute rings */ ring->has_compute_vm_bug = has_compute_vm_bug; else ring->has_compute_vm_bug = false; } } bool amdgpu_vm_need_pipeline_sync(struct amdgpu_ring *ring, struct amdgpu_job *job) { struct amdgpu_device *adev = ring->adev; unsigned vmhub = ring->funcs->vmhub; struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; struct amdgpu_vmid *id; bool gds_switch_needed; bool vm_flush_needed = job->vm_needs_flush || ring->has_compute_vm_bug; if (job->vmid == 0) return false; id = &id_mgr->ids[job->vmid]; gds_switch_needed = ring->funcs->emit_gds_switch && ( id->gds_base != job->gds_base || id->gds_size != job->gds_size || id->gws_base != job->gws_base || id->gws_size != job->gws_size || id->oa_base != job->oa_base || id->oa_size != job->oa_size); if (amdgpu_vmid_had_gpu_reset(adev, id)) return true; return vm_flush_needed || gds_switch_needed; } static bool amdgpu_vm_is_large_bar(struct amdgpu_device *adev) { return (adev->mc.real_vram_size == adev->mc.visible_vram_size); } /** * amdgpu_vm_flush - hardware flush the vm * * @ring: ring to use for flush * @vmid: vmid number to use * @pd_addr: address of the page directory * * Emit a VM flush when it is necessary. */ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, bool need_pipe_sync) { struct amdgpu_device *adev = ring->adev; unsigned vmhub = ring->funcs->vmhub; struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; struct amdgpu_vmid *id = &id_mgr->ids[job->vmid]; bool gds_switch_needed = ring->funcs->emit_gds_switch && ( id->gds_base != job->gds_base || id->gds_size != job->gds_size || id->gws_base != job->gws_base || id->gws_size != job->gws_size || id->oa_base != job->oa_base || id->oa_size != job->oa_size); bool vm_flush_needed = job->vm_needs_flush; unsigned patch_offset = 0; int r; if (amdgpu_vmid_had_gpu_reset(adev, id)) { gds_switch_needed = true; vm_flush_needed = true; }