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