1b8e80941Smrg/* 2b8e80941Smrg * Copyright © 2018 Intel Corporation 3b8e80941Smrg * 4b8e80941Smrg * Permission is hereby granted, free of charge, to any person obtaining a 5b8e80941Smrg * copy of this software and associated documentation files (the "Software"), 6b8e80941Smrg * to deal in the Software without restriction, including without limitation 7b8e80941Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8b8e80941Smrg * and/or sell copies of the Software, and to permit persons to whom the 9b8e80941Smrg * Software is furnished to do so, subject to the following conditions: 10b8e80941Smrg * 11b8e80941Smrg * The above copyright notice and this permission notice (including the next 12b8e80941Smrg * paragraph) shall be included in all copies or substantial portions of the 13b8e80941Smrg * Software. 14b8e80941Smrg * 15b8e80941Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16b8e80941Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17b8e80941Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18b8e80941Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19b8e80941Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20b8e80941Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21b8e80941Smrg * IN THE SOFTWARE. 22b8e80941Smrg */ 23b8e80941Smrg 24b8e80941Smrg#include <stdlib.h> 25b8e80941Smrg 26b8e80941Smrg#include "util/u_math.h" 27b8e80941Smrg#include "util/vma.h" 28b8e80941Smrg 29b8e80941Smrgstruct util_vma_hole { 30b8e80941Smrg struct list_head link; 31b8e80941Smrg uint64_t offset; 32b8e80941Smrg uint64_t size; 33b8e80941Smrg}; 34b8e80941Smrg 35b8e80941Smrg#define util_vma_foreach_hole(_hole, _heap) \ 36b8e80941Smrg list_for_each_entry(struct util_vma_hole, _hole, &(_heap)->holes, link) 37b8e80941Smrg 38b8e80941Smrg#define util_vma_foreach_hole_safe(_hole, _heap) \ 39b8e80941Smrg list_for_each_entry_safe(struct util_vma_hole, _hole, &(_heap)->holes, link) 40b8e80941Smrg 41b8e80941Smrgvoid 42b8e80941Smrgutil_vma_heap_init(struct util_vma_heap *heap, 43b8e80941Smrg uint64_t start, uint64_t size) 44b8e80941Smrg{ 45b8e80941Smrg list_inithead(&heap->holes); 46b8e80941Smrg util_vma_heap_free(heap, start, size); 47b8e80941Smrg} 48b8e80941Smrg 49b8e80941Smrgvoid 50b8e80941Smrgutil_vma_heap_finish(struct util_vma_heap *heap) 51b8e80941Smrg{ 52b8e80941Smrg util_vma_foreach_hole_safe(hole, heap) 53b8e80941Smrg free(hole); 54b8e80941Smrg} 55b8e80941Smrg 56b8e80941Smrg#ifndef NDEBUG 57b8e80941Smrgstatic void 58b8e80941Smrgutil_vma_heap_validate(struct util_vma_heap *heap) 59b8e80941Smrg{ 60b8e80941Smrg uint64_t prev_offset = 0; 61b8e80941Smrg util_vma_foreach_hole(hole, heap) { 62b8e80941Smrg assert(hole->offset > 0); 63b8e80941Smrg assert(hole->size > 0); 64b8e80941Smrg 65b8e80941Smrg if (&hole->link == heap->holes.next) { 66b8e80941Smrg /* This must be the top-most hole. Assert that, if it overflows, it 67b8e80941Smrg * overflows to 0, i.e. 2^64. 68b8e80941Smrg */ 69b8e80941Smrg assert(hole->size + hole->offset == 0 || 70b8e80941Smrg hole->size + hole->offset > hole->offset); 71b8e80941Smrg } else { 72b8e80941Smrg /* This is not the top-most hole so it must not overflow and, in 73b8e80941Smrg * fact, must be strictly lower than the top-most hole. If 74b8e80941Smrg * hole->size + hole->offset == prev_offset, then we failed to join 75b8e80941Smrg * holes during a util_vma_heap_free. 76b8e80941Smrg */ 77b8e80941Smrg assert(hole->size + hole->offset > hole->offset && 78b8e80941Smrg hole->size + hole->offset < prev_offset); 79b8e80941Smrg } 80b8e80941Smrg prev_offset = hole->offset; 81b8e80941Smrg } 82b8e80941Smrg} 83b8e80941Smrg#else 84b8e80941Smrg#define util_vma_heap_validate(heap) 85b8e80941Smrg#endif 86b8e80941Smrg 87b8e80941Smrguint64_t 88b8e80941Smrgutil_vma_heap_alloc(struct util_vma_heap *heap, 89b8e80941Smrg uint64_t size, uint64_t alignment) 90b8e80941Smrg{ 91b8e80941Smrg /* The caller is expected to reject zero-size allocations */ 92b8e80941Smrg assert(size > 0); 93b8e80941Smrg assert(alignment > 0); 94b8e80941Smrg 95b8e80941Smrg util_vma_heap_validate(heap); 96b8e80941Smrg 97b8e80941Smrg util_vma_foreach_hole_safe(hole, heap) { 98b8e80941Smrg if (size > hole->size) 99b8e80941Smrg continue; 100b8e80941Smrg 101b8e80941Smrg /* Compute the offset as the highest address where a chunk of the given 102b8e80941Smrg * size can be without going over the top of the hole. 103b8e80941Smrg * 104b8e80941Smrg * This calculation is known to not overflow because we know that 105b8e80941Smrg * hole->size + hole->offset can only overflow to 0 and size > 0. 106b8e80941Smrg */ 107b8e80941Smrg uint64_t offset = (hole->size - size) + hole->offset; 108b8e80941Smrg 109b8e80941Smrg /* Align the offset. We align down and not up because we are allocating 110b8e80941Smrg * from the top of the hole and not the bottom. 111b8e80941Smrg */ 112b8e80941Smrg offset = (offset / alignment) * alignment; 113b8e80941Smrg 114b8e80941Smrg if (offset < hole->offset) 115b8e80941Smrg continue; 116b8e80941Smrg 117b8e80941Smrg if (offset == hole->offset && size == hole->size) { 118b8e80941Smrg /* Just get rid of the hole. */ 119b8e80941Smrg list_del(&hole->link); 120b8e80941Smrg free(hole); 121b8e80941Smrg util_vma_heap_validate(heap); 122b8e80941Smrg return offset; 123b8e80941Smrg } 124b8e80941Smrg 125b8e80941Smrg assert(offset - hole->offset <= hole->size - size); 126b8e80941Smrg uint64_t waste = (hole->size - size) - (offset - hole->offset); 127b8e80941Smrg if (waste == 0) { 128b8e80941Smrg /* We allocated at the top. Shrink the hole down. */ 129b8e80941Smrg hole->size -= size; 130b8e80941Smrg util_vma_heap_validate(heap); 131b8e80941Smrg return offset; 132b8e80941Smrg } 133b8e80941Smrg 134b8e80941Smrg if (offset == hole->offset) { 135b8e80941Smrg /* We allocated at the bottom. Shrink the hole up. */ 136b8e80941Smrg hole->offset += size; 137b8e80941Smrg hole->size -= size; 138b8e80941Smrg util_vma_heap_validate(heap); 139b8e80941Smrg return offset; 140b8e80941Smrg } 141b8e80941Smrg 142b8e80941Smrg /* We allocated in the middle. We need to split the old hole into two 143b8e80941Smrg * holes, one high and one low. 144b8e80941Smrg */ 145b8e80941Smrg struct util_vma_hole *high_hole = calloc(1, sizeof(*hole)); 146b8e80941Smrg high_hole->offset = offset + size; 147b8e80941Smrg high_hole->size = waste; 148b8e80941Smrg 149b8e80941Smrg /* Adjust the hole to be the amount of space left at he bottom of the 150b8e80941Smrg * original hole. 151b8e80941Smrg */ 152b8e80941Smrg hole->size = offset - hole->offset; 153b8e80941Smrg 154b8e80941Smrg /* Place the new hole before the old hole so that the list is in order 155b8e80941Smrg * from high to low. 156b8e80941Smrg */ 157b8e80941Smrg list_addtail(&high_hole->link, &hole->link); 158b8e80941Smrg 159b8e80941Smrg util_vma_heap_validate(heap); 160b8e80941Smrg 161b8e80941Smrg return offset; 162b8e80941Smrg } 163b8e80941Smrg 164b8e80941Smrg /* Failed to allocate */ 165b8e80941Smrg return 0; 166b8e80941Smrg} 167b8e80941Smrg 168b8e80941Smrgvoid 169b8e80941Smrgutil_vma_heap_free(struct util_vma_heap *heap, 170b8e80941Smrg uint64_t offset, uint64_t size) 171b8e80941Smrg{ 172b8e80941Smrg /* An offset of 0 is reserved for allocation failure. It is not a valid 173b8e80941Smrg * address and cannot be freed. 174b8e80941Smrg */ 175b8e80941Smrg assert(offset > 0); 176b8e80941Smrg 177b8e80941Smrg /* Freeing something with a size of 0 is also not valid. */ 178b8e80941Smrg assert(size > 0); 179b8e80941Smrg 180b8e80941Smrg /* It's possible for offset + size to wrap around if we touch the top of 181b8e80941Smrg * the 64-bit address space, but we cannot go any higher than 2^64. 182b8e80941Smrg */ 183b8e80941Smrg assert(offset + size == 0 || offset + size > offset); 184b8e80941Smrg 185b8e80941Smrg util_vma_heap_validate(heap); 186b8e80941Smrg 187b8e80941Smrg /* Find immediately higher and lower holes if they exist. */ 188b8e80941Smrg struct util_vma_hole *high_hole = NULL, *low_hole = NULL; 189b8e80941Smrg util_vma_foreach_hole(hole, heap) { 190b8e80941Smrg if (hole->offset <= offset) { 191b8e80941Smrg low_hole = hole; 192b8e80941Smrg break; 193b8e80941Smrg } 194b8e80941Smrg high_hole = hole; 195b8e80941Smrg } 196b8e80941Smrg 197b8e80941Smrg if (high_hole) 198b8e80941Smrg assert(offset + size <= high_hole->offset); 199b8e80941Smrg bool high_adjacent = high_hole && offset + size == high_hole->offset; 200b8e80941Smrg 201b8e80941Smrg if (low_hole) { 202b8e80941Smrg assert(low_hole->offset + low_hole->size > low_hole->offset); 203b8e80941Smrg assert(low_hole->offset + low_hole->size <= offset); 204b8e80941Smrg } 205b8e80941Smrg bool low_adjacent = low_hole && low_hole->offset + low_hole->size == offset; 206b8e80941Smrg 207b8e80941Smrg if (low_adjacent && high_adjacent) { 208b8e80941Smrg /* Merge the two holes */ 209b8e80941Smrg low_hole->size += size + high_hole->size; 210b8e80941Smrg list_del(&high_hole->link); 211b8e80941Smrg free(high_hole); 212b8e80941Smrg } else if (low_adjacent) { 213b8e80941Smrg /* Merge into the low hole */ 214b8e80941Smrg low_hole->size += size; 215b8e80941Smrg } else if (high_adjacent) { 216b8e80941Smrg /* Merge into the high hole */ 217b8e80941Smrg high_hole->offset = offset; 218b8e80941Smrg high_hole->size += size; 219b8e80941Smrg } else { 220b8e80941Smrg /* Neither hole is adjacent; make a new one */ 221b8e80941Smrg struct util_vma_hole *hole = calloc(1, sizeof(*hole)); 222b8e80941Smrg 223b8e80941Smrg hole->offset = offset; 224b8e80941Smrg hole->size = size; 225b8e80941Smrg 226b8e80941Smrg /* Add it after the high hole so we maintain high-to-low ordering */ 227b8e80941Smrg if (high_hole) 228b8e80941Smrg list_add(&hole->link, &high_hole->link); 229b8e80941Smrg else 230b8e80941Smrg list_add(&hole->link, &heap->holes); 231b8e80941Smrg } 232b8e80941Smrg 233b8e80941Smrg util_vma_heap_validate(heap); 234b8e80941Smrg} 235