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