1797 lines · 49.5 KB · C
RawDownload
/* * Copyright 2018 Advanced Micro Devices, Inc. * All Rights Reserved. * * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * */ #include <linux/io-64-nonatomic-lo-hi.h>#ifdef CONFIG_X86#include <asm/hypervisor.h>#endif #include "amdgpu.h"#include "amdgpu_gmc.h"#include "amdgpu_ras.h"#include "amdgpu_reset.h"#include "amdgpu_xgmi.h"#include "amdgpu_atomfirmware.h" #include <drm/drm_drv.h>#include <drm/ttm/ttm_tt.h> static const u64 four_gb = 0x100000000ULL; bool amdgpu_gmc_is_pdb0_enabled(struct amdgpu_device *adev){	return adev->gmc.xgmi.connected_to_cpu || amdgpu_virt_xgmi_migrate_enabled(adev);} /** * amdgpu_gmc_pdb0_alloc - allocate vram for pdb0 * * @adev: amdgpu_device pointer * * Allocate video memory for pdb0 and map it for CPU access * Returns 0 for success, error for failure. */int amdgpu_gmc_pdb0_alloc(struct amdgpu_device *adev){	int r;	struct amdgpu_bo_param bp;	u64 vram_size = adev->gmc.xgmi.node_segment_size * adev->gmc.xgmi.num_physical_nodes;	uint32_t pde0_page_shift = adev->gmc.vmid0_page_table_block_size + 21;	uint32_t npdes = (vram_size + (1ULL << pde0_page_shift) - 1) >> pde0_page_shift; 	memset(&bp, 0, sizeof(bp));	bp.size = PAGE_ALIGN((npdes + 1) * 8);	bp.byte_align = PAGE_SIZE;	bp.domain = AMDGPU_GEM_DOMAIN_VRAM;	bp.flags = AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED |		AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS;	bp.type = ttm_bo_type_kernel;	bp.resv = NULL;	bp.bo_ptr_size = sizeof(struct amdgpu_bo); 	r = amdgpu_bo_create(adev, &bp, &adev->gmc.pdb0_bo);	if (r)		return r; 	r = amdgpu_bo_reserve(adev->gmc.pdb0_bo, false);	if (unlikely(r != 0))		goto bo_reserve_failure; 	r = amdgpu_bo_pin(adev->gmc.pdb0_bo, AMDGPU_GEM_DOMAIN_VRAM);	if (r)		goto bo_pin_failure;	r = amdgpu_bo_kmap(adev->gmc.pdb0_bo, &adev->gmc.ptr_pdb0);	if (r)		goto bo_kmap_failure; 	amdgpu_bo_unreserve(adev->gmc.pdb0_bo);	return 0; bo_kmap_failure:	amdgpu_bo_unpin(adev->gmc.pdb0_bo);bo_pin_failure:	amdgpu_bo_unreserve(adev->gmc.pdb0_bo);bo_reserve_failure:	amdgpu_bo_unref(&adev->gmc.pdb0_bo);	return r;} /** * amdgpu_gmc_get_pde_for_bo - get the PDE for a BO * * @bo: the BO to get the PDE for * @level: the level in the PD hirarchy * @addr: resulting addr * @flags: resulting flags * * Get the address and flags to be used for a PDE (Page Directory Entry). */void amdgpu_gmc_get_pde_for_bo(struct amdgpu_bo *bo, int level,			       uint64_t *addr, uint64_t *flags){	struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); 	switch (bo->tbo.resource->mem_type) {	case TTM_PL_TT:		*addr = bo->tbo.ttm->dma_address[0];		break;	case TTM_PL_VRAM:		*addr = amdgpu_bo_gpu_offset(bo);		break;	default:		*addr = 0;		break;	}	*flags = amdgpu_ttm_tt_pde_flags(bo->tbo.ttm, bo->tbo.resource);	amdgpu_gmc_get_vm_pde(adev, level, addr, flags);} /* * amdgpu_gmc_pd_addr - return the address of the root directory */uint64_t amdgpu_gmc_pd_addr(struct amdgpu_bo *bo){	struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev);	uint64_t pd_addr; 	/* TODO: move that into ASIC specific code */	if (adev->asic_type >= CHIP_VEGA10) {		uint64_t flags = AMDGPU_PTE_VALID; 		amdgpu_gmc_get_pde_for_bo(bo, -1, &pd_addr, &flags);		pd_addr |= flags;	} else {		pd_addr = amdgpu_bo_gpu_offset(bo);	}	return pd_addr;} /** * amdgpu_gmc_set_pte_pde - update the page tables using CPU * * @adev: amdgpu_device pointer * @cpu_pt_addr: cpu address of the page table * @gpu_page_idx: entry in the page table to update * @addr: dst addr to write into pte/pde * @flags: access flags * * Update the page tables using CPU. */int amdgpu_gmc_set_pte_pde(struct amdgpu_device *adev, void *cpu_pt_addr,				uint32_t gpu_page_idx, uint64_t addr,				uint64_t flags){	void __iomem *ptr = (void *)cpu_pt_addr;	uint64_t value; 	/*	 * The following is for PTE only. GART does not have PDEs.	*/	value = addr & adev->gmc.pte_addr_mask;	value |= flags;	writeq(value, ptr + (gpu_page_idx * 8)); 	return 0;} /** * amdgpu_gmc_agp_addr - return the address in the AGP address space * * @bo: TTM BO which needs the address, must be in GTT domain * * Tries to figure out how to access the BO through the AGP aperture. Returns * AMDGPU_BO_INVALID_OFFSET if that is not possible. */uint64_t amdgpu_gmc_agp_addr(struct ttm_buffer_object *bo){	struct amdgpu_device *adev = amdgpu_ttm_adev(bo->bdev); 	if (!bo->ttm)		return AMDGPU_BO_INVALID_OFFSET; 	if (bo->ttm->num_pages != 1 || bo->ttm->caching == ttm_cached)		return AMDGPU_BO_INVALID_OFFSET; 	if (bo->ttm->dma_address[0] + PAGE_SIZE >= adev->gmc.agp_size)		return AMDGPU_BO_INVALID_OFFSET; 	return adev->gmc.agp_start + bo->ttm->dma_address[0];} /** * amdgpu_gmc_vram_location - try to find VRAM location * * @adev: amdgpu device structure holding all necessary information * @mc: memory controller structure holding memory information * @base: base address at which to put VRAM * * Function will try to place VRAM at base address provided * as parameter. */void amdgpu_gmc_vram_location(struct amdgpu_device *adev, struct amdgpu_gmc *mc,			      u64 base){	uint64_t vis_limit = (uint64_t)amdgpu_vis_vram_limit << 20;	uint64_t limit = (uint64_t)amdgpu_vram_limit << 20; 	mc->vram_start = base;	mc->vram_end = mc->vram_start + mc->mc_vram_size - 1;	if (limit < mc->real_vram_size)		mc->real_vram_size = limit; 	if (vis_limit && vis_limit < mc->visible_vram_size)		mc->visible_vram_size = vis_limit; 	if (mc->real_vram_size < mc->visible_vram_size)		mc->visible_vram_size = mc->real_vram_size; 	if (mc->xgmi.num_physical_nodes == 0) {		mc->fb_start = mc->vram_start;		mc->fb_end = mc->vram_end;	}	dev_info(adev->dev, "VRAM: %lluM 0x%016llX - 0x%016llX (%lluM used)\n",			mc->mc_vram_size >> 20, mc->vram_start,			mc->vram_end, mc->real_vram_size >> 20);} /** amdgpu_gmc_sysvm_location - place vram and gart in sysvm aperture * * @adev: amdgpu device structure holding all necessary information * @mc: memory controller structure holding memory information * * This function is only used if use GART for FB translation. In such * case, we use sysvm aperture (vmid0 page tables) for both vram * and gart (aka system memory) access. * * GPUVM (and our organization of vmid0 page tables) require sysvm * aperture to be placed at a location aligned with 8 times of native * page size. For example, if vm_context0_cntl.page_table_block_size * is 12, then native page size is 8G (2M*2^12), sysvm should start * with a 64G aligned address. For simplicity, we just put sysvm at * address 0. So vram start at address 0 and gart is right after vram. */void amdgpu_gmc_sysvm_location(struct amdgpu_device *adev, struct amdgpu_gmc *mc){	u64 hive_vram_start = 0;	u64 hive_vram_end = mc->xgmi.node_segment_size * mc->xgmi.num_physical_nodes - 1;	mc->vram_start = mc->xgmi.node_segment_size * mc->xgmi.physical_node_id;	mc->vram_end = mc->vram_start + mc->xgmi.node_segment_size - 1;	/* node_segment_size may not 4GB aligned on SRIOV, align up is needed. */	mc->gart_start = ALIGN(hive_vram_end + 1, four_gb);	mc->gart_end = mc->gart_start + mc->gart_size - 1;	if (amdgpu_virt_xgmi_migrate_enabled(adev)) {		/* set mc->vram_start to 0 to switch the returned GPU address of		 * amdgpu_bo_create_reserved() from FB aperture to GART aperture.		 */		mc->vram_start = 0;		mc->vram_end = mc->vram_start + mc->mc_vram_size - 1;		mc->visible_vram_size = min(mc->visible_vram_size, mc->real_vram_size);	} else {		mc->fb_start = hive_vram_start;		mc->fb_end = hive_vram_end;	}	dev_info(adev->dev, "VRAM: %lluM 0x%016llX - 0x%016llX (%lluM used)\n",			mc->mc_vram_size >> 20, mc->vram_start,			mc->vram_end, mc->real_vram_size >> 20);	dev_info(adev->dev, "GART: %lluM 0x%016llX - 0x%016llX\n",			mc->gart_size >> 20, mc->gart_start, mc->gart_end);} void amdgpu_gmc_set_gart_size(struct amdgpu_device *adev, u64 default_size){	if (amdgpu_gart_size == -1)		adev->gmc.gart_size =			default_size + adev->pm.smu_prv_buffer_size;	else		adev->gmc.gart_size = (u64)amdgpu_gart_size << 20;} /** * amdgpu_gmc_gart_location - try to find GART location * * @adev: amdgpu device structure holding all necessary information * @mc: memory controller structure holding memory information * @gart_placement: GART placement policy with respect to VRAM * * Function will try to place GART before or after VRAM. * If GART size is bigger than space left then we ajust GART size. * Thus function will never fails. */void amdgpu_gmc_gart_location(struct amdgpu_device *adev, struct amdgpu_gmc *mc,			      enum amdgpu_gart_placement gart_placement){	u64 size_af, size_bf;	/*To avoid the hole, limit the max mc address to AMDGPU_GMC_HOLE_START*/	u64 max_mc_address = min(adev->gmc.mc_mask, AMDGPU_GMC_HOLE_START - 1); 	/* VCE doesn't like it when BOs cross a 4GB segment, so align	 * the GART base on a 4GB boundary as well.	 */	size_bf = mc->fb_start;	size_af = max_mc_address + 1 - ALIGN(mc->fb_end + 1, four_gb); 	if (mc->gart_size > max(size_bf, size_af)) {		dev_warn(adev->dev, "limiting GART\n");		mc->gart_size = max(size_bf, size_af);	} 	switch (gart_placement) {	case AMDGPU_GART_PLACEMENT_HIGH:		mc->gart_start = max_mc_address - mc->gart_size + 1;		break;	case AMDGPU_GART_PLACEMENT_LOW:		if (size_bf >= mc->gart_size)			mc->gart_start = 0;		else			mc->gart_start = ALIGN(mc->fb_end, four_gb);		break;	case AMDGPU_GART_PLACEMENT_BEST_FIT:	default:		if ((size_bf >= mc->gart_size && size_bf < size_af) ||		    (size_af < mc->gart_size))			mc->gart_start = 0;		else			mc->gart_start = max_mc_address - mc->gart_size + 1;		break;	} 	mc->gart_start &= ~(four_gb - 1);	mc->gart_end = mc->gart_start + mc->gart_size - 1;	dev_info(adev->dev, "GART: %lluM 0x%016llX - 0x%016llX\n",			mc->gart_size >> 20, mc->gart_start, mc->gart_end);} /** * amdgpu_gmc_agp_location - try to find AGP location * @adev: amdgpu device structure holding all necessary information * @mc: memory controller structure holding memory information * * Function will place try to find a place for the AGP BAR in the MC address * space. * * AGP BAR will be assigned the largest available hole in the address space. * Should be called after VRAM and GART locations are setup. */void amdgpu_gmc_agp_location(struct amdgpu_device *adev, struct amdgpu_gmc *mc){	const uint64_t sixteen_gb = 1ULL << 34;	const uint64_t sixteen_gb_mask = ~(sixteen_gb - 1);	u64 size_af, size_bf; 	if (mc->fb_start > mc->gart_start) {		size_bf = (mc->fb_start & sixteen_gb_mask) -			ALIGN(mc->gart_end + 1, sixteen_gb);		size_af = mc->mc_mask + 1 - ALIGN(mc->fb_end + 1, sixteen_gb);	} else {		size_bf = mc->fb_start & sixteen_gb_mask;		size_af = (mc->gart_start & sixteen_gb_mask) -			ALIGN(mc->fb_end + 1, sixteen_gb);	} 	if (size_bf > size_af) {		mc->agp_start = (mc->fb_start - size_bf) & sixteen_gb_mask;		mc->agp_size = size_bf;	} else {		mc->agp_start = ALIGN(mc->fb_end + 1, sixteen_gb);		mc->agp_size = size_af;	} 	mc->agp_end = mc->agp_start + mc->agp_size - 1;	dev_info(adev->dev, "AGP: %lluM 0x%016llX - 0x%016llX\n",			mc->agp_size >> 20, mc->agp_start, mc->agp_end);} /** * amdgpu_gmc_set_agp_default - Set the default AGP aperture value. * @adev: amdgpu device structure holding all necessary information * @mc: memory controller structure holding memory information * * To disable the AGP aperture, you need to set the start to a larger * value than the end.  This function sets the default value which * can then be overridden using amdgpu_gmc_agp_location() if you want * to enable the AGP aperture on a specific chip. * */void amdgpu_gmc_set_agp_default(struct amdgpu_device *adev,				struct amdgpu_gmc *mc){	mc->agp_start = 0xffffffffffff;	mc->agp_end = 0;	mc->agp_size = 0;} /** * amdgpu_gmc_fault_key - get hask key from vm fault address and pasid * * @addr: 48 bit physical address, page aligned (36 significant bits) * @pasid: 16 bit process address space identifier */static inline uint64_t amdgpu_gmc_fault_key(uint64_t addr, uint16_t pasid){	return addr << 4 | pasid;} /** * amdgpu_gmc_filter_faults - filter VM faults * * @adev: amdgpu device structure * @ih: interrupt ring that the fault received from * @addr: address of the VM fault * @pasid: PASID of the process causing the fault * @timestamp: timestamp of the fault * * Returns: * True if the fault was filtered and should not be processed further. * False if the fault is a new one and needs to be handled. */bool amdgpu_gmc_filter_faults(struct amdgpu_device *adev,			      struct amdgpu_ih_ring *ih, uint64_t addr,			      uint16_t pasid, uint64_t timestamp){	struct amdgpu_gmc *gmc = &adev->gmc;	uint64_t stamp, key = amdgpu_gmc_fault_key(addr, pasid);	struct amdgpu_gmc_fault *fault;	uint32_t hash; 	/* Stale retry fault if timestamp goes backward */	if (amdgpu_ih_ts_after(timestamp, ih->processed_timestamp))		return true; 	/* If we don't have space left in the ring buffer return immediately */	stamp = max(timestamp, AMDGPU_GMC_FAULT_TIMEOUT + 1) -		AMDGPU_GMC_FAULT_TIMEOUT;	if (gmc->fault_ring[gmc->last_fault].timestamp >= stamp)		return true; 	/* Try to find the fault in the hash */	hash = hash_64(key, AMDGPU_GMC_FAULT_HASH_ORDER);	fault = &gmc->fault_ring[gmc->fault_hash[hash].idx];	while (fault->timestamp >= stamp) {		uint64_t tmp; 		if (atomic64_read(&fault->key) == key) {			/*			 * if we get a fault which is already present in			 * the fault_ring and the timestamp of			 * the fault is after the expired timestamp,			 * then this is a new fault that needs to be added			 * into the fault ring.			 */			if (fault->timestamp_expiry != 0 &&			    amdgpu_ih_ts_after(fault->timestamp_expiry,					       timestamp))				break;			else				return true;		} 		tmp = fault->timestamp;		fault = &gmc->fault_ring[fault->next]; 		/* Check if the entry was reused */		if (fault->timestamp >= tmp)			break;	} 	/* Add the fault to the ring */	fault = &gmc->fault_ring[gmc->last_fault];	atomic64_set(&fault->key, key);	fault->timestamp = timestamp; 	/* And update the hash */	fault->next = gmc->fault_hash[hash].idx;	gmc->fault_hash[hash].idx = gmc->last_fault++;	return false;} /** * amdgpu_gmc_filter_faults_remove - remove address from VM faults filter * * @adev: amdgpu device structure * @addr: address of the VM fault * @pasid: PASID of the process causing the fault * * Remove the address from fault filter, then future vm fault on this address * will pass to retry fault handler to recover. */void amdgpu_gmc_filter_faults_remove(struct amdgpu_device *adev, uint64_t addr,

Lines 1–500 of 1797.