1848b8605Smrg/* 2b8e80941Smrg * Copyright © 2014-2015 Broadcom 3848b8605Smrg * 4848b8605Smrg * Permission is hereby granted, free of charge, to any person obtaining a 5848b8605Smrg * copy of this software and associated documentation files (the "Software"), 6848b8605Smrg * to deal in the Software without restriction, including without limitation 7848b8605Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8848b8605Smrg * and/or sell copies of the Software, and to permit persons to whom the 9848b8605Smrg * Software is furnished to do so, subject to the following conditions: 10848b8605Smrg * 11848b8605Smrg * The above copyright notice and this permission notice (including the next 12848b8605Smrg * paragraph) shall be included in all copies or substantial portions of the 13848b8605Smrg * Software. 14848b8605Smrg * 15848b8605Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16848b8605Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17848b8605Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18848b8605Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19848b8605Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20848b8605Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21848b8605Smrg * IN THE SOFTWARE. 22848b8605Smrg */ 23848b8605Smrg 24848b8605Smrg#include <errno.h> 25848b8605Smrg#include <err.h> 26848b8605Smrg#include <sys/mman.h> 27b8e80941Smrg#include <fcntl.h> 28848b8605Smrg#include <xf86drm.h> 29848b8605Smrg#include <xf86drmMode.h> 30848b8605Smrg 31b8e80941Smrg#include "util/u_hash_table.h" 32848b8605Smrg#include "util/u_memory.h" 33b8e80941Smrg#include "util/u_string.h" 34b8e80941Smrg#include "util/ralloc.h" 35848b8605Smrg 36848b8605Smrg#include "vc4_context.h" 37848b8605Smrg#include "vc4_screen.h" 38848b8605Smrg 39b8e80941Smrg#ifdef HAVE_VALGRIND 40b8e80941Smrg#include <valgrind.h> 41b8e80941Smrg#include <memcheck.h> 42b8e80941Smrg#define VG(x) x 43b8e80941Smrg#else 44b8e80941Smrg#define VG(x) 45b8e80941Smrg#endif 46b8e80941Smrg 47b8e80941Smrgstatic bool dump_stats = false; 48b8e80941Smrg 49b8e80941Smrgstatic void 50b8e80941Smrgvc4_bo_cache_free_all(struct vc4_bo_cache *cache); 51b8e80941Smrg 52b8e80941Smrgvoid 53b8e80941Smrgvc4_bo_debug_describe(char* buf, const struct vc4_bo *ptr) 54848b8605Smrg{ 55b8e80941Smrg util_sprintf(buf, "vc4_bo<%s,%u,%u>", ptr->name ? ptr->name : "?", 56b8e80941Smrg ptr->handle, ptr->size); 57b8e80941Smrg} 58848b8605Smrg 59b8e80941Smrgvoid 60b8e80941Smrgvc4_bo_label(struct vc4_screen *screen, struct vc4_bo *bo, const char *fmt, ...) 61b8e80941Smrg{ 62b8e80941Smrg /* Perform BO labeling by default on debug builds (so that you get 63b8e80941Smrg * whole-system allocation information), or if VC4_DEBUG=surf is set 64b8e80941Smrg * (for debugging a single app's allocation). 65b8e80941Smrg */ 66b8e80941Smrg#ifndef DEBUG 67b8e80941Smrg if (!(vc4_debug & VC4_DEBUG_SURFACE)) 68b8e80941Smrg return; 69b8e80941Smrg#endif 70b8e80941Smrg va_list va; 71b8e80941Smrg va_start(va, fmt); 72b8e80941Smrg char *name = ralloc_vasprintf(NULL, fmt, va); 73b8e80941Smrg va_end(va); 74848b8605Smrg 75b8e80941Smrg struct drm_vc4_label_bo label = { 76b8e80941Smrg .handle = bo->handle, 77b8e80941Smrg .len = strlen(name), 78b8e80941Smrg .name = (uintptr_t)name, 79b8e80941Smrg }; 80b8e80941Smrg vc4_ioctl(screen->fd, DRM_IOCTL_VC4_LABEL_BO, &label); 81848b8605Smrg 82b8e80941Smrg ralloc_free(name); 83b8e80941Smrg} 84848b8605Smrg 85b8e80941Smrgstatic void 86b8e80941Smrgvc4_bo_dump_stats(struct vc4_screen *screen) 87b8e80941Smrg{ 88b8e80941Smrg struct vc4_bo_cache *cache = &screen->bo_cache; 89848b8605Smrg 90b8e80941Smrg fprintf(stderr, " BOs allocated: %d\n", screen->bo_count); 91b8e80941Smrg fprintf(stderr, " BOs size: %dkb\n", screen->bo_size / 1024); 92b8e80941Smrg fprintf(stderr, " BOs cached: %d\n", cache->bo_count); 93b8e80941Smrg fprintf(stderr, " BOs cached size: %dkb\n", cache->bo_size / 1024); 94848b8605Smrg 95b8e80941Smrg if (!list_empty(&cache->time_list)) { 96b8e80941Smrg struct vc4_bo *first = LIST_ENTRY(struct vc4_bo, 97b8e80941Smrg cache->time_list.next, 98b8e80941Smrg time_list); 99b8e80941Smrg struct vc4_bo *last = LIST_ENTRY(struct vc4_bo, 100b8e80941Smrg cache->time_list.prev, 101b8e80941Smrg time_list); 102b8e80941Smrg 103b8e80941Smrg fprintf(stderr, " oldest cache time: %ld\n", 104b8e80941Smrg (long)first->free_time); 105b8e80941Smrg fprintf(stderr, " newest cache time: %ld\n", 106b8e80941Smrg (long)last->free_time); 107b8e80941Smrg 108b8e80941Smrg struct timespec time; 109b8e80941Smrg clock_gettime(CLOCK_MONOTONIC, &time); 110b8e80941Smrg fprintf(stderr, " now: %ld\n", 111b8e80941Smrg time.tv_sec); 112b8e80941Smrg } 113848b8605Smrg} 114848b8605Smrg 115b8e80941Smrgstatic void 116b8e80941Smrgvc4_bo_remove_from_cache(struct vc4_bo_cache *cache, struct vc4_bo *bo) 117b8e80941Smrg{ 118b8e80941Smrg list_del(&bo->time_list); 119b8e80941Smrg list_del(&bo->size_list); 120b8e80941Smrg cache->bo_count--; 121b8e80941Smrg cache->bo_size -= bo->size; 122b8e80941Smrg} 123b8e80941Smrg 124b8e80941Smrgstatic void vc4_bo_purgeable(struct vc4_bo *bo) 125b8e80941Smrg{ 126b8e80941Smrg struct drm_vc4_gem_madvise arg = { 127b8e80941Smrg .handle = bo->handle, 128b8e80941Smrg .madv = VC4_MADV_DONTNEED, 129b8e80941Smrg }; 130b8e80941Smrg 131b8e80941Smrg if (bo->screen->has_madvise) 132b8e80941Smrg vc4_ioctl(bo->screen->fd, DRM_IOCTL_VC4_GEM_MADVISE, &arg); 133b8e80941Smrg} 134b8e80941Smrg 135b8e80941Smrgstatic bool vc4_bo_unpurgeable(struct vc4_bo *bo) 136b8e80941Smrg{ 137b8e80941Smrg struct drm_vc4_gem_madvise arg = { 138b8e80941Smrg .handle = bo->handle, 139b8e80941Smrg .madv = VC4_MADV_WILLNEED, 140b8e80941Smrg }; 141b8e80941Smrg 142b8e80941Smrg if (!bo->screen->has_madvise) 143b8e80941Smrg return true; 144b8e80941Smrg 145b8e80941Smrg if (vc4_ioctl(bo->screen->fd, DRM_IOCTL_VC4_GEM_MADVISE, &arg)) 146b8e80941Smrg return false; 147b8e80941Smrg 148b8e80941Smrg return arg.retained; 149b8e80941Smrg} 150b8e80941Smrg 151b8e80941Smrgstatic void 152848b8605Smrgvc4_bo_free(struct vc4_bo *bo) 153848b8605Smrg{ 154848b8605Smrg struct vc4_screen *screen = bo->screen; 155848b8605Smrg 156848b8605Smrg if (bo->map) { 157b8e80941Smrg if (using_vc4_simulator && bo->name && 158b8e80941Smrg strcmp(bo->name, "winsys") == 0) { 159848b8605Smrg free(bo->map); 160b8e80941Smrg } else { 161b8e80941Smrg munmap(bo->map, bo->size); 162b8e80941Smrg VG(VALGRIND_FREELIKE_BLOCK(bo->map, 0)); 163848b8605Smrg } 164848b8605Smrg } 165848b8605Smrg 166848b8605Smrg struct drm_gem_close c; 167b8e80941Smrg memset(&c, 0, sizeof(c)); 168848b8605Smrg c.handle = bo->handle; 169b8e80941Smrg int ret = vc4_ioctl(screen->fd, DRM_IOCTL_GEM_CLOSE, &c); 170848b8605Smrg if (ret != 0) 171848b8605Smrg fprintf(stderr, "close object %d: %s\n", bo->handle, strerror(errno)); 172848b8605Smrg 173b8e80941Smrg screen->bo_count--; 174b8e80941Smrg screen->bo_size -= bo->size; 175b8e80941Smrg 176b8e80941Smrg if (dump_stats) { 177b8e80941Smrg fprintf(stderr, "Freed %s%s%dkb:\n", 178b8e80941Smrg bo->name ? bo->name : "", 179b8e80941Smrg bo->name ? " " : "", 180b8e80941Smrg bo->size / 1024); 181b8e80941Smrg vc4_bo_dump_stats(screen); 182b8e80941Smrg } 183b8e80941Smrg 184848b8605Smrg free(bo); 185848b8605Smrg} 186848b8605Smrg 187b8e80941Smrgstatic struct vc4_bo * 188b8e80941Smrgvc4_bo_from_cache(struct vc4_screen *screen, uint32_t size, const char *name) 189b8e80941Smrg{ 190b8e80941Smrg struct vc4_bo_cache *cache = &screen->bo_cache; 191b8e80941Smrg uint32_t page_index = size / 4096 - 1; 192b8e80941Smrg struct vc4_bo *iter, *tmp, *bo = NULL; 193b8e80941Smrg 194b8e80941Smrg if (cache->size_list_size <= page_index) 195b8e80941Smrg return NULL; 196b8e80941Smrg 197b8e80941Smrg mtx_lock(&cache->lock); 198b8e80941Smrg LIST_FOR_EACH_ENTRY_SAFE(iter, tmp, &cache->size_list[page_index], 199b8e80941Smrg size_list) { 200b8e80941Smrg /* Check that the BO has gone idle. If not, then none of the 201b8e80941Smrg * other BOs (pushed to the list after later rendering) are 202b8e80941Smrg * likely to be idle, either. 203b8e80941Smrg */ 204b8e80941Smrg if (!vc4_bo_wait(iter, 0, NULL)) 205b8e80941Smrg break; 206b8e80941Smrg 207b8e80941Smrg if (!vc4_bo_unpurgeable(iter)) { 208b8e80941Smrg /* The BO has been purged. Free it and try to find 209b8e80941Smrg * another one in the cache. 210b8e80941Smrg */ 211b8e80941Smrg vc4_bo_remove_from_cache(cache, iter); 212b8e80941Smrg vc4_bo_free(iter); 213b8e80941Smrg continue; 214b8e80941Smrg } 215b8e80941Smrg 216b8e80941Smrg bo = iter; 217b8e80941Smrg pipe_reference_init(&bo->reference, 1); 218b8e80941Smrg vc4_bo_remove_from_cache(cache, bo); 219b8e80941Smrg 220b8e80941Smrg vc4_bo_label(screen, bo, "%s", name); 221b8e80941Smrg bo->name = name; 222b8e80941Smrg break; 223b8e80941Smrg } 224b8e80941Smrg mtx_unlock(&cache->lock); 225b8e80941Smrg return bo; 226b8e80941Smrg} 227b8e80941Smrg 228848b8605Smrgstruct vc4_bo * 229b8e80941Smrgvc4_bo_alloc(struct vc4_screen *screen, uint32_t size, const char *name) 230848b8605Smrg{ 231b8e80941Smrg bool cleared_and_retried = false; 232b8e80941Smrg struct drm_vc4_create_bo create; 233b8e80941Smrg struct vc4_bo *bo; 234b8e80941Smrg int ret; 235b8e80941Smrg 236b8e80941Smrg size = align(size, 4096); 237b8e80941Smrg 238b8e80941Smrg bo = vc4_bo_from_cache(screen, size, name); 239b8e80941Smrg if (bo) { 240b8e80941Smrg if (dump_stats) { 241b8e80941Smrg fprintf(stderr, "Allocated %s %dkb from cache:\n", 242b8e80941Smrg name, size / 1024); 243b8e80941Smrg vc4_bo_dump_stats(screen); 244b8e80941Smrg } 245b8e80941Smrg return bo; 246b8e80941Smrg } 247b8e80941Smrg 248b8e80941Smrg bo = CALLOC_STRUCT(vc4_bo); 249b8e80941Smrg if (!bo) 250b8e80941Smrg return NULL; 251b8e80941Smrg 252b8e80941Smrg pipe_reference_init(&bo->reference, 1); 253b8e80941Smrg bo->screen = screen; 254b8e80941Smrg bo->size = size; 255b8e80941Smrg bo->name = name; 256b8e80941Smrg bo->private = true; 257b8e80941Smrg 258b8e80941Smrg retry: 259b8e80941Smrg memset(&create, 0, sizeof(create)); 260b8e80941Smrg create.size = size; 261b8e80941Smrg 262b8e80941Smrg ret = vc4_ioctl(screen->fd, DRM_IOCTL_VC4_CREATE_BO, &create); 263b8e80941Smrg bo->handle = create.handle; 264b8e80941Smrg 265b8e80941Smrg if (ret != 0) { 266b8e80941Smrg if (!list_empty(&screen->bo_cache.time_list) && 267b8e80941Smrg !cleared_and_retried) { 268b8e80941Smrg cleared_and_retried = true; 269b8e80941Smrg vc4_bo_cache_free_all(&screen->bo_cache); 270b8e80941Smrg goto retry; 271b8e80941Smrg } 272848b8605Smrg 273848b8605Smrg free(bo); 274848b8605Smrg return NULL; 275848b8605Smrg } 276848b8605Smrg 277b8e80941Smrg screen->bo_count++; 278b8e80941Smrg screen->bo_size += bo->size; 279b8e80941Smrg if (dump_stats) { 280b8e80941Smrg fprintf(stderr, "Allocated %s %dkb:\n", name, size / 1024); 281b8e80941Smrg vc4_bo_dump_stats(screen); 282b8e80941Smrg } 283b8e80941Smrg 284b8e80941Smrg vc4_bo_label(screen, bo, "%s", name); 285b8e80941Smrg 286b8e80941Smrg return bo; 287b8e80941Smrg} 288b8e80941Smrg 289b8e80941Smrgvoid 290b8e80941Smrgvc4_bo_last_unreference(struct vc4_bo *bo) 291b8e80941Smrg{ 292b8e80941Smrg struct vc4_screen *screen = bo->screen; 293b8e80941Smrg 294b8e80941Smrg struct timespec time; 295b8e80941Smrg clock_gettime(CLOCK_MONOTONIC, &time); 296b8e80941Smrg mtx_lock(&screen->bo_cache.lock); 297b8e80941Smrg vc4_bo_last_unreference_locked_timed(bo, time.tv_sec); 298b8e80941Smrg mtx_unlock(&screen->bo_cache.lock); 299b8e80941Smrg} 300b8e80941Smrg 301b8e80941Smrgstatic void 302b8e80941Smrgfree_stale_bos(struct vc4_screen *screen, time_t time) 303b8e80941Smrg{ 304b8e80941Smrg struct vc4_bo_cache *cache = &screen->bo_cache; 305b8e80941Smrg bool freed_any = false; 306b8e80941Smrg 307b8e80941Smrg list_for_each_entry_safe(struct vc4_bo, bo, &cache->time_list, 308b8e80941Smrg time_list) { 309b8e80941Smrg if (dump_stats && !freed_any) { 310b8e80941Smrg fprintf(stderr, "Freeing stale BOs:\n"); 311b8e80941Smrg vc4_bo_dump_stats(screen); 312b8e80941Smrg freed_any = true; 313b8e80941Smrg } 314b8e80941Smrg 315b8e80941Smrg /* If it's more than a second old, free it. */ 316b8e80941Smrg if (time - bo->free_time > 2) { 317b8e80941Smrg vc4_bo_remove_from_cache(cache, bo); 318b8e80941Smrg vc4_bo_free(bo); 319b8e80941Smrg } else { 320b8e80941Smrg break; 321b8e80941Smrg } 322b8e80941Smrg } 323b8e80941Smrg 324b8e80941Smrg if (dump_stats && freed_any) { 325b8e80941Smrg fprintf(stderr, "Freed stale BOs:\n"); 326b8e80941Smrg vc4_bo_dump_stats(screen); 327b8e80941Smrg } 328b8e80941Smrg} 329b8e80941Smrg 330b8e80941Smrgstatic void 331b8e80941Smrgvc4_bo_cache_free_all(struct vc4_bo_cache *cache) 332b8e80941Smrg{ 333b8e80941Smrg mtx_lock(&cache->lock); 334b8e80941Smrg list_for_each_entry_safe(struct vc4_bo, bo, &cache->time_list, 335b8e80941Smrg time_list) { 336b8e80941Smrg vc4_bo_remove_from_cache(cache, bo); 337b8e80941Smrg vc4_bo_free(bo); 338b8e80941Smrg } 339b8e80941Smrg mtx_unlock(&cache->lock); 340b8e80941Smrg} 341b8e80941Smrg 342b8e80941Smrgvoid 343b8e80941Smrgvc4_bo_last_unreference_locked_timed(struct vc4_bo *bo, time_t time) 344b8e80941Smrg{ 345b8e80941Smrg struct vc4_screen *screen = bo->screen; 346b8e80941Smrg struct vc4_bo_cache *cache = &screen->bo_cache; 347b8e80941Smrg uint32_t page_index = bo->size / 4096 - 1; 348b8e80941Smrg 349b8e80941Smrg if (!bo->private) { 350b8e80941Smrg vc4_bo_free(bo); 351b8e80941Smrg return; 352b8e80941Smrg } 353b8e80941Smrg 354b8e80941Smrg if (cache->size_list_size <= page_index) { 355b8e80941Smrg struct list_head *new_list = 356b8e80941Smrg ralloc_array(screen, struct list_head, page_index + 1); 357b8e80941Smrg 358b8e80941Smrg /* Move old list contents over (since the array has moved, and 359b8e80941Smrg * therefore the pointers to the list heads have to change). 360b8e80941Smrg */ 361b8e80941Smrg for (int i = 0; i < cache->size_list_size; i++) 362b8e80941Smrg list_replace(&cache->size_list[i], &new_list[i]); 363b8e80941Smrg for (int i = cache->size_list_size; i < page_index + 1; i++) 364b8e80941Smrg list_inithead(&new_list[i]); 365b8e80941Smrg 366b8e80941Smrg cache->size_list = new_list; 367b8e80941Smrg cache->size_list_size = page_index + 1; 368b8e80941Smrg } 369b8e80941Smrg 370b8e80941Smrg vc4_bo_purgeable(bo); 371b8e80941Smrg bo->free_time = time; 372b8e80941Smrg list_addtail(&bo->size_list, &cache->size_list[page_index]); 373b8e80941Smrg list_addtail(&bo->time_list, &cache->time_list); 374b8e80941Smrg cache->bo_count++; 375b8e80941Smrg cache->bo_size += bo->size; 376b8e80941Smrg if (dump_stats) { 377b8e80941Smrg fprintf(stderr, "Freed %s %dkb to cache:\n", 378b8e80941Smrg bo->name, bo->size / 1024); 379b8e80941Smrg vc4_bo_dump_stats(screen); 380b8e80941Smrg } 381b8e80941Smrg bo->name = NULL; 382b8e80941Smrg vc4_bo_label(screen, bo, "mesa cache"); 383b8e80941Smrg 384b8e80941Smrg free_stale_bos(screen, time); 385b8e80941Smrg} 386b8e80941Smrg 387b8e80941Smrgstatic struct vc4_bo * 388b8e80941Smrgvc4_bo_open_handle(struct vc4_screen *screen, 389b8e80941Smrg uint32_t handle, uint32_t size) 390b8e80941Smrg{ 391b8e80941Smrg struct vc4_bo *bo; 392b8e80941Smrg 393b8e80941Smrg assert(size); 394b8e80941Smrg 395b8e80941Smrg mtx_lock(&screen->bo_handles_mutex); 396b8e80941Smrg 397b8e80941Smrg bo = util_hash_table_get(screen->bo_handles, (void*)(uintptr_t)handle); 398b8e80941Smrg if (bo) { 399b8e80941Smrg vc4_bo_reference(bo); 400b8e80941Smrg goto done; 401b8e80941Smrg } 402b8e80941Smrg 403b8e80941Smrg bo = CALLOC_STRUCT(vc4_bo); 404848b8605Smrg pipe_reference_init(&bo->reference, 1); 405848b8605Smrg bo->screen = screen; 406b8e80941Smrg bo->handle = handle; 407b8e80941Smrg bo->size = size; 408848b8605Smrg bo->name = "winsys"; 409b8e80941Smrg bo->private = false; 410848b8605Smrg 411848b8605Smrg#ifdef USE_VC4_SIMULATOR 412b8e80941Smrg vc4_simulator_open_from_handle(screen->fd, bo->handle, bo->size); 413848b8605Smrg bo->map = malloc(bo->size); 414848b8605Smrg#endif 415848b8605Smrg 416b8e80941Smrg util_hash_table_set(screen->bo_handles, (void *)(uintptr_t)handle, bo); 417b8e80941Smrg 418b8e80941Smrgdone: 419b8e80941Smrg mtx_unlock(&screen->bo_handles_mutex); 420848b8605Smrg return bo; 421848b8605Smrg} 422848b8605Smrg 423848b8605Smrgstruct vc4_bo * 424b8e80941Smrgvc4_bo_open_name(struct vc4_screen *screen, uint32_t name) 425b8e80941Smrg{ 426b8e80941Smrg struct drm_gem_open o = { 427b8e80941Smrg .name = name 428b8e80941Smrg }; 429b8e80941Smrg int ret = vc4_ioctl(screen->fd, DRM_IOCTL_GEM_OPEN, &o); 430b8e80941Smrg if (ret) { 431b8e80941Smrg fprintf(stderr, "Failed to open bo %d: %s\n", 432b8e80941Smrg name, strerror(errno)); 433b8e80941Smrg return NULL; 434b8e80941Smrg } 435b8e80941Smrg 436b8e80941Smrg return vc4_bo_open_handle(screen, o.handle, o.size); 437b8e80941Smrg} 438b8e80941Smrg 439b8e80941Smrgstruct vc4_bo * 440b8e80941Smrgvc4_bo_open_dmabuf(struct vc4_screen *screen, int fd) 441b8e80941Smrg{ 442b8e80941Smrg uint32_t handle; 443b8e80941Smrg int ret = drmPrimeFDToHandle(screen->fd, fd, &handle); 444b8e80941Smrg int size; 445b8e80941Smrg if (ret) { 446b8e80941Smrg fprintf(stderr, "Failed to get vc4 handle for dmabuf %d\n", fd); 447b8e80941Smrg return NULL; 448b8e80941Smrg } 449b8e80941Smrg 450b8e80941Smrg /* Determine the size of the bo we were handed. */ 451b8e80941Smrg size = lseek(fd, 0, SEEK_END); 452b8e80941Smrg if (size == -1) { 453b8e80941Smrg fprintf(stderr, "Couldn't get size of dmabuf fd %d.\n", fd); 454b8e80941Smrg return NULL; 455b8e80941Smrg } 456b8e80941Smrg 457b8e80941Smrg return vc4_bo_open_handle(screen, handle, size); 458b8e80941Smrg} 459b8e80941Smrg 460b8e80941Smrgint 461b8e80941Smrgvc4_bo_get_dmabuf(struct vc4_bo *bo) 462b8e80941Smrg{ 463b8e80941Smrg int fd; 464b8e80941Smrg int ret = drmPrimeHandleToFD(bo->screen->fd, bo->handle, 465b8e80941Smrg O_CLOEXEC, &fd); 466b8e80941Smrg if (ret != 0) { 467b8e80941Smrg fprintf(stderr, "Failed to export gem bo %d to dmabuf\n", 468b8e80941Smrg bo->handle); 469b8e80941Smrg return -1; 470b8e80941Smrg } 471b8e80941Smrg 472b8e80941Smrg mtx_lock(&bo->screen->bo_handles_mutex); 473b8e80941Smrg bo->private = false; 474b8e80941Smrg util_hash_table_set(bo->screen->bo_handles, (void *)(uintptr_t)bo->handle, bo); 475b8e80941Smrg mtx_unlock(&bo->screen->bo_handles_mutex); 476b8e80941Smrg 477b8e80941Smrg return fd; 478b8e80941Smrg} 479b8e80941Smrg 480b8e80941Smrgstruct vc4_bo * 481b8e80941Smrgvc4_bo_alloc_shader(struct vc4_screen *screen, const void *data, uint32_t size) 482848b8605Smrg{ 483848b8605Smrg struct vc4_bo *bo; 484b8e80941Smrg int ret; 485b8e80941Smrg 486b8e80941Smrg bo = CALLOC_STRUCT(vc4_bo); 487b8e80941Smrg if (!bo) 488b8e80941Smrg return NULL; 489b8e80941Smrg 490b8e80941Smrg pipe_reference_init(&bo->reference, 1); 491b8e80941Smrg bo->screen = screen; 492b8e80941Smrg bo->size = align(size, 4096); 493b8e80941Smrg bo->name = "code"; 494b8e80941Smrg bo->private = false; /* Make sure it doesn't go back to the cache. */ 495b8e80941Smrg 496b8e80941Smrg struct drm_vc4_create_shader_bo create = { 497b8e80941Smrg .size = size, 498b8e80941Smrg .data = (uintptr_t)data, 499b8e80941Smrg }; 500b8e80941Smrg 501b8e80941Smrg ret = vc4_ioctl(screen->fd, DRM_IOCTL_VC4_CREATE_SHADER_BO, 502b8e80941Smrg &create); 503b8e80941Smrg bo->handle = create.handle; 504b8e80941Smrg 505b8e80941Smrg if (ret != 0) { 506b8e80941Smrg fprintf(stderr, "create shader ioctl failure\n"); 507b8e80941Smrg abort(); 508b8e80941Smrg } 509b8e80941Smrg 510b8e80941Smrg screen->bo_count++; 511b8e80941Smrg screen->bo_size += bo->size; 512b8e80941Smrg if (dump_stats) { 513b8e80941Smrg fprintf(stderr, "Allocated shader %dkb:\n", bo->size / 1024); 514b8e80941Smrg vc4_bo_dump_stats(screen); 515b8e80941Smrg } 516848b8605Smrg 517848b8605Smrg return bo; 518848b8605Smrg} 519848b8605Smrg 520848b8605Smrgbool 521848b8605Smrgvc4_bo_flink(struct vc4_bo *bo, uint32_t *name) 522848b8605Smrg{ 523848b8605Smrg struct drm_gem_flink flink = { 524848b8605Smrg .handle = bo->handle, 525848b8605Smrg }; 526b8e80941Smrg int ret = vc4_ioctl(bo->screen->fd, DRM_IOCTL_GEM_FLINK, &flink); 527848b8605Smrg if (ret) { 528848b8605Smrg fprintf(stderr, "Failed to flink bo %d: %s\n", 529848b8605Smrg bo->handle, strerror(errno)); 530848b8605Smrg free(bo); 531848b8605Smrg return false; 532848b8605Smrg } 533848b8605Smrg 534b8e80941Smrg bo->private = false; 535848b8605Smrg *name = flink.name; 536848b8605Smrg 537848b8605Smrg return true; 538848b8605Smrg} 539848b8605Smrg 540b8e80941Smrgstatic int vc4_wait_seqno_ioctl(int fd, uint64_t seqno, uint64_t timeout_ns) 541b8e80941Smrg{ 542b8e80941Smrg struct drm_vc4_wait_seqno wait = { 543b8e80941Smrg .seqno = seqno, 544b8e80941Smrg .timeout_ns = timeout_ns, 545b8e80941Smrg }; 546b8e80941Smrg int ret = vc4_ioctl(fd, DRM_IOCTL_VC4_WAIT_SEQNO, &wait); 547b8e80941Smrg if (ret == -1) 548b8e80941Smrg return -errno; 549b8e80941Smrg else 550b8e80941Smrg return 0; 551b8e80941Smrg 552b8e80941Smrg} 553b8e80941Smrg 554b8e80941Smrgbool 555b8e80941Smrgvc4_wait_seqno(struct vc4_screen *screen, uint64_t seqno, uint64_t timeout_ns, 556b8e80941Smrg const char *reason) 557b8e80941Smrg{ 558b8e80941Smrg if (screen->finished_seqno >= seqno) 559b8e80941Smrg return true; 560b8e80941Smrg 561b8e80941Smrg if (unlikely(vc4_debug & VC4_DEBUG_PERF) && timeout_ns && reason) { 562b8e80941Smrg if (vc4_wait_seqno_ioctl(screen->fd, seqno, 0) == -ETIME) { 563b8e80941Smrg fprintf(stderr, "Blocking on seqno %lld for %s\n", 564b8e80941Smrg (long long)seqno, reason); 565b8e80941Smrg } 566b8e80941Smrg } 567b8e80941Smrg 568b8e80941Smrg int ret = vc4_wait_seqno_ioctl(screen->fd, seqno, timeout_ns); 569b8e80941Smrg if (ret) { 570b8e80941Smrg if (ret != -ETIME) { 571b8e80941Smrg fprintf(stderr, "wait failed: %d\n", ret); 572b8e80941Smrg abort(); 573b8e80941Smrg } 574b8e80941Smrg 575b8e80941Smrg return false; 576b8e80941Smrg } 577b8e80941Smrg 578b8e80941Smrg screen->finished_seqno = seqno; 579b8e80941Smrg return true; 580b8e80941Smrg} 581b8e80941Smrg 582b8e80941Smrgstatic int vc4_wait_bo_ioctl(int fd, uint32_t handle, uint64_t timeout_ns) 583b8e80941Smrg{ 584b8e80941Smrg struct drm_vc4_wait_bo wait = { 585b8e80941Smrg .handle = handle, 586b8e80941Smrg .timeout_ns = timeout_ns, 587b8e80941Smrg }; 588b8e80941Smrg int ret = vc4_ioctl(fd, DRM_IOCTL_VC4_WAIT_BO, &wait); 589b8e80941Smrg if (ret == -1) 590b8e80941Smrg return -errno; 591b8e80941Smrg else 592b8e80941Smrg return 0; 593b8e80941Smrg 594b8e80941Smrg} 595b8e80941Smrg 596b8e80941Smrgbool 597b8e80941Smrgvc4_bo_wait(struct vc4_bo *bo, uint64_t timeout_ns, const char *reason) 598b8e80941Smrg{ 599b8e80941Smrg struct vc4_screen *screen = bo->screen; 600b8e80941Smrg 601b8e80941Smrg if (unlikely(vc4_debug & VC4_DEBUG_PERF) && timeout_ns && reason) { 602b8e80941Smrg if (vc4_wait_bo_ioctl(screen->fd, bo->handle, 0) == -ETIME) { 603b8e80941Smrg fprintf(stderr, "Blocking on %s BO for %s\n", 604b8e80941Smrg bo->name, reason); 605b8e80941Smrg } 606b8e80941Smrg } 607b8e80941Smrg 608b8e80941Smrg int ret = vc4_wait_bo_ioctl(screen->fd, bo->handle, timeout_ns); 609b8e80941Smrg if (ret) { 610b8e80941Smrg if (ret != -ETIME) { 611b8e80941Smrg fprintf(stderr, "wait failed: %d\n", ret); 612b8e80941Smrg abort(); 613b8e80941Smrg } 614b8e80941Smrg 615b8e80941Smrg return false; 616b8e80941Smrg } 617b8e80941Smrg 618b8e80941Smrg return true; 619b8e80941Smrg} 620b8e80941Smrg 621848b8605Smrgvoid * 622b8e80941Smrgvc4_bo_map_unsynchronized(struct vc4_bo *bo) 623848b8605Smrg{ 624b8e80941Smrg uint64_t offset; 625848b8605Smrg int ret; 626848b8605Smrg 627848b8605Smrg if (bo->map) 628848b8605Smrg return bo->map; 629848b8605Smrg 630b8e80941Smrg struct drm_vc4_mmap_bo map; 631848b8605Smrg memset(&map, 0, sizeof(map)); 632848b8605Smrg map.handle = bo->handle; 633b8e80941Smrg ret = vc4_ioctl(bo->screen->fd, DRM_IOCTL_VC4_MMAP_BO, &map); 634b8e80941Smrg offset = map.offset; 635b8e80941Smrg if (ret != 0) { 636b8e80941Smrg fprintf(stderr, "map ioctl failure\n"); 637b8e80941Smrg abort(); 638b8e80941Smrg } 639848b8605Smrg 640848b8605Smrg bo->map = mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED, 641b8e80941Smrg bo->screen->fd, offset); 642848b8605Smrg if (bo->map == MAP_FAILED) { 643b8e80941Smrg fprintf(stderr, "mmap of bo %d (offset 0x%016llx, size %d) failed\n", 644b8e80941Smrg bo->handle, (long long)offset, bo->size); 645b8e80941Smrg abort(); 646848b8605Smrg } 647b8e80941Smrg VG(VALGRIND_MALLOCLIKE_BLOCK(bo->map, bo->size, 0, false)); 648848b8605Smrg 649848b8605Smrg return bo->map; 650848b8605Smrg} 651b8e80941Smrg 652b8e80941Smrgvoid * 653b8e80941Smrgvc4_bo_map(struct vc4_bo *bo) 654b8e80941Smrg{ 655b8e80941Smrg void *map = vc4_bo_map_unsynchronized(bo); 656b8e80941Smrg 657b8e80941Smrg bool ok = vc4_bo_wait(bo, PIPE_TIMEOUT_INFINITE, "bo map"); 658b8e80941Smrg if (!ok) { 659b8e80941Smrg fprintf(stderr, "BO wait for map failed\n"); 660b8e80941Smrg abort(); 661b8e80941Smrg } 662b8e80941Smrg 663b8e80941Smrg return map; 664b8e80941Smrg} 665b8e80941Smrg 666b8e80941Smrgvoid 667b8e80941Smrgvc4_bufmgr_destroy(struct pipe_screen *pscreen) 668b8e80941Smrg{ 669b8e80941Smrg struct vc4_screen *screen = vc4_screen(pscreen); 670b8e80941Smrg struct vc4_bo_cache *cache = &screen->bo_cache; 671b8e80941Smrg 672b8e80941Smrg vc4_bo_cache_free_all(cache); 673b8e80941Smrg 674b8e80941Smrg if (dump_stats) { 675b8e80941Smrg fprintf(stderr, "BO stats after screen destroy:\n"); 676b8e80941Smrg vc4_bo_dump_stats(screen); 677b8e80941Smrg } 678b8e80941Smrg} 679