1 // SPDX-License-Identifier: MIT 2 /* 3 * Copyright © 2021 Intel Corporation 4 */ 5 6 #include <linux/slab.h> 7 8 #include <drm/ttm/ttm_bo_driver.h> 9 #include <drm/ttm/ttm_placement.h> 10 11 #include "i915_ttm_buddy_manager.h" 12 13 #include "i915_buddy.h" 14 #include "i915_gem.h" 15 16 struct i915_ttm_buddy_manager { 17 struct ttm_resource_manager manager; 18 struct i915_buddy_mm mm; 19 struct list_head reserved; 20 struct mutex lock; 21 u64 default_page_size; 22 }; 23 24 static struct i915_ttm_buddy_manager * 25 to_buddy_manager(struct ttm_resource_manager *man) 26 { 27 return container_of(man, struct i915_ttm_buddy_manager, manager); 28 } 29 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) 34 { 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; 40 u64 min_page_size; 41 u64 size; 42 int err; 43 44 GEM_BUG_ON(place->fpfn || place->lpfn); 45 46 bman_res = kzalloc(sizeof(*bman_res), GFP_KERNEL); 47 if (!bman_res) 48 return -ENOMEM; 49 50 ttm_resource_init(bo, place, &bman_res->base); 51 INIT_LIST_HEAD(&bman_res->blocks); 52 bman_res->mm = mm; 53 54 GEM_BUG_ON(!bman_res->base.num_pages); 55 size = bman_res->base.num_pages << PAGE_SHIFT; 56 57 min_page_size = bman->default_page_size; 58 if (bo->page_alignment) 59 min_page_size = bo->page_alignment << PAGE_SHIFT; 60 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); 66 } 67 68 if (size > mm->size) { 69 err = -E2BIG; 70 goto err_free_res; 71 } 72 73 n_pages = size >> ilog2(mm->chunk_size); 74 75 do { 76 struct i915_buddy_block *block; 77 unsigned int order; 78 79 order = fls(n_pages) - 1; 80 GEM_BUG_ON(order > mm->max_order); 81 GEM_BUG_ON(order < min_order); 82 83 do { 84 mutex_lock(&bman->lock); 85 block = i915_buddy_alloc(mm, order); 86 mutex_unlock(&bman->lock); 87 if (!IS_ERR(block)) 88 break; 89 90 if (order-- == min_order) { 91 err = -ENOSPC; 92 goto err_free_blocks; 93 } 94 } while (1); 95 96 n_pages -= BIT(order); 97 98 list_add_tail(&block->link, &bman_res->blocks); 99 100 if (!n_pages) 101 break; 102 } while (1); 103 104 *res = &bman_res->base; 105 return 0; 106 107 err_free_blocks: 108 mutex_lock(&bman->lock); 109 i915_buddy_free_list(mm, &bman_res->blocks); 110 mutex_unlock(&bman->lock); 111 err_free_res: 112 kfree(bman_res); 113 return err; 114 } 115 116 static void i915_ttm_buddy_man_free(struct ttm_resource_manager *man, 117 struct ttm_resource *res) 118 { 119 struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res); 120 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man); 121 122 mutex_lock(&bman->lock); 123 i915_buddy_free_list(&bman->mm, &bman_res->blocks); 124 mutex_unlock(&bman->lock); 125 126 kfree(bman_res); 127 } 128 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, 132 }; 133 134 135 /** 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 145 * order-zero 146 * 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. 155 * 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. 159 * 160 * Return: 0 on success, negative error code on failure. 161 */ 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, 165 u64 chunk_size) 166 { 167 struct ttm_resource_manager *man; 168 struct i915_ttm_buddy_manager *bman; 169 int err; 170 171 bman = kzalloc(sizeof(*bman), GFP_KERNEL); 172 if (!bman) 173 return -ENOMEM; 174 175 err = i915_buddy_init(&bman->mm, size, chunk_size); 176 if (err) 177 goto err_free_bman; 178 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; 183 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); 188 189 ttm_resource_manager_set_used(man, true); 190 ttm_set_driver_manager(bdev, type, man); 191 192 return 0; 193 194 err_free_bman: 195 kfree(bman); 196 return err; 197 } 198 199 /** 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 203 * 204 * Note that if we reserved anything with &i915_ttm_buddy_man_reserve, this will 205 * also be freed for us here. 206 * 207 * Return: 0 on success, negative error code on failure. 208 */ 209 int i915_ttm_buddy_man_fini(struct ttm_device *bdev, unsigned int type) 210 { 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; 214 int ret; 215 216 ttm_resource_manager_set_used(man, false); 217 218 ret = ttm_resource_manager_evict_all(bdev, man); 219 if (ret) 220 return ret; 221 222 ttm_set_driver_manager(bdev, type, NULL); 223 224 mutex_lock(&bman->lock); 225 i915_buddy_free_list(mm, &bman->reserved); 226 i915_buddy_fini(mm); 227 mutex_unlock(&bman->lock); 228 229 ttm_resource_manager_cleanup(man); 230 kfree(bman); 231 232 return 0; 233 } 234 235 /** 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 240 * 241 * Note that the starting address for the region is always assumed to be zero. 242 * 243 * Return: 0 on success, negative error code on failure. 244 */ 245 int i915_ttm_buddy_man_reserve(struct ttm_resource_manager *man, 246 u64 start, u64 size) 247 { 248 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man); 249 struct i915_buddy_mm *mm = &bman->mm; 250 int ret; 251 252 mutex_lock(&bman->lock); 253 ret = i915_buddy_alloc_range(mm, &bman->reserved, start, size); 254 mutex_unlock(&bman->lock); 255 256 return ret; 257 } 258 259