/* * 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. Load more
/* * 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,