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