i915_ttm_buddy_manager.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. // SPDX-License-Identifier: MIT
  2. /*
  3. * Copyright © 2021 Intel Corporation
  4. */
  5. #include <linux/slab.h>
  6. #include <drm/drm_buddy.h>
  7. #include <drm/drm_print.h>
  8. #include <drm/ttm/ttm_placement.h>
  9. #include <drm/ttm/ttm_bo.h>
  10. #include "i915_ttm_buddy_manager.h"
  11. #include "i915_gem.h"
  12. struct i915_ttm_buddy_manager {
  13. struct ttm_resource_manager manager;
  14. struct drm_buddy mm;
  15. struct list_head reserved;
  16. struct mutex lock;
  17. unsigned long visible_size;
  18. unsigned long visible_avail;
  19. unsigned long visible_reserved;
  20. u64 default_page_size;
  21. };
  22. static struct i915_ttm_buddy_manager *
  23. to_buddy_manager(struct ttm_resource_manager *man)
  24. {
  25. return container_of(man, struct i915_ttm_buddy_manager, manager);
  26. }
  27. static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man,
  28. struct ttm_buffer_object *bo,
  29. const struct ttm_place *place,
  30. struct ttm_resource **res)
  31. {
  32. struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
  33. struct i915_ttm_buddy_resource *bman_res;
  34. struct drm_buddy *mm = &bman->mm;
  35. unsigned long n_pages, lpfn;
  36. u64 min_page_size;
  37. u64 size;
  38. int err;
  39. lpfn = place->lpfn;
  40. if (!lpfn)
  41. lpfn = man->size;
  42. bman_res = kzalloc_obj(*bman_res);
  43. if (!bman_res)
  44. return -ENOMEM;
  45. ttm_resource_init(bo, place, &bman_res->base);
  46. INIT_LIST_HEAD(&bman_res->blocks);
  47. bman_res->mm = mm;
  48. if (place->flags & TTM_PL_FLAG_TOPDOWN)
  49. bman_res->flags |= DRM_BUDDY_TOPDOWN_ALLOCATION;
  50. if (place->flags & TTM_PL_FLAG_CONTIGUOUS)
  51. bman_res->flags |= DRM_BUDDY_CONTIGUOUS_ALLOCATION;
  52. if (place->fpfn || lpfn != man->size)
  53. bman_res->flags |= DRM_BUDDY_RANGE_ALLOCATION;
  54. GEM_BUG_ON(!bman_res->base.size);
  55. size = bman_res->base.size;
  56. min_page_size = bman->default_page_size;
  57. if (bo->page_alignment)
  58. min_page_size = bo->page_alignment << PAGE_SHIFT;
  59. GEM_BUG_ON(min_page_size < mm->chunk_size);
  60. GEM_BUG_ON(!IS_ALIGNED(size, min_page_size));
  61. if (size > lpfn << PAGE_SHIFT) {
  62. err = -E2BIG;
  63. goto err_free_res;
  64. }
  65. n_pages = size >> ilog2(mm->chunk_size);
  66. mutex_lock(&bman->lock);
  67. if (lpfn <= bman->visible_size && n_pages > bman->visible_avail) {
  68. mutex_unlock(&bman->lock);
  69. err = -ENOSPC;
  70. goto err_free_res;
  71. }
  72. err = drm_buddy_alloc_blocks(mm, (u64)place->fpfn << PAGE_SHIFT,
  73. (u64)lpfn << PAGE_SHIFT,
  74. (u64)n_pages << PAGE_SHIFT,
  75. min_page_size,
  76. &bman_res->blocks,
  77. bman_res->flags);
  78. if (unlikely(err))
  79. goto err_free_blocks;
  80. if (lpfn <= bman->visible_size) {
  81. bman_res->used_visible_size = PFN_UP(bman_res->base.size);
  82. } else {
  83. struct drm_buddy_block *block;
  84. list_for_each_entry(block, &bman_res->blocks, link) {
  85. unsigned long start =
  86. drm_buddy_block_offset(block) >> PAGE_SHIFT;
  87. if (start < bman->visible_size) {
  88. unsigned long end = start +
  89. (drm_buddy_block_size(mm, block) >> PAGE_SHIFT);
  90. bman_res->used_visible_size +=
  91. min(end, bman->visible_size) - start;
  92. }
  93. }
  94. }
  95. if (bman_res->used_visible_size)
  96. bman->visible_avail -= bman_res->used_visible_size;
  97. mutex_unlock(&bman->lock);
  98. *res = &bman_res->base;
  99. return 0;
  100. err_free_blocks:
  101. drm_buddy_free_list(mm, &bman_res->blocks, 0);
  102. mutex_unlock(&bman->lock);
  103. err_free_res:
  104. ttm_resource_fini(man, &bman_res->base);
  105. kfree(bman_res);
  106. return err;
  107. }
  108. static void i915_ttm_buddy_man_free(struct ttm_resource_manager *man,
  109. struct ttm_resource *res)
  110. {
  111. struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res);
  112. struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
  113. mutex_lock(&bman->lock);
  114. drm_buddy_free_list(&bman->mm, &bman_res->blocks, 0);
  115. bman->visible_avail += bman_res->used_visible_size;
  116. mutex_unlock(&bman->lock);
  117. ttm_resource_fini(man, res);
  118. kfree(bman_res);
  119. }
  120. static bool i915_ttm_buddy_man_intersects(struct ttm_resource_manager *man,
  121. struct ttm_resource *res,
  122. const struct ttm_place *place,
  123. size_t size)
  124. {
  125. struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res);
  126. struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
  127. struct drm_buddy *mm = &bman->mm;
  128. struct drm_buddy_block *block;
  129. if (!place->fpfn && !place->lpfn)
  130. return true;
  131. GEM_BUG_ON(!place->lpfn);
  132. /*
  133. * If we just want something mappable then we can quickly check
  134. * if the current victim resource is using any of the CPU
  135. * visible portion.
  136. */
  137. if (!place->fpfn &&
  138. place->lpfn == i915_ttm_buddy_man_visible_size(man))
  139. return bman_res->used_visible_size > 0;
  140. /* Check each drm buddy block individually */
  141. list_for_each_entry(block, &bman_res->blocks, link) {
  142. unsigned long fpfn =
  143. drm_buddy_block_offset(block) >> PAGE_SHIFT;
  144. unsigned long lpfn = fpfn +
  145. (drm_buddy_block_size(mm, block) >> PAGE_SHIFT);
  146. if (place->fpfn < lpfn && place->lpfn > fpfn)
  147. return true;
  148. }
  149. return false;
  150. }
  151. static bool i915_ttm_buddy_man_compatible(struct ttm_resource_manager *man,
  152. struct ttm_resource *res,
  153. const struct ttm_place *place,
  154. size_t size)
  155. {
  156. struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res);
  157. struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
  158. struct drm_buddy *mm = &bman->mm;
  159. struct drm_buddy_block *block;
  160. if (!place->fpfn && !place->lpfn)
  161. return true;
  162. GEM_BUG_ON(!place->lpfn);
  163. if (!place->fpfn &&
  164. place->lpfn == i915_ttm_buddy_man_visible_size(man))
  165. return bman_res->used_visible_size == PFN_UP(res->size);
  166. /* Check each drm buddy block individually */
  167. list_for_each_entry(block, &bman_res->blocks, link) {
  168. unsigned long fpfn =
  169. drm_buddy_block_offset(block) >> PAGE_SHIFT;
  170. unsigned long lpfn = fpfn +
  171. (drm_buddy_block_size(mm, block) >> PAGE_SHIFT);
  172. if (fpfn < place->fpfn || lpfn > place->lpfn)
  173. return false;
  174. }
  175. return true;
  176. }
  177. static void i915_ttm_buddy_man_debug(struct ttm_resource_manager *man,
  178. struct drm_printer *printer)
  179. {
  180. struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
  181. struct drm_buddy_block *block;
  182. mutex_lock(&bman->lock);
  183. drm_printf(printer, "default_page_size: %lluKiB\n",
  184. bman->default_page_size >> 10);
  185. drm_printf(printer, "visible_avail: %lluMiB\n",
  186. (u64)bman->visible_avail << PAGE_SHIFT >> 20);
  187. drm_printf(printer, "visible_size: %lluMiB\n",
  188. (u64)bman->visible_size << PAGE_SHIFT >> 20);
  189. drm_printf(printer, "visible_reserved: %lluMiB\n",
  190. (u64)bman->visible_reserved << PAGE_SHIFT >> 20);
  191. drm_buddy_print(&bman->mm, printer);
  192. drm_printf(printer, "reserved:\n");
  193. list_for_each_entry(block, &bman->reserved, link)
  194. drm_buddy_block_print(&bman->mm, block, printer);
  195. mutex_unlock(&bman->lock);
  196. }
  197. static const struct ttm_resource_manager_func i915_ttm_buddy_manager_func = {
  198. .alloc = i915_ttm_buddy_man_alloc,
  199. .free = i915_ttm_buddy_man_free,
  200. .intersects = i915_ttm_buddy_man_intersects,
  201. .compatible = i915_ttm_buddy_man_compatible,
  202. .debug = i915_ttm_buddy_man_debug,
  203. };
  204. /**
  205. * i915_ttm_buddy_man_init - Setup buddy allocator based ttm manager
  206. * @bdev: The ttm device
  207. * @type: Memory type we want to manage
  208. * @use_tt: Set use_tt for the manager
  209. * @size: The size in bytes to manage
  210. * @visible_size: The CPU visible size in bytes to manage
  211. * @default_page_size: The default minimum page size in bytes for allocations,
  212. * this must be at least as large as @chunk_size, and can be overridden by
  213. * setting the BO page_alignment, to be larger or smaller as needed.
  214. * @chunk_size: The minimum page size in bytes for our allocations i.e
  215. * order-zero
  216. *
  217. * Note that the starting address is assumed to be zero here, since this
  218. * simplifies keeping the property where allocated blocks having natural
  219. * power-of-two alignment. So long as the real starting address is some large
  220. * power-of-two, or naturally start from zero, then this should be fine. Also
  221. * the &i915_ttm_buddy_man_reserve interface can be used to preserve alignment
  222. * if say there is some unusable range from the start of the region. We can
  223. * revisit this in the future and make the interface accept an actual starting
  224. * offset and let it take care of the rest.
  225. *
  226. * Note that if the @size is not aligned to the @chunk_size then we perform the
  227. * required rounding to get the usable size. The final size in pages can be
  228. * taken from &ttm_resource_manager.size.
  229. *
  230. * Return: 0 on success, negative error code on failure.
  231. */
  232. int i915_ttm_buddy_man_init(struct ttm_device *bdev,
  233. unsigned int type, bool use_tt,
  234. u64 size, u64 visible_size, u64 default_page_size,
  235. u64 chunk_size)
  236. {
  237. struct ttm_resource_manager *man;
  238. struct i915_ttm_buddy_manager *bman;
  239. int err;
  240. bman = kzalloc_obj(*bman);
  241. if (!bman)
  242. return -ENOMEM;
  243. err = drm_buddy_init(&bman->mm, size, chunk_size);
  244. if (err)
  245. goto err_free_bman;
  246. mutex_init(&bman->lock);
  247. INIT_LIST_HEAD(&bman->reserved);
  248. GEM_BUG_ON(default_page_size < chunk_size);
  249. bman->default_page_size = default_page_size;
  250. bman->visible_size = visible_size >> PAGE_SHIFT;
  251. bman->visible_avail = bman->visible_size;
  252. man = &bman->manager;
  253. man->use_tt = use_tt;
  254. man->func = &i915_ttm_buddy_manager_func;
  255. ttm_resource_manager_init(man, bdev, bman->mm.size >> PAGE_SHIFT);
  256. ttm_resource_manager_set_used(man, true);
  257. ttm_set_driver_manager(bdev, type, man);
  258. return 0;
  259. err_free_bman:
  260. kfree(bman);
  261. return err;
  262. }
  263. /**
  264. * i915_ttm_buddy_man_fini - Destroy the buddy allocator ttm manager
  265. * @bdev: The ttm device
  266. * @type: Memory type we want to manage
  267. *
  268. * Note that if we reserved anything with &i915_ttm_buddy_man_reserve, this will
  269. * also be freed for us here.
  270. *
  271. * Return: 0 on success, negative error code on failure.
  272. */
  273. int i915_ttm_buddy_man_fini(struct ttm_device *bdev, unsigned int type)
  274. {
  275. struct ttm_resource_manager *man = ttm_manager_type(bdev, type);
  276. struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
  277. struct drm_buddy *mm = &bman->mm;
  278. int ret;
  279. ttm_resource_manager_set_used(man, false);
  280. ret = ttm_resource_manager_evict_all(bdev, man);
  281. if (ret)
  282. return ret;
  283. ttm_set_driver_manager(bdev, type, NULL);
  284. mutex_lock(&bman->lock);
  285. drm_buddy_free_list(mm, &bman->reserved, 0);
  286. drm_buddy_fini(mm);
  287. bman->visible_avail += bman->visible_reserved;
  288. WARN_ON_ONCE(bman->visible_avail != bman->visible_size);
  289. mutex_unlock(&bman->lock);
  290. ttm_resource_manager_cleanup(man);
  291. kfree(bman);
  292. return 0;
  293. }
  294. /**
  295. * i915_ttm_buddy_man_reserve - Reserve address range
  296. * @man: The buddy allocator ttm manager
  297. * @start: The offset in bytes, where the region start is assumed to be zero
  298. * @size: The size in bytes
  299. *
  300. * Note that the starting address for the region is always assumed to be zero.
  301. *
  302. * Return: 0 on success, negative error code on failure.
  303. */
  304. int i915_ttm_buddy_man_reserve(struct ttm_resource_manager *man,
  305. u64 start, u64 size)
  306. {
  307. struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
  308. struct drm_buddy *mm = &bman->mm;
  309. unsigned long fpfn = start >> PAGE_SHIFT;
  310. unsigned long flags = 0;
  311. int ret;
  312. flags |= DRM_BUDDY_RANGE_ALLOCATION;
  313. mutex_lock(&bman->lock);
  314. ret = drm_buddy_alloc_blocks(mm, start,
  315. start + size,
  316. size, mm->chunk_size,
  317. &bman->reserved,
  318. flags);
  319. if (fpfn < bman->visible_size) {
  320. unsigned long lpfn = fpfn + (size >> PAGE_SHIFT);
  321. unsigned long visible = min(lpfn, bman->visible_size) - fpfn;
  322. bman->visible_reserved += visible;
  323. bman->visible_avail -= visible;
  324. }
  325. mutex_unlock(&bman->lock);
  326. return ret;
  327. }
  328. /**
  329. * i915_ttm_buddy_man_visible_size - Return the size of the CPU visible portion
  330. * in pages.
  331. * @man: The buddy allocator ttm manager
  332. */
  333. u64 i915_ttm_buddy_man_visible_size(struct ttm_resource_manager *man)
  334. {
  335. struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
  336. return bman->visible_size;
  337. }
  338. /**
  339. * i915_ttm_buddy_man_avail - Query the avail tracking for the manager.
  340. *
  341. * @man: The buddy allocator ttm manager
  342. * @avail: The total available memory in pages for the entire manager.
  343. * @visible_avail: The total available memory in pages for the CPU visible
  344. * portion. Note that this will always give the same value as @avail on
  345. * configurations that don't have a small BAR.
  346. */
  347. void i915_ttm_buddy_man_avail(struct ttm_resource_manager *man,
  348. u64 *avail, u64 *visible_avail)
  349. {
  350. struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
  351. mutex_lock(&bman->lock);
  352. *avail = bman->mm.avail >> PAGE_SHIFT;
  353. *visible_avail = bman->visible_avail;
  354. mutex_unlock(&bman->lock);
  355. }
  356. #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
  357. void i915_ttm_buddy_man_force_visible_size(struct ttm_resource_manager *man,
  358. u64 size)
  359. {
  360. struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
  361. bman->visible_size = size;
  362. }
  363. #endif