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