3039 lines · 75.9 KB · C
RawDownload
/* * 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"#include "amdgpu_amdkfd.h"#include "amdgpu_gmc.h" /** * DOC: 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 /** * struct amdgpu_pte_update_params - Local structure * * Encapsulate some VM table update parameters to reduce * the number of function parameters * */struct amdgpu_pte_update_params { 	/**	 * @adev: amdgpu device we do this update for	 */	struct amdgpu_device *adev; 	/**	 * @vm: optional amdgpu_vm we do this update for	 */	struct amdgpu_vm *vm; 	/**	 * @src: address where to copy page table entries from	 */	uint64_t src; 	/**	 * @ib: indirect buffer to fill with commands	 */	struct amdgpu_ib *ib; 	/**	 * @func: Function which actually does the update	 */	void (*func)(struct amdgpu_pte_update_params *params,		     struct amdgpu_bo *bo, uint64_t pe,		     uint64_t addr, unsigned count, uint32_t incr,		     uint64_t flags);	/**	 * @pages_addr:	 *	 * DMA addresses to use for mapping, used during VM update by CPU	 */	dma_addr_t *pages_addr; 	/**	 * @kptr:	 *	 * Kernel pointer of PD/PT BO that needs to be updated,	 * used during VM update by CPU	 */	void *kptr;}; /** * struct amdgpu_prt_cb - Helper to disable partial resident texture feature from a fence callback */struct amdgpu_prt_cb { 	/**	 * @adev: amdgpu device	 */	struct amdgpu_device *adev; 	/**	 * @cb: callback	 */	struct dma_fence_cb cb;}; /** * amdgpu_vm_bo_base_init - Adds bo to the list of bos associated with the vm * * @base: base structure for tracking BO usage in a VM * @vm: vm to which bo is to be added * @bo: amdgpu buffer object * * Initialize a bo_va_base structure and add it to the appropriate lists * */static void amdgpu_vm_bo_base_init(struct amdgpu_vm_bo_base *base,				   struct amdgpu_vm *vm,				   struct amdgpu_bo *bo){	base->vm = vm;	base->bo = bo;	INIT_LIST_HEAD(&base->bo_list);	INIT_LIST_HEAD(&base->vm_status); 	if (!bo)		return;	list_add_tail(&base->bo_list, &bo->va); 	if (bo->tbo.type == ttm_bo_type_kernel)		list_move(&base->vm_status, &vm->relocated); 	if (bo->tbo.resv != vm->root.base.bo->tbo.resv)		return; 	if (bo->preferred_domains &	    amdgpu_mem_type_to_domain(bo->tbo.mem.mem_type))		return; 	/*	 * we checked all the prerequisites, but it looks like this per vm bo	 * is currently evicted. add the bo to the evicted list to make sure it	 * is validated on next vm use to avoid fault.	 * */	list_move_tail(&base->vm_status, &vm->evicted);	base->moved = true;} /** * amdgpu_vm_level_shift - return the addr shift for each level * * @adev: amdgpu_device pointer * @level: VMPT level * * 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 * @level: VMPT level * * Returns: * 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 * @level: VMPT level * * Returns: * 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. * * Returns: * Validation result. */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;	struct amdgpu_vm_bo_base *bo_base, *tmp;	int r = 0; 	list_for_each_entry_safe(bo_base, tmp, &vm->evicted, vm_status) {		struct amdgpu_bo *bo = bo_base->bo; 		if (bo->parent) {			r = validate(param, bo);			if (r)				break; 			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) {			spin_lock(&vm->moved_lock);			list_move(&bo_base->vm_status, &vm->moved);			spin_unlock(&vm->moved_lock);		} else {			list_move(&bo_base->vm_status, &vm->relocated);		}	} 	spin_lock(&glob->lru_lock);	list_for_each_entry(bo_base, &vm->idle, vm_status) {		struct amdgpu_bo *bo = bo_base->bo; 		if (!bo->parent)			continue; 		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); 	return r;} /** * amdgpu_vm_ready - check VM is ready for updates * * @vm: VM to check * * Check if all VM PDs/PTs are ready for updates * * Returns: * True if eviction list is empty. */bool amdgpu_vm_ready(struct amdgpu_vm *vm){	return list_empty(&vm->evicted);} /** * amdgpu_vm_clear_bo - initially clear the PDs/PTs * * @adev: amdgpu_device pointer * @vm: VM to clear BO from * @bo: BO to clear * @level: level this BO is at * @pte_support_ats: indicate ATS support from PTE * * Root PD needs to be reserved when calling this. * * Returns: * 0 on success, errno otherwise. */static int amdgpu_vm_clear_bo(struct amdgpu_device *adev,			      struct amdgpu_vm *vm, struct amdgpu_bo *bo,			      unsigned level, bool pte_support_ats){	struct ttm_operation_ctx ctx = { true, false };	struct dma_fence *fence = NULL;	unsigned entries, ats_entries;	struct amdgpu_ring *ring;	struct amdgpu_job *job;	uint64_t addr;	int r; 	entries = amdgpu_bo_size(bo) / 8; 	if (pte_support_ats) {		if (level == adev->vm_manager.root_level) {			ats_entries = amdgpu_vm_level_shift(adev, level);			ats_entries += AMDGPU_GPU_PAGE_SHIFT;			ats_entries = AMDGPU_VA_HOLE_START >> ats_entries;			ats_entries = min(ats_entries, entries);			entries -= ats_entries;		} else {			ats_entries = entries;			entries = 0;		}	} else {		ats_entries = 0;	} 	ring = container_of(vm->entity.rq->sched, struct amdgpu_ring, sched); 	r = reservation_object_reserve_shared(bo->tbo.resv);	if (r)		return r; 	r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx);	if (r)		goto error; 	r = amdgpu_job_alloc_with_ib(adev, 64, &job);	if (r)		goto error; 	addr = amdgpu_bo_gpu_offset(bo);	if (ats_entries) {		uint64_t ats_value; 		ats_value = AMDGPU_PTE_DEFAULT_ATC;		if (level != AMDGPU_VM_PTB)			ats_value |= AMDGPU_PDE_PTE; 		amdgpu_vm_set_pte_pde(adev, &job->ibs[0], addr, 0,				      ats_entries, 0, ats_value);		addr += ats_entries * 8;	} 	if (entries)		amdgpu_vm_set_pte_pde(adev, &job->ibs[0], addr, 0,				      entries, 0, 0); 	amdgpu_ring_pad_ib(ring, &job->ibs[0]); 	WARN_ON(job->ibs[0].length_dw > 64);	r = amdgpu_sync_resv(adev, &job->sync, bo->tbo.resv,			     AMDGPU_FENCE_OWNER_UNDEFINED, false);	if (r)		goto error_free; 	r = amdgpu_job_submit(job, &vm->entity, AMDGPU_FENCE_OWNER_UNDEFINED,			      &fence);	if (r)		goto error_free; 	amdgpu_bo_fence(bo, fence, true);	dma_fence_put(fence); 	if (bo->shadow)		return amdgpu_vm_clear_bo(adev, vm, bo->shadow,					  level, pte_support_ats); 	return 0; error_free:	amdgpu_job_free(job); error:	return r;} /** * amdgpu_vm_alloc_levels - allocate the PD/PT levels * * @adev: amdgpu_device pointer * @vm: requested vm * @parent: parent PT * @saddr: start of the address range * @eaddr: end of the address range * @level: VMPT level * @ats: indicate ATS support from PTE * * Make sure the page directories and page tables are allocated * * Returns: * 0 on success, errno otherwise. */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, bool ats){	unsigned shift = amdgpu_vm_level_shift(adev, level);	unsigned pt_idx, from, to;	u64 flags;	int r; 	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;	if (vm->root.base.bo->shadow)		flags |= AMDGPU_GEM_CREATE_SHADOW;

Lines 1–500 of 3039.