1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright © 2018 Intel Corporation 3bf215546Sopenharmony_ci * 4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10bf215546Sopenharmony_ci * 11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13bf215546Sopenharmony_ci * Software. 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21bf215546Sopenharmony_ci * IN THE SOFTWARE. 22bf215546Sopenharmony_ci */ 23bf215546Sopenharmony_ci 24bf215546Sopenharmony_ci#include <stdlib.h> 25bf215546Sopenharmony_ci#include <inttypes.h> 26bf215546Sopenharmony_ci 27bf215546Sopenharmony_ci#include "util/u_math.h" 28bf215546Sopenharmony_ci#include "util/vma.h" 29bf215546Sopenharmony_ci 30bf215546Sopenharmony_cistruct util_vma_hole { 31bf215546Sopenharmony_ci struct list_head link; 32bf215546Sopenharmony_ci uint64_t offset; 33bf215546Sopenharmony_ci uint64_t size; 34bf215546Sopenharmony_ci}; 35bf215546Sopenharmony_ci 36bf215546Sopenharmony_ci#define util_vma_foreach_hole(_hole, _heap) \ 37bf215546Sopenharmony_ci list_for_each_entry(struct util_vma_hole, _hole, &(_heap)->holes, link) 38bf215546Sopenharmony_ci 39bf215546Sopenharmony_ci#define util_vma_foreach_hole_safe(_hole, _heap) \ 40bf215546Sopenharmony_ci list_for_each_entry_safe(struct util_vma_hole, _hole, &(_heap)->holes, link) 41bf215546Sopenharmony_ci 42bf215546Sopenharmony_ci#define util_vma_foreach_hole_safe_rev(_hole, _heap) \ 43bf215546Sopenharmony_ci list_for_each_entry_safe_rev(struct util_vma_hole, _hole, &(_heap)->holes, link) 44bf215546Sopenharmony_ci 45bf215546Sopenharmony_civoid 46bf215546Sopenharmony_ciutil_vma_heap_init(struct util_vma_heap *heap, 47bf215546Sopenharmony_ci uint64_t start, uint64_t size) 48bf215546Sopenharmony_ci{ 49bf215546Sopenharmony_ci list_inithead(&heap->holes); 50bf215546Sopenharmony_ci util_vma_heap_free(heap, start, size); 51bf215546Sopenharmony_ci 52bf215546Sopenharmony_ci /* Default to using high addresses */ 53bf215546Sopenharmony_ci heap->alloc_high = true; 54bf215546Sopenharmony_ci} 55bf215546Sopenharmony_ci 56bf215546Sopenharmony_civoid 57bf215546Sopenharmony_ciutil_vma_heap_finish(struct util_vma_heap *heap) 58bf215546Sopenharmony_ci{ 59bf215546Sopenharmony_ci util_vma_foreach_hole_safe(hole, heap) 60bf215546Sopenharmony_ci free(hole); 61bf215546Sopenharmony_ci} 62bf215546Sopenharmony_ci 63bf215546Sopenharmony_ci#ifndef NDEBUG 64bf215546Sopenharmony_cistatic void 65bf215546Sopenharmony_ciutil_vma_heap_validate(struct util_vma_heap *heap) 66bf215546Sopenharmony_ci{ 67bf215546Sopenharmony_ci uint64_t prev_offset = 0; 68bf215546Sopenharmony_ci util_vma_foreach_hole(hole, heap) { 69bf215546Sopenharmony_ci assert(hole->offset > 0); 70bf215546Sopenharmony_ci assert(hole->size > 0); 71bf215546Sopenharmony_ci 72bf215546Sopenharmony_ci if (&hole->link == heap->holes.next) { 73bf215546Sopenharmony_ci /* This must be the top-most hole. Assert that, if it overflows, it 74bf215546Sopenharmony_ci * overflows to 0, i.e. 2^64. 75bf215546Sopenharmony_ci */ 76bf215546Sopenharmony_ci assert(hole->size + hole->offset == 0 || 77bf215546Sopenharmony_ci hole->size + hole->offset > hole->offset); 78bf215546Sopenharmony_ci } else { 79bf215546Sopenharmony_ci /* This is not the top-most hole so it must not overflow and, in 80bf215546Sopenharmony_ci * fact, must be strictly lower than the top-most hole. If 81bf215546Sopenharmony_ci * hole->size + hole->offset == prev_offset, then we failed to join 82bf215546Sopenharmony_ci * holes during a util_vma_heap_free. 83bf215546Sopenharmony_ci */ 84bf215546Sopenharmony_ci assert(hole->size + hole->offset > hole->offset && 85bf215546Sopenharmony_ci hole->size + hole->offset < prev_offset); 86bf215546Sopenharmony_ci } 87bf215546Sopenharmony_ci prev_offset = hole->offset; 88bf215546Sopenharmony_ci } 89bf215546Sopenharmony_ci} 90bf215546Sopenharmony_ci#else 91bf215546Sopenharmony_ci#define util_vma_heap_validate(heap) 92bf215546Sopenharmony_ci#endif 93bf215546Sopenharmony_ci 94bf215546Sopenharmony_cistatic void 95bf215546Sopenharmony_ciutil_vma_hole_alloc(struct util_vma_hole *hole, 96bf215546Sopenharmony_ci uint64_t offset, uint64_t size) 97bf215546Sopenharmony_ci{ 98bf215546Sopenharmony_ci assert(hole->offset <= offset); 99bf215546Sopenharmony_ci assert(hole->size >= offset - hole->offset + size); 100bf215546Sopenharmony_ci 101bf215546Sopenharmony_ci if (offset == hole->offset && size == hole->size) { 102bf215546Sopenharmony_ci /* Just get rid of the hole. */ 103bf215546Sopenharmony_ci list_del(&hole->link); 104bf215546Sopenharmony_ci free(hole); 105bf215546Sopenharmony_ci return; 106bf215546Sopenharmony_ci } 107bf215546Sopenharmony_ci 108bf215546Sopenharmony_ci assert(offset - hole->offset <= hole->size - size); 109bf215546Sopenharmony_ci uint64_t waste = (hole->size - size) - (offset - hole->offset); 110bf215546Sopenharmony_ci if (waste == 0) { 111bf215546Sopenharmony_ci /* We allocated at the top. Shrink the hole down. */ 112bf215546Sopenharmony_ci hole->size -= size; 113bf215546Sopenharmony_ci return; 114bf215546Sopenharmony_ci } 115bf215546Sopenharmony_ci 116bf215546Sopenharmony_ci if (offset == hole->offset) { 117bf215546Sopenharmony_ci /* We allocated at the bottom. Shrink the hole up. */ 118bf215546Sopenharmony_ci hole->offset += size; 119bf215546Sopenharmony_ci hole->size -= size; 120bf215546Sopenharmony_ci return; 121bf215546Sopenharmony_ci } 122bf215546Sopenharmony_ci 123bf215546Sopenharmony_ci /* We allocated in the middle. We need to split the old hole into two 124bf215546Sopenharmony_ci * holes, one high and one low. 125bf215546Sopenharmony_ci */ 126bf215546Sopenharmony_ci struct util_vma_hole *high_hole = calloc(1, sizeof(*hole)); 127bf215546Sopenharmony_ci high_hole->offset = offset + size; 128bf215546Sopenharmony_ci high_hole->size = waste; 129bf215546Sopenharmony_ci 130bf215546Sopenharmony_ci /* Adjust the hole to be the amount of space left at he bottom of the 131bf215546Sopenharmony_ci * original hole. 132bf215546Sopenharmony_ci */ 133bf215546Sopenharmony_ci hole->size = offset - hole->offset; 134bf215546Sopenharmony_ci 135bf215546Sopenharmony_ci /* Place the new hole before the old hole so that the list is in order 136bf215546Sopenharmony_ci * from high to low. 137bf215546Sopenharmony_ci */ 138bf215546Sopenharmony_ci list_addtail(&high_hole->link, &hole->link); 139bf215546Sopenharmony_ci} 140bf215546Sopenharmony_ci 141bf215546Sopenharmony_ciuint64_t 142bf215546Sopenharmony_ciutil_vma_heap_alloc(struct util_vma_heap *heap, 143bf215546Sopenharmony_ci uint64_t size, uint64_t alignment) 144bf215546Sopenharmony_ci{ 145bf215546Sopenharmony_ci /* The caller is expected to reject zero-size allocations */ 146bf215546Sopenharmony_ci assert(size > 0); 147bf215546Sopenharmony_ci assert(alignment > 0); 148bf215546Sopenharmony_ci 149bf215546Sopenharmony_ci util_vma_heap_validate(heap); 150bf215546Sopenharmony_ci 151bf215546Sopenharmony_ci if (heap->alloc_high) { 152bf215546Sopenharmony_ci util_vma_foreach_hole_safe(hole, heap) { 153bf215546Sopenharmony_ci if (size > hole->size) 154bf215546Sopenharmony_ci continue; 155bf215546Sopenharmony_ci 156bf215546Sopenharmony_ci /* Compute the offset as the highest address where a chunk of the 157bf215546Sopenharmony_ci * given size can be without going over the top of the hole. 158bf215546Sopenharmony_ci * 159bf215546Sopenharmony_ci * This calculation is known to not overflow because we know that 160bf215546Sopenharmony_ci * hole->size + hole->offset can only overflow to 0 and size > 0. 161bf215546Sopenharmony_ci */ 162bf215546Sopenharmony_ci uint64_t offset = (hole->size - size) + hole->offset; 163bf215546Sopenharmony_ci 164bf215546Sopenharmony_ci /* Align the offset. We align down and not up because we are 165bf215546Sopenharmony_ci * allocating from the top of the hole and not the bottom. 166bf215546Sopenharmony_ci */ 167bf215546Sopenharmony_ci offset = (offset / alignment) * alignment; 168bf215546Sopenharmony_ci 169bf215546Sopenharmony_ci if (offset < hole->offset) 170bf215546Sopenharmony_ci continue; 171bf215546Sopenharmony_ci 172bf215546Sopenharmony_ci util_vma_hole_alloc(hole, offset, size); 173bf215546Sopenharmony_ci util_vma_heap_validate(heap); 174bf215546Sopenharmony_ci return offset; 175bf215546Sopenharmony_ci } 176bf215546Sopenharmony_ci } else { 177bf215546Sopenharmony_ci util_vma_foreach_hole_safe_rev(hole, heap) { 178bf215546Sopenharmony_ci if (size > hole->size) 179bf215546Sopenharmony_ci continue; 180bf215546Sopenharmony_ci 181bf215546Sopenharmony_ci uint64_t offset = hole->offset; 182bf215546Sopenharmony_ci 183bf215546Sopenharmony_ci /* Align the offset */ 184bf215546Sopenharmony_ci uint64_t misalign = offset % alignment; 185bf215546Sopenharmony_ci if (misalign) { 186bf215546Sopenharmony_ci uint64_t pad = alignment - misalign; 187bf215546Sopenharmony_ci if (pad > hole->size - size) 188bf215546Sopenharmony_ci continue; 189bf215546Sopenharmony_ci 190bf215546Sopenharmony_ci offset += pad; 191bf215546Sopenharmony_ci } 192bf215546Sopenharmony_ci 193bf215546Sopenharmony_ci util_vma_hole_alloc(hole, offset, size); 194bf215546Sopenharmony_ci util_vma_heap_validate(heap); 195bf215546Sopenharmony_ci return offset; 196bf215546Sopenharmony_ci } 197bf215546Sopenharmony_ci } 198bf215546Sopenharmony_ci 199bf215546Sopenharmony_ci /* Failed to allocate */ 200bf215546Sopenharmony_ci return 0; 201bf215546Sopenharmony_ci} 202bf215546Sopenharmony_ci 203bf215546Sopenharmony_cibool 204bf215546Sopenharmony_ciutil_vma_heap_alloc_addr(struct util_vma_heap *heap, 205bf215546Sopenharmony_ci uint64_t offset, uint64_t size) 206bf215546Sopenharmony_ci{ 207bf215546Sopenharmony_ci /* An offset of 0 is reserved for allocation failure. It is not a valid 208bf215546Sopenharmony_ci * address and cannot be allocated. 209bf215546Sopenharmony_ci */ 210bf215546Sopenharmony_ci assert(offset > 0); 211bf215546Sopenharmony_ci 212bf215546Sopenharmony_ci /* Allocating something with a size of 0 is also not valid. */ 213bf215546Sopenharmony_ci assert(size > 0); 214bf215546Sopenharmony_ci 215bf215546Sopenharmony_ci /* It's possible for offset + size to wrap around if we touch the top of 216bf215546Sopenharmony_ci * the 64-bit address space, but we cannot go any higher than 2^64. 217bf215546Sopenharmony_ci */ 218bf215546Sopenharmony_ci assert(offset + size == 0 || offset + size > offset); 219bf215546Sopenharmony_ci 220bf215546Sopenharmony_ci /* Find the hole if one exists. */ 221bf215546Sopenharmony_ci util_vma_foreach_hole_safe(hole, heap) { 222bf215546Sopenharmony_ci if (hole->offset > offset) 223bf215546Sopenharmony_ci continue; 224bf215546Sopenharmony_ci 225bf215546Sopenharmony_ci /* Holes are ordered high-to-low so the first hole we find with 226bf215546Sopenharmony_ci * hole->offset <= is our hole. If it's not big enough to contain the 227bf215546Sopenharmony_ci * requested range, then the allocation fails. 228bf215546Sopenharmony_ci */ 229bf215546Sopenharmony_ci assert(hole->offset <= offset); 230bf215546Sopenharmony_ci if (hole->size < offset - hole->offset + size) 231bf215546Sopenharmony_ci return false; 232bf215546Sopenharmony_ci 233bf215546Sopenharmony_ci util_vma_hole_alloc(hole, offset, size); 234bf215546Sopenharmony_ci return true; 235bf215546Sopenharmony_ci } 236bf215546Sopenharmony_ci 237bf215546Sopenharmony_ci /* We didn't find a suitable hole */ 238bf215546Sopenharmony_ci return false; 239bf215546Sopenharmony_ci} 240bf215546Sopenharmony_ci 241bf215546Sopenharmony_civoid 242bf215546Sopenharmony_ciutil_vma_heap_free(struct util_vma_heap *heap, 243bf215546Sopenharmony_ci uint64_t offset, uint64_t size) 244bf215546Sopenharmony_ci{ 245bf215546Sopenharmony_ci /* An offset of 0 is reserved for allocation failure. It is not a valid 246bf215546Sopenharmony_ci * address and cannot be freed. 247bf215546Sopenharmony_ci */ 248bf215546Sopenharmony_ci assert(offset > 0); 249bf215546Sopenharmony_ci 250bf215546Sopenharmony_ci /* Freeing something with a size of 0 is also not valid. */ 251bf215546Sopenharmony_ci assert(size > 0); 252bf215546Sopenharmony_ci 253bf215546Sopenharmony_ci /* It's possible for offset + size to wrap around if we touch the top of 254bf215546Sopenharmony_ci * the 64-bit address space, but we cannot go any higher than 2^64. 255bf215546Sopenharmony_ci */ 256bf215546Sopenharmony_ci assert(offset + size == 0 || offset + size > offset); 257bf215546Sopenharmony_ci 258bf215546Sopenharmony_ci util_vma_heap_validate(heap); 259bf215546Sopenharmony_ci 260bf215546Sopenharmony_ci /* Find immediately higher and lower holes if they exist. */ 261bf215546Sopenharmony_ci struct util_vma_hole *high_hole = NULL, *low_hole = NULL; 262bf215546Sopenharmony_ci util_vma_foreach_hole(hole, heap) { 263bf215546Sopenharmony_ci if (hole->offset <= offset) { 264bf215546Sopenharmony_ci low_hole = hole; 265bf215546Sopenharmony_ci break; 266bf215546Sopenharmony_ci } 267bf215546Sopenharmony_ci high_hole = hole; 268bf215546Sopenharmony_ci } 269bf215546Sopenharmony_ci 270bf215546Sopenharmony_ci if (high_hole) 271bf215546Sopenharmony_ci assert(offset + size <= high_hole->offset); 272bf215546Sopenharmony_ci bool high_adjacent = high_hole && offset + size == high_hole->offset; 273bf215546Sopenharmony_ci 274bf215546Sopenharmony_ci if (low_hole) { 275bf215546Sopenharmony_ci assert(low_hole->offset + low_hole->size > low_hole->offset); 276bf215546Sopenharmony_ci assert(low_hole->offset + low_hole->size <= offset); 277bf215546Sopenharmony_ci } 278bf215546Sopenharmony_ci bool low_adjacent = low_hole && low_hole->offset + low_hole->size == offset; 279bf215546Sopenharmony_ci 280bf215546Sopenharmony_ci if (low_adjacent && high_adjacent) { 281bf215546Sopenharmony_ci /* Merge the two holes */ 282bf215546Sopenharmony_ci low_hole->size += size + high_hole->size; 283bf215546Sopenharmony_ci list_del(&high_hole->link); 284bf215546Sopenharmony_ci free(high_hole); 285bf215546Sopenharmony_ci } else if (low_adjacent) { 286bf215546Sopenharmony_ci /* Merge into the low hole */ 287bf215546Sopenharmony_ci low_hole->size += size; 288bf215546Sopenharmony_ci } else if (high_adjacent) { 289bf215546Sopenharmony_ci /* Merge into the high hole */ 290bf215546Sopenharmony_ci high_hole->offset = offset; 291bf215546Sopenharmony_ci high_hole->size += size; 292bf215546Sopenharmony_ci } else { 293bf215546Sopenharmony_ci /* Neither hole is adjacent; make a new one */ 294bf215546Sopenharmony_ci struct util_vma_hole *hole = calloc(1, sizeof(*hole)); 295bf215546Sopenharmony_ci 296bf215546Sopenharmony_ci hole->offset = offset; 297bf215546Sopenharmony_ci hole->size = size; 298bf215546Sopenharmony_ci 299bf215546Sopenharmony_ci /* Add it after the high hole so we maintain high-to-low ordering */ 300bf215546Sopenharmony_ci if (high_hole) 301bf215546Sopenharmony_ci list_add(&hole->link, &high_hole->link); 302bf215546Sopenharmony_ci else 303bf215546Sopenharmony_ci list_add(&hole->link, &heap->holes); 304bf215546Sopenharmony_ci } 305bf215546Sopenharmony_ci 306bf215546Sopenharmony_ci util_vma_heap_validate(heap); 307bf215546Sopenharmony_ci} 308bf215546Sopenharmony_ci 309bf215546Sopenharmony_civoid 310bf215546Sopenharmony_ciutil_vma_heap_print(struct util_vma_heap *heap, FILE *fp, 311bf215546Sopenharmony_ci const char *tab, uint64_t total_size) 312bf215546Sopenharmony_ci{ 313bf215546Sopenharmony_ci fprintf(fp, "%sutil_vma_heap:\n", tab); 314bf215546Sopenharmony_ci 315bf215546Sopenharmony_ci uint64_t total_free = 0; 316bf215546Sopenharmony_ci util_vma_foreach_hole(hole, heap) { 317bf215546Sopenharmony_ci fprintf(fp, "%s hole: offset = %"PRIu64" (0x%"PRIx64", " 318bf215546Sopenharmony_ci "size = %"PRIu64" (0x%"PRIx64")\n", 319bf215546Sopenharmony_ci tab, hole->offset, hole->offset, hole->size, hole->size); 320bf215546Sopenharmony_ci total_free += hole->size; 321bf215546Sopenharmony_ci } 322bf215546Sopenharmony_ci assert(total_free <= total_size); 323bf215546Sopenharmony_ci fprintf(fp, "%s%"PRIu64"B (0x%"PRIx64") free (%.2f%% full)\n", 324bf215546Sopenharmony_ci tab, total_free, total_free, 325bf215546Sopenharmony_ci ((double)(total_size - total_free) / (double)total_size) * 100); 326bf215546Sopenharmony_ci} 327