1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright 2016 Advanced Micro Devices, Inc. 3bf215546Sopenharmony_ci * All Rights Reserved. 4bf215546Sopenharmony_ci * 5bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining 6bf215546Sopenharmony_ci * a copy of this software and associated documentation files (the 7bf215546Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 8bf215546Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 9bf215546Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 10bf215546Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 11bf215546Sopenharmony_ci * the following conditions: 12bf215546Sopenharmony_ci * 13bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the 14bf215546Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 15bf215546Sopenharmony_ci * of the Software. 16bf215546Sopenharmony_ci * 17bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18bf215546Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 19bf215546Sopenharmony_ci * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 20bf215546Sopenharmony_ci * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS 21bf215546Sopenharmony_ci * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23bf215546Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 24bf215546Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 25bf215546Sopenharmony_ci * 26bf215546Sopenharmony_ci */ 27bf215546Sopenharmony_ci 28bf215546Sopenharmony_ci#include "pb_slab.h" 29bf215546Sopenharmony_ci 30bf215546Sopenharmony_ci#include "util/u_math.h" 31bf215546Sopenharmony_ci#include "util/u_memory.h" 32bf215546Sopenharmony_ci 33bf215546Sopenharmony_ci/* All slab allocations from the same heap and with the same size belong 34bf215546Sopenharmony_ci * to the same group. 35bf215546Sopenharmony_ci */ 36bf215546Sopenharmony_cistruct pb_slab_group 37bf215546Sopenharmony_ci{ 38bf215546Sopenharmony_ci /* Slabs with allocation candidates. Typically, slabs in this list should 39bf215546Sopenharmony_ci * have some free entries. 40bf215546Sopenharmony_ci * 41bf215546Sopenharmony_ci * However, when the head becomes full we purposefully keep it around 42bf215546Sopenharmony_ci * until the next allocation attempt, at which time we try a reclaim. 43bf215546Sopenharmony_ci * The intention is to keep serving allocations from the same slab as long 44bf215546Sopenharmony_ci * as possible for better locality. 45bf215546Sopenharmony_ci * 46bf215546Sopenharmony_ci * Due to a race in new slab allocation, additional slabs in this list 47bf215546Sopenharmony_ci * can be fully allocated as well. 48bf215546Sopenharmony_ci */ 49bf215546Sopenharmony_ci struct list_head slabs; 50bf215546Sopenharmony_ci}; 51bf215546Sopenharmony_ci 52bf215546Sopenharmony_ci 53bf215546Sopenharmony_cistatic void 54bf215546Sopenharmony_cipb_slab_reclaim(struct pb_slabs *slabs, struct pb_slab_entry *entry) 55bf215546Sopenharmony_ci{ 56bf215546Sopenharmony_ci struct pb_slab *slab = entry->slab; 57bf215546Sopenharmony_ci 58bf215546Sopenharmony_ci list_del(&entry->head); /* remove from reclaim list */ 59bf215546Sopenharmony_ci list_add(&entry->head, &slab->free); 60bf215546Sopenharmony_ci slab->num_free++; 61bf215546Sopenharmony_ci 62bf215546Sopenharmony_ci /* Add slab to the group's list if it isn't already linked. */ 63bf215546Sopenharmony_ci if (!list_is_linked(&slab->head)) { 64bf215546Sopenharmony_ci struct pb_slab_group *group = &slabs->groups[entry->group_index]; 65bf215546Sopenharmony_ci list_addtail(&slab->head, &group->slabs); 66bf215546Sopenharmony_ci } 67bf215546Sopenharmony_ci 68bf215546Sopenharmony_ci if (slab->num_free >= slab->num_entries) { 69bf215546Sopenharmony_ci list_del(&slab->head); 70bf215546Sopenharmony_ci slabs->slab_free(slabs->priv, slab); 71bf215546Sopenharmony_ci } 72bf215546Sopenharmony_ci} 73bf215546Sopenharmony_ci 74bf215546Sopenharmony_ci#define MAX_FAILED_RECLAIMS 2 75bf215546Sopenharmony_ci 76bf215546Sopenharmony_cistatic void 77bf215546Sopenharmony_cipb_slabs_reclaim_locked(struct pb_slabs *slabs) 78bf215546Sopenharmony_ci{ 79bf215546Sopenharmony_ci struct pb_slab_entry *entry, *next; 80bf215546Sopenharmony_ci unsigned num_failed_reclaims = 0; 81bf215546Sopenharmony_ci LIST_FOR_EACH_ENTRY_SAFE(entry, next, &slabs->reclaim, head) { 82bf215546Sopenharmony_ci if (slabs->can_reclaim(slabs->priv, entry)) { 83bf215546Sopenharmony_ci pb_slab_reclaim(slabs, entry); 84bf215546Sopenharmony_ci /* there are typically three possible scenarios when reclaiming: 85bf215546Sopenharmony_ci * - all entries reclaimed 86bf215546Sopenharmony_ci * - no entries reclaimed 87bf215546Sopenharmony_ci * - all but one entry reclaimed 88bf215546Sopenharmony_ci * in the scenario where a slab contains many (10+) unused entries, 89bf215546Sopenharmony_ci * the driver should not walk the entire list, as this is likely to 90bf215546Sopenharmony_ci * result in zero reclaims if the first few entries fail to reclaim 91bf215546Sopenharmony_ci */ 92bf215546Sopenharmony_ci } else if (++num_failed_reclaims >= MAX_FAILED_RECLAIMS) { 93bf215546Sopenharmony_ci break; 94bf215546Sopenharmony_ci } 95bf215546Sopenharmony_ci } 96bf215546Sopenharmony_ci} 97bf215546Sopenharmony_ci 98bf215546Sopenharmony_cistatic void 99bf215546Sopenharmony_cipb_slabs_reclaim_all_locked(struct pb_slabs *slabs) 100bf215546Sopenharmony_ci{ 101bf215546Sopenharmony_ci struct pb_slab_entry *entry, *next; 102bf215546Sopenharmony_ci LIST_FOR_EACH_ENTRY_SAFE(entry, next, &slabs->reclaim, head) { 103bf215546Sopenharmony_ci if (slabs->can_reclaim(slabs->priv, entry)) { 104bf215546Sopenharmony_ci pb_slab_reclaim(slabs, entry); 105bf215546Sopenharmony_ci } 106bf215546Sopenharmony_ci } 107bf215546Sopenharmony_ci} 108bf215546Sopenharmony_ci 109bf215546Sopenharmony_ci/* Allocate a slab entry of the given size from the given heap. 110bf215546Sopenharmony_ci * 111bf215546Sopenharmony_ci * This will try to re-use entries that have previously been freed. However, 112bf215546Sopenharmony_ci * if no entries are free (or all free entries are still "in flight" as 113bf215546Sopenharmony_ci * determined by the can_reclaim fallback function), a new slab will be 114bf215546Sopenharmony_ci * requested via the slab_alloc callback. 115bf215546Sopenharmony_ci * 116bf215546Sopenharmony_ci * Note that slab_free can also be called by this function. 117bf215546Sopenharmony_ci */ 118bf215546Sopenharmony_cistruct pb_slab_entry * 119bf215546Sopenharmony_cipb_slab_alloc_reclaimed(struct pb_slabs *slabs, unsigned size, unsigned heap, bool reclaim_all) 120bf215546Sopenharmony_ci{ 121bf215546Sopenharmony_ci unsigned order = MAX2(slabs->min_order, util_logbase2_ceil(size)); 122bf215546Sopenharmony_ci unsigned group_index; 123bf215546Sopenharmony_ci struct pb_slab_group *group; 124bf215546Sopenharmony_ci struct pb_slab *slab; 125bf215546Sopenharmony_ci struct pb_slab_entry *entry; 126bf215546Sopenharmony_ci unsigned entry_size = 1 << order; 127bf215546Sopenharmony_ci bool three_fourths = false; 128bf215546Sopenharmony_ci 129bf215546Sopenharmony_ci /* If the size is <= 3/4 of the entry size, use a slab with entries using 130bf215546Sopenharmony_ci * 3/4 sizes to reduce overallocation. 131bf215546Sopenharmony_ci */ 132bf215546Sopenharmony_ci if (slabs->allow_three_fourths_allocations && size <= entry_size * 3 / 4) { 133bf215546Sopenharmony_ci entry_size = entry_size * 3 / 4; 134bf215546Sopenharmony_ci three_fourths = true; 135bf215546Sopenharmony_ci } 136bf215546Sopenharmony_ci 137bf215546Sopenharmony_ci assert(order < slabs->min_order + slabs->num_orders); 138bf215546Sopenharmony_ci assert(heap < slabs->num_heaps); 139bf215546Sopenharmony_ci 140bf215546Sopenharmony_ci group_index = (heap * slabs->num_orders + (order - slabs->min_order)) * 141bf215546Sopenharmony_ci (1 + slabs->allow_three_fourths_allocations) + three_fourths; 142bf215546Sopenharmony_ci group = &slabs->groups[group_index]; 143bf215546Sopenharmony_ci 144bf215546Sopenharmony_ci simple_mtx_lock(&slabs->mutex); 145bf215546Sopenharmony_ci 146bf215546Sopenharmony_ci /* If there is no candidate slab at all, or the first slab has no free 147bf215546Sopenharmony_ci * entries, try reclaiming entries. 148bf215546Sopenharmony_ci */ 149bf215546Sopenharmony_ci if (list_is_empty(&group->slabs) || 150bf215546Sopenharmony_ci list_is_empty(&list_entry(group->slabs.next, struct pb_slab, head)->free)) { 151bf215546Sopenharmony_ci if (reclaim_all) 152bf215546Sopenharmony_ci pb_slabs_reclaim_all_locked(slabs); 153bf215546Sopenharmony_ci else 154bf215546Sopenharmony_ci pb_slabs_reclaim_locked(slabs); 155bf215546Sopenharmony_ci } 156bf215546Sopenharmony_ci 157bf215546Sopenharmony_ci /* Remove slabs without free entries. */ 158bf215546Sopenharmony_ci while (!list_is_empty(&group->slabs)) { 159bf215546Sopenharmony_ci slab = list_entry(group->slabs.next, struct pb_slab, head); 160bf215546Sopenharmony_ci if (!list_is_empty(&slab->free)) 161bf215546Sopenharmony_ci break; 162bf215546Sopenharmony_ci 163bf215546Sopenharmony_ci list_del(&slab->head); 164bf215546Sopenharmony_ci } 165bf215546Sopenharmony_ci 166bf215546Sopenharmony_ci if (list_is_empty(&group->slabs)) { 167bf215546Sopenharmony_ci /* Drop the mutex temporarily to prevent a deadlock where the allocation 168bf215546Sopenharmony_ci * calls back into slab functions (most likely to happen for 169bf215546Sopenharmony_ci * pb_slab_reclaim if memory is low). 170bf215546Sopenharmony_ci * 171bf215546Sopenharmony_ci * There's a chance that racing threads will end up allocating multiple 172bf215546Sopenharmony_ci * slabs for the same group, but that doesn't hurt correctness. 173bf215546Sopenharmony_ci */ 174bf215546Sopenharmony_ci simple_mtx_unlock(&slabs->mutex); 175bf215546Sopenharmony_ci slab = slabs->slab_alloc(slabs->priv, heap, entry_size, group_index); 176bf215546Sopenharmony_ci if (!slab) 177bf215546Sopenharmony_ci return NULL; 178bf215546Sopenharmony_ci simple_mtx_lock(&slabs->mutex); 179bf215546Sopenharmony_ci 180bf215546Sopenharmony_ci list_add(&slab->head, &group->slabs); 181bf215546Sopenharmony_ci } 182bf215546Sopenharmony_ci 183bf215546Sopenharmony_ci entry = list_entry(slab->free.next, struct pb_slab_entry, head); 184bf215546Sopenharmony_ci list_del(&entry->head); 185bf215546Sopenharmony_ci slab->num_free--; 186bf215546Sopenharmony_ci 187bf215546Sopenharmony_ci simple_mtx_unlock(&slabs->mutex); 188bf215546Sopenharmony_ci 189bf215546Sopenharmony_ci return entry; 190bf215546Sopenharmony_ci} 191bf215546Sopenharmony_ci 192bf215546Sopenharmony_cistruct pb_slab_entry * 193bf215546Sopenharmony_cipb_slab_alloc(struct pb_slabs *slabs, unsigned size, unsigned heap) 194bf215546Sopenharmony_ci{ 195bf215546Sopenharmony_ci return pb_slab_alloc_reclaimed(slabs, size, heap, false); 196bf215546Sopenharmony_ci} 197bf215546Sopenharmony_ci 198bf215546Sopenharmony_ci/* Free the given slab entry. 199bf215546Sopenharmony_ci * 200bf215546Sopenharmony_ci * The entry may still be in use e.g. by in-flight command submissions. The 201bf215546Sopenharmony_ci * can_reclaim callback function will be called to determine whether the entry 202bf215546Sopenharmony_ci * can be handed out again by pb_slab_alloc. 203bf215546Sopenharmony_ci */ 204bf215546Sopenharmony_civoid 205bf215546Sopenharmony_cipb_slab_free(struct pb_slabs* slabs, struct pb_slab_entry *entry) 206bf215546Sopenharmony_ci{ 207bf215546Sopenharmony_ci simple_mtx_lock(&slabs->mutex); 208bf215546Sopenharmony_ci list_addtail(&entry->head, &slabs->reclaim); 209bf215546Sopenharmony_ci simple_mtx_unlock(&slabs->mutex); 210bf215546Sopenharmony_ci} 211bf215546Sopenharmony_ci 212bf215546Sopenharmony_ci/* Check if any of the entries handed to pb_slab_free are ready to be re-used. 213bf215546Sopenharmony_ci * 214bf215546Sopenharmony_ci * This may end up freeing some slabs and is therefore useful to try to reclaim 215bf215546Sopenharmony_ci * some no longer used memory. However, calling this function is not strictly 216bf215546Sopenharmony_ci * required since pb_slab_alloc will eventually do the same thing. 217bf215546Sopenharmony_ci */ 218bf215546Sopenharmony_civoid 219bf215546Sopenharmony_cipb_slabs_reclaim(struct pb_slabs *slabs) 220bf215546Sopenharmony_ci{ 221bf215546Sopenharmony_ci simple_mtx_lock(&slabs->mutex); 222bf215546Sopenharmony_ci pb_slabs_reclaim_locked(slabs); 223bf215546Sopenharmony_ci simple_mtx_unlock(&slabs->mutex); 224bf215546Sopenharmony_ci} 225bf215546Sopenharmony_ci 226bf215546Sopenharmony_ci/* Initialize the slabs manager. 227bf215546Sopenharmony_ci * 228bf215546Sopenharmony_ci * The minimum and maximum size of slab entries are 2^min_order and 229bf215546Sopenharmony_ci * 2^max_order, respectively. 230bf215546Sopenharmony_ci * 231bf215546Sopenharmony_ci * priv will be passed to the given callback functions. 232bf215546Sopenharmony_ci */ 233bf215546Sopenharmony_cibool 234bf215546Sopenharmony_cipb_slabs_init(struct pb_slabs *slabs, 235bf215546Sopenharmony_ci unsigned min_order, unsigned max_order, 236bf215546Sopenharmony_ci unsigned num_heaps, bool allow_three_fourth_allocations, 237bf215546Sopenharmony_ci void *priv, 238bf215546Sopenharmony_ci slab_can_reclaim_fn *can_reclaim, 239bf215546Sopenharmony_ci slab_alloc_fn *slab_alloc, 240bf215546Sopenharmony_ci slab_free_fn *slab_free) 241bf215546Sopenharmony_ci{ 242bf215546Sopenharmony_ci unsigned num_groups; 243bf215546Sopenharmony_ci unsigned i; 244bf215546Sopenharmony_ci 245bf215546Sopenharmony_ci assert(min_order <= max_order); 246bf215546Sopenharmony_ci assert(max_order < sizeof(unsigned) * 8 - 1); 247bf215546Sopenharmony_ci 248bf215546Sopenharmony_ci slabs->min_order = min_order; 249bf215546Sopenharmony_ci slabs->num_orders = max_order - min_order + 1; 250bf215546Sopenharmony_ci slabs->num_heaps = num_heaps; 251bf215546Sopenharmony_ci slabs->allow_three_fourths_allocations = allow_three_fourth_allocations; 252bf215546Sopenharmony_ci 253bf215546Sopenharmony_ci slabs->priv = priv; 254bf215546Sopenharmony_ci slabs->can_reclaim = can_reclaim; 255bf215546Sopenharmony_ci slabs->slab_alloc = slab_alloc; 256bf215546Sopenharmony_ci slabs->slab_free = slab_free; 257bf215546Sopenharmony_ci 258bf215546Sopenharmony_ci list_inithead(&slabs->reclaim); 259bf215546Sopenharmony_ci 260bf215546Sopenharmony_ci num_groups = slabs->num_orders * slabs->num_heaps * 261bf215546Sopenharmony_ci (1 + allow_three_fourth_allocations); 262bf215546Sopenharmony_ci slabs->groups = CALLOC(num_groups, sizeof(*slabs->groups)); 263bf215546Sopenharmony_ci if (!slabs->groups) 264bf215546Sopenharmony_ci return false; 265bf215546Sopenharmony_ci 266bf215546Sopenharmony_ci for (i = 0; i < num_groups; ++i) { 267bf215546Sopenharmony_ci struct pb_slab_group *group = &slabs->groups[i]; 268bf215546Sopenharmony_ci list_inithead(&group->slabs); 269bf215546Sopenharmony_ci } 270bf215546Sopenharmony_ci 271bf215546Sopenharmony_ci (void) simple_mtx_init(&slabs->mutex, mtx_plain); 272bf215546Sopenharmony_ci 273bf215546Sopenharmony_ci return true; 274bf215546Sopenharmony_ci} 275bf215546Sopenharmony_ci 276bf215546Sopenharmony_ci/* Shutdown the slab manager. 277bf215546Sopenharmony_ci * 278bf215546Sopenharmony_ci * This will free all allocated slabs and internal structures, even if some 279bf215546Sopenharmony_ci * of the slab entries are still in flight (i.e. if can_reclaim would return 280bf215546Sopenharmony_ci * false). 281bf215546Sopenharmony_ci */ 282bf215546Sopenharmony_civoid 283bf215546Sopenharmony_cipb_slabs_deinit(struct pb_slabs *slabs) 284bf215546Sopenharmony_ci{ 285bf215546Sopenharmony_ci /* Reclaim all slab entries (even those that are still in flight). This 286bf215546Sopenharmony_ci * implicitly calls slab_free for everything. 287bf215546Sopenharmony_ci */ 288bf215546Sopenharmony_ci while (!list_is_empty(&slabs->reclaim)) { 289bf215546Sopenharmony_ci struct pb_slab_entry *entry = 290bf215546Sopenharmony_ci list_entry(slabs->reclaim.next, struct pb_slab_entry, head); 291bf215546Sopenharmony_ci pb_slab_reclaim(slabs, entry); 292bf215546Sopenharmony_ci } 293bf215546Sopenharmony_ci 294bf215546Sopenharmony_ci FREE(slabs->groups); 295bf215546Sopenharmony_ci simple_mtx_destroy(&slabs->mutex); 296bf215546Sopenharmony_ci} 297