blob: d6ec9b4f0156e4be5762dd7a6f2db77e64122914 [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 *
22*/
23
24#include <stdlib.h>
25#include <string.h>
Sabre Shao23fab592015-07-09 13:50:36 +080026#include <errno.h>
Alex Deucher09361392015-04-20 12:04:22 -040027#include "amdgpu.h"
28#include "amdgpu_drm.h"
29#include "amdgpu_internal.h"
30#include "util_math.h"
31
Ken Wang322d02d2015-05-21 17:21:21 +080032static struct amdgpu_bo_va_mgr vamgr = {{0}};
33
Sabre Shao12802da2015-07-09 13:53:24 +080034int amdgpu_va_range_query(amdgpu_device_handle dev,
35 enum amdgpu_gpu_va_range type, uint64_t *start, uint64_t *end)
36{
37 if (type == amdgpu_gpu_va_range_general) {
38 *start = dev->dev_info.virtual_address_offset;
39 *end = dev->dev_info.virtual_address_max;
40 return 0;
41 }
42 return -EINVAL;
43}
44
Ken Wang322d02d2015-05-21 17:21:21 +080045static void amdgpu_vamgr_init(struct amdgpu_bo_va_mgr *mgr, struct amdgpu_device *dev)
Alex Deucher09361392015-04-20 12:04:22 -040046{
Ken Wang322d02d2015-05-21 17:21:21 +080047 mgr->va_offset = dev->dev_info.virtual_address_offset;
48 mgr->va_max = dev->dev_info.virtual_address_max;
49 mgr->va_alignment = dev->dev_info.virtual_address_alignment;
Alex Deucher09361392015-04-20 12:04:22 -040050
Ken Wang322d02d2015-05-21 17:21:21 +080051 list_inithead(&mgr->va_holes);
52 pthread_mutex_init(&mgr->bo_va_mutex, NULL);
53}
Alex Deucher09361392015-04-20 12:04:22 -040054
Ken Wang322d02d2015-05-21 17:21:21 +080055static void amdgpu_vamgr_deinit(struct amdgpu_bo_va_mgr *mgr)
56{
57 struct amdgpu_bo_va_hole *hole;
58 LIST_FOR_EACH_ENTRY(hole, &mgr->va_holes, list) {
59 list_del(&hole->list);
60 free(hole);
61 }
62 pthread_mutex_destroy(&mgr->bo_va_mutex);
63}
64
65struct amdgpu_bo_va_mgr * amdgpu_vamgr_get_global(struct amdgpu_device *dev)
66{
67 int ref;
68 ref = atomic_inc_return(&vamgr.refcount);
69
70 if (ref == 1)
71 amdgpu_vamgr_init(&vamgr, dev);
72 return &vamgr;
73}
74
75void amdgpu_vamgr_reference(struct amdgpu_bo_va_mgr **dst,
76 struct amdgpu_bo_va_mgr *src)
77{
78 if (update_references(&(*dst)->refcount, NULL))
79 amdgpu_vamgr_deinit(*dst);
80 *dst = src;
Alex Deucher09361392015-04-20 12:04:22 -040081}
82
Ken Wang5b019082015-07-09 13:48:25 +080083uint64_t amdgpu_vamgr_find_va(struct amdgpu_bo_va_mgr *mgr, uint64_t size,
84 uint64_t alignment, uint64_t base_required)
Alex Deucher09361392015-04-20 12:04:22 -040085{
monk.liu9066acf2015-05-13 13:58:43 +080086 struct amdgpu_bo_va_hole *hole, *n;
87 uint64_t offset = 0, waste = 0;
Alex Deucher09361392015-04-20 12:04:22 -040088
monk.liu9066acf2015-05-13 13:58:43 +080089 alignment = MAX2(alignment, mgr->va_alignment);
90 size = ALIGN(size, mgr->va_alignment);
Alex Deucher09361392015-04-20 12:04:22 -040091
Ken Wang5b019082015-07-09 13:48:25 +080092 if (base_required % alignment)
93 return AMDGPU_INVALID_VA_ADDRESS;
94
monk.liu9066acf2015-05-13 13:58:43 +080095 pthread_mutex_lock(&mgr->bo_va_mutex);
96 /* TODO: using more appropriate way to track the holes */
97 /* first look for a hole */
Ken Wang5b019082015-07-09 13:48:25 +080098 LIST_FOR_EACH_ENTRY_SAFE(hole, n, &vamgr.va_holes, list) {
99 if (base_required) {
100 if(hole->offset > base_required ||
101 (hole->offset + hole->size) < (base_required + size))
102 continue;
103 waste = base_required - hole->offset;
104 offset = base_required;
105 } else {
106 offset = hole->offset;
107 waste = offset % alignment;
108 waste = waste ? alignment - waste : 0;
109 offset += waste;
110 if (offset >= (hole->offset + hole->size)) {
111 continue;
112 }
monk.liu9066acf2015-05-13 13:58:43 +0800113 }
114 if (!waste && hole->size == size) {
115 offset = hole->offset;
116 list_del(&hole->list);
117 free(hole);
118 pthread_mutex_unlock(&mgr->bo_va_mutex);
119 return offset;
120 }
121 if ((hole->size - waste) > size) {
122 if (waste) {
Ken Wang5b019082015-07-09 13:48:25 +0800123 n = calloc(1, sizeof(struct amdgpu_bo_va_hole));
monk.liu9066acf2015-05-13 13:58:43 +0800124 n->size = waste;
125 n->offset = hole->offset;
126 list_add(&n->list, &hole->list);
127 }
128 hole->size -= (size + waste);
129 hole->offset += size + waste;
130 pthread_mutex_unlock(&mgr->bo_va_mutex);
131 return offset;
132 }
133 if ((hole->size - waste) == size) {
134 hole->size = waste;
135 pthread_mutex_unlock(&mgr->bo_va_mutex);
136 return offset;
137 }
138 }
Alex Deucher09361392015-04-20 12:04:22 -0400139
Ken Wang5b019082015-07-09 13:48:25 +0800140 if (base_required) {
141 if (base_required < mgr->va_offset)
142 return AMDGPU_INVALID_VA_ADDRESS;
143 offset = mgr->va_offset;
144 waste = base_required - mgr->va_offset;
145 } else {
146 offset = mgr->va_offset;
147 waste = offset % alignment;
148 waste = waste ? alignment - waste : 0;
149 }
Jammy Zhou241cf6d2015-05-13 01:14:11 +0800150
151 if (offset + waste + size > mgr->va_max) {
152 pthread_mutex_unlock(&mgr->bo_va_mutex);
153 return AMDGPU_INVALID_VA_ADDRESS;
154 }
155
monk.liu9066acf2015-05-13 13:58:43 +0800156 if (waste) {
157 n = calloc(1, sizeof(struct amdgpu_bo_va_hole));
158 n->size = waste;
159 n->offset = offset;
160 list_add(&n->list, &mgr->va_holes);
161 }
Ken Wang5b019082015-07-09 13:48:25 +0800162
monk.liu9066acf2015-05-13 13:58:43 +0800163 offset += waste;
164 mgr->va_offset += size + waste;
165 pthread_mutex_unlock(&mgr->bo_va_mutex);
166 return offset;
Alex Deucher09361392015-04-20 12:04:22 -0400167}
168
Ken Wang322d02d2015-05-21 17:21:21 +0800169void amdgpu_vamgr_free_va(struct amdgpu_bo_va_mgr *mgr,
170 uint64_t va, uint64_t size)
Alex Deucher09361392015-04-20 12:04:22 -0400171{
monk.liu9066acf2015-05-13 13:58:43 +0800172 struct amdgpu_bo_va_hole *hole;
Alex Deucher09361392015-04-20 12:04:22 -0400173
monk.liud3e71952015-05-13 14:01:53 +0800174 if (va == AMDGPU_INVALID_VA_ADDRESS)
175 return;
176
monk.liu9066acf2015-05-13 13:58:43 +0800177 size = ALIGN(size, mgr->va_alignment);
Alex Deucher09361392015-04-20 12:04:22 -0400178
monk.liu9066acf2015-05-13 13:58:43 +0800179 pthread_mutex_lock(&mgr->bo_va_mutex);
180 if ((va + size) == mgr->va_offset) {
181 mgr->va_offset = va;
182 /* Delete uppermost hole if it reaches the new top */
183 if (!LIST_IS_EMPTY(&mgr->va_holes)) {
184 hole = container_of(mgr->va_holes.next, hole, list);
185 if ((hole->offset + hole->size) == va) {
186 mgr->va_offset = hole->offset;
187 list_del(&hole->list);
188 free(hole);
189 }
190 }
191 } else {
192 struct amdgpu_bo_va_hole *next;
Alex Deucher09361392015-04-20 12:04:22 -0400193
monk.liu9066acf2015-05-13 13:58:43 +0800194 hole = container_of(&mgr->va_holes, hole, list);
195 LIST_FOR_EACH_ENTRY(next, &mgr->va_holes, list) {
196 if (next->offset < va)
197 break;
198 hole = next;
199 }
Alex Deucher09361392015-04-20 12:04:22 -0400200
monk.liu9066acf2015-05-13 13:58:43 +0800201 if (&hole->list != &mgr->va_holes) {
202 /* Grow upper hole if it's adjacent */
203 if (hole->offset == (va + size)) {
204 hole->offset = va;
205 hole->size += size;
206 /* Merge lower hole if it's adjacent */
207 if (next != hole
208 && &next->list != &mgr->va_holes
209 && (next->offset + next->size) == va) {
210 next->size += hole->size;
211 list_del(&hole->list);
212 free(hole);
213 }
214 goto out;
215 }
216 }
Alex Deucher09361392015-04-20 12:04:22 -0400217
monk.liu9066acf2015-05-13 13:58:43 +0800218 /* Grow lower hole if it's adjacent */
219 if (next != hole && &next->list != &mgr->va_holes &&
220 (next->offset + next->size) == va) {
221 next->size += size;
222 goto out;
223 }
Alex Deucher09361392015-04-20 12:04:22 -0400224
monk.liu9066acf2015-05-13 13:58:43 +0800225 /* FIXME on allocation failure we just lose virtual address space
226 * maybe print a warning
227 */
228 next = calloc(1, sizeof(struct amdgpu_bo_va_hole));
229 if (next) {
230 next->size = size;
231 next->offset = va;
232 list_add(&next->list, &hole->list);
233 }
234 }
Alex Deucher09361392015-04-20 12:04:22 -0400235out:
monk.liu9066acf2015-05-13 13:58:43 +0800236 pthread_mutex_unlock(&mgr->bo_va_mutex);
Alex Deucher09361392015-04-20 12:04:22 -0400237}
Sabre Shao23fab592015-07-09 13:50:36 +0800238
239int amdgpu_va_range_alloc(amdgpu_device_handle dev,
240 enum amdgpu_gpu_va_range va_range_type,
241 uint64_t size,
242 uint64_t va_base_alignment,
243 uint64_t va_base_required,
244 uint64_t *va_base_allocated,
245 amdgpu_va_handle *va_range_handle)
246{
247 va_base_alignment = MAX2(va_base_alignment, dev->vamgr->va_alignment);
248 size = ALIGN(size, vamgr.va_alignment);
249
250 *va_base_allocated = amdgpu_vamgr_find_va(dev->vamgr, size,
251 va_base_alignment, va_base_required);
252
253 if (*va_base_allocated != AMDGPU_INVALID_VA_ADDRESS) {
254 struct amdgpu_va* va;
255 va = calloc(1, sizeof(struct amdgpu_va));
256 if(!va){
257 amdgpu_vamgr_free_va(dev->vamgr, *va_base_allocated, size);
258 return -ENOMEM;
259 }
260 va->dev = dev;
261 va->address = *va_base_allocated;
262 va->size = size;
263 va->range = va_range_type;
264 *va_range_handle = va;
265 } else {
266 return -EINVAL;
267 }
268
269 return 0;
270}
271
272int amdgpu_va_range_free(amdgpu_va_handle va_range_handle)
273{
274 if(!va_range_handle || !va_range_handle->address)
275 return 0;
276 amdgpu_vamgr_free_va(va_range_handle->dev->vamgr, va_range_handle->address,
277 va_range_handle->size);
278 free(va_range_handle);
279 return 0;
280}