xref: /third_party/mesa3d/src/util/slab.c (revision bf215546)
1/*
2 * Copyright 2010 Marek Olšák <maraeo@gmail.com>
3 * Copyright 2016 Advanced Micro Devices, Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * on the rights to use, copy, modify, merge, publish, distribute, sub
9 * license, and/or sell copies of the Software, and to permit persons to whom
10 * the Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
22 * USE OR OTHER DEALINGS IN THE SOFTWARE. */
23
24#include "slab.h"
25#include "macros.h"
26#include "u_atomic.h"
27#include <stdint.h>
28#include <stdbool.h>
29#include <string.h>
30
31#define SLAB_MAGIC_ALLOCATED 0xcafe4321
32#define SLAB_MAGIC_FREE 0x7ee01234
33
34#ifndef NDEBUG
35#define SET_MAGIC(element, value)   (element)->magic = (value)
36#define CHECK_MAGIC(element, value) assert((element)->magic == (value))
37#else
38#define SET_MAGIC(element, value)
39#define CHECK_MAGIC(element, value)
40#endif
41
42/* One array element within a big buffer. */
43struct slab_element_header {
44   /* The next element in the free or migrated list. */
45   struct slab_element_header *next;
46
47   /* This is either
48    * - a pointer to the child pool to which this element belongs, or
49    * - a pointer to the orphaned page of the element, with the least
50    *   significant bit set to 1.
51    */
52   intptr_t owner;
53
54#ifndef NDEBUG
55   intptr_t magic;
56#endif
57};
58
59/* The page is an array of allocations in one block. */
60struct slab_page_header {
61   union {
62      /* Next page in the same child pool. */
63      struct slab_page_header *next;
64
65      /* Number of remaining, non-freed elements (for orphaned pages). */
66      unsigned num_remaining;
67   } u;
68   /* Memory after the last member is dedicated to the page itself.
69    * The allocated size is always larger than this structure.
70    */
71};
72
73
74static struct slab_element_header *
75slab_get_element(struct slab_parent_pool *parent,
76                 struct slab_page_header *page, unsigned index)
77{
78   return (struct slab_element_header*)
79          ((uint8_t*)&page[1] + (parent->element_size * index));
80}
81
82/* The given object/element belongs to an orphaned page (i.e. the owning child
83 * pool has been destroyed). Mark the element as freed and free the whole page
84 * when no elements are left in it.
85 */
86static void
87slab_free_orphaned(struct slab_element_header *elt)
88{
89   struct slab_page_header *page;
90
91   assert(elt->owner & 1);
92
93   page = (struct slab_page_header *)(elt->owner & ~(intptr_t)1);
94   if (!p_atomic_dec_return(&page->u.num_remaining))
95      free(page);
96}
97
98/**
99 * Create a parent pool for the allocation of same-sized objects.
100 *
101 * \param item_size     Size of one object.
102 * \param num_items     Number of objects to allocate at once.
103 */
104void
105slab_create_parent(struct slab_parent_pool *parent,
106                   unsigned item_size,
107                   unsigned num_items)
108{
109   simple_mtx_init(&parent->mutex, mtx_plain);
110   parent->element_size = ALIGN_POT(sizeof(struct slab_element_header) + item_size,
111                                    sizeof(intptr_t));
112   parent->num_elements = num_items;
113   parent->item_size = item_size;
114}
115
116void
117slab_destroy_parent(struct slab_parent_pool *parent)
118{
119   simple_mtx_destroy(&parent->mutex);
120}
121
122/**
123 * Create a child pool linked to the given parent.
124 */
125void slab_create_child(struct slab_child_pool *pool,
126                       struct slab_parent_pool *parent)
127{
128   pool->parent = parent;
129   pool->pages = NULL;
130   pool->free = NULL;
131   pool->migrated = NULL;
132}
133
134/**
135 * Destroy the child pool.
136 *
137 * Pages associated to the pool will be orphaned. They are eventually freed
138 * when all objects in them are freed.
139 */
140void slab_destroy_child(struct slab_child_pool *pool)
141{
142   if (!pool->parent)
143      return; /* the slab probably wasn't even created */
144
145   simple_mtx_lock(&pool->parent->mutex);
146
147   while (pool->pages) {
148      struct slab_page_header *page = pool->pages;
149      pool->pages = page->u.next;
150      p_atomic_set(&page->u.num_remaining, pool->parent->num_elements);
151
152      for (unsigned i = 0; i < pool->parent->num_elements; ++i) {
153         struct slab_element_header *elt = slab_get_element(pool->parent, page, i);
154         p_atomic_set(&elt->owner, (intptr_t)page | 1);
155      }
156   }
157
158   while (pool->migrated) {
159      struct slab_element_header *elt = pool->migrated;
160      pool->migrated = elt->next;
161      slab_free_orphaned(elt);
162   }
163
164   simple_mtx_unlock(&pool->parent->mutex);
165
166   while (pool->free) {
167      struct slab_element_header *elt = pool->free;
168      pool->free = elt->next;
169      slab_free_orphaned(elt);
170   }
171
172   /* Guard against use-after-free. */
173   pool->parent = NULL;
174}
175
176static bool
177slab_add_new_page(struct slab_child_pool *pool)
178{
179   struct slab_page_header *page = malloc(sizeof(struct slab_page_header) +
180      pool->parent->num_elements * pool->parent->element_size);
181
182   if (!page)
183      return false;
184
185   for (unsigned i = 0; i < pool->parent->num_elements; ++i) {
186      struct slab_element_header *elt = slab_get_element(pool->parent, page, i);
187      elt->owner = (intptr_t)pool;
188      assert(!(elt->owner & 1));
189
190      elt->next = pool->free;
191      pool->free = elt;
192      SET_MAGIC(elt, SLAB_MAGIC_FREE);
193   }
194
195   page->u.next = pool->pages;
196   pool->pages = page;
197
198   return true;
199}
200
201/**
202 * Allocate an object from the child pool. Single-threaded (i.e. the caller
203 * must ensure that no operation happens on the same child pool in another
204 * thread).
205 */
206void *
207slab_alloc(struct slab_child_pool *pool)
208{
209   struct slab_element_header *elt;
210
211   if (!pool->free) {
212      /* First, collect elements that belong to us but were freed from a
213       * different child pool.
214       */
215      simple_mtx_lock(&pool->parent->mutex);
216      pool->free = pool->migrated;
217      pool->migrated = NULL;
218      simple_mtx_unlock(&pool->parent->mutex);
219
220      /* Now allocate a new page. */
221      if (!pool->free && !slab_add_new_page(pool))
222         return NULL;
223   }
224
225   elt = pool->free;
226   pool->free = elt->next;
227
228   CHECK_MAGIC(elt, SLAB_MAGIC_FREE);
229   SET_MAGIC(elt, SLAB_MAGIC_ALLOCATED);
230
231   return &elt[1];
232}
233
234/**
235 * Same as slab_alloc but memset the returned object to 0.
236 */
237void *
238slab_zalloc(struct slab_child_pool *pool)
239{
240   void *r = slab_alloc(pool);
241   if (r)
242      memset(r, 0, pool->parent->item_size);
243   return r;
244}
245
246/**
247 * Free an object allocated from the slab. Single-threaded (i.e. the caller
248 * must ensure that no operation happens on the same child pool in another
249 * thread).
250 *
251 * Freeing an object in a different child pool from the one where it was
252 * allocated is allowed, as long the pool belong to the same parent. No
253 * additional locking is required in this case.
254 */
255void slab_free(struct slab_child_pool *pool, void *ptr)
256{
257   struct slab_element_header *elt = ((struct slab_element_header*)ptr - 1);
258   intptr_t owner_int;
259
260   CHECK_MAGIC(elt, SLAB_MAGIC_ALLOCATED);
261   SET_MAGIC(elt, SLAB_MAGIC_FREE);
262
263   if (p_atomic_read(&elt->owner) == (intptr_t)pool) {
264      /* This is the simple case: The caller guarantees that we can safely
265       * access the free list.
266       */
267      elt->next = pool->free;
268      pool->free = elt;
269      return;
270   }
271
272   /* The slow case: migration or an orphaned page. */
273   if (pool->parent)
274      simple_mtx_lock(&pool->parent->mutex);
275
276   /* Note: we _must_ re-read elt->owner here because the owning child pool
277    * may have been destroyed by another thread in the meantime.
278    */
279   owner_int = p_atomic_read(&elt->owner);
280
281   if (!(owner_int & 1)) {
282      struct slab_child_pool *owner = (struct slab_child_pool *)owner_int;
283      elt->next = owner->migrated;
284      owner->migrated = elt;
285      if (pool->parent)
286         simple_mtx_unlock(&pool->parent->mutex);
287   } else {
288      if (pool->parent)
289         simple_mtx_unlock(&pool->parent->mutex);
290
291      slab_free_orphaned(elt);
292   }
293}
294
295/**
296 * Allocate an object from the slab. Single-threaded (no mutex).
297 */
298void *
299slab_alloc_st(struct slab_mempool *mempool)
300{
301   return slab_alloc(&mempool->child);
302}
303
304/**
305 * Free an object allocated from the slab. Single-threaded (no mutex).
306 */
307void
308slab_free_st(struct slab_mempool *mempool, void *ptr)
309{
310   slab_free(&mempool->child, ptr);
311}
312
313void
314slab_destroy(struct slab_mempool *mempool)
315{
316   slab_destroy_child(&mempool->child);
317   slab_destroy_parent(&mempool->parent);
318}
319
320/**
321 * Create an allocator for same-sized objects.
322 *
323 * \param item_size     Size of one object.
324 * \param num_items     Number of objects to allocate at once.
325 */
326void
327slab_create(struct slab_mempool *mempool,
328            unsigned item_size,
329            unsigned num_items)
330{
331   slab_create_parent(&mempool->parent, item_size, num_items);
332   slab_create_child(&mempool->child, &mempool->parent);
333}
334