1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright © 2019 Raspberry Pi Ltd 3bf215546Sopenharmony_ci * 4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10bf215546Sopenharmony_ci * 11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13bf215546Sopenharmony_ci * Software. 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21bf215546Sopenharmony_ci * IN THE SOFTWARE. 22bf215546Sopenharmony_ci */ 23bf215546Sopenharmony_ci 24bf215546Sopenharmony_ci#include "v3dv_private.h" 25bf215546Sopenharmony_ci 26bf215546Sopenharmony_ci#include <errno.h> 27bf215546Sopenharmony_ci#include <sys/mman.h> 28bf215546Sopenharmony_ci 29bf215546Sopenharmony_ci#include "drm-uapi/v3d_drm.h" 30bf215546Sopenharmony_ci#include "util/u_memory.h" 31bf215546Sopenharmony_ci 32bf215546Sopenharmony_ci/* Default max size of the bo cache, in MB. 33bf215546Sopenharmony_ci * 34bf215546Sopenharmony_ci * FIXME: we got this value when testing some apps using the rpi4 with 4GB, 35bf215546Sopenharmony_ci * but it should depend on the total amount of RAM. But for that we would need 36bf215546Sopenharmony_ci * to test on real hw with different amount of RAM. Using this value for now. 37bf215546Sopenharmony_ci */ 38bf215546Sopenharmony_ci#define DEFAULT_MAX_BO_CACHE_SIZE 512 39bf215546Sopenharmony_ci 40bf215546Sopenharmony_ci/* Discarded to use a V3D_DEBUG for this, as it would mean adding a run-time 41bf215546Sopenharmony_ci * check for most of the calls 42bf215546Sopenharmony_ci */ 43bf215546Sopenharmony_cistatic const bool dump_stats = false; 44bf215546Sopenharmony_ci 45bf215546Sopenharmony_cistatic void 46bf215546Sopenharmony_cibo_dump_stats(struct v3dv_device *device) 47bf215546Sopenharmony_ci{ 48bf215546Sopenharmony_ci struct v3dv_bo_cache *cache = &device->bo_cache; 49bf215546Sopenharmony_ci 50bf215546Sopenharmony_ci fprintf(stderr, " BOs allocated: %d\n", device->bo_count); 51bf215546Sopenharmony_ci fprintf(stderr, " BOs size: %dkb\n", device->bo_size / 1024); 52bf215546Sopenharmony_ci fprintf(stderr, " BOs cached: %d\n", cache->cache_count); 53bf215546Sopenharmony_ci fprintf(stderr, " BOs cached size: %dkb\n", cache->cache_size / 1024); 54bf215546Sopenharmony_ci 55bf215546Sopenharmony_ci if (!list_is_empty(&cache->time_list)) { 56bf215546Sopenharmony_ci struct v3dv_bo *first = list_first_entry(&cache->time_list, 57bf215546Sopenharmony_ci struct v3dv_bo, 58bf215546Sopenharmony_ci time_list); 59bf215546Sopenharmony_ci struct v3dv_bo *last = list_last_entry(&cache->time_list, 60bf215546Sopenharmony_ci struct v3dv_bo, 61bf215546Sopenharmony_ci time_list); 62bf215546Sopenharmony_ci 63bf215546Sopenharmony_ci fprintf(stderr, " oldest cache time: %ld\n", 64bf215546Sopenharmony_ci (long)first->free_time); 65bf215546Sopenharmony_ci fprintf(stderr, " newest cache time: %ld\n", 66bf215546Sopenharmony_ci (long)last->free_time); 67bf215546Sopenharmony_ci 68bf215546Sopenharmony_ci struct timespec time; 69bf215546Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &time); 70bf215546Sopenharmony_ci fprintf(stderr, " now: %lld\n", 71bf215546Sopenharmony_ci (long long)time.tv_sec); 72bf215546Sopenharmony_ci } 73bf215546Sopenharmony_ci 74bf215546Sopenharmony_ci if (cache->size_list_size) { 75bf215546Sopenharmony_ci uint32_t empty_size_list = 0; 76bf215546Sopenharmony_ci for (uint32_t i = 0; i < cache->size_list_size; i++) { 77bf215546Sopenharmony_ci if (list_is_empty(&cache->size_list[i])) 78bf215546Sopenharmony_ci empty_size_list++; 79bf215546Sopenharmony_ci } 80bf215546Sopenharmony_ci fprintf(stderr, " Empty size_list lists: %d\n", empty_size_list); 81bf215546Sopenharmony_ci } 82bf215546Sopenharmony_ci} 83bf215546Sopenharmony_ci 84bf215546Sopenharmony_cistatic void 85bf215546Sopenharmony_cibo_remove_from_cache(struct v3dv_bo_cache *cache, struct v3dv_bo *bo) 86bf215546Sopenharmony_ci{ 87bf215546Sopenharmony_ci list_del(&bo->time_list); 88bf215546Sopenharmony_ci list_del(&bo->size_list); 89bf215546Sopenharmony_ci 90bf215546Sopenharmony_ci cache->cache_count--; 91bf215546Sopenharmony_ci cache->cache_size -= bo->size; 92bf215546Sopenharmony_ci} 93bf215546Sopenharmony_ci 94bf215546Sopenharmony_cistatic struct v3dv_bo * 95bf215546Sopenharmony_cibo_from_cache(struct v3dv_device *device, uint32_t size, const char *name) 96bf215546Sopenharmony_ci{ 97bf215546Sopenharmony_ci struct v3dv_bo_cache *cache = &device->bo_cache; 98bf215546Sopenharmony_ci uint32_t page_index = size / 4096 - 1; 99bf215546Sopenharmony_ci 100bf215546Sopenharmony_ci if (cache->size_list_size <= page_index) 101bf215546Sopenharmony_ci return NULL; 102bf215546Sopenharmony_ci 103bf215546Sopenharmony_ci struct v3dv_bo *bo = NULL; 104bf215546Sopenharmony_ci 105bf215546Sopenharmony_ci mtx_lock(&cache->lock); 106bf215546Sopenharmony_ci if (!list_is_empty(&cache->size_list[page_index])) { 107bf215546Sopenharmony_ci bo = list_first_entry(&cache->size_list[page_index], 108bf215546Sopenharmony_ci struct v3dv_bo, size_list); 109bf215546Sopenharmony_ci 110bf215546Sopenharmony_ci /* Check that the BO has gone idle. If not, then we want to 111bf215546Sopenharmony_ci * allocate something new instead, since we assume that the 112bf215546Sopenharmony_ci * user will proceed to CPU map it and fill it with stuff. 113bf215546Sopenharmony_ci */ 114bf215546Sopenharmony_ci if (!v3dv_bo_wait(device, bo, 0)) { 115bf215546Sopenharmony_ci mtx_unlock(&cache->lock); 116bf215546Sopenharmony_ci return NULL; 117bf215546Sopenharmony_ci } 118bf215546Sopenharmony_ci 119bf215546Sopenharmony_ci bo_remove_from_cache(cache, bo); 120bf215546Sopenharmony_ci bo->name = name; 121bf215546Sopenharmony_ci p_atomic_set(&bo->refcnt, 1); 122bf215546Sopenharmony_ci } 123bf215546Sopenharmony_ci mtx_unlock(&cache->lock); 124bf215546Sopenharmony_ci return bo; 125bf215546Sopenharmony_ci} 126bf215546Sopenharmony_ci 127bf215546Sopenharmony_cistatic bool 128bf215546Sopenharmony_cibo_free(struct v3dv_device *device, 129bf215546Sopenharmony_ci struct v3dv_bo *bo) 130bf215546Sopenharmony_ci{ 131bf215546Sopenharmony_ci if (!bo) 132bf215546Sopenharmony_ci return true; 133bf215546Sopenharmony_ci 134bf215546Sopenharmony_ci assert(p_atomic_read(&bo->refcnt) == 0); 135bf215546Sopenharmony_ci assert(bo->map == NULL); 136bf215546Sopenharmony_ci 137bf215546Sopenharmony_ci struct drm_gem_close c; 138bf215546Sopenharmony_ci memset(&c, 0, sizeof(c)); 139bf215546Sopenharmony_ci c.handle = bo->handle; 140bf215546Sopenharmony_ci int ret = v3dv_ioctl(device->pdevice->render_fd, DRM_IOCTL_GEM_CLOSE, &c); 141bf215546Sopenharmony_ci if (ret != 0) 142bf215546Sopenharmony_ci fprintf(stderr, "close object %d: %s\n", bo->handle, strerror(errno)); 143bf215546Sopenharmony_ci 144bf215546Sopenharmony_ci device->bo_count--; 145bf215546Sopenharmony_ci device->bo_size -= bo->size; 146bf215546Sopenharmony_ci 147bf215546Sopenharmony_ci if (dump_stats) { 148bf215546Sopenharmony_ci fprintf(stderr, "Freed %s%s%dkb:\n", 149bf215546Sopenharmony_ci bo->name ? bo->name : "", 150bf215546Sopenharmony_ci bo->name ? " " : "", 151bf215546Sopenharmony_ci bo->size / 1024); 152bf215546Sopenharmony_ci bo_dump_stats(device); 153bf215546Sopenharmony_ci } 154bf215546Sopenharmony_ci 155bf215546Sopenharmony_ci /* Our BO structs are stored in a sparse array in the physical device, 156bf215546Sopenharmony_ci * so we don't want to free the BO pointer, instead we want to reset it 157bf215546Sopenharmony_ci * to 0, to signal that array entry as being free. 158bf215546Sopenharmony_ci */ 159bf215546Sopenharmony_ci memset(bo, 0, sizeof(*bo)); 160bf215546Sopenharmony_ci 161bf215546Sopenharmony_ci return ret == 0; 162bf215546Sopenharmony_ci} 163bf215546Sopenharmony_ci 164bf215546Sopenharmony_cistatic void 165bf215546Sopenharmony_cibo_cache_free_all(struct v3dv_device *device, 166bf215546Sopenharmony_ci bool with_lock) 167bf215546Sopenharmony_ci{ 168bf215546Sopenharmony_ci struct v3dv_bo_cache *cache = &device->bo_cache; 169bf215546Sopenharmony_ci 170bf215546Sopenharmony_ci if (with_lock) 171bf215546Sopenharmony_ci mtx_lock(&cache->lock); 172bf215546Sopenharmony_ci list_for_each_entry_safe(struct v3dv_bo, bo, &cache->time_list, 173bf215546Sopenharmony_ci time_list) { 174bf215546Sopenharmony_ci bo_remove_from_cache(cache, bo); 175bf215546Sopenharmony_ci bo_free(device, bo); 176bf215546Sopenharmony_ci } 177bf215546Sopenharmony_ci if (with_lock) 178bf215546Sopenharmony_ci mtx_unlock(&cache->lock); 179bf215546Sopenharmony_ci 180bf215546Sopenharmony_ci} 181bf215546Sopenharmony_ci 182bf215546Sopenharmony_civoid 183bf215546Sopenharmony_civ3dv_bo_init(struct v3dv_bo *bo, 184bf215546Sopenharmony_ci uint32_t handle, 185bf215546Sopenharmony_ci uint32_t size, 186bf215546Sopenharmony_ci uint32_t offset, 187bf215546Sopenharmony_ci const char *name, 188bf215546Sopenharmony_ci bool private) 189bf215546Sopenharmony_ci{ 190bf215546Sopenharmony_ci p_atomic_set(&bo->refcnt, 1); 191bf215546Sopenharmony_ci bo->handle = handle; 192bf215546Sopenharmony_ci bo->handle_bit = 1ull << (handle % 64); 193bf215546Sopenharmony_ci bo->size = size; 194bf215546Sopenharmony_ci bo->offset = offset; 195bf215546Sopenharmony_ci bo->map = NULL; 196bf215546Sopenharmony_ci bo->map_size = 0; 197bf215546Sopenharmony_ci bo->name = name; 198bf215546Sopenharmony_ci bo->private = private; 199bf215546Sopenharmony_ci bo->dumb_handle = -1; 200bf215546Sopenharmony_ci list_inithead(&bo->list_link); 201bf215546Sopenharmony_ci} 202bf215546Sopenharmony_ci 203bf215546Sopenharmony_cistruct v3dv_bo * 204bf215546Sopenharmony_civ3dv_bo_alloc(struct v3dv_device *device, 205bf215546Sopenharmony_ci uint32_t size, 206bf215546Sopenharmony_ci const char *name, 207bf215546Sopenharmony_ci bool private) 208bf215546Sopenharmony_ci{ 209bf215546Sopenharmony_ci struct v3dv_bo *bo; 210bf215546Sopenharmony_ci 211bf215546Sopenharmony_ci const uint32_t page_align = 4096; /* Always allocate full pages */ 212bf215546Sopenharmony_ci size = align(size, page_align); 213bf215546Sopenharmony_ci 214bf215546Sopenharmony_ci if (private) { 215bf215546Sopenharmony_ci bo = bo_from_cache(device, size, name); 216bf215546Sopenharmony_ci if (bo) { 217bf215546Sopenharmony_ci if (dump_stats) { 218bf215546Sopenharmony_ci fprintf(stderr, "Allocated %s %dkb from cache:\n", 219bf215546Sopenharmony_ci name, size / 1024); 220bf215546Sopenharmony_ci bo_dump_stats(device); 221bf215546Sopenharmony_ci } 222bf215546Sopenharmony_ci return bo; 223bf215546Sopenharmony_ci } 224bf215546Sopenharmony_ci } 225bf215546Sopenharmony_ci 226bf215546Sopenharmony_ci retry: 227bf215546Sopenharmony_ci ; 228bf215546Sopenharmony_ci 229bf215546Sopenharmony_ci bool cleared_and_retried = false; 230bf215546Sopenharmony_ci struct drm_v3d_create_bo create = { 231bf215546Sopenharmony_ci .size = size 232bf215546Sopenharmony_ci }; 233bf215546Sopenharmony_ci 234bf215546Sopenharmony_ci int ret = v3dv_ioctl(device->pdevice->render_fd, 235bf215546Sopenharmony_ci DRM_IOCTL_V3D_CREATE_BO, &create); 236bf215546Sopenharmony_ci if (ret != 0) { 237bf215546Sopenharmony_ci if (!list_is_empty(&device->bo_cache.time_list) && 238bf215546Sopenharmony_ci !cleared_and_retried) { 239bf215546Sopenharmony_ci cleared_and_retried = true; 240bf215546Sopenharmony_ci bo_cache_free_all(device, true); 241bf215546Sopenharmony_ci goto retry; 242bf215546Sopenharmony_ci } 243bf215546Sopenharmony_ci 244bf215546Sopenharmony_ci fprintf(stderr, "Failed to allocate device memory for BO\n"); 245bf215546Sopenharmony_ci return NULL; 246bf215546Sopenharmony_ci } 247bf215546Sopenharmony_ci 248bf215546Sopenharmony_ci assert(create.offset % page_align == 0); 249bf215546Sopenharmony_ci assert((create.offset & 0xffffffff) == create.offset); 250bf215546Sopenharmony_ci 251bf215546Sopenharmony_ci bo = v3dv_device_lookup_bo(device->pdevice, create.handle); 252bf215546Sopenharmony_ci assert(bo && bo->handle == 0); 253bf215546Sopenharmony_ci 254bf215546Sopenharmony_ci v3dv_bo_init(bo, create.handle, size, create.offset, name, private); 255bf215546Sopenharmony_ci 256bf215546Sopenharmony_ci device->bo_count++; 257bf215546Sopenharmony_ci device->bo_size += bo->size; 258bf215546Sopenharmony_ci if (dump_stats) { 259bf215546Sopenharmony_ci fprintf(stderr, "Allocated %s %dkb:\n", name, size / 1024); 260bf215546Sopenharmony_ci bo_dump_stats(device); 261bf215546Sopenharmony_ci } 262bf215546Sopenharmony_ci 263bf215546Sopenharmony_ci return bo; 264bf215546Sopenharmony_ci} 265bf215546Sopenharmony_ci 266bf215546Sopenharmony_cibool 267bf215546Sopenharmony_civ3dv_bo_map_unsynchronized(struct v3dv_device *device, 268bf215546Sopenharmony_ci struct v3dv_bo *bo, 269bf215546Sopenharmony_ci uint32_t size) 270bf215546Sopenharmony_ci{ 271bf215546Sopenharmony_ci assert(bo != NULL && size <= bo->size); 272bf215546Sopenharmony_ci 273bf215546Sopenharmony_ci if (bo->map) 274bf215546Sopenharmony_ci return bo->map; 275bf215546Sopenharmony_ci 276bf215546Sopenharmony_ci struct drm_v3d_mmap_bo map; 277bf215546Sopenharmony_ci memset(&map, 0, sizeof(map)); 278bf215546Sopenharmony_ci map.handle = bo->handle; 279bf215546Sopenharmony_ci int ret = v3dv_ioctl(device->pdevice->render_fd, 280bf215546Sopenharmony_ci DRM_IOCTL_V3D_MMAP_BO, &map); 281bf215546Sopenharmony_ci if (ret != 0) { 282bf215546Sopenharmony_ci fprintf(stderr, "map ioctl failure\n"); 283bf215546Sopenharmony_ci return false; 284bf215546Sopenharmony_ci } 285bf215546Sopenharmony_ci 286bf215546Sopenharmony_ci bo->map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, 287bf215546Sopenharmony_ci device->pdevice->render_fd, map.offset); 288bf215546Sopenharmony_ci if (bo->map == MAP_FAILED) { 289bf215546Sopenharmony_ci fprintf(stderr, "mmap of bo %d (offset 0x%016llx, size %d) failed\n", 290bf215546Sopenharmony_ci bo->handle, (long long)map.offset, (uint32_t)bo->size); 291bf215546Sopenharmony_ci return false; 292bf215546Sopenharmony_ci } 293bf215546Sopenharmony_ci VG(VALGRIND_MALLOCLIKE_BLOCK(bo->map, bo->size, 0, false)); 294bf215546Sopenharmony_ci 295bf215546Sopenharmony_ci bo->map_size = size; 296bf215546Sopenharmony_ci 297bf215546Sopenharmony_ci return true; 298bf215546Sopenharmony_ci} 299bf215546Sopenharmony_ci 300bf215546Sopenharmony_cibool 301bf215546Sopenharmony_civ3dv_bo_wait(struct v3dv_device *device, 302bf215546Sopenharmony_ci struct v3dv_bo *bo, 303bf215546Sopenharmony_ci uint64_t timeout_ns) 304bf215546Sopenharmony_ci{ 305bf215546Sopenharmony_ci struct drm_v3d_wait_bo wait = { 306bf215546Sopenharmony_ci .handle = bo->handle, 307bf215546Sopenharmony_ci .timeout_ns = timeout_ns, 308bf215546Sopenharmony_ci }; 309bf215546Sopenharmony_ci return v3dv_ioctl(device->pdevice->render_fd, 310bf215546Sopenharmony_ci DRM_IOCTL_V3D_WAIT_BO, &wait) == 0; 311bf215546Sopenharmony_ci} 312bf215546Sopenharmony_ci 313bf215546Sopenharmony_cibool 314bf215546Sopenharmony_civ3dv_bo_map(struct v3dv_device *device, struct v3dv_bo *bo, uint32_t size) 315bf215546Sopenharmony_ci{ 316bf215546Sopenharmony_ci assert(bo && size <= bo->size); 317bf215546Sopenharmony_ci 318bf215546Sopenharmony_ci bool ok = v3dv_bo_map_unsynchronized(device, bo, size); 319bf215546Sopenharmony_ci if (!ok) 320bf215546Sopenharmony_ci return false; 321bf215546Sopenharmony_ci 322bf215546Sopenharmony_ci ok = v3dv_bo_wait(device, bo, PIPE_TIMEOUT_INFINITE); 323bf215546Sopenharmony_ci if (!ok) { 324bf215546Sopenharmony_ci fprintf(stderr, "memory wait for map failed\n"); 325bf215546Sopenharmony_ci return false; 326bf215546Sopenharmony_ci } 327bf215546Sopenharmony_ci 328bf215546Sopenharmony_ci return true; 329bf215546Sopenharmony_ci} 330bf215546Sopenharmony_ci 331bf215546Sopenharmony_civoid 332bf215546Sopenharmony_civ3dv_bo_unmap(struct v3dv_device *device, struct v3dv_bo *bo) 333bf215546Sopenharmony_ci{ 334bf215546Sopenharmony_ci assert(bo && bo->map && bo->map_size > 0); 335bf215546Sopenharmony_ci 336bf215546Sopenharmony_ci munmap(bo->map, bo->map_size); 337bf215546Sopenharmony_ci VG(VALGRIND_FREELIKE_BLOCK(bo->map, 0)); 338bf215546Sopenharmony_ci bo->map = NULL; 339bf215546Sopenharmony_ci bo->map_size = 0; 340bf215546Sopenharmony_ci} 341bf215546Sopenharmony_ci 342bf215546Sopenharmony_cistatic boolean 343bf215546Sopenharmony_cireallocate_size_list(struct v3dv_bo_cache *cache, 344bf215546Sopenharmony_ci struct v3dv_device *device, 345bf215546Sopenharmony_ci uint32_t size) 346bf215546Sopenharmony_ci{ 347bf215546Sopenharmony_ci struct list_head *new_list = 348bf215546Sopenharmony_ci vk_alloc(&device->vk.alloc, sizeof(struct list_head) * size, 8, 349bf215546Sopenharmony_ci VK_SYSTEM_ALLOCATION_SCOPE_DEVICE); 350bf215546Sopenharmony_ci 351bf215546Sopenharmony_ci if (!new_list) { 352bf215546Sopenharmony_ci fprintf(stderr, "Failed to allocate host memory for cache bo list\n"); 353bf215546Sopenharmony_ci return false; 354bf215546Sopenharmony_ci } 355bf215546Sopenharmony_ci struct list_head *old_list = cache->size_list; 356bf215546Sopenharmony_ci 357bf215546Sopenharmony_ci /* Move old list contents over (since the array has moved, and 358bf215546Sopenharmony_ci * therefore the pointers to the list heads have to change). 359bf215546Sopenharmony_ci */ 360bf215546Sopenharmony_ci for (int i = 0; i < cache->size_list_size; i++) { 361bf215546Sopenharmony_ci struct list_head *old_head = &cache->size_list[i]; 362bf215546Sopenharmony_ci if (list_is_empty(old_head)) { 363bf215546Sopenharmony_ci list_inithead(&new_list[i]); 364bf215546Sopenharmony_ci } else { 365bf215546Sopenharmony_ci new_list[i].next = old_head->next; 366bf215546Sopenharmony_ci new_list[i].prev = old_head->prev; 367bf215546Sopenharmony_ci new_list[i].next->prev = &new_list[i]; 368bf215546Sopenharmony_ci new_list[i].prev->next = &new_list[i]; 369bf215546Sopenharmony_ci } 370bf215546Sopenharmony_ci } 371bf215546Sopenharmony_ci for (int i = cache->size_list_size; i < size; i++) 372bf215546Sopenharmony_ci list_inithead(&new_list[i]); 373bf215546Sopenharmony_ci 374bf215546Sopenharmony_ci cache->size_list = new_list; 375bf215546Sopenharmony_ci cache->size_list_size = size; 376bf215546Sopenharmony_ci vk_free(&device->vk.alloc, old_list); 377bf215546Sopenharmony_ci 378bf215546Sopenharmony_ci return true; 379bf215546Sopenharmony_ci} 380bf215546Sopenharmony_ci 381bf215546Sopenharmony_civoid 382bf215546Sopenharmony_civ3dv_bo_cache_init(struct v3dv_device *device) 383bf215546Sopenharmony_ci{ 384bf215546Sopenharmony_ci device->bo_size = 0; 385bf215546Sopenharmony_ci device->bo_count = 0; 386bf215546Sopenharmony_ci list_inithead(&device->bo_cache.time_list); 387bf215546Sopenharmony_ci /* FIXME: perhaps set a initial size for the size-list, to avoid run-time 388bf215546Sopenharmony_ci * reallocations 389bf215546Sopenharmony_ci */ 390bf215546Sopenharmony_ci device->bo_cache.size_list_size = 0; 391bf215546Sopenharmony_ci 392bf215546Sopenharmony_ci const char *max_cache_size_str = getenv("V3DV_MAX_BO_CACHE_SIZE"); 393bf215546Sopenharmony_ci if (max_cache_size_str == NULL) 394bf215546Sopenharmony_ci device->bo_cache.max_cache_size = DEFAULT_MAX_BO_CACHE_SIZE; 395bf215546Sopenharmony_ci else 396bf215546Sopenharmony_ci device->bo_cache.max_cache_size = atoll(max_cache_size_str); 397bf215546Sopenharmony_ci 398bf215546Sopenharmony_ci if (dump_stats) { 399bf215546Sopenharmony_ci fprintf(stderr, "MAX BO CACHE SIZE: %iMB\n", device->bo_cache.max_cache_size); 400bf215546Sopenharmony_ci } 401bf215546Sopenharmony_ci 402bf215546Sopenharmony_ci device->bo_cache.max_cache_size *= 1024 * 1024; 403bf215546Sopenharmony_ci device->bo_cache.cache_count = 0; 404bf215546Sopenharmony_ci device->bo_cache.cache_size = 0; 405bf215546Sopenharmony_ci} 406bf215546Sopenharmony_ci 407bf215546Sopenharmony_civoid 408bf215546Sopenharmony_civ3dv_bo_cache_destroy(struct v3dv_device *device) 409bf215546Sopenharmony_ci{ 410bf215546Sopenharmony_ci bo_cache_free_all(device, true); 411bf215546Sopenharmony_ci vk_free(&device->vk.alloc, device->bo_cache.size_list); 412bf215546Sopenharmony_ci 413bf215546Sopenharmony_ci if (dump_stats) { 414bf215546Sopenharmony_ci fprintf(stderr, "BO stats after screen destroy:\n"); 415bf215546Sopenharmony_ci bo_dump_stats(device); 416bf215546Sopenharmony_ci } 417bf215546Sopenharmony_ci} 418bf215546Sopenharmony_ci 419bf215546Sopenharmony_ci 420bf215546Sopenharmony_cistatic void 421bf215546Sopenharmony_cifree_stale_bos(struct v3dv_device *device, 422bf215546Sopenharmony_ci time_t time) 423bf215546Sopenharmony_ci{ 424bf215546Sopenharmony_ci struct v3dv_bo_cache *cache = &device->bo_cache; 425bf215546Sopenharmony_ci bool freed_any = false; 426bf215546Sopenharmony_ci 427bf215546Sopenharmony_ci list_for_each_entry_safe(struct v3dv_bo, bo, &cache->time_list, 428bf215546Sopenharmony_ci time_list) { 429bf215546Sopenharmony_ci /* If it's more than a second old, free it. */ 430bf215546Sopenharmony_ci if (time - bo->free_time > 2) { 431bf215546Sopenharmony_ci if (dump_stats && !freed_any) { 432bf215546Sopenharmony_ci fprintf(stderr, "Freeing stale BOs:\n"); 433bf215546Sopenharmony_ci bo_dump_stats(device); 434bf215546Sopenharmony_ci freed_any = true; 435bf215546Sopenharmony_ci } 436bf215546Sopenharmony_ci 437bf215546Sopenharmony_ci bo_remove_from_cache(cache, bo); 438bf215546Sopenharmony_ci bo_free(device, bo); 439bf215546Sopenharmony_ci } else { 440bf215546Sopenharmony_ci break; 441bf215546Sopenharmony_ci } 442bf215546Sopenharmony_ci } 443bf215546Sopenharmony_ci 444bf215546Sopenharmony_ci if (dump_stats && freed_any) { 445bf215546Sopenharmony_ci fprintf(stderr, "Freed stale BOs:\n"); 446bf215546Sopenharmony_ci bo_dump_stats(device); 447bf215546Sopenharmony_ci } 448bf215546Sopenharmony_ci} 449bf215546Sopenharmony_ci 450bf215546Sopenharmony_cibool 451bf215546Sopenharmony_civ3dv_bo_free(struct v3dv_device *device, 452bf215546Sopenharmony_ci struct v3dv_bo *bo) 453bf215546Sopenharmony_ci{ 454bf215546Sopenharmony_ci if (!bo) 455bf215546Sopenharmony_ci return true; 456bf215546Sopenharmony_ci 457bf215546Sopenharmony_ci if (!p_atomic_dec_zero(&bo->refcnt)) 458bf215546Sopenharmony_ci return true; 459bf215546Sopenharmony_ci 460bf215546Sopenharmony_ci if (bo->map) 461bf215546Sopenharmony_ci v3dv_bo_unmap(device, bo); 462bf215546Sopenharmony_ci 463bf215546Sopenharmony_ci struct timespec time; 464bf215546Sopenharmony_ci struct v3dv_bo_cache *cache = &device->bo_cache; 465bf215546Sopenharmony_ci uint32_t page_index = bo->size / 4096 - 1; 466bf215546Sopenharmony_ci 467bf215546Sopenharmony_ci if (bo->private && 468bf215546Sopenharmony_ci bo->size > cache->max_cache_size - cache->cache_size) { 469bf215546Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &time); 470bf215546Sopenharmony_ci mtx_lock(&cache->lock); 471bf215546Sopenharmony_ci free_stale_bos(device, time.tv_sec); 472bf215546Sopenharmony_ci mtx_unlock(&cache->lock); 473bf215546Sopenharmony_ci } 474bf215546Sopenharmony_ci 475bf215546Sopenharmony_ci if (!bo->private || 476bf215546Sopenharmony_ci bo->size > cache->max_cache_size - cache->cache_size) { 477bf215546Sopenharmony_ci return bo_free(device, bo); 478bf215546Sopenharmony_ci } 479bf215546Sopenharmony_ci 480bf215546Sopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &time); 481bf215546Sopenharmony_ci mtx_lock(&cache->lock); 482bf215546Sopenharmony_ci 483bf215546Sopenharmony_ci if (cache->size_list_size <= page_index) { 484bf215546Sopenharmony_ci if (!reallocate_size_list(cache, device, page_index + 1)) { 485bf215546Sopenharmony_ci bool outcome = bo_free(device, bo); 486bf215546Sopenharmony_ci /* If the reallocation failed, it usually means that we are out of 487bf215546Sopenharmony_ci * memory, so we also free all the bo cache. We need to call it to 488bf215546Sopenharmony_ci * not use the cache lock, as we are already under it. 489bf215546Sopenharmony_ci */ 490bf215546Sopenharmony_ci bo_cache_free_all(device, false); 491bf215546Sopenharmony_ci mtx_unlock(&cache->lock); 492bf215546Sopenharmony_ci return outcome; 493bf215546Sopenharmony_ci } 494bf215546Sopenharmony_ci } 495bf215546Sopenharmony_ci 496bf215546Sopenharmony_ci bo->free_time = time.tv_sec; 497bf215546Sopenharmony_ci list_addtail(&bo->size_list, &cache->size_list[page_index]); 498bf215546Sopenharmony_ci list_addtail(&bo->time_list, &cache->time_list); 499bf215546Sopenharmony_ci 500bf215546Sopenharmony_ci cache->cache_count++; 501bf215546Sopenharmony_ci cache->cache_size += bo->size; 502bf215546Sopenharmony_ci 503bf215546Sopenharmony_ci if (dump_stats) { 504bf215546Sopenharmony_ci fprintf(stderr, "Freed %s %dkb to cache:\n", 505bf215546Sopenharmony_ci bo->name, bo->size / 1024); 506bf215546Sopenharmony_ci bo_dump_stats(device); 507bf215546Sopenharmony_ci } 508bf215546Sopenharmony_ci bo->name = NULL; 509bf215546Sopenharmony_ci 510bf215546Sopenharmony_ci free_stale_bos(device, time.tv_sec); 511bf215546Sopenharmony_ci 512bf215546Sopenharmony_ci mtx_unlock(&cache->lock); 513bf215546Sopenharmony_ci 514bf215546Sopenharmony_ci return true; 515bf215546Sopenharmony_ci} 516