1 /*
2 * Copyright 2014 Advanced Micro Devices, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 * OTHER DEALINGS IN THE SOFTWARE.
21 *
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <errno.h>
31 #include "amdgpu.h"
32 #include "amdgpu_drm.h"
33 #include "amdgpu_internal.h"
34 #include "util_math.h"
35
amdgpu_va_range_query(amdgpu_device_handle dev,enum amdgpu_gpu_va_range type,uint64_t * start,uint64_t * end)36 int amdgpu_va_range_query(amdgpu_device_handle dev,
37 enum amdgpu_gpu_va_range type, uint64_t *start, uint64_t *end)
38 {
39 if (type == amdgpu_gpu_va_range_general) {
40 *start = dev->dev_info.virtual_address_offset;
41 *end = dev->dev_info.virtual_address_max;
42 return 0;
43 }
44 return -EINVAL;
45 }
46
amdgpu_vamgr_init(struct amdgpu_bo_va_mgr * mgr,uint64_t start,uint64_t max,uint64_t alignment)47 drm_private void amdgpu_vamgr_init(struct amdgpu_bo_va_mgr *mgr, uint64_t start,
48 uint64_t max, uint64_t alignment)
49 {
50 mgr->va_offset = start;
51 mgr->va_max = max;
52 mgr->va_alignment = alignment;
53
54 list_inithead(&mgr->va_holes);
55 pthread_mutex_init(&mgr->bo_va_mutex, NULL);
56 }
57
amdgpu_vamgr_deinit(struct amdgpu_bo_va_mgr * mgr)58 drm_private void amdgpu_vamgr_deinit(struct amdgpu_bo_va_mgr *mgr)
59 {
60 struct amdgpu_bo_va_hole *hole, *tmp;
61 LIST_FOR_EACH_ENTRY_SAFE(hole, tmp, &mgr->va_holes, list) {
62 list_del(&hole->list);
63 free(hole);
64 }
65 pthread_mutex_destroy(&mgr->bo_va_mutex);
66 }
67
68 drm_private uint64_t
amdgpu_vamgr_find_va(struct amdgpu_bo_va_mgr * mgr,uint64_t size,uint64_t alignment,uint64_t base_required)69 amdgpu_vamgr_find_va(struct amdgpu_bo_va_mgr *mgr, uint64_t size,
70 uint64_t alignment, uint64_t base_required)
71 {
72 struct amdgpu_bo_va_hole *hole, *n;
73 uint64_t offset = 0, waste = 0;
74
75 alignment = MAX2(alignment, mgr->va_alignment);
76 size = ALIGN(size, mgr->va_alignment);
77
78 if (base_required % alignment)
79 return AMDGPU_INVALID_VA_ADDRESS;
80
81 pthread_mutex_lock(&mgr->bo_va_mutex);
82 /* TODO: using more appropriate way to track the holes */
83 /* first look for a hole */
84 LIST_FOR_EACH_ENTRY_SAFE(hole, n, &mgr->va_holes, list) {
85 if (base_required) {
86 if(hole->offset > base_required ||
87 (hole->offset + hole->size) < (base_required + size))
88 continue;
89 waste = base_required - hole->offset;
90 offset = base_required;
91 } else {
92 offset = hole->offset;
93 waste = offset % alignment;
94 waste = waste ? alignment - waste : 0;
95 offset += waste;
96 if (offset >= (hole->offset + hole->size)) {
97 continue;
98 }
99 }
100 if (!waste && hole->size == size) {
101 offset = hole->offset;
102 list_del(&hole->list);
103 free(hole);
104 pthread_mutex_unlock(&mgr->bo_va_mutex);
105 return offset;
106 }
107 if ((hole->size - waste) > size) {
108 if (waste) {
109 n = calloc(1, sizeof(struct amdgpu_bo_va_hole));
110 n->size = waste;
111 n->offset = hole->offset;
112 list_add(&n->list, &hole->list);
113 }
114 hole->size -= (size + waste);
115 hole->offset += size + waste;
116 pthread_mutex_unlock(&mgr->bo_va_mutex);
117 return offset;
118 }
119 if ((hole->size - waste) == size) {
120 hole->size = waste;
121 pthread_mutex_unlock(&mgr->bo_va_mutex);
122 return offset;
123 }
124 }
125
126 if (base_required) {
127 if (base_required < mgr->va_offset) {
128 pthread_mutex_unlock(&mgr->bo_va_mutex);
129 return AMDGPU_INVALID_VA_ADDRESS;
130 }
131 offset = mgr->va_offset;
132 waste = base_required - mgr->va_offset;
133 } else {
134 offset = mgr->va_offset;
135 waste = offset % alignment;
136 waste = waste ? alignment - waste : 0;
137 }
138
139 if (offset + waste + size > mgr->va_max) {
140 pthread_mutex_unlock(&mgr->bo_va_mutex);
141 return AMDGPU_INVALID_VA_ADDRESS;
142 }
143
144 if (waste) {
145 n = calloc(1, sizeof(struct amdgpu_bo_va_hole));
146 n->size = waste;
147 n->offset = offset;
148 list_add(&n->list, &mgr->va_holes);
149 }
150
151 offset += waste;
152 mgr->va_offset += size + waste;
153 pthread_mutex_unlock(&mgr->bo_va_mutex);
154 return offset;
155 }
156
157 drm_private void
amdgpu_vamgr_free_va(struct amdgpu_bo_va_mgr * mgr,uint64_t va,uint64_t size)158 amdgpu_vamgr_free_va(struct amdgpu_bo_va_mgr *mgr, uint64_t va, uint64_t size)
159 {
160 struct amdgpu_bo_va_hole *hole;
161
162 if (va == AMDGPU_INVALID_VA_ADDRESS)
163 return;
164
165 size = ALIGN(size, mgr->va_alignment);
166
167 pthread_mutex_lock(&mgr->bo_va_mutex);
168 if ((va + size) == mgr->va_offset) {
169 mgr->va_offset = va;
170 /* Delete uppermost hole if it reaches the new top */
171 if (!LIST_IS_EMPTY(&mgr->va_holes)) {
172 hole = container_of(mgr->va_holes.next, hole, list);
173 if ((hole->offset + hole->size) == va) {
174 mgr->va_offset = hole->offset;
175 list_del(&hole->list);
176 free(hole);
177 }
178 }
179 } else {
180 struct amdgpu_bo_va_hole *next;
181
182 hole = container_of(&mgr->va_holes, hole, list);
183 LIST_FOR_EACH_ENTRY(next, &mgr->va_holes, list) {
184 if (next->offset < va)
185 break;
186 hole = next;
187 }
188
189 if (&hole->list != &mgr->va_holes) {
190 /* Grow upper hole if it's adjacent */
191 if (hole->offset == (va + size)) {
192 hole->offset = va;
193 hole->size += size;
194 /* Merge lower hole if it's adjacent */
195 if (next != hole
196 && &next->list != &mgr->va_holes
197 && (next->offset + next->size) == va) {
198 next->size += hole->size;
199 list_del(&hole->list);
200 free(hole);
201 }
202 goto out;
203 }
204 }
205
206 /* Grow lower hole if it's adjacent */
207 if (next != hole && &next->list != &mgr->va_holes &&
208 (next->offset + next->size) == va) {
209 next->size += size;
210 goto out;
211 }
212
213 /* FIXME on allocation failure we just lose virtual address space
214 * maybe print a warning
215 */
216 next = calloc(1, sizeof(struct amdgpu_bo_va_hole));
217 if (next) {
218 next->size = size;
219 next->offset = va;
220 list_add(&next->list, &hole->list);
221 }
222 }
223 out:
224 pthread_mutex_unlock(&mgr->bo_va_mutex);
225 }
226
amdgpu_va_range_alloc(amdgpu_device_handle dev,enum amdgpu_gpu_va_range va_range_type,uint64_t size,uint64_t va_base_alignment,uint64_t va_base_required,uint64_t * va_base_allocated,amdgpu_va_handle * va_range_handle,uint64_t flags)227 int amdgpu_va_range_alloc(amdgpu_device_handle dev,
228 enum amdgpu_gpu_va_range va_range_type,
229 uint64_t size,
230 uint64_t va_base_alignment,
231 uint64_t va_base_required,
232 uint64_t *va_base_allocated,
233 amdgpu_va_handle *va_range_handle,
234 uint64_t flags)
235 {
236 struct amdgpu_bo_va_mgr *vamgr;
237
238 if (flags & AMDGPU_VA_RANGE_32_BIT)
239 vamgr = dev->vamgr_32;
240 else
241 vamgr = dev->vamgr;
242
243 va_base_alignment = MAX2(va_base_alignment, vamgr->va_alignment);
244 size = ALIGN(size, vamgr->va_alignment);
245
246 *va_base_allocated = amdgpu_vamgr_find_va(vamgr, size,
247 va_base_alignment, va_base_required);
248
249 if (!(flags & AMDGPU_VA_RANGE_32_BIT) &&
250 (*va_base_allocated == AMDGPU_INVALID_VA_ADDRESS)) {
251 /* fallback to 32bit address */
252 vamgr = dev->vamgr_32;
253 *va_base_allocated = amdgpu_vamgr_find_va(vamgr, size,
254 va_base_alignment, va_base_required);
255 }
256
257 if (*va_base_allocated != AMDGPU_INVALID_VA_ADDRESS) {
258 struct amdgpu_va* va;
259 va = calloc(1, sizeof(struct amdgpu_va));
260 if(!va){
261 amdgpu_vamgr_free_va(vamgr, *va_base_allocated, size);
262 return -ENOMEM;
263 }
264 va->dev = dev;
265 va->address = *va_base_allocated;
266 va->size = size;
267 va->range = va_range_type;
268 va->vamgr = vamgr;
269 *va_range_handle = va;
270 } else {
271 return -EINVAL;
272 }
273
274 return 0;
275 }
276
amdgpu_va_range_free(amdgpu_va_handle va_range_handle)277 int amdgpu_va_range_free(amdgpu_va_handle va_range_handle)
278 {
279 if(!va_range_handle || !va_range_handle->address)
280 return 0;
281
282 amdgpu_vamgr_free_va(va_range_handle->vamgr,
283 va_range_handle->address,
284 va_range_handle->size);
285 free(va_range_handle);
286 return 0;
287 }
288