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