xref: /aosp_15_r20/external/mesa3d/src/util/u_idalloc.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /**************************************************************************
2  *
3  * Copyright 2017 Valve Corporation
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21  * IN NO EVENT SHALL THE AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR
22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  **************************************************************************/
27 
28 /**
29  * @file
30  * A simple allocator that allocates and release "numbers".
31  *
32  * @author Samuel Pitoiset <[email protected]>
33  */
34 
35 #include "util/u_idalloc.h"
36 #include "util/u_math.h"
37 #include <stdlib.h>
38 
39 ASSERTED static bool
util_idalloc_exists(struct util_idalloc * buf,unsigned id)40 util_idalloc_exists(struct util_idalloc *buf, unsigned id)
41 {
42    return id / 32 < buf->num_set_elements &&
43           buf->data[id / 32] & BITFIELD_BIT(id % 32);
44 }
45 
46 static void
util_idalloc_resize(struct util_idalloc * buf,unsigned new_num_elements)47 util_idalloc_resize(struct util_idalloc *buf, unsigned new_num_elements)
48 {
49    if (new_num_elements > buf->num_elements) {
50       buf->data = realloc(buf->data, new_num_elements * sizeof(*buf->data));
51       memset(&buf->data[buf->num_elements], 0,
52              (new_num_elements - buf->num_elements) * sizeof(*buf->data));
53       buf->num_elements = new_num_elements;
54    }
55 }
56 
57 void
util_idalloc_init(struct util_idalloc * buf,unsigned initial_num_ids)58 util_idalloc_init(struct util_idalloc *buf, unsigned initial_num_ids)
59 {
60    memset(buf, 0, sizeof(*buf));
61    assert(initial_num_ids);
62    util_idalloc_resize(buf, DIV_ROUND_UP(initial_num_ids, 32));
63 }
64 
65 void
util_idalloc_fini(struct util_idalloc * buf)66 util_idalloc_fini(struct util_idalloc *buf)
67 {
68    if (buf->data)
69       free(buf->data);
70 }
71 
72 unsigned
util_idalloc_alloc(struct util_idalloc * buf)73 util_idalloc_alloc(struct util_idalloc *buf)
74 {
75    unsigned num_elements = buf->num_elements;
76 
77    for (unsigned i = buf->lowest_free_idx; i < num_elements; i++) {
78       if (buf->data[i] == 0xffffffff)
79          continue;
80 
81       unsigned bit = ffs(~buf->data[i]) - 1;
82       buf->data[i] |= 1u << bit;
83       buf->lowest_free_idx = i;
84       buf->num_set_elements = MAX2(buf->num_set_elements, i + 1);
85       return i * 32 + bit;
86    }
87 
88    /* No slots available, resize and return the first free. */
89    util_idalloc_resize(buf, MAX2(num_elements, 1) * 2);
90 
91    buf->lowest_free_idx = num_elements;
92    buf->data[num_elements] |= 1;
93    buf->num_set_elements = MAX2(buf->num_set_elements, num_elements + 1);
94    return num_elements * 32;
95 }
96 
97 static unsigned
find_free_block(struct util_idalloc * buf,unsigned start)98 find_free_block(struct util_idalloc *buf, unsigned start)
99 {
100    for (unsigned i = start; i < buf->num_elements; i++) {
101       if (!buf->data[i])
102          return i;
103    }
104    return buf->num_elements;
105 }
106 
107 /* Allocate a range of consecutive IDs. Return the first ID. */
108 unsigned
util_idalloc_alloc_range(struct util_idalloc * buf,unsigned num)109 util_idalloc_alloc_range(struct util_idalloc *buf, unsigned num)
110 {
111    if (num == 1)
112       return util_idalloc_alloc(buf);
113 
114    unsigned num_alloc = DIV_ROUND_UP(num, 32);
115    unsigned num_elements = buf->num_elements;
116    unsigned base = find_free_block(buf, buf->lowest_free_idx);
117 
118    while (1) {
119       unsigned i;
120       for (i = base;
121            i < num_elements && i - base < num_alloc && !buf->data[i]; i++);
122 
123       if (i - base == num_alloc)
124          goto ret; /* found */
125 
126       if (i == num_elements)
127          break; /* not found */
128 
129       /* continue searching */
130       base = !buf->data[i] ? i : i + 1;
131    }
132 
133    /* No slots available, allocate more. */
134    util_idalloc_resize(buf, num_elements * 2 + num_alloc);
135 
136 ret:
137    /* Mark the bits as used. */
138    for (unsigned i = base; i < base + num_alloc - (num % 32 != 0); i++)
139       buf->data[i] = 0xffffffff;
140    if (num % 32 != 0)
141       buf->data[base + num_alloc - 1] |= BITFIELD_MASK(num % 32);
142 
143    if (buf->lowest_free_idx == base)
144       buf->lowest_free_idx = base + num / 32;
145 
146    buf->num_set_elements = MAX2(buf->num_set_elements, base + num_alloc);
147 
148    /* Validate this algorithm. */
149    for (unsigned i = 0; i < num; i++)
150       assert(util_idalloc_exists(buf, base * 32 + i));
151 
152    return base * 32;
153 }
154 
155 void
util_idalloc_free(struct util_idalloc * buf,unsigned id)156 util_idalloc_free(struct util_idalloc *buf, unsigned id)
157 {
158    unsigned idx = id / 32;
159 
160    if (idx >= buf->num_elements)
161        return;
162 
163    buf->lowest_free_idx = MIN2(idx, buf->lowest_free_idx);
164    buf->data[idx] &= ~(1u << (id % 32));
165 
166    /* Decrease num_used to the last used element + 1. */
167    if (buf->num_set_elements == idx + 1) {
168       while (buf->num_set_elements > 0 && !buf->data[buf->num_set_elements - 1])
169          buf->num_set_elements--;
170    }
171 }
172 
173 void
util_idalloc_reserve(struct util_idalloc * buf,unsigned id)174 util_idalloc_reserve(struct util_idalloc *buf, unsigned id)
175 {
176    unsigned idx = id / 32;
177 
178    if (idx >= buf->num_elements)
179       util_idalloc_resize(buf, (idx + 1) * 2);
180    buf->data[idx] |= 1u << (id % 32);
181    buf->num_set_elements = MAX2(buf->num_set_elements, idx + 1);
182 }
183 
184 /*********************************************
185  * util_idalloc_mt
186  *********************************************/
187 
188 void
util_idalloc_mt_init(struct util_idalloc_mt * buf,unsigned initial_num_ids,bool skip_zero)189 util_idalloc_mt_init(struct util_idalloc_mt *buf,
190                      unsigned initial_num_ids, bool skip_zero)
191 {
192    simple_mtx_init(&buf->mutex, mtx_plain);
193    util_idalloc_init(&buf->buf, initial_num_ids);
194    buf->skip_zero = skip_zero;
195 
196    if (skip_zero) {
197       ASSERTED unsigned zero = util_idalloc_alloc(&buf->buf);
198       assert(zero == 0);
199    }
200 }
201 
202 /* Callback for drivers using u_threaded_context (abbreviated as tc). */
203 void
util_idalloc_mt_init_tc(struct util_idalloc_mt * buf)204 util_idalloc_mt_init_tc(struct util_idalloc_mt *buf)
205 {
206    util_idalloc_mt_init(buf, 1 << 16, true);
207 }
208 
209 void
util_idalloc_mt_fini(struct util_idalloc_mt * buf)210 util_idalloc_mt_fini(struct util_idalloc_mt *buf)
211 {
212    util_idalloc_fini(&buf->buf);
213    simple_mtx_destroy(&buf->mutex);
214 }
215 
216 unsigned
util_idalloc_mt_alloc(struct util_idalloc_mt * buf)217 util_idalloc_mt_alloc(struct util_idalloc_mt *buf)
218 {
219    simple_mtx_lock(&buf->mutex);
220    unsigned id = util_idalloc_alloc(&buf->buf);
221    simple_mtx_unlock(&buf->mutex);
222    return id;
223 }
224 
225 void
util_idalloc_mt_free(struct util_idalloc_mt * buf,unsigned id)226 util_idalloc_mt_free(struct util_idalloc_mt *buf, unsigned id)
227 {
228    if (id == 0 && buf->skip_zero)
229       return;
230 
231    simple_mtx_lock(&buf->mutex);
232    util_idalloc_free(&buf->buf, id);
233    simple_mtx_unlock(&buf->mutex);
234 }
235 
236 /*********************************************
237  * util_idalloc_sparse
238  *********************************************/
239 
240 void
util_idalloc_sparse_init(struct util_idalloc_sparse * buf)241 util_idalloc_sparse_init(struct util_idalloc_sparse *buf)
242 {
243    static_assert(IS_POT_NONZERO(ARRAY_SIZE(buf->segment)),
244          "buf->segment[] must have a power of two number of elements");
245 
246    for (unsigned i = 0; i < ARRAY_SIZE(buf->segment); i++)
247       util_idalloc_init(&buf->segment[i], 1);
248 }
249 
250 void
util_idalloc_sparse_fini(struct util_idalloc_sparse * buf)251 util_idalloc_sparse_fini(struct util_idalloc_sparse *buf)
252 {
253    for (unsigned i = 0; i < ARRAY_SIZE(buf->segment); i++)
254       util_idalloc_fini(&buf->segment[i]);
255 }
256 
257 unsigned
util_idalloc_sparse_alloc(struct util_idalloc_sparse * buf)258 util_idalloc_sparse_alloc(struct util_idalloc_sparse *buf)
259 {
260    unsigned max_ids = UTIL_IDALLOC_MAX_IDS_PER_SEGMENT(buf);
261 
262    for (unsigned i = 0; i < ARRAY_SIZE(buf->segment); i++) {
263       if (buf->segment[i].lowest_free_idx <
264           UTIL_IDALLOC_MAX_ELEMS_PER_SEGMENT(buf))
265          return max_ids * i + util_idalloc_alloc(&buf->segment[i]);
266    }
267 
268    fprintf(stderr, "mesa: util_idalloc_sparse_alloc: "
269                    "all 2^32 IDs are used, this shouldn't happen\n");
270    assert(0);
271    return 0;
272 }
273 
274 unsigned
util_idalloc_sparse_alloc_range(struct util_idalloc_sparse * buf,unsigned num)275 util_idalloc_sparse_alloc_range(struct util_idalloc_sparse *buf, unsigned num)
276 {
277    unsigned max_ids = UTIL_IDALLOC_MAX_IDS_PER_SEGMENT(buf);
278    unsigned num_elems = DIV_ROUND_UP(num, 32);
279 
280    /* TODO: This doesn't try to find a range that spans 2 different segments */
281    for (unsigned i = 0; i < ARRAY_SIZE(buf->segment); i++) {
282       if (buf->segment[i].lowest_free_idx + num_elems <=
283           UTIL_IDALLOC_MAX_ELEMS_PER_SEGMENT(buf)) {
284          unsigned base = util_idalloc_alloc_range(&buf->segment[i], num);
285 
286          if (base + num <= max_ids)
287             return max_ids * i + base;
288 
289          /* Back off the allocation and try again with the next segment. */
290          for (unsigned i = 0; i < num; i++)
291             util_idalloc_free(&buf->segment[i], base + i);
292       }
293    }
294 
295    fprintf(stderr, "mesa: util_idalloc_sparse_alloc_range: can't find a free consecutive range of IDs\n");
296    assert(0);
297    return 0;
298 }
299 
300 void
util_idalloc_sparse_free(struct util_idalloc_sparse * buf,unsigned id)301 util_idalloc_sparse_free(struct util_idalloc_sparse *buf, unsigned id)
302 {
303    unsigned max_ids = UTIL_IDALLOC_MAX_IDS_PER_SEGMENT(buf);
304    util_idalloc_free(&buf->segment[id / max_ids], id % max_ids);
305 }
306 
307 void
util_idalloc_sparse_reserve(struct util_idalloc_sparse * buf,unsigned id)308 util_idalloc_sparse_reserve(struct util_idalloc_sparse *buf, unsigned id)
309 {
310    unsigned max_ids = UTIL_IDALLOC_MAX_IDS_PER_SEGMENT(buf);
311    util_idalloc_reserve(&buf->segment[id / max_ids], id % max_ids);
312 }
313