amdgpu_vamgr.c revision 02fa736b
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#include <stdlib.h> 25#include <string.h> 26#include <errno.h> 27#include "amdgpu.h" 28#include "amdgpu_drm.h" 29#include "amdgpu_internal.h" 30#include "util_math.h" 31 32drm_public int amdgpu_va_range_query(amdgpu_device_handle dev, 33 enum amdgpu_gpu_va_range type, 34 uint64_t *start, uint64_t *end) 35{ 36 if (type != amdgpu_gpu_va_range_general) 37 return -EINVAL; 38 39 *start = dev->dev_info.virtual_address_offset; 40 *end = dev->dev_info.virtual_address_max; 41 return 0; 42} 43 44drm_private void amdgpu_vamgr_init(struct amdgpu_bo_va_mgr *mgr, uint64_t start, 45 uint64_t max, uint64_t alignment) 46{ 47 struct amdgpu_bo_va_hole *n; 48 49 mgr->va_max = max; 50 mgr->va_alignment = alignment; 51 52 list_inithead(&mgr->va_holes); 53 pthread_mutex_init(&mgr->bo_va_mutex, NULL); 54 pthread_mutex_lock(&mgr->bo_va_mutex); 55 n = calloc(1, sizeof(struct amdgpu_bo_va_hole)); 56 n->size = mgr->va_max - start; 57 n->offset = start; 58 list_add(&n->list, &mgr->va_holes); 59 pthread_mutex_unlock(&mgr->bo_va_mutex); 60} 61 62drm_private void amdgpu_vamgr_deinit(struct amdgpu_bo_va_mgr *mgr) 63{ 64 struct amdgpu_bo_va_hole *hole, *tmp; 65 LIST_FOR_EACH_ENTRY_SAFE(hole, tmp, &mgr->va_holes, list) { 66 list_del(&hole->list); 67 free(hole); 68 } 69 pthread_mutex_destroy(&mgr->bo_va_mutex); 70} 71 72static int 73amdgpu_vamgr_subtract_hole(struct amdgpu_bo_va_hole *hole, uint64_t start_va, 74 uint64_t end_va) 75{ 76 if (start_va > hole->offset && end_va - hole->offset < hole->size) { 77 struct amdgpu_bo_va_hole *n = calloc(1, sizeof(struct amdgpu_bo_va_hole)); 78 if (!n) 79 return -ENOMEM; 80 81 n->size = start_va - hole->offset; 82 n->offset = hole->offset; 83 list_add(&n->list, &hole->list); 84 85 hole->size -= (end_va - hole->offset); 86 hole->offset = end_va; 87 } else if (start_va > hole->offset) { 88 hole->size = start_va - hole->offset; 89 } else if (end_va - hole->offset < hole->size) { 90 hole->size -= (end_va - hole->offset); 91 hole->offset = end_va; 92 } else { 93 list_del(&hole->list); 94 free(hole); 95 } 96 97 return 0; 98} 99 100static int 101amdgpu_vamgr_find_va(struct amdgpu_bo_va_mgr *mgr, uint64_t size, 102 uint64_t alignment, uint64_t base_required, 103 bool search_from_top, uint64_t *va_out) 104{ 105 struct amdgpu_bo_va_hole *hole, *n; 106 uint64_t offset = 0; 107 int ret; 108 109 110 alignment = MAX2(alignment, mgr->va_alignment); 111 size = ALIGN(size, mgr->va_alignment); 112 113 if (base_required % alignment) 114 return -EINVAL; 115 116 pthread_mutex_lock(&mgr->bo_va_mutex); 117 if (!search_from_top) { 118 LIST_FOR_EACH_ENTRY_SAFE_REV(hole, n, &mgr->va_holes, list) { 119 if (base_required) { 120 if (hole->offset > base_required || 121 (hole->offset + hole->size) < (base_required + size)) 122 continue; 123 offset = base_required; 124 } else { 125 uint64_t waste = hole->offset % alignment; 126 waste = waste ? alignment - waste : 0; 127 offset = hole->offset + waste; 128 if (offset >= (hole->offset + hole->size) || 129 size > (hole->offset + hole->size) - offset) { 130 continue; 131 } 132 } 133 ret = amdgpu_vamgr_subtract_hole(hole, offset, offset + size); 134 pthread_mutex_unlock(&mgr->bo_va_mutex); 135 *va_out = offset; 136 return ret; 137 } 138 } else { 139 LIST_FOR_EACH_ENTRY_SAFE(hole, n, &mgr->va_holes, list) { 140 if (base_required) { 141 if (hole->offset > base_required || 142 (hole->offset + hole->size) < (base_required + size)) 143 continue; 144 offset = base_required; 145 } else { 146 if (size > hole->size) 147 continue; 148 149 offset = hole->offset + hole->size - size; 150 offset -= offset % alignment; 151 if (offset < hole->offset) { 152 continue; 153 } 154 } 155 156 ret = amdgpu_vamgr_subtract_hole(hole, offset, offset + size); 157 pthread_mutex_unlock(&mgr->bo_va_mutex); 158 *va_out = offset; 159 return ret; 160 } 161 } 162 163 pthread_mutex_unlock(&mgr->bo_va_mutex); 164 return -ENOMEM; 165} 166 167static void 168amdgpu_vamgr_free_va(struct amdgpu_bo_va_mgr *mgr, uint64_t va, uint64_t size) 169{ 170 struct amdgpu_bo_va_hole *hole, *next; 171 172 if (va == AMDGPU_INVALID_VA_ADDRESS) 173 return; 174 175 size = ALIGN(size, mgr->va_alignment); 176 177 pthread_mutex_lock(&mgr->bo_va_mutex); 178 hole = container_of(&mgr->va_holes, hole, list); 179 LIST_FOR_EACH_ENTRY(next, &mgr->va_holes, list) { 180 if (next->offset < va) 181 break; 182 hole = next; 183 } 184 185 if (&hole->list != &mgr->va_holes) { 186 /* Grow upper hole if it's adjacent */ 187 if (hole->offset == (va + size)) { 188 hole->offset = va; 189 hole->size += size; 190 /* Merge lower hole if it's adjacent */ 191 if (next != hole && 192 &next->list != &mgr->va_holes && 193 (next->offset + next->size) == va) { 194 next->size += hole->size; 195 list_del(&hole->list); 196 free(hole); 197 } 198 goto out; 199 } 200 } 201 202 /* Grow lower hole if it's adjacent */ 203 if (next != hole && &next->list != &mgr->va_holes && 204 (next->offset + next->size) == va) { 205 next->size += size; 206 goto out; 207 } 208 209 /* FIXME on allocation failure we just lose virtual address space 210 * maybe print a warning 211 */ 212 next = calloc(1, sizeof(struct amdgpu_bo_va_hole)); 213 if (next) { 214 next->size = size; 215 next->offset = va; 216 list_add(&next->list, &hole->list); 217 } 218 219out: 220 pthread_mutex_unlock(&mgr->bo_va_mutex); 221} 222 223drm_public int amdgpu_va_range_alloc(amdgpu_device_handle dev, 224 enum amdgpu_gpu_va_range va_range_type, 225 uint64_t size, 226 uint64_t va_base_alignment, 227 uint64_t va_base_required, 228 uint64_t *va_base_allocated, 229 amdgpu_va_handle *va_range_handle, 230 uint64_t flags) 231{ 232 struct amdgpu_bo_va_mgr *vamgr; 233 bool search_from_top = !!(flags & AMDGPU_VA_RANGE_REPLAYABLE); 234 int ret; 235 236 /* Clear the flag when the high VA manager is not initialized */ 237 if (flags & AMDGPU_VA_RANGE_HIGH && !dev->vamgr_high_32.va_max) 238 flags &= ~AMDGPU_VA_RANGE_HIGH; 239 240 if (flags & AMDGPU_VA_RANGE_HIGH) { 241 if (flags & AMDGPU_VA_RANGE_32_BIT) 242 vamgr = &dev->vamgr_high_32; 243 else 244 vamgr = &dev->vamgr_high; 245 } else { 246 if (flags & AMDGPU_VA_RANGE_32_BIT) 247 vamgr = &dev->vamgr_32; 248 else 249 vamgr = &dev->vamgr; 250 } 251 252 va_base_alignment = MAX2(va_base_alignment, vamgr->va_alignment); 253 size = ALIGN(size, vamgr->va_alignment); 254 255 ret = amdgpu_vamgr_find_va(vamgr, size, 256 va_base_alignment, va_base_required, 257 search_from_top, va_base_allocated); 258 259 if (!(flags & AMDGPU_VA_RANGE_32_BIT) && ret) { 260 /* fallback to 32bit address */ 261 if (flags & AMDGPU_VA_RANGE_HIGH) 262 vamgr = &dev->vamgr_high_32; 263 else 264 vamgr = &dev->vamgr_32; 265 ret = amdgpu_vamgr_find_va(vamgr, size, 266 va_base_alignment, va_base_required, 267 search_from_top, va_base_allocated); 268 } 269 270 if (!ret) { 271 struct amdgpu_va* va; 272 va = calloc(1, sizeof(struct amdgpu_va)); 273 if(!va){ 274 amdgpu_vamgr_free_va(vamgr, *va_base_allocated, size); 275 return -ENOMEM; 276 } 277 va->dev = dev; 278 va->address = *va_base_allocated; 279 va->size = size; 280 va->range = va_range_type; 281 va->vamgr = vamgr; 282 *va_range_handle = va; 283 } 284 285 return ret; 286} 287 288drm_public int amdgpu_va_range_free(amdgpu_va_handle va_range_handle) 289{ 290 if(!va_range_handle || !va_range_handle->address) 291 return 0; 292 293 amdgpu_vamgr_free_va(va_range_handle->vamgr, 294 va_range_handle->address, 295 va_range_handle->size); 296 free(va_range_handle); 297 return 0; 298} 299