Path: blob/21.2-virgl/src/gallium/drivers/nouveau/nouveau_mm.c
4570 views
1#include <inttypes.h>23#include "util/u_inlines.h"4#include "util/u_memory.h"5#include "util/list.h"67#include "nouveau_winsys.h"8#include "nouveau_screen.h"9#include "nouveau_mm.h"1011/* TODO: Higher orders can waste a lot of space for npot size buffers, should12* add an extra cache for such buffer objects.13*14* HACK: Max order == 21 to accommodate TF2's 1.5 MiB, frequently reallocated15* vertex buffer (VM flush (?) decreases performance dramatically).16*/1718#define MM_MIN_ORDER 7 /* >= 6 to not violate ARB_map_buffer_alignment */19#define MM_MAX_ORDER 212021#define MM_NUM_BUCKETS (MM_MAX_ORDER - MM_MIN_ORDER + 1)2223#define MM_MIN_SIZE (1 << MM_MIN_ORDER)24#define MM_MAX_SIZE (1 << MM_MAX_ORDER)2526struct mm_bucket {27struct list_head free;28struct list_head used;29struct list_head full;30int num_free;31};3233struct nouveau_mman {34struct nouveau_device *dev;35struct mm_bucket bucket[MM_NUM_BUCKETS];36uint32_t domain;37union nouveau_bo_config config;38uint64_t allocated;39};4041struct mm_slab {42struct list_head head;43struct nouveau_bo *bo;44struct nouveau_mman *cache;45int order;46int count;47int free;48uint32_t bits[0];49};5051static int52mm_slab_alloc(struct mm_slab *slab)53{54int i, n, b;5556if (slab->free == 0)57return -1;5859for (i = 0; i < (slab->count + 31) / 32; ++i) {60b = ffs(slab->bits[i]) - 1;61if (b >= 0) {62n = i * 32 + b;63assert(n < slab->count);64slab->free--;65slab->bits[i] &= ~(1 << b);66return n;67}68}69return -1;70}7172static inline void73mm_slab_free(struct mm_slab *slab, int i)74{75assert(i < slab->count);76slab->bits[i / 32] |= 1 << (i % 32);77slab->free++;78assert(slab->free <= slab->count);79}8081static inline int82mm_get_order(uint32_t size)83{84int s = __builtin_clz(size) ^ 31;8586if (size > (1 << s))87s += 1;88return s;89}9091static struct mm_bucket *92mm_bucket_by_order(struct nouveau_mman *cache, int order)93{94if (order > MM_MAX_ORDER)95return NULL;96return &cache->bucket[MAX2(order, MM_MIN_ORDER) - MM_MIN_ORDER];97}9899static struct mm_bucket *100mm_bucket_by_size(struct nouveau_mman *cache, unsigned size)101{102return mm_bucket_by_order(cache, mm_get_order(size));103}104105/* size of bo allocation for slab with chunks of (1 << chunk_order) bytes */106static inline uint32_t107mm_default_slab_size(unsigned chunk_order)108{109static const int8_t slab_order[MM_MAX_ORDER - MM_MIN_ORDER + 1] =110{11112, 12, 13, 14, 14, 17, 17, 17, 17, 19, 19, 20, 21, 22, 22112};113114assert(chunk_order <= MM_MAX_ORDER && chunk_order >= MM_MIN_ORDER);115116return 1 << slab_order[chunk_order - MM_MIN_ORDER];117}118119static int120mm_slab_new(struct nouveau_mman *cache, struct mm_bucket *bucket, int chunk_order)121{122struct mm_slab *slab;123int words, ret;124const uint32_t size = mm_default_slab_size(chunk_order);125126words = ((size >> chunk_order) + 31) / 32;127assert(words);128129slab = MALLOC(sizeof(struct mm_slab) + words * 4);130if (!slab)131return PIPE_ERROR_OUT_OF_MEMORY;132133memset(&slab->bits[0], ~0, words * 4);134135slab->bo = NULL;136137ret = nouveau_bo_new(cache->dev, cache->domain, 0, size, &cache->config,138&slab->bo);139if (ret) {140FREE(slab);141return PIPE_ERROR_OUT_OF_MEMORY;142}143144list_inithead(&slab->head);145146slab->cache = cache;147slab->order = chunk_order;148slab->count = slab->free = size >> chunk_order;149150assert(bucket == mm_bucket_by_order(cache, chunk_order));151list_add(&slab->head, &bucket->free);152153cache->allocated += size;154155if (nouveau_mesa_debug)156debug_printf("MM: new slab, total memory = %"PRIu64" KiB\n",157cache->allocated / 1024);158159return PIPE_OK;160}161162/* @return token to identify slab or NULL if we just allocated a new bo */163struct nouveau_mm_allocation *164nouveau_mm_allocate(struct nouveau_mman *cache,165uint32_t size, struct nouveau_bo **bo, uint32_t *offset)166{167struct mm_bucket *bucket;168struct mm_slab *slab;169struct nouveau_mm_allocation *alloc;170int ret;171172bucket = mm_bucket_by_size(cache, size);173if (!bucket) {174ret = nouveau_bo_new(cache->dev, cache->domain, 0, size, &cache->config,175bo);176if (ret)177debug_printf("bo_new(%x, %x): %i\n",178size, cache->config.nv50.memtype, ret);179180*offset = 0;181return NULL;182}183184if (!list_is_empty(&bucket->used)) {185slab = LIST_ENTRY(struct mm_slab, bucket->used.next, head);186} else {187if (list_is_empty(&bucket->free)) {188mm_slab_new(cache, bucket, MAX2(mm_get_order(size), MM_MIN_ORDER));189}190slab = LIST_ENTRY(struct mm_slab, bucket->free.next, head);191192list_del(&slab->head);193list_add(&slab->head, &bucket->used);194}195196*offset = mm_slab_alloc(slab) << slab->order;197198alloc = MALLOC_STRUCT(nouveau_mm_allocation);199if (!alloc)200return NULL;201202nouveau_bo_ref(slab->bo, bo);203204if (slab->free == 0) {205list_del(&slab->head);206list_add(&slab->head, &bucket->full);207}208209alloc->offset = *offset;210alloc->priv = (void *)slab;211212return alloc;213}214215void216nouveau_mm_free(struct nouveau_mm_allocation *alloc)217{218struct mm_slab *slab = (struct mm_slab *)alloc->priv;219struct mm_bucket *bucket = mm_bucket_by_order(slab->cache, slab->order);220221mm_slab_free(slab, alloc->offset >> slab->order);222223if (slab->free == slab->count) {224list_del(&slab->head);225list_addtail(&slab->head, &bucket->free);226} else227if (slab->free == 1) {228list_del(&slab->head);229list_addtail(&slab->head, &bucket->used);230}231232FREE(alloc);233}234235void236nouveau_mm_free_work(void *data)237{238nouveau_mm_free(data);239}240241struct nouveau_mman *242nouveau_mm_create(struct nouveau_device *dev, uint32_t domain,243union nouveau_bo_config *config)244{245struct nouveau_mman *cache = MALLOC_STRUCT(nouveau_mman);246int i;247248if (!cache)249return NULL;250251cache->dev = dev;252cache->domain = domain;253cache->config = *config;254cache->allocated = 0;255256for (i = 0; i < MM_NUM_BUCKETS; ++i) {257list_inithead(&cache->bucket[i].free);258list_inithead(&cache->bucket[i].used);259list_inithead(&cache->bucket[i].full);260}261262return cache;263}264265static inline void266nouveau_mm_free_slabs(struct list_head *head)267{268struct mm_slab *slab, *next;269270LIST_FOR_EACH_ENTRY_SAFE(slab, next, head, head) {271list_del(&slab->head);272nouveau_bo_ref(NULL, &slab->bo);273FREE(slab);274}275}276277void278nouveau_mm_destroy(struct nouveau_mman *cache)279{280int i;281282if (!cache)283return;284285for (i = 0; i < MM_NUM_BUCKETS; ++i) {286if (!list_is_empty(&cache->bucket[i].used) ||287!list_is_empty(&cache->bucket[i].full))288debug_printf("WARNING: destroying GPU memory cache "289"with some buffers still in use\n");290291nouveau_mm_free_slabs(&cache->bucket[i].free);292nouveau_mm_free_slabs(&cache->bucket[i].used);293nouveau_mm_free_slabs(&cache->bucket[i].full);294}295296FREE(cache);297}298299300