util: rename list_empty() to list_is_empty()
[mesa.git] / src / gallium / drivers / vc4 / vc4_bufmgr.c
1 /*
2 * Copyright © 2014-2015 Broadcom
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 */
23
24 #include <errno.h>
25 #include <err.h>
26 #include <sys/mman.h>
27 #include <fcntl.h>
28 #include <xf86drm.h>
29 #include <xf86drmMode.h>
30
31 #include "util/u_hash_table.h"
32 #include "util/u_memory.h"
33 #include "util/u_string.h"
34 #include "util/ralloc.h"
35
36 #include "vc4_context.h"
37 #include "vc4_screen.h"
38
39 #ifdef HAVE_VALGRIND
40 #include <valgrind.h>
41 #include <memcheck.h>
42 #define VG(x) x
43 #else
44 #define VG(x)
45 #endif
46
47 static bool dump_stats = false;
48
49 static void
50 vc4_bo_cache_free_all(struct vc4_bo_cache *cache);
51
52 void
53 vc4_bo_debug_describe(char* buf, const struct vc4_bo *ptr)
54 {
55 sprintf(buf, "vc4_bo<%s,%u,%u>", ptr->name ? ptr->name : "?",
56 ptr->handle, ptr->size);
57 }
58
59 void
60 vc4_bo_label(struct vc4_screen *screen, struct vc4_bo *bo, const char *fmt, ...)
61 {
62 /* Perform BO labeling by default on debug builds (so that you get
63 * whole-system allocation information), or if VC4_DEBUG=surf is set
64 * (for debugging a single app's allocation).
65 */
66 #ifndef DEBUG
67 if (!(vc4_debug & VC4_DEBUG_SURFACE))
68 return;
69 #endif
70 va_list va;
71 va_start(va, fmt);
72 char *name = ralloc_vasprintf(NULL, fmt, va);
73 va_end(va);
74
75 struct drm_vc4_label_bo label = {
76 .handle = bo->handle,
77 .len = strlen(name),
78 .name = (uintptr_t)name,
79 };
80 vc4_ioctl(screen->fd, DRM_IOCTL_VC4_LABEL_BO, &label);
81
82 ralloc_free(name);
83 }
84
85 static void
86 vc4_bo_dump_stats(struct vc4_screen *screen)
87 {
88 struct vc4_bo_cache *cache = &screen->bo_cache;
89
90 fprintf(stderr, " BOs allocated: %d\n", screen->bo_count);
91 fprintf(stderr, " BOs size: %dkb\n", screen->bo_size / 1024);
92 fprintf(stderr, " BOs cached: %d\n", cache->bo_count);
93 fprintf(stderr, " BOs cached size: %dkb\n", cache->bo_size / 1024);
94
95 if (!list_is_empty(&cache->time_list)) {
96 struct vc4_bo *first = LIST_ENTRY(struct vc4_bo,
97 cache->time_list.next,
98 time_list);
99 struct vc4_bo *last = LIST_ENTRY(struct vc4_bo,
100 cache->time_list.prev,
101 time_list);
102
103 fprintf(stderr, " oldest cache time: %ld\n",
104 (long)first->free_time);
105 fprintf(stderr, " newest cache time: %ld\n",
106 (long)last->free_time);
107
108 struct timespec time;
109 clock_gettime(CLOCK_MONOTONIC, &time);
110 fprintf(stderr, " now: %ld\n",
111 time.tv_sec);
112 }
113 }
114
115 static void
116 vc4_bo_remove_from_cache(struct vc4_bo_cache *cache, struct vc4_bo *bo)
117 {
118 list_del(&bo->time_list);
119 list_del(&bo->size_list);
120 cache->bo_count--;
121 cache->bo_size -= bo->size;
122 }
123
124 static void vc4_bo_purgeable(struct vc4_bo *bo)
125 {
126 struct drm_vc4_gem_madvise arg = {
127 .handle = bo->handle,
128 .madv = VC4_MADV_DONTNEED,
129 };
130
131 if (bo->screen->has_madvise)
132 vc4_ioctl(bo->screen->fd, DRM_IOCTL_VC4_GEM_MADVISE, &arg);
133 }
134
135 static bool vc4_bo_unpurgeable(struct vc4_bo *bo)
136 {
137 struct drm_vc4_gem_madvise arg = {
138 .handle = bo->handle,
139 .madv = VC4_MADV_WILLNEED,
140 };
141
142 if (!bo->screen->has_madvise)
143 return true;
144
145 if (vc4_ioctl(bo->screen->fd, DRM_IOCTL_VC4_GEM_MADVISE, &arg))
146 return false;
147
148 return arg.retained;
149 }
150
151 static void
152 vc4_bo_free(struct vc4_bo *bo)
153 {
154 struct vc4_screen *screen = bo->screen;
155
156 if (bo->map) {
157 if (using_vc4_simulator && bo->name &&
158 strcmp(bo->name, "winsys") == 0) {
159 free(bo->map);
160 } else {
161 munmap(bo->map, bo->size);
162 VG(VALGRIND_FREELIKE_BLOCK(bo->map, 0));
163 }
164 }
165
166 struct drm_gem_close c;
167 memset(&c, 0, sizeof(c));
168 c.handle = bo->handle;
169 int ret = vc4_ioctl(screen->fd, DRM_IOCTL_GEM_CLOSE, &c);
170 if (ret != 0)
171 fprintf(stderr, "close object %d: %s\n", bo->handle, strerror(errno));
172
173 screen->bo_count--;
174 screen->bo_size -= bo->size;
175
176 if (dump_stats) {
177 fprintf(stderr, "Freed %s%s%dkb:\n",
178 bo->name ? bo->name : "",
179 bo->name ? " " : "",
180 bo->size / 1024);
181 vc4_bo_dump_stats(screen);
182 }
183
184 free(bo);
185 }
186
187 static struct vc4_bo *
188 vc4_bo_from_cache(struct vc4_screen *screen, uint32_t size, const char *name)
189 {
190 struct vc4_bo_cache *cache = &screen->bo_cache;
191 uint32_t page_index = size / 4096 - 1;
192 struct vc4_bo *iter, *tmp, *bo = NULL;
193
194 if (cache->size_list_size <= page_index)
195 return NULL;
196
197 mtx_lock(&cache->lock);
198 LIST_FOR_EACH_ENTRY_SAFE(iter, tmp, &cache->size_list[page_index],
199 size_list) {
200 /* Check that the BO has gone idle. If not, then none of the
201 * other BOs (pushed to the list after later rendering) are
202 * likely to be idle, either.
203 */
204 if (!vc4_bo_wait(iter, 0, NULL))
205 break;
206
207 if (!vc4_bo_unpurgeable(iter)) {
208 /* The BO has been purged. Free it and try to find
209 * another one in the cache.
210 */
211 vc4_bo_remove_from_cache(cache, iter);
212 vc4_bo_free(iter);
213 continue;
214 }
215
216 bo = iter;
217 pipe_reference_init(&bo->reference, 1);
218 vc4_bo_remove_from_cache(cache, bo);
219
220 vc4_bo_label(screen, bo, "%s", name);
221 bo->name = name;
222 break;
223 }
224 mtx_unlock(&cache->lock);
225 return bo;
226 }
227
228 struct vc4_bo *
229 vc4_bo_alloc(struct vc4_screen *screen, uint32_t size, const char *name)
230 {
231 bool cleared_and_retried = false;
232 struct drm_vc4_create_bo create;
233 struct vc4_bo *bo;
234 int ret;
235
236 size = align(size, 4096);
237
238 bo = vc4_bo_from_cache(screen, size, name);
239 if (bo) {
240 if (dump_stats) {
241 fprintf(stderr, "Allocated %s %dkb from cache:\n",
242 name, size / 1024);
243 vc4_bo_dump_stats(screen);
244 }
245 return bo;
246 }
247
248 bo = CALLOC_STRUCT(vc4_bo);
249 if (!bo)
250 return NULL;
251
252 pipe_reference_init(&bo->reference, 1);
253 bo->screen = screen;
254 bo->size = size;
255 bo->name = name;
256 bo->private = true;
257
258 retry:
259 memset(&create, 0, sizeof(create));
260 create.size = size;
261
262 ret = vc4_ioctl(screen->fd, DRM_IOCTL_VC4_CREATE_BO, &create);
263 bo->handle = create.handle;
264
265 if (ret != 0) {
266 if (!list_is_empty(&screen->bo_cache.time_list) &&
267 !cleared_and_retried) {
268 cleared_and_retried = true;
269 vc4_bo_cache_free_all(&screen->bo_cache);
270 goto retry;
271 }
272
273 free(bo);
274 return NULL;
275 }
276
277 screen->bo_count++;
278 screen->bo_size += bo->size;
279 if (dump_stats) {
280 fprintf(stderr, "Allocated %s %dkb:\n", name, size / 1024);
281 vc4_bo_dump_stats(screen);
282 }
283
284 vc4_bo_label(screen, bo, "%s", name);
285
286 return bo;
287 }
288
289 void
290 vc4_bo_last_unreference(struct vc4_bo *bo)
291 {
292 struct vc4_screen *screen = bo->screen;
293
294 struct timespec time;
295 clock_gettime(CLOCK_MONOTONIC, &time);
296 mtx_lock(&screen->bo_cache.lock);
297 vc4_bo_last_unreference_locked_timed(bo, time.tv_sec);
298 mtx_unlock(&screen->bo_cache.lock);
299 }
300
301 static void
302 free_stale_bos(struct vc4_screen *screen, time_t time)
303 {
304 struct vc4_bo_cache *cache = &screen->bo_cache;
305 bool freed_any = false;
306
307 list_for_each_entry_safe(struct vc4_bo, bo, &cache->time_list,
308 time_list) {
309 if (dump_stats && !freed_any) {
310 fprintf(stderr, "Freeing stale BOs:\n");
311 vc4_bo_dump_stats(screen);
312 freed_any = true;
313 }
314
315 /* If it's more than a second old, free it. */
316 if (time - bo->free_time > 2) {
317 vc4_bo_remove_from_cache(cache, bo);
318 vc4_bo_free(bo);
319 } else {
320 break;
321 }
322 }
323
324 if (dump_stats && freed_any) {
325 fprintf(stderr, "Freed stale BOs:\n");
326 vc4_bo_dump_stats(screen);
327 }
328 }
329
330 static void
331 vc4_bo_cache_free_all(struct vc4_bo_cache *cache)
332 {
333 mtx_lock(&cache->lock);
334 list_for_each_entry_safe(struct vc4_bo, bo, &cache->time_list,
335 time_list) {
336 vc4_bo_remove_from_cache(cache, bo);
337 vc4_bo_free(bo);
338 }
339 mtx_unlock(&cache->lock);
340 }
341
342 void
343 vc4_bo_last_unreference_locked_timed(struct vc4_bo *bo, time_t time)
344 {
345 struct vc4_screen *screen = bo->screen;
346 struct vc4_bo_cache *cache = &screen->bo_cache;
347 uint32_t page_index = bo->size / 4096 - 1;
348
349 if (!bo->private) {
350 vc4_bo_free(bo);
351 return;
352 }
353
354 if (cache->size_list_size <= page_index) {
355 struct list_head *new_list =
356 ralloc_array(screen, struct list_head, page_index + 1);
357
358 /* Move old list contents over (since the array has moved, and
359 * therefore the pointers to the list heads have to change).
360 */
361 for (int i = 0; i < cache->size_list_size; i++)
362 list_replace(&cache->size_list[i], &new_list[i]);
363 for (int i = cache->size_list_size; i < page_index + 1; i++)
364 list_inithead(&new_list[i]);
365
366 cache->size_list = new_list;
367 cache->size_list_size = page_index + 1;
368 }
369
370 vc4_bo_purgeable(bo);
371 bo->free_time = time;
372 list_addtail(&bo->size_list, &cache->size_list[page_index]);
373 list_addtail(&bo->time_list, &cache->time_list);
374 cache->bo_count++;
375 cache->bo_size += bo->size;
376 if (dump_stats) {
377 fprintf(stderr, "Freed %s %dkb to cache:\n",
378 bo->name, bo->size / 1024);
379 vc4_bo_dump_stats(screen);
380 }
381 bo->name = NULL;
382 vc4_bo_label(screen, bo, "mesa cache");
383
384 free_stale_bos(screen, time);
385 }
386
387 static struct vc4_bo *
388 vc4_bo_open_handle(struct vc4_screen *screen,
389 uint32_t handle, uint32_t size)
390 {
391 struct vc4_bo *bo;
392
393 assert(size);
394
395 mtx_lock(&screen->bo_handles_mutex);
396
397 bo = util_hash_table_get(screen->bo_handles, (void*)(uintptr_t)handle);
398 if (bo) {
399 vc4_bo_reference(bo);
400 goto done;
401 }
402
403 bo = CALLOC_STRUCT(vc4_bo);
404 pipe_reference_init(&bo->reference, 1);
405 bo->screen = screen;
406 bo->handle = handle;
407 bo->size = size;
408 bo->name = "winsys";
409 bo->private = false;
410
411 #ifdef USE_VC4_SIMULATOR
412 vc4_simulator_open_from_handle(screen->fd, bo->handle, bo->size);
413 bo->map = malloc(bo->size);
414 #endif
415
416 util_hash_table_set(screen->bo_handles, (void *)(uintptr_t)handle, bo);
417
418 done:
419 mtx_unlock(&screen->bo_handles_mutex);
420 return bo;
421 }
422
423 struct vc4_bo *
424 vc4_bo_open_name(struct vc4_screen *screen, uint32_t name)
425 {
426 struct drm_gem_open o = {
427 .name = name
428 };
429 int ret = vc4_ioctl(screen->fd, DRM_IOCTL_GEM_OPEN, &o);
430 if (ret) {
431 fprintf(stderr, "Failed to open bo %d: %s\n",
432 name, strerror(errno));
433 return NULL;
434 }
435
436 return vc4_bo_open_handle(screen, o.handle, o.size);
437 }
438
439 struct vc4_bo *
440 vc4_bo_open_dmabuf(struct vc4_screen *screen, int fd)
441 {
442 uint32_t handle;
443 int ret = drmPrimeFDToHandle(screen->fd, fd, &handle);
444 int size;
445 if (ret) {
446 fprintf(stderr, "Failed to get vc4 handle for dmabuf %d\n", fd);
447 return NULL;
448 }
449
450 /* Determine the size of the bo we were handed. */
451 size = lseek(fd, 0, SEEK_END);
452 if (size == -1) {
453 fprintf(stderr, "Couldn't get size of dmabuf fd %d.\n", fd);
454 return NULL;
455 }
456
457 return vc4_bo_open_handle(screen, handle, size);
458 }
459
460 int
461 vc4_bo_get_dmabuf(struct vc4_bo *bo)
462 {
463 int fd;
464 int ret = drmPrimeHandleToFD(bo->screen->fd, bo->handle,
465 O_CLOEXEC, &fd);
466 if (ret != 0) {
467 fprintf(stderr, "Failed to export gem bo %d to dmabuf\n",
468 bo->handle);
469 return -1;
470 }
471
472 mtx_lock(&bo->screen->bo_handles_mutex);
473 bo->private = false;
474 util_hash_table_set(bo->screen->bo_handles, (void *)(uintptr_t)bo->handle, bo);
475 mtx_unlock(&bo->screen->bo_handles_mutex);
476
477 return fd;
478 }
479
480 struct vc4_bo *
481 vc4_bo_alloc_shader(struct vc4_screen *screen, const void *data, uint32_t size)
482 {
483 struct vc4_bo *bo;
484 int ret;
485
486 bo = CALLOC_STRUCT(vc4_bo);
487 if (!bo)
488 return NULL;
489
490 pipe_reference_init(&bo->reference, 1);
491 bo->screen = screen;
492 bo->size = align(size, 4096);
493 bo->name = "code";
494 bo->private = false; /* Make sure it doesn't go back to the cache. */
495
496 struct drm_vc4_create_shader_bo create = {
497 .size = size,
498 .data = (uintptr_t)data,
499 };
500
501 ret = vc4_ioctl(screen->fd, DRM_IOCTL_VC4_CREATE_SHADER_BO,
502 &create);
503 bo->handle = create.handle;
504
505 if (ret != 0) {
506 fprintf(stderr, "create shader ioctl failure\n");
507 abort();
508 }
509
510 screen->bo_count++;
511 screen->bo_size += bo->size;
512 if (dump_stats) {
513 fprintf(stderr, "Allocated shader %dkb:\n", bo->size / 1024);
514 vc4_bo_dump_stats(screen);
515 }
516
517 return bo;
518 }
519
520 bool
521 vc4_bo_flink(struct vc4_bo *bo, uint32_t *name)
522 {
523 struct drm_gem_flink flink = {
524 .handle = bo->handle,
525 };
526 int ret = vc4_ioctl(bo->screen->fd, DRM_IOCTL_GEM_FLINK, &flink);
527 if (ret) {
528 fprintf(stderr, "Failed to flink bo %d: %s\n",
529 bo->handle, strerror(errno));
530 free(bo);
531 return false;
532 }
533
534 bo->private = false;
535 *name = flink.name;
536
537 return true;
538 }
539
540 static int vc4_wait_seqno_ioctl(int fd, uint64_t seqno, uint64_t timeout_ns)
541 {
542 struct drm_vc4_wait_seqno wait = {
543 .seqno = seqno,
544 .timeout_ns = timeout_ns,
545 };
546 int ret = vc4_ioctl(fd, DRM_IOCTL_VC4_WAIT_SEQNO, &wait);
547 if (ret == -1)
548 return -errno;
549 else
550 return 0;
551
552 }
553
554 bool
555 vc4_wait_seqno(struct vc4_screen *screen, uint64_t seqno, uint64_t timeout_ns,
556 const char *reason)
557 {
558 if (screen->finished_seqno >= seqno)
559 return true;
560
561 if (unlikely(vc4_debug & VC4_DEBUG_PERF) && timeout_ns && reason) {
562 if (vc4_wait_seqno_ioctl(screen->fd, seqno, 0) == -ETIME) {
563 fprintf(stderr, "Blocking on seqno %lld for %s\n",
564 (long long)seqno, reason);
565 }
566 }
567
568 int ret = vc4_wait_seqno_ioctl(screen->fd, seqno, timeout_ns);
569 if (ret) {
570 if (ret != -ETIME) {
571 fprintf(stderr, "wait failed: %d\n", ret);
572 abort();
573 }
574
575 return false;
576 }
577
578 screen->finished_seqno = seqno;
579 return true;
580 }
581
582 static int vc4_wait_bo_ioctl(int fd, uint32_t handle, uint64_t timeout_ns)
583 {
584 struct drm_vc4_wait_bo wait = {
585 .handle = handle,
586 .timeout_ns = timeout_ns,
587 };
588 int ret = vc4_ioctl(fd, DRM_IOCTL_VC4_WAIT_BO, &wait);
589 if (ret == -1)
590 return -errno;
591 else
592 return 0;
593
594 }
595
596 bool
597 vc4_bo_wait(struct vc4_bo *bo, uint64_t timeout_ns, const char *reason)
598 {
599 struct vc4_screen *screen = bo->screen;
600
601 if (unlikely(vc4_debug & VC4_DEBUG_PERF) && timeout_ns && reason) {
602 if (vc4_wait_bo_ioctl(screen->fd, bo->handle, 0) == -ETIME) {
603 fprintf(stderr, "Blocking on %s BO for %s\n",
604 bo->name, reason);
605 }
606 }
607
608 int ret = vc4_wait_bo_ioctl(screen->fd, bo->handle, timeout_ns);
609 if (ret) {
610 if (ret != -ETIME) {
611 fprintf(stderr, "wait failed: %d\n", ret);
612 abort();
613 }
614
615 return false;
616 }
617
618 return true;
619 }
620
621 void *
622 vc4_bo_map_unsynchronized(struct vc4_bo *bo)
623 {
624 uint64_t offset;
625 int ret;
626
627 if (bo->map)
628 return bo->map;
629
630 struct drm_vc4_mmap_bo map;
631 memset(&map, 0, sizeof(map));
632 map.handle = bo->handle;
633 ret = vc4_ioctl(bo->screen->fd, DRM_IOCTL_VC4_MMAP_BO, &map);
634 offset = map.offset;
635 if (ret != 0) {
636 fprintf(stderr, "map ioctl failure\n");
637 abort();
638 }
639
640 bo->map = mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
641 bo->screen->fd, offset);
642 if (bo->map == MAP_FAILED) {
643 fprintf(stderr, "mmap of bo %d (offset 0x%016llx, size %d) failed\n",
644 bo->handle, (long long)offset, bo->size);
645 abort();
646 }
647 VG(VALGRIND_MALLOCLIKE_BLOCK(bo->map, bo->size, 0, false));
648
649 return bo->map;
650 }
651
652 void *
653 vc4_bo_map(struct vc4_bo *bo)
654 {
655 void *map = vc4_bo_map_unsynchronized(bo);
656
657 bool ok = vc4_bo_wait(bo, PIPE_TIMEOUT_INFINITE, "bo map");
658 if (!ok) {
659 fprintf(stderr, "BO wait for map failed\n");
660 abort();
661 }
662
663 return map;
664 }
665
666 void
667 vc4_bufmgr_destroy(struct pipe_screen *pscreen)
668 {
669 struct vc4_screen *screen = vc4_screen(pscreen);
670 struct vc4_bo_cache *cache = &screen->bo_cache;
671
672 vc4_bo_cache_free_all(cache);
673
674 if (dump_stats) {
675 fprintf(stderr, "BO stats after screen destroy:\n");
676 vc4_bo_dump_stats(screen);
677 }
678 }