| /* | |
| * Copyright 2014 Advanced Micro Devices, Inc. | |
| * | |
| * Permission is hereby granted, free of charge, to any person obtaining a | |
| * copy of this software and associated documentation files (the "Software"), | |
| * to deal in the Software without restriction, including without limitation | |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| * and/or sell copies of the Software, and to permit persons to whom the | |
| * Software is furnished to do so, subject to the following conditions: | |
| * | |
| * The above copyright notice and this permission notice shall be included in | |
| * all copies or substantial portions of the Software. | |
| * | |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
| * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
| * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
| * OTHER DEALINGS IN THE SOFTWARE. | |
| * | |
| */ | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <errno.h> | |
| #include "amdgpu.h" | |
| #include "amdgpu_drm.h" | |
| #include "amdgpu_internal.h" | |
| #include "util_math.h" | |
| drm_public int amdgpu_va_range_query(amdgpu_device_handle dev, | |
| enum amdgpu_gpu_va_range type, | |
| uint64_t *start, uint64_t *end) | |
| { | |
| if (type != amdgpu_gpu_va_range_general) | |
| return -EINVAL; | |
| *start = dev->dev_info.virtual_address_offset; | |
| *end = dev->dev_info.virtual_address_max; | |
| return 0; | |
| } | |
| drm_private void amdgpu_vamgr_init(struct amdgpu_bo_va_mgr *mgr, uint64_t start, | |
| uint64_t max, uint64_t alignment) | |
| { | |
| struct amdgpu_bo_va_hole *n; | |
| mgr->va_max = max; | |
| mgr->va_alignment = alignment; | |
| list_inithead(&mgr->va_holes); | |
| pthread_mutex_init(&mgr->bo_va_mutex, NULL); | |
| pthread_mutex_lock(&mgr->bo_va_mutex); | |
| n = calloc(1, sizeof(struct amdgpu_bo_va_hole)); | |
| n->size = mgr->va_max - start; | |
| n->offset = start; | |
| list_add(&n->list, &mgr->va_holes); | |
| pthread_mutex_unlock(&mgr->bo_va_mutex); | |
| } | |
| drm_private void amdgpu_vamgr_deinit(struct amdgpu_bo_va_mgr *mgr) | |
| { | |
| struct amdgpu_bo_va_hole *hole, *tmp; | |
| LIST_FOR_EACH_ENTRY_SAFE(hole, tmp, &mgr->va_holes, list) { | |
| list_del(&hole->list); | |
| free(hole); | |
| } | |
| pthread_mutex_destroy(&mgr->bo_va_mutex); | |
| } | |
| static drm_private uint64_t | |
| amdgpu_vamgr_find_va(struct amdgpu_bo_va_mgr *mgr, uint64_t size, | |
| uint64_t alignment, uint64_t base_required) | |
| { | |
| struct amdgpu_bo_va_hole *hole, *n; | |
| uint64_t offset = 0, waste = 0; | |
| alignment = MAX2(alignment, mgr->va_alignment); | |
| size = ALIGN(size, mgr->va_alignment); | |
| if (base_required % alignment) | |
| return AMDGPU_INVALID_VA_ADDRESS; | |
| pthread_mutex_lock(&mgr->bo_va_mutex); | |
| LIST_FOR_EACH_ENTRY_SAFE_REV(hole, n, &mgr->va_holes, list) { | |
| if (base_required) { | |
| if (hole->offset > base_required || | |
| (hole->offset + hole->size) < (base_required + size)) | |
| continue; | |
| waste = base_required - hole->offset; | |
| offset = base_required; | |
| } else { | |
| offset = hole->offset; | |
| waste = offset % alignment; | |
| waste = waste ? alignment - waste : 0; | |
| offset += waste; | |
| if (offset >= (hole->offset + hole->size)) { | |
| continue; | |
| } | |
| } | |
| if (!waste && hole->size == size) { | |
| offset = hole->offset; | |
| list_del(&hole->list); | |
| free(hole); | |
| pthread_mutex_unlock(&mgr->bo_va_mutex); | |
| return offset; | |
| } | |
| if ((hole->size - waste) > size) { | |
| if (waste) { | |
| n = calloc(1, sizeof(struct amdgpu_bo_va_hole)); | |
| n->size = waste; | |
| n->offset = hole->offset; | |
| list_add(&n->list, &hole->list); | |
| } | |
| hole->size -= (size + waste); | |
| hole->offset += size + waste; | |
| pthread_mutex_unlock(&mgr->bo_va_mutex); | |
| return offset; | |
| } | |
| if ((hole->size - waste) == size) { | |
| hole->size = waste; | |
| pthread_mutex_unlock(&mgr->bo_va_mutex); | |
| return offset; | |
| } | |
| } | |
| pthread_mutex_unlock(&mgr->bo_va_mutex); | |
| return AMDGPU_INVALID_VA_ADDRESS; | |
| } | |
| static drm_private void | |
| amdgpu_vamgr_free_va(struct amdgpu_bo_va_mgr *mgr, uint64_t va, uint64_t size) | |
| { | |
| struct amdgpu_bo_va_hole *hole, *next; | |
| if (va == AMDGPU_INVALID_VA_ADDRESS) | |
| return; | |
| size = ALIGN(size, mgr->va_alignment); | |
| pthread_mutex_lock(&mgr->bo_va_mutex); | |
| hole = container_of(&mgr->va_holes, hole, list); | |
| LIST_FOR_EACH_ENTRY(next, &mgr->va_holes, list) { | |
| if (next->offset < va) | |
| break; | |
| hole = next; | |
| } | |
| if (&hole->list != &mgr->va_holes) { | |
| /* Grow upper hole if it's adjacent */ | |
| if (hole->offset == (va + size)) { | |
| hole->offset = va; | |
| hole->size += size; | |
| /* Merge lower hole if it's adjacent */ | |
| if (next != hole && | |
| &next->list != &mgr->va_holes && | |
| (next->offset + next->size) == va) { | |
| next->size += hole->size; | |
| list_del(&hole->list); | |
| free(hole); | |
| } | |
| goto out; | |
| } | |
| } | |
| /* Grow lower hole if it's adjacent */ | |
| if (next != hole && &next->list != &mgr->va_holes && | |
| (next->offset + next->size) == va) { | |
| next->size += size; | |
| goto out; | |
| } | |
| /* FIXME on allocation failure we just lose virtual address space | |
| * maybe print a warning | |
| */ | |
| next = calloc(1, sizeof(struct amdgpu_bo_va_hole)); | |
| if (next) { | |
| next->size = size; | |
| next->offset = va; | |
| list_add(&next->list, &hole->list); | |
| } | |
| out: | |
| pthread_mutex_unlock(&mgr->bo_va_mutex); | |
| } | |
| drm_public int amdgpu_va_range_alloc(amdgpu_device_handle dev, | |
| enum amdgpu_gpu_va_range va_range_type, | |
| uint64_t size, | |
| uint64_t va_base_alignment, | |
| uint64_t va_base_required, | |
| uint64_t *va_base_allocated, | |
| amdgpu_va_handle *va_range_handle, | |
| uint64_t flags) | |
| { | |
| struct amdgpu_bo_va_mgr *vamgr; | |
| /* Clear the flag when the high VA manager is not initialized */ | |
| if (flags & AMDGPU_VA_RANGE_HIGH && !dev->vamgr_high_32.va_max) | |
| flags &= ~AMDGPU_VA_RANGE_HIGH; | |
| if (flags & AMDGPU_VA_RANGE_HIGH) { | |
| if (flags & AMDGPU_VA_RANGE_32_BIT) | |
| vamgr = &dev->vamgr_high_32; | |
| else | |
| vamgr = &dev->vamgr_high; | |
| } else { | |
| if (flags & AMDGPU_VA_RANGE_32_BIT) | |
| vamgr = &dev->vamgr_32; | |
| else | |
| vamgr = &dev->vamgr; | |
| } | |
| va_base_alignment = MAX2(va_base_alignment, vamgr->va_alignment); | |
| size = ALIGN(size, vamgr->va_alignment); | |
| *va_base_allocated = amdgpu_vamgr_find_va(vamgr, size, | |
| va_base_alignment, va_base_required); | |
| if (!(flags & AMDGPU_VA_RANGE_32_BIT) && | |
| (*va_base_allocated == AMDGPU_INVALID_VA_ADDRESS)) { | |
| /* fallback to 32bit address */ | |
| if (flags & AMDGPU_VA_RANGE_HIGH) | |
| vamgr = &dev->vamgr_high_32; | |
| else | |
| vamgr = &dev->vamgr_32; | |
| *va_base_allocated = amdgpu_vamgr_find_va(vamgr, size, | |
| va_base_alignment, va_base_required); | |
| } | |
| if (*va_base_allocated != AMDGPU_INVALID_VA_ADDRESS) { | |
| struct amdgpu_va* va; | |
| va = calloc(1, sizeof(struct amdgpu_va)); | |
| if(!va){ | |
| amdgpu_vamgr_free_va(vamgr, *va_base_allocated, size); | |
| return -ENOMEM; | |
| } | |
| va->dev = dev; | |
| va->address = *va_base_allocated; | |
| va->size = size; | |
| va->range = va_range_type; | |
| va->vamgr = vamgr; | |
| *va_range_handle = va; | |
| } else { | |
| return -EINVAL; | |
| } | |
| return 0; | |
| } | |
| drm_public int amdgpu_va_range_free(amdgpu_va_handle va_range_handle) | |
| { | |
| if(!va_range_handle || !va_range_handle->address) | |
| return 0; | |
| amdgpu_vamgr_free_va(va_range_handle->vamgr, | |
| va_range_handle->address, | |
| va_range_handle->size); | |
| free(va_range_handle); | |
| return 0; | |
| } |