Path: blob/21.2-virgl/src/gallium/drivers/v3d/v3d_bufmgr.c
4570 views
/*1* Copyright © 2014-2017 Broadcom2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING19* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS20* IN THE SOFTWARE.21*/2223#include <errno.h>24#include <err.h>25#include <sys/mman.h>26#include <fcntl.h>27#include <xf86drm.h>28#include <xf86drmMode.h>2930#include "util/u_hash_table.h"31#include "util/u_memory.h"32#include "util/ralloc.h"3334#include "v3d_context.h"35#include "v3d_screen.h"3637static bool dump_stats = false;3839static void40v3d_bo_cache_free_all(struct v3d_bo_cache *cache);4142static void43v3d_bo_dump_stats(struct v3d_screen *screen)44{45struct v3d_bo_cache *cache = &screen->bo_cache;4647uint32_t cache_count = 0;48uint32_t cache_size = 0;49list_for_each_entry(struct v3d_bo, bo, &cache->time_list, time_list) {50cache_count++;51cache_size += bo->size;52}5354fprintf(stderr, " BOs allocated: %d\n", screen->bo_count);55fprintf(stderr, " BOs size: %dkb\n", screen->bo_size / 1024);56fprintf(stderr, " BOs cached: %d\n", cache_count);57fprintf(stderr, " BOs cached size: %dkb\n", cache_size / 1024);5859if (!list_is_empty(&cache->time_list)) {60struct v3d_bo *first = list_first_entry(&cache->time_list,61struct v3d_bo,62time_list);63struct v3d_bo *last = list_last_entry(&cache->time_list,64struct v3d_bo,65time_list);6667fprintf(stderr, " oldest cache time: %ld\n",68(long)first->free_time);69fprintf(stderr, " newest cache time: %ld\n",70(long)last->free_time);7172struct timespec time;73clock_gettime(CLOCK_MONOTONIC, &time);74fprintf(stderr, " now: %jd\n",75(intmax_t)time.tv_sec);76}77}7879static void80v3d_bo_remove_from_cache(struct v3d_bo_cache *cache, struct v3d_bo *bo)81{82list_del(&bo->time_list);83list_del(&bo->size_list);84}8586static struct v3d_bo *87v3d_bo_from_cache(struct v3d_screen *screen, uint32_t size, const char *name)88{89struct v3d_bo_cache *cache = &screen->bo_cache;90uint32_t page_index = size / 4096 - 1;9192if (cache->size_list_size <= page_index)93return NULL;9495struct v3d_bo *bo = NULL;96mtx_lock(&cache->lock);97if (!list_is_empty(&cache->size_list[page_index])) {98bo = list_first_entry(&cache->size_list[page_index],99struct v3d_bo, size_list);100101/* Check that the BO has gone idle. If not, then we want to102* allocate something new instead, since we assume that the103* user will proceed to CPU map it and fill it with stuff.104*/105if (!v3d_bo_wait(bo, 0, NULL)) {106mtx_unlock(&cache->lock);107return NULL;108}109110pipe_reference_init(&bo->reference, 1);111v3d_bo_remove_from_cache(cache, bo);112113bo->name = name;114}115mtx_unlock(&cache->lock);116return bo;117}118119struct v3d_bo *120v3d_bo_alloc(struct v3d_screen *screen, uint32_t size, const char *name)121{122struct v3d_bo *bo;123int ret;124125/* The CLIF dumping requires that there is no whitespace in the name.126*/127assert(!strchr(name, ' '));128129size = align(size, 4096);130131bo = v3d_bo_from_cache(screen, size, name);132if (bo) {133if (dump_stats) {134fprintf(stderr, "Allocated %s %dkb from cache:\n",135name, size / 1024);136v3d_bo_dump_stats(screen);137}138return bo;139}140141bo = CALLOC_STRUCT(v3d_bo);142if (!bo)143return NULL;144145pipe_reference_init(&bo->reference, 1);146bo->screen = screen;147bo->size = size;148bo->name = name;149bo->private = true;150151retry:152;153154bool cleared_and_retried = false;155struct drm_v3d_create_bo create = {156.size = size157};158159ret = v3d_ioctl(screen->fd, DRM_IOCTL_V3D_CREATE_BO, &create);160bo->handle = create.handle;161bo->offset = create.offset;162163if (ret != 0) {164if (!list_is_empty(&screen->bo_cache.time_list) &&165!cleared_and_retried) {166cleared_and_retried = true;167v3d_bo_cache_free_all(&screen->bo_cache);168goto retry;169}170171free(bo);172return NULL;173}174175screen->bo_count++;176screen->bo_size += bo->size;177if (dump_stats) {178fprintf(stderr, "Allocated %s %dkb:\n", name, size / 1024);179v3d_bo_dump_stats(screen);180}181182return bo;183}184185void186v3d_bo_last_unreference(struct v3d_bo *bo)187{188struct v3d_screen *screen = bo->screen;189190struct timespec time;191clock_gettime(CLOCK_MONOTONIC, &time);192mtx_lock(&screen->bo_cache.lock);193v3d_bo_last_unreference_locked_timed(bo, time.tv_sec);194mtx_unlock(&screen->bo_cache.lock);195}196197static void198v3d_bo_free(struct v3d_bo *bo)199{200struct v3d_screen *screen = bo->screen;201202if (bo->map) {203if (using_v3d_simulator && bo->name &&204strcmp(bo->name, "winsys") == 0) {205free(bo->map);206} else {207munmap(bo->map, bo->size);208VG(VALGRIND_FREELIKE_BLOCK(bo->map, 0));209}210}211212struct drm_gem_close c;213memset(&c, 0, sizeof(c));214c.handle = bo->handle;215int ret = v3d_ioctl(screen->fd, DRM_IOCTL_GEM_CLOSE, &c);216if (ret != 0)217fprintf(stderr, "close object %d: %s\n", bo->handle, strerror(errno));218219screen->bo_count--;220screen->bo_size -= bo->size;221222if (dump_stats) {223fprintf(stderr, "Freed %s%s%dkb:\n",224bo->name ? bo->name : "",225bo->name ? " " : "",226bo->size / 1024);227v3d_bo_dump_stats(screen);228}229230free(bo);231}232233static void234free_stale_bos(struct v3d_screen *screen, time_t time)235{236struct v3d_bo_cache *cache = &screen->bo_cache;237bool freed_any = false;238239list_for_each_entry_safe(struct v3d_bo, bo, &cache->time_list,240time_list) {241/* If it's more than a second old, free it. */242if (time - bo->free_time > 2) {243if (dump_stats && !freed_any) {244fprintf(stderr, "Freeing stale BOs:\n");245v3d_bo_dump_stats(screen);246freed_any = true;247}248v3d_bo_remove_from_cache(cache, bo);249v3d_bo_free(bo);250} else {251break;252}253}254255if (dump_stats && freed_any) {256fprintf(stderr, "Freed stale BOs:\n");257v3d_bo_dump_stats(screen);258}259}260261static void262v3d_bo_cache_free_all(struct v3d_bo_cache *cache)263{264mtx_lock(&cache->lock);265list_for_each_entry_safe(struct v3d_bo, bo, &cache->time_list,266time_list) {267v3d_bo_remove_from_cache(cache, bo);268v3d_bo_free(bo);269}270mtx_unlock(&cache->lock);271}272273void274v3d_bo_last_unreference_locked_timed(struct v3d_bo *bo, time_t time)275{276struct v3d_screen *screen = bo->screen;277struct v3d_bo_cache *cache = &screen->bo_cache;278uint32_t page_index = bo->size / 4096 - 1;279280if (!bo->private) {281v3d_bo_free(bo);282return;283}284285if (cache->size_list_size <= page_index) {286struct list_head *new_list =287ralloc_array(screen, struct list_head, page_index + 1);288289/* Move old list contents over (since the array has moved, and290* therefore the pointers to the list heads have to change).291*/292for (int i = 0; i < cache->size_list_size; i++) {293struct list_head *old_head = &cache->size_list[i];294if (list_is_empty(old_head))295list_inithead(&new_list[i]);296else {297new_list[i].next = old_head->next;298new_list[i].prev = old_head->prev;299new_list[i].next->prev = &new_list[i];300new_list[i].prev->next = &new_list[i];301}302}303for (int i = cache->size_list_size; i < page_index + 1; i++)304list_inithead(&new_list[i]);305306cache->size_list = new_list;307cache->size_list_size = page_index + 1;308}309310bo->free_time = time;311list_addtail(&bo->size_list, &cache->size_list[page_index]);312list_addtail(&bo->time_list, &cache->time_list);313if (dump_stats) {314fprintf(stderr, "Freed %s %dkb to cache:\n",315bo->name, bo->size / 1024);316v3d_bo_dump_stats(screen);317}318bo->name = NULL;319320free_stale_bos(screen, time);321}322323static struct v3d_bo *324v3d_bo_open_handle(struct v3d_screen *screen,325uint32_t handle, uint32_t size)326{327struct v3d_bo *bo;328329assert(size);330331mtx_lock(&screen->bo_handles_mutex);332333bo = util_hash_table_get(screen->bo_handles, (void*)(uintptr_t)handle);334if (bo) {335pipe_reference(NULL, &bo->reference);336goto done;337}338339bo = CALLOC_STRUCT(v3d_bo);340pipe_reference_init(&bo->reference, 1);341bo->screen = screen;342bo->handle = handle;343bo->size = size;344bo->name = "winsys";345bo->private = false;346347#ifdef USE_V3D_SIMULATOR348v3d_simulator_open_from_handle(screen->fd, bo->handle, bo->size);349bo->map = malloc(bo->size);350#endif351352struct drm_v3d_get_bo_offset get = {353.handle = handle,354};355int ret = v3d_ioctl(screen->fd, DRM_IOCTL_V3D_GET_BO_OFFSET, &get);356if (ret) {357fprintf(stderr, "Failed to get BO offset: %s\n",358strerror(errno));359free(bo->map);360free(bo);361bo = NULL;362goto done;363}364bo->offset = get.offset;365assert(bo->offset != 0);366367_mesa_hash_table_insert(screen->bo_handles, (void *)(uintptr_t)handle, bo);368369screen->bo_count++;370screen->bo_size += bo->size;371372done:373mtx_unlock(&screen->bo_handles_mutex);374return bo;375}376377struct v3d_bo *378v3d_bo_open_name(struct v3d_screen *screen, uint32_t name)379{380struct drm_gem_open o = {381.name = name382};383int ret = v3d_ioctl(screen->fd, DRM_IOCTL_GEM_OPEN, &o);384if (ret) {385fprintf(stderr, "Failed to open bo %d: %s\n",386name, strerror(errno));387return NULL;388}389390return v3d_bo_open_handle(screen, o.handle, o.size);391}392393struct v3d_bo *394v3d_bo_open_dmabuf(struct v3d_screen *screen, int fd)395{396uint32_t handle;397int ret = drmPrimeFDToHandle(screen->fd, fd, &handle);398int size;399if (ret) {400fprintf(stderr, "Failed to get v3d handle for dmabuf %d\n", fd);401return NULL;402}403404/* Determine the size of the bo we were handed. */405size = lseek(fd, 0, SEEK_END);406if (size == -1) {407fprintf(stderr, "Couldn't get size of dmabuf fd %d.\n", fd);408return NULL;409}410411return v3d_bo_open_handle(screen, handle, size);412}413414int415v3d_bo_get_dmabuf(struct v3d_bo *bo)416{417int fd;418int ret = drmPrimeHandleToFD(bo->screen->fd, bo->handle,419O_CLOEXEC, &fd);420if (ret != 0) {421fprintf(stderr, "Failed to export gem bo %d to dmabuf\n",422bo->handle);423return -1;424}425426mtx_lock(&bo->screen->bo_handles_mutex);427bo->private = false;428_mesa_hash_table_insert(bo->screen->bo_handles, (void *)(uintptr_t)bo->handle, bo);429mtx_unlock(&bo->screen->bo_handles_mutex);430431return fd;432}433434bool435v3d_bo_flink(struct v3d_bo *bo, uint32_t *name)436{437struct drm_gem_flink flink = {438.handle = bo->handle,439};440int ret = v3d_ioctl(bo->screen->fd, DRM_IOCTL_GEM_FLINK, &flink);441if (ret) {442fprintf(stderr, "Failed to flink bo %d: %s\n",443bo->handle, strerror(errno));444free(bo);445return false;446}447448bo->private = false;449*name = flink.name;450451return true;452}453454static int v3d_wait_bo_ioctl(int fd, uint32_t handle, uint64_t timeout_ns)455{456struct drm_v3d_wait_bo wait = {457.handle = handle,458.timeout_ns = timeout_ns,459};460int ret = v3d_ioctl(fd, DRM_IOCTL_V3D_WAIT_BO, &wait);461if (ret == -1)462return -errno;463else464return 0;465466}467468bool469v3d_bo_wait(struct v3d_bo *bo, uint64_t timeout_ns, const char *reason)470{471struct v3d_screen *screen = bo->screen;472473if (unlikely(V3D_DEBUG & V3D_DEBUG_PERF) && timeout_ns && reason) {474if (v3d_wait_bo_ioctl(screen->fd, bo->handle, 0) == -ETIME) {475fprintf(stderr, "Blocking on %s BO for %s\n",476bo->name, reason);477}478}479480int ret = v3d_wait_bo_ioctl(screen->fd, bo->handle, timeout_ns);481if (ret) {482if (ret != -ETIME) {483fprintf(stderr, "wait failed: %d\n", ret);484abort();485}486487return false;488}489490return true;491}492493void *494v3d_bo_map_unsynchronized(struct v3d_bo *bo)495{496uint64_t offset;497int ret;498499if (bo->map)500return bo->map;501502struct drm_v3d_mmap_bo map;503memset(&map, 0, sizeof(map));504map.handle = bo->handle;505ret = v3d_ioctl(bo->screen->fd, DRM_IOCTL_V3D_MMAP_BO, &map);506offset = map.offset;507if (ret != 0) {508fprintf(stderr, "map ioctl failure\n");509abort();510}511512bo->map = mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,513bo->screen->fd, offset);514if (bo->map == MAP_FAILED) {515fprintf(stderr, "mmap of bo %d (offset 0x%016llx, size %d) failed\n",516bo->handle, (long long)offset, bo->size);517abort();518}519VG(VALGRIND_MALLOCLIKE_BLOCK(bo->map, bo->size, 0, false));520521return bo->map;522}523524void *525v3d_bo_map(struct v3d_bo *bo)526{527void *map = v3d_bo_map_unsynchronized(bo);528529bool ok = v3d_bo_wait(bo, PIPE_TIMEOUT_INFINITE, "bo map");530if (!ok) {531fprintf(stderr, "BO wait for map failed\n");532abort();533}534535return map;536}537538void539v3d_bufmgr_destroy(struct pipe_screen *pscreen)540{541struct v3d_screen *screen = v3d_screen(pscreen);542struct v3d_bo_cache *cache = &screen->bo_cache;543544v3d_bo_cache_free_all(cache);545546if (dump_stats) {547fprintf(stderr, "BO stats after screen destroy:\n");548v3d_bo_dump_stats(screen);549}550}551552553