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 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 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 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 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 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 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 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