Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright 2010 Marek Olk <maraeo (at) 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 ALIGN(value, align) (((value) + (align) - 1) & ~((align) - 1))
     32 
     33 #define SLAB_MAGIC_ALLOCATED 0xcafe4321
     34 #define SLAB_MAGIC_FREE 0x7ee01234
     35 
     36 #ifdef DEBUG
     37 #define SET_MAGIC(element, value)   (element)->magic = (value)
     38 #define CHECK_MAGIC(element, value) assert((element)->magic == (value))
     39 #else
     40 #define SET_MAGIC(element, value)
     41 #define CHECK_MAGIC(element, value)
     42 #endif
     43 
     44 /* One array element within a big buffer. */
     45 struct slab_element_header {
     46    /* The next element in the free or migrated list. */
     47    struct slab_element_header *next;
     48 
     49    /* This is either
     50     * - a pointer to the child pool to which this element belongs, or
     51     * - a pointer to the orphaned page of the element, with the least
     52     *   significant bit set to 1.
     53     */
     54    intptr_t owner;
     55 
     56 #ifdef DEBUG
     57    intptr_t magic;
     58 #endif
     59 };
     60 
     61 /* The page is an array of allocations in one block. */
     62 struct slab_page_header {
     63    union {
     64       /* Next page in the same child pool. */
     65       struct slab_page_header *next;
     66 
     67       /* Number of remaining, non-freed elements (for orphaned pages). */
     68       unsigned num_remaining;
     69    } u;
     70    /* Memory after the last member is dedicated to the page itself.
     71     * The allocated size is always larger than this structure.
     72     */
     73 };
     74 
     75 
     76 static struct slab_element_header *
     77 slab_get_element(struct slab_parent_pool *parent,
     78                  struct slab_page_header *page, unsigned index)
     79 {
     80    return (struct slab_element_header*)
     81           ((uint8_t*)&page[1] + (parent->element_size * index));
     82 }
     83 
     84 /* The given object/element belongs to an orphaned page (i.e. the owning child
     85  * pool has been destroyed). Mark the element as freed and free the whole page
     86  * when no elements are left in it.
     87  */
     88 static void
     89 slab_free_orphaned(struct slab_element_header *elt)
     90 {
     91    struct slab_page_header *page;
     92 
     93    assert(elt->owner & 1);
     94 
     95    page = (struct slab_page_header *)(elt->owner & ~(intptr_t)1);
     96    if (!p_atomic_dec_return(&page->u.num_remaining))
     97       free(page);
     98 }
     99 
    100 /**
    101  * Create a parent pool for the allocation of same-sized objects.
    102  *
    103  * \param item_size     Size of one object.
    104  * \param num_items     Number of objects to allocate at once.
    105  */
    106 void
    107 slab_create_parent(struct slab_parent_pool *parent,
    108                    unsigned item_size,
    109                    unsigned num_items)
    110 {
    111    mtx_init(&parent->mutex, mtx_plain);
    112    parent->element_size = ALIGN(sizeof(struct slab_element_header) + item_size,
    113                                 sizeof(intptr_t));
    114    parent->num_elements = num_items;
    115 }
    116 
    117 void
    118 slab_destroy_parent(struct slab_parent_pool *parent)
    119 {
    120    mtx_destroy(&parent->mutex);
    121 }
    122 
    123 /**
    124  * Create a child pool linked to the given parent.
    125  */
    126 void slab_create_child(struct slab_child_pool *pool,
    127                        struct slab_parent_pool *parent)
    128 {
    129    pool->parent = parent;
    130    pool->pages = NULL;
    131    pool->free = NULL;
    132    pool->migrated = NULL;
    133 }
    134 
    135 /**
    136  * Destroy the child pool.
    137  *
    138  * Pages associated to the pool will be orphaned. They are eventually freed
    139  * when all objects in them are freed.
    140  */
    141 void slab_destroy_child(struct slab_child_pool *pool)
    142 {
    143    if (!pool->parent)
    144       return; /* the slab probably wasn't even created */
    145 
    146    mtx_lock(&pool->parent->mutex);
    147 
    148    while (pool->pages) {
    149       struct slab_page_header *page = pool->pages;
    150       pool->pages = page->u.next;
    151       p_atomic_set(&page->u.num_remaining, pool->parent->num_elements);
    152 
    153       for (unsigned i = 0; i < pool->parent->num_elements; ++i) {
    154          struct slab_element_header *elt = slab_get_element(pool->parent, page, i);
    155          p_atomic_set(&elt->owner, (intptr_t)page | 1);
    156       }
    157    }
    158 
    159    while (pool->migrated) {
    160       struct slab_element_header *elt = pool->migrated;
    161       pool->migrated = elt->next;
    162       slab_free_orphaned(elt);
    163    }
    164 
    165    mtx_unlock(&pool->parent->mutex);
    166 
    167    while (pool->free) {
    168       struct slab_element_header *elt = pool->free;
    169       pool->free = elt->next;
    170       slab_free_orphaned(elt);
    171    }
    172 
    173    /* Guard against use-after-free. */
    174    pool->parent = NULL;
    175 }
    176 
    177 static bool
    178 slab_add_new_page(struct slab_child_pool *pool)
    179 {
    180    struct slab_page_header *page = malloc(sizeof(struct slab_page_header) +
    181       pool->parent->num_elements * pool->parent->element_size);
    182 
    183    if (!page)
    184       return false;
    185 
    186    for (unsigned i = 0; i < pool->parent->num_elements; ++i) {
    187       struct slab_element_header *elt = slab_get_element(pool->parent, page, i);
    188       elt->owner = (intptr_t)pool;
    189       assert(!(elt->owner & 1));
    190 
    191       elt->next = pool->free;
    192       pool->free = elt;
    193       SET_MAGIC(elt, SLAB_MAGIC_FREE);
    194    }
    195 
    196    page->u.next = pool->pages;
    197    pool->pages = page;
    198 
    199    return true;
    200 }
    201 
    202 /**
    203  * Allocate an object from the child pool. Single-threaded (i.e. the caller
    204  * must ensure that no operation happens on the same child pool in another
    205  * thread).
    206  */
    207 void *
    208 slab_alloc(struct slab_child_pool *pool)
    209 {
    210    struct slab_element_header *elt;
    211 
    212    if (!pool->free) {
    213       /* First, collect elements that belong to us but were freed from a
    214        * different child pool.
    215        */
    216       mtx_lock(&pool->parent->mutex);
    217       pool->free = pool->migrated;
    218       pool->migrated = NULL;
    219       mtx_unlock(&pool->parent->mutex);
    220 
    221       /* Now allocate a new page. */
    222       if (!pool->free && !slab_add_new_page(pool))
    223          return NULL;
    224    }
    225 
    226    elt = pool->free;
    227    pool->free = elt->next;
    228 
    229    CHECK_MAGIC(elt, SLAB_MAGIC_FREE);
    230    SET_MAGIC(elt, SLAB_MAGIC_ALLOCATED);
    231 
    232    return &elt[1];
    233 }
    234 
    235 /**
    236  * Free an object allocated from the slab. Single-threaded (i.e. the caller
    237  * must ensure that no operation happens on the same child pool in another
    238  * thread).
    239  *
    240  * Freeing an object in a different child pool from the one where it was
    241  * allocated is allowed, as long the pool belong to the same parent. No
    242  * additional locking is required in this case.
    243  */
    244 void slab_free(struct slab_child_pool *pool, void *ptr)
    245 {
    246    struct slab_element_header *elt = ((struct slab_element_header*)ptr - 1);
    247    intptr_t owner_int;
    248 
    249    CHECK_MAGIC(elt, SLAB_MAGIC_ALLOCATED);
    250    SET_MAGIC(elt, SLAB_MAGIC_FREE);
    251 
    252    if (p_atomic_read(&elt->owner) == (intptr_t)pool) {
    253       /* This is the simple case: The caller guarantees that we can safely
    254        * access the free list.
    255        */
    256       elt->next = pool->free;
    257       pool->free = elt;
    258       return;
    259    }
    260 
    261    /* The slow case: migration or an orphaned page. */
    262    mtx_lock(&pool->parent->mutex);
    263 
    264    /* Note: we _must_ re-read elt->owner here because the owning child pool
    265     * may have been destroyed by another thread in the meantime.
    266     */
    267    owner_int = p_atomic_read(&elt->owner);
    268 
    269    if (!(owner_int & 1)) {
    270       struct slab_child_pool *owner = (struct slab_child_pool *)owner_int;
    271       elt->next = owner->migrated;
    272       owner->migrated = elt;
    273       mtx_unlock(&pool->parent->mutex);
    274    } else {
    275       mtx_unlock(&pool->parent->mutex);
    276 
    277       slab_free_orphaned(elt);
    278    }
    279 }
    280 
    281 /**
    282  * Allocate an object from the slab. Single-threaded (no mutex).
    283  */
    284 void *
    285 slab_alloc_st(struct slab_mempool *pool)
    286 {
    287    return slab_alloc(&pool->child);
    288 }
    289 
    290 /**
    291  * Free an object allocated from the slab. Single-threaded (no mutex).
    292  */
    293 void
    294 slab_free_st(struct slab_mempool *pool, void *ptr)
    295 {
    296    slab_free(&pool->child, ptr);
    297 }
    298 
    299 void
    300 slab_destroy(struct slab_mempool *pool)
    301 {
    302    slab_destroy_child(&pool->child);
    303    slab_destroy_parent(&pool->parent);
    304 }
    305 
    306 /**
    307  * Create an allocator for same-sized objects.
    308  *
    309  * \param item_size     Size of one object.
    310  * \param num_items     Number of objects to allocate at once.
    311  */
    312 void
    313 slab_create(struct slab_mempool *pool,
    314             unsigned item_size,
    315             unsigned num_items)
    316 {
    317    slab_create_parent(&pool->parent, item_size, num_items);
    318    slab_create_child(&pool->child, &pool->parent);
    319 }
    320