diff options
author | Alex Waterman <alexw@nvidia.com> | 2017-04-17 19:26:28 -0400 |
---|---|---|
committer | mobile promotions <svcmobile_promotions@nvidia.com> | 2017-05-19 18:34:01 -0400 |
commit | d37e8f7dcf190f31f9c0c12583db2bb0c0d313c0 (patch) | |
tree | 4807c89bf40954c54804c3a8dd88c16849181f29 /drivers/gpu/nvgpu/common/mm | |
parent | dcb744acfbbc11e66cac2d0a674a42e62d908b9d (diff) |
gpu: nvgpu: Split VM interface out
This patch begins the major rework of the GPU's virtual memory manager
(VMM). The VMM is the piece of code that handles the userspace interface
to buffers and their mappings into the GMMU. The core data structure is
the VM - for now still known as 'struct vm_gk20a'. Each one of these
structs represents one addres space to which channels or TSGs may bind
themselves to.
The VMM splits the interface up into two broad categories. First there's
the common, OS independent interfaces; and second there's the OS specific
interfaces.
OS independent
--------------
This is the code that manages the lifetime of VMs, the buffers inside
VMs (search, batch mapping) creation, destruction, etc.
OS Specific
-----------
This handles mapping of buffers represented as they are represented by
the OS (dma_buf's for example on Linux).
This patch is by no means complete. There's still Linux specific functions
scattered in ostensibly OS independent code. This is the first step. A
patch that rewrites everything in one go would simply be too big to
effectively review.
Instead the goal of this change is to simply separate out the basic
OS specific and OS agnostic interfaces into their own header files. The
next series of patches will start to pull the relevant implementations
into OS specific C files and common C files.
JIRA NVGPU-12
JIRA NVGPU-30
Change-Id: I242c7206047b6c769296226d855b7e44d5c4bfa8
Signed-off-by: Alex Waterman <alexw@nvidia.com>
Reviewed-on: http://git-master/r/1464939
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
Diffstat (limited to 'drivers/gpu/nvgpu/common/mm')
-rw-r--r-- | drivers/gpu/nvgpu/common/mm/vm.c | 129 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/common/mm/vm_area.c | 223 |
2 files changed, 352 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/common/mm/vm.c b/drivers/gpu/nvgpu/common/mm/vm.c new file mode 100644 index 00000000..eaf30fd0 --- /dev/null +++ b/drivers/gpu/nvgpu/common/mm/vm.c | |||
@@ -0,0 +1,129 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | #include <nvgpu/vm.h> | ||
18 | #include <nvgpu/lock.h> | ||
19 | #include <nvgpu/list.h> | ||
20 | #include <nvgpu/rbtree.h> | ||
21 | #include <nvgpu/semaphore.h> | ||
22 | |||
23 | #include "gk20a/gk20a.h" | ||
24 | #include "gk20a/mm_gk20a.h" | ||
25 | |||
26 | void nvgpu_vm_mapping_batch_start(struct vm_gk20a_mapping_batch *mapping_batch) | ||
27 | { | ||
28 | memset(mapping_batch, 0, sizeof(*mapping_batch)); | ||
29 | mapping_batch->gpu_l2_flushed = false; | ||
30 | mapping_batch->need_tlb_invalidate = false; | ||
31 | } | ||
32 | |||
33 | void nvgpu_vm_mapping_batch_finish_locked( | ||
34 | struct vm_gk20a *vm, struct vm_gk20a_mapping_batch *mapping_batch) | ||
35 | { | ||
36 | /* hanging kref_put batch pointer? */ | ||
37 | WARN_ON(vm->kref_put_batch == mapping_batch); | ||
38 | |||
39 | if (mapping_batch->need_tlb_invalidate) { | ||
40 | struct gk20a *g = gk20a_from_vm(vm); | ||
41 | g->ops.fb.tlb_invalidate(g, &vm->pdb.mem); | ||
42 | } | ||
43 | } | ||
44 | |||
45 | void nvgpu_vm_mapping_batch_finish(struct vm_gk20a *vm, | ||
46 | struct vm_gk20a_mapping_batch *mapping_batch) | ||
47 | { | ||
48 | nvgpu_mutex_acquire(&vm->update_gmmu_lock); | ||
49 | nvgpu_vm_mapping_batch_finish_locked(vm, mapping_batch); | ||
50 | nvgpu_mutex_release(&vm->update_gmmu_lock); | ||
51 | } | ||
52 | |||
53 | void nvgpu_vm_remove_support_nofree(struct vm_gk20a *vm) | ||
54 | { | ||
55 | struct mapped_buffer_node *mapped_buffer; | ||
56 | struct vm_reserved_va_node *va_node, *va_node_tmp; | ||
57 | struct nvgpu_rbtree_node *node = NULL; | ||
58 | struct gk20a *g = vm->mm->g; | ||
59 | |||
60 | gk20a_dbg_fn(""); | ||
61 | |||
62 | /* | ||
63 | * Do this outside of the update_gmmu_lock since unmapping the semaphore | ||
64 | * pool involves unmapping a GMMU mapping which means aquiring the | ||
65 | * update_gmmu_lock. | ||
66 | */ | ||
67 | if (!(g->gpu_characteristics.flags & NVGPU_GPU_FLAGS_HAS_SYNCPOINTS)) { | ||
68 | if (vm->sema_pool) { | ||
69 | nvgpu_semaphore_pool_unmap(vm->sema_pool, vm); | ||
70 | nvgpu_semaphore_pool_put(vm->sema_pool); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | nvgpu_mutex_acquire(&vm->update_gmmu_lock); | ||
75 | |||
76 | nvgpu_rbtree_enum_start(0, &node, vm->mapped_buffers); | ||
77 | while (node) { | ||
78 | mapped_buffer = mapped_buffer_from_rbtree_node(node); | ||
79 | nvgpu_vm_unmap_locked(mapped_buffer, NULL); | ||
80 | nvgpu_rbtree_enum_start(0, &node, vm->mapped_buffers); | ||
81 | } | ||
82 | |||
83 | /* destroy remaining reserved memory areas */ | ||
84 | nvgpu_list_for_each_entry_safe(va_node, va_node_tmp, | ||
85 | &vm->reserved_va_list, | ||
86 | vm_reserved_va_node, reserved_va_list) { | ||
87 | nvgpu_list_del(&va_node->reserved_va_list); | ||
88 | nvgpu_kfree(vm->mm->g, va_node); | ||
89 | } | ||
90 | |||
91 | nvgpu_deinit_vm(vm); | ||
92 | |||
93 | nvgpu_mutex_release(&vm->update_gmmu_lock); | ||
94 | } | ||
95 | |||
96 | void nvgpu_vm_remove_support(struct vm_gk20a *vm) | ||
97 | { | ||
98 | nvgpu_vm_remove_support_nofree(vm); | ||
99 | /* vm is not used anymore. release it. */ | ||
100 | nvgpu_kfree(vm->mm->g, vm); | ||
101 | } | ||
102 | |||
103 | static void nvgpu_vm_remove_support_kref(struct kref *ref) | ||
104 | { | ||
105 | struct vm_gk20a *vm = container_of(ref, struct vm_gk20a, ref); | ||
106 | struct gk20a *g = gk20a_from_vm(vm); | ||
107 | |||
108 | g->ops.mm.vm_remove(vm); | ||
109 | } | ||
110 | |||
111 | void nvgpu_vm_get(struct vm_gk20a *vm) | ||
112 | { | ||
113 | kref_get(&vm->ref); | ||
114 | } | ||
115 | |||
116 | void nvgpu_vm_put(struct vm_gk20a *vm) | ||
117 | { | ||
118 | kref_put(&vm->ref, nvgpu_vm_remove_support_kref); | ||
119 | } | ||
120 | |||
121 | void gk20a_remove_vm(struct vm_gk20a *vm, struct nvgpu_mem *inst_block) | ||
122 | { | ||
123 | struct gk20a *g = vm->mm->g; | ||
124 | |||
125 | gk20a_dbg_fn(""); | ||
126 | |||
127 | gk20a_free_inst_block(g, inst_block); | ||
128 | nvgpu_vm_remove_support_nofree(vm); | ||
129 | } | ||
diff --git a/drivers/gpu/nvgpu/common/mm/vm_area.c b/drivers/gpu/nvgpu/common/mm/vm_area.c new file mode 100644 index 00000000..7b831947 --- /dev/null +++ b/drivers/gpu/nvgpu/common/mm/vm_area.c | |||
@@ -0,0 +1,223 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms and conditions of the GNU General Public License, | ||
6 | * version 2, as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
11 | * more details. | ||
12 | * | ||
13 | * You should have received a copy of the GNU General Public License | ||
14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
15 | */ | ||
16 | |||
17 | #include <nvgpu/vm.h> | ||
18 | #include <nvgpu/vm_area.h> | ||
19 | |||
20 | #include "gk20a/gk20a.h" | ||
21 | #include "gk20a/mm_gk20a.h" | ||
22 | |||
23 | struct nvgpu_vm_area *nvgpu_vm_area_find(struct vm_gk20a *vm, u64 addr) | ||
24 | { | ||
25 | struct nvgpu_vm_area *vm_area; | ||
26 | |||
27 | nvgpu_list_for_each_entry(vm_area, &vm->vm_area_list, | ||
28 | nvgpu_vm_area, vm_area_list) { | ||
29 | if (addr >= vm_area->addr && | ||
30 | addr < (u64)vm_area->addr + (u64)vm_area->size) | ||
31 | return vm_area; | ||
32 | } | ||
33 | |||
34 | return NULL; | ||
35 | } | ||
36 | |||
37 | int nvgpu_vm_area_validate_buffer(struct vm_gk20a *vm, | ||
38 | u64 map_addr, u64 map_size, int pgsz_idx, | ||
39 | struct nvgpu_vm_area **pvm_area) | ||
40 | { | ||
41 | struct gk20a *g = vm->mm->g; | ||
42 | struct nvgpu_vm_area *vm_area; | ||
43 | struct nvgpu_mapped_buf *buffer; | ||
44 | u64 map_end = map_addr + map_size; | ||
45 | |||
46 | /* can wrap around with insane map_size; zero is disallowed too */ | ||
47 | if (map_end <= map_addr) { | ||
48 | nvgpu_warn(g, "fixed offset mapping with invalid map_size"); | ||
49 | return -EINVAL; | ||
50 | } | ||
51 | |||
52 | if (map_addr & (vm->gmmu_page_sizes[pgsz_idx] - 1)) { | ||
53 | nvgpu_err(g, "map offset must be buffer page size aligned 0x%llx", | ||
54 | map_addr); | ||
55 | return -EINVAL; | ||
56 | } | ||
57 | |||
58 | /* Find the space reservation, but it's ok to have none for | ||
59 | * userspace-managed address spaces */ | ||
60 | vm_area = nvgpu_vm_area_find(vm, map_addr); | ||
61 | if (!vm_area && !vm->userspace_managed) { | ||
62 | nvgpu_warn(g, "fixed offset mapping without space allocation"); | ||
63 | return -EINVAL; | ||
64 | } | ||
65 | |||
66 | /* Mapped area should fit inside va, if there's one */ | ||
67 | if (vm_area && map_end > vm_area->addr + vm_area->size) { | ||
68 | nvgpu_warn(g, "fixed offset mapping size overflows va node"); | ||
69 | return -EINVAL; | ||
70 | } | ||
71 | |||
72 | /* check that this mapping does not collide with existing | ||
73 | * mappings by checking the buffer with the highest GPU VA | ||
74 | * that is less than our buffer end */ | ||
75 | buffer = __nvgpu_vm_find_mapped_buf_less_than( | ||
76 | vm, map_addr + map_size); | ||
77 | if (buffer && buffer->addr + buffer->size > map_addr) { | ||
78 | nvgpu_warn(g, "overlapping buffer map requested"); | ||
79 | return -EINVAL; | ||
80 | } | ||
81 | |||
82 | *pvm_area = vm_area; | ||
83 | |||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | int nvgpu_vm_area_alloc(struct vm_gk20a *vm, u32 pages, u32 page_size, | ||
88 | u64 *addr, u32 flags) | ||
89 | { | ||
90 | struct gk20a *g = vm->mm->g; | ||
91 | struct nvgpu_allocator *vma; | ||
92 | struct nvgpu_vm_area *vm_area; | ||
93 | u64 vaddr_start = 0; | ||
94 | int pgsz_idx = gmmu_page_size_small; | ||
95 | |||
96 | nvgpu_log(g, gpu_dbg_map, | ||
97 | "ADD vm_area: pgsz=%#-8x pages=%-9u addr=%#-14llx flags=0x%x", | ||
98 | page_size, pages, *addr, flags); | ||
99 | |||
100 | for (; pgsz_idx < gmmu_nr_page_sizes; pgsz_idx++) { | ||
101 | if (vm->gmmu_page_sizes[pgsz_idx] == page_size) | ||
102 | break; | ||
103 | } | ||
104 | |||
105 | if (pgsz_idx > gmmu_page_size_big) | ||
106 | return -EINVAL; | ||
107 | |||
108 | if (!vm->big_pages && pgsz_idx == gmmu_page_size_big) | ||
109 | return -EINVAL; | ||
110 | |||
111 | vm_area = nvgpu_kzalloc(g, sizeof(*vm_area)); | ||
112 | if (!vm_area) | ||
113 | goto clean_up_err; | ||
114 | |||
115 | vma = vm->vma[pgsz_idx]; | ||
116 | if (flags & NVGPU_AS_ALLOC_SPACE_FLAGS_FIXED_OFFSET) | ||
117 | vaddr_start = nvgpu_alloc_fixed(vma, *addr, | ||
118 | (u64)pages * | ||
119 | (u64)page_size, | ||
120 | page_size); | ||
121 | else | ||
122 | vaddr_start = nvgpu_alloc(vma, | ||
123 | (u64)pages * | ||
124 | (u64)page_size); | ||
125 | |||
126 | if (!vaddr_start) | ||
127 | goto clean_up_err; | ||
128 | |||
129 | vm_area->flags = flags; | ||
130 | vm_area->addr = vaddr_start; | ||
131 | vm_area->size = (u64)page_size * (u64)pages; | ||
132 | vm_area->pgsz_idx = pgsz_idx; | ||
133 | nvgpu_init_list_node(&vm_area->buffer_list_head); | ||
134 | nvgpu_init_list_node(&vm_area->vm_area_list); | ||
135 | |||
136 | nvgpu_mutex_acquire(&vm->update_gmmu_lock); | ||
137 | |||
138 | if (flags & NVGPU_AS_ALLOC_SPACE_FLAGS_SPARSE) { | ||
139 | u64 map_addr = g->ops.mm.gmmu_map(vm, vaddr_start, | ||
140 | NULL, | ||
141 | 0, | ||
142 | vm_area->size, | ||
143 | pgsz_idx, | ||
144 | 0, | ||
145 | 0, | ||
146 | flags, | ||
147 | gk20a_mem_flag_none, | ||
148 | false, | ||
149 | true, | ||
150 | false, | ||
151 | NULL, | ||
152 | APERTURE_INVALID); | ||
153 | if (!map_addr) { | ||
154 | nvgpu_mutex_release(&vm->update_gmmu_lock); | ||
155 | goto clean_up_err; | ||
156 | } | ||
157 | |||
158 | vm_area->sparse = true; | ||
159 | } | ||
160 | nvgpu_list_add_tail(&vm_area->vm_area_list, &vm->vm_area_list); | ||
161 | |||
162 | nvgpu_mutex_release(&vm->update_gmmu_lock); | ||
163 | |||
164 | *addr = vaddr_start; | ||
165 | return 0; | ||
166 | |||
167 | clean_up_err: | ||
168 | if (vaddr_start) | ||
169 | nvgpu_free(vma, vaddr_start); | ||
170 | if (vm_area) | ||
171 | nvgpu_kfree(g, vm_area); | ||
172 | return -ENOMEM; | ||
173 | } | ||
174 | |||
175 | int nvgpu_vm_area_free(struct vm_gk20a *vm, u64 addr) | ||
176 | { | ||
177 | struct gk20a *g = gk20a_from_vm(vm); | ||
178 | struct nvgpu_mapped_buf *buffer, *n; | ||
179 | struct nvgpu_vm_area *vm_area; | ||
180 | |||
181 | nvgpu_mutex_acquire(&vm->update_gmmu_lock); | ||
182 | vm_area = nvgpu_vm_area_find(vm, addr); | ||
183 | if (!vm_area) { | ||
184 | nvgpu_mutex_release(&vm->update_gmmu_lock); | ||
185 | return 0; | ||
186 | } | ||
187 | nvgpu_list_del(&vm_area->vm_area_list); | ||
188 | nvgpu_mutex_release(&vm->update_gmmu_lock); | ||
189 | |||
190 | nvgpu_log(g, gpu_dbg_map, | ||
191 | "DEL vm_area: pgsz=%#-8x pages=%-9llu " | ||
192 | "addr=%#-14llx flags=0x%x", | ||
193 | vm->gmmu_page_sizes[vm_area->pgsz_idx], | ||
194 | vm_area->size / vm->gmmu_page_sizes[vm_area->pgsz_idx], | ||
195 | vm_area->addr, | ||
196 | vm_area->flags); | ||
197 | |||
198 | /* Decrement the ref count on all buffers in this vm_area. This | ||
199 | * allows userspace to let the kernel free mappings that are | ||
200 | * only used by this vm_area. */ | ||
201 | nvgpu_list_for_each_entry_safe(buffer, n, | ||
202 | &vm_area->buffer_list_head, | ||
203 | nvgpu_mapped_buf, buffer_list) { | ||
204 | nvgpu_list_del(&buffer->buffer_list); | ||
205 | kref_put(&buffer->ref, gk20a_vm_unmap_locked_kref); | ||
206 | } | ||
207 | |||
208 | /* if this was a sparse mapping, free the va */ | ||
209 | if (vm_area->sparse) | ||
210 | g->ops.mm.gmmu_unmap(vm, | ||
211 | vm_area->addr, | ||
212 | vm_area->size, | ||
213 | vm_area->pgsz_idx, | ||
214 | true, | ||
215 | gk20a_mem_flag_none, | ||
216 | true, | ||
217 | NULL); | ||
218 | |||
219 | nvgpu_free(vm->vma[vm_area->pgsz_idx], vm_area->addr); | ||
220 | nvgpu_kfree(g, vm_area); | ||
221 | |||
222 | return 0; | ||
223 | } | ||