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