1 // SPDX-License-Identifier: MIT
3 * Copyright © 2021 Intel Corporation
6 #include <linux/slab.h>
8 #include <drm/ttm/ttm_bo_driver.h>
9 #include <drm/ttm/ttm_placement.h>
11 #include "i915_ttm_buddy_manager.h"
13 #include "i915_buddy.h"
16 struct i915_ttm_buddy_manager {
17 struct ttm_resource_manager manager;
18 struct i915_buddy_mm mm;
19 struct list_head reserved;
21 u64 default_page_size;
24 static struct i915_ttm_buddy_manager *
25 to_buddy_manager(struct ttm_resource_manager *man)
27 return container_of(man, struct i915_ttm_buddy_manager, manager);
30 static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
31 struct ttm_buffer_object *bo,
32 const struct ttm_place *place,
33 struct ttm_resource **res)
35 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
36 struct i915_ttm_buddy_resource *bman_res;
37 struct i915_buddy_mm *mm = &bman->mm;
38 unsigned long n_pages;
39 unsigned int min_order;
44 GEM_BUG_ON(place->fpfn || place->lpfn);
46 bman_res = kzalloc(sizeof(*bman_res), GFP_KERNEL);
50 ttm_resource_init(bo, place, &bman_res->base);
51 INIT_LIST_HEAD(&bman_res->blocks);
54 GEM_BUG_ON(!bman_res->base.num_pages);
55 size = bman_res->base.num_pages << PAGE_SHIFT;
57 min_page_size = bman->default_page_size;
58 if (bo->page_alignment)
59 min_page_size = bo->page_alignment << PAGE_SHIFT;
61 GEM_BUG_ON(min_page_size < mm->chunk_size);
62 min_order = ilog2(min_page_size) - ilog2(mm->chunk_size);
63 if (place->flags & TTM_PL_FLAG_CONTIGUOUS) {
64 size = roundup_pow_of_two(size);
65 min_order = ilog2(size) - ilog2(mm->chunk_size);
68 if (size > mm->size) {
73 n_pages = size >> ilog2(mm->chunk_size);
76 struct i915_buddy_block *block;
79 order = fls(n_pages) - 1;
80 GEM_BUG_ON(order > mm->max_order);
81 GEM_BUG_ON(order < min_order);
84 mutex_lock(&bman->lock);
85 block = i915_buddy_alloc(mm, order);
86 mutex_unlock(&bman->lock);
90 if (order-- == min_order) {
96 n_pages -= BIT(order);
98 list_add_tail(&block->link, &bman_res->blocks);
104 *res = &bman_res->base;
108 mutex_lock(&bman->lock);
109 i915_buddy_free_list(mm, &bman_res->blocks);
110 mutex_unlock(&bman->lock);
116 static void i915_ttm_buddy_man_free(struct ttm_resource_manager *man,
117 struct ttm_resource *res)
119 struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res);
120 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
122 mutex_lock(&bman->lock);
123 i915_buddy_free_list(&bman->mm, &bman_res->blocks);
124 mutex_unlock(&bman->lock);
129 static const struct ttm_resource_manager_func i915_ttm_buddy_manager_func = {
130 .alloc = i915_ttm_buddy_man_alloc,
131 .free = i915_ttm_buddy_man_free,
136 * i915_ttm_buddy_man_init - Setup buddy allocator based ttm manager
137 * @bdev: The ttm device
138 * @type: Memory type we want to manage
139 * @use_tt: Set use_tt for the manager
140 * @size: The size in bytes to manage
141 * @default_page_size: The default minimum page size in bytes for allocations,
142 * this must be at least as large as @chunk_size, and can be overridden by
143 * setting the BO page_alignment, to be larger or smaller as needed.
144 * @chunk_size: The minimum page size in bytes for our allocations i.e
147 * Note that the starting address is assumed to be zero here, since this
148 * simplifies keeping the property where allocated blocks having natural
149 * power-of-two alignment. So long as the real starting address is some large
150 * power-of-two, or naturally start from zero, then this should be fine. Also
151 * the &i915_ttm_buddy_man_reserve interface can be used to preserve alignment
152 * if say there is some unusable range from the start of the region. We can
153 * revisit this in the future and make the interface accept an actual starting
154 * offset and let it take care of the rest.
156 * Note that if the @size is not aligned to the @chunk_size then we perform the
157 * required rounding to get the usable size. The final size in pages can be
158 * taken from &ttm_resource_manager.size.
160 * Return: 0 on success, negative error code on failure.
162 int i915_ttm_buddy_man_init(struct ttm_device *bdev,
163 unsigned int type, bool use_tt,
164 u64 size, u64 default_page_size,
167 struct ttm_resource_manager *man;
168 struct i915_ttm_buddy_manager *bman;
171 bman = kzalloc(sizeof(*bman), GFP_KERNEL);
175 err = i915_buddy_init(&bman->mm, size, chunk_size);
179 mutex_init(&bman->lock);
180 INIT_LIST_HEAD(&bman->reserved);
181 GEM_BUG_ON(default_page_size < chunk_size);
182 bman->default_page_size = default_page_size;
184 man = &bman->manager;
185 man->use_tt = use_tt;
186 man->func = &i915_ttm_buddy_manager_func;
187 ttm_resource_manager_init(man, bman->mm.size >> PAGE_SHIFT);
189 ttm_resource_manager_set_used(man, true);
190 ttm_set_driver_manager(bdev, type, man);
200 * i915_ttm_buddy_man_fini - Destroy the buddy allocator ttm manager
201 * @bdev: The ttm device
202 * @type: Memory type we want to manage
204 * Note that if we reserved anything with &i915_ttm_buddy_man_reserve, this will
205 * also be freed for us here.
207 * Return: 0 on success, negative error code on failure.
209 int i915_ttm_buddy_man_fini(struct ttm_device *bdev, unsigned int type)
211 struct ttm_resource_manager *man = ttm_manager_type(bdev, type);
212 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
213 struct i915_buddy_mm *mm = &bman->mm;
216 ttm_resource_manager_set_used(man, false);
218 ret = ttm_resource_manager_evict_all(bdev, man);
222 ttm_set_driver_manager(bdev, type, NULL);
224 mutex_lock(&bman->lock);
225 i915_buddy_free_list(mm, &bman->reserved);
227 mutex_unlock(&bman->lock);
229 ttm_resource_manager_cleanup(man);
236 * i915_ttm_buddy_man_reserve - Reserve address range
237 * @man: The buddy allocator ttm manager
238 * @start: The offset in bytes, where the region start is assumed to be zero
239 * @size: The size in bytes
241 * Note that the starting address for the region is always assumed to be zero.
243 * Return: 0 on success, negative error code on failure.
245 int i915_ttm_buddy_man_reserve(struct ttm_resource_manager *man,
248 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
249 struct i915_buddy_mm *mm = &bman->mm;
252 mutex_lock(&bman->lock);
253 ret = i915_buddy_alloc_range(mm, &bman->reserved, start, size);
254 mutex_unlock(&bman->lock);