1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright © 2022 Google LLC
3bf215546Sopenharmony_ci * SPDX-License-Identifier: MIT
4bf215546Sopenharmony_ci */
5bf215546Sopenharmony_ci
6bf215546Sopenharmony_ci/**
7bf215546Sopenharmony_ci * Suballocator for space within BOs.
8bf215546Sopenharmony_ci *
9bf215546Sopenharmony_ci * BOs are allocated at PAGE_SIZE (typically 4k) granularity, so small
10bf215546Sopenharmony_ci * allocations are a waste to have in their own BO.  Moreover, on DRM we track a
11bf215546Sopenharmony_ci * list of all BOs currently allocated and submit the whole list for validation
12bf215546Sopenharmony_ci * (busy tracking and implicit sync) on every submit, and that validation is a
13bf215546Sopenharmony_ci * non-trivial cost.  So, being able to pack multiple allocations into a BO can
14bf215546Sopenharmony_ci * be a significant performance win.
15bf215546Sopenharmony_ci *
16bf215546Sopenharmony_ci * The allocator tracks a current BO it is linearly allocating from, and up to
17bf215546Sopenharmony_ci * one extra BO returned to the pool when all of its previous suballocations
18bf215546Sopenharmony_ci * have been freed. This means that fragmentation can be an issue for
19bf215546Sopenharmony_ci * default_size > PAGE_SIZE and small allocations.  Also, excessive BO
20bf215546Sopenharmony_ci * reallocation may happen for workloads where default size < working set size.
21bf215546Sopenharmony_ci */
22bf215546Sopenharmony_ci
23bf215546Sopenharmony_ci#include "tu_suballoc.h"
24bf215546Sopenharmony_ci
25bf215546Sopenharmony_ci/* Initializes a BO sub-allocator using refcounts on BOs.
26bf215546Sopenharmony_ci */
27bf215546Sopenharmony_civoid
28bf215546Sopenharmony_citu_bo_suballocator_init(struct tu_suballocator *suballoc,
29bf215546Sopenharmony_ci                        struct tu_device *dev,
30bf215546Sopenharmony_ci                        uint32_t default_size, uint32_t flags)
31bf215546Sopenharmony_ci{
32bf215546Sopenharmony_ci   suballoc->dev = dev;
33bf215546Sopenharmony_ci   suballoc->default_size = default_size;
34bf215546Sopenharmony_ci   suballoc->flags = flags;
35bf215546Sopenharmony_ci   suballoc->bo = NULL;
36bf215546Sopenharmony_ci   suballoc->cached_bo = NULL;
37bf215546Sopenharmony_ci}
38bf215546Sopenharmony_ci
39bf215546Sopenharmony_civoid
40bf215546Sopenharmony_citu_bo_suballocator_finish(struct tu_suballocator *suballoc)
41bf215546Sopenharmony_ci{
42bf215546Sopenharmony_ci   if (suballoc->bo)
43bf215546Sopenharmony_ci      tu_bo_finish(suballoc->dev, suballoc->bo);
44bf215546Sopenharmony_ci   if (suballoc->cached_bo)
45bf215546Sopenharmony_ci      tu_bo_finish(suballoc->dev, suballoc->cached_bo);
46bf215546Sopenharmony_ci}
47bf215546Sopenharmony_ci
48bf215546Sopenharmony_ciVkResult
49bf215546Sopenharmony_citu_suballoc_bo_alloc(struct tu_suballoc_bo *suballoc_bo,
50bf215546Sopenharmony_ci                     struct tu_suballocator *suballoc,
51bf215546Sopenharmony_ci                     uint32_t size, uint32_t align)
52bf215546Sopenharmony_ci{
53bf215546Sopenharmony_ci   struct tu_bo *bo = suballoc->bo;
54bf215546Sopenharmony_ci   if (bo) {
55bf215546Sopenharmony_ci      uint32_t offset = ALIGN(suballoc->next_offset, align);
56bf215546Sopenharmony_ci      if (offset + size <= bo->size) {
57bf215546Sopenharmony_ci         suballoc_bo->bo = tu_bo_get_ref(bo);
58bf215546Sopenharmony_ci         suballoc_bo->iova = bo->iova + offset;
59bf215546Sopenharmony_ci         suballoc_bo->size = size;
60bf215546Sopenharmony_ci
61bf215546Sopenharmony_ci         suballoc->next_offset = offset + size;
62bf215546Sopenharmony_ci         return VK_SUCCESS;
63bf215546Sopenharmony_ci      } else {
64bf215546Sopenharmony_ci         tu_bo_finish(suballoc->dev, bo);
65bf215546Sopenharmony_ci         suballoc->bo = NULL;
66bf215546Sopenharmony_ci      }
67bf215546Sopenharmony_ci   }
68bf215546Sopenharmony_ci
69bf215546Sopenharmony_ci   uint32_t alloc_size = MAX2(size, suballoc->default_size);
70bf215546Sopenharmony_ci
71bf215546Sopenharmony_ci   /* Reuse a recycled suballoc BO if we have one and it's big enough, otherwise free it. */
72bf215546Sopenharmony_ci   if (suballoc->cached_bo) {
73bf215546Sopenharmony_ci      if (alloc_size <= suballoc->cached_bo->size)
74bf215546Sopenharmony_ci         suballoc->bo = suballoc->cached_bo;
75bf215546Sopenharmony_ci      else
76bf215546Sopenharmony_ci         tu_bo_finish(suballoc->dev, suballoc->cached_bo);
77bf215546Sopenharmony_ci      suballoc->cached_bo = NULL;
78bf215546Sopenharmony_ci   }
79bf215546Sopenharmony_ci
80bf215546Sopenharmony_ci   /* Allocate the new BO if we didn't have one cached. */
81bf215546Sopenharmony_ci   if (!suballoc->bo) {
82bf215546Sopenharmony_ci      VkResult result = tu_bo_init_new(suballoc->dev, &suballoc->bo,
83bf215546Sopenharmony_ci                                       alloc_size,
84bf215546Sopenharmony_ci                                       suballoc->flags);
85bf215546Sopenharmony_ci      if (result != VK_SUCCESS)
86bf215546Sopenharmony_ci         return result;
87bf215546Sopenharmony_ci   }
88bf215546Sopenharmony_ci
89bf215546Sopenharmony_ci   VkResult result = tu_bo_map(suballoc->dev, suballoc->bo);
90bf215546Sopenharmony_ci   if (result != VK_SUCCESS) {
91bf215546Sopenharmony_ci      tu_bo_finish(suballoc->dev, suballoc->bo);
92bf215546Sopenharmony_ci      return VK_ERROR_OUT_OF_HOST_MEMORY;
93bf215546Sopenharmony_ci   }
94bf215546Sopenharmony_ci
95bf215546Sopenharmony_ci   suballoc_bo->bo = tu_bo_get_ref(suballoc->bo);
96bf215546Sopenharmony_ci   suballoc_bo->iova = suballoc_bo->bo->iova;
97bf215546Sopenharmony_ci   suballoc_bo->size = size;
98bf215546Sopenharmony_ci   suballoc->next_offset = size;
99bf215546Sopenharmony_ci
100bf215546Sopenharmony_ci   return VK_SUCCESS;
101bf215546Sopenharmony_ci}
102bf215546Sopenharmony_ci
103bf215546Sopenharmony_civoid
104bf215546Sopenharmony_citu_suballoc_bo_free(struct tu_suballocator *suballoc, struct tu_suballoc_bo *bo)
105bf215546Sopenharmony_ci{
106bf215546Sopenharmony_ci   if (!bo->bo)
107bf215546Sopenharmony_ci      return;
108bf215546Sopenharmony_ci
109bf215546Sopenharmony_ci   /* If we we held the last reference to this BO, so just move it to the
110bf215546Sopenharmony_ci    * suballocator for the next time we need to allocate.
111bf215546Sopenharmony_ci    */
112bf215546Sopenharmony_ci   if (p_atomic_read(&bo->bo->refcnt) == 1 && !suballoc->cached_bo) {
113bf215546Sopenharmony_ci      suballoc->cached_bo = bo->bo;
114bf215546Sopenharmony_ci      return;
115bf215546Sopenharmony_ci   }
116bf215546Sopenharmony_ci
117bf215546Sopenharmony_ci   /* Otherwise, drop the refcount on it normally. */
118bf215546Sopenharmony_ci   tu_bo_finish(suballoc->dev, bo->bo);
119bf215546Sopenharmony_ci}
120bf215546Sopenharmony_ci
121bf215546Sopenharmony_civoid *
122bf215546Sopenharmony_citu_suballoc_bo_map(struct tu_suballoc_bo *bo)
123bf215546Sopenharmony_ci{
124bf215546Sopenharmony_ci   return bo->bo->map + (bo->iova - bo->bo->iova);
125bf215546Sopenharmony_ci}
126