drm_mm.c 29 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025
  1. /**************************************************************************
  2. *
  3. * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
  4. * All Rights Reserved.
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a
  7. * copy of this software and associated documentation files (the
  8. * "Software"), to deal in the Software without restriction, including
  9. * without limitation the rights to use, copy, modify, merge, publish,
  10. * distribute, sub license, and/or sell copies of the Software, and to
  11. * permit persons to whom the Software is furnished to do so, subject to
  12. * the following conditions:
  13. *
  14. * The above copyright notice and this permission notice (including the
  15. * next paragraph) shall be included in all copies or substantial portions
  16. * of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
  21. * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
  22. * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  23. * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  24. * USE OR OTHER DEALINGS IN THE SOFTWARE.
  25. *
  26. *
  27. **************************************************************************/
  28. /*
  29. * Generic simple memory manager implementation. Intended to be used as a base
  30. * class implementation for more advanced memory managers.
  31. *
  32. * Note that the algorithm used is quite simple and there might be substantial
  33. * performance gains if a smarter free list is implemented. Currently it is just an
  34. * unordered stack of free regions. This could easily be improved if an RB-tree
  35. * is used instead. At least if we expect heavy fragmentation.
  36. *
  37. * Aligned allocations can also see improvement.
  38. *
  39. * Authors:
  40. * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
  41. */
  42. #include <drm/drmP.h>
  43. #include <drm/drm_mm.h>
  44. #include <linux/slab.h>
  45. #include <linux/seq_file.h>
  46. #include <linux/export.h>
  47. #include <linux/interval_tree_generic.h>
  48. /**
  49. * DOC: Overview
  50. *
  51. * drm_mm provides a simple range allocator. The drivers are free to use the
  52. * resource allocator from the linux core if it suits them, the upside of drm_mm
  53. * is that it's in the DRM core. Which means that it's easier to extend for
  54. * some of the crazier special purpose needs of gpus.
  55. *
  56. * The main data struct is &drm_mm, allocations are tracked in &drm_mm_node.
  57. * Drivers are free to embed either of them into their own suitable
  58. * datastructures. drm_mm itself will not do any allocations of its own, so if
  59. * drivers choose not to embed nodes they need to still allocate them
  60. * themselves.
  61. *
  62. * The range allocator also supports reservation of preallocated blocks. This is
  63. * useful for taking over initial mode setting configurations from the firmware,
  64. * where an object needs to be created which exactly matches the firmware's
  65. * scanout target. As long as the range is still free it can be inserted anytime
  66. * after the allocator is initialized, which helps with avoiding looped
  67. * depencies in the driver load sequence.
  68. *
  69. * drm_mm maintains a stack of most recently freed holes, which of all
  70. * simplistic datastructures seems to be a fairly decent approach to clustering
  71. * allocations and avoiding too much fragmentation. This means free space
  72. * searches are O(num_holes). Given that all the fancy features drm_mm supports
  73. * something better would be fairly complex and since gfx thrashing is a fairly
  74. * steep cliff not a real concern. Removing a node again is O(1).
  75. *
  76. * drm_mm supports a few features: Alignment and range restrictions can be
  77. * supplied. Further more every &drm_mm_node has a color value (which is just an
  78. * opaqua unsigned long) which in conjunction with a driver callback can be used
  79. * to implement sophisticated placement restrictions. The i915 DRM driver uses
  80. * this to implement guard pages between incompatible caching domains in the
  81. * graphics TT.
  82. *
  83. * Two behaviors are supported for searching and allocating: bottom-up and top-down.
  84. * The default is bottom-up. Top-down allocation can be used if the memory area
  85. * has different restrictions, or just to reduce fragmentation.
  86. *
  87. * Finally iteration helpers to walk all nodes and all holes are provided as are
  88. * some basic allocator dumpers for debugging.
  89. */
  90. static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
  91. u64 size,
  92. unsigned alignment,
  93. unsigned long color,
  94. enum drm_mm_search_flags flags);
  95. static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
  96. u64 size,
  97. unsigned alignment,
  98. unsigned long color,
  99. u64 start,
  100. u64 end,
  101. enum drm_mm_search_flags flags);
  102. #ifdef CONFIG_DRM_DEBUG_MM
  103. #include <linux/stackdepot.h>
  104. #define STACKDEPTH 32
  105. #define BUFSZ 4096
  106. static noinline void save_stack(struct drm_mm_node *node)
  107. {
  108. unsigned long entries[STACKDEPTH];
  109. struct stack_trace trace = {
  110. .entries = entries,
  111. .max_entries = STACKDEPTH,
  112. .skip = 1
  113. };
  114. save_stack_trace(&trace);
  115. if (trace.nr_entries != 0 &&
  116. trace.entries[trace.nr_entries-1] == ULONG_MAX)
  117. trace.nr_entries--;
  118. /* May be called under spinlock, so avoid sleeping */
  119. node->stack = depot_save_stack(&trace, GFP_NOWAIT);
  120. }
  121. static void show_leaks(struct drm_mm *mm)
  122. {
  123. struct drm_mm_node *node;
  124. unsigned long entries[STACKDEPTH];
  125. char *buf;
  126. buf = kmalloc(BUFSZ, GFP_KERNEL);
  127. if (!buf)
  128. return;
  129. list_for_each_entry(node, &mm->head_node.node_list, node_list) {
  130. struct stack_trace trace = {
  131. .entries = entries,
  132. .max_entries = STACKDEPTH
  133. };
  134. if (!node->stack) {
  135. DRM_ERROR("node [%08llx + %08llx]: unknown owner\n",
  136. node->start, node->size);
  137. continue;
  138. }
  139. depot_fetch_stack(node->stack, &trace);
  140. snprint_stack_trace(buf, BUFSZ, &trace, 0);
  141. DRM_ERROR("node [%08llx + %08llx]: inserted at\n%s",
  142. node->start, node->size, buf);
  143. }
  144. kfree(buf);
  145. }
  146. #undef STACKDEPTH
  147. #undef BUFSZ
  148. #else
  149. static void save_stack(struct drm_mm_node *node) { }
  150. static void show_leaks(struct drm_mm *mm) { }
  151. #endif
  152. #define START(node) ((node)->start)
  153. #define LAST(node) ((node)->start + (node)->size - 1)
  154. INTERVAL_TREE_DEFINE(struct drm_mm_node, rb,
  155. u64, __subtree_last,
  156. START, LAST, static inline, drm_mm_interval_tree)
  157. struct drm_mm_node *
  158. drm_mm_interval_first(struct drm_mm *mm, u64 start, u64 last)
  159. {
  160. return drm_mm_interval_tree_iter_first(&mm->interval_tree,
  161. start, last);
  162. }
  163. EXPORT_SYMBOL(drm_mm_interval_first);
  164. struct drm_mm_node *
  165. drm_mm_interval_next(struct drm_mm_node *node, u64 start, u64 last)
  166. {
  167. return drm_mm_interval_tree_iter_next(node, start, last);
  168. }
  169. EXPORT_SYMBOL(drm_mm_interval_next);
  170. static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node,
  171. struct drm_mm_node *node)
  172. {
  173. struct drm_mm *mm = hole_node->mm;
  174. struct rb_node **link, *rb;
  175. struct drm_mm_node *parent;
  176. node->__subtree_last = LAST(node);
  177. if (hole_node->allocated) {
  178. rb = &hole_node->rb;
  179. while (rb) {
  180. parent = rb_entry(rb, struct drm_mm_node, rb);
  181. if (parent->__subtree_last >= node->__subtree_last)
  182. break;
  183. parent->__subtree_last = node->__subtree_last;
  184. rb = rb_parent(rb);
  185. }
  186. rb = &hole_node->rb;
  187. link = &hole_node->rb.rb_right;
  188. } else {
  189. rb = NULL;
  190. link = &mm->interval_tree.rb_node;
  191. }
  192. while (*link) {
  193. rb = *link;
  194. parent = rb_entry(rb, struct drm_mm_node, rb);
  195. if (parent->__subtree_last < node->__subtree_last)
  196. parent->__subtree_last = node->__subtree_last;
  197. if (node->start < parent->start)
  198. link = &parent->rb.rb_left;
  199. else
  200. link = &parent->rb.rb_right;
  201. }
  202. rb_link_node(&node->rb, rb, link);
  203. rb_insert_augmented(&node->rb,
  204. &mm->interval_tree,
  205. &drm_mm_interval_tree_augment);
  206. }
  207. static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
  208. struct drm_mm_node *node,
  209. u64 size, unsigned alignment,
  210. unsigned long color,
  211. enum drm_mm_allocator_flags flags)
  212. {
  213. struct drm_mm *mm = hole_node->mm;
  214. u64 hole_start = drm_mm_hole_node_start(hole_node);
  215. u64 hole_end = drm_mm_hole_node_end(hole_node);
  216. u64 adj_start = hole_start;
  217. u64 adj_end = hole_end;
  218. BUG_ON(node->allocated);
  219. if (mm->color_adjust)
  220. mm->color_adjust(hole_node, color, &adj_start, &adj_end);
  221. if (flags & DRM_MM_CREATE_TOP)
  222. adj_start = adj_end - size;
  223. if (alignment) {
  224. u64 tmp = adj_start;
  225. unsigned rem;
  226. rem = do_div(tmp, alignment);
  227. if (rem) {
  228. if (flags & DRM_MM_CREATE_TOP)
  229. adj_start -= rem;
  230. else
  231. adj_start += alignment - rem;
  232. }
  233. }
  234. BUG_ON(adj_start < hole_start);
  235. BUG_ON(adj_end > hole_end);
  236. if (adj_start == hole_start) {
  237. hole_node->hole_follows = 0;
  238. list_del(&hole_node->hole_stack);
  239. }
  240. node->start = adj_start;
  241. node->size = size;
  242. node->mm = mm;
  243. node->color = color;
  244. node->allocated = 1;
  245. list_add(&node->node_list, &hole_node->node_list);
  246. drm_mm_interval_tree_add_node(hole_node, node);
  247. BUG_ON(node->start + node->size > adj_end);
  248. node->hole_follows = 0;
  249. if (__drm_mm_hole_node_start(node) < hole_end) {
  250. list_add(&node->hole_stack, &mm->hole_stack);
  251. node->hole_follows = 1;
  252. }
  253. save_stack(node);
  254. }
  255. /**
  256. * drm_mm_reserve_node - insert an pre-initialized node
  257. * @mm: drm_mm allocator to insert @node into
  258. * @node: drm_mm_node to insert
  259. *
  260. * This functions inserts an already set-up drm_mm_node into the allocator,
  261. * meaning that start, size and color must be set by the caller. This is useful
  262. * to initialize the allocator with preallocated objects which must be set-up
  263. * before the range allocator can be set-up, e.g. when taking over a firmware
  264. * framebuffer.
  265. *
  266. * Returns:
  267. * 0 on success, -ENOSPC if there's no hole where @node is.
  268. */
  269. int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
  270. {
  271. u64 end = node->start + node->size;
  272. struct drm_mm_node *hole;
  273. u64 hole_start, hole_end;
  274. if (WARN_ON(node->size == 0))
  275. return -EINVAL;
  276. end = node->start + node->size;
  277. /* Find the relevant hole to add our node to */
  278. hole = drm_mm_interval_tree_iter_first(&mm->interval_tree,
  279. node->start, ~(u64)0);
  280. if (hole) {
  281. if (hole->start < end)
  282. return -ENOSPC;
  283. } else {
  284. hole = list_entry(&mm->head_node.node_list,
  285. typeof(*hole), node_list);
  286. }
  287. hole = list_last_entry(&hole->node_list, typeof(*hole), node_list);
  288. if (!hole->hole_follows)
  289. return -ENOSPC;
  290. hole_start = __drm_mm_hole_node_start(hole);
  291. hole_end = __drm_mm_hole_node_end(hole);
  292. if (hole_start > node->start || hole_end < end)
  293. return -ENOSPC;
  294. node->mm = mm;
  295. node->allocated = 1;
  296. list_add(&node->node_list, &hole->node_list);
  297. drm_mm_interval_tree_add_node(hole, node);
  298. if (node->start == hole_start) {
  299. hole->hole_follows = 0;
  300. list_del(&hole->hole_stack);
  301. }
  302. node->hole_follows = 0;
  303. if (end != hole_end) {
  304. list_add(&node->hole_stack, &mm->hole_stack);
  305. node->hole_follows = 1;
  306. }
  307. save_stack(node);
  308. return 0;
  309. }
  310. EXPORT_SYMBOL(drm_mm_reserve_node);
  311. /**
  312. * drm_mm_insert_node_generic - search for space and insert @node
  313. * @mm: drm_mm to allocate from
  314. * @node: preallocate node to insert
  315. * @size: size of the allocation
  316. * @alignment: alignment of the allocation
  317. * @color: opaque tag value to use for this node
  318. * @sflags: flags to fine-tune the allocation search
  319. * @aflags: flags to fine-tune the allocation behavior
  320. *
  321. * The preallocated node must be cleared to 0.
  322. *
  323. * Returns:
  324. * 0 on success, -ENOSPC if there's no suitable hole.
  325. */
  326. int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node,
  327. u64 size, unsigned alignment,
  328. unsigned long color,
  329. enum drm_mm_search_flags sflags,
  330. enum drm_mm_allocator_flags aflags)
  331. {
  332. struct drm_mm_node *hole_node;
  333. if (WARN_ON(size == 0))
  334. return -EINVAL;
  335. hole_node = drm_mm_search_free_generic(mm, size, alignment,
  336. color, sflags);
  337. if (!hole_node)
  338. return -ENOSPC;
  339. drm_mm_insert_helper(hole_node, node, size, alignment, color, aflags);
  340. return 0;
  341. }
  342. EXPORT_SYMBOL(drm_mm_insert_node_generic);
  343. static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
  344. struct drm_mm_node *node,
  345. u64 size, unsigned alignment,
  346. unsigned long color,
  347. u64 start, u64 end,
  348. enum drm_mm_allocator_flags flags)
  349. {
  350. struct drm_mm *mm = hole_node->mm;
  351. u64 hole_start = drm_mm_hole_node_start(hole_node);
  352. u64 hole_end = drm_mm_hole_node_end(hole_node);
  353. u64 adj_start = hole_start;
  354. u64 adj_end = hole_end;
  355. BUG_ON(!hole_node->hole_follows || node->allocated);
  356. if (adj_start < start)
  357. adj_start = start;
  358. if (adj_end > end)
  359. adj_end = end;
  360. if (mm->color_adjust)
  361. mm->color_adjust(hole_node, color, &adj_start, &adj_end);
  362. if (flags & DRM_MM_CREATE_TOP)
  363. adj_start = adj_end - size;
  364. if (alignment) {
  365. u64 tmp = adj_start;
  366. unsigned rem;
  367. rem = do_div(tmp, alignment);
  368. if (rem) {
  369. if (flags & DRM_MM_CREATE_TOP)
  370. adj_start -= rem;
  371. else
  372. adj_start += alignment - rem;
  373. }
  374. }
  375. if (adj_start == hole_start) {
  376. hole_node->hole_follows = 0;
  377. list_del(&hole_node->hole_stack);
  378. }
  379. node->start = adj_start;
  380. node->size = size;
  381. node->mm = mm;
  382. node->color = color;
  383. node->allocated = 1;
  384. list_add(&node->node_list, &hole_node->node_list);
  385. drm_mm_interval_tree_add_node(hole_node, node);
  386. BUG_ON(node->start < start);
  387. BUG_ON(node->start < adj_start);
  388. BUG_ON(node->start + node->size > adj_end);
  389. BUG_ON(node->start + node->size > end);
  390. node->hole_follows = 0;
  391. if (__drm_mm_hole_node_start(node) < hole_end) {
  392. list_add(&node->hole_stack, &mm->hole_stack);
  393. node->hole_follows = 1;
  394. }
  395. save_stack(node);
  396. }
  397. /**
  398. * drm_mm_insert_node_in_range_generic - ranged search for space and insert @node
  399. * @mm: drm_mm to allocate from
  400. * @node: preallocate node to insert
  401. * @size: size of the allocation
  402. * @alignment: alignment of the allocation
  403. * @color: opaque tag value to use for this node
  404. * @start: start of the allowed range for this node
  405. * @end: end of the allowed range for this node
  406. * @sflags: flags to fine-tune the allocation search
  407. * @aflags: flags to fine-tune the allocation behavior
  408. *
  409. * The preallocated node must be cleared to 0.
  410. *
  411. * Returns:
  412. * 0 on success, -ENOSPC if there's no suitable hole.
  413. */
  414. int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node,
  415. u64 size, unsigned alignment,
  416. unsigned long color,
  417. u64 start, u64 end,
  418. enum drm_mm_search_flags sflags,
  419. enum drm_mm_allocator_flags aflags)
  420. {
  421. struct drm_mm_node *hole_node;
  422. if (WARN_ON(size == 0))
  423. return -EINVAL;
  424. hole_node = drm_mm_search_free_in_range_generic(mm,
  425. size, alignment, color,
  426. start, end, sflags);
  427. if (!hole_node)
  428. return -ENOSPC;
  429. drm_mm_insert_helper_range(hole_node, node,
  430. size, alignment, color,
  431. start, end, aflags);
  432. return 0;
  433. }
  434. EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic);
  435. /**
  436. * drm_mm_remove_node - Remove a memory node from the allocator.
  437. * @node: drm_mm_node to remove
  438. *
  439. * This just removes a node from its drm_mm allocator. The node does not need to
  440. * be cleared again before it can be re-inserted into this or any other drm_mm
  441. * allocator. It is a bug to call this function on a un-allocated node.
  442. */
  443. void drm_mm_remove_node(struct drm_mm_node *node)
  444. {
  445. struct drm_mm *mm = node->mm;
  446. struct drm_mm_node *prev_node;
  447. if (WARN_ON(!node->allocated))
  448. return;
  449. BUG_ON(node->scanned_block || node->scanned_prev_free
  450. || node->scanned_next_free);
  451. prev_node =
  452. list_entry(node->node_list.prev, struct drm_mm_node, node_list);
  453. if (node->hole_follows) {
  454. BUG_ON(__drm_mm_hole_node_start(node) ==
  455. __drm_mm_hole_node_end(node));
  456. list_del(&node->hole_stack);
  457. } else
  458. BUG_ON(__drm_mm_hole_node_start(node) !=
  459. __drm_mm_hole_node_end(node));
  460. if (!prev_node->hole_follows) {
  461. prev_node->hole_follows = 1;
  462. list_add(&prev_node->hole_stack, &mm->hole_stack);
  463. } else
  464. list_move(&prev_node->hole_stack, &mm->hole_stack);
  465. drm_mm_interval_tree_remove(node, &mm->interval_tree);
  466. list_del(&node->node_list);
  467. node->allocated = 0;
  468. }
  469. EXPORT_SYMBOL(drm_mm_remove_node);
  470. static int check_free_hole(u64 start, u64 end, u64 size, unsigned alignment)
  471. {
  472. if (end - start < size)
  473. return 0;
  474. if (alignment) {
  475. u64 tmp = start;
  476. unsigned rem;
  477. rem = do_div(tmp, alignment);
  478. if (rem)
  479. start += alignment - rem;
  480. }
  481. return end >= start + size;
  482. }
  483. static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
  484. u64 size,
  485. unsigned alignment,
  486. unsigned long color,
  487. enum drm_mm_search_flags flags)
  488. {
  489. struct drm_mm_node *entry;
  490. struct drm_mm_node *best;
  491. u64 adj_start;
  492. u64 adj_end;
  493. u64 best_size;
  494. BUG_ON(mm->scanned_blocks);
  495. best = NULL;
  496. best_size = ~0UL;
  497. __drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
  498. flags & DRM_MM_SEARCH_BELOW) {
  499. u64 hole_size = adj_end - adj_start;
  500. if (mm->color_adjust) {
  501. mm->color_adjust(entry, color, &adj_start, &adj_end);
  502. if (adj_end <= adj_start)
  503. continue;
  504. }
  505. if (!check_free_hole(adj_start, adj_end, size, alignment))
  506. continue;
  507. if (!(flags & DRM_MM_SEARCH_BEST))
  508. return entry;
  509. if (hole_size < best_size) {
  510. best = entry;
  511. best_size = hole_size;
  512. }
  513. }
  514. return best;
  515. }
  516. static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
  517. u64 size,
  518. unsigned alignment,
  519. unsigned long color,
  520. u64 start,
  521. u64 end,
  522. enum drm_mm_search_flags flags)
  523. {
  524. struct drm_mm_node *entry;
  525. struct drm_mm_node *best;
  526. u64 adj_start;
  527. u64 adj_end;
  528. u64 best_size;
  529. BUG_ON(mm->scanned_blocks);
  530. best = NULL;
  531. best_size = ~0UL;
  532. __drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
  533. flags & DRM_MM_SEARCH_BELOW) {
  534. u64 hole_size = adj_end - adj_start;
  535. if (adj_start < start)
  536. adj_start = start;
  537. if (adj_end > end)
  538. adj_end = end;
  539. if (mm->color_adjust) {
  540. mm->color_adjust(entry, color, &adj_start, &adj_end);
  541. if (adj_end <= adj_start)
  542. continue;
  543. }
  544. if (!check_free_hole(adj_start, adj_end, size, alignment))
  545. continue;
  546. if (!(flags & DRM_MM_SEARCH_BEST))
  547. return entry;
  548. if (hole_size < best_size) {
  549. best = entry;
  550. best_size = hole_size;
  551. }
  552. }
  553. return best;
  554. }
  555. /**
  556. * drm_mm_replace_node - move an allocation from @old to @new
  557. * @old: drm_mm_node to remove from the allocator
  558. * @new: drm_mm_node which should inherit @old's allocation
  559. *
  560. * This is useful for when drivers embed the drm_mm_node structure and hence
  561. * can't move allocations by reassigning pointers. It's a combination of remove
  562. * and insert with the guarantee that the allocation start will match.
  563. */
  564. void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
  565. {
  566. list_replace(&old->node_list, &new->node_list);
  567. list_replace(&old->hole_stack, &new->hole_stack);
  568. rb_replace_node(&old->rb, &new->rb, &old->mm->interval_tree);
  569. new->hole_follows = old->hole_follows;
  570. new->mm = old->mm;
  571. new->start = old->start;
  572. new->size = old->size;
  573. new->color = old->color;
  574. new->__subtree_last = old->__subtree_last;
  575. old->allocated = 0;
  576. new->allocated = 1;
  577. }
  578. EXPORT_SYMBOL(drm_mm_replace_node);
  579. /**
  580. * DOC: lru scan roaster
  581. *
  582. * Very often GPUs need to have continuous allocations for a given object. When
  583. * evicting objects to make space for a new one it is therefore not most
  584. * efficient when we simply start to select all objects from the tail of an LRU
  585. * until there's a suitable hole: Especially for big objects or nodes that
  586. * otherwise have special allocation constraints there's a good chance we evict
  587. * lots of (smaller) objects unecessarily.
  588. *
  589. * The DRM range allocator supports this use-case through the scanning
  590. * interfaces. First a scan operation needs to be initialized with
  591. * drm_mm_init_scan() or drm_mm_init_scan_with_range(). The the driver adds
  592. * objects to the roaster (probably by walking an LRU list, but this can be
  593. * freely implemented) until a suitable hole is found or there's no further
  594. * evitable object.
  595. *
  596. * The the driver must walk through all objects again in exactly the reverse
  597. * order to restore the allocator state. Note that while the allocator is used
  598. * in the scan mode no other operation is allowed.
  599. *
  600. * Finally the driver evicts all objects selected in the scan. Adding and
  601. * removing an object is O(1), and since freeing a node is also O(1) the overall
  602. * complexity is O(scanned_objects). So like the free stack which needs to be
  603. * walked before a scan operation even begins this is linear in the number of
  604. * objects. It doesn't seem to hurt badly.
  605. */
  606. /**
  607. * drm_mm_init_scan - initialize lru scanning
  608. * @mm: drm_mm to scan
  609. * @size: size of the allocation
  610. * @alignment: alignment of the allocation
  611. * @color: opaque tag value to use for the allocation
  612. *
  613. * This simply sets up the scanning routines with the parameters for the desired
  614. * hole. Note that there's no need to specify allocation flags, since they only
  615. * change the place a node is allocated from within a suitable hole.
  616. *
  617. * Warning:
  618. * As long as the scan list is non-empty, no other operations than
  619. * adding/removing nodes to/from the scan list are allowed.
  620. */
  621. void drm_mm_init_scan(struct drm_mm *mm,
  622. u64 size,
  623. unsigned alignment,
  624. unsigned long color)
  625. {
  626. mm->scan_color = color;
  627. mm->scan_alignment = alignment;
  628. mm->scan_size = size;
  629. mm->scanned_blocks = 0;
  630. mm->scan_hit_start = 0;
  631. mm->scan_hit_end = 0;
  632. mm->scan_check_range = 0;
  633. mm->prev_scanned_node = NULL;
  634. }
  635. EXPORT_SYMBOL(drm_mm_init_scan);
  636. /**
  637. * drm_mm_init_scan - initialize range-restricted lru scanning
  638. * @mm: drm_mm to scan
  639. * @size: size of the allocation
  640. * @alignment: alignment of the allocation
  641. * @color: opaque tag value to use for the allocation
  642. * @start: start of the allowed range for the allocation
  643. * @end: end of the allowed range for the allocation
  644. *
  645. * This simply sets up the scanning routines with the parameters for the desired
  646. * hole. Note that there's no need to specify allocation flags, since they only
  647. * change the place a node is allocated from within a suitable hole.
  648. *
  649. * Warning:
  650. * As long as the scan list is non-empty, no other operations than
  651. * adding/removing nodes to/from the scan list are allowed.
  652. */
  653. void drm_mm_init_scan_with_range(struct drm_mm *mm,
  654. u64 size,
  655. unsigned alignment,
  656. unsigned long color,
  657. u64 start,
  658. u64 end)
  659. {
  660. mm->scan_color = color;
  661. mm->scan_alignment = alignment;
  662. mm->scan_size = size;
  663. mm->scanned_blocks = 0;
  664. mm->scan_hit_start = 0;
  665. mm->scan_hit_end = 0;
  666. mm->scan_start = start;
  667. mm->scan_end = end;
  668. mm->scan_check_range = 1;
  669. mm->prev_scanned_node = NULL;
  670. }
  671. EXPORT_SYMBOL(drm_mm_init_scan_with_range);
  672. /**
  673. * drm_mm_scan_add_block - add a node to the scan list
  674. * @node: drm_mm_node to add
  675. *
  676. * Add a node to the scan list that might be freed to make space for the desired
  677. * hole.
  678. *
  679. * Returns:
  680. * True if a hole has been found, false otherwise.
  681. */
  682. bool drm_mm_scan_add_block(struct drm_mm_node *node)
  683. {
  684. struct drm_mm *mm = node->mm;
  685. struct drm_mm_node *prev_node;
  686. u64 hole_start, hole_end;
  687. u64 adj_start, adj_end;
  688. mm->scanned_blocks++;
  689. BUG_ON(node->scanned_block);
  690. node->scanned_block = 1;
  691. prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
  692. node_list);
  693. node->scanned_preceeds_hole = prev_node->hole_follows;
  694. prev_node->hole_follows = 1;
  695. list_del(&node->node_list);
  696. node->node_list.prev = &prev_node->node_list;
  697. node->node_list.next = &mm->prev_scanned_node->node_list;
  698. mm->prev_scanned_node = node;
  699. adj_start = hole_start = drm_mm_hole_node_start(prev_node);
  700. adj_end = hole_end = drm_mm_hole_node_end(prev_node);
  701. if (mm->scan_check_range) {
  702. if (adj_start < mm->scan_start)
  703. adj_start = mm->scan_start;
  704. if (adj_end > mm->scan_end)
  705. adj_end = mm->scan_end;
  706. }
  707. if (mm->color_adjust)
  708. mm->color_adjust(prev_node, mm->scan_color,
  709. &adj_start, &adj_end);
  710. if (check_free_hole(adj_start, adj_end,
  711. mm->scan_size, mm->scan_alignment)) {
  712. mm->scan_hit_start = hole_start;
  713. mm->scan_hit_end = hole_end;
  714. return true;
  715. }
  716. return false;
  717. }
  718. EXPORT_SYMBOL(drm_mm_scan_add_block);
  719. /**
  720. * drm_mm_scan_remove_block - remove a node from the scan list
  721. * @node: drm_mm_node to remove
  722. *
  723. * Nodes _must_ be removed in the exact same order from the scan list as they
  724. * have been added, otherwise the internal state of the memory manager will be
  725. * corrupted.
  726. *
  727. * When the scan list is empty, the selected memory nodes can be freed. An
  728. * immediately following drm_mm_search_free with !DRM_MM_SEARCH_BEST will then
  729. * return the just freed block (because its at the top of the free_stack list).
  730. *
  731. * Returns:
  732. * True if this block should be evicted, false otherwise. Will always
  733. * return false when no hole has been found.
  734. */
  735. bool drm_mm_scan_remove_block(struct drm_mm_node *node)
  736. {
  737. struct drm_mm *mm = node->mm;
  738. struct drm_mm_node *prev_node;
  739. mm->scanned_blocks--;
  740. BUG_ON(!node->scanned_block);
  741. node->scanned_block = 0;
  742. prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
  743. node_list);
  744. prev_node->hole_follows = node->scanned_preceeds_hole;
  745. list_add(&node->node_list, &prev_node->node_list);
  746. return (drm_mm_hole_node_end(node) > mm->scan_hit_start &&
  747. node->start < mm->scan_hit_end);
  748. }
  749. EXPORT_SYMBOL(drm_mm_scan_remove_block);
  750. /**
  751. * drm_mm_clean - checks whether an allocator is clean
  752. * @mm: drm_mm allocator to check
  753. *
  754. * Returns:
  755. * True if the allocator is completely free, false if there's still a node
  756. * allocated in it.
  757. */
  758. bool drm_mm_clean(struct drm_mm * mm)
  759. {
  760. struct list_head *head = &mm->head_node.node_list;
  761. return (head->next->next == head);
  762. }
  763. EXPORT_SYMBOL(drm_mm_clean);
  764. /**
  765. * drm_mm_init - initialize a drm-mm allocator
  766. * @mm: the drm_mm structure to initialize
  767. * @start: start of the range managed by @mm
  768. * @size: end of the range managed by @mm
  769. *
  770. * Note that @mm must be cleared to 0 before calling this function.
  771. */
  772. void drm_mm_init(struct drm_mm * mm, u64 start, u64 size)
  773. {
  774. INIT_LIST_HEAD(&mm->hole_stack);
  775. mm->scanned_blocks = 0;
  776. /* Clever trick to avoid a special case in the free hole tracking. */
  777. INIT_LIST_HEAD(&mm->head_node.node_list);
  778. mm->head_node.hole_follows = 1;
  779. mm->head_node.scanned_block = 0;
  780. mm->head_node.scanned_prev_free = 0;
  781. mm->head_node.scanned_next_free = 0;
  782. mm->head_node.mm = mm;
  783. mm->head_node.start = start + size;
  784. mm->head_node.size = start - mm->head_node.start;
  785. list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack);
  786. mm->interval_tree = RB_ROOT;
  787. mm->color_adjust = NULL;
  788. }
  789. EXPORT_SYMBOL(drm_mm_init);
  790. /**
  791. * drm_mm_takedown - clean up a drm_mm allocator
  792. * @mm: drm_mm allocator to clean up
  793. *
  794. * Note that it is a bug to call this function on an allocator which is not
  795. * clean.
  796. */
  797. void drm_mm_takedown(struct drm_mm *mm)
  798. {
  799. if (WARN(!list_empty(&mm->head_node.node_list),
  800. "Memory manager not clean during takedown.\n"))
  801. show_leaks(mm);
  802. }
  803. EXPORT_SYMBOL(drm_mm_takedown);
  804. static u64 drm_mm_debug_hole(struct drm_mm_node *entry,
  805. const char *prefix)
  806. {
  807. u64 hole_start, hole_end, hole_size;
  808. if (entry->hole_follows) {
  809. hole_start = drm_mm_hole_node_start(entry);
  810. hole_end = drm_mm_hole_node_end(entry);
  811. hole_size = hole_end - hole_start;
  812. pr_debug("%s %#llx-%#llx: %llu: free\n", prefix, hole_start,
  813. hole_end, hole_size);
  814. return hole_size;
  815. }
  816. return 0;
  817. }
  818. /**
  819. * drm_mm_debug_table - dump allocator state to dmesg
  820. * @mm: drm_mm allocator to dump
  821. * @prefix: prefix to use for dumping to dmesg
  822. */
  823. void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
  824. {
  825. struct drm_mm_node *entry;
  826. u64 total_used = 0, total_free = 0, total = 0;
  827. total_free += drm_mm_debug_hole(&mm->head_node, prefix);
  828. drm_mm_for_each_node(entry, mm) {
  829. pr_debug("%s %#llx-%#llx: %llu: used\n", prefix, entry->start,
  830. entry->start + entry->size, entry->size);
  831. total_used += entry->size;
  832. total_free += drm_mm_debug_hole(entry, prefix);
  833. }
  834. total = total_free + total_used;
  835. pr_debug("%s total: %llu, used %llu free %llu\n", prefix, total,
  836. total_used, total_free);
  837. }
  838. EXPORT_SYMBOL(drm_mm_debug_table);
  839. #if defined(CONFIG_DEBUG_FS)
  840. static u64 drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *entry)
  841. {
  842. u64 hole_start, hole_end, hole_size;
  843. if (entry->hole_follows) {
  844. hole_start = drm_mm_hole_node_start(entry);
  845. hole_end = drm_mm_hole_node_end(entry);
  846. hole_size = hole_end - hole_start;
  847. seq_printf(m, "%#018llx-%#018llx: %llu: free\n", hole_start,
  848. hole_end, hole_size);
  849. return hole_size;
  850. }
  851. return 0;
  852. }
  853. /**
  854. * drm_mm_dump_table - dump allocator state to a seq_file
  855. * @m: seq_file to dump to
  856. * @mm: drm_mm allocator to dump
  857. */
  858. int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
  859. {
  860. struct drm_mm_node *entry;
  861. u64 total_used = 0, total_free = 0, total = 0;
  862. total_free += drm_mm_dump_hole(m, &mm->head_node);
  863. drm_mm_for_each_node(entry, mm) {
  864. seq_printf(m, "%#018llx-%#018llx: %llu: used\n", entry->start,
  865. entry->start + entry->size, entry->size);
  866. total_used += entry->size;
  867. total_free += drm_mm_dump_hole(m, entry);
  868. }
  869. total = total_free + total_used;
  870. seq_printf(m, "total: %llu, used %llu free %llu\n", total,
  871. total_used, total_free);
  872. return 0;
  873. }
  874. EXPORT_SYMBOL(drm_mm_dump_table);
  875. #endif