• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 Etnaviv Project
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 FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  *
23  * Authors:
24  *    Christian Gmeiner <christian.gmeiner@gmail.com>
25  */
26 
27 #include "etnaviv_priv.h"
28 #include "etnaviv_drmif.h"
29 
add_bucket(struct etna_bo_cache * cache,int size)30 static void add_bucket(struct etna_bo_cache *cache, int size)
31 {
32 	unsigned i = cache->num_buckets;
33 
34 	assert(i < ARRAY_SIZE(cache->cache_bucket));
35 
36 	list_inithead(&cache->cache_bucket[i].list);
37 	cache->cache_bucket[i].size = size;
38 	cache->num_buckets++;
39 }
40 
etna_bo_cache_init(struct etna_bo_cache * cache)41 void etna_bo_cache_init(struct etna_bo_cache *cache)
42 {
43 	unsigned long size, cache_max_size = 64 * 1024 * 1024;
44 
45 	/* OK, so power of two buckets was too wasteful of memory.
46 	 * Give 3 other sizes between each power of two, to hopefully
47 	 * cover things accurately enough.  (The alternative is
48 	 * probably to just go for exact matching of sizes, and assume
49 	 * that for things like composited window resize the tiled
50 	 * width/height alignment and rounding of sizes to pages will
51 	 * get us useful cache hit rates anyway)
52 	 */
53 	add_bucket(cache, 4096);
54 	add_bucket(cache, 4096 * 2);
55 	add_bucket(cache, 4096 * 3);
56 
57 	/* Initialize the linked lists for BO reuse cache. */
58 	for (size = 4 * 4096; size <= cache_max_size; size *= 2) {
59 		add_bucket(cache, size);
60 		add_bucket(cache, size + size * 1 / 4);
61 		add_bucket(cache, size + size * 2 / 4);
62 		add_bucket(cache, size + size * 3 / 4);
63 	}
64 }
65 
66 /* Frees older cached buffers.  Called under etna_drm_table_lock */
etna_bo_cache_cleanup(struct etna_bo_cache * cache,time_t time)67 void etna_bo_cache_cleanup(struct etna_bo_cache *cache, time_t time)
68 {
69 	unsigned i;
70 
71 	if (cache->time == time)
72 		return;
73 
74 	for (i = 0; i < cache->num_buckets; i++) {
75 		struct etna_bo_bucket *bucket = &cache->cache_bucket[i];
76 		struct etna_bo *bo;
77 
78 		while (!list_is_empty(&bucket->list)) {
79 			bo = list_entry(bucket->list.next, struct etna_bo, list);
80 
81 			/* keep things in cache for at least 1 second: */
82 			if (time && ((time - bo->free_time) <= 1))
83 				break;
84 
85 			VG_BO_OBTAIN(bo);
86 			list_del(&bo->list);
87 			etna_bo_free(bo);
88 		}
89 	}
90 
91 	cache->time = time;
92 }
93 
get_bucket(struct etna_bo_cache * cache,uint32_t size)94 static struct etna_bo_bucket *get_bucket(struct etna_bo_cache *cache, uint32_t size)
95 {
96 	unsigned i;
97 
98 	/* hmm, this is what intel does, but I suppose we could calculate our
99 	 * way to the correct bucket size rather than looping..
100 	 */
101 	for (i = 0; i < cache->num_buckets; i++) {
102 		struct etna_bo_bucket *bucket = &cache->cache_bucket[i];
103 		if (bucket->size >= size) {
104 			return bucket;
105 		}
106 	}
107 
108 	return NULL;
109 }
110 
find_in_bucket(struct etna_bo_bucket * bucket,uint32_t flags)111 static struct etna_bo *find_in_bucket(struct etna_bo_bucket *bucket, uint32_t flags)
112 {
113 	struct etna_bo *bo = NULL, *tmp;
114 
115 	simple_mtx_lock(&etna_device_lock);
116 
117 	if (list_is_empty(&bucket->list))
118 		goto out_unlock;
119 
120 	LIST_FOR_EACH_ENTRY_SAFE(bo, tmp, &bucket->list, list) {
121 		/* skip BOs with different flags */
122 		if (bo->flags != flags)
123 			continue;
124 
125 		/* check if the first BO with matching flags is idle */
126 		if (etna_bo_is_idle(bo)) {
127 			list_delinit(&bo->list);
128 			goto out_unlock;
129 		}
130 
131 		/* If the oldest BO is still busy, don't try younger ones */
132 		break;
133 	}
134 
135 	/* There was no matching buffer found */
136 	bo = NULL;
137 
138 out_unlock:
139 	simple_mtx_unlock(&etna_device_lock);
140 
141 	return bo;
142 }
143 
144 /* allocate a new (un-tiled) buffer object
145  *
146  * NOTE: size is potentially rounded up to bucket size
147  */
etna_bo_cache_alloc(struct etna_bo_cache * cache,uint32_t * size,uint32_t flags)148 struct etna_bo *etna_bo_cache_alloc(struct etna_bo_cache *cache, uint32_t *size,
149     uint32_t flags)
150 {
151 	struct etna_bo *bo;
152 	struct etna_bo_bucket *bucket;
153 
154 	*size = ALIGN(*size, 4096);
155 	bucket = get_bucket(cache, *size);
156 
157 	/* see if we can be green and recycle: */
158 	if (bucket) {
159 		*size = bucket->size;
160 		bo = find_in_bucket(bucket, flags);
161 		if (bo) {
162 			VG_BO_OBTAIN(bo);
163 			p_atomic_set(&bo->refcnt, 1);
164 			etna_device_ref(bo->dev);
165 			return bo;
166 		}
167 	}
168 
169 	return NULL;
170 }
171 
etna_bo_cache_free(struct etna_bo_cache * cache,struct etna_bo * bo)172 int etna_bo_cache_free(struct etna_bo_cache *cache, struct etna_bo *bo)
173 {
174 	struct etna_bo_bucket *bucket;
175 
176 	simple_mtx_assert_locked(&etna_device_lock);
177 
178 	bucket = get_bucket(cache, bo->size);
179 
180 	/* see if we can be green and recycle: */
181 	if (bucket) {
182 		struct timespec time;
183 
184 		clock_gettime(CLOCK_MONOTONIC, &time);
185 
186 		bo->free_time = time.tv_sec;
187 		VG_BO_RELEASE(bo);
188 		list_addtail(&bo->list, &bucket->list);
189 		etna_bo_cache_cleanup(cache, time.tv_sec);
190 
191 		/* bo's in the bucket cache don't have a ref and
192 		 * don't hold a ref to the dev:
193 		 */
194 		etna_device_del_locked(bo->dev);
195 
196 		return 0;
197 	}
198 
199 	return -1;
200 }
201