blob: e2a4c875e0db7162c32da0969bc0af94ed11ebd1 [file] [log] [blame]
Alex Deucher09361392015-04-20 12:04:22 -04001/*
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 *
Emil Velikova30da8e2015-08-07 17:20:51 +010022 */
Alex Deucher09361392015-04-20 12:04:22 -040023
Emil Velikovf4c2bfd2015-08-07 17:17:43 +010024#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
Alex Deucher09361392015-04-20 12:04:22 -040028#include <stdlib.h>
29#include <string.h>
Sabre Shao23fab592015-07-09 13:50:36 +080030#include <errno.h>
Alex Deucher09361392015-04-20 12:04:22 -040031#include "amdgpu.h"
32#include "amdgpu_drm.h"
33#include "amdgpu_internal.h"
34#include "util_math.h"
35
Ken Wang322d02d2015-05-21 17:21:21 +080036static struct amdgpu_bo_va_mgr vamgr = {{0}};
37
Sabre Shao12802da2015-07-09 13:53:24 +080038int amdgpu_va_range_query(amdgpu_device_handle dev,
39 enum amdgpu_gpu_va_range type, uint64_t *start, uint64_t *end)
40{
41 if (type == amdgpu_gpu_va_range_general) {
42 *start = dev->dev_info.virtual_address_offset;
43 *end = dev->dev_info.virtual_address_max;
44 return 0;
45 }
46 return -EINVAL;
47}
48
Ken Wang322d02d2015-05-21 17:21:21 +080049static void amdgpu_vamgr_init(struct amdgpu_bo_va_mgr *mgr, struct amdgpu_device *dev)
Alex Deucher09361392015-04-20 12:04:22 -040050{
Ken Wang322d02d2015-05-21 17:21:21 +080051 mgr->va_offset = dev->dev_info.virtual_address_offset;
52 mgr->va_max = dev->dev_info.virtual_address_max;
53 mgr->va_alignment = dev->dev_info.virtual_address_alignment;
Alex Deucher09361392015-04-20 12:04:22 -040054
Ken Wang322d02d2015-05-21 17:21:21 +080055 list_inithead(&mgr->va_holes);
56 pthread_mutex_init(&mgr->bo_va_mutex, NULL);
57}
Alex Deucher09361392015-04-20 12:04:22 -040058
Ken Wang322d02d2015-05-21 17:21:21 +080059static void amdgpu_vamgr_deinit(struct amdgpu_bo_va_mgr *mgr)
60{
61 struct amdgpu_bo_va_hole *hole;
62 LIST_FOR_EACH_ENTRY(hole, &mgr->va_holes, list) {
63 list_del(&hole->list);
64 free(hole);
65 }
66 pthread_mutex_destroy(&mgr->bo_va_mutex);
67}
68
69struct amdgpu_bo_va_mgr * amdgpu_vamgr_get_global(struct amdgpu_device *dev)
70{
71 int ref;
72 ref = atomic_inc_return(&vamgr.refcount);
73
74 if (ref == 1)
75 amdgpu_vamgr_init(&vamgr, dev);
76 return &vamgr;
77}
78
79void amdgpu_vamgr_reference(struct amdgpu_bo_va_mgr **dst,
80 struct amdgpu_bo_va_mgr *src)
81{
82 if (update_references(&(*dst)->refcount, NULL))
83 amdgpu_vamgr_deinit(*dst);
84 *dst = src;
Alex Deucher09361392015-04-20 12:04:22 -040085}
86
Ken Wang5b019082015-07-09 13:48:25 +080087uint64_t amdgpu_vamgr_find_va(struct amdgpu_bo_va_mgr *mgr, uint64_t size,
88 uint64_t alignment, uint64_t base_required)
Alex Deucher09361392015-04-20 12:04:22 -040089{
monk.liu9066acf2015-05-13 13:58:43 +080090 struct amdgpu_bo_va_hole *hole, *n;
91 uint64_t offset = 0, waste = 0;
Alex Deucher09361392015-04-20 12:04:22 -040092
monk.liu9066acf2015-05-13 13:58:43 +080093 alignment = MAX2(alignment, mgr->va_alignment);
94 size = ALIGN(size, mgr->va_alignment);
Alex Deucher09361392015-04-20 12:04:22 -040095
Ken Wang5b019082015-07-09 13:48:25 +080096 if (base_required % alignment)
97 return AMDGPU_INVALID_VA_ADDRESS;
98
monk.liu9066acf2015-05-13 13:58:43 +080099 pthread_mutex_lock(&mgr->bo_va_mutex);
100 /* TODO: using more appropriate way to track the holes */
101 /* first look for a hole */
Ken Wang5b019082015-07-09 13:48:25 +0800102 LIST_FOR_EACH_ENTRY_SAFE(hole, n, &vamgr.va_holes, list) {
103 if (base_required) {
104 if(hole->offset > base_required ||
105 (hole->offset + hole->size) < (base_required + size))
106 continue;
107 waste = base_required - hole->offset;
108 offset = base_required;
109 } else {
110 offset = hole->offset;
111 waste = offset % alignment;
112 waste = waste ? alignment - waste : 0;
113 offset += waste;
114 if (offset >= (hole->offset + hole->size)) {
115 continue;
116 }
monk.liu9066acf2015-05-13 13:58:43 +0800117 }
118 if (!waste && hole->size == size) {
119 offset = hole->offset;
120 list_del(&hole->list);
121 free(hole);
122 pthread_mutex_unlock(&mgr->bo_va_mutex);
123 return offset;
124 }
125 if ((hole->size - waste) > size) {
126 if (waste) {
Ken Wang5b019082015-07-09 13:48:25 +0800127 n = calloc(1, sizeof(struct amdgpu_bo_va_hole));
monk.liu9066acf2015-05-13 13:58:43 +0800128 n->size = waste;
129 n->offset = hole->offset;
130 list_add(&n->list, &hole->list);
131 }
132 hole->size -= (size + waste);
133 hole->offset += size + waste;
134 pthread_mutex_unlock(&mgr->bo_va_mutex);
135 return offset;
136 }
137 if ((hole->size - waste) == size) {
138 hole->size = waste;
139 pthread_mutex_unlock(&mgr->bo_va_mutex);
140 return offset;
141 }
142 }
Alex Deucher09361392015-04-20 12:04:22 -0400143
Ken Wang5b019082015-07-09 13:48:25 +0800144 if (base_required) {
145 if (base_required < mgr->va_offset)
146 return AMDGPU_INVALID_VA_ADDRESS;
147 offset = mgr->va_offset;
148 waste = base_required - mgr->va_offset;
149 } else {
150 offset = mgr->va_offset;
151 waste = offset % alignment;
152 waste = waste ? alignment - waste : 0;
153 }
Jammy Zhou241cf6d2015-05-13 01:14:11 +0800154
155 if (offset + waste + size > mgr->va_max) {
156 pthread_mutex_unlock(&mgr->bo_va_mutex);
157 return AMDGPU_INVALID_VA_ADDRESS;
158 }
159
monk.liu9066acf2015-05-13 13:58:43 +0800160 if (waste) {
161 n = calloc(1, sizeof(struct amdgpu_bo_va_hole));
162 n->size = waste;
163 n->offset = offset;
164 list_add(&n->list, &mgr->va_holes);
165 }
Ken Wang5b019082015-07-09 13:48:25 +0800166
monk.liu9066acf2015-05-13 13:58:43 +0800167 offset += waste;
168 mgr->va_offset += size + waste;
169 pthread_mutex_unlock(&mgr->bo_va_mutex);
170 return offset;
Alex Deucher09361392015-04-20 12:04:22 -0400171}
172
Ken Wang322d02d2015-05-21 17:21:21 +0800173void amdgpu_vamgr_free_va(struct amdgpu_bo_va_mgr *mgr,
174 uint64_t va, uint64_t size)
Alex Deucher09361392015-04-20 12:04:22 -0400175{
monk.liu9066acf2015-05-13 13:58:43 +0800176 struct amdgpu_bo_va_hole *hole;
Alex Deucher09361392015-04-20 12:04:22 -0400177
monk.liud3e71952015-05-13 14:01:53 +0800178 if (va == AMDGPU_INVALID_VA_ADDRESS)
179 return;
180
monk.liu9066acf2015-05-13 13:58:43 +0800181 size = ALIGN(size, mgr->va_alignment);
Alex Deucher09361392015-04-20 12:04:22 -0400182
monk.liu9066acf2015-05-13 13:58:43 +0800183 pthread_mutex_lock(&mgr->bo_va_mutex);
184 if ((va + size) == mgr->va_offset) {
185 mgr->va_offset = va;
186 /* Delete uppermost hole if it reaches the new top */
187 if (!LIST_IS_EMPTY(&mgr->va_holes)) {
188 hole = container_of(mgr->va_holes.next, hole, list);
189 if ((hole->offset + hole->size) == va) {
190 mgr->va_offset = hole->offset;
191 list_del(&hole->list);
192 free(hole);
193 }
194 }
195 } else {
196 struct amdgpu_bo_va_hole *next;
Alex Deucher09361392015-04-20 12:04:22 -0400197
monk.liu9066acf2015-05-13 13:58:43 +0800198 hole = container_of(&mgr->va_holes, hole, list);
199 LIST_FOR_EACH_ENTRY(next, &mgr->va_holes, list) {
200 if (next->offset < va)
201 break;
202 hole = next;
203 }
Alex Deucher09361392015-04-20 12:04:22 -0400204
monk.liu9066acf2015-05-13 13:58:43 +0800205 if (&hole->list != &mgr->va_holes) {
206 /* Grow upper hole if it's adjacent */
207 if (hole->offset == (va + size)) {
208 hole->offset = va;
209 hole->size += size;
210 /* Merge lower hole if it's adjacent */
211 if (next != hole
212 && &next->list != &mgr->va_holes
213 && (next->offset + next->size) == va) {
214 next->size += hole->size;
215 list_del(&hole->list);
216 free(hole);
217 }
218 goto out;
219 }
220 }
Alex Deucher09361392015-04-20 12:04:22 -0400221
monk.liu9066acf2015-05-13 13:58:43 +0800222 /* Grow lower hole if it's adjacent */
223 if (next != hole && &next->list != &mgr->va_holes &&
224 (next->offset + next->size) == va) {
225 next->size += size;
226 goto out;
227 }
Alex Deucher09361392015-04-20 12:04:22 -0400228
monk.liu9066acf2015-05-13 13:58:43 +0800229 /* FIXME on allocation failure we just lose virtual address space
230 * maybe print a warning
231 */
232 next = calloc(1, sizeof(struct amdgpu_bo_va_hole));
233 if (next) {
234 next->size = size;
235 next->offset = va;
236 list_add(&next->list, &hole->list);
237 }
238 }
Alex Deucher09361392015-04-20 12:04:22 -0400239out:
monk.liu9066acf2015-05-13 13:58:43 +0800240 pthread_mutex_unlock(&mgr->bo_va_mutex);
Alex Deucher09361392015-04-20 12:04:22 -0400241}
Sabre Shao23fab592015-07-09 13:50:36 +0800242
243int amdgpu_va_range_alloc(amdgpu_device_handle dev,
244 enum amdgpu_gpu_va_range va_range_type,
245 uint64_t size,
246 uint64_t va_base_alignment,
247 uint64_t va_base_required,
248 uint64_t *va_base_allocated,
Jammy Zhou95d0f352015-07-16 10:29:58 +0800249 amdgpu_va_handle *va_range_handle,
250 uint64_t flags)
Sabre Shao23fab592015-07-09 13:50:36 +0800251{
252 va_base_alignment = MAX2(va_base_alignment, dev->vamgr->va_alignment);
253 size = ALIGN(size, vamgr.va_alignment);
254
255 *va_base_allocated = amdgpu_vamgr_find_va(dev->vamgr, size,
256 va_base_alignment, va_base_required);
257
258 if (*va_base_allocated != AMDGPU_INVALID_VA_ADDRESS) {
259 struct amdgpu_va* va;
260 va = calloc(1, sizeof(struct amdgpu_va));
261 if(!va){
262 amdgpu_vamgr_free_va(dev->vamgr, *va_base_allocated, size);
263 return -ENOMEM;
264 }
265 va->dev = dev;
266 va->address = *va_base_allocated;
267 va->size = size;
268 va->range = va_range_type;
269 *va_range_handle = va;
270 } else {
271 return -EINVAL;
272 }
273
274 return 0;
275}
276
277int amdgpu_va_range_free(amdgpu_va_handle va_range_handle)
278{
279 if(!va_range_handle || !va_range_handle->address)
280 return 0;
281 amdgpu_vamgr_free_va(va_range_handle->dev->vamgr, va_range_handle->address,
282 va_range_handle->size);
283 free(va_range_handle);
284 return 0;
285}