1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright © 2019 Raspberry Pi Ltd
3bf215546Sopenharmony_ci *
4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci *
11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next
12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13bf215546Sopenharmony_ci * Software.
14bf215546Sopenharmony_ci *
15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21bf215546Sopenharmony_ci * IN THE SOFTWARE.
22bf215546Sopenharmony_ci */
23bf215546Sopenharmony_ci
24bf215546Sopenharmony_ci#include "v3dv_private.h"
25bf215546Sopenharmony_ci
26bf215546Sopenharmony_ci#include <errno.h>
27bf215546Sopenharmony_ci#include <sys/mman.h>
28bf215546Sopenharmony_ci
29bf215546Sopenharmony_ci#include "drm-uapi/v3d_drm.h"
30bf215546Sopenharmony_ci#include "util/u_memory.h"
31bf215546Sopenharmony_ci
32bf215546Sopenharmony_ci/* Default max size of the bo cache, in MB.
33bf215546Sopenharmony_ci *
34bf215546Sopenharmony_ci * FIXME: we got this value when testing some apps using the rpi4 with 4GB,
35bf215546Sopenharmony_ci * but it should depend on the total amount of RAM. But for that we would need
36bf215546Sopenharmony_ci * to test on real hw with different amount of RAM. Using this value for now.
37bf215546Sopenharmony_ci */
38bf215546Sopenharmony_ci#define DEFAULT_MAX_BO_CACHE_SIZE 512
39bf215546Sopenharmony_ci
40bf215546Sopenharmony_ci/* Discarded to use a V3D_DEBUG for this, as it would mean adding a run-time
41bf215546Sopenharmony_ci * check for most of the calls
42bf215546Sopenharmony_ci */
43bf215546Sopenharmony_cistatic const bool dump_stats = false;
44bf215546Sopenharmony_ci
45bf215546Sopenharmony_cistatic void
46bf215546Sopenharmony_cibo_dump_stats(struct v3dv_device *device)
47bf215546Sopenharmony_ci{
48bf215546Sopenharmony_ci   struct v3dv_bo_cache *cache = &device->bo_cache;
49bf215546Sopenharmony_ci
50bf215546Sopenharmony_ci   fprintf(stderr, "  BOs allocated:   %d\n", device->bo_count);
51bf215546Sopenharmony_ci   fprintf(stderr, "  BOs size:        %dkb\n", device->bo_size / 1024);
52bf215546Sopenharmony_ci   fprintf(stderr, "  BOs cached:      %d\n", cache->cache_count);
53bf215546Sopenharmony_ci   fprintf(stderr, "  BOs cached size: %dkb\n", cache->cache_size / 1024);
54bf215546Sopenharmony_ci
55bf215546Sopenharmony_ci   if (!list_is_empty(&cache->time_list)) {
56bf215546Sopenharmony_ci      struct v3dv_bo *first = list_first_entry(&cache->time_list,
57bf215546Sopenharmony_ci                                              struct v3dv_bo,
58bf215546Sopenharmony_ci                                              time_list);
59bf215546Sopenharmony_ci      struct v3dv_bo *last = list_last_entry(&cache->time_list,
60bf215546Sopenharmony_ci                                            struct v3dv_bo,
61bf215546Sopenharmony_ci                                            time_list);
62bf215546Sopenharmony_ci
63bf215546Sopenharmony_ci      fprintf(stderr, "  oldest cache time: %ld\n",
64bf215546Sopenharmony_ci              (long)first->free_time);
65bf215546Sopenharmony_ci      fprintf(stderr, "  newest cache time: %ld\n",
66bf215546Sopenharmony_ci              (long)last->free_time);
67bf215546Sopenharmony_ci
68bf215546Sopenharmony_ci      struct timespec time;
69bf215546Sopenharmony_ci      clock_gettime(CLOCK_MONOTONIC, &time);
70bf215546Sopenharmony_ci      fprintf(stderr, "  now:               %lld\n",
71bf215546Sopenharmony_ci              (long long)time.tv_sec);
72bf215546Sopenharmony_ci   }
73bf215546Sopenharmony_ci
74bf215546Sopenharmony_ci   if (cache->size_list_size) {
75bf215546Sopenharmony_ci      uint32_t empty_size_list = 0;
76bf215546Sopenharmony_ci      for (uint32_t i = 0; i < cache->size_list_size; i++) {
77bf215546Sopenharmony_ci         if (list_is_empty(&cache->size_list[i]))
78bf215546Sopenharmony_ci            empty_size_list++;
79bf215546Sopenharmony_ci      }
80bf215546Sopenharmony_ci      fprintf(stderr, "  Empty size_list lists: %d\n", empty_size_list);
81bf215546Sopenharmony_ci   }
82bf215546Sopenharmony_ci}
83bf215546Sopenharmony_ci
84bf215546Sopenharmony_cistatic void
85bf215546Sopenharmony_cibo_remove_from_cache(struct v3dv_bo_cache *cache, struct v3dv_bo *bo)
86bf215546Sopenharmony_ci{
87bf215546Sopenharmony_ci   list_del(&bo->time_list);
88bf215546Sopenharmony_ci   list_del(&bo->size_list);
89bf215546Sopenharmony_ci
90bf215546Sopenharmony_ci   cache->cache_count--;
91bf215546Sopenharmony_ci   cache->cache_size -= bo->size;
92bf215546Sopenharmony_ci}
93bf215546Sopenharmony_ci
94bf215546Sopenharmony_cistatic struct v3dv_bo *
95bf215546Sopenharmony_cibo_from_cache(struct v3dv_device *device, uint32_t size, const char *name)
96bf215546Sopenharmony_ci{
97bf215546Sopenharmony_ci   struct v3dv_bo_cache *cache = &device->bo_cache;
98bf215546Sopenharmony_ci   uint32_t page_index = size / 4096 - 1;
99bf215546Sopenharmony_ci
100bf215546Sopenharmony_ci   if (cache->size_list_size <= page_index)
101bf215546Sopenharmony_ci      return NULL;
102bf215546Sopenharmony_ci
103bf215546Sopenharmony_ci   struct v3dv_bo *bo = NULL;
104bf215546Sopenharmony_ci
105bf215546Sopenharmony_ci   mtx_lock(&cache->lock);
106bf215546Sopenharmony_ci   if (!list_is_empty(&cache->size_list[page_index])) {
107bf215546Sopenharmony_ci      bo = list_first_entry(&cache->size_list[page_index],
108bf215546Sopenharmony_ci                            struct v3dv_bo, size_list);
109bf215546Sopenharmony_ci
110bf215546Sopenharmony_ci      /* Check that the BO has gone idle.  If not, then we want to
111bf215546Sopenharmony_ci       * allocate something new instead, since we assume that the
112bf215546Sopenharmony_ci       * user will proceed to CPU map it and fill it with stuff.
113bf215546Sopenharmony_ci       */
114bf215546Sopenharmony_ci      if (!v3dv_bo_wait(device, bo, 0)) {
115bf215546Sopenharmony_ci         mtx_unlock(&cache->lock);
116bf215546Sopenharmony_ci         return NULL;
117bf215546Sopenharmony_ci      }
118bf215546Sopenharmony_ci
119bf215546Sopenharmony_ci      bo_remove_from_cache(cache, bo);
120bf215546Sopenharmony_ci      bo->name = name;
121bf215546Sopenharmony_ci      p_atomic_set(&bo->refcnt, 1);
122bf215546Sopenharmony_ci   }
123bf215546Sopenharmony_ci   mtx_unlock(&cache->lock);
124bf215546Sopenharmony_ci   return bo;
125bf215546Sopenharmony_ci}
126bf215546Sopenharmony_ci
127bf215546Sopenharmony_cistatic bool
128bf215546Sopenharmony_cibo_free(struct v3dv_device *device,
129bf215546Sopenharmony_ci        struct v3dv_bo *bo)
130bf215546Sopenharmony_ci{
131bf215546Sopenharmony_ci   if (!bo)
132bf215546Sopenharmony_ci      return true;
133bf215546Sopenharmony_ci
134bf215546Sopenharmony_ci   assert(p_atomic_read(&bo->refcnt) == 0);
135bf215546Sopenharmony_ci   assert(bo->map == NULL);
136bf215546Sopenharmony_ci
137bf215546Sopenharmony_ci   struct drm_gem_close c;
138bf215546Sopenharmony_ci   memset(&c, 0, sizeof(c));
139bf215546Sopenharmony_ci   c.handle = bo->handle;
140bf215546Sopenharmony_ci   int ret = v3dv_ioctl(device->pdevice->render_fd, DRM_IOCTL_GEM_CLOSE, &c);
141bf215546Sopenharmony_ci   if (ret != 0)
142bf215546Sopenharmony_ci      fprintf(stderr, "close object %d: %s\n", bo->handle, strerror(errno));
143bf215546Sopenharmony_ci
144bf215546Sopenharmony_ci   device->bo_count--;
145bf215546Sopenharmony_ci   device->bo_size -= bo->size;
146bf215546Sopenharmony_ci
147bf215546Sopenharmony_ci   if (dump_stats) {
148bf215546Sopenharmony_ci      fprintf(stderr, "Freed %s%s%dkb:\n",
149bf215546Sopenharmony_ci              bo->name ? bo->name : "",
150bf215546Sopenharmony_ci              bo->name ? " " : "",
151bf215546Sopenharmony_ci              bo->size / 1024);
152bf215546Sopenharmony_ci      bo_dump_stats(device);
153bf215546Sopenharmony_ci   }
154bf215546Sopenharmony_ci
155bf215546Sopenharmony_ci   /* Our BO structs are stored in a sparse array in the physical device,
156bf215546Sopenharmony_ci    * so we don't want to free the BO pointer, instead we want to reset it
157bf215546Sopenharmony_ci    * to 0, to signal that array entry as being free.
158bf215546Sopenharmony_ci    */
159bf215546Sopenharmony_ci   memset(bo, 0, sizeof(*bo));
160bf215546Sopenharmony_ci
161bf215546Sopenharmony_ci   return ret == 0;
162bf215546Sopenharmony_ci}
163bf215546Sopenharmony_ci
164bf215546Sopenharmony_cistatic void
165bf215546Sopenharmony_cibo_cache_free_all(struct v3dv_device *device,
166bf215546Sopenharmony_ci                       bool with_lock)
167bf215546Sopenharmony_ci{
168bf215546Sopenharmony_ci   struct v3dv_bo_cache *cache = &device->bo_cache;
169bf215546Sopenharmony_ci
170bf215546Sopenharmony_ci   if (with_lock)
171bf215546Sopenharmony_ci      mtx_lock(&cache->lock);
172bf215546Sopenharmony_ci   list_for_each_entry_safe(struct v3dv_bo, bo, &cache->time_list,
173bf215546Sopenharmony_ci                            time_list) {
174bf215546Sopenharmony_ci      bo_remove_from_cache(cache, bo);
175bf215546Sopenharmony_ci      bo_free(device, bo);
176bf215546Sopenharmony_ci   }
177bf215546Sopenharmony_ci   if (with_lock)
178bf215546Sopenharmony_ci      mtx_unlock(&cache->lock);
179bf215546Sopenharmony_ci
180bf215546Sopenharmony_ci}
181bf215546Sopenharmony_ci
182bf215546Sopenharmony_civoid
183bf215546Sopenharmony_civ3dv_bo_init(struct v3dv_bo *bo,
184bf215546Sopenharmony_ci             uint32_t handle,
185bf215546Sopenharmony_ci             uint32_t size,
186bf215546Sopenharmony_ci             uint32_t offset,
187bf215546Sopenharmony_ci             const char *name,
188bf215546Sopenharmony_ci             bool private)
189bf215546Sopenharmony_ci{
190bf215546Sopenharmony_ci   p_atomic_set(&bo->refcnt, 1);
191bf215546Sopenharmony_ci   bo->handle = handle;
192bf215546Sopenharmony_ci   bo->handle_bit = 1ull << (handle % 64);
193bf215546Sopenharmony_ci   bo->size = size;
194bf215546Sopenharmony_ci   bo->offset = offset;
195bf215546Sopenharmony_ci   bo->map = NULL;
196bf215546Sopenharmony_ci   bo->map_size = 0;
197bf215546Sopenharmony_ci   bo->name = name;
198bf215546Sopenharmony_ci   bo->private = private;
199bf215546Sopenharmony_ci   bo->dumb_handle = -1;
200bf215546Sopenharmony_ci   list_inithead(&bo->list_link);
201bf215546Sopenharmony_ci}
202bf215546Sopenharmony_ci
203bf215546Sopenharmony_cistruct v3dv_bo *
204bf215546Sopenharmony_civ3dv_bo_alloc(struct v3dv_device *device,
205bf215546Sopenharmony_ci              uint32_t size,
206bf215546Sopenharmony_ci              const char *name,
207bf215546Sopenharmony_ci              bool private)
208bf215546Sopenharmony_ci{
209bf215546Sopenharmony_ci   struct v3dv_bo *bo;
210bf215546Sopenharmony_ci
211bf215546Sopenharmony_ci   const uint32_t page_align = 4096; /* Always allocate full pages */
212bf215546Sopenharmony_ci   size = align(size, page_align);
213bf215546Sopenharmony_ci
214bf215546Sopenharmony_ci   if (private) {
215bf215546Sopenharmony_ci      bo = bo_from_cache(device, size, name);
216bf215546Sopenharmony_ci      if (bo) {
217bf215546Sopenharmony_ci         if (dump_stats) {
218bf215546Sopenharmony_ci            fprintf(stderr, "Allocated %s %dkb from cache:\n",
219bf215546Sopenharmony_ci                    name, size / 1024);
220bf215546Sopenharmony_ci            bo_dump_stats(device);
221bf215546Sopenharmony_ci         }
222bf215546Sopenharmony_ci         return bo;
223bf215546Sopenharmony_ci      }
224bf215546Sopenharmony_ci   }
225bf215546Sopenharmony_ci
226bf215546Sopenharmony_ci retry:
227bf215546Sopenharmony_ci   ;
228bf215546Sopenharmony_ci
229bf215546Sopenharmony_ci   bool cleared_and_retried = false;
230bf215546Sopenharmony_ci   struct drm_v3d_create_bo create = {
231bf215546Sopenharmony_ci      .size = size
232bf215546Sopenharmony_ci   };
233bf215546Sopenharmony_ci
234bf215546Sopenharmony_ci   int ret = v3dv_ioctl(device->pdevice->render_fd,
235bf215546Sopenharmony_ci                        DRM_IOCTL_V3D_CREATE_BO, &create);
236bf215546Sopenharmony_ci   if (ret != 0) {
237bf215546Sopenharmony_ci      if (!list_is_empty(&device->bo_cache.time_list) &&
238bf215546Sopenharmony_ci          !cleared_and_retried) {
239bf215546Sopenharmony_ci         cleared_and_retried = true;
240bf215546Sopenharmony_ci         bo_cache_free_all(device, true);
241bf215546Sopenharmony_ci         goto retry;
242bf215546Sopenharmony_ci      }
243bf215546Sopenharmony_ci
244bf215546Sopenharmony_ci      fprintf(stderr, "Failed to allocate device memory for BO\n");
245bf215546Sopenharmony_ci      return NULL;
246bf215546Sopenharmony_ci   }
247bf215546Sopenharmony_ci
248bf215546Sopenharmony_ci   assert(create.offset % page_align == 0);
249bf215546Sopenharmony_ci   assert((create.offset & 0xffffffff) == create.offset);
250bf215546Sopenharmony_ci
251bf215546Sopenharmony_ci   bo = v3dv_device_lookup_bo(device->pdevice, create.handle);
252bf215546Sopenharmony_ci   assert(bo && bo->handle == 0);
253bf215546Sopenharmony_ci
254bf215546Sopenharmony_ci   v3dv_bo_init(bo, create.handle, size, create.offset, name, private);
255bf215546Sopenharmony_ci
256bf215546Sopenharmony_ci   device->bo_count++;
257bf215546Sopenharmony_ci   device->bo_size += bo->size;
258bf215546Sopenharmony_ci   if (dump_stats) {
259bf215546Sopenharmony_ci      fprintf(stderr, "Allocated %s %dkb:\n", name, size / 1024);
260bf215546Sopenharmony_ci      bo_dump_stats(device);
261bf215546Sopenharmony_ci   }
262bf215546Sopenharmony_ci
263bf215546Sopenharmony_ci   return bo;
264bf215546Sopenharmony_ci}
265bf215546Sopenharmony_ci
266bf215546Sopenharmony_cibool
267bf215546Sopenharmony_civ3dv_bo_map_unsynchronized(struct v3dv_device *device,
268bf215546Sopenharmony_ci                           struct v3dv_bo *bo,
269bf215546Sopenharmony_ci                           uint32_t size)
270bf215546Sopenharmony_ci{
271bf215546Sopenharmony_ci   assert(bo != NULL && size <= bo->size);
272bf215546Sopenharmony_ci
273bf215546Sopenharmony_ci   if (bo->map)
274bf215546Sopenharmony_ci      return bo->map;
275bf215546Sopenharmony_ci
276bf215546Sopenharmony_ci   struct drm_v3d_mmap_bo map;
277bf215546Sopenharmony_ci   memset(&map, 0, sizeof(map));
278bf215546Sopenharmony_ci   map.handle = bo->handle;
279bf215546Sopenharmony_ci   int ret = v3dv_ioctl(device->pdevice->render_fd,
280bf215546Sopenharmony_ci                        DRM_IOCTL_V3D_MMAP_BO, &map);
281bf215546Sopenharmony_ci   if (ret != 0) {
282bf215546Sopenharmony_ci      fprintf(stderr, "map ioctl failure\n");
283bf215546Sopenharmony_ci      return false;
284bf215546Sopenharmony_ci   }
285bf215546Sopenharmony_ci
286bf215546Sopenharmony_ci   bo->map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED,
287bf215546Sopenharmony_ci                  device->pdevice->render_fd, map.offset);
288bf215546Sopenharmony_ci   if (bo->map == MAP_FAILED) {
289bf215546Sopenharmony_ci      fprintf(stderr, "mmap of bo %d (offset 0x%016llx, size %d) failed\n",
290bf215546Sopenharmony_ci              bo->handle, (long long)map.offset, (uint32_t)bo->size);
291bf215546Sopenharmony_ci      return false;
292bf215546Sopenharmony_ci   }
293bf215546Sopenharmony_ci   VG(VALGRIND_MALLOCLIKE_BLOCK(bo->map, bo->size, 0, false));
294bf215546Sopenharmony_ci
295bf215546Sopenharmony_ci   bo->map_size = size;
296bf215546Sopenharmony_ci
297bf215546Sopenharmony_ci   return true;
298bf215546Sopenharmony_ci}
299bf215546Sopenharmony_ci
300bf215546Sopenharmony_cibool
301bf215546Sopenharmony_civ3dv_bo_wait(struct v3dv_device *device,
302bf215546Sopenharmony_ci             struct v3dv_bo *bo,
303bf215546Sopenharmony_ci             uint64_t timeout_ns)
304bf215546Sopenharmony_ci{
305bf215546Sopenharmony_ci   struct drm_v3d_wait_bo wait = {
306bf215546Sopenharmony_ci      .handle = bo->handle,
307bf215546Sopenharmony_ci      .timeout_ns = timeout_ns,
308bf215546Sopenharmony_ci   };
309bf215546Sopenharmony_ci   return v3dv_ioctl(device->pdevice->render_fd,
310bf215546Sopenharmony_ci                     DRM_IOCTL_V3D_WAIT_BO, &wait) == 0;
311bf215546Sopenharmony_ci}
312bf215546Sopenharmony_ci
313bf215546Sopenharmony_cibool
314bf215546Sopenharmony_civ3dv_bo_map(struct v3dv_device *device, struct v3dv_bo *bo, uint32_t size)
315bf215546Sopenharmony_ci{
316bf215546Sopenharmony_ci   assert(bo && size <= bo->size);
317bf215546Sopenharmony_ci
318bf215546Sopenharmony_ci   bool ok = v3dv_bo_map_unsynchronized(device, bo, size);
319bf215546Sopenharmony_ci   if (!ok)
320bf215546Sopenharmony_ci      return false;
321bf215546Sopenharmony_ci
322bf215546Sopenharmony_ci   ok = v3dv_bo_wait(device, bo, PIPE_TIMEOUT_INFINITE);
323bf215546Sopenharmony_ci   if (!ok) {
324bf215546Sopenharmony_ci      fprintf(stderr, "memory wait for map failed\n");
325bf215546Sopenharmony_ci      return false;
326bf215546Sopenharmony_ci   }
327bf215546Sopenharmony_ci
328bf215546Sopenharmony_ci   return true;
329bf215546Sopenharmony_ci}
330bf215546Sopenharmony_ci
331bf215546Sopenharmony_civoid
332bf215546Sopenharmony_civ3dv_bo_unmap(struct v3dv_device *device, struct v3dv_bo *bo)
333bf215546Sopenharmony_ci{
334bf215546Sopenharmony_ci   assert(bo && bo->map && bo->map_size > 0);
335bf215546Sopenharmony_ci
336bf215546Sopenharmony_ci   munmap(bo->map, bo->map_size);
337bf215546Sopenharmony_ci   VG(VALGRIND_FREELIKE_BLOCK(bo->map, 0));
338bf215546Sopenharmony_ci   bo->map = NULL;
339bf215546Sopenharmony_ci   bo->map_size = 0;
340bf215546Sopenharmony_ci}
341bf215546Sopenharmony_ci
342bf215546Sopenharmony_cistatic boolean
343bf215546Sopenharmony_cireallocate_size_list(struct v3dv_bo_cache *cache,
344bf215546Sopenharmony_ci                     struct v3dv_device *device,
345bf215546Sopenharmony_ci                     uint32_t size)
346bf215546Sopenharmony_ci{
347bf215546Sopenharmony_ci   struct list_head *new_list =
348bf215546Sopenharmony_ci      vk_alloc(&device->vk.alloc, sizeof(struct list_head) * size, 8,
349bf215546Sopenharmony_ci               VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
350bf215546Sopenharmony_ci
351bf215546Sopenharmony_ci   if (!new_list) {
352bf215546Sopenharmony_ci      fprintf(stderr, "Failed to allocate host memory for cache bo list\n");
353bf215546Sopenharmony_ci      return false;
354bf215546Sopenharmony_ci   }
355bf215546Sopenharmony_ci   struct list_head *old_list = cache->size_list;
356bf215546Sopenharmony_ci
357bf215546Sopenharmony_ci   /* Move old list contents over (since the array has moved, and
358bf215546Sopenharmony_ci    * therefore the pointers to the list heads have to change).
359bf215546Sopenharmony_ci    */
360bf215546Sopenharmony_ci   for (int i = 0; i < cache->size_list_size; i++) {
361bf215546Sopenharmony_ci      struct list_head *old_head = &cache->size_list[i];
362bf215546Sopenharmony_ci      if (list_is_empty(old_head)) {
363bf215546Sopenharmony_ci         list_inithead(&new_list[i]);
364bf215546Sopenharmony_ci      } else {
365bf215546Sopenharmony_ci         new_list[i].next = old_head->next;
366bf215546Sopenharmony_ci         new_list[i].prev = old_head->prev;
367bf215546Sopenharmony_ci         new_list[i].next->prev = &new_list[i];
368bf215546Sopenharmony_ci         new_list[i].prev->next = &new_list[i];
369bf215546Sopenharmony_ci      }
370bf215546Sopenharmony_ci   }
371bf215546Sopenharmony_ci   for (int i = cache->size_list_size; i < size; i++)
372bf215546Sopenharmony_ci      list_inithead(&new_list[i]);
373bf215546Sopenharmony_ci
374bf215546Sopenharmony_ci   cache->size_list = new_list;
375bf215546Sopenharmony_ci   cache->size_list_size = size;
376bf215546Sopenharmony_ci   vk_free(&device->vk.alloc, old_list);
377bf215546Sopenharmony_ci
378bf215546Sopenharmony_ci   return true;
379bf215546Sopenharmony_ci}
380bf215546Sopenharmony_ci
381bf215546Sopenharmony_civoid
382bf215546Sopenharmony_civ3dv_bo_cache_init(struct v3dv_device *device)
383bf215546Sopenharmony_ci{
384bf215546Sopenharmony_ci   device->bo_size = 0;
385bf215546Sopenharmony_ci   device->bo_count = 0;
386bf215546Sopenharmony_ci   list_inithead(&device->bo_cache.time_list);
387bf215546Sopenharmony_ci   /* FIXME: perhaps set a initial size for the size-list, to avoid run-time
388bf215546Sopenharmony_ci    * reallocations
389bf215546Sopenharmony_ci    */
390bf215546Sopenharmony_ci   device->bo_cache.size_list_size = 0;
391bf215546Sopenharmony_ci
392bf215546Sopenharmony_ci   const char *max_cache_size_str = getenv("V3DV_MAX_BO_CACHE_SIZE");
393bf215546Sopenharmony_ci   if (max_cache_size_str == NULL)
394bf215546Sopenharmony_ci      device->bo_cache.max_cache_size = DEFAULT_MAX_BO_CACHE_SIZE;
395bf215546Sopenharmony_ci   else
396bf215546Sopenharmony_ci      device->bo_cache.max_cache_size = atoll(max_cache_size_str);
397bf215546Sopenharmony_ci
398bf215546Sopenharmony_ci   if (dump_stats) {
399bf215546Sopenharmony_ci      fprintf(stderr, "MAX BO CACHE SIZE: %iMB\n", device->bo_cache.max_cache_size);
400bf215546Sopenharmony_ci   }
401bf215546Sopenharmony_ci
402bf215546Sopenharmony_ci   device->bo_cache.max_cache_size *= 1024 * 1024;
403bf215546Sopenharmony_ci   device->bo_cache.cache_count = 0;
404bf215546Sopenharmony_ci   device->bo_cache.cache_size = 0;
405bf215546Sopenharmony_ci}
406bf215546Sopenharmony_ci
407bf215546Sopenharmony_civoid
408bf215546Sopenharmony_civ3dv_bo_cache_destroy(struct v3dv_device *device)
409bf215546Sopenharmony_ci{
410bf215546Sopenharmony_ci   bo_cache_free_all(device, true);
411bf215546Sopenharmony_ci   vk_free(&device->vk.alloc, device->bo_cache.size_list);
412bf215546Sopenharmony_ci
413bf215546Sopenharmony_ci   if (dump_stats) {
414bf215546Sopenharmony_ci      fprintf(stderr, "BO stats after screen destroy:\n");
415bf215546Sopenharmony_ci      bo_dump_stats(device);
416bf215546Sopenharmony_ci   }
417bf215546Sopenharmony_ci}
418bf215546Sopenharmony_ci
419bf215546Sopenharmony_ci
420bf215546Sopenharmony_cistatic void
421bf215546Sopenharmony_cifree_stale_bos(struct v3dv_device *device,
422bf215546Sopenharmony_ci               time_t time)
423bf215546Sopenharmony_ci{
424bf215546Sopenharmony_ci   struct v3dv_bo_cache *cache = &device->bo_cache;
425bf215546Sopenharmony_ci   bool freed_any = false;
426bf215546Sopenharmony_ci
427bf215546Sopenharmony_ci   list_for_each_entry_safe(struct v3dv_bo, bo, &cache->time_list,
428bf215546Sopenharmony_ci                            time_list) {
429bf215546Sopenharmony_ci      /* If it's more than a second old, free it. */
430bf215546Sopenharmony_ci      if (time - bo->free_time > 2) {
431bf215546Sopenharmony_ci         if (dump_stats && !freed_any) {
432bf215546Sopenharmony_ci            fprintf(stderr, "Freeing stale BOs:\n");
433bf215546Sopenharmony_ci            bo_dump_stats(device);
434bf215546Sopenharmony_ci            freed_any = true;
435bf215546Sopenharmony_ci         }
436bf215546Sopenharmony_ci
437bf215546Sopenharmony_ci         bo_remove_from_cache(cache, bo);
438bf215546Sopenharmony_ci         bo_free(device, bo);
439bf215546Sopenharmony_ci      } else {
440bf215546Sopenharmony_ci         break;
441bf215546Sopenharmony_ci      }
442bf215546Sopenharmony_ci   }
443bf215546Sopenharmony_ci
444bf215546Sopenharmony_ci   if (dump_stats && freed_any) {
445bf215546Sopenharmony_ci      fprintf(stderr, "Freed stale BOs:\n");
446bf215546Sopenharmony_ci      bo_dump_stats(device);
447bf215546Sopenharmony_ci   }
448bf215546Sopenharmony_ci}
449bf215546Sopenharmony_ci
450bf215546Sopenharmony_cibool
451bf215546Sopenharmony_civ3dv_bo_free(struct v3dv_device *device,
452bf215546Sopenharmony_ci             struct v3dv_bo *bo)
453bf215546Sopenharmony_ci{
454bf215546Sopenharmony_ci   if (!bo)
455bf215546Sopenharmony_ci      return true;
456bf215546Sopenharmony_ci
457bf215546Sopenharmony_ci   if (!p_atomic_dec_zero(&bo->refcnt))
458bf215546Sopenharmony_ci      return true;
459bf215546Sopenharmony_ci
460bf215546Sopenharmony_ci   if (bo->map)
461bf215546Sopenharmony_ci      v3dv_bo_unmap(device, bo);
462bf215546Sopenharmony_ci
463bf215546Sopenharmony_ci   struct timespec time;
464bf215546Sopenharmony_ci   struct v3dv_bo_cache *cache = &device->bo_cache;
465bf215546Sopenharmony_ci   uint32_t page_index = bo->size / 4096 - 1;
466bf215546Sopenharmony_ci
467bf215546Sopenharmony_ci   if (bo->private &&
468bf215546Sopenharmony_ci       bo->size > cache->max_cache_size - cache->cache_size) {
469bf215546Sopenharmony_ci      clock_gettime(CLOCK_MONOTONIC, &time);
470bf215546Sopenharmony_ci      mtx_lock(&cache->lock);
471bf215546Sopenharmony_ci      free_stale_bos(device, time.tv_sec);
472bf215546Sopenharmony_ci      mtx_unlock(&cache->lock);
473bf215546Sopenharmony_ci   }
474bf215546Sopenharmony_ci
475bf215546Sopenharmony_ci   if (!bo->private ||
476bf215546Sopenharmony_ci       bo->size > cache->max_cache_size - cache->cache_size) {
477bf215546Sopenharmony_ci      return bo_free(device, bo);
478bf215546Sopenharmony_ci   }
479bf215546Sopenharmony_ci
480bf215546Sopenharmony_ci   clock_gettime(CLOCK_MONOTONIC, &time);
481bf215546Sopenharmony_ci   mtx_lock(&cache->lock);
482bf215546Sopenharmony_ci
483bf215546Sopenharmony_ci   if (cache->size_list_size <= page_index) {
484bf215546Sopenharmony_ci      if (!reallocate_size_list(cache, device, page_index + 1)) {
485bf215546Sopenharmony_ci         bool outcome = bo_free(device, bo);
486bf215546Sopenharmony_ci         /* If the reallocation failed, it usually means that we are out of
487bf215546Sopenharmony_ci          * memory, so we also free all the bo cache. We need to call it to
488bf215546Sopenharmony_ci          * not use the cache lock, as we are already under it.
489bf215546Sopenharmony_ci          */
490bf215546Sopenharmony_ci         bo_cache_free_all(device, false);
491bf215546Sopenharmony_ci         mtx_unlock(&cache->lock);
492bf215546Sopenharmony_ci         return outcome;
493bf215546Sopenharmony_ci      }
494bf215546Sopenharmony_ci   }
495bf215546Sopenharmony_ci
496bf215546Sopenharmony_ci   bo->free_time = time.tv_sec;
497bf215546Sopenharmony_ci   list_addtail(&bo->size_list, &cache->size_list[page_index]);
498bf215546Sopenharmony_ci   list_addtail(&bo->time_list, &cache->time_list);
499bf215546Sopenharmony_ci
500bf215546Sopenharmony_ci   cache->cache_count++;
501bf215546Sopenharmony_ci   cache->cache_size += bo->size;
502bf215546Sopenharmony_ci
503bf215546Sopenharmony_ci   if (dump_stats) {
504bf215546Sopenharmony_ci      fprintf(stderr, "Freed %s %dkb to cache:\n",
505bf215546Sopenharmony_ci              bo->name, bo->size / 1024);
506bf215546Sopenharmony_ci      bo_dump_stats(device);
507bf215546Sopenharmony_ci   }
508bf215546Sopenharmony_ci   bo->name = NULL;
509bf215546Sopenharmony_ci
510bf215546Sopenharmony_ci   free_stale_bos(device, time.tv_sec);
511bf215546Sopenharmony_ci
512bf215546Sopenharmony_ci   mtx_unlock(&cache->lock);
513bf215546Sopenharmony_ci
514bf215546Sopenharmony_ci   return true;
515bf215546Sopenharmony_ci}
516