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/ralloc.h" 34 35 #include "vc4_context.h" 36 #include "vc4_screen.h" 37 38 #ifdef HAVE_VALGRIND 39 #include <valgrind.h> 40 #include <memcheck.h> 41 #define VG(x) x 42 #else 43 #define VG(x) 44 #endif 45 46 static bool dump_stats = false; 47 48 static void 49 vc4_bo_cache_free_all(struct vc4_bo_cache *cache); 50 51 static void 52 vc4_bo_dump_stats(struct vc4_screen *screen) 53 { 54 struct vc4_bo_cache *cache = &screen->bo_cache; 55 56 fprintf(stderr, " BOs allocated: %d\n", screen->bo_count); 57 fprintf(stderr, " BOs size: %dkb\n", screen->bo_size / 1024); 58 fprintf(stderr, " BOs cached: %d\n", cache->bo_count); 59 fprintf(stderr, " BOs cached size: %dkb\n", cache->bo_size / 1024); 60 61 if (!list_empty(&cache->time_list)) { 62 struct vc4_bo *first = LIST_ENTRY(struct vc4_bo, 63 cache->time_list.next, 64 time_list); 65 struct vc4_bo *last = LIST_ENTRY(struct vc4_bo, 66 cache->time_list.prev, 67 time_list); 68 69 fprintf(stderr, " oldest cache time: %ld\n", 70 (long)first->free_time); 71 fprintf(stderr, " newest cache time: %ld\n", 72 (long)last->free_time); 73 74 struct timespec time; 75 clock_gettime(CLOCK_MONOTONIC, &time); 76 fprintf(stderr, " now: %ld\n", 77 time.tv_sec); 78 } 79 } 80 81 static void 82 vc4_bo_remove_from_cache(struct vc4_bo_cache *cache, struct vc4_bo *bo) 83 { 84 list_del(&bo->time_list); 85 list_del(&bo->size_list); 86 cache->bo_count--; 87 cache->bo_size -= bo->size; 88 } 89 90 static struct vc4_bo * 91 vc4_bo_from_cache(struct vc4_screen *screen, uint32_t size, const char *name) 92 { 93 struct vc4_bo_cache *cache = &screen->bo_cache; 94 uint32_t page_index = size / 4096 - 1; 95 96 if (cache->size_list_size <= page_index) 97 return NULL; 98 99 struct vc4_bo *bo = NULL; 100 pipe_mutex_lock(cache->lock); 101 if (!list_empty(&cache->size_list[page_index])) { 102 bo = LIST_ENTRY(struct vc4_bo, cache->size_list[page_index].next, 103 size_list); 104 105 /* Check that the BO has gone idle. If not, then we want to 106 * allocate something new instead, since we assume that the 107 * user will proceed to CPU map it and fill it with stuff. 108 */ 109 if (!vc4_bo_wait(bo, 0, NULL)) { 110 pipe_mutex_unlock(cache->lock); 111 return NULL; 112 } 113 114 pipe_reference_init(&bo->reference, 1); 115 vc4_bo_remove_from_cache(cache, bo); 116 117 bo->name = name; 118 } 119 pipe_mutex_unlock(cache->lock); 120 return bo; 121 } 122 123 struct vc4_bo * 124 vc4_bo_alloc(struct vc4_screen *screen, uint32_t size, const char *name) 125 { 126 struct vc4_bo *bo; 127 int ret; 128 129 size = align(size, 4096); 130 131 bo = vc4_bo_from_cache(screen, size, name); 132 if (bo) { 133 if (dump_stats) { 134 fprintf(stderr, "Allocated %s %dkb from cache:\n", 135 name, size / 1024); 136 vc4_bo_dump_stats(screen); 137 } 138 return bo; 139 } 140 141 bo = CALLOC_STRUCT(vc4_bo); 142 if (!bo) 143 return NULL; 144 145 pipe_reference_init(&bo->reference, 1); 146 bo->screen = screen; 147 bo->size = size; 148 bo->name = name; 149 bo->private = true; 150 151 retry: 152 ; 153 154 bool cleared_and_retried = false; 155 struct drm_vc4_create_bo create = { 156 .size = size 157 }; 158 159 ret = vc4_ioctl(screen->fd, DRM_IOCTL_VC4_CREATE_BO, &create); 160 bo->handle = create.handle; 161 162 if (ret != 0) { 163 if (!list_empty(&screen->bo_cache.time_list) && 164 !cleared_and_retried) { 165 cleared_and_retried = true; 166 vc4_bo_cache_free_all(&screen->bo_cache); 167 goto retry; 168 } 169 170 free(bo); 171 return NULL; 172 } 173 174 screen->bo_count++; 175 screen->bo_size += bo->size; 176 if (dump_stats) { 177 fprintf(stderr, "Allocated %s %dkb:\n", name, size / 1024); 178 vc4_bo_dump_stats(screen); 179 } 180 181 return bo; 182 } 183 184 void 185 vc4_bo_last_unreference(struct vc4_bo *bo) 186 { 187 struct vc4_screen *screen = bo->screen; 188 189 struct timespec time; 190 clock_gettime(CLOCK_MONOTONIC, &time); 191 pipe_mutex_lock(screen->bo_cache.lock); 192 vc4_bo_last_unreference_locked_timed(bo, time.tv_sec); 193 pipe_mutex_unlock(screen->bo_cache.lock); 194 } 195 196 static void 197 vc4_bo_free(struct vc4_bo *bo) 198 { 199 struct vc4_screen *screen = bo->screen; 200 201 if (bo->map) { 202 if (using_vc4_simulator && bo->name && 203 strcmp(bo->name, "winsys") == 0) { 204 free(bo->map); 205 } else { 206 munmap(bo->map, bo->size); 207 VG(VALGRIND_FREELIKE_BLOCK(bo->map, 0)); 208 } 209 } 210 211 struct drm_gem_close c; 212 memset(&c, 0, sizeof(c)); 213 c.handle = bo->handle; 214 int ret = vc4_ioctl(screen->fd, DRM_IOCTL_GEM_CLOSE, &c); 215 if (ret != 0) 216 fprintf(stderr, "close object %d: %s\n", bo->handle, strerror(errno)); 217 218 screen->bo_count--; 219 screen->bo_size -= bo->size; 220 221 if (dump_stats) { 222 fprintf(stderr, "Freed %s%s%dkb:\n", 223 bo->name ? bo->name : "", 224 bo->name ? " " : "", 225 bo->size / 1024); 226 vc4_bo_dump_stats(screen); 227 } 228 229 free(bo); 230 } 231 232 static void 233 free_stale_bos(struct vc4_screen *screen, time_t time) 234 { 235 struct vc4_bo_cache *cache = &screen->bo_cache; 236 bool freed_any = false; 237 238 list_for_each_entry_safe(struct vc4_bo, bo, &cache->time_list, 239 time_list) { 240 if (dump_stats && !freed_any) { 241 fprintf(stderr, "Freeing stale BOs:\n"); 242 vc4_bo_dump_stats(screen); 243 freed_any = true; 244 } 245 246 /* If it's more than a second old, free it. */ 247 if (time - bo->free_time > 2) { 248 vc4_bo_remove_from_cache(cache, bo); 249 vc4_bo_free(bo); 250 } else { 251 break; 252 } 253 } 254 255 if (dump_stats && freed_any) { 256 fprintf(stderr, "Freed stale BOs:\n"); 257 vc4_bo_dump_stats(screen); 258 } 259 } 260 261 static void 262 vc4_bo_cache_free_all(struct vc4_bo_cache *cache) 263 { 264 pipe_mutex_lock(cache->lock); 265 list_for_each_entry_safe(struct vc4_bo, bo, &cache->time_list, 266 time_list) { 267 vc4_bo_remove_from_cache(cache, bo); 268 vc4_bo_free(bo); 269 } 270 pipe_mutex_unlock(cache->lock); 271 } 272 273 void 274 vc4_bo_last_unreference_locked_timed(struct vc4_bo *bo, time_t time) 275 { 276 struct vc4_screen *screen = bo->screen; 277 struct vc4_bo_cache *cache = &screen->bo_cache; 278 uint32_t page_index = bo->size / 4096 - 1; 279 280 if (!bo->private) { 281 vc4_bo_free(bo); 282 return; 283 } 284 285 if (cache->size_list_size <= page_index) { 286 struct list_head *new_list = 287 ralloc_array(screen, struct list_head, page_index + 1); 288 289 /* Move old list contents over (since the array has moved, and 290 * therefore the pointers to the list heads have to change). 291 */ 292 for (int i = 0; i < cache->size_list_size; i++) 293 list_replace(&cache->size_list[i], &new_list[i]); 294 for (int i = cache->size_list_size; i < page_index + 1; i++) 295 list_inithead(&new_list[i]); 296 297 cache->size_list = new_list; 298 cache->size_list_size = page_index + 1; 299 } 300 301 bo->free_time = time; 302 list_addtail(&bo->size_list, &cache->size_list[page_index]); 303 list_addtail(&bo->time_list, &cache->time_list); 304 cache->bo_count++; 305 cache->bo_size += bo->size; 306 if (dump_stats) { 307 fprintf(stderr, "Freed %s %dkb to cache:\n", 308 bo->name, bo->size / 1024); 309 vc4_bo_dump_stats(screen); 310 } 311 bo->name = NULL; 312 313 free_stale_bos(screen, time); 314 } 315 316 static struct vc4_bo * 317 vc4_bo_open_handle(struct vc4_screen *screen, 318 uint32_t winsys_stride, 319 uint32_t handle, uint32_t size) 320 { 321 struct vc4_bo *bo; 322 323 assert(size); 324 325 pipe_mutex_lock(screen->bo_handles_mutex); 326 327 bo = util_hash_table_get(screen->bo_handles, (void*)(uintptr_t)handle); 328 if (bo) { 329 pipe_reference(NULL, &bo->reference); 330 goto done; 331 } 332 333 bo = CALLOC_STRUCT(vc4_bo); 334 pipe_reference_init(&bo->reference, 1); 335 bo->screen = screen; 336 bo->handle = handle; 337 bo->size = size; 338 bo->name = "winsys"; 339 bo->private = false; 340 341 #ifdef USE_VC4_SIMULATOR 342 vc4_simulator_open_from_handle(screen->fd, winsys_stride, 343 bo->handle, bo->size); 344 bo->map = malloc(bo->size); 345 #endif 346 347 util_hash_table_set(screen->bo_handles, (void *)(uintptr_t)handle, bo); 348 349 done: 350 pipe_mutex_unlock(screen->bo_handles_mutex); 351 return bo; 352 } 353 354 struct vc4_bo * 355 vc4_bo_open_name(struct vc4_screen *screen, uint32_t name, 356 uint32_t winsys_stride) 357 { 358 struct drm_gem_open o = { 359 .name = name 360 }; 361 int ret = vc4_ioctl(screen->fd, DRM_IOCTL_GEM_OPEN, &o); 362 if (ret) { 363 fprintf(stderr, "Failed to open bo %d: %s\n", 364 name, strerror(errno)); 365 return NULL; 366 } 367 368 return vc4_bo_open_handle(screen, winsys_stride, o.handle, o.size); 369 } 370 371 struct vc4_bo * 372 vc4_bo_open_dmabuf(struct vc4_screen *screen, int fd, uint32_t winsys_stride) 373 { 374 uint32_t handle; 375 int ret = drmPrimeFDToHandle(screen->fd, fd, &handle); 376 int size; 377 if (ret) { 378 fprintf(stderr, "Failed to get vc4 handle for dmabuf %d\n", fd); 379 return NULL; 380 } 381 382 /* Determine the size of the bo we were handed. */ 383 size = lseek(fd, 0, SEEK_END); 384 if (size == -1) { 385 fprintf(stderr, "Couldn't get size of dmabuf fd %d.\n", fd); 386 return NULL; 387 } 388 389 return vc4_bo_open_handle(screen, winsys_stride, handle, size); 390 } 391 392 int 393 vc4_bo_get_dmabuf(struct vc4_bo *bo) 394 { 395 int fd; 396 int ret = drmPrimeHandleToFD(bo->screen->fd, bo->handle, 397 O_CLOEXEC, &fd); 398 if (ret != 0) { 399 fprintf(stderr, "Failed to export gem bo %d to dmabuf\n", 400 bo->handle); 401 return -1; 402 } 403 404 pipe_mutex_lock(bo->screen->bo_handles_mutex); 405 bo->private = false; 406 util_hash_table_set(bo->screen->bo_handles, (void *)(uintptr_t)bo->handle, bo); 407 pipe_mutex_unlock(bo->screen->bo_handles_mutex); 408 409 return fd; 410 } 411 412 struct vc4_bo * 413 vc4_bo_alloc_shader(struct vc4_screen *screen, const void *data, uint32_t size) 414 { 415 struct vc4_bo *bo; 416 int ret; 417 418 bo = CALLOC_STRUCT(vc4_bo); 419 if (!bo) 420 return NULL; 421 422 pipe_reference_init(&bo->reference, 1); 423 bo->screen = screen; 424 bo->size = align(size, 4096); 425 bo->name = "code"; 426 bo->private = false; /* Make sure it doesn't go back to the cache. */ 427 428 struct drm_vc4_create_shader_bo create = { 429 .size = size, 430 .data = (uintptr_t)data, 431 }; 432 433 ret = vc4_ioctl(screen->fd, DRM_IOCTL_VC4_CREATE_SHADER_BO, 434 &create); 435 bo->handle = create.handle; 436 437 if (ret != 0) { 438 fprintf(stderr, "create shader ioctl failure\n"); 439 abort(); 440 } 441 442 screen->bo_count++; 443 screen->bo_size += bo->size; 444 if (dump_stats) { 445 fprintf(stderr, "Allocated shader %dkb:\n", bo->size / 1024); 446 vc4_bo_dump_stats(screen); 447 } 448 449 return bo; 450 } 451 452 bool 453 vc4_bo_flink(struct vc4_bo *bo, uint32_t *name) 454 { 455 struct drm_gem_flink flink = { 456 .handle = bo->handle, 457 }; 458 int ret = vc4_ioctl(bo->screen->fd, DRM_IOCTL_GEM_FLINK, &flink); 459 if (ret) { 460 fprintf(stderr, "Failed to flink bo %d: %s\n", 461 bo->handle, strerror(errno)); 462 free(bo); 463 return false; 464 } 465 466 bo->private = false; 467 *name = flink.name; 468 469 return true; 470 } 471 472 static int vc4_wait_seqno_ioctl(int fd, uint64_t seqno, uint64_t timeout_ns) 473 { 474 struct drm_vc4_wait_seqno wait = { 475 .seqno = seqno, 476 .timeout_ns = timeout_ns, 477 }; 478 int ret = vc4_ioctl(fd, DRM_IOCTL_VC4_WAIT_SEQNO, &wait); 479 if (ret == -1) 480 return -errno; 481 else 482 return 0; 483 484 } 485 486 bool 487 vc4_wait_seqno(struct vc4_screen *screen, uint64_t seqno, uint64_t timeout_ns, 488 const char *reason) 489 { 490 if (screen->finished_seqno >= seqno) 491 return true; 492 493 if (unlikely(vc4_debug & VC4_DEBUG_PERF) && timeout_ns && reason) { 494 if (vc4_wait_seqno_ioctl(screen->fd, seqno, 0) == -ETIME) { 495 fprintf(stderr, "Blocking on seqno %lld for %s\n", 496 (long long)seqno, reason); 497 } 498 } 499 500 int ret = vc4_wait_seqno_ioctl(screen->fd, seqno, timeout_ns); 501 if (ret) { 502 if (ret != -ETIME) { 503 fprintf(stderr, "wait failed: %d\n", ret); 504 abort(); 505 } 506 507 return false; 508 } 509 510 screen->finished_seqno = seqno; 511 return true; 512 } 513 514 static int vc4_wait_bo_ioctl(int fd, uint32_t handle, uint64_t timeout_ns) 515 { 516 struct drm_vc4_wait_bo wait = { 517 .handle = handle, 518 .timeout_ns = timeout_ns, 519 }; 520 int ret = vc4_ioctl(fd, DRM_IOCTL_VC4_WAIT_BO, &wait); 521 if (ret == -1) 522 return -errno; 523 else 524 return 0; 525 526 } 527 528 bool 529 vc4_bo_wait(struct vc4_bo *bo, uint64_t timeout_ns, const char *reason) 530 { 531 struct vc4_screen *screen = bo->screen; 532 533 if (unlikely(vc4_debug & VC4_DEBUG_PERF) && timeout_ns && reason) { 534 if (vc4_wait_bo_ioctl(screen->fd, bo->handle, 0) == -ETIME) { 535 fprintf(stderr, "Blocking on %s BO for %s\n", 536 bo->name, reason); 537 } 538 } 539 540 int ret = vc4_wait_bo_ioctl(screen->fd, bo->handle, timeout_ns); 541 if (ret) { 542 if (ret != -ETIME) { 543 fprintf(stderr, "wait failed: %d\n", ret); 544 abort(); 545 } 546 547 return false; 548 } 549 550 return true; 551 } 552 553 void * 554 vc4_bo_map_unsynchronized(struct vc4_bo *bo) 555 { 556 uint64_t offset; 557 int ret; 558 559 if (bo->map) 560 return bo->map; 561 562 struct drm_vc4_mmap_bo map; 563 memset(&map, 0, sizeof(map)); 564 map.handle = bo->handle; 565 ret = vc4_ioctl(bo->screen->fd, DRM_IOCTL_VC4_MMAP_BO, &map); 566 offset = map.offset; 567 if (ret != 0) { 568 fprintf(stderr, "map ioctl failure\n"); 569 abort(); 570 } 571 572 bo->map = mmap(NULL, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED, 573 bo->screen->fd, offset); 574 if (bo->map == MAP_FAILED) { 575 fprintf(stderr, "mmap of bo %d (offset 0x%016llx, size %d) failed\n", 576 bo->handle, (long long)offset, bo->size); 577 abort(); 578 } 579 VG(VALGRIND_MALLOCLIKE_BLOCK(bo->map, bo->size, 0, false)); 580 581 return bo->map; 582 } 583 584 void * 585 vc4_bo_map(struct vc4_bo *bo) 586 { 587 void *map = vc4_bo_map_unsynchronized(bo); 588 589 bool ok = vc4_bo_wait(bo, PIPE_TIMEOUT_INFINITE, "bo map"); 590 if (!ok) { 591 fprintf(stderr, "BO wait for map failed\n"); 592 abort(); 593 } 594 595 return map; 596 } 597 598 void 599 vc4_bufmgr_destroy(struct pipe_screen *pscreen) 600 { 601 struct vc4_screen *screen = vc4_screen(pscreen); 602 struct vc4_bo_cache *cache = &screen->bo_cache; 603 604 vc4_bo_cache_free_all(cache); 605 606 if (dump_stats) { 607 fprintf(stderr, "BO stats after screen destroy:\n"); 608 vc4_bo_dump_stats(screen); 609 } 610 } 611