1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright © 2022 Google LLC 3bf215546Sopenharmony_ci * SPDX-License-Identifier: MIT 4bf215546Sopenharmony_ci */ 5bf215546Sopenharmony_ci 6bf215546Sopenharmony_ci/** 7bf215546Sopenharmony_ci * Suballocator for space within BOs. 8bf215546Sopenharmony_ci * 9bf215546Sopenharmony_ci * BOs are allocated at PAGE_SIZE (typically 4k) granularity, so small 10bf215546Sopenharmony_ci * allocations are a waste to have in their own BO. Moreover, on DRM we track a 11bf215546Sopenharmony_ci * list of all BOs currently allocated and submit the whole list for validation 12bf215546Sopenharmony_ci * (busy tracking and implicit sync) on every submit, and that validation is a 13bf215546Sopenharmony_ci * non-trivial cost. So, being able to pack multiple allocations into a BO can 14bf215546Sopenharmony_ci * be a significant performance win. 15bf215546Sopenharmony_ci * 16bf215546Sopenharmony_ci * The allocator tracks a current BO it is linearly allocating from, and up to 17bf215546Sopenharmony_ci * one extra BO returned to the pool when all of its previous suballocations 18bf215546Sopenharmony_ci * have been freed. This means that fragmentation can be an issue for 19bf215546Sopenharmony_ci * default_size > PAGE_SIZE and small allocations. Also, excessive BO 20bf215546Sopenharmony_ci * reallocation may happen for workloads where default size < working set size. 21bf215546Sopenharmony_ci */ 22bf215546Sopenharmony_ci 23bf215546Sopenharmony_ci#include "tu_suballoc.h" 24bf215546Sopenharmony_ci 25bf215546Sopenharmony_ci/* Initializes a BO sub-allocator using refcounts on BOs. 26bf215546Sopenharmony_ci */ 27bf215546Sopenharmony_civoid 28bf215546Sopenharmony_citu_bo_suballocator_init(struct tu_suballocator *suballoc, 29bf215546Sopenharmony_ci struct tu_device *dev, 30bf215546Sopenharmony_ci uint32_t default_size, uint32_t flags) 31bf215546Sopenharmony_ci{ 32bf215546Sopenharmony_ci suballoc->dev = dev; 33bf215546Sopenharmony_ci suballoc->default_size = default_size; 34bf215546Sopenharmony_ci suballoc->flags = flags; 35bf215546Sopenharmony_ci suballoc->bo = NULL; 36bf215546Sopenharmony_ci suballoc->cached_bo = NULL; 37bf215546Sopenharmony_ci} 38bf215546Sopenharmony_ci 39bf215546Sopenharmony_civoid 40bf215546Sopenharmony_citu_bo_suballocator_finish(struct tu_suballocator *suballoc) 41bf215546Sopenharmony_ci{ 42bf215546Sopenharmony_ci if (suballoc->bo) 43bf215546Sopenharmony_ci tu_bo_finish(suballoc->dev, suballoc->bo); 44bf215546Sopenharmony_ci if (suballoc->cached_bo) 45bf215546Sopenharmony_ci tu_bo_finish(suballoc->dev, suballoc->cached_bo); 46bf215546Sopenharmony_ci} 47bf215546Sopenharmony_ci 48bf215546Sopenharmony_ciVkResult 49bf215546Sopenharmony_citu_suballoc_bo_alloc(struct tu_suballoc_bo *suballoc_bo, 50bf215546Sopenharmony_ci struct tu_suballocator *suballoc, 51bf215546Sopenharmony_ci uint32_t size, uint32_t align) 52bf215546Sopenharmony_ci{ 53bf215546Sopenharmony_ci struct tu_bo *bo = suballoc->bo; 54bf215546Sopenharmony_ci if (bo) { 55bf215546Sopenharmony_ci uint32_t offset = ALIGN(suballoc->next_offset, align); 56bf215546Sopenharmony_ci if (offset + size <= bo->size) { 57bf215546Sopenharmony_ci suballoc_bo->bo = tu_bo_get_ref(bo); 58bf215546Sopenharmony_ci suballoc_bo->iova = bo->iova + offset; 59bf215546Sopenharmony_ci suballoc_bo->size = size; 60bf215546Sopenharmony_ci 61bf215546Sopenharmony_ci suballoc->next_offset = offset + size; 62bf215546Sopenharmony_ci return VK_SUCCESS; 63bf215546Sopenharmony_ci } else { 64bf215546Sopenharmony_ci tu_bo_finish(suballoc->dev, bo); 65bf215546Sopenharmony_ci suballoc->bo = NULL; 66bf215546Sopenharmony_ci } 67bf215546Sopenharmony_ci } 68bf215546Sopenharmony_ci 69bf215546Sopenharmony_ci uint32_t alloc_size = MAX2(size, suballoc->default_size); 70bf215546Sopenharmony_ci 71bf215546Sopenharmony_ci /* Reuse a recycled suballoc BO if we have one and it's big enough, otherwise free it. */ 72bf215546Sopenharmony_ci if (suballoc->cached_bo) { 73bf215546Sopenharmony_ci if (alloc_size <= suballoc->cached_bo->size) 74bf215546Sopenharmony_ci suballoc->bo = suballoc->cached_bo; 75bf215546Sopenharmony_ci else 76bf215546Sopenharmony_ci tu_bo_finish(suballoc->dev, suballoc->cached_bo); 77bf215546Sopenharmony_ci suballoc->cached_bo = NULL; 78bf215546Sopenharmony_ci } 79bf215546Sopenharmony_ci 80bf215546Sopenharmony_ci /* Allocate the new BO if we didn't have one cached. */ 81bf215546Sopenharmony_ci if (!suballoc->bo) { 82bf215546Sopenharmony_ci VkResult result = tu_bo_init_new(suballoc->dev, &suballoc->bo, 83bf215546Sopenharmony_ci alloc_size, 84bf215546Sopenharmony_ci suballoc->flags); 85bf215546Sopenharmony_ci if (result != VK_SUCCESS) 86bf215546Sopenharmony_ci return result; 87bf215546Sopenharmony_ci } 88bf215546Sopenharmony_ci 89bf215546Sopenharmony_ci VkResult result = tu_bo_map(suballoc->dev, suballoc->bo); 90bf215546Sopenharmony_ci if (result != VK_SUCCESS) { 91bf215546Sopenharmony_ci tu_bo_finish(suballoc->dev, suballoc->bo); 92bf215546Sopenharmony_ci return VK_ERROR_OUT_OF_HOST_MEMORY; 93bf215546Sopenharmony_ci } 94bf215546Sopenharmony_ci 95bf215546Sopenharmony_ci suballoc_bo->bo = tu_bo_get_ref(suballoc->bo); 96bf215546Sopenharmony_ci suballoc_bo->iova = suballoc_bo->bo->iova; 97bf215546Sopenharmony_ci suballoc_bo->size = size; 98bf215546Sopenharmony_ci suballoc->next_offset = size; 99bf215546Sopenharmony_ci 100bf215546Sopenharmony_ci return VK_SUCCESS; 101bf215546Sopenharmony_ci} 102bf215546Sopenharmony_ci 103bf215546Sopenharmony_civoid 104bf215546Sopenharmony_citu_suballoc_bo_free(struct tu_suballocator *suballoc, struct tu_suballoc_bo *bo) 105bf215546Sopenharmony_ci{ 106bf215546Sopenharmony_ci if (!bo->bo) 107bf215546Sopenharmony_ci return; 108bf215546Sopenharmony_ci 109bf215546Sopenharmony_ci /* If we we held the last reference to this BO, so just move it to the 110bf215546Sopenharmony_ci * suballocator for the next time we need to allocate. 111bf215546Sopenharmony_ci */ 112bf215546Sopenharmony_ci if (p_atomic_read(&bo->bo->refcnt) == 1 && !suballoc->cached_bo) { 113bf215546Sopenharmony_ci suballoc->cached_bo = bo->bo; 114bf215546Sopenharmony_ci return; 115bf215546Sopenharmony_ci } 116bf215546Sopenharmony_ci 117bf215546Sopenharmony_ci /* Otherwise, drop the refcount on it normally. */ 118bf215546Sopenharmony_ci tu_bo_finish(suballoc->dev, bo->bo); 119bf215546Sopenharmony_ci} 120bf215546Sopenharmony_ci 121bf215546Sopenharmony_civoid * 122bf215546Sopenharmony_citu_suballoc_bo_map(struct tu_suballoc_bo *bo) 123bf215546Sopenharmony_ci{ 124bf215546Sopenharmony_ci return bo->bo->map + (bo->iova - bo->bo->iova); 125bf215546Sopenharmony_ci} 126