1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright 2021 Google LLC 3bf215546Sopenharmony_ci * SPDX-License-Identifier: MIT 4bf215546Sopenharmony_ci */ 5bf215546Sopenharmony_ci 6bf215546Sopenharmony_ci#include "vn_renderer_internal.h" 7bf215546Sopenharmony_ci 8bf215546Sopenharmony_ci/* 3 seconds */ 9bf215546Sopenharmony_ci#define VN_RENDERER_SHMEM_CACHE_EXPIRACY (3ll * 1000 * 1000) 10bf215546Sopenharmony_ci 11bf215546Sopenharmony_civoid 12bf215546Sopenharmony_civn_renderer_shmem_cache_init(struct vn_renderer_shmem_cache *cache, 13bf215546Sopenharmony_ci struct vn_renderer *renderer, 14bf215546Sopenharmony_ci vn_renderer_shmem_cache_destroy_func destroy_func) 15bf215546Sopenharmony_ci{ 16bf215546Sopenharmony_ci /* cache->bucket_mask is 32-bit and u_bit_scan is used */ 17bf215546Sopenharmony_ci static_assert(ARRAY_SIZE(cache->buckets) <= 32, ""); 18bf215546Sopenharmony_ci 19bf215546Sopenharmony_ci cache->renderer = renderer; 20bf215546Sopenharmony_ci cache->destroy_func = destroy_func; 21bf215546Sopenharmony_ci 22bf215546Sopenharmony_ci simple_mtx_init(&cache->mutex, mtx_plain); 23bf215546Sopenharmony_ci 24bf215546Sopenharmony_ci for (uint32_t i = 0; i < ARRAY_SIZE(cache->buckets); i++) { 25bf215546Sopenharmony_ci struct vn_renderer_shmem_bucket *bucket = &cache->buckets[i]; 26bf215546Sopenharmony_ci list_inithead(&bucket->shmems); 27bf215546Sopenharmony_ci } 28bf215546Sopenharmony_ci 29bf215546Sopenharmony_ci cache->initialized = true; 30bf215546Sopenharmony_ci} 31bf215546Sopenharmony_ci 32bf215546Sopenharmony_civoid 33bf215546Sopenharmony_civn_renderer_shmem_cache_fini(struct vn_renderer_shmem_cache *cache) 34bf215546Sopenharmony_ci{ 35bf215546Sopenharmony_ci if (!cache->initialized) 36bf215546Sopenharmony_ci return; 37bf215546Sopenharmony_ci 38bf215546Sopenharmony_ci while (cache->bucket_mask) { 39bf215546Sopenharmony_ci const int idx = u_bit_scan(&cache->bucket_mask); 40bf215546Sopenharmony_ci struct vn_renderer_shmem_bucket *bucket = &cache->buckets[idx]; 41bf215546Sopenharmony_ci 42bf215546Sopenharmony_ci list_for_each_entry_safe(struct vn_renderer_shmem, shmem, 43bf215546Sopenharmony_ci &bucket->shmems, cache_head) 44bf215546Sopenharmony_ci cache->destroy_func(cache->renderer, shmem); 45bf215546Sopenharmony_ci } 46bf215546Sopenharmony_ci 47bf215546Sopenharmony_ci simple_mtx_destroy(&cache->mutex); 48bf215546Sopenharmony_ci} 49bf215546Sopenharmony_ci 50bf215546Sopenharmony_cistatic struct vn_renderer_shmem_bucket * 51bf215546Sopenharmony_cichoose_bucket(struct vn_renderer_shmem_cache *cache, 52bf215546Sopenharmony_ci size_t size, 53bf215546Sopenharmony_ci int *out_idx) 54bf215546Sopenharmony_ci{ 55bf215546Sopenharmony_ci assert(size); 56bf215546Sopenharmony_ci if (unlikely(!util_is_power_of_two_or_zero64(size))) 57bf215546Sopenharmony_ci return NULL; 58bf215546Sopenharmony_ci 59bf215546Sopenharmony_ci const uint32_t idx = ffsll(size) - 1; 60bf215546Sopenharmony_ci if (unlikely(idx >= ARRAY_SIZE(cache->buckets))) 61bf215546Sopenharmony_ci return NULL; 62bf215546Sopenharmony_ci 63bf215546Sopenharmony_ci *out_idx = idx; 64bf215546Sopenharmony_ci return &cache->buckets[idx]; 65bf215546Sopenharmony_ci} 66bf215546Sopenharmony_ci 67bf215546Sopenharmony_cistatic void 68bf215546Sopenharmony_civn_renderer_shmem_cache_remove_expired_locked( 69bf215546Sopenharmony_ci struct vn_renderer_shmem_cache *cache, int64_t now) 70bf215546Sopenharmony_ci{ 71bf215546Sopenharmony_ci uint32_t bucket_mask = cache->bucket_mask; 72bf215546Sopenharmony_ci while (bucket_mask) { 73bf215546Sopenharmony_ci const int idx = u_bit_scan(&bucket_mask); 74bf215546Sopenharmony_ci struct vn_renderer_shmem_bucket *bucket = &cache->buckets[idx]; 75bf215546Sopenharmony_ci 76bf215546Sopenharmony_ci assert(!list_is_empty(&bucket->shmems)); 77bf215546Sopenharmony_ci const struct vn_renderer_shmem *last_shmem = list_last_entry( 78bf215546Sopenharmony_ci &bucket->shmems, struct vn_renderer_shmem, cache_head); 79bf215546Sopenharmony_ci 80bf215546Sopenharmony_ci /* remove expired shmems but keep at least the last one */ 81bf215546Sopenharmony_ci list_for_each_entry_safe(struct vn_renderer_shmem, shmem, 82bf215546Sopenharmony_ci &bucket->shmems, cache_head) { 83bf215546Sopenharmony_ci if (shmem == last_shmem || 84bf215546Sopenharmony_ci now - shmem->cache_timestamp < VN_RENDERER_SHMEM_CACHE_EXPIRACY) 85bf215546Sopenharmony_ci break; 86bf215546Sopenharmony_ci 87bf215546Sopenharmony_ci list_del(&shmem->cache_head); 88bf215546Sopenharmony_ci cache->destroy_func(cache->renderer, shmem); 89bf215546Sopenharmony_ci } 90bf215546Sopenharmony_ci } 91bf215546Sopenharmony_ci} 92bf215546Sopenharmony_ci 93bf215546Sopenharmony_cibool 94bf215546Sopenharmony_civn_renderer_shmem_cache_add(struct vn_renderer_shmem_cache *cache, 95bf215546Sopenharmony_ci struct vn_renderer_shmem *shmem) 96bf215546Sopenharmony_ci{ 97bf215546Sopenharmony_ci assert(!vn_refcount_is_valid(&shmem->refcount)); 98bf215546Sopenharmony_ci 99bf215546Sopenharmony_ci int idx; 100bf215546Sopenharmony_ci struct vn_renderer_shmem_bucket *bucket = 101bf215546Sopenharmony_ci choose_bucket(cache, shmem->mmap_size, &idx); 102bf215546Sopenharmony_ci if (!bucket) 103bf215546Sopenharmony_ci return false; 104bf215546Sopenharmony_ci 105bf215546Sopenharmony_ci const int64_t now = os_time_get(); 106bf215546Sopenharmony_ci shmem->cache_timestamp = now; 107bf215546Sopenharmony_ci 108bf215546Sopenharmony_ci simple_mtx_lock(&cache->mutex); 109bf215546Sopenharmony_ci 110bf215546Sopenharmony_ci vn_renderer_shmem_cache_remove_expired_locked(cache, now); 111bf215546Sopenharmony_ci 112bf215546Sopenharmony_ci list_addtail(&shmem->cache_head, &bucket->shmems); 113bf215546Sopenharmony_ci cache->bucket_mask |= 1 << idx; 114bf215546Sopenharmony_ci 115bf215546Sopenharmony_ci simple_mtx_unlock(&cache->mutex); 116bf215546Sopenharmony_ci 117bf215546Sopenharmony_ci return true; 118bf215546Sopenharmony_ci} 119bf215546Sopenharmony_ci 120bf215546Sopenharmony_cistruct vn_renderer_shmem * 121bf215546Sopenharmony_civn_renderer_shmem_cache_get(struct vn_renderer_shmem_cache *cache, 122bf215546Sopenharmony_ci size_t size) 123bf215546Sopenharmony_ci{ 124bf215546Sopenharmony_ci int idx; 125bf215546Sopenharmony_ci struct vn_renderer_shmem_bucket *bucket = choose_bucket(cache, size, &idx); 126bf215546Sopenharmony_ci if (!bucket) { 127bf215546Sopenharmony_ci VN_TRACE_SCOPE("shmem cache skip"); 128bf215546Sopenharmony_ci simple_mtx_lock(&cache->mutex); 129bf215546Sopenharmony_ci cache->debug.cache_skip_count++; 130bf215546Sopenharmony_ci simple_mtx_unlock(&cache->mutex); 131bf215546Sopenharmony_ci return NULL; 132bf215546Sopenharmony_ci } 133bf215546Sopenharmony_ci 134bf215546Sopenharmony_ci struct vn_renderer_shmem *shmem = NULL; 135bf215546Sopenharmony_ci 136bf215546Sopenharmony_ci simple_mtx_lock(&cache->mutex); 137bf215546Sopenharmony_ci if (cache->bucket_mask & (1 << idx)) { 138bf215546Sopenharmony_ci assert(!list_is_empty(&bucket->shmems)); 139bf215546Sopenharmony_ci shmem = list_first_entry(&bucket->shmems, struct vn_renderer_shmem, 140bf215546Sopenharmony_ci cache_head); 141bf215546Sopenharmony_ci list_del(&shmem->cache_head); 142bf215546Sopenharmony_ci 143bf215546Sopenharmony_ci if (list_is_empty(&bucket->shmems)) 144bf215546Sopenharmony_ci cache->bucket_mask &= ~(1 << idx); 145bf215546Sopenharmony_ci 146bf215546Sopenharmony_ci cache->debug.cache_hit_count++; 147bf215546Sopenharmony_ci } else { 148bf215546Sopenharmony_ci VN_TRACE_SCOPE("shmem cache miss"); 149bf215546Sopenharmony_ci cache->debug.cache_miss_count++; 150bf215546Sopenharmony_ci } 151bf215546Sopenharmony_ci simple_mtx_unlock(&cache->mutex); 152bf215546Sopenharmony_ci 153bf215546Sopenharmony_ci return shmem; 154bf215546Sopenharmony_ci} 155bf215546Sopenharmony_ci 156bf215546Sopenharmony_ci/* for debugging only */ 157bf215546Sopenharmony_civoid 158bf215546Sopenharmony_civn_renderer_shmem_cache_debug_dump(struct vn_renderer_shmem_cache *cache) 159bf215546Sopenharmony_ci{ 160bf215546Sopenharmony_ci simple_mtx_lock(&cache->mutex); 161bf215546Sopenharmony_ci 162bf215546Sopenharmony_ci vn_log(NULL, "dumping shmem cache"); 163bf215546Sopenharmony_ci vn_log(NULL, " cache skip: %d", cache->debug.cache_skip_count); 164bf215546Sopenharmony_ci vn_log(NULL, " cache hit: %d", cache->debug.cache_hit_count); 165bf215546Sopenharmony_ci vn_log(NULL, " cache miss: %d", cache->debug.cache_miss_count); 166bf215546Sopenharmony_ci 167bf215546Sopenharmony_ci uint32_t bucket_mask = cache->bucket_mask; 168bf215546Sopenharmony_ci while (bucket_mask) { 169bf215546Sopenharmony_ci const int idx = u_bit_scan(&bucket_mask); 170bf215546Sopenharmony_ci const struct vn_renderer_shmem_bucket *bucket = &cache->buckets[idx]; 171bf215546Sopenharmony_ci uint32_t count = 0; 172bf215546Sopenharmony_ci list_for_each_entry(struct vn_renderer_shmem, shmem, &bucket->shmems, 173bf215546Sopenharmony_ci cache_head) 174bf215546Sopenharmony_ci count++; 175bf215546Sopenharmony_ci if (count) 176bf215546Sopenharmony_ci vn_log(NULL, " buckets[%d]: %d shmems", idx, count); 177bf215546Sopenharmony_ci } 178bf215546Sopenharmony_ci 179bf215546Sopenharmony_ci simple_mtx_unlock(&cache->mutex); 180bf215546Sopenharmony_ci} 181