Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/amdgpu: Add PASID management

Allows assigning a PASID to a VM for identifying VMs involved in page
faults. The global PASID manager is also exported in the KFD
interface so that AMDGPU and KFD can share the PASID space.

PASIDs of different sizes can be requested. On APUs, the PASID size
is deterined by the capabilities of the IOMMU. So KFD must be able
to allocate PASIDs in a smaller range.

Signed-off-by: Felix Kuehling <Felix.Kuehling@amd.com>
Acked-by: Alex Deucher <alexander.deucher@amd.com>
Reviewed-by: Oded Gabbay <oded.gabbay@gmail.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>

authored by

Felix Kuehling and committed by
Alex Deucher
02208441 ca290da8

+97 -4
+2
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v7.c
··· 169 169 .get_vmem_size = get_vmem_size, 170 170 .get_gpu_clock_counter = get_gpu_clock_counter, 171 171 .get_max_engine_clock_in_mhz = get_max_engine_clock_in_mhz, 172 + .alloc_pasid = amdgpu_vm_alloc_pasid, 173 + .free_pasid = amdgpu_vm_free_pasid, 172 174 .program_sh_mem_settings = kgd_program_sh_mem_settings, 173 175 .set_pasid_vmid_mapping = kgd_set_pasid_vmid_mapping, 174 176 .init_pipeline = kgd_init_pipeline,
+2
drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v8.c
··· 128 128 .get_vmem_size = get_vmem_size, 129 129 .get_gpu_clock_counter = get_gpu_clock_counter, 130 130 .get_max_engine_clock_in_mhz = get_max_engine_clock_in_mhz, 131 + .alloc_pasid = amdgpu_vm_alloc_pasid, 132 + .free_pasid = amdgpu_vm_free_pasid, 131 133 .program_sh_mem_settings = kgd_program_sh_mem_settings, 132 134 .set_pasid_vmid_mapping = kgd_set_pasid_vmid_mapping, 133 135 .init_pipeline = kgd_init_pipeline,
+1 -1
drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c
··· 825 825 } 826 826 827 827 r = amdgpu_vm_init(adev, &fpriv->vm, 828 - AMDGPU_VM_CONTEXT_GFX); 828 + AMDGPU_VM_CONTEXT_GFX, 0); 829 829 if (r) { 830 830 kfree(fpriv); 831 831 goto out_suspend;
+74 -1
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c
··· 27 27 */ 28 28 #include <linux/dma-fence-array.h> 29 29 #include <linux/interval_tree_generic.h> 30 + #include <linux/idr.h> 30 31 #include <drm/drmP.h> 31 32 #include <drm/amdgpu_drm.h> 32 33 #include "amdgpu.h" 33 34 #include "amdgpu_trace.h" 35 + 36 + /* 37 + * PASID manager 38 + * 39 + * PASIDs are global address space identifiers that can be shared 40 + * between the GPU, an IOMMU and the driver. VMs on different devices 41 + * may use the same PASID if they share the same address 42 + * space. Therefore PASIDs are allocated using a global IDA. VMs are 43 + * looked up from the PASID per amdgpu_device. 44 + */ 45 + static DEFINE_IDA(amdgpu_vm_pasid_ida); 46 + 47 + /** 48 + * amdgpu_vm_alloc_pasid - Allocate a PASID 49 + * @bits: Maximum width of the PASID in bits, must be at least 1 50 + * 51 + * Allocates a PASID of the given width while keeping smaller PASIDs 52 + * available if possible. 53 + * 54 + * Returns a positive integer on success. Returns %-EINVAL if bits==0. 55 + * Returns %-ENOSPC if no PASID was available. Returns %-ENOMEM on 56 + * memory allocation failure. 57 + */ 58 + int amdgpu_vm_alloc_pasid(unsigned int bits) 59 + { 60 + int pasid = -EINVAL; 61 + 62 + for (bits = min(bits, 31U); bits > 0; bits--) { 63 + pasid = ida_simple_get(&amdgpu_vm_pasid_ida, 64 + 1U << (bits - 1), 1U << bits, 65 + GFP_KERNEL); 66 + if (pasid != -ENOSPC) 67 + break; 68 + } 69 + 70 + return pasid; 71 + } 72 + 73 + /** 74 + * amdgpu_vm_free_pasid - Free a PASID 75 + * @pasid: PASID to free 76 + */ 77 + void amdgpu_vm_free_pasid(unsigned int pasid) 78 + { 79 + ida_simple_remove(&amdgpu_vm_pasid_ida, pasid); 80 + } 34 81 35 82 /* 36 83 * GPUVM ··· 2586 2539 * Init @vm fields. 2587 2540 */ 2588 2541 int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm, 2589 - int vm_context) 2542 + int vm_context, unsigned int pasid) 2590 2543 { 2591 2544 const unsigned align = min(AMDGPU_VM_PTB_ALIGN_SIZE, 2592 2545 AMDGPU_VM_PTE_COUNT(adev) * 8); ··· 2667 2620 goto error_free_root; 2668 2621 } 2669 2622 2623 + if (pasid) { 2624 + unsigned long flags; 2625 + 2626 + spin_lock_irqsave(&adev->vm_manager.pasid_lock, flags); 2627 + r = idr_alloc(&adev->vm_manager.pasid_idr, vm, pasid, pasid + 1, 2628 + GFP_ATOMIC); 2629 + spin_unlock_irqrestore(&adev->vm_manager.pasid_lock, flags); 2630 + if (r < 0) 2631 + goto error_free_root; 2632 + 2633 + vm->pasid = pasid; 2634 + } 2635 + 2670 2636 return 0; 2671 2637 2672 2638 error_free_root: ··· 2732 2672 struct amdgpu_bo_va_mapping *mapping, *tmp; 2733 2673 bool prt_fini_needed = !!adev->gart.gart_funcs->set_prt; 2734 2674 int i; 2675 + 2676 + if (vm->pasid) { 2677 + unsigned long flags; 2678 + 2679 + spin_lock_irqsave(&adev->vm_manager.pasid_lock, flags); 2680 + idr_remove(&adev->vm_manager.pasid_idr, vm->pasid); 2681 + spin_unlock_irqrestore(&adev->vm_manager.pasid_lock, flags); 2682 + } 2735 2683 2736 2684 amd_sched_entity_fini(vm->entity.sched, &vm->entity); 2737 2685 ··· 2820 2752 adev->vm_manager.vm_update_mode = 0; 2821 2753 #endif 2822 2754 2755 + idr_init(&adev->vm_manager.pasid_idr); 2756 + spin_lock_init(&adev->vm_manager.pasid_lock); 2823 2757 } 2824 2758 2825 2759 /** ··· 2834 2764 void amdgpu_vm_manager_fini(struct amdgpu_device *adev) 2835 2765 { 2836 2766 unsigned i, j; 2767 + 2768 + WARN_ON(!idr_is_empty(&adev->vm_manager.pasid_idr)); 2769 + idr_destroy(&adev->vm_manager.pasid_idr); 2837 2770 2838 2771 for (i = 0; i < AMDGPU_MAX_VMHUBS; ++i) { 2839 2772 struct amdgpu_vm_id_manager *id_mgr =
+12 -2
drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h
··· 25 25 #define __AMDGPU_VM_H__ 26 26 27 27 #include <linux/rbtree.h> 28 + #include <linux/idr.h> 28 29 29 30 #include "gpu_scheduler.h" 30 31 #include "amdgpu_sync.h" ··· 149 148 /* Scheduler entity for page table updates */ 150 149 struct amd_sched_entity entity; 151 150 152 - /* client id */ 151 + /* client id and PASID (TODO: replace client_id with PASID) */ 153 152 u64 client_id; 153 + unsigned int pasid; 154 154 /* dedicated to vm */ 155 155 struct amdgpu_vm_id *reserved_vmid[AMDGPU_MAX_VMHUBS]; 156 156 ··· 222 220 * BIT1[= 0] Compute updated by SDMA [= 1] by CPU 223 221 */ 224 222 int vm_update_mode; 223 + 224 + /* PASID to VM mapping, will be used in interrupt context to 225 + * look up VM of a page fault 226 + */ 227 + struct idr pasid_idr; 228 + spinlock_t pasid_lock; 225 229 }; 226 230 231 + int amdgpu_vm_alloc_pasid(unsigned int bits); 232 + void amdgpu_vm_free_pasid(unsigned int pasid); 227 233 void amdgpu_vm_manager_init(struct amdgpu_device *adev); 228 234 void amdgpu_vm_manager_fini(struct amdgpu_device *adev); 229 235 int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm, 230 - int vm_context); 236 + int vm_context, unsigned int pasid); 231 237 void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm); 232 238 void amdgpu_vm_get_pd_bo(struct amdgpu_vm *vm, 233 239 struct list_head *validated,
+6
drivers/gpu/drm/amd/include/kgd_kfd_interface.h
··· 112 112 * 113 113 * @get_max_engine_clock_in_mhz: Retrieves maximum GPU clock in MHz 114 114 * 115 + * @alloc_pasid: Allocate a PASID 116 + * @free_pasid: Free a PASID 117 + * 115 118 * @program_sh_mem_settings: A function that should initiate the memory 116 119 * properties such as main aperture memory type (cache / non cached) and 117 120 * secondary aperture base address, size and memory type. ··· 162 159 uint64_t (*get_gpu_clock_counter)(struct kgd_dev *kgd); 163 160 164 161 uint32_t (*get_max_engine_clock_in_mhz)(struct kgd_dev *kgd); 162 + 163 + int (*alloc_pasid)(unsigned int bits); 164 + void (*free_pasid)(unsigned int pasid); 165 165 166 166 /* Register access functions */ 167 167 void (*program_sh_mem_settings)(struct kgd_dev *kgd, uint32_t vmid,