diff --git a/src/util/vma.c b/src/util/vma.c index 714ef01b211d7c7b90ec3c3b54b22df524f73047..f17ec0c08f1211e46a3220138247a19e678a558e 100644 --- a/src/util/vma.c +++ b/src/util/vma.c @@ -38,12 +38,18 @@ struct util_vma_hole { #define util_vma_foreach_hole_safe(_hole, _heap) \ list_for_each_entry_safe(struct util_vma_hole, _hole, &(_heap)->holes, link) +#define util_vma_foreach_hole_safe_rev(_hole, _heap) \ + list_for_each_entry_safe_rev(struct util_vma_hole, _hole, &(_heap)->holes, link) + void util_vma_heap_init(struct util_vma_heap *heap, uint64_t start, uint64_t size) { list_inithead(&heap->holes); util_vma_heap_free(heap, start, size); + + /* Default to using high addresses */ + heap->alloc_high = true; } void @@ -141,29 +147,52 @@ util_vma_heap_alloc(struct util_vma_heap *heap, util_vma_heap_validate(heap); - util_vma_foreach_hole_safe(hole, heap) { - if (size > hole->size) - continue; + if (heap->alloc_high) { + util_vma_foreach_hole_safe(hole, heap) { + if (size > hole->size) + continue; - /* Compute the offset as the highest address where a chunk of the given - * size can be without going over the top of the hole. - * - * This calculation is known to not overflow because we know that - * hole->size + hole->offset can only overflow to 0 and size > 0. - */ - uint64_t offset = (hole->size - size) + hole->offset; + /* Compute the offset as the highest address where a chunk of the + * given size can be without going over the top of the hole. + * + * This calculation is known to not overflow because we know that + * hole->size + hole->offset can only overflow to 0 and size > 0. + */ + uint64_t offset = (hole->size - size) + hole->offset; - /* Align the offset. We align down and not up because we are allocating - * from the top of the hole and not the bottom. - */ - offset = (offset / alignment) * alignment; + /* Align the offset. We align down and not up because we are + * allocating from the top of the hole and not the bottom. + */ + offset = (offset / alignment) * alignment; - if (offset < hole->offset) - continue; + if (offset < hole->offset) + continue; - util_vma_hole_alloc(hole, offset, size); - util_vma_heap_validate(heap); - return offset; + util_vma_hole_alloc(hole, offset, size); + util_vma_heap_validate(heap); + return offset; + } + } else { + util_vma_foreach_hole_safe_rev(hole, heap) { + if (size > hole->size) + continue; + + uint64_t offset = hole->offset; + + /* Align the offset */ + uint64_t misalign = offset % alignment; + if (misalign) { + uint64_t pad = alignment - misalign; + if (pad > hole->size - size) + continue; + + offset += pad; + } + + util_vma_hole_alloc(hole, offset, size); + util_vma_heap_validate(heap); + return offset; + } } /* Failed to allocate */ diff --git a/src/util/vma.h b/src/util/vma.h index 91c7ee6e66a40fc76cd49e337a0de0841c986145..58b4f8bf94115ec8214e1991136a929f8d4482fa 100644 --- a/src/util/vma.h +++ b/src/util/vma.h @@ -34,6 +34,12 @@ extern "C" { struct util_vma_heap { struct list_head holes; + + /** If true, util_vma_heap_alloc will prefer high addresses + * + * Default is true. + */ + bool alloc_high; }; void util_vma_heap_init(struct util_vma_heap *heap,