diff options
Diffstat (limited to 'drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | 75 |
1 files changed, 74 insertions, 1 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 2196bca7331c..9b795915cab1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c | |||
@@ -27,12 +27,59 @@ | |||
27 | */ | 27 | */ |
28 | #include <linux/dma-fence-array.h> | 28 | #include <linux/dma-fence-array.h> |
29 | #include <linux/interval_tree_generic.h> | 29 | #include <linux/interval_tree_generic.h> |
30 | #include <linux/idr.h> | ||
30 | #include <drm/drmP.h> | 31 | #include <drm/drmP.h> |
31 | #include <drm/amdgpu_drm.h> | 32 | #include <drm/amdgpu_drm.h> |
32 | #include "amdgpu.h" | 33 | #include "amdgpu.h" |
33 | #include "amdgpu_trace.h" | 34 | #include "amdgpu_trace.h" |
34 | 35 | ||
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 | } | ||
81 | |||
82 | /* | ||
36 | * GPUVM | 83 | * GPUVM |
37 | * GPUVM is similar to the legacy gart on older asics, however | 84 | * GPUVM is similar to the legacy gart on older asics, however |
38 | * rather than there being a single global gart table | 85 | * rather than there being a single global gart table |
@@ -2539,7 +2586,7 @@ void amdgpu_vm_adjust_size(struct amdgpu_device *adev, uint64_t vm_size, uint32_ | |||
2539 | * Init @vm fields. | 2586 | * Init @vm fields. |
2540 | */ | 2587 | */ |
2541 | int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm, | 2588 | int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm, |
2542 | int vm_context) | 2589 | int vm_context, unsigned int pasid) |
2543 | { | 2590 | { |
2544 | const unsigned align = min(AMDGPU_VM_PTB_ALIGN_SIZE, | 2591 | const unsigned align = min(AMDGPU_VM_PTB_ALIGN_SIZE, |
2545 | AMDGPU_VM_PTE_COUNT(adev) * 8); | 2592 | AMDGPU_VM_PTE_COUNT(adev) * 8); |
@@ -2620,6 +2667,19 @@ int amdgpu_vm_init(struct amdgpu_device *adev, struct amdgpu_vm *vm, | |||
2620 | goto error_free_root; | 2667 | goto error_free_root; |
2621 | } | 2668 | } |
2622 | 2669 | ||
2670 | if (pasid) { | ||
2671 | unsigned long flags; | ||
2672 | |||
2673 | spin_lock_irqsave(&adev->vm_manager.pasid_lock, flags); | ||
2674 | r = idr_alloc(&adev->vm_manager.pasid_idr, vm, pasid, pasid + 1, | ||
2675 | GFP_ATOMIC); | ||
2676 | spin_unlock_irqrestore(&adev->vm_manager.pasid_lock, flags); | ||
2677 | if (r < 0) | ||
2678 | goto error_free_root; | ||
2679 | |||
2680 | vm->pasid = pasid; | ||
2681 | } | ||
2682 | |||
2623 | return 0; | 2683 | return 0; |
2624 | 2684 | ||
2625 | error_free_root: | 2685 | error_free_root: |
@@ -2673,6 +2733,14 @@ void amdgpu_vm_fini(struct amdgpu_device *adev, struct amdgpu_vm *vm) | |||
2673 | bool prt_fini_needed = !!adev->gart.gart_funcs->set_prt; | 2733 | bool prt_fini_needed = !!adev->gart.gart_funcs->set_prt; |
2674 | int i; | 2734 | int i; |
2675 | 2735 | ||
2736 | if (vm->pasid) { | ||
2737 | unsigned long flags; | ||
2738 | |||
2739 | spin_lock_irqsave(&adev->vm_manager.pasid_lock, flags); | ||
2740 | idr_remove(&adev->vm_manager.pasid_idr, vm->pasid); | ||
2741 | spin_unlock_irqrestore(&adev->vm_manager.pasid_lock, flags); | ||
2742 | } | ||
2743 | |||
2676 | amd_sched_entity_fini(vm->entity.sched, &vm->entity); | 2744 | amd_sched_entity_fini(vm->entity.sched, &vm->entity); |
2677 | 2745 | ||
2678 | if (!RB_EMPTY_ROOT(&vm->va)) { | 2746 | if (!RB_EMPTY_ROOT(&vm->va)) { |
@@ -2752,6 +2820,8 @@ void amdgpu_vm_manager_init(struct amdgpu_device *adev) | |||
2752 | adev->vm_manager.vm_update_mode = 0; | 2820 | adev->vm_manager.vm_update_mode = 0; |
2753 | #endif | 2821 | #endif |
2754 | 2822 | ||
2823 | idr_init(&adev->vm_manager.pasid_idr); | ||
2824 | spin_lock_init(&adev->vm_manager.pasid_lock); | ||
2755 | } | 2825 | } |
2756 | 2826 | ||
2757 | /** | 2827 | /** |
@@ -2765,6 +2835,9 @@ void amdgpu_vm_manager_fini(struct amdgpu_device *adev) | |||
2765 | { | 2835 | { |
2766 | unsigned i, j; | 2836 | unsigned i, j; |
2767 | 2837 | ||
2838 | WARN_ON(!idr_is_empty(&adev->vm_manager.pasid_idr)); | ||
2839 | idr_destroy(&adev->vm_manager.pasid_idr); | ||
2840 | |||
2768 | for (i = 0; i < AMDGPU_MAX_VMHUBS; ++i) { | 2841 | for (i = 0; i < AMDGPU_MAX_VMHUBS; ++i) { |
2769 | struct amdgpu_vm_id_manager *id_mgr = | 2842 | struct amdgpu_vm_id_manager *id_mgr = |
2770 | &adev->vm_manager.id_mgr[i]; | 2843 | &adev->vm_manager.id_mgr[i]; |