0001
0002
0003
0004
0005
0006 #include <linux/slab.h>
0007
0008 #include <drm/ttm/ttm_bo_driver.h>
0009 #include <drm/ttm/ttm_placement.h>
0010
0011 #include <drm/drm_buddy.h>
0012
0013 #include "i915_ttm_buddy_manager.h"
0014
0015 #include "i915_gem.h"
0016
0017 struct i915_ttm_buddy_manager {
0018 struct ttm_resource_manager manager;
0019 struct drm_buddy mm;
0020 struct list_head reserved;
0021 struct mutex lock;
0022 unsigned long visible_size;
0023 unsigned long visible_avail;
0024 unsigned long visible_reserved;
0025 u64 default_page_size;
0026 };
0027
0028 static struct i915_ttm_buddy_manager *
0029 to_buddy_manager(struct ttm_resource_manager *man)
0030 {
0031 return container_of(man, struct i915_ttm_buddy_manager, manager);
0032 }
0033
0034 static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
0035 struct ttm_buffer_object *bo,
0036 const struct ttm_place *place,
0037 struct ttm_resource **res)
0038 {
0039 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
0040 struct i915_ttm_buddy_resource *bman_res;
0041 struct drm_buddy *mm = &bman->mm;
0042 unsigned long n_pages, lpfn;
0043 u64 min_page_size;
0044 u64 size;
0045 int err;
0046
0047 lpfn = place->lpfn;
0048 if (!lpfn)
0049 lpfn = man->size;
0050
0051 bman_res = kzalloc(sizeof(*bman_res), GFP_KERNEL);
0052 if (!bman_res)
0053 return -ENOMEM;
0054
0055 ttm_resource_init(bo, place, &bman_res->base);
0056 INIT_LIST_HEAD(&bman_res->blocks);
0057 bman_res->mm = mm;
0058
0059 if (place->flags & TTM_PL_FLAG_TOPDOWN)
0060 bman_res->flags |= DRM_BUDDY_TOPDOWN_ALLOCATION;
0061
0062 if (place->fpfn || lpfn != man->size)
0063 bman_res->flags |= DRM_BUDDY_RANGE_ALLOCATION;
0064
0065 GEM_BUG_ON(!bman_res->base.num_pages);
0066 size = bman_res->base.num_pages << PAGE_SHIFT;
0067
0068 min_page_size = bman->default_page_size;
0069 if (bo->page_alignment)
0070 min_page_size = bo->page_alignment << PAGE_SHIFT;
0071
0072 GEM_BUG_ON(min_page_size < mm->chunk_size);
0073 GEM_BUG_ON(!IS_ALIGNED(size, min_page_size));
0074
0075 if (place->fpfn + bman_res->base.num_pages != place->lpfn &&
0076 place->flags & TTM_PL_FLAG_CONTIGUOUS) {
0077 unsigned long pages;
0078
0079 size = roundup_pow_of_two(size);
0080 min_page_size = size;
0081
0082 pages = size >> ilog2(mm->chunk_size);
0083 if (pages > lpfn)
0084 lpfn = pages;
0085 }
0086
0087 if (size > lpfn << PAGE_SHIFT) {
0088 err = -E2BIG;
0089 goto err_free_res;
0090 }
0091
0092 n_pages = size >> ilog2(mm->chunk_size);
0093
0094 mutex_lock(&bman->lock);
0095 if (lpfn <= bman->visible_size && n_pages > bman->visible_avail) {
0096 mutex_unlock(&bman->lock);
0097 err = -ENOSPC;
0098 goto err_free_res;
0099 }
0100
0101 err = drm_buddy_alloc_blocks(mm, (u64)place->fpfn << PAGE_SHIFT,
0102 (u64)lpfn << PAGE_SHIFT,
0103 (u64)n_pages << PAGE_SHIFT,
0104 min_page_size,
0105 &bman_res->blocks,
0106 bman_res->flags);
0107 if (unlikely(err))
0108 goto err_free_blocks;
0109
0110 if (place->flags & TTM_PL_FLAG_CONTIGUOUS) {
0111 u64 original_size = (u64)bman_res->base.num_pages << PAGE_SHIFT;
0112
0113 drm_buddy_block_trim(mm,
0114 original_size,
0115 &bman_res->blocks);
0116 }
0117
0118 if (lpfn <= bman->visible_size) {
0119 bman_res->used_visible_size = bman_res->base.num_pages;
0120 } else {
0121 struct drm_buddy_block *block;
0122
0123 list_for_each_entry(block, &bman_res->blocks, link) {
0124 unsigned long start =
0125 drm_buddy_block_offset(block) >> PAGE_SHIFT;
0126
0127 if (start < bman->visible_size) {
0128 unsigned long end = start +
0129 (drm_buddy_block_size(mm, block) >> PAGE_SHIFT);
0130
0131 bman_res->used_visible_size +=
0132 min(end, bman->visible_size) - start;
0133 }
0134 }
0135 }
0136
0137 if (bman_res->used_visible_size)
0138 bman->visible_avail -= bman_res->used_visible_size;
0139
0140 mutex_unlock(&bman->lock);
0141
0142 if (place->lpfn - place->fpfn == n_pages)
0143 bman_res->base.start = place->fpfn;
0144 else if (lpfn <= bman->visible_size)
0145 bman_res->base.start = 0;
0146 else
0147 bman_res->base.start = bman->visible_size;
0148
0149 *res = &bman_res->base;
0150 return 0;
0151
0152 err_free_blocks:
0153 drm_buddy_free_list(mm, &bman_res->blocks);
0154 mutex_unlock(&bman->lock);
0155 err_free_res:
0156 ttm_resource_fini(man, &bman_res->base);
0157 kfree(bman_res);
0158 return err;
0159 }
0160
0161 static void i915_ttm_buddy_man_free(struct ttm_resource_manager *man,
0162 struct ttm_resource *res)
0163 {
0164 struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res);
0165 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
0166
0167 mutex_lock(&bman->lock);
0168 drm_buddy_free_list(&bman->mm, &bman_res->blocks);
0169 bman->visible_avail += bman_res->used_visible_size;
0170 mutex_unlock(&bman->lock);
0171
0172 ttm_resource_fini(man, res);
0173 kfree(bman_res);
0174 }
0175
0176 static void i915_ttm_buddy_man_debug(struct ttm_resource_manager *man,
0177 struct drm_printer *printer)
0178 {
0179 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
0180 struct drm_buddy_block *block;
0181
0182 mutex_lock(&bman->lock);
0183 drm_printf(printer, "default_page_size: %lluKiB\n",
0184 bman->default_page_size >> 10);
0185 drm_printf(printer, "visible_avail: %lluMiB\n",
0186 (u64)bman->visible_avail << PAGE_SHIFT >> 20);
0187 drm_printf(printer, "visible_size: %lluMiB\n",
0188 (u64)bman->visible_size << PAGE_SHIFT >> 20);
0189 drm_printf(printer, "visible_reserved: %lluMiB\n",
0190 (u64)bman->visible_reserved << PAGE_SHIFT >> 20);
0191
0192 drm_buddy_print(&bman->mm, printer);
0193
0194 drm_printf(printer, "reserved:\n");
0195 list_for_each_entry(block, &bman->reserved, link)
0196 drm_buddy_block_print(&bman->mm, block, printer);
0197 mutex_unlock(&bman->lock);
0198 }
0199
0200 static const struct ttm_resource_manager_func i915_ttm_buddy_manager_func = {
0201 .alloc = i915_ttm_buddy_man_alloc,
0202 .free = i915_ttm_buddy_man_free,
0203 .debug = i915_ttm_buddy_man_debug,
0204 };
0205
0206
0207
0208
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219
0220
0221
0222
0223
0224
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234 int i915_ttm_buddy_man_init(struct ttm_device *bdev,
0235 unsigned int type, bool use_tt,
0236 u64 size, u64 visible_size, u64 default_page_size,
0237 u64 chunk_size)
0238 {
0239 struct ttm_resource_manager *man;
0240 struct i915_ttm_buddy_manager *bman;
0241 int err;
0242
0243 bman = kzalloc(sizeof(*bman), GFP_KERNEL);
0244 if (!bman)
0245 return -ENOMEM;
0246
0247 err = drm_buddy_init(&bman->mm, size, chunk_size);
0248 if (err)
0249 goto err_free_bman;
0250
0251 mutex_init(&bman->lock);
0252 INIT_LIST_HEAD(&bman->reserved);
0253 GEM_BUG_ON(default_page_size < chunk_size);
0254 bman->default_page_size = default_page_size;
0255 bman->visible_size = visible_size >> PAGE_SHIFT;
0256 bman->visible_avail = bman->visible_size;
0257
0258 man = &bman->manager;
0259 man->use_tt = use_tt;
0260 man->func = &i915_ttm_buddy_manager_func;
0261 ttm_resource_manager_init(man, bdev, bman->mm.size >> PAGE_SHIFT);
0262
0263 ttm_resource_manager_set_used(man, true);
0264 ttm_set_driver_manager(bdev, type, man);
0265
0266 return 0;
0267
0268 err_free_bman:
0269 kfree(bman);
0270 return err;
0271 }
0272
0273
0274
0275
0276
0277
0278
0279
0280
0281
0282
0283 int i915_ttm_buddy_man_fini(struct ttm_device *bdev, unsigned int type)
0284 {
0285 struct ttm_resource_manager *man = ttm_manager_type(bdev, type);
0286 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
0287 struct drm_buddy *mm = &bman->mm;
0288 int ret;
0289
0290 ttm_resource_manager_set_used(man, false);
0291
0292 ret = ttm_resource_manager_evict_all(bdev, man);
0293 if (ret)
0294 return ret;
0295
0296 ttm_set_driver_manager(bdev, type, NULL);
0297
0298 mutex_lock(&bman->lock);
0299 drm_buddy_free_list(mm, &bman->reserved);
0300 drm_buddy_fini(mm);
0301 bman->visible_avail += bman->visible_reserved;
0302 WARN_ON_ONCE(bman->visible_avail != bman->visible_size);
0303 mutex_unlock(&bman->lock);
0304
0305 ttm_resource_manager_cleanup(man);
0306 kfree(bman);
0307
0308 return 0;
0309 }
0310
0311
0312
0313
0314
0315
0316
0317
0318
0319
0320
0321 int i915_ttm_buddy_man_reserve(struct ttm_resource_manager *man,
0322 u64 start, u64 size)
0323 {
0324 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
0325 struct drm_buddy *mm = &bman->mm;
0326 unsigned long fpfn = start >> PAGE_SHIFT;
0327 unsigned long flags = 0;
0328 int ret;
0329
0330 flags |= DRM_BUDDY_RANGE_ALLOCATION;
0331
0332 mutex_lock(&bman->lock);
0333 ret = drm_buddy_alloc_blocks(mm, start,
0334 start + size,
0335 size, mm->chunk_size,
0336 &bman->reserved,
0337 flags);
0338
0339 if (fpfn < bman->visible_size) {
0340 unsigned long lpfn = fpfn + (size >> PAGE_SHIFT);
0341 unsigned long visible = min(lpfn, bman->visible_size) - fpfn;
0342
0343 bman->visible_reserved += visible;
0344 bman->visible_avail -= visible;
0345 }
0346 mutex_unlock(&bman->lock);
0347
0348 return ret;
0349 }
0350
0351
0352
0353
0354
0355
0356 u64 i915_ttm_buddy_man_visible_size(struct ttm_resource_manager *man)
0357 {
0358 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
0359
0360 return bman->visible_size;
0361 }
0362
0363
0364
0365
0366
0367
0368
0369
0370
0371
0372 void i915_ttm_buddy_man_avail(struct ttm_resource_manager *man,
0373 u64 *avail, u64 *visible_avail)
0374 {
0375 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
0376
0377 mutex_lock(&bman->lock);
0378 *avail = bman->mm.avail >> PAGE_SHIFT;
0379 *visible_avail = bman->visible_avail;
0380 mutex_unlock(&bman->lock);
0381 }
0382
0383 #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
0384 void i915_ttm_buddy_man_force_visible_size(struct ttm_resource_manager *man,
0385 u64 size)
0386 {
0387 struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
0388
0389 bman->visible_size = size;
0390 }
0391 #endif