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