diff options
Diffstat (limited to 'drivers/gpu/nvgpu/common')
-rw-r--r-- | drivers/gpu/nvgpu/common/linux/ioctl_as.c | 21 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/common/linux/vm_priv.h | 62 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/common/mm/vm.c | 129 | ||||
-rw-r--r-- | drivers/gpu/nvgpu/common/mm/vm_area.c | 223 |
4 files changed, 425 insertions, 10 deletions
diff --git a/drivers/gpu/nvgpu/common/linux/ioctl_as.c b/drivers/gpu/nvgpu/common/linux/ioctl_as.c index 4bbcedda..7a24a14f 100644 --- a/drivers/gpu/nvgpu/common/linux/ioctl_as.c +++ b/drivers/gpu/nvgpu/common/linux/ioctl_as.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include "gk20a/gk20a.h" | 28 | #include "gk20a/gk20a.h" |
29 | #include "gk20a/platform_gk20a.h" | 29 | #include "gk20a/platform_gk20a.h" |
30 | #include "ioctl_as.h" | 30 | #include "ioctl_as.h" |
31 | #include "vm_priv.h" | ||
31 | 32 | ||
32 | static int gk20a_as_ioctl_bind_channel( | 33 | static int gk20a_as_ioctl_bind_channel( |
33 | struct gk20a_as_share *as_share, | 34 | struct gk20a_as_share *as_share, |
@@ -72,7 +73,7 @@ static int gk20a_as_ioctl_map_buffer_ex( | |||
72 | { | 73 | { |
73 | gk20a_dbg_fn(""); | 74 | gk20a_dbg_fn(""); |
74 | 75 | ||
75 | return gk20a_vm_map_buffer(as_share->vm, args->dmabuf_fd, | 76 | return nvgpu_vm_map_buffer(as_share->vm, args->dmabuf_fd, |
76 | &args->offset, args->flags, | 77 | &args->offset, args->flags, |
77 | args->kind, | 78 | args->kind, |
78 | args->buffer_offset, | 79 | args->buffer_offset, |
@@ -85,7 +86,7 @@ static int gk20a_as_ioctl_map_buffer( | |||
85 | struct nvgpu_as_map_buffer_args *args) | 86 | struct nvgpu_as_map_buffer_args *args) |
86 | { | 87 | { |
87 | gk20a_dbg_fn(""); | 88 | gk20a_dbg_fn(""); |
88 | return gk20a_vm_map_buffer(as_share->vm, args->dmabuf_fd, | 89 | return nvgpu_vm_map_buffer(as_share->vm, args->dmabuf_fd, |
89 | &args->o_a.offset, | 90 | &args->o_a.offset, |
90 | args->flags, NV_KIND_DEFAULT, | 91 | args->flags, NV_KIND_DEFAULT, |
91 | 0, 0, NULL); | 92 | 0, 0, NULL); |
@@ -97,7 +98,7 @@ static int gk20a_as_ioctl_unmap_buffer( | |||
97 | struct nvgpu_as_unmap_buffer_args *args) | 98 | struct nvgpu_as_unmap_buffer_args *args) |
98 | { | 99 | { |
99 | gk20a_dbg_fn(""); | 100 | gk20a_dbg_fn(""); |
100 | return gk20a_vm_unmap_buffer(as_share->vm, args->offset, NULL); | 101 | return nvgpu_vm_unmap_buffer(as_share->vm, args->offset, NULL); |
101 | } | 102 | } |
102 | 103 | ||
103 | static int gk20a_as_ioctl_map_buffer_batch( | 104 | static int gk20a_as_ioctl_map_buffer_batch( |
@@ -123,7 +124,7 @@ static int gk20a_as_ioctl_map_buffer_batch( | |||
123 | args->num_maps > g->gpu_characteristics.map_buffer_batch_limit) | 124 | args->num_maps > g->gpu_characteristics.map_buffer_batch_limit) |
124 | return -EINVAL; | 125 | return -EINVAL; |
125 | 126 | ||
126 | gk20a_vm_mapping_batch_start(&batch); | 127 | nvgpu_vm_mapping_batch_start(&batch); |
127 | 128 | ||
128 | for (i = 0; i < args->num_unmaps; ++i) { | 129 | for (i = 0; i < args->num_unmaps; ++i) { |
129 | struct nvgpu_as_unmap_buffer_args unmap_args; | 130 | struct nvgpu_as_unmap_buffer_args unmap_args; |
@@ -134,14 +135,14 @@ static int gk20a_as_ioctl_map_buffer_batch( | |||
134 | break; | 135 | break; |
135 | } | 136 | } |
136 | 137 | ||
137 | err = gk20a_vm_unmap_buffer(as_share->vm, unmap_args.offset, | 138 | err = nvgpu_vm_unmap_buffer(as_share->vm, unmap_args.offset, |
138 | &batch); | 139 | &batch); |
139 | if (err) | 140 | if (err) |
140 | break; | 141 | break; |
141 | } | 142 | } |
142 | 143 | ||
143 | if (err) { | 144 | if (err) { |
144 | gk20a_vm_mapping_batch_finish(as_share->vm, &batch); | 145 | nvgpu_vm_mapping_batch_finish(as_share->vm, &batch); |
145 | 146 | ||
146 | args->num_unmaps = i; | 147 | args->num_unmaps = i; |
147 | args->num_maps = 0; | 148 | args->num_maps = 0; |
@@ -158,7 +159,7 @@ static int gk20a_as_ioctl_map_buffer_batch( | |||
158 | break; | 159 | break; |
159 | } | 160 | } |
160 | 161 | ||
161 | err = gk20a_vm_map_buffer( | 162 | err = nvgpu_vm_map_buffer( |
162 | as_share->vm, map_args.dmabuf_fd, | 163 | as_share->vm, map_args.dmabuf_fd, |
163 | &map_args.offset, map_args.flags, | 164 | &map_args.offset, map_args.flags, |
164 | map_args.kind, | 165 | map_args.kind, |
@@ -169,7 +170,7 @@ static int gk20a_as_ioctl_map_buffer_batch( | |||
169 | break; | 170 | break; |
170 | } | 171 | } |
171 | 172 | ||
172 | gk20a_vm_mapping_batch_finish(as_share->vm, &batch); | 173 | nvgpu_vm_mapping_batch_finish(as_share->vm, &batch); |
173 | 174 | ||
174 | if (err) | 175 | if (err) |
175 | args->num_maps = i; | 176 | args->num_maps = i; |
@@ -228,7 +229,7 @@ static int gk20a_as_ioctl_get_buffer_compbits_info( | |||
228 | struct nvgpu_as_get_buffer_compbits_info_args *args) | 229 | struct nvgpu_as_get_buffer_compbits_info_args *args) |
229 | { | 230 | { |
230 | gk20a_dbg_fn(""); | 231 | gk20a_dbg_fn(""); |
231 | return gk20a_vm_get_compbits_info(as_share->vm, | 232 | return nvgpu_vm_get_compbits_info(as_share->vm, |
232 | args->mapping_gva, | 233 | args->mapping_gva, |
233 | &args->compbits_win_size, | 234 | &args->compbits_win_size, |
234 | &args->compbits_win_ctagline, | 235 | &args->compbits_win_ctagline, |
@@ -241,7 +242,7 @@ static int gk20a_as_ioctl_map_buffer_compbits( | |||
241 | struct nvgpu_as_map_buffer_compbits_args *args) | 242 | struct nvgpu_as_map_buffer_compbits_args *args) |
242 | { | 243 | { |
243 | gk20a_dbg_fn(""); | 244 | gk20a_dbg_fn(""); |
244 | return gk20a_vm_map_compbits(as_share->vm, | 245 | return nvgpu_vm_map_compbits(as_share->vm, |
245 | args->mapping_gva, | 246 | args->mapping_gva, |
246 | &args->compbits_win_gva, | 247 | &args->compbits_win_gva, |
247 | &args->mapping_iova, | 248 | &args->mapping_iova, |
diff --git a/drivers/gpu/nvgpu/common/linux/vm_priv.h b/drivers/gpu/nvgpu/common/linux/vm_priv.h new file mode 100644 index 00000000..c0fb0ffe --- /dev/null +++ b/drivers/gpu/nvgpu/common/linux/vm_priv.h | |||
@@ -0,0 +1,62 @@ | |||
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 | #ifndef __COMMON_LINUX_VM_PRIV_H__ | ||
18 | #define __COMMON_LINUX_VM_PRIV_H__ | ||
19 | |||
20 | #include <nvgpu/types.h> | ||
21 | |||
22 | struct sg_table; | ||
23 | struct dma_buf; | ||
24 | |||
25 | struct vm_gk20a; | ||
26 | struct vm_gk20a_mapping_batch; | ||
27 | |||
28 | u64 nvgpu_vm_map(struct vm_gk20a *vm, | ||
29 | struct dma_buf *dmabuf, | ||
30 | u64 offset_align, | ||
31 | u32 flags /*NVGPU_AS_MAP_BUFFER_FLAGS_*/, | ||
32 | int kind, | ||
33 | struct sg_table **sgt, | ||
34 | bool user_mapped, | ||
35 | int rw_flag, | ||
36 | u64 buffer_offset, | ||
37 | u64 mapping_size, | ||
38 | struct vm_gk20a_mapping_batch *mapping_batch); | ||
39 | |||
40 | int nvgpu_vm_map_compbits(struct vm_gk20a *vm, | ||
41 | u64 mapping_gva, | ||
42 | u64 *compbits_win_gva, | ||
43 | u64 *mapping_iova, | ||
44 | u32 flags); | ||
45 | |||
46 | /* Note: batch may be NULL if map op is not part of a batch */ | ||
47 | int nvgpu_vm_map_buffer(struct vm_gk20a *vm, | ||
48 | int dmabuf_fd, | ||
49 | u64 *offset_align, | ||
50 | u32 flags, /* NVGPU_AS_MAP_BUFFER_FLAGS_ */ | ||
51 | int kind, | ||
52 | u64 buffer_offset, | ||
53 | u64 mapping_size, | ||
54 | struct vm_gk20a_mapping_batch *batch); | ||
55 | |||
56 | void nvgpu_vm_unmap(struct vm_gk20a *vm, u64 offset); | ||
57 | |||
58 | /* find buffer corresponding to va */ | ||
59 | int nvgpu_vm_find_buffer(struct vm_gk20a *vm, u64 gpu_va, | ||
60 | struct dma_buf **dmabuf, | ||
61 | u64 *offset); | ||
62 | #endif | ||
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 | } | ||