1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright 2016 Advanced Micro Devices, Inc.
3bf215546Sopenharmony_ci * All Rights Reserved.
4bf215546Sopenharmony_ci *
5bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining
6bf215546Sopenharmony_ci * a copy of this software and associated documentation files (the
7bf215546Sopenharmony_ci * "Software"), to deal in the Software without restriction, including
8bf215546Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish,
9bf215546Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to
10bf215546Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to
11bf215546Sopenharmony_ci * the following conditions:
12bf215546Sopenharmony_ci *
13bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the
14bf215546Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
15bf215546Sopenharmony_ci * of the Software.
16bf215546Sopenharmony_ci *
17bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18bf215546Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19bf215546Sopenharmony_ci * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20bf215546Sopenharmony_ci * NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS, AUTHORS
21bf215546Sopenharmony_ci * AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23bf215546Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24bf215546Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE.
25bf215546Sopenharmony_ci *
26bf215546Sopenharmony_ci */
27bf215546Sopenharmony_ci
28bf215546Sopenharmony_ci#include "pb_slab.h"
29bf215546Sopenharmony_ci
30bf215546Sopenharmony_ci#include "util/u_math.h"
31bf215546Sopenharmony_ci#include "util/u_memory.h"
32bf215546Sopenharmony_ci
33bf215546Sopenharmony_ci/* All slab allocations from the same heap and with the same size belong
34bf215546Sopenharmony_ci * to the same group.
35bf215546Sopenharmony_ci */
36bf215546Sopenharmony_cistruct pb_slab_group
37bf215546Sopenharmony_ci{
38bf215546Sopenharmony_ci   /* Slabs with allocation candidates. Typically, slabs in this list should
39bf215546Sopenharmony_ci    * have some free entries.
40bf215546Sopenharmony_ci    *
41bf215546Sopenharmony_ci    * However, when the head becomes full we purposefully keep it around
42bf215546Sopenharmony_ci    * until the next allocation attempt, at which time we try a reclaim.
43bf215546Sopenharmony_ci    * The intention is to keep serving allocations from the same slab as long
44bf215546Sopenharmony_ci    * as possible for better locality.
45bf215546Sopenharmony_ci    *
46bf215546Sopenharmony_ci    * Due to a race in new slab allocation, additional slabs in this list
47bf215546Sopenharmony_ci    * can be fully allocated as well.
48bf215546Sopenharmony_ci    */
49bf215546Sopenharmony_ci   struct list_head slabs;
50bf215546Sopenharmony_ci};
51bf215546Sopenharmony_ci
52bf215546Sopenharmony_ci
53bf215546Sopenharmony_cistatic void
54bf215546Sopenharmony_cipb_slab_reclaim(struct pb_slabs *slabs, struct pb_slab_entry *entry)
55bf215546Sopenharmony_ci{
56bf215546Sopenharmony_ci   struct pb_slab *slab = entry->slab;
57bf215546Sopenharmony_ci
58bf215546Sopenharmony_ci   list_del(&entry->head); /* remove from reclaim list */
59bf215546Sopenharmony_ci   list_add(&entry->head, &slab->free);
60bf215546Sopenharmony_ci   slab->num_free++;
61bf215546Sopenharmony_ci
62bf215546Sopenharmony_ci   /* Add slab to the group's list if it isn't already linked. */
63bf215546Sopenharmony_ci   if (!list_is_linked(&slab->head)) {
64bf215546Sopenharmony_ci      struct pb_slab_group *group = &slabs->groups[entry->group_index];
65bf215546Sopenharmony_ci      list_addtail(&slab->head, &group->slabs);
66bf215546Sopenharmony_ci   }
67bf215546Sopenharmony_ci
68bf215546Sopenharmony_ci   if (slab->num_free >= slab->num_entries) {
69bf215546Sopenharmony_ci      list_del(&slab->head);
70bf215546Sopenharmony_ci      slabs->slab_free(slabs->priv, slab);
71bf215546Sopenharmony_ci   }
72bf215546Sopenharmony_ci}
73bf215546Sopenharmony_ci
74bf215546Sopenharmony_ci#define MAX_FAILED_RECLAIMS 2
75bf215546Sopenharmony_ci
76bf215546Sopenharmony_cistatic void
77bf215546Sopenharmony_cipb_slabs_reclaim_locked(struct pb_slabs *slabs)
78bf215546Sopenharmony_ci{
79bf215546Sopenharmony_ci   struct pb_slab_entry *entry, *next;
80bf215546Sopenharmony_ci   unsigned num_failed_reclaims = 0;
81bf215546Sopenharmony_ci   LIST_FOR_EACH_ENTRY_SAFE(entry, next, &slabs->reclaim, head) {
82bf215546Sopenharmony_ci      if (slabs->can_reclaim(slabs->priv, entry)) {
83bf215546Sopenharmony_ci         pb_slab_reclaim(slabs, entry);
84bf215546Sopenharmony_ci      /* there are typically three possible scenarios when reclaiming:
85bf215546Sopenharmony_ci       * - all entries reclaimed
86bf215546Sopenharmony_ci       * - no entries reclaimed
87bf215546Sopenharmony_ci       * - all but one entry reclaimed
88bf215546Sopenharmony_ci       * in the scenario where a slab contains many (10+) unused entries,
89bf215546Sopenharmony_ci       * the driver should not walk the entire list, as this is likely to
90bf215546Sopenharmony_ci       * result in zero reclaims if the first few entries fail to reclaim
91bf215546Sopenharmony_ci       */
92bf215546Sopenharmony_ci      } else if (++num_failed_reclaims >= MAX_FAILED_RECLAIMS) {
93bf215546Sopenharmony_ci         break;
94bf215546Sopenharmony_ci      }
95bf215546Sopenharmony_ci   }
96bf215546Sopenharmony_ci}
97bf215546Sopenharmony_ci
98bf215546Sopenharmony_cistatic void
99bf215546Sopenharmony_cipb_slabs_reclaim_all_locked(struct pb_slabs *slabs)
100bf215546Sopenharmony_ci{
101bf215546Sopenharmony_ci   struct pb_slab_entry *entry, *next;
102bf215546Sopenharmony_ci   LIST_FOR_EACH_ENTRY_SAFE(entry, next, &slabs->reclaim, head) {
103bf215546Sopenharmony_ci      if (slabs->can_reclaim(slabs->priv, entry)) {
104bf215546Sopenharmony_ci         pb_slab_reclaim(slabs, entry);
105bf215546Sopenharmony_ci      }
106bf215546Sopenharmony_ci   }
107bf215546Sopenharmony_ci}
108bf215546Sopenharmony_ci
109bf215546Sopenharmony_ci/* Allocate a slab entry of the given size from the given heap.
110bf215546Sopenharmony_ci *
111bf215546Sopenharmony_ci * This will try to re-use entries that have previously been freed. However,
112bf215546Sopenharmony_ci * if no entries are free (or all free entries are still "in flight" as
113bf215546Sopenharmony_ci * determined by the can_reclaim fallback function), a new slab will be
114bf215546Sopenharmony_ci * requested via the slab_alloc callback.
115bf215546Sopenharmony_ci *
116bf215546Sopenharmony_ci * Note that slab_free can also be called by this function.
117bf215546Sopenharmony_ci */
118bf215546Sopenharmony_cistruct pb_slab_entry *
119bf215546Sopenharmony_cipb_slab_alloc_reclaimed(struct pb_slabs *slabs, unsigned size, unsigned heap, bool reclaim_all)
120bf215546Sopenharmony_ci{
121bf215546Sopenharmony_ci   unsigned order = MAX2(slabs->min_order, util_logbase2_ceil(size));
122bf215546Sopenharmony_ci   unsigned group_index;
123bf215546Sopenharmony_ci   struct pb_slab_group *group;
124bf215546Sopenharmony_ci   struct pb_slab *slab;
125bf215546Sopenharmony_ci   struct pb_slab_entry *entry;
126bf215546Sopenharmony_ci   unsigned entry_size = 1 << order;
127bf215546Sopenharmony_ci   bool three_fourths = false;
128bf215546Sopenharmony_ci
129bf215546Sopenharmony_ci   /* If the size is <= 3/4 of the entry size, use a slab with entries using
130bf215546Sopenharmony_ci    * 3/4 sizes to reduce overallocation.
131bf215546Sopenharmony_ci    */
132bf215546Sopenharmony_ci   if (slabs->allow_three_fourths_allocations && size <= entry_size * 3 / 4) {
133bf215546Sopenharmony_ci      entry_size = entry_size * 3 / 4;
134bf215546Sopenharmony_ci      three_fourths = true;
135bf215546Sopenharmony_ci   }
136bf215546Sopenharmony_ci
137bf215546Sopenharmony_ci   assert(order < slabs->min_order + slabs->num_orders);
138bf215546Sopenharmony_ci   assert(heap < slabs->num_heaps);
139bf215546Sopenharmony_ci
140bf215546Sopenharmony_ci   group_index = (heap * slabs->num_orders + (order - slabs->min_order)) *
141bf215546Sopenharmony_ci                 (1 + slabs->allow_three_fourths_allocations) + three_fourths;
142bf215546Sopenharmony_ci   group = &slabs->groups[group_index];
143bf215546Sopenharmony_ci
144bf215546Sopenharmony_ci   simple_mtx_lock(&slabs->mutex);
145bf215546Sopenharmony_ci
146bf215546Sopenharmony_ci   /* If there is no candidate slab at all, or the first slab has no free
147bf215546Sopenharmony_ci    * entries, try reclaiming entries.
148bf215546Sopenharmony_ci    */
149bf215546Sopenharmony_ci   if (list_is_empty(&group->slabs) ||
150bf215546Sopenharmony_ci       list_is_empty(&list_entry(group->slabs.next, struct pb_slab, head)->free)) {
151bf215546Sopenharmony_ci      if (reclaim_all)
152bf215546Sopenharmony_ci         pb_slabs_reclaim_all_locked(slabs);
153bf215546Sopenharmony_ci      else
154bf215546Sopenharmony_ci         pb_slabs_reclaim_locked(slabs);
155bf215546Sopenharmony_ci   }
156bf215546Sopenharmony_ci
157bf215546Sopenharmony_ci   /* Remove slabs without free entries. */
158bf215546Sopenharmony_ci   while (!list_is_empty(&group->slabs)) {
159bf215546Sopenharmony_ci      slab = list_entry(group->slabs.next, struct pb_slab, head);
160bf215546Sopenharmony_ci      if (!list_is_empty(&slab->free))
161bf215546Sopenharmony_ci         break;
162bf215546Sopenharmony_ci
163bf215546Sopenharmony_ci      list_del(&slab->head);
164bf215546Sopenharmony_ci   }
165bf215546Sopenharmony_ci
166bf215546Sopenharmony_ci   if (list_is_empty(&group->slabs)) {
167bf215546Sopenharmony_ci      /* Drop the mutex temporarily to prevent a deadlock where the allocation
168bf215546Sopenharmony_ci       * calls back into slab functions (most likely to happen for
169bf215546Sopenharmony_ci       * pb_slab_reclaim if memory is low).
170bf215546Sopenharmony_ci       *
171bf215546Sopenharmony_ci       * There's a chance that racing threads will end up allocating multiple
172bf215546Sopenharmony_ci       * slabs for the same group, but that doesn't hurt correctness.
173bf215546Sopenharmony_ci       */
174bf215546Sopenharmony_ci      simple_mtx_unlock(&slabs->mutex);
175bf215546Sopenharmony_ci      slab = slabs->slab_alloc(slabs->priv, heap, entry_size, group_index);
176bf215546Sopenharmony_ci      if (!slab)
177bf215546Sopenharmony_ci         return NULL;
178bf215546Sopenharmony_ci      simple_mtx_lock(&slabs->mutex);
179bf215546Sopenharmony_ci
180bf215546Sopenharmony_ci      list_add(&slab->head, &group->slabs);
181bf215546Sopenharmony_ci   }
182bf215546Sopenharmony_ci
183bf215546Sopenharmony_ci   entry = list_entry(slab->free.next, struct pb_slab_entry, head);
184bf215546Sopenharmony_ci   list_del(&entry->head);
185bf215546Sopenharmony_ci   slab->num_free--;
186bf215546Sopenharmony_ci
187bf215546Sopenharmony_ci   simple_mtx_unlock(&slabs->mutex);
188bf215546Sopenharmony_ci
189bf215546Sopenharmony_ci   return entry;
190bf215546Sopenharmony_ci}
191bf215546Sopenharmony_ci
192bf215546Sopenharmony_cistruct pb_slab_entry *
193bf215546Sopenharmony_cipb_slab_alloc(struct pb_slabs *slabs, unsigned size, unsigned heap)
194bf215546Sopenharmony_ci{
195bf215546Sopenharmony_ci   return pb_slab_alloc_reclaimed(slabs, size, heap, false);
196bf215546Sopenharmony_ci}
197bf215546Sopenharmony_ci
198bf215546Sopenharmony_ci/* Free the given slab entry.
199bf215546Sopenharmony_ci *
200bf215546Sopenharmony_ci * The entry may still be in use e.g. by in-flight command submissions. The
201bf215546Sopenharmony_ci * can_reclaim callback function will be called to determine whether the entry
202bf215546Sopenharmony_ci * can be handed out again by pb_slab_alloc.
203bf215546Sopenharmony_ci */
204bf215546Sopenharmony_civoid
205bf215546Sopenharmony_cipb_slab_free(struct pb_slabs* slabs, struct pb_slab_entry *entry)
206bf215546Sopenharmony_ci{
207bf215546Sopenharmony_ci   simple_mtx_lock(&slabs->mutex);
208bf215546Sopenharmony_ci   list_addtail(&entry->head, &slabs->reclaim);
209bf215546Sopenharmony_ci   simple_mtx_unlock(&slabs->mutex);
210bf215546Sopenharmony_ci}
211bf215546Sopenharmony_ci
212bf215546Sopenharmony_ci/* Check if any of the entries handed to pb_slab_free are ready to be re-used.
213bf215546Sopenharmony_ci *
214bf215546Sopenharmony_ci * This may end up freeing some slabs and is therefore useful to try to reclaim
215bf215546Sopenharmony_ci * some no longer used memory. However, calling this function is not strictly
216bf215546Sopenharmony_ci * required since pb_slab_alloc will eventually do the same thing.
217bf215546Sopenharmony_ci */
218bf215546Sopenharmony_civoid
219bf215546Sopenharmony_cipb_slabs_reclaim(struct pb_slabs *slabs)
220bf215546Sopenharmony_ci{
221bf215546Sopenharmony_ci   simple_mtx_lock(&slabs->mutex);
222bf215546Sopenharmony_ci   pb_slabs_reclaim_locked(slabs);
223bf215546Sopenharmony_ci   simple_mtx_unlock(&slabs->mutex);
224bf215546Sopenharmony_ci}
225bf215546Sopenharmony_ci
226bf215546Sopenharmony_ci/* Initialize the slabs manager.
227bf215546Sopenharmony_ci *
228bf215546Sopenharmony_ci * The minimum and maximum size of slab entries are 2^min_order and
229bf215546Sopenharmony_ci * 2^max_order, respectively.
230bf215546Sopenharmony_ci *
231bf215546Sopenharmony_ci * priv will be passed to the given callback functions.
232bf215546Sopenharmony_ci */
233bf215546Sopenharmony_cibool
234bf215546Sopenharmony_cipb_slabs_init(struct pb_slabs *slabs,
235bf215546Sopenharmony_ci              unsigned min_order, unsigned max_order,
236bf215546Sopenharmony_ci              unsigned num_heaps, bool allow_three_fourth_allocations,
237bf215546Sopenharmony_ci              void *priv,
238bf215546Sopenharmony_ci              slab_can_reclaim_fn *can_reclaim,
239bf215546Sopenharmony_ci              slab_alloc_fn *slab_alloc,
240bf215546Sopenharmony_ci              slab_free_fn *slab_free)
241bf215546Sopenharmony_ci{
242bf215546Sopenharmony_ci   unsigned num_groups;
243bf215546Sopenharmony_ci   unsigned i;
244bf215546Sopenharmony_ci
245bf215546Sopenharmony_ci   assert(min_order <= max_order);
246bf215546Sopenharmony_ci   assert(max_order < sizeof(unsigned) * 8 - 1);
247bf215546Sopenharmony_ci
248bf215546Sopenharmony_ci   slabs->min_order = min_order;
249bf215546Sopenharmony_ci   slabs->num_orders = max_order - min_order + 1;
250bf215546Sopenharmony_ci   slabs->num_heaps = num_heaps;
251bf215546Sopenharmony_ci   slabs->allow_three_fourths_allocations = allow_three_fourth_allocations;
252bf215546Sopenharmony_ci
253bf215546Sopenharmony_ci   slabs->priv = priv;
254bf215546Sopenharmony_ci   slabs->can_reclaim = can_reclaim;
255bf215546Sopenharmony_ci   slabs->slab_alloc = slab_alloc;
256bf215546Sopenharmony_ci   slabs->slab_free = slab_free;
257bf215546Sopenharmony_ci
258bf215546Sopenharmony_ci   list_inithead(&slabs->reclaim);
259bf215546Sopenharmony_ci
260bf215546Sopenharmony_ci   num_groups = slabs->num_orders * slabs->num_heaps *
261bf215546Sopenharmony_ci                (1 + allow_three_fourth_allocations);
262bf215546Sopenharmony_ci   slabs->groups = CALLOC(num_groups, sizeof(*slabs->groups));
263bf215546Sopenharmony_ci   if (!slabs->groups)
264bf215546Sopenharmony_ci      return false;
265bf215546Sopenharmony_ci
266bf215546Sopenharmony_ci   for (i = 0; i < num_groups; ++i) {
267bf215546Sopenharmony_ci      struct pb_slab_group *group = &slabs->groups[i];
268bf215546Sopenharmony_ci      list_inithead(&group->slabs);
269bf215546Sopenharmony_ci   }
270bf215546Sopenharmony_ci
271bf215546Sopenharmony_ci   (void) simple_mtx_init(&slabs->mutex, mtx_plain);
272bf215546Sopenharmony_ci
273bf215546Sopenharmony_ci   return true;
274bf215546Sopenharmony_ci}
275bf215546Sopenharmony_ci
276bf215546Sopenharmony_ci/* Shutdown the slab manager.
277bf215546Sopenharmony_ci *
278bf215546Sopenharmony_ci * This will free all allocated slabs and internal structures, even if some
279bf215546Sopenharmony_ci * of the slab entries are still in flight (i.e. if can_reclaim would return
280bf215546Sopenharmony_ci * false).
281bf215546Sopenharmony_ci */
282bf215546Sopenharmony_civoid
283bf215546Sopenharmony_cipb_slabs_deinit(struct pb_slabs *slabs)
284bf215546Sopenharmony_ci{
285bf215546Sopenharmony_ci   /* Reclaim all slab entries (even those that are still in flight). This
286bf215546Sopenharmony_ci    * implicitly calls slab_free for everything.
287bf215546Sopenharmony_ci    */
288bf215546Sopenharmony_ci   while (!list_is_empty(&slabs->reclaim)) {
289bf215546Sopenharmony_ci      struct pb_slab_entry *entry =
290bf215546Sopenharmony_ci         list_entry(slabs->reclaim.next, struct pb_slab_entry, head);
291bf215546Sopenharmony_ci      pb_slab_reclaim(slabs, entry);
292bf215546Sopenharmony_ci   }
293bf215546Sopenharmony_ci
294bf215546Sopenharmony_ci   FREE(slabs->groups);
295bf215546Sopenharmony_ci   simple_mtx_destroy(&slabs->mutex);
296bf215546Sopenharmony_ci}
297