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