1bf215546Sopenharmony_ci/************************************************************************** 2bf215546Sopenharmony_ci * 3bf215546Sopenharmony_ci * Copyright 2007-2008 VMware, Inc. 4bf215546Sopenharmony_ci * Copyright 2015 Advanced Micro Devices, Inc. 5bf215546Sopenharmony_ci * All Rights Reserved. 6bf215546Sopenharmony_ci * 7bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 8bf215546Sopenharmony_ci * copy of this software and associated documentation files (the 9bf215546Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 10bf215546Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 11bf215546Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 12bf215546Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 13bf215546Sopenharmony_ci * the following conditions: 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the 16bf215546Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 17bf215546Sopenharmony_ci * of the Software. 18bf215546Sopenharmony_ci * 19bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 20bf215546Sopenharmony_ci * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21bf215546Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. 22bf215546Sopenharmony_ci * IN NO EVENT SHALL AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR 23bf215546Sopenharmony_ci * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 24bf215546Sopenharmony_ci * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25bf215546Sopenharmony_ci * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26bf215546Sopenharmony_ci * 27bf215546Sopenharmony_ci **************************************************************************/ 28bf215546Sopenharmony_ci 29bf215546Sopenharmony_ci#include "pb_cache.h" 30bf215546Sopenharmony_ci#include "util/u_memory.h" 31bf215546Sopenharmony_ci#include "util/os_time.h" 32bf215546Sopenharmony_ci 33bf215546Sopenharmony_ci 34bf215546Sopenharmony_ci/** 35bf215546Sopenharmony_ci * Actually destroy the buffer. 36bf215546Sopenharmony_ci */ 37bf215546Sopenharmony_cistatic void 38bf215546Sopenharmony_cidestroy_buffer_locked(struct pb_cache_entry *entry) 39bf215546Sopenharmony_ci{ 40bf215546Sopenharmony_ci struct pb_cache *mgr = entry->mgr; 41bf215546Sopenharmony_ci struct pb_buffer *buf = entry->buffer; 42bf215546Sopenharmony_ci 43bf215546Sopenharmony_ci assert(!pipe_is_referenced(&buf->reference)); 44bf215546Sopenharmony_ci if (list_is_linked(&entry->head)) { 45bf215546Sopenharmony_ci list_del(&entry->head); 46bf215546Sopenharmony_ci assert(mgr->num_buffers); 47bf215546Sopenharmony_ci --mgr->num_buffers; 48bf215546Sopenharmony_ci mgr->cache_size -= buf->size; 49bf215546Sopenharmony_ci } 50bf215546Sopenharmony_ci mgr->destroy_buffer(mgr->winsys, buf); 51bf215546Sopenharmony_ci} 52bf215546Sopenharmony_ci 53bf215546Sopenharmony_ci/** 54bf215546Sopenharmony_ci * Free as many cache buffers from the list head as possible. 55bf215546Sopenharmony_ci */ 56bf215546Sopenharmony_cistatic void 57bf215546Sopenharmony_cirelease_expired_buffers_locked(struct list_head *cache, 58bf215546Sopenharmony_ci int64_t current_time) 59bf215546Sopenharmony_ci{ 60bf215546Sopenharmony_ci struct list_head *curr, *next; 61bf215546Sopenharmony_ci struct pb_cache_entry *entry; 62bf215546Sopenharmony_ci 63bf215546Sopenharmony_ci curr = cache->next; 64bf215546Sopenharmony_ci next = curr->next; 65bf215546Sopenharmony_ci while (curr != cache) { 66bf215546Sopenharmony_ci entry = list_entry(curr, struct pb_cache_entry, head); 67bf215546Sopenharmony_ci 68bf215546Sopenharmony_ci if (!os_time_timeout(entry->start, entry->end, current_time)) 69bf215546Sopenharmony_ci break; 70bf215546Sopenharmony_ci 71bf215546Sopenharmony_ci destroy_buffer_locked(entry); 72bf215546Sopenharmony_ci 73bf215546Sopenharmony_ci curr = next; 74bf215546Sopenharmony_ci next = curr->next; 75bf215546Sopenharmony_ci } 76bf215546Sopenharmony_ci} 77bf215546Sopenharmony_ci 78bf215546Sopenharmony_ci/** 79bf215546Sopenharmony_ci * Add a buffer to the cache. This is typically done when the buffer is 80bf215546Sopenharmony_ci * being released. 81bf215546Sopenharmony_ci */ 82bf215546Sopenharmony_civoid 83bf215546Sopenharmony_cipb_cache_add_buffer(struct pb_cache_entry *entry) 84bf215546Sopenharmony_ci{ 85bf215546Sopenharmony_ci struct pb_cache *mgr = entry->mgr; 86bf215546Sopenharmony_ci struct list_head *cache = &mgr->buckets[entry->bucket_index]; 87bf215546Sopenharmony_ci struct pb_buffer *buf = entry->buffer; 88bf215546Sopenharmony_ci unsigned i; 89bf215546Sopenharmony_ci 90bf215546Sopenharmony_ci simple_mtx_lock(&mgr->mutex); 91bf215546Sopenharmony_ci assert(!pipe_is_referenced(&buf->reference)); 92bf215546Sopenharmony_ci 93bf215546Sopenharmony_ci int64_t current_time = os_time_get(); 94bf215546Sopenharmony_ci 95bf215546Sopenharmony_ci for (i = 0; i < mgr->num_heaps; i++) 96bf215546Sopenharmony_ci release_expired_buffers_locked(&mgr->buckets[i], current_time); 97bf215546Sopenharmony_ci 98bf215546Sopenharmony_ci /* Directly release any buffer that exceeds the limit. */ 99bf215546Sopenharmony_ci if (mgr->cache_size + buf->size > mgr->max_cache_size) { 100bf215546Sopenharmony_ci mgr->destroy_buffer(mgr->winsys, buf); 101bf215546Sopenharmony_ci simple_mtx_unlock(&mgr->mutex); 102bf215546Sopenharmony_ci return; 103bf215546Sopenharmony_ci } 104bf215546Sopenharmony_ci 105bf215546Sopenharmony_ci entry->start = os_time_get(); 106bf215546Sopenharmony_ci entry->end = entry->start + mgr->usecs; 107bf215546Sopenharmony_ci list_addtail(&entry->head, cache); 108bf215546Sopenharmony_ci ++mgr->num_buffers; 109bf215546Sopenharmony_ci mgr->cache_size += buf->size; 110bf215546Sopenharmony_ci simple_mtx_unlock(&mgr->mutex); 111bf215546Sopenharmony_ci} 112bf215546Sopenharmony_ci 113bf215546Sopenharmony_ci/** 114bf215546Sopenharmony_ci * \return 1 if compatible and can be reclaimed 115bf215546Sopenharmony_ci * 0 if incompatible 116bf215546Sopenharmony_ci * -1 if compatible and can't be reclaimed 117bf215546Sopenharmony_ci */ 118bf215546Sopenharmony_cistatic int 119bf215546Sopenharmony_cipb_cache_is_buffer_compat(struct pb_cache_entry *entry, 120bf215546Sopenharmony_ci pb_size size, unsigned alignment, unsigned usage) 121bf215546Sopenharmony_ci{ 122bf215546Sopenharmony_ci struct pb_cache *mgr = entry->mgr; 123bf215546Sopenharmony_ci struct pb_buffer *buf = entry->buffer; 124bf215546Sopenharmony_ci 125bf215546Sopenharmony_ci if (!pb_check_usage(usage, buf->usage)) 126bf215546Sopenharmony_ci return 0; 127bf215546Sopenharmony_ci 128bf215546Sopenharmony_ci /* be lenient with size */ 129bf215546Sopenharmony_ci if (buf->size < size || 130bf215546Sopenharmony_ci buf->size > (unsigned) (mgr->size_factor * size)) 131bf215546Sopenharmony_ci return 0; 132bf215546Sopenharmony_ci 133bf215546Sopenharmony_ci if (usage & mgr->bypass_usage) 134bf215546Sopenharmony_ci return 0; 135bf215546Sopenharmony_ci 136bf215546Sopenharmony_ci if (!pb_check_alignment(alignment, 1u << buf->alignment_log2)) 137bf215546Sopenharmony_ci return 0; 138bf215546Sopenharmony_ci 139bf215546Sopenharmony_ci return mgr->can_reclaim(mgr->winsys, buf) ? 1 : -1; 140bf215546Sopenharmony_ci} 141bf215546Sopenharmony_ci 142bf215546Sopenharmony_ci/** 143bf215546Sopenharmony_ci * Find a compatible buffer in the cache, return it, and remove it 144bf215546Sopenharmony_ci * from the cache. 145bf215546Sopenharmony_ci */ 146bf215546Sopenharmony_cistruct pb_buffer * 147bf215546Sopenharmony_cipb_cache_reclaim_buffer(struct pb_cache *mgr, pb_size size, 148bf215546Sopenharmony_ci unsigned alignment, unsigned usage, 149bf215546Sopenharmony_ci unsigned bucket_index) 150bf215546Sopenharmony_ci{ 151bf215546Sopenharmony_ci struct pb_cache_entry *entry; 152bf215546Sopenharmony_ci struct pb_cache_entry *cur_entry; 153bf215546Sopenharmony_ci struct list_head *cur, *next; 154bf215546Sopenharmony_ci int64_t now; 155bf215546Sopenharmony_ci int ret = 0; 156bf215546Sopenharmony_ci 157bf215546Sopenharmony_ci assert(bucket_index < mgr->num_heaps); 158bf215546Sopenharmony_ci struct list_head *cache = &mgr->buckets[bucket_index]; 159bf215546Sopenharmony_ci 160bf215546Sopenharmony_ci simple_mtx_lock(&mgr->mutex); 161bf215546Sopenharmony_ci 162bf215546Sopenharmony_ci entry = NULL; 163bf215546Sopenharmony_ci cur = cache->next; 164bf215546Sopenharmony_ci next = cur->next; 165bf215546Sopenharmony_ci 166bf215546Sopenharmony_ci /* search in the expired buffers, freeing them in the process */ 167bf215546Sopenharmony_ci now = os_time_get(); 168bf215546Sopenharmony_ci while (cur != cache) { 169bf215546Sopenharmony_ci cur_entry = list_entry(cur, struct pb_cache_entry, head); 170bf215546Sopenharmony_ci 171bf215546Sopenharmony_ci if (!entry && (ret = pb_cache_is_buffer_compat(cur_entry, size, 172bf215546Sopenharmony_ci alignment, usage)) > 0) 173bf215546Sopenharmony_ci entry = cur_entry; 174bf215546Sopenharmony_ci else if (os_time_timeout(cur_entry->start, cur_entry->end, now)) 175bf215546Sopenharmony_ci destroy_buffer_locked(cur_entry); 176bf215546Sopenharmony_ci else 177bf215546Sopenharmony_ci /* This buffer (and all hereafter) are still hot in cache */ 178bf215546Sopenharmony_ci break; 179bf215546Sopenharmony_ci 180bf215546Sopenharmony_ci /* the buffer is busy (and probably all remaining ones too) */ 181bf215546Sopenharmony_ci if (ret == -1) 182bf215546Sopenharmony_ci break; 183bf215546Sopenharmony_ci 184bf215546Sopenharmony_ci cur = next; 185bf215546Sopenharmony_ci next = cur->next; 186bf215546Sopenharmony_ci } 187bf215546Sopenharmony_ci 188bf215546Sopenharmony_ci /* keep searching in the hot buffers */ 189bf215546Sopenharmony_ci if (!entry && ret != -1) { 190bf215546Sopenharmony_ci while (cur != cache) { 191bf215546Sopenharmony_ci cur_entry = list_entry(cur, struct pb_cache_entry, head); 192bf215546Sopenharmony_ci ret = pb_cache_is_buffer_compat(cur_entry, size, alignment, usage); 193bf215546Sopenharmony_ci 194bf215546Sopenharmony_ci if (ret > 0) { 195bf215546Sopenharmony_ci entry = cur_entry; 196bf215546Sopenharmony_ci break; 197bf215546Sopenharmony_ci } 198bf215546Sopenharmony_ci if (ret == -1) 199bf215546Sopenharmony_ci break; 200bf215546Sopenharmony_ci /* no need to check the timeout here */ 201bf215546Sopenharmony_ci cur = next; 202bf215546Sopenharmony_ci next = cur->next; 203bf215546Sopenharmony_ci } 204bf215546Sopenharmony_ci } 205bf215546Sopenharmony_ci 206bf215546Sopenharmony_ci /* found a compatible buffer, return it */ 207bf215546Sopenharmony_ci if (entry) { 208bf215546Sopenharmony_ci struct pb_buffer *buf = entry->buffer; 209bf215546Sopenharmony_ci 210bf215546Sopenharmony_ci mgr->cache_size -= buf->size; 211bf215546Sopenharmony_ci list_del(&entry->head); 212bf215546Sopenharmony_ci --mgr->num_buffers; 213bf215546Sopenharmony_ci simple_mtx_unlock(&mgr->mutex); 214bf215546Sopenharmony_ci /* Increase refcount */ 215bf215546Sopenharmony_ci pipe_reference_init(&buf->reference, 1); 216bf215546Sopenharmony_ci return buf; 217bf215546Sopenharmony_ci } 218bf215546Sopenharmony_ci 219bf215546Sopenharmony_ci simple_mtx_unlock(&mgr->mutex); 220bf215546Sopenharmony_ci return NULL; 221bf215546Sopenharmony_ci} 222bf215546Sopenharmony_ci 223bf215546Sopenharmony_ci/** 224bf215546Sopenharmony_ci * Empty the cache. Useful when there is not enough memory. 225bf215546Sopenharmony_ci */ 226bf215546Sopenharmony_civoid 227bf215546Sopenharmony_cipb_cache_release_all_buffers(struct pb_cache *mgr) 228bf215546Sopenharmony_ci{ 229bf215546Sopenharmony_ci struct list_head *curr, *next; 230bf215546Sopenharmony_ci struct pb_cache_entry *buf; 231bf215546Sopenharmony_ci unsigned i; 232bf215546Sopenharmony_ci 233bf215546Sopenharmony_ci simple_mtx_lock(&mgr->mutex); 234bf215546Sopenharmony_ci for (i = 0; i < mgr->num_heaps; i++) { 235bf215546Sopenharmony_ci struct list_head *cache = &mgr->buckets[i]; 236bf215546Sopenharmony_ci 237bf215546Sopenharmony_ci curr = cache->next; 238bf215546Sopenharmony_ci next = curr->next; 239bf215546Sopenharmony_ci while (curr != cache) { 240bf215546Sopenharmony_ci buf = list_entry(curr, struct pb_cache_entry, head); 241bf215546Sopenharmony_ci destroy_buffer_locked(buf); 242bf215546Sopenharmony_ci curr = next; 243bf215546Sopenharmony_ci next = curr->next; 244bf215546Sopenharmony_ci } 245bf215546Sopenharmony_ci } 246bf215546Sopenharmony_ci simple_mtx_unlock(&mgr->mutex); 247bf215546Sopenharmony_ci} 248bf215546Sopenharmony_ci 249bf215546Sopenharmony_civoid 250bf215546Sopenharmony_cipb_cache_init_entry(struct pb_cache *mgr, struct pb_cache_entry *entry, 251bf215546Sopenharmony_ci struct pb_buffer *buf, unsigned bucket_index) 252bf215546Sopenharmony_ci{ 253bf215546Sopenharmony_ci assert(bucket_index < mgr->num_heaps); 254bf215546Sopenharmony_ci 255bf215546Sopenharmony_ci memset(entry, 0, sizeof(*entry)); 256bf215546Sopenharmony_ci entry->buffer = buf; 257bf215546Sopenharmony_ci entry->mgr = mgr; 258bf215546Sopenharmony_ci entry->bucket_index = bucket_index; 259bf215546Sopenharmony_ci} 260bf215546Sopenharmony_ci 261bf215546Sopenharmony_ci/** 262bf215546Sopenharmony_ci * Initialize a caching buffer manager. 263bf215546Sopenharmony_ci * 264bf215546Sopenharmony_ci * @param mgr The cache buffer manager 265bf215546Sopenharmony_ci * @param num_heaps Number of separate caches/buckets indexed by bucket_index 266bf215546Sopenharmony_ci * for faster buffer matching (alternative to slower 267bf215546Sopenharmony_ci * "usage"-based matching). 268bf215546Sopenharmony_ci * @param usecs Unused buffers may be released from the cache after this 269bf215546Sopenharmony_ci * time 270bf215546Sopenharmony_ci * @param size_factor Declare buffers that are size_factor times bigger than 271bf215546Sopenharmony_ci * the requested size as cache hits. 272bf215546Sopenharmony_ci * @param bypass_usage Bitmask. If (requested usage & bypass_usage) != 0, 273bf215546Sopenharmony_ci * buffer allocation requests are rejected. 274bf215546Sopenharmony_ci * @param maximum_cache_size Maximum size of all unused buffers the cache can 275bf215546Sopenharmony_ci * hold. 276bf215546Sopenharmony_ci * @param destroy_buffer Function that destroys a buffer for good. 277bf215546Sopenharmony_ci * @param can_reclaim Whether a buffer can be reclaimed (e.g. is not busy) 278bf215546Sopenharmony_ci */ 279bf215546Sopenharmony_civoid 280bf215546Sopenharmony_cipb_cache_init(struct pb_cache *mgr, uint num_heaps, 281bf215546Sopenharmony_ci uint usecs, float size_factor, 282bf215546Sopenharmony_ci unsigned bypass_usage, uint64_t maximum_cache_size, 283bf215546Sopenharmony_ci void *winsys, 284bf215546Sopenharmony_ci void (*destroy_buffer)(void *winsys, struct pb_buffer *buf), 285bf215546Sopenharmony_ci bool (*can_reclaim)(void *winsys, struct pb_buffer *buf)) 286bf215546Sopenharmony_ci{ 287bf215546Sopenharmony_ci unsigned i; 288bf215546Sopenharmony_ci 289bf215546Sopenharmony_ci mgr->buckets = CALLOC(num_heaps, sizeof(struct list_head)); 290bf215546Sopenharmony_ci if (!mgr->buckets) 291bf215546Sopenharmony_ci return; 292bf215546Sopenharmony_ci 293bf215546Sopenharmony_ci for (i = 0; i < num_heaps; i++) 294bf215546Sopenharmony_ci list_inithead(&mgr->buckets[i]); 295bf215546Sopenharmony_ci 296bf215546Sopenharmony_ci (void) simple_mtx_init(&mgr->mutex, mtx_plain); 297bf215546Sopenharmony_ci mgr->winsys = winsys; 298bf215546Sopenharmony_ci mgr->cache_size = 0; 299bf215546Sopenharmony_ci mgr->max_cache_size = maximum_cache_size; 300bf215546Sopenharmony_ci mgr->num_heaps = num_heaps; 301bf215546Sopenharmony_ci mgr->usecs = usecs; 302bf215546Sopenharmony_ci mgr->num_buffers = 0; 303bf215546Sopenharmony_ci mgr->bypass_usage = bypass_usage; 304bf215546Sopenharmony_ci mgr->size_factor = size_factor; 305bf215546Sopenharmony_ci mgr->destroy_buffer = destroy_buffer; 306bf215546Sopenharmony_ci mgr->can_reclaim = can_reclaim; 307bf215546Sopenharmony_ci} 308bf215546Sopenharmony_ci 309bf215546Sopenharmony_ci/** 310bf215546Sopenharmony_ci * Deinitialize the manager completely. 311bf215546Sopenharmony_ci */ 312bf215546Sopenharmony_civoid 313bf215546Sopenharmony_cipb_cache_deinit(struct pb_cache *mgr) 314bf215546Sopenharmony_ci{ 315bf215546Sopenharmony_ci pb_cache_release_all_buffers(mgr); 316bf215546Sopenharmony_ci simple_mtx_destroy(&mgr->mutex); 317bf215546Sopenharmony_ci FREE(mgr->buckets); 318bf215546Sopenharmony_ci mgr->buckets = NULL; 319bf215546Sopenharmony_ci} 320