1bf215546Sopenharmony_ci 2bf215546Sopenharmony_ci#include <inttypes.h> 3bf215546Sopenharmony_ci 4bf215546Sopenharmony_ci#include "util/simple_mtx.h" 5bf215546Sopenharmony_ci#include "util/u_inlines.h" 6bf215546Sopenharmony_ci#include "util/u_memory.h" 7bf215546Sopenharmony_ci#include "util/list.h" 8bf215546Sopenharmony_ci 9bf215546Sopenharmony_ci#include "nouveau_winsys.h" 10bf215546Sopenharmony_ci#include "nouveau_screen.h" 11bf215546Sopenharmony_ci#include "nouveau_mm.h" 12bf215546Sopenharmony_ci 13bf215546Sopenharmony_ci/* TODO: Higher orders can waste a lot of space for npot size buffers, should 14bf215546Sopenharmony_ci * add an extra cache for such buffer objects. 15bf215546Sopenharmony_ci * 16bf215546Sopenharmony_ci * HACK: Max order == 21 to accommodate TF2's 1.5 MiB, frequently reallocated 17bf215546Sopenharmony_ci * vertex buffer (VM flush (?) decreases performance dramatically). 18bf215546Sopenharmony_ci */ 19bf215546Sopenharmony_ci 20bf215546Sopenharmony_ci#define MM_MIN_ORDER 7 /* >= 6 to not violate ARB_map_buffer_alignment */ 21bf215546Sopenharmony_ci#define MM_MAX_ORDER 21 22bf215546Sopenharmony_ci 23bf215546Sopenharmony_ci#define MM_NUM_BUCKETS (MM_MAX_ORDER - MM_MIN_ORDER + 1) 24bf215546Sopenharmony_ci 25bf215546Sopenharmony_ci#define MM_MIN_SIZE (1 << MM_MIN_ORDER) 26bf215546Sopenharmony_ci#define MM_MAX_SIZE (1 << MM_MAX_ORDER) 27bf215546Sopenharmony_ci 28bf215546Sopenharmony_cistruct mm_bucket { 29bf215546Sopenharmony_ci struct list_head free; 30bf215546Sopenharmony_ci struct list_head used; 31bf215546Sopenharmony_ci struct list_head full; 32bf215546Sopenharmony_ci int num_free; 33bf215546Sopenharmony_ci simple_mtx_t lock; 34bf215546Sopenharmony_ci}; 35bf215546Sopenharmony_ci 36bf215546Sopenharmony_cistruct nouveau_mman { 37bf215546Sopenharmony_ci struct nouveau_device *dev; 38bf215546Sopenharmony_ci struct mm_bucket bucket[MM_NUM_BUCKETS]; 39bf215546Sopenharmony_ci uint32_t domain; 40bf215546Sopenharmony_ci union nouveau_bo_config config; 41bf215546Sopenharmony_ci uint64_t allocated; 42bf215546Sopenharmony_ci}; 43bf215546Sopenharmony_ci 44bf215546Sopenharmony_cistruct mm_slab { 45bf215546Sopenharmony_ci struct list_head head; 46bf215546Sopenharmony_ci struct nouveau_bo *bo; 47bf215546Sopenharmony_ci struct nouveau_mman *cache; 48bf215546Sopenharmony_ci int order; 49bf215546Sopenharmony_ci int count; 50bf215546Sopenharmony_ci int free; 51bf215546Sopenharmony_ci uint32_t bits[0]; 52bf215546Sopenharmony_ci}; 53bf215546Sopenharmony_ci 54bf215546Sopenharmony_cistatic int 55bf215546Sopenharmony_cimm_slab_alloc(struct mm_slab *slab) 56bf215546Sopenharmony_ci{ 57bf215546Sopenharmony_ci int i, n, b; 58bf215546Sopenharmony_ci 59bf215546Sopenharmony_ci if (slab->free == 0) 60bf215546Sopenharmony_ci return -1; 61bf215546Sopenharmony_ci 62bf215546Sopenharmony_ci for (i = 0; i < (slab->count + 31) / 32; ++i) { 63bf215546Sopenharmony_ci b = ffs(slab->bits[i]) - 1; 64bf215546Sopenharmony_ci if (b >= 0) { 65bf215546Sopenharmony_ci n = i * 32 + b; 66bf215546Sopenharmony_ci assert(n < slab->count); 67bf215546Sopenharmony_ci slab->free--; 68bf215546Sopenharmony_ci slab->bits[i] &= ~(1 << b); 69bf215546Sopenharmony_ci return n; 70bf215546Sopenharmony_ci } 71bf215546Sopenharmony_ci } 72bf215546Sopenharmony_ci return -1; 73bf215546Sopenharmony_ci} 74bf215546Sopenharmony_ci 75bf215546Sopenharmony_cistatic inline void 76bf215546Sopenharmony_cimm_slab_free(struct mm_slab *slab, int i) 77bf215546Sopenharmony_ci{ 78bf215546Sopenharmony_ci assert(i < slab->count); 79bf215546Sopenharmony_ci slab->bits[i / 32] |= 1 << (i % 32); 80bf215546Sopenharmony_ci slab->free++; 81bf215546Sopenharmony_ci assert(slab->free <= slab->count); 82bf215546Sopenharmony_ci} 83bf215546Sopenharmony_ci 84bf215546Sopenharmony_cistatic inline int 85bf215546Sopenharmony_cimm_get_order(uint32_t size) 86bf215546Sopenharmony_ci{ 87bf215546Sopenharmony_ci int s = __builtin_clz(size) ^ 31; 88bf215546Sopenharmony_ci 89bf215546Sopenharmony_ci if (size > (1 << s)) 90bf215546Sopenharmony_ci s += 1; 91bf215546Sopenharmony_ci return s; 92bf215546Sopenharmony_ci} 93bf215546Sopenharmony_ci 94bf215546Sopenharmony_cistatic struct mm_bucket * 95bf215546Sopenharmony_cimm_bucket_by_order(struct nouveau_mman *cache, int order) 96bf215546Sopenharmony_ci{ 97bf215546Sopenharmony_ci if (order > MM_MAX_ORDER) 98bf215546Sopenharmony_ci return NULL; 99bf215546Sopenharmony_ci return &cache->bucket[MAX2(order, MM_MIN_ORDER) - MM_MIN_ORDER]; 100bf215546Sopenharmony_ci} 101bf215546Sopenharmony_ci 102bf215546Sopenharmony_cistatic struct mm_bucket * 103bf215546Sopenharmony_cimm_bucket_by_size(struct nouveau_mman *cache, unsigned size) 104bf215546Sopenharmony_ci{ 105bf215546Sopenharmony_ci return mm_bucket_by_order(cache, mm_get_order(size)); 106bf215546Sopenharmony_ci} 107bf215546Sopenharmony_ci 108bf215546Sopenharmony_ci/* size of bo allocation for slab with chunks of (1 << chunk_order) bytes */ 109bf215546Sopenharmony_cistatic inline uint32_t 110bf215546Sopenharmony_cimm_default_slab_size(unsigned chunk_order) 111bf215546Sopenharmony_ci{ 112bf215546Sopenharmony_ci static const int8_t slab_order[MM_MAX_ORDER - MM_MIN_ORDER + 1] = 113bf215546Sopenharmony_ci { 114bf215546Sopenharmony_ci 12, 12, 13, 14, 14, 17, 17, 17, 17, 19, 19, 20, 21, 22, 22 115bf215546Sopenharmony_ci }; 116bf215546Sopenharmony_ci 117bf215546Sopenharmony_ci assert(chunk_order <= MM_MAX_ORDER && chunk_order >= MM_MIN_ORDER); 118bf215546Sopenharmony_ci 119bf215546Sopenharmony_ci return 1 << slab_order[chunk_order - MM_MIN_ORDER]; 120bf215546Sopenharmony_ci} 121bf215546Sopenharmony_ci 122bf215546Sopenharmony_cistatic int 123bf215546Sopenharmony_cimm_slab_new(struct nouveau_mman *cache, struct mm_bucket *bucket, int chunk_order) 124bf215546Sopenharmony_ci{ 125bf215546Sopenharmony_ci struct mm_slab *slab; 126bf215546Sopenharmony_ci int words, ret; 127bf215546Sopenharmony_ci const uint32_t size = mm_default_slab_size(chunk_order); 128bf215546Sopenharmony_ci 129bf215546Sopenharmony_ci simple_mtx_assert_locked(&bucket->lock); 130bf215546Sopenharmony_ci 131bf215546Sopenharmony_ci words = ((size >> chunk_order) + 31) / 32; 132bf215546Sopenharmony_ci assert(words); 133bf215546Sopenharmony_ci 134bf215546Sopenharmony_ci slab = MALLOC(sizeof(struct mm_slab) + words * 4); 135bf215546Sopenharmony_ci if (!slab) 136bf215546Sopenharmony_ci return PIPE_ERROR_OUT_OF_MEMORY; 137bf215546Sopenharmony_ci 138bf215546Sopenharmony_ci memset(&slab->bits[0], ~0, words * 4); 139bf215546Sopenharmony_ci 140bf215546Sopenharmony_ci slab->bo = NULL; 141bf215546Sopenharmony_ci 142bf215546Sopenharmony_ci ret = nouveau_bo_new(cache->dev, cache->domain, 0, size, &cache->config, 143bf215546Sopenharmony_ci &slab->bo); 144bf215546Sopenharmony_ci if (ret) { 145bf215546Sopenharmony_ci FREE(slab); 146bf215546Sopenharmony_ci return PIPE_ERROR_OUT_OF_MEMORY; 147bf215546Sopenharmony_ci } 148bf215546Sopenharmony_ci 149bf215546Sopenharmony_ci list_inithead(&slab->head); 150bf215546Sopenharmony_ci 151bf215546Sopenharmony_ci slab->cache = cache; 152bf215546Sopenharmony_ci slab->order = chunk_order; 153bf215546Sopenharmony_ci slab->count = slab->free = size >> chunk_order; 154bf215546Sopenharmony_ci 155bf215546Sopenharmony_ci assert(bucket == mm_bucket_by_order(cache, chunk_order)); 156bf215546Sopenharmony_ci list_add(&slab->head, &bucket->free); 157bf215546Sopenharmony_ci 158bf215546Sopenharmony_ci p_atomic_add(&cache->allocated, size); 159bf215546Sopenharmony_ci 160bf215546Sopenharmony_ci if (nouveau_mesa_debug) 161bf215546Sopenharmony_ci debug_printf("MM: new slab, total memory = %"PRIu64" KiB\n", 162bf215546Sopenharmony_ci cache->allocated / 1024); 163bf215546Sopenharmony_ci 164bf215546Sopenharmony_ci return PIPE_OK; 165bf215546Sopenharmony_ci} 166bf215546Sopenharmony_ci 167bf215546Sopenharmony_ci/* @return token to identify slab or NULL if we just allocated a new bo */ 168bf215546Sopenharmony_cistruct nouveau_mm_allocation * 169bf215546Sopenharmony_cinouveau_mm_allocate(struct nouveau_mman *cache, 170bf215546Sopenharmony_ci uint32_t size, struct nouveau_bo **bo, uint32_t *offset) 171bf215546Sopenharmony_ci{ 172bf215546Sopenharmony_ci struct mm_bucket *bucket; 173bf215546Sopenharmony_ci struct mm_slab *slab; 174bf215546Sopenharmony_ci struct nouveau_mm_allocation *alloc; 175bf215546Sopenharmony_ci int ret; 176bf215546Sopenharmony_ci 177bf215546Sopenharmony_ci bucket = mm_bucket_by_size(cache, size); 178bf215546Sopenharmony_ci if (!bucket) { 179bf215546Sopenharmony_ci ret = nouveau_bo_new(cache->dev, cache->domain, 0, size, &cache->config, 180bf215546Sopenharmony_ci bo); 181bf215546Sopenharmony_ci if (ret) 182bf215546Sopenharmony_ci debug_printf("bo_new(%x, %x): %i\n", 183bf215546Sopenharmony_ci size, cache->config.nv50.memtype, ret); 184bf215546Sopenharmony_ci 185bf215546Sopenharmony_ci *offset = 0; 186bf215546Sopenharmony_ci return NULL; 187bf215546Sopenharmony_ci } 188bf215546Sopenharmony_ci 189bf215546Sopenharmony_ci alloc = MALLOC_STRUCT(nouveau_mm_allocation); 190bf215546Sopenharmony_ci if (!alloc) 191bf215546Sopenharmony_ci return NULL; 192bf215546Sopenharmony_ci 193bf215546Sopenharmony_ci simple_mtx_lock(&bucket->lock); 194bf215546Sopenharmony_ci if (!list_is_empty(&bucket->used)) { 195bf215546Sopenharmony_ci slab = list_entry(bucket->used.next, struct mm_slab, head); 196bf215546Sopenharmony_ci } else { 197bf215546Sopenharmony_ci if (list_is_empty(&bucket->free)) { 198bf215546Sopenharmony_ci mm_slab_new(cache, bucket, MAX2(mm_get_order(size), MM_MIN_ORDER)); 199bf215546Sopenharmony_ci } 200bf215546Sopenharmony_ci slab = list_entry(bucket->free.next, struct mm_slab, head); 201bf215546Sopenharmony_ci 202bf215546Sopenharmony_ci list_del(&slab->head); 203bf215546Sopenharmony_ci list_add(&slab->head, &bucket->used); 204bf215546Sopenharmony_ci } 205bf215546Sopenharmony_ci 206bf215546Sopenharmony_ci *offset = mm_slab_alloc(slab) << slab->order; 207bf215546Sopenharmony_ci 208bf215546Sopenharmony_ci nouveau_bo_ref(slab->bo, bo); 209bf215546Sopenharmony_ci 210bf215546Sopenharmony_ci if (slab->free == 0) { 211bf215546Sopenharmony_ci list_del(&slab->head); 212bf215546Sopenharmony_ci list_add(&slab->head, &bucket->full); 213bf215546Sopenharmony_ci } 214bf215546Sopenharmony_ci simple_mtx_unlock(&bucket->lock); 215bf215546Sopenharmony_ci 216bf215546Sopenharmony_ci alloc->offset = *offset; 217bf215546Sopenharmony_ci alloc->priv = (void *)slab; 218bf215546Sopenharmony_ci 219bf215546Sopenharmony_ci return alloc; 220bf215546Sopenharmony_ci} 221bf215546Sopenharmony_ci 222bf215546Sopenharmony_civoid 223bf215546Sopenharmony_cinouveau_mm_free(struct nouveau_mm_allocation *alloc) 224bf215546Sopenharmony_ci{ 225bf215546Sopenharmony_ci struct mm_slab *slab = (struct mm_slab *)alloc->priv; 226bf215546Sopenharmony_ci struct mm_bucket *bucket = mm_bucket_by_order(slab->cache, slab->order); 227bf215546Sopenharmony_ci 228bf215546Sopenharmony_ci simple_mtx_lock(&bucket->lock); 229bf215546Sopenharmony_ci mm_slab_free(slab, alloc->offset >> slab->order); 230bf215546Sopenharmony_ci 231bf215546Sopenharmony_ci if (slab->free == slab->count) { 232bf215546Sopenharmony_ci list_del(&slab->head); 233bf215546Sopenharmony_ci list_addtail(&slab->head, &bucket->free); 234bf215546Sopenharmony_ci } else 235bf215546Sopenharmony_ci if (slab->free == 1) { 236bf215546Sopenharmony_ci list_del(&slab->head); 237bf215546Sopenharmony_ci list_addtail(&slab->head, &bucket->used); 238bf215546Sopenharmony_ci } 239bf215546Sopenharmony_ci simple_mtx_unlock(&bucket->lock); 240bf215546Sopenharmony_ci 241bf215546Sopenharmony_ci FREE(alloc); 242bf215546Sopenharmony_ci} 243bf215546Sopenharmony_ci 244bf215546Sopenharmony_civoid 245bf215546Sopenharmony_cinouveau_mm_free_work(void *data) 246bf215546Sopenharmony_ci{ 247bf215546Sopenharmony_ci nouveau_mm_free(data); 248bf215546Sopenharmony_ci} 249bf215546Sopenharmony_ci 250bf215546Sopenharmony_cistruct nouveau_mman * 251bf215546Sopenharmony_cinouveau_mm_create(struct nouveau_device *dev, uint32_t domain, 252bf215546Sopenharmony_ci union nouveau_bo_config *config) 253bf215546Sopenharmony_ci{ 254bf215546Sopenharmony_ci struct nouveau_mman *cache = MALLOC_STRUCT(nouveau_mman); 255bf215546Sopenharmony_ci int i; 256bf215546Sopenharmony_ci 257bf215546Sopenharmony_ci if (!cache) 258bf215546Sopenharmony_ci return NULL; 259bf215546Sopenharmony_ci 260bf215546Sopenharmony_ci cache->dev = dev; 261bf215546Sopenharmony_ci cache->domain = domain; 262bf215546Sopenharmony_ci cache->config = *config; 263bf215546Sopenharmony_ci cache->allocated = 0; 264bf215546Sopenharmony_ci 265bf215546Sopenharmony_ci for (i = 0; i < MM_NUM_BUCKETS; ++i) { 266bf215546Sopenharmony_ci list_inithead(&cache->bucket[i].free); 267bf215546Sopenharmony_ci list_inithead(&cache->bucket[i].used); 268bf215546Sopenharmony_ci list_inithead(&cache->bucket[i].full); 269bf215546Sopenharmony_ci simple_mtx_init(&cache->bucket[i].lock, mtx_plain); 270bf215546Sopenharmony_ci } 271bf215546Sopenharmony_ci 272bf215546Sopenharmony_ci return cache; 273bf215546Sopenharmony_ci} 274bf215546Sopenharmony_ci 275bf215546Sopenharmony_cistatic inline void 276bf215546Sopenharmony_cinouveau_mm_free_slabs(struct list_head *head) 277bf215546Sopenharmony_ci{ 278bf215546Sopenharmony_ci struct mm_slab *slab, *next; 279bf215546Sopenharmony_ci 280bf215546Sopenharmony_ci LIST_FOR_EACH_ENTRY_SAFE(slab, next, head, head) { 281bf215546Sopenharmony_ci list_del(&slab->head); 282bf215546Sopenharmony_ci nouveau_bo_ref(NULL, &slab->bo); 283bf215546Sopenharmony_ci FREE(slab); 284bf215546Sopenharmony_ci } 285bf215546Sopenharmony_ci} 286bf215546Sopenharmony_ci 287bf215546Sopenharmony_civoid 288bf215546Sopenharmony_cinouveau_mm_destroy(struct nouveau_mman *cache) 289bf215546Sopenharmony_ci{ 290bf215546Sopenharmony_ci int i; 291bf215546Sopenharmony_ci 292bf215546Sopenharmony_ci if (!cache) 293bf215546Sopenharmony_ci return; 294bf215546Sopenharmony_ci 295bf215546Sopenharmony_ci for (i = 0; i < MM_NUM_BUCKETS; ++i) { 296bf215546Sopenharmony_ci if (!list_is_empty(&cache->bucket[i].used) || 297bf215546Sopenharmony_ci !list_is_empty(&cache->bucket[i].full)) 298bf215546Sopenharmony_ci debug_printf("WARNING: destroying GPU memory cache " 299bf215546Sopenharmony_ci "with some buffers still in use\n"); 300bf215546Sopenharmony_ci 301bf215546Sopenharmony_ci nouveau_mm_free_slabs(&cache->bucket[i].free); 302bf215546Sopenharmony_ci nouveau_mm_free_slabs(&cache->bucket[i].used); 303bf215546Sopenharmony_ci nouveau_mm_free_slabs(&cache->bucket[i].full); 304bf215546Sopenharmony_ci simple_mtx_destroy(&cache->bucket[i].lock); 305bf215546Sopenharmony_ci } 306bf215546Sopenharmony_ci 307bf215546Sopenharmony_ci FREE(cache); 308bf215546Sopenharmony_ci} 309