nouveau_mm.c revision 3464ebd5
1 2#include "util/u_inlines.h" 3#include "util/u_memory.h" 4#include "util/u_double_list.h" 5 6#include "nouveau_screen.h" 7#include "nouveau_mm.h" 8 9#include "nouveau/nouveau_bo.h" 10 11#define MM_MIN_ORDER 7 12#define MM_MAX_ORDER 20 13 14#define MM_NUM_BUCKETS (MM_MAX_ORDER - MM_MIN_ORDER + 1) 15 16#define MM_MIN_SIZE (1 << MM_MIN_ORDER) 17#define MM_MAX_SIZE (1 << MM_MAX_ORDER) 18 19struct mm_bucket { 20 struct list_head free; 21 struct list_head used; 22 struct list_head full; 23 int num_free; 24}; 25 26struct nouveau_mman { 27 struct nouveau_device *dev; 28 struct mm_bucket bucket[MM_NUM_BUCKETS]; 29 uint32_t storage_type; 30 uint32_t domain; 31 uint64_t allocated; 32}; 33 34struct mm_slab { 35 struct list_head head; 36 struct nouveau_bo *bo; 37 struct nouveau_mman *cache; 38 int order; 39 int count; 40 int free; 41 uint32_t bits[0]; 42}; 43 44static int 45mm_slab_alloc(struct mm_slab *slab) 46{ 47 int i, n, b; 48 49 if (slab->free == 0) 50 return -1; 51 52 for (i = 0; i < (slab->count + 31) / 32; ++i) { 53 b = ffs(slab->bits[i]) - 1; 54 if (b >= 0) { 55 n = i * 32 + b; 56 assert(n < slab->count); 57 slab->free--; 58 slab->bits[i] &= ~(1 << b); 59 return n; 60 } 61 } 62 return -1; 63} 64 65static INLINE void 66mm_slab_free(struct mm_slab *slab, int i) 67{ 68 assert(i < slab->count); 69 slab->bits[i / 32] |= 1 << (i % 32); 70 slab->free++; 71 assert(slab->free <= slab->count); 72} 73 74static INLINE int 75mm_get_order(uint32_t size) 76{ 77 int s = __builtin_clz(size) ^ 31; 78 79 if (size > (1 << s)) 80 s += 1; 81 return s; 82} 83 84static struct mm_bucket * 85mm_bucket_by_order(struct nouveau_mman *cache, int order) 86{ 87 if (order > MM_MAX_ORDER) 88 return NULL; 89 return &cache->bucket[MAX2(order, MM_MIN_ORDER) - MM_MIN_ORDER]; 90} 91 92static struct mm_bucket * 93mm_bucket_by_size(struct nouveau_mman *cache, unsigned size) 94{ 95 return mm_bucket_by_order(cache, mm_get_order(size)); 96} 97 98/* size of bo allocation for slab with chunks of (1 << chunk_order) bytes */ 99static INLINE uint32_t 100mm_default_slab_size(unsigned chunk_order) 101{ 102 static const int8_t slab_order[MM_MAX_ORDER - MM_MIN_ORDER + 1] = 103 { 104 12, 12, 13, 14, 14, 17, 17, 17, 17, 19, 19, 20, 21, 22 105 }; 106 107 assert(chunk_order <= MM_MAX_ORDER && chunk_order >= MM_MIN_ORDER); 108 109 return 1 << slab_order[chunk_order - MM_MIN_ORDER]; 110} 111 112static int 113mm_slab_new(struct nouveau_mman *cache, int chunk_order) 114{ 115 struct mm_slab *slab; 116 int words, ret; 117 const uint32_t size = mm_default_slab_size(chunk_order); 118 119 words = ((size >> chunk_order) + 31) / 32; 120 assert(words); 121 122 slab = MALLOC(sizeof(struct mm_slab) + words * 4); 123 if (!slab) 124 return PIPE_ERROR_OUT_OF_MEMORY; 125 126 memset(&slab->bits[0], ~0, words * 4); 127 128 slab->bo = NULL; 129 ret = nouveau_bo_new_tile(cache->dev, cache->domain, 0, size, 130 0, cache->storage_type, &slab->bo); 131 if (ret) { 132 FREE(slab); 133 return PIPE_ERROR_OUT_OF_MEMORY; 134 } 135 136 LIST_INITHEAD(&slab->head); 137 138 slab->cache = cache; 139 slab->order = chunk_order; 140 slab->count = slab->free = size >> chunk_order; 141 142 LIST_ADD(&slab->head, &mm_bucket_by_order(cache, chunk_order)->free); 143 144 cache->allocated += size; 145 146 debug_printf("MM: new slab, total memory = %llu KiB\n", 147 cache->allocated / 1024); 148 149 return PIPE_OK; 150} 151 152/* @return token to identify slab or NULL if we just allocated a new bo */ 153struct nouveau_mm_allocation * 154nouveau_mm_allocate(struct nouveau_mman *cache, 155 uint32_t size, struct nouveau_bo **bo, uint32_t *offset) 156{ 157 struct mm_bucket *bucket; 158 struct mm_slab *slab; 159 struct nouveau_mm_allocation *alloc; 160 int ret; 161 162 bucket = mm_bucket_by_size(cache, size); 163 if (!bucket) { 164 ret = nouveau_bo_new_tile(cache->dev, cache->domain, 0, size, 165 0, cache->storage_type, bo); 166 if (ret) 167 debug_printf("bo_new(%x, %x): %i\n", size, cache->storage_type, ret); 168 169 *offset = 0; 170 return NULL; 171 } 172 173 if (!LIST_IS_EMPTY(&bucket->used)) { 174 slab = LIST_ENTRY(struct mm_slab, bucket->used.next, head); 175 } else { 176 if (LIST_IS_EMPTY(&bucket->free)) { 177 mm_slab_new(cache, MAX2(mm_get_order(size), MM_MIN_ORDER)); 178 } 179 slab = LIST_ENTRY(struct mm_slab, bucket->free.next, head); 180 181 LIST_DEL(&slab->head); 182 LIST_ADD(&slab->head, &bucket->used); 183 } 184 185 *offset = mm_slab_alloc(slab) << slab->order; 186 187 alloc = MALLOC_STRUCT(nouveau_mm_allocation); 188 if (!alloc) 189 return NULL; 190 191 nouveau_bo_ref(slab->bo, bo); 192 193 if (slab->free == 0) { 194 LIST_DEL(&slab->head); 195 LIST_ADD(&slab->head, &bucket->full); 196 } 197 198 alloc->next = NULL; 199 alloc->offset = *offset; 200 alloc->priv = (void *)slab; 201 202 return alloc; 203} 204 205void 206nouveau_mm_free(struct nouveau_mm_allocation *alloc) 207{ 208 struct mm_slab *slab = (struct mm_slab *)alloc->priv; 209 struct mm_bucket *bucket = mm_bucket_by_order(slab->cache, slab->order); 210 211 mm_slab_free(slab, alloc->offset >> slab->order); 212 213 if (slab->free == 1) { 214 LIST_DEL(&slab->head); 215 216 if (slab->count > 1) 217 LIST_ADDTAIL(&slab->head, &bucket->used); 218 else 219 LIST_ADDTAIL(&slab->head, &bucket->free); 220 } 221 222 FREE(alloc); 223} 224 225void 226nouveau_mm_free_work(void *data) 227{ 228 nouveau_mm_free(data); 229} 230 231struct nouveau_mman * 232nouveau_mm_create(struct nouveau_device *dev, uint32_t domain, 233 uint32_t storage_type) 234{ 235 struct nouveau_mman *cache = MALLOC_STRUCT(nouveau_mman); 236 int i; 237 238 if (!cache) 239 return NULL; 240 241 cache->dev = dev; 242 cache->domain = domain; 243 cache->storage_type = storage_type; 244 cache->allocated = 0; 245 246 for (i = 0; i < MM_NUM_BUCKETS; ++i) { 247 LIST_INITHEAD(&cache->bucket[i].free); 248 LIST_INITHEAD(&cache->bucket[i].used); 249 LIST_INITHEAD(&cache->bucket[i].full); 250 } 251 252 return cache; 253} 254 255static INLINE void 256nouveau_mm_free_slabs(struct list_head *head) 257{ 258 struct mm_slab *slab, *next; 259 260 LIST_FOR_EACH_ENTRY_SAFE(slab, next, head, head) { 261 LIST_DEL(&slab->head); 262 nouveau_bo_ref(NULL, &slab->bo); 263 FREE(slab); 264 } 265} 266 267void 268nouveau_mm_destroy(struct nouveau_mman *cache) 269{ 270 int i; 271 272 if (!cache) 273 return; 274 275 for (i = 0; i < MM_NUM_BUCKETS; ++i) { 276 if (!LIST_IS_EMPTY(&cache->bucket[i].used) || 277 !LIST_IS_EMPTY(&cache->bucket[i].full)) 278 debug_printf("WARNING: destroying GPU memory cache " 279 "with some buffers still in use\n"); 280 281 nouveau_mm_free_slabs(&cache->bucket[i].free); 282 nouveau_mm_free_slabs(&cache->bucket[i].used); 283 nouveau_mm_free_slabs(&cache->bucket[i].full); 284 } 285 286 FREE(cache); 287} 288 289