xref: /aosp_15_r20/external/mesa3d/src/imagination/vulkan/pvr_bo.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2022 Imagination Technologies Ltd.
3  *
4  * based in part on tu driver which is:
5  * Copyright © 2022 Google LLC
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the next
15  * paragraph) shall be included in all copies or substantial portions of the
16  * Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  */
26 
27 #include <assert.h>
28 #include <limits.h>
29 #include <stddef.h>
30 #include <stdint.h>
31 #include <stdio.h>
32 #include <vulkan/vulkan.h>
33 
34 #if defined(HAVE_VALGRIND)
35 #   include <valgrind.h>
36 #   include <memcheck.h>
37 #endif
38 
39 #include "hwdef/rogue_hw_utils.h"
40 #include "pvr_bo.h"
41 #include "pvr_debug.h"
42 #include "pvr_dump.h"
43 #include "pvr_private.h"
44 #include "pvr_types.h"
45 #include "pvr_util.h"
46 #include "pvr_winsys.h"
47 #include "util/macros.h"
48 #include "util/rb_tree.h"
49 #include "util/simple_mtx.h"
50 #include "util/u_debug.h"
51 #include "vk_alloc.h"
52 #include "vk_log.h"
53 
54 struct pvr_bo_store {
55    struct rb_tree tree;
56    simple_mtx_t mutex;
57    uint32_t size;
58 };
59 
60 struct pvr_bo_store_entry {
61    struct rb_node node;
62    struct pvr_bo bo;
63 };
64 
65 #define entry_from_node(node_) \
66    container_of(node_, struct pvr_bo_store_entry, node)
67 #define entry_from_bo(bo_) container_of(bo_, struct pvr_bo_store_entry, bo)
68 
pvr_dev_addr_cmp(const pvr_dev_addr_t a,const pvr_dev_addr_t b)69 static inline int pvr_dev_addr_cmp(const pvr_dev_addr_t a,
70                                    const pvr_dev_addr_t b)
71 {
72    const uint64_t addr_a = a.addr;
73    const uint64_t addr_b = b.addr;
74 
75    if (addr_a < addr_b)
76       return 1;
77    else if (addr_a > addr_b)
78       return -1;
79    else
80       return 0;
81 }
82 
83 /* Borrowed from pandecode. Using this comparator allows us to lookup intervals
84  * in the RB-tree without storing extra information.
85  */
pvr_bo_store_entry_cmp_key(const struct rb_node * node,const void * const key)86 static inline int pvr_bo_store_entry_cmp_key(const struct rb_node *node,
87                                              const void *const key)
88 {
89    const struct pvr_winsys_vma *const vma = entry_from_node(node)->bo.vma;
90    const pvr_dev_addr_t addr = *(const pvr_dev_addr_t *)key;
91 
92    if (addr.addr >= vma->dev_addr.addr &&
93        addr.addr < (vma->dev_addr.addr + vma->size)) {
94       return 0;
95    }
96 
97    return pvr_dev_addr_cmp(vma->dev_addr, addr);
98 }
99 
pvr_bo_store_entry_cmp(const struct rb_node * const a,const struct rb_node * const b)100 static inline int pvr_bo_store_entry_cmp(const struct rb_node *const a,
101                                          const struct rb_node *const b)
102 {
103    return pvr_dev_addr_cmp(entry_from_node(a)->bo.vma->dev_addr,
104                            entry_from_node(b)->bo.vma->dev_addr);
105 }
106 
pvr_bo_store_create(struct pvr_device * device)107 VkResult pvr_bo_store_create(struct pvr_device *device)
108 {
109    struct pvr_bo_store *store;
110 
111    if (!PVR_IS_DEBUG_SET(TRACK_BOS)) {
112       device->bo_store = NULL;
113       return VK_SUCCESS;
114    }
115 
116    store = vk_alloc(&device->vk.alloc,
117                     sizeof(*store),
118                     8,
119                     VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
120    if (!store)
121       return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
122 
123    rb_tree_init(&store->tree);
124    store->size = 0;
125    simple_mtx_init(&store->mutex, mtx_plain);
126 
127    device->bo_store = store;
128 
129    return VK_SUCCESS;
130 }
131 
pvr_bo_store_destroy(struct pvr_device * device)132 void pvr_bo_store_destroy(struct pvr_device *device)
133 {
134    struct pvr_bo_store *store = device->bo_store;
135 
136    if (likely(!store))
137       return;
138 
139    if (unlikely(!rb_tree_is_empty(&store->tree))) {
140       debug_warning("Non-empty BO store destroyed; dump follows");
141       pvr_bo_store_dump(device);
142    }
143 
144    simple_mtx_destroy(&store->mutex);
145 
146    vk_free(&device->vk.alloc, store);
147 
148    device->bo_store = NULL;
149 }
150 
pvr_bo_store_insert(struct pvr_bo_store * const store,struct pvr_bo * const bo)151 static void pvr_bo_store_insert(struct pvr_bo_store *const store,
152                                 struct pvr_bo *const bo)
153 {
154    if (likely(!store))
155       return;
156 
157    simple_mtx_lock(&store->mutex);
158    rb_tree_insert(&store->tree,
159                   &entry_from_bo(bo)->node,
160                   pvr_bo_store_entry_cmp);
161    store->size++;
162    simple_mtx_unlock(&store->mutex);
163 }
164 
pvr_bo_store_remove(struct pvr_bo_store * const store,struct pvr_bo * const bo)165 static void pvr_bo_store_remove(struct pvr_bo_store *const store,
166                                 struct pvr_bo *const bo)
167 {
168    if (likely(!store))
169       return;
170 
171    simple_mtx_lock(&store->mutex);
172    rb_tree_remove(&store->tree, &entry_from_bo(bo)->node);
173    store->size--;
174    simple_mtx_unlock(&store->mutex);
175 }
176 
pvr_bo_store_lookup(struct pvr_device * const device,const pvr_dev_addr_t addr)177 struct pvr_bo *pvr_bo_store_lookup(struct pvr_device *const device,
178                                    const pvr_dev_addr_t addr)
179 {
180    struct pvr_bo_store *const store = device->bo_store;
181    struct rb_node *node;
182 
183    if (unlikely(!store))
184       return NULL;
185 
186    simple_mtx_lock(&store->mutex);
187    node = rb_tree_search(&store->tree, &addr, pvr_bo_store_entry_cmp_key);
188    simple_mtx_unlock(&store->mutex);
189 
190    if (!node)
191       return NULL;
192 
193    return &entry_from_node(node)->bo;
194 }
195 
pvr_bo_dump_line(struct pvr_dump_ctx * const ctx,const struct pvr_bo * bo,const uint32_t index,const uint32_t nr_bos_log10)196 static void pvr_bo_dump_line(struct pvr_dump_ctx *const ctx,
197                              const struct pvr_bo *bo,
198                              const uint32_t index,
199                              const uint32_t nr_bos_log10)
200 {
201    static const char *const pretty_sizes[64 + 1] = {
202       "",        "1 B",     "2 B",     "4 B",     "8 B",     "16 B",
203       "32 B",    "64 B",    "128 B",   "256 B",   "512 B",   "1 KiB",
204       "2 KiB",   "4 KiB",   "8 KiB",   "16 KiB",  "32 KiB",  "64 KiB",
205       "128 KiB", "256 KiB", "512 KiB", "1 MiB",   "2 MiB",   "4 MiB",
206       "8 MiB",   "16 MiB",  "32 MiB",  "64 MiB",  "128 MiB", "256 MiB",
207       "512 MiB", "1 GiB",   "2 GiB",   "4 GiB",   "8 GiB",   "16 GiB",
208       "32 GiB",  "64 GiB",  "128 GiB", "256 GiB", "512 GiB", "1 TiB",
209       "2 TiB",   "4 TiB",   "8 TiB",   "16 TiB",  "32 TiB",  "64 TiB",
210       "128 TiB", "256 TiB", "512 TiB", "1 PiB",   "2 PiB",   "4 PiB",
211       "8 PiB",   "16 PiB",  "32 PiB",  "64 PiB",  "128 PiB", "256 PiB",
212       "512 PiB", "1 EiB",   "2 EiB",   "4 EiB",   "8 EiB",
213    };
214 
215    const uint64_t size = bo->vma->size;
216    const uint32_t size_log2 =
217       util_is_power_of_two_or_zero64(size) ? util_last_bit(size) : 0;
218 
219    pvr_dump_println(ctx,
220                     "[%0*" PRIu32 "] " PVR_DEV_ADDR_FMT " -> %*p "
221                     "(%s%s0x%" PRIx64 " bytes)",
222                     nr_bos_log10,
223                     index,
224                     bo->vma->dev_addr.addr,
225                     (int)sizeof(void *) * 2 + 2, /* nr hex digits + 0x prefix */
226                     bo->bo->map,
227                     pretty_sizes[size_log2],
228                     size_log2 ? ", " : "",
229                     size);
230 }
231 
pvr_bo_store_dump(struct pvr_device * const device)232 bool pvr_bo_store_dump(struct pvr_device *const device)
233 {
234    struct pvr_bo_store *const store = device->bo_store;
235    const uint32_t nr_bos = store->size;
236    const uint32_t nr_bos_log10 = u32_dec_digits(nr_bos);
237    struct pvr_dump_ctx ctx;
238    uint32_t bo_idx = 0;
239 
240    if (unlikely(!store)) {
241       debug_warning("Requested BO store dump, but no BO store is present.");
242       return false;
243    }
244 
245    pvr_dump_begin(&ctx, stderr, "BO STORE", 1);
246 
247    pvr_dump_println(&ctx, "Dumping %" PRIu32 " BO store entries...", nr_bos);
248 
249    pvr_dump_indent(&ctx);
250    rb_tree_foreach_safe (struct pvr_bo_store_entry, entry, &store->tree, node) {
251       pvr_bo_dump_line(&ctx, &entry->bo, bo_idx++, nr_bos_log10);
252    }
253    pvr_dump_dedent(&ctx);
254 
255    return pvr_dump_end(&ctx);
256 }
257 
pvr_bo_list_dump(struct pvr_dump_ctx * const ctx,const struct list_head * const bo_list,const uint32_t nr_bos)258 void pvr_bo_list_dump(struct pvr_dump_ctx *const ctx,
259                       const struct list_head *const bo_list,
260                       const uint32_t nr_bos)
261 {
262    const uint32_t real_nr_bos = nr_bos ? nr_bos : list_length(bo_list);
263    const uint32_t nr_bos_log10 = u32_dec_digits(real_nr_bos);
264    uint32_t bo_idx = 0;
265 
266    list_for_each_entry (struct pvr_bo, bo, bo_list, link) {
267       pvr_bo_dump_line(ctx, bo, bo_idx++, nr_bos_log10);
268    }
269 }
270 
pvr_bo_alloc_to_winsys_flags(uint64_t flags)271 static uint32_t pvr_bo_alloc_to_winsys_flags(uint64_t flags)
272 {
273    uint32_t ws_flags = 0;
274 
275    if (flags & (PVR_BO_ALLOC_FLAG_CPU_ACCESS | PVR_BO_ALLOC_FLAG_CPU_MAPPED))
276       ws_flags |= PVR_WINSYS_BO_FLAG_CPU_ACCESS;
277 
278    if (flags & PVR_BO_ALLOC_FLAG_GPU_UNCACHED)
279       ws_flags |= PVR_WINSYS_BO_FLAG_GPU_UNCACHED;
280 
281    if (flags & PVR_BO_ALLOC_FLAG_PM_FW_PROTECT)
282       ws_flags |= PVR_WINSYS_BO_FLAG_PM_FW_PROTECT;
283 
284    return ws_flags;
285 }
286 
287 static inline struct pvr_bo *
pvr_bo_alloc_bo(const struct pvr_device * const device)288 pvr_bo_alloc_bo(const struct pvr_device *const device)
289 {
290    size_t size;
291    void *ptr;
292 
293    if (unlikely(device->bo_store))
294       size = sizeof(struct pvr_bo_store_entry);
295    else
296       size = sizeof(struct pvr_bo);
297 
298    ptr =
299       vk_alloc(&device->vk.alloc, size, 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
300    if (unlikely(!ptr))
301       return NULL;
302 
303    if (unlikely(device->bo_store))
304       return &((struct pvr_bo_store_entry *)ptr)->bo;
305    else
306       return (struct pvr_bo *)ptr;
307 }
308 
pvr_bo_free_bo(const struct pvr_device * const device,struct pvr_bo * const bo)309 static inline void pvr_bo_free_bo(const struct pvr_device *const device,
310                                   struct pvr_bo *const bo)
311 {
312    void *ptr;
313 
314    if (unlikely(device->bo_store))
315       ptr = entry_from_bo(bo);
316    else
317       ptr = bo;
318 
319    vk_free(&device->vk.alloc, ptr);
320 }
321 
322 /**
323  * \brief Helper interface to allocate a GPU buffer and map it to both host and
324  * device virtual memory. Host mapping is conditional and is controlled by
325  * flags.
326  *
327  * \param[in] device      Logical device pointer.
328  * \param[in] heap        Heap to allocate device virtual address from.
329  * \param[in] size        Size of buffer to allocate.
330  * \param[in] alignment   Required alignment of the allocation. Must be a power
331  *                        of two.
332  * \param[in] flags       Controls allocation, CPU and GPU mapping behavior
333  *                        using PVR_BO_ALLOC_FLAG_*.
334  * \param[out] pvr_bo_out On success output buffer is returned in this pointer.
335  * \return VK_SUCCESS on success, or error code otherwise.
336  *
337  * \sa #pvr_bo_free()
338  */
pvr_bo_alloc(struct pvr_device * device,struct pvr_winsys_heap * heap,uint64_t size,uint64_t alignment,uint64_t flags,struct pvr_bo ** const pvr_bo_out)339 VkResult pvr_bo_alloc(struct pvr_device *device,
340                       struct pvr_winsys_heap *heap,
341                       uint64_t size,
342                       uint64_t alignment,
343                       uint64_t flags,
344                       struct pvr_bo **const pvr_bo_out)
345 {
346    struct pvr_bo *pvr_bo;
347    VkResult result;
348 
349    pvr_bo = pvr_bo_alloc_bo(device);
350    if (!pvr_bo) {
351       result = vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
352       goto err_out;
353    }
354 
355    pvr_bo->ref_count = 1;
356 
357    result = device->ws->ops->buffer_create(device->ws,
358                                            size,
359                                            alignment,
360                                            PVR_WINSYS_BO_TYPE_GPU,
361                                            pvr_bo_alloc_to_winsys_flags(flags),
362                                            &pvr_bo->bo);
363    if (result != VK_SUCCESS)
364       goto err_free_bo;
365 
366    if (flags & PVR_BO_ALLOC_FLAG_CPU_MAPPED) {
367       result = device->ws->ops->buffer_map(pvr_bo->bo);
368       if (result != VK_SUCCESS)
369          goto err_buffer_destroy;
370 
371       VG(VALGRIND_MAKE_MEM_DEFINED(pvr_bo->bo->map, pvr_bo->bo->size));
372    }
373 
374    result = device->ws->ops->heap_alloc(heap, size, alignment, &pvr_bo->vma);
375    if (result != VK_SUCCESS)
376       goto err_buffer_unmap;
377 
378    result = device->ws->ops->vma_map(pvr_bo->vma, pvr_bo->bo, 0, size, NULL);
379    if (result != VK_SUCCESS)
380       goto err_heap_free;
381 
382    pvr_bo_store_insert(device->bo_store, pvr_bo);
383    *pvr_bo_out = pvr_bo;
384 
385    return VK_SUCCESS;
386 
387 err_heap_free:
388    device->ws->ops->heap_free(pvr_bo->vma);
389 
390 err_buffer_unmap:
391    if (flags & PVR_BO_ALLOC_FLAG_CPU_MAPPED)
392       device->ws->ops->buffer_unmap(pvr_bo->bo);
393 
394 err_buffer_destroy:
395    device->ws->ops->buffer_destroy(pvr_bo->bo);
396 
397 err_free_bo:
398    pvr_bo_free_bo(device, pvr_bo);
399 
400 err_out:
401    return result;
402 }
403 
404 /**
405  * \brief Interface to map the buffer into host virtual address space.
406  *
407  * Buffer should have been created with the #PVR_BO_ALLOC_FLAG_CPU_ACCESS
408  * flag. It should also not already be mapped or it should have been unmapped
409  * using #pvr_bo_cpu_unmap() before mapping again.
410  *
411  * \param[in] device Logical device pointer.
412  * \param[in] pvr_bo Buffer to map.
413  * \return Valid host virtual address on success, or NULL otherwise.
414  *
415  * \sa #pvr_bo_alloc(), #PVR_BO_ALLOC_FLAG_CPU_MAPPED
416  */
pvr_bo_cpu_map(struct pvr_device * device,struct pvr_bo * pvr_bo)417 VkResult pvr_bo_cpu_map(struct pvr_device *device, struct pvr_bo *pvr_bo)
418 {
419    assert(!pvr_bo->bo->map);
420 
421    return device->ws->ops->buffer_map(pvr_bo->bo);
422 }
423 
424 /**
425  * \brief Interface to unmap the buffer from host virtual address space.
426  *
427  * Buffer should have a valid mapping, created either using #pvr_bo_cpu_map() or
428  * by passing #PVR_BO_ALLOC_FLAG_CPU_MAPPED flag to #pvr_bo_alloc() at
429  * allocation time.
430  *
431  * Buffer can be remapped using #pvr_bo_cpu_map().
432  *
433  * \param[in] device Logical device pointer.
434  * \param[in] pvr_bo Buffer to unmap.
435  */
pvr_bo_cpu_unmap(struct pvr_device * device,struct pvr_bo * pvr_bo)436 void pvr_bo_cpu_unmap(struct pvr_device *device, struct pvr_bo *pvr_bo)
437 {
438    struct pvr_winsys_bo *bo = pvr_bo->bo;
439 
440    assert(bo->map);
441 
442 #if defined(HAVE_VALGRIND)
443    if (!bo->vbits)
444       bo->vbits = vk_alloc(&device->vk.alloc,
445                            bo->size,
446                            8,
447                            VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
448    if (bo->vbits) {
449       unsigned ret = VALGRIND_GET_VBITS(bo->map, bo->vbits, bo->size);
450       if (ret != 0 && ret != 1)
451          mesa_loge("Failed to get vbits; expect bad valgrind results.");
452    } else {
453       mesa_loge("Failed to alloc vbits storage; expect bad valgrind results.");
454    }
455 #endif /* defined(HAVE_VALGRIND) */
456 
457    device->ws->ops->buffer_unmap(bo);
458 }
459 
460 /**
461  * \brief Interface to free the buffer object.
462  *
463  * \param[in] device Logical device pointer.
464  * \param[in] pvr_bo Buffer to free.
465  *
466  * \sa #pvr_bo_alloc()
467  */
pvr_bo_free(struct pvr_device * device,struct pvr_bo * pvr_bo)468 void pvr_bo_free(struct pvr_device *device, struct pvr_bo *pvr_bo)
469 {
470    if (!pvr_bo)
471       return;
472 
473    if (!p_atomic_dec_zero(&pvr_bo->ref_count))
474       return;
475 
476 #if defined(HAVE_VALGRIND)
477    vk_free(&device->vk.alloc, pvr_bo->bo->vbits);
478 #endif /* defined(HAVE_VALGRIND) */
479 
480    pvr_bo_store_remove(device->bo_store, pvr_bo);
481 
482    device->ws->ops->vma_unmap(pvr_bo->vma);
483    device->ws->ops->heap_free(pvr_bo->vma);
484 
485    if (pvr_bo->bo->map)
486       device->ws->ops->buffer_unmap(pvr_bo->bo);
487 
488    device->ws->ops->buffer_destroy(pvr_bo->bo);
489 
490    pvr_bo_free_bo(device, pvr_bo);
491 }
492 
493 /**
494  * \brief Interface to initialize a pvr_suballocator.
495  *
496  * \param[in] allocator    Sub-allocator to initialize.
497  * \param[in] heap         Heap to sub-allocate device virtual address from.
498  * \param[in] device       Logical device pointer.
499  * \param[in] default_size Minimum size used for pvr bo(s).
500  *
501  * \sa #pvr_bo_suballocator_fini()
502  */
pvr_bo_suballocator_init(struct pvr_suballocator * allocator,struct pvr_winsys_heap * heap,struct pvr_device * device,uint32_t default_size)503 void pvr_bo_suballocator_init(struct pvr_suballocator *allocator,
504                               struct pvr_winsys_heap *heap,
505                               struct pvr_device *device,
506                               uint32_t default_size)
507 {
508    *allocator = (struct pvr_suballocator){
509       .device = device,
510       .default_size = default_size,
511       .heap = heap,
512    };
513 
514    simple_mtx_init(&allocator->mtx, mtx_plain);
515 }
516 
517 /**
518  * \brief Interface to destroy a pvr_suballocator.
519  *
520  * \param[in] allocator Sub-allocator to clean-up.
521  *
522  * \sa #pvr_bo_suballocator_init()
523  */
pvr_bo_suballocator_fini(struct pvr_suballocator * allocator)524 void pvr_bo_suballocator_fini(struct pvr_suballocator *allocator)
525 {
526    pvr_bo_free(allocator->device, allocator->bo);
527    pvr_bo_free(allocator->device, allocator->bo_cached);
528 
529    simple_mtx_destroy(&allocator->mtx);
530 }
531 
pvr_bo_get_ref(struct pvr_bo * bo)532 static inline struct pvr_bo *pvr_bo_get_ref(struct pvr_bo *bo)
533 {
534    p_atomic_inc(&bo->ref_count);
535 
536    return bo;
537 }
538 
539 /**
540  * \brief Interface to sub-allocate buffer objects.
541  *
542  * \param[in]  allocator       Sub-allocator used to make a sub-allocation.
543  * \param[in]  size            Size of buffer to sub-alllocate.
544  * \param[in]  align           Required alignment of the allocation. Must be
545  *                             a power of two.
546  * \param[in]  zero_on_alloc   Require memory for the sub-allocation to be 0.
547  * \param[out] suballoc_bo_out On success points to the sub-allocated buffer
548  *                             object.
549  * \return VK_SUCCESS on success, or error code otherwise.
550  *
551  * \sa # pvr_bo_suballoc_free()
552  */
pvr_bo_suballoc(struct pvr_suballocator * allocator,uint32_t size,uint32_t align,bool zero_on_alloc,struct pvr_suballoc_bo ** const suballoc_bo_out)553 VkResult pvr_bo_suballoc(struct pvr_suballocator *allocator,
554                          uint32_t size,
555                          uint32_t align,
556                          bool zero_on_alloc,
557                          struct pvr_suballoc_bo **const suballoc_bo_out)
558 {
559    const struct pvr_device_info *dev_info =
560       &allocator->device->pdevice->dev_info;
561    const uint32_t cache_line_size = rogue_get_slc_cache_line_size(dev_info);
562    struct pvr_suballoc_bo *suballoc_bo;
563    uint32_t alloc_size, aligned_size;
564    VkResult result;
565 
566    suballoc_bo = vk_alloc(&allocator->device->vk.alloc,
567                           sizeof(*suballoc_bo),
568                           8,
569                           VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
570    if (!suballoc_bo)
571       return vk_error(allocator->device, VK_ERROR_OUT_OF_HOST_MEMORY);
572 
573    /* This cache line value is used for all type of allocations (i.e. USC, PDS,
574     * transfer and general), so always align them to at least the size of the
575     * cache line.
576     */
577    align = MAX2(align, cache_line_size);
578    assert(util_is_power_of_two_nonzero(align));
579    aligned_size = ALIGN_POT(size, align);
580 
581    simple_mtx_lock(&allocator->mtx);
582 
583    if (allocator->bo) {
584       uint32_t aligned_offset = ALIGN_POT(allocator->next_offset, align);
585 
586       if (aligned_offset + aligned_size <= allocator->bo->bo->size) {
587          suballoc_bo->allocator = allocator;
588          suballoc_bo->bo = pvr_bo_get_ref(allocator->bo);
589          suballoc_bo->dev_addr =
590             PVR_DEV_ADDR_OFFSET(allocator->bo->vma->dev_addr, aligned_offset);
591          suballoc_bo->offset = aligned_offset;
592          suballoc_bo->size = aligned_size;
593 
594          allocator->next_offset = aligned_offset + aligned_size;
595 
596          if (zero_on_alloc)
597             memset(pvr_bo_suballoc_get_map_addr(suballoc_bo), 0, aligned_size);
598 
599          *suballoc_bo_out = suballoc_bo;
600          simple_mtx_unlock(&allocator->mtx);
601 
602          return VK_SUCCESS;
603       } else {
604          pvr_bo_free(allocator->device, allocator->bo);
605          allocator->bo = NULL;
606       }
607    }
608 
609    alloc_size = MAX2(aligned_size, ALIGN_POT(allocator->default_size, align));
610 
611    if (allocator->bo_cached) {
612       struct pvr_winsys_bo *bo_cached = allocator->bo_cached->bo;
613 
614       if (alloc_size <= bo_cached->size)
615          allocator->bo = allocator->bo_cached;
616       else
617          pvr_bo_free(allocator->device, allocator->bo_cached);
618 
619       allocator->bo_cached = NULL;
620    }
621 
622    if (!allocator->bo) {
623       result = pvr_bo_alloc(allocator->device,
624                             allocator->heap,
625                             alloc_size,
626                             align,
627                             PVR_BO_ALLOC_FLAG_CPU_MAPPED,
628                             &allocator->bo);
629       if (result != VK_SUCCESS) {
630          vk_free(&allocator->device->vk.alloc, suballoc_bo);
631          simple_mtx_unlock(&allocator->mtx);
632          return result;
633       }
634    }
635 
636    suballoc_bo->allocator = allocator;
637    suballoc_bo->bo = pvr_bo_get_ref(allocator->bo);
638    suballoc_bo->dev_addr = allocator->bo->vma->dev_addr;
639    suballoc_bo->offset = 0;
640    suballoc_bo->size = aligned_size;
641 
642    allocator->next_offset = aligned_size;
643 
644    if (zero_on_alloc)
645       memset(pvr_bo_suballoc_get_map_addr(suballoc_bo), 0, aligned_size);
646 
647    *suballoc_bo_out = suballoc_bo;
648    simple_mtx_unlock(&allocator->mtx);
649 
650    return VK_SUCCESS;
651 }
652 
653 /**
654  * \brief Interface to free a sub-allocated buffer object.
655  *
656  * \param[in] suballoc_bo Sub-allocated buffer object to free.
657  *
658  * \sa #pvr_bo_suballoc()
659  */
pvr_bo_suballoc_free(struct pvr_suballoc_bo * suballoc_bo)660 void pvr_bo_suballoc_free(struct pvr_suballoc_bo *suballoc_bo)
661 {
662    if (!suballoc_bo)
663       return;
664 
665    simple_mtx_lock(&suballoc_bo->allocator->mtx);
666 
667    if (p_atomic_read(&suballoc_bo->bo->ref_count) == 1 &&
668        !suballoc_bo->allocator->bo_cached) {
669       suballoc_bo->allocator->bo_cached = suballoc_bo->bo;
670    } else {
671       pvr_bo_free(suballoc_bo->allocator->device, suballoc_bo->bo);
672    }
673 
674    simple_mtx_unlock(&suballoc_bo->allocator->mtx);
675 
676    vk_free(&suballoc_bo->allocator->device->vk.alloc, suballoc_bo);
677 }
678 
679 /**
680  * \brief Interface to retrieve sub-allocated memory offset from the host
681  * virtual address space.
682  *
683  * \param[in] suballoc_bo Sub-allocated buffer object pointer.
684  *
685  * \return Valid host virtual address on success.
686  *
687  * \sa #pvr_bo_suballoc()
688  */
pvr_bo_suballoc_get_map_addr(const struct pvr_suballoc_bo * suballoc_bo)689 void *pvr_bo_suballoc_get_map_addr(const struct pvr_suballoc_bo *suballoc_bo)
690 {
691    const struct pvr_bo *pvr_bo = suballoc_bo->bo;
692 
693    assert((uint8_t *)pvr_bo->bo->map + suballoc_bo->offset <
694           (uint8_t *)pvr_bo->bo->map + pvr_bo->bo->size);
695 
696    return (uint8_t *)pvr_bo->bo->map + suballoc_bo->offset;
697 }
698 
699 #if defined(HAVE_VALGRIND)
pvr_bo_cpu_map_unchanged(struct pvr_device * device,struct pvr_bo * pvr_bo)700 VkResult pvr_bo_cpu_map_unchanged(struct pvr_device *device,
701                                   struct pvr_bo *pvr_bo)
702 {
703    VkResult result = pvr_bo_cpu_map(device, pvr_bo);
704    if (result == VK_SUCCESS) {
705       unsigned ret = VALGRIND_SET_VBITS(pvr_bo->bo->map,
706                                         pvr_bo->bo->vbits,
707                                         pvr_bo->bo->size);
708       if (ret != 0 && ret != 1)
709          mesa_loge("Failed to set vbits; expect bad valgrind results.");
710    }
711 
712    return result;
713 }
714 #endif /* defined(HAVE_VALGRIND) */
715