summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/common/mm/vm_area.c
diff options
context:
space:
mode:
authorAlex Waterman <alexw@nvidia.com>2017-04-17 19:26:28 -0400
committermobile promotions <svcmobile_promotions@nvidia.com>2017-05-19 18:34:01 -0400
commitd37e8f7dcf190f31f9c0c12583db2bb0c0d313c0 (patch)
tree4807c89bf40954c54804c3a8dd88c16849181f29 /drivers/gpu/nvgpu/common/mm/vm_area.c
parentdcb744acfbbc11e66cac2d0a674a42e62d908b9d (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/vm_area.c')
-rw-r--r--drivers/gpu/nvgpu/common/mm/vm_area.c223
1 files changed, 223 insertions, 0 deletions
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
23struct 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
37int 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
87int 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
167clean_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
175int 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}