X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fgallium%2Fdrivers%2Fvc4%2Fvc4_bufmgr.c;h=cf6a5114b876ff32bd88bc4339138cbcb1d2f981;hb=774a556b6dc0d49f9f29c438349a66e69062e6e4;hp=8f9d9c3ff77d0cf2cccfb88ea4a04748cc3b40ef;hpb=c821ccf0e3a051e5e867792898ae9b8f08e4601a;p=mesa.git diff --git a/src/gallium/drivers/vc4/vc4_bufmgr.c b/src/gallium/drivers/vc4/vc4_bufmgr.c index 8f9d9c3ff77..cf6a5114b87 100644 --- a/src/gallium/drivers/vc4/vc4_bufmgr.c +++ b/src/gallium/drivers/vc4/vc4_bufmgr.c @@ -1,5 +1,5 @@ /* - * Copyright © 2014 Broadcom + * Copyright © 2014-2015 Broadcom * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -28,14 +28,64 @@ #include #include +#include "util/u_hash_table.h" #include "util/u_memory.h" #include "util/ralloc.h" #include "vc4_context.h" #include "vc4_screen.h" -#define container_of(ptr, type, field) \ - (type*)((char*)ptr - offsetof(type, field)) +#ifdef HAVE_VALGRIND +#include +#include +#define VG(x) x +#else +#define VG(x) +#endif + +static bool dump_stats = false; + +static void +vc4_bo_cache_free_all(struct vc4_bo_cache *cache); + +static void +vc4_bo_dump_stats(struct vc4_screen *screen) +{ + struct vc4_bo_cache *cache = &screen->bo_cache; + + fprintf(stderr, " BOs allocated: %d\n", screen->bo_count); + fprintf(stderr, " BOs size: %dkb\n", screen->bo_size / 1024); + fprintf(stderr, " BOs cached: %d\n", cache->bo_count); + fprintf(stderr, " BOs cached size: %dkb\n", cache->bo_size / 1024); + + if (!list_empty(&cache->time_list)) { + struct vc4_bo *first = LIST_ENTRY(struct vc4_bo, + cache->time_list.next, + time_list); + struct vc4_bo *last = LIST_ENTRY(struct vc4_bo, + cache->time_list.prev, + time_list); + + fprintf(stderr, " oldest cache time: %ld\n", + (long)first->free_time); + fprintf(stderr, " newest cache time: %ld\n", + (long)last->free_time); + + struct timespec time; + clock_gettime(CLOCK_MONOTONIC, &time); + fprintf(stderr, " now: %ld\n", + time.tv_sec); + } +} + +static void +vc4_bo_remove_from_cache(struct vc4_bo_cache *cache, struct vc4_bo *bo) +{ + list_del(&bo->time_list); + list_del(&bo->size_list); + cache->bo_count--; + cache->bo_size -= bo->size; +} static struct vc4_bo * vc4_bo_from_cache(struct vc4_screen *screen, uint32_t size, const char *name) @@ -48,12 +98,21 @@ vc4_bo_from_cache(struct vc4_screen *screen, uint32_t size, const char *name) struct vc4_bo *bo = NULL; pipe_mutex_lock(cache->lock); - if (!is_empty_list(&cache->size_list[page_index])) { - struct simple_node *node = last_elem(&cache->size_list[page_index]); - bo = container_of(node, struct vc4_bo, size_list); + if (!list_empty(&cache->size_list[page_index])) { + bo = LIST_ENTRY(struct vc4_bo, cache->size_list[page_index].next, + size_list); + + /* Check that the BO has gone idle. If not, then we want to + * allocate something new instead, since we assume that the + * user will proceed to CPU map it and fill it with stuff. + */ + if (!vc4_bo_wait(bo, 0, NULL)) { + pipe_mutex_unlock(cache->lock); + return NULL; + } + pipe_reference_init(&bo->reference, 1); - remove_from_list(&bo->time_list); - remove_from_list(&bo->size_list); + vc4_bo_remove_from_cache(cache, bo); bo->name = name; } @@ -70,8 +129,14 @@ vc4_bo_alloc(struct vc4_screen *screen, uint32_t size, const char *name) size = align(size, 4096); bo = vc4_bo_from_cache(screen, size, name); - if (bo) + if (bo) { + if (dump_stats) { + fprintf(stderr, "Allocated %s %dkb from cache:\n", + name, size / 1024); + vc4_bo_dump_stats(screen); + } return bo; + } bo = CALLOC_STRUCT(vc4_bo); if (!bo) @@ -83,6 +148,8 @@ vc4_bo_alloc(struct vc4_screen *screen, uint32_t size, const char *name) bo->name = name; bo->private = true; + bool cleared_and_retried = false; +retry: if (!using_vc4_simulator) { struct drm_vc4_create_bo create; memset(&create, 0, sizeof(create)); @@ -104,8 +171,22 @@ vc4_bo_alloc(struct vc4_screen *screen, uint32_t size, const char *name) assert(create.size >= size); } if (ret != 0) { - fprintf(stderr, "create ioctl failure\n"); - abort(); + if (!list_empty(&screen->bo_cache.time_list) && + !cleared_and_retried) { + cleared_and_retried = true; + vc4_bo_cache_free_all(&screen->bo_cache); + goto retry; + } + + free(bo); + return NULL; + } + + screen->bo_count++; + screen->bo_size += bo->size; + if (dump_stats) { + fprintf(stderr, "Allocated %s %dkb:\n", name, size / 1024); + vc4_bo_dump_stats(screen); } return bo; @@ -136,6 +217,7 @@ vc4_bo_free(struct vc4_bo *bo) } #endif munmap(bo->map, bo->size); + VG(VALGRIND_FREELIKE_BLOCK(bo->map, 0)); } struct drm_gem_close c; @@ -145,26 +227,59 @@ vc4_bo_free(struct vc4_bo *bo) if (ret != 0) fprintf(stderr, "close object %d: %s\n", bo->handle, strerror(errno)); + screen->bo_count--; + screen->bo_size -= bo->size; + + if (dump_stats) { + fprintf(stderr, "Freed %s%s%dkb:\n", + bo->name ? bo->name : "", + bo->name ? " " : "", + bo->size / 1024); + vc4_bo_dump_stats(screen); + } + free(bo); } static void free_stale_bos(struct vc4_screen *screen, time_t time) { - while (!is_empty_list(&screen->bo_cache.time_list)) { - struct simple_node *node = - first_elem(&screen->bo_cache.time_list); - struct vc4_bo *bo = container_of(node, struct vc4_bo, time_list); + struct vc4_bo_cache *cache = &screen->bo_cache; + bool freed_any = false; + + list_for_each_entry_safe(struct vc4_bo, bo, &cache->time_list, + time_list) { + if (dump_stats && !freed_any) { + fprintf(stderr, "Freeing stale BOs:\n"); + vc4_bo_dump_stats(screen); + freed_any = true; + } /* If it's more than a second old, free it. */ if (time - bo->free_time > 2) { - remove_from_list(&bo->time_list); - remove_from_list(&bo->size_list); + vc4_bo_remove_from_cache(cache, bo); vc4_bo_free(bo); } else { break; } } + + if (dump_stats && freed_any) { + fprintf(stderr, "Freed stale BOs:\n"); + vc4_bo_dump_stats(screen); + } +} + +static void +vc4_bo_cache_free_all(struct vc4_bo_cache *cache) +{ + pipe_mutex_lock(cache->lock); + list_for_each_entry_safe(struct vc4_bo, bo, &cache->time_list, + time_list) { + vc4_bo_remove_from_cache(cache, bo); + vc4_bo_free(bo); + } + pipe_mutex_unlock(cache->lock); } void @@ -180,16 +295,16 @@ vc4_bo_last_unreference_locked_timed(struct vc4_bo *bo, time_t time) } if (cache->size_list_size <= page_index) { - struct simple_node *new_list = - ralloc_array(screen, struct simple_node, page_index + 1); + struct list_head *new_list = + ralloc_array(screen, struct list_head, page_index + 1); /* Move old list contents over (since the array has moved, and - * therefore the pointers to the list heads have to change. + * therefore the pointers to the list heads have to change). */ for (int i = 0; i < cache->size_list_size; i++) { - struct simple_node *old_head = &cache->size_list[i]; - if (is_empty_list(old_head)) - make_empty_list(&new_list[i]); + struct list_head *old_head = &cache->size_list[i]; + if (list_empty(old_head)) + list_inithead(&new_list[i]); else { new_list[i].next = old_head->next; new_list[i].prev = old_head->prev; @@ -198,15 +313,23 @@ vc4_bo_last_unreference_locked_timed(struct vc4_bo *bo, time_t time) } } for (int i = cache->size_list_size; i < page_index + 1; i++) - make_empty_list(&new_list[i]); + list_inithead(&new_list[i]); cache->size_list = new_list; cache->size_list_size = page_index + 1; } bo->free_time = time; - insert_at_tail(&cache->size_list[page_index], &bo->size_list); - insert_at_tail(&cache->time_list, &bo->time_list); + list_addtail(&bo->size_list, &cache->size_list[page_index]); + list_addtail(&bo->time_list, &cache->time_list); + cache->bo_count++; + cache->bo_size += bo->size; + if (dump_stats) { + fprintf(stderr, "Freed %s %dkb to cache:\n", + bo->name, bo->size / 1024); + vc4_bo_dump_stats(screen); + } + bo->name = NULL; free_stale_bos(screen, time); } @@ -216,10 +339,19 @@ vc4_bo_open_handle(struct vc4_screen *screen, uint32_t winsys_stride, uint32_t handle, uint32_t size) { - struct vc4_bo *bo = CALLOC_STRUCT(vc4_bo); + struct vc4_bo *bo; assert(size); + pipe_mutex_lock(screen->bo_handles_mutex); + + bo = util_hash_table_get(screen->bo_handles, (void*)(uintptr_t)handle); + if (bo) { + pipe_reference(NULL, &bo->reference); + goto done; + } + + bo = CALLOC_STRUCT(vc4_bo); pipe_reference_init(&bo->reference, 1); bo->screen = screen; bo->handle = handle; @@ -234,6 +366,10 @@ vc4_bo_open_handle(struct vc4_screen *screen, bo->map = malloc(bo->size); #endif + util_hash_table_set(screen->bo_handles, (void *)(uintptr_t)handle, bo); + +done: + pipe_mutex_unlock(screen->bo_handles_mutex); return bo; } @@ -286,21 +422,67 @@ vc4_bo_get_dmabuf(struct vc4_bo *bo) bo->handle); return -1; } + + pipe_mutex_lock(bo->screen->bo_handles_mutex); bo->private = false; + util_hash_table_set(bo->screen->bo_handles, (void *)(uintptr_t)bo->handle, bo); + pipe_mutex_unlock(bo->screen->bo_handles_mutex); return fd; } struct vc4_bo * -vc4_bo_alloc_mem(struct vc4_screen *screen, const void *data, uint32_t size, - const char *name) +vc4_bo_alloc_shader(struct vc4_screen *screen, const void *data, uint32_t size) { - void *map; struct vc4_bo *bo; + int ret; + + bo = CALLOC_STRUCT(vc4_bo); + if (!bo) + return NULL; + + pipe_reference_init(&bo->reference, 1); + bo->screen = screen; + bo->size = align(size, 4096); + bo->name = "code"; + bo->private = false; /* Make sure it doesn't go back to the cache. */ + + if (!using_vc4_simulator) { + struct drm_vc4_create_shader_bo create = { + .size = size, + .data = (uintptr_t)data, + }; + + ret = drmIoctl(screen->fd, DRM_IOCTL_VC4_CREATE_SHADER_BO, + &create); + bo->handle = create.handle; + } else { + struct drm_mode_create_dumb create; + memset(&create, 0, sizeof(create)); + + create.width = 128; + create.bpp = 8; + create.height = (size + 127) / 128; + + ret = drmIoctl(screen->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create); + bo->handle = create.handle; + assert(create.size >= size); + + vc4_bo_map(bo); + memcpy(bo->map, data, size); + } + if (ret != 0) { + fprintf(stderr, "create shader ioctl failure\n"); + abort(); + } + + screen->bo_count++; + screen->bo_size += bo->size; + if (dump_stats) { + fprintf(stderr, "Allocated shader %dkb:\n", bo->size / 1024); + vc4_bo_dump_stats(screen); + } - bo = vc4_bo_alloc(screen, size, name); - map = vc4_bo_map(bo); - memcpy(map, data, size); return bo; } @@ -324,63 +506,91 @@ vc4_bo_flink(struct vc4_bo *bo, uint32_t *name) return true; } +static int vc4_wait_seqno_ioctl(int fd, uint64_t seqno, uint64_t timeout_ns) +{ + if (using_vc4_simulator) + return 0; + + struct drm_vc4_wait_seqno wait = { + .seqno = seqno, + .timeout_ns = timeout_ns, + }; + int ret = drmIoctl(fd, DRM_IOCTL_VC4_WAIT_SEQNO, &wait); + if (ret == -1) + return -errno; + else + return 0; + +} + bool -vc4_wait_seqno(struct vc4_screen *screen, uint64_t seqno, uint64_t timeout_ns) +vc4_wait_seqno(struct vc4_screen *screen, uint64_t seqno, uint64_t timeout_ns, + const char *reason) { if (screen->finished_seqno >= seqno) return true; - struct drm_vc4_wait_seqno wait; - memset(&wait, 0, sizeof(wait)); - wait.seqno = seqno; - wait.timeout_ns = timeout_ns; - - int ret; - if (!using_vc4_simulator) - ret = drmIoctl(screen->fd, DRM_IOCTL_VC4_WAIT_SEQNO, &wait); - else { - wait.seqno = screen->finished_seqno; - ret = 0; + if (unlikely(vc4_debug & VC4_DEBUG_PERF) && timeout_ns && reason) { + if (vc4_wait_seqno_ioctl(screen->fd, seqno, 0) == -ETIME) { + fprintf(stderr, "Blocking on seqno %lld for %s\n", + (long long)seqno, reason); + } } - if (ret == 0) { - screen->finished_seqno = wait.seqno; - return true; - } + int ret = vc4_wait_seqno_ioctl(screen->fd, seqno, timeout_ns); + if (ret) { + if (ret != -ETIME) { + fprintf(stderr, "wait failed: %d\n", ret); + abort(); + } - if (errno != ETIME) { - fprintf(stderr, "wait failed: %d\n", ret); - abort(); + return false; } - return false; + screen->finished_seqno = seqno; + return true; +} + +static int vc4_wait_bo_ioctl(int fd, uint32_t handle, uint64_t timeout_ns) +{ + if (using_vc4_simulator) + return 0; + + struct drm_vc4_wait_bo wait = { + .handle = handle, + .timeout_ns = timeout_ns, + }; + int ret = drmIoctl(fd, DRM_IOCTL_VC4_WAIT_BO, &wait); + if (ret == -1) + return -errno; + else + return 0; + } bool -vc4_bo_wait(struct vc4_bo *bo, uint64_t timeout_ns) +vc4_bo_wait(struct vc4_bo *bo, uint64_t timeout_ns, const char *reason) { struct vc4_screen *screen = bo->screen; - struct drm_vc4_wait_bo wait; - memset(&wait, 0, sizeof(wait)); - wait.handle = bo->handle; - wait.timeout_ns = timeout_ns; - - int ret; - if (!using_vc4_simulator) - ret = drmIoctl(screen->fd, DRM_IOCTL_VC4_WAIT_BO, &wait); - else - ret = 0; + if (unlikely(vc4_debug & VC4_DEBUG_PERF) && timeout_ns && reason) { + if (vc4_wait_bo_ioctl(screen->fd, bo->handle, 0) == -ETIME) { + fprintf(stderr, "Blocking on %s BO for %s\n", + bo->name, reason); + } + } - if (ret == 0) - return true; + int ret = vc4_wait_bo_ioctl(screen->fd, bo->handle, timeout_ns); + if (ret) { + if (ret != -ETIME) { + fprintf(stderr, "wait failed: %d\n", ret); + abort(); + } - if (errno != ETIME) { - fprintf(stderr, "wait failed: %d\n", ret); - abort(); + return false; } - return false; + return true; } void * @@ -417,6 +627,7 @@ vc4_bo_map_unsynchronized(struct vc4_bo *bo) bo->handle, (long long)offset, bo->size); abort(); } + VG(VALGRIND_MALLOCLIKE_BLOCK(bo->map, bo->size, 0, false)); return bo->map; } @@ -426,7 +637,7 @@ vc4_bo_map(struct vc4_bo *bo) { void *map = vc4_bo_map_unsynchronized(bo); - bool ok = vc4_bo_wait(bo, PIPE_TIMEOUT_INFINITE); + bool ok = vc4_bo_wait(bo, PIPE_TIMEOUT_INFINITE, "bo map"); if (!ok) { fprintf(stderr, "BO wait for map failed\n"); abort(); @@ -441,12 +652,10 @@ vc4_bufmgr_destroy(struct pipe_screen *pscreen) struct vc4_screen *screen = vc4_screen(pscreen); struct vc4_bo_cache *cache = &screen->bo_cache; - while (!is_empty_list(&cache->time_list)) { - struct simple_node *node = first_elem(&cache->time_list); - struct vc4_bo *bo = container_of(node, struct vc4_bo, time_list); + vc4_bo_cache_free_all(cache); - remove_from_list(&bo->time_list); - remove_from_list(&bo->size_list); - vc4_bo_free(bo); + if (dump_stats) { + fprintf(stderr, "BO stats after screen destroy:\n"); + vc4_bo_dump_stats(screen); } }