1d722e3fbSopenharmony_ci/* -*- mode: C; c-file-style: "k&r"; tab-width 4; indent-tabs-mode: t; -*- */ 2d722e3fbSopenharmony_ci 3d722e3fbSopenharmony_ci/* 4d722e3fbSopenharmony_ci * Copyright (C) 2016 Rob Clark <robclark@freedesktop.org> 5d722e3fbSopenharmony_ci * 6d722e3fbSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 7d722e3fbSopenharmony_ci * copy of this software and associated documentation files (the "Software"), 8d722e3fbSopenharmony_ci * to deal in the Software without restriction, including without limitation 9d722e3fbSopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10d722e3fbSopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 11d722e3fbSopenharmony_ci * Software is furnished to do so, subject to the following conditions: 12d722e3fbSopenharmony_ci * 13d722e3fbSopenharmony_ci * The above copyright notice and this permission notice (including the next 14d722e3fbSopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 15d722e3fbSopenharmony_ci * Software. 16d722e3fbSopenharmony_ci * 17d722e3fbSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18d722e3fbSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19d722e3fbSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20d722e3fbSopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21d722e3fbSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22d722e3fbSopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23d722e3fbSopenharmony_ci * SOFTWARE. 24d722e3fbSopenharmony_ci * 25d722e3fbSopenharmony_ci * Authors: 26d722e3fbSopenharmony_ci * Rob Clark <robclark@freedesktop.org> 27d722e3fbSopenharmony_ci */ 28d722e3fbSopenharmony_ci 29d722e3fbSopenharmony_ci#include "freedreno_drmif.h" 30d722e3fbSopenharmony_ci#include "freedreno_priv.h" 31d722e3fbSopenharmony_ci 32d722e3fbSopenharmony_cidrm_private void bo_del(struct fd_bo *bo); 33d722e3fbSopenharmony_cidrm_private extern pthread_mutex_t table_lock; 34d722e3fbSopenharmony_ci 35d722e3fbSopenharmony_cistatic void 36d722e3fbSopenharmony_ciadd_bucket(struct fd_bo_cache *cache, int size) 37d722e3fbSopenharmony_ci{ 38d722e3fbSopenharmony_ci unsigned int i = cache->num_buckets; 39d722e3fbSopenharmony_ci 40d722e3fbSopenharmony_ci assert(i < ARRAY_SIZE(cache->cache_bucket)); 41d722e3fbSopenharmony_ci 42d722e3fbSopenharmony_ci list_inithead(&cache->cache_bucket[i].list); 43d722e3fbSopenharmony_ci cache->cache_bucket[i].size = size; 44d722e3fbSopenharmony_ci cache->num_buckets++; 45d722e3fbSopenharmony_ci} 46d722e3fbSopenharmony_ci 47d722e3fbSopenharmony_ci/** 48d722e3fbSopenharmony_ci * @coarse: if true, only power-of-two bucket sizes, otherwise 49d722e3fbSopenharmony_ci * fill in for a bit smoother size curve.. 50d722e3fbSopenharmony_ci */ 51d722e3fbSopenharmony_cidrm_private void 52d722e3fbSopenharmony_cifd_bo_cache_init(struct fd_bo_cache *cache, int coarse) 53d722e3fbSopenharmony_ci{ 54d722e3fbSopenharmony_ci unsigned long size, cache_max_size = 64 * 1024 * 1024; 55d722e3fbSopenharmony_ci 56d722e3fbSopenharmony_ci /* OK, so power of two buckets was too wasteful of memory. 57d722e3fbSopenharmony_ci * Give 3 other sizes between each power of two, to hopefully 58d722e3fbSopenharmony_ci * cover things accurately enough. (The alternative is 59d722e3fbSopenharmony_ci * probably to just go for exact matching of sizes, and assume 60d722e3fbSopenharmony_ci * that for things like composited window resize the tiled 61d722e3fbSopenharmony_ci * width/height alignment and rounding of sizes to pages will 62d722e3fbSopenharmony_ci * get us useful cache hit rates anyway) 63d722e3fbSopenharmony_ci */ 64d722e3fbSopenharmony_ci add_bucket(cache, 4096); 65d722e3fbSopenharmony_ci add_bucket(cache, 4096 * 2); 66d722e3fbSopenharmony_ci if (!coarse) 67d722e3fbSopenharmony_ci add_bucket(cache, 4096 * 3); 68d722e3fbSopenharmony_ci 69d722e3fbSopenharmony_ci /* Initialize the linked lists for BO reuse cache. */ 70d722e3fbSopenharmony_ci for (size = 4 * 4096; size <= cache_max_size; size *= 2) { 71d722e3fbSopenharmony_ci add_bucket(cache, size); 72d722e3fbSopenharmony_ci if (!coarse) { 73d722e3fbSopenharmony_ci add_bucket(cache, size + size * 1 / 4); 74d722e3fbSopenharmony_ci add_bucket(cache, size + size * 2 / 4); 75d722e3fbSopenharmony_ci add_bucket(cache, size + size * 3 / 4); 76d722e3fbSopenharmony_ci } 77d722e3fbSopenharmony_ci } 78d722e3fbSopenharmony_ci} 79d722e3fbSopenharmony_ci 80d722e3fbSopenharmony_ci/* Frees older cached buffers. Called under table_lock */ 81d722e3fbSopenharmony_cidrm_private void 82d722e3fbSopenharmony_cifd_bo_cache_cleanup(struct fd_bo_cache *cache, time_t time) 83d722e3fbSopenharmony_ci{ 84d722e3fbSopenharmony_ci int i; 85d722e3fbSopenharmony_ci 86d722e3fbSopenharmony_ci if (cache->time == time) 87d722e3fbSopenharmony_ci return; 88d722e3fbSopenharmony_ci 89d722e3fbSopenharmony_ci for (i = 0; i < cache->num_buckets; i++) { 90d722e3fbSopenharmony_ci struct fd_bo_bucket *bucket = &cache->cache_bucket[i]; 91d722e3fbSopenharmony_ci struct fd_bo *bo; 92d722e3fbSopenharmony_ci 93d722e3fbSopenharmony_ci while (!LIST_IS_EMPTY(&bucket->list)) { 94d722e3fbSopenharmony_ci bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list); 95d722e3fbSopenharmony_ci 96d722e3fbSopenharmony_ci /* keep things in cache for at least 1 second: */ 97d722e3fbSopenharmony_ci if (time && ((time - bo->free_time) <= 1)) 98d722e3fbSopenharmony_ci break; 99d722e3fbSopenharmony_ci 100d722e3fbSopenharmony_ci VG_BO_OBTAIN(bo); 101d722e3fbSopenharmony_ci list_del(&bo->list); 102d722e3fbSopenharmony_ci bo_del(bo); 103d722e3fbSopenharmony_ci } 104d722e3fbSopenharmony_ci } 105d722e3fbSopenharmony_ci 106d722e3fbSopenharmony_ci cache->time = time; 107d722e3fbSopenharmony_ci} 108d722e3fbSopenharmony_ci 109d722e3fbSopenharmony_cistatic struct fd_bo_bucket * get_bucket(struct fd_bo_cache *cache, uint32_t size) 110d722e3fbSopenharmony_ci{ 111d722e3fbSopenharmony_ci int i; 112d722e3fbSopenharmony_ci 113d722e3fbSopenharmony_ci /* hmm, this is what intel does, but I suppose we could calculate our 114d722e3fbSopenharmony_ci * way to the correct bucket size rather than looping.. 115d722e3fbSopenharmony_ci */ 116d722e3fbSopenharmony_ci for (i = 0; i < cache->num_buckets; i++) { 117d722e3fbSopenharmony_ci struct fd_bo_bucket *bucket = &cache->cache_bucket[i]; 118d722e3fbSopenharmony_ci if (bucket->size >= size) { 119d722e3fbSopenharmony_ci return bucket; 120d722e3fbSopenharmony_ci } 121d722e3fbSopenharmony_ci } 122d722e3fbSopenharmony_ci 123d722e3fbSopenharmony_ci return NULL; 124d722e3fbSopenharmony_ci} 125d722e3fbSopenharmony_ci 126d722e3fbSopenharmony_cistatic int is_idle(struct fd_bo *bo) 127d722e3fbSopenharmony_ci{ 128d722e3fbSopenharmony_ci return fd_bo_cpu_prep(bo, NULL, 129d722e3fbSopenharmony_ci DRM_FREEDRENO_PREP_READ | 130d722e3fbSopenharmony_ci DRM_FREEDRENO_PREP_WRITE | 131d722e3fbSopenharmony_ci DRM_FREEDRENO_PREP_NOSYNC) == 0; 132d722e3fbSopenharmony_ci} 133d722e3fbSopenharmony_ci 134d722e3fbSopenharmony_cistatic struct fd_bo *find_in_bucket(struct fd_bo_bucket *bucket, uint32_t flags) 135d722e3fbSopenharmony_ci{ 136d722e3fbSopenharmony_ci struct fd_bo *bo = NULL; 137d722e3fbSopenharmony_ci 138d722e3fbSopenharmony_ci /* TODO .. if we had an ALLOC_FOR_RENDER flag like intel, we could 139d722e3fbSopenharmony_ci * skip the busy check.. if it is only going to be a render target 140d722e3fbSopenharmony_ci * then we probably don't need to stall.. 141d722e3fbSopenharmony_ci * 142d722e3fbSopenharmony_ci * NOTE that intel takes ALLOC_FOR_RENDER bo's from the list tail 143d722e3fbSopenharmony_ci * (MRU, since likely to be in GPU cache), rather than head (LRU).. 144d722e3fbSopenharmony_ci */ 145d722e3fbSopenharmony_ci pthread_mutex_lock(&table_lock); 146d722e3fbSopenharmony_ci if (!LIST_IS_EMPTY(&bucket->list)) { 147d722e3fbSopenharmony_ci bo = LIST_ENTRY(struct fd_bo, bucket->list.next, list); 148d722e3fbSopenharmony_ci /* TODO check for compatible flags? */ 149d722e3fbSopenharmony_ci if (is_idle(bo)) { 150d722e3fbSopenharmony_ci list_del(&bo->list); 151d722e3fbSopenharmony_ci } else { 152d722e3fbSopenharmony_ci bo = NULL; 153d722e3fbSopenharmony_ci } 154d722e3fbSopenharmony_ci } 155d722e3fbSopenharmony_ci pthread_mutex_unlock(&table_lock); 156d722e3fbSopenharmony_ci 157d722e3fbSopenharmony_ci return bo; 158d722e3fbSopenharmony_ci} 159d722e3fbSopenharmony_ci 160d722e3fbSopenharmony_ci/* NOTE: size is potentially rounded up to bucket size: */ 161d722e3fbSopenharmony_cidrm_private struct fd_bo * 162d722e3fbSopenharmony_cifd_bo_cache_alloc(struct fd_bo_cache *cache, uint32_t *size, uint32_t flags) 163d722e3fbSopenharmony_ci{ 164d722e3fbSopenharmony_ci struct fd_bo *bo = NULL; 165d722e3fbSopenharmony_ci struct fd_bo_bucket *bucket; 166d722e3fbSopenharmony_ci 167d722e3fbSopenharmony_ci *size = ALIGN(*size, 4096); 168d722e3fbSopenharmony_ci bucket = get_bucket(cache, *size); 169d722e3fbSopenharmony_ci 170d722e3fbSopenharmony_ci /* see if we can be green and recycle: */ 171d722e3fbSopenharmony_ciretry: 172d722e3fbSopenharmony_ci if (bucket) { 173d722e3fbSopenharmony_ci *size = bucket->size; 174d722e3fbSopenharmony_ci bo = find_in_bucket(bucket, flags); 175d722e3fbSopenharmony_ci if (bo) { 176d722e3fbSopenharmony_ci VG_BO_OBTAIN(bo); 177d722e3fbSopenharmony_ci if (bo->funcs->madvise(bo, TRUE) <= 0) { 178d722e3fbSopenharmony_ci /* we've lost the backing pages, delete and try again: */ 179d722e3fbSopenharmony_ci pthread_mutex_lock(&table_lock); 180d722e3fbSopenharmony_ci bo_del(bo); 181d722e3fbSopenharmony_ci pthread_mutex_unlock(&table_lock); 182d722e3fbSopenharmony_ci goto retry; 183d722e3fbSopenharmony_ci } 184d722e3fbSopenharmony_ci atomic_set(&bo->refcnt, 1); 185d722e3fbSopenharmony_ci fd_device_ref(bo->dev); 186d722e3fbSopenharmony_ci return bo; 187d722e3fbSopenharmony_ci } 188d722e3fbSopenharmony_ci } 189d722e3fbSopenharmony_ci 190d722e3fbSopenharmony_ci return NULL; 191d722e3fbSopenharmony_ci} 192d722e3fbSopenharmony_ci 193d722e3fbSopenharmony_cidrm_private int 194d722e3fbSopenharmony_cifd_bo_cache_free(struct fd_bo_cache *cache, struct fd_bo *bo) 195d722e3fbSopenharmony_ci{ 196d722e3fbSopenharmony_ci struct fd_bo_bucket *bucket = get_bucket(cache, bo->size); 197d722e3fbSopenharmony_ci 198d722e3fbSopenharmony_ci /* see if we can be green and recycle: */ 199d722e3fbSopenharmony_ci if (bucket) { 200d722e3fbSopenharmony_ci struct timespec time; 201d722e3fbSopenharmony_ci 202d722e3fbSopenharmony_ci bo->funcs->madvise(bo, FALSE); 203d722e3fbSopenharmony_ci 204d722e3fbSopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &time); 205d722e3fbSopenharmony_ci 206d722e3fbSopenharmony_ci bo->free_time = time.tv_sec; 207d722e3fbSopenharmony_ci VG_BO_RELEASE(bo); 208d722e3fbSopenharmony_ci list_addtail(&bo->list, &bucket->list); 209d722e3fbSopenharmony_ci fd_bo_cache_cleanup(cache, time.tv_sec); 210d722e3fbSopenharmony_ci 211d722e3fbSopenharmony_ci /* bo's in the bucket cache don't have a ref and 212d722e3fbSopenharmony_ci * don't hold a ref to the dev: 213d722e3fbSopenharmony_ci */ 214d722e3fbSopenharmony_ci fd_device_del_locked(bo->dev); 215d722e3fbSopenharmony_ci 216d722e3fbSopenharmony_ci return 0; 217d722e3fbSopenharmony_ci } 218d722e3fbSopenharmony_ci 219d722e3fbSopenharmony_ci return -1; 220d722e3fbSopenharmony_ci} 221