Path: blob/21.2-virgl/src/gallium/drivers/vc4/vc4_bufmgr.c
4570 views
/*1* Copyright © 2014-2015 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/u_string.h"33#include "util/ralloc.h"3435#include "vc4_context.h"36#include "vc4_screen.h"3738static bool dump_stats = false;3940static void41vc4_bo_cache_free_all(struct vc4_bo_cache *cache);4243void44vc4_bo_debug_describe(char* buf, const struct vc4_bo *ptr)45{46sprintf(buf, "vc4_bo<%s,%u,%u>", ptr->name ? ptr->name : "?",47ptr->handle, ptr->size);48}4950void51vc4_bo_label(struct vc4_screen *screen, struct vc4_bo *bo, const char *fmt, ...)52{53/* Perform BO labeling by default on debug builds (so that you get54* whole-system allocation information), or if VC4_DEBUG=surf is set55* (for debugging a single app's allocation).56*/57#ifndef DEBUG58if (!(vc4_debug & VC4_DEBUG_SURFACE))59return;60#endif61va_list va;62va_start(va, fmt);63char *name = ralloc_vasprintf(NULL, fmt, va);64va_end(va);6566struct drm_vc4_label_bo label = {67.handle = bo->handle,68.len = strlen(name),69.name = (uintptr_t)name,70};71vc4_ioctl(screen->fd, DRM_IOCTL_VC4_LABEL_BO, &label);7273ralloc_free(name);74}7576static void77vc4_bo_dump_stats(struct vc4_screen *screen)78{79struct vc4_bo_cache *cache = &screen->bo_cache;8081fprintf(stderr, " BOs allocated: %d\n", screen->bo_count);82fprintf(stderr, " BOs size: %dkb\n", screen->bo_size / 1024);83fprintf(stderr, " BOs cached: %d\n", cache->bo_count);84fprintf(stderr, " BOs cached size: %dkb\n", cache->bo_size / 1024);8586if (!list_is_empty(&cache->time_list)) {87struct vc4_bo *first = LIST_ENTRY(struct vc4_bo,88cache->time_list.next,89time_list);90struct vc4_bo *last = LIST_ENTRY(struct vc4_bo,91cache->time_list.prev,92time_list);9394fprintf(stderr, " oldest cache time: %ld\n",95(long)first->free_time);96fprintf(stderr, " newest cache time: %ld\n",97(long)last->free_time);9899struct timespec time;100clock_gettime(CLOCK_MONOTONIC, &time);101fprintf(stderr, " now: %jd\n",102(intmax_t)time.tv_sec);103}104}105106static void107vc4_bo_remove_from_cache(struct vc4_bo_cache *cache, struct vc4_bo *bo)108{109list_del(&bo->time_list);110list_del(&bo->size_list);111cache->bo_count--;112cache->bo_size -= bo->size;113}114115static void vc4_bo_purgeable(struct vc4_bo *bo)116{117struct drm_vc4_gem_madvise arg = {118.handle = bo->handle,119.madv = VC4_MADV_DONTNEED,120};121122if (bo->screen->has_madvise)123vc4_ioctl(bo->screen->fd, DRM_IOCTL_VC4_GEM_MADVISE, &arg);124}125126static bool vc4_bo_unpurgeable(struct vc4_bo *bo)127{128struct drm_vc4_gem_madvise arg = {129.handle = bo->handle,130.madv = VC4_MADV_WILLNEED,131};132133if (!bo->screen->has_madvise)134return true;135136if (vc4_ioctl(bo->screen->fd, DRM_IOCTL_VC4_GEM_MADVISE, &arg))137return false;138139return arg.retained;140}141142static void143vc4_bo_free(struct vc4_bo *bo)144{145struct vc4_screen *screen = bo->screen;146147if (bo->map) {148if (using_vc4_simulator && bo->name &&149strcmp(bo->name, "winsys") == 0) {150free(bo->map);151} else {152munmap(bo->map, bo->size);153VG(VALGRIND_FREELIKE_BLOCK(bo->map, 0));154}155}156157struct drm_gem_close c;158memset(&c, 0, sizeof(c));159c.handle = bo->handle;160int ret = vc4_ioctl(screen->fd, DRM_IOCTL_GEM_CLOSE, &c);161if (ret != 0)162fprintf(stderr, "close object %d: %s\n", bo->handle, strerror(errno));163164screen->bo_count--;165screen->bo_size -= bo->size;166167if (dump_stats) {168fprintf(stderr, "Freed %s%s%dkb:\n",169bo->name ? bo->name : "",170bo->name ? " " : "",171bo->size / 1024);172vc4_bo_dump_stats(screen);173}174175free(bo);176}177178static struct vc4_bo *179vc4_bo_from_cache(struct vc4_screen *screen, uint32_t size, const char *name)180{181struct vc4_bo_cache *cache = &screen->bo_cache;182uint32_t page_index = size / 4096 - 1;183struct vc4_bo *iter, *tmp, *bo = NULL;184185if (cache->size_list_size <= page_index)186return NULL;187188mtx_lock(&cache->lock);189LIST_FOR_EACH_ENTRY_SAFE(iter, tmp, &cache->size_list[page_index],190size_list) {191/* Check that the BO has gone idle. If not, then none of the192* other BOs (pushed to the list after later rendering) are193* likely to be idle, either.194*/195if (!vc4_bo_wait(iter, 0, NULL))196break;197198if (!vc4_bo_unpurgeable(iter)) {199/* The BO has been purged. Free it and try to find200* another one in the cache.201*/202vc4_bo_remove_from_cache(cache, iter);203vc4_bo_free(iter);204continue;205}206207bo = iter;208pipe_reference_init(&bo->reference, 1);209vc4_bo_remove_from_cache(cache, bo);210211vc4_bo_label(screen, bo, "%s", name);212bo->name = name;213break;214}215mtx_unlock(&cache->lock);216return bo;217}218219struct vc4_bo *220vc4_bo_alloc(struct vc4_screen *screen, uint32_t size, const char *name)221{222bool cleared_and_retried = false;223struct drm_vc4_create_bo create;224struct vc4_bo *bo;225int ret;226227size = align(size, 4096);228229bo = vc4_bo_from_cache(screen, size, name);230if (bo) {231if (dump_stats) {232fprintf(stderr, "Allocated %s %dkb from cache:\n",233name, size / 1024);234vc4_bo_dump_stats(screen);235}236return bo;237}238239bo = CALLOC_STRUCT(vc4_bo);240if (!bo)241return NULL;242243pipe_reference_init(&bo->reference, 1);244bo->screen = screen;245bo->size = size;246bo->name = name;247bo->private = true;248249retry:250memset(&create, 0, sizeof(create));251create.size = size;252253ret = vc4_ioctl(screen->fd, DRM_IOCTL_VC4_CREATE_BO, &create);254bo->handle = create.handle;255256if (ret != 0) {257if (!list_is_empty(&screen->bo_cache.time_list) &&258!cleared_and_retried) {259cleared_and_retried = true;260vc4_bo_cache_free_all(&screen->bo_cache);261goto retry;262}263264free(bo);265return NULL;266}267268screen->bo_count++;269screen->bo_size += bo->size;270if (dump_stats) {271fprintf(stderr, "Allocated %s %dkb:\n", name, size / 1024);272vc4_bo_dump_stats(screen);273}274275vc4_bo_label(screen, bo, "%s", name);276277return bo;278}279280void281vc4_bo_last_unreference(struct vc4_bo *bo)282{283struct vc4_screen *screen = bo->screen;284285struct timespec time;286clock_gettime(CLOCK_MONOTONIC, &time);287mtx_lock(&screen->bo_cache.lock);288vc4_bo_last_unreference_locked_timed(bo, time.tv_sec);289mtx_unlock(&screen->bo_cache.lock);290}291292static void293free_stale_bos(struct vc4_screen *screen, time_t time)294{295struct vc4_bo_cache *cache = &screen->bo_cache;296bool freed_any = false;297298list_for_each_entry_safe(struct vc4_bo, bo, &cache->time_list,299time_list) {300if (dump_stats && !freed_any) {301fprintf(stderr, "Freeing stale BOs:\n");302vc4_bo_dump_stats(screen);303freed_any = true;304}305306/* If it's more than a second old, free it. */307if (time - bo->free_time > 2) {308vc4_bo_remove_from_cache(cache, bo);309vc4_bo_free(bo);310} else {311break;312}313}314315if (dump_stats && freed_any) {316fprintf(stderr, "Freed stale BOs:\n");317vc4_bo_dump_stats(screen);318}319}320321static void322vc4_bo_cache_free_all(struct vc4_bo_cache *cache)323{324mtx_lock(&cache->lock);325list_for_each_entry_safe(struct vc4_bo, bo, &cache->time_list,326time_list) {327vc4_bo_remove_from_cache(cache, bo);328vc4_bo_free(bo);329}330mtx_unlock(&cache->lock);331}332333void334vc4_bo_last_unreference_locked_timed(struct vc4_bo *bo, time_t time)335{336struct vc4_screen *screen = bo->screen;337struct vc4_bo_cache *cache = &screen->bo_cache;338uint32_t page_index = bo->size / 4096 - 1;339340if (!bo->private) {341vc4_bo_free(bo);342return;343}344345if (cache->size_list_size <= page_index) {346struct list_head *new_list =347ralloc_array(screen, struct list_head, page_index + 1);348349/* Move old list contents over (since the array has moved, and350* therefore the pointers to the list heads have to change).351*/352for (int i = 0; i < cache->size_list_size; i++)353list_replace(&cache->size_list[i], &new_list[i]);354for (int i = cache->size_list_size; i < page_index + 1; i++)355list_inithead(&new_list[i]);356357cache->size_list = new_list;358cache->size_list_size = page_index + 1;359}360361vc4_bo_purgeable(bo);362bo->free_time = time;363list_addtail(&bo->size_list, &cache->size_list[page_index]);364list_addtail(&bo->time_list, &cache->time_list);365cache->bo_count++;366cache->bo_size += bo->size;367if (dump_stats) {368fprintf(stderr, "Freed %s %dkb to cache:\n",369bo->name, bo->size / 1024);370vc4_bo_dump_stats(screen);371}372bo->name = NULL;373vc4_bo_label(screen, bo, "mesa cache");374375free_stale_bos(screen, time);376}377378static struct vc4_bo *379vc4_bo_open_handle(struct vc4_screen *screen,380uint32_t handle, uint32_t size)381{382struct vc4_bo *bo;383384assert(size);385386mtx_lock(&screen->bo_handles_mutex);387388bo = util_hash_table_get(screen->bo_handles, (void*)(uintptr_t)handle);389if (bo) {390vc4_bo_reference(bo);391goto done;392}393394bo = CALLOC_STRUCT(vc4_bo);395pipe_reference_init(&bo->reference, 1);396bo->screen = screen;397bo->handle = handle;398bo->size = size;399bo->name = "winsys";400bo->private = false;401402#ifdef USE_VC4_SIMULATOR403vc4_simulator_open_from_handle(screen->fd, bo->handle, bo->size);404bo->map = malloc(bo->size);405#endif406407_mesa_hash_table_insert(screen->bo_handles, (void *)(uintptr_t)handle, bo);408409done:410mtx_unlock(&screen->bo_handles_mutex);411return bo;412}413414struct vc4_bo *415vc4_bo_open_name(struct vc4_screen *screen, uint32_t name)416{417struct drm_gem_open o = {418.name = name419};420int ret = vc4_ioctl(screen->fd, DRM_IOCTL_GEM_OPEN, &o);421if (ret) {422fprintf(stderr, "Failed to open bo %d: %s\n",423name, strerror(errno));424return NULL;425}426427return vc4_bo_open_handle(screen, o.handle, o.size);428}429430struct vc4_bo *431vc4_bo_open_dmabuf(struct vc4_screen *screen, int fd)432{433uint32_t handle;434int ret = drmPrimeFDToHandle(screen->fd, fd, &handle);435int size;436if (ret) {437fprintf(stderr, "Failed to get vc4 handle for dmabuf %d\n", fd);438return NULL;439}440441/* Determine the size of the bo we were handed. */442size = lseek(fd, 0, SEEK_END);443if (size == -1) {444fprintf(stderr, "Couldn't get size of dmabuf fd %d.\n", fd);445return NULL;446}447448return vc4_bo_open_handle(screen, handle, size);449}450451int452vc4_bo_get_dmabuf(struct vc4_bo *bo)453{454int fd;455int ret = drmPrimeHandleToFD(bo->screen->fd, bo->handle,456O_CLOEXEC, &fd);457if (ret != 0) {458fprintf(stderr, "Failed to export gem bo %d to dmabuf\n",459bo->handle);460return -1;461}462463mtx_lock(&bo->screen->bo_handles_mutex);464bo->private = false;465_mesa_hash_table_insert(bo->screen->bo_handles, (void *)(uintptr_t)bo->handle, bo);466mtx_unlock(&bo->screen->bo_handles_mutex);467468return fd;469}470471struct vc4_bo *472vc4_bo_alloc_shader(struct vc4_screen *screen, const void *data, uint32_t size)473{474struct vc4_bo *bo;475int ret;476477bo = CALLOC_STRUCT(vc4_bo);478if (!bo)479return NULL;480481pipe_reference_init(&bo->reference, 1);482bo->screen = screen;483bo->size = align(size, 4096);484bo->name = "code";485bo->private = false; /* Make sure it doesn't go back to the cache. */486487struct drm_vc4_create_shader_bo create = {488.size = size,489.data = (uintptr_t)data,490};491492ret = vc4_ioctl(screen->fd, DRM_IOCTL_VC4_CREATE_SHADER_BO,493&create);494bo->handle = create.handle;495496if (ret != 0) {497fprintf(stderr, "create shader ioctl failure\n");498abort();499}500501screen->bo_count++;502screen->bo_size += bo->size;503if (dump_stats) {504fprintf(stderr, "Allocated shader %dkb:\n", bo->size / 1024);505vc4_bo_dump_stats(screen);506}507508return bo;509}510511bool512vc4_bo_flink(struct vc4_bo *bo, uint32_t *name)513{514struct drm_gem_flink flink = {515.handle = bo->handle,516};517int ret = vc4_ioctl(bo->screen->fd, DRM_IOCTL_GEM_FLINK, &flink);518if (ret) {519fprintf(stderr, "Failed to flink bo %d: %s\n",520bo->handle, strerror(errno));521free(bo);522return false;523}524525bo->private = false;526*name = flink.name;527528return true;529}530531static int vc4_wait_seqno_ioctl(int fd, uint64_t seqno, uint64_t timeout_ns)532{533struct drm_vc4_wait_seqno wait = {534.seqno = seqno,535.timeout_ns = timeout_ns,536};537int ret = vc4_ioctl(fd, DRM_IOCTL_VC4_WAIT_SEQNO, &wait);538if (ret == -1)539return -errno;540else541return 0;542543}544545bool546vc4_wait_seqno(struct vc4_screen *screen, uint64_t seqno, uint64_t timeout_ns,547const char *reason)548{549if (screen->finished_seqno >= seqno)550return true;551552if (unlikely(vc4_debug & VC4_DEBUG_PERF) && timeout_ns && reason) {553if (vc4_wait_seqno_ioctl(screen->fd, seqno, 0) == -ETIME) {554fprintf(stderr, "Blocking on seqno %lld for %s\n",555(long long)seqno, reason);556}557}558559int ret = vc4_wait_seqno_ioctl(screen->fd, seqno, timeout_ns);560if (ret) {561if (ret != -ETIME) {562fprintf(stderr, "wait failed: %d\n", ret);563abort();564}565566return false;567}568569screen->finished_seqno = seqno;570return true;571}572573static int vc4_wait_bo_ioctl(int fd, uint32_t handle, uint64_t timeout_ns)574{575struct drm_vc4_wait_bo wait = {576.handle = handle,577.timeout_ns = timeout_ns,578};579int ret = vc4_ioctl(fd, DRM_IOCTL_VC4_WAIT_BO, &wait);580if (ret == -1)581return -errno;582else583return 0;584585}586587bool588vc4_bo_wait(struct vc4_bo *bo, uint64_t timeout_ns, const char *reason)589{590struct vc4_screen *screen = bo->screen;591592if (unlikely(vc4_debug & VC4_DEBUG_PERF) && timeout_ns && reason) {593if (vc4_wait_bo_ioctl(screen->fd, bo->handle, 0) == -ETIME) {594fprintf(stderr, "Blocking on %s BO for %s\n",595bo->name, reason);596}597}598599int ret = vc4_wait_bo_ioctl(screen->fd, bo->handle, timeout_ns);600if (ret) {601if (ret != -ETIME) {602fprintf(stderr, "wait failed: %d\n", ret);603abort();604}605606return false;607}608609return true;610}611612void *613vc4_bo_map_unsynchronized(struct vc4_bo *bo)614{615uint64_t offset;616int ret;617618if (bo->map)619return bo->map;620621struct drm_vc4_mmap_bo map;622memset(&map, 0, sizeof(map));623map.handle = bo->handle;624ret = vc4_ioctl(bo->screen->fd, DRM_IOCTL_VC4_MMAP_BO, &map);625offset = map.offset;626if (ret != 0) {627fprintf(stderr, "map ioctl failure\n");628abort();629}630631bo->map = mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,632bo->screen->fd, offset);633if (bo->map == MAP_FAILED) {634fprintf(stderr, "mmap of bo %d (offset 0x%016llx, size %d) failed\n",635bo->handle, (long long)offset, bo->size);636abort();637}638VG(VALGRIND_MALLOCLIKE_BLOCK(bo->map, bo->size, 0, false));639640return bo->map;641}642643void *644vc4_bo_map(struct vc4_bo *bo)645{646void *map = vc4_bo_map_unsynchronized(bo);647648bool ok = vc4_bo_wait(bo, PIPE_TIMEOUT_INFINITE, "bo map");649if (!ok) {650fprintf(stderr, "BO wait for map failed\n");651abort();652}653654return map;655}656657void658vc4_bufmgr_destroy(struct pipe_screen *pscreen)659{660struct vc4_screen *screen = vc4_screen(pscreen);661struct vc4_bo_cache *cache = &screen->bo_cache;662663vc4_bo_cache_free_all(cache);664665if (dump_stats) {666fprintf(stderr, "BO stats after screen destroy:\n");667vc4_bo_dump_stats(screen);668}669}670671672