1746fbbdbSjsg /**************************************************************************
2746fbbdbSjsg *
3746fbbdbSjsg * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
47f4dd379Sjsg * Copyright 2016 Intel Corporation
5746fbbdbSjsg * All Rights Reserved.
6746fbbdbSjsg *
7746fbbdbSjsg * Permission is hereby granted, free of charge, to any person obtaining a
8746fbbdbSjsg * copy of this software and associated documentation files (the
9746fbbdbSjsg * "Software"), to deal in the Software without restriction, including
10746fbbdbSjsg * without limitation the rights to use, copy, modify, merge, publish,
11746fbbdbSjsg * distribute, sub license, and/or sell copies of the Software, and to
12746fbbdbSjsg * permit persons to whom the Software is furnished to do so, subject to
13746fbbdbSjsg * the following conditions:
14746fbbdbSjsg *
15746fbbdbSjsg * The above copyright notice and this permission notice (including the
16746fbbdbSjsg * next paragraph) shall be included in all copies or substantial portions
17746fbbdbSjsg * of the Software.
18746fbbdbSjsg *
19746fbbdbSjsg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20746fbbdbSjsg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21746fbbdbSjsg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22746fbbdbSjsg * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23746fbbdbSjsg * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24746fbbdbSjsg * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25746fbbdbSjsg * USE OR OTHER DEALINGS IN THE SOFTWARE.
26746fbbdbSjsg *
27746fbbdbSjsg *
28746fbbdbSjsg **************************************************************************/
29746fbbdbSjsg
30746fbbdbSjsg /*
31746fbbdbSjsg * Generic simple memory manager implementation. Intended to be used as a base
32746fbbdbSjsg * class implementation for more advanced memory managers.
33746fbbdbSjsg *
34746fbbdbSjsg * Note that the algorithm used is quite simple and there might be substantial
357f4dd379Sjsg * performance gains if a smarter free list is implemented. Currently it is
367f4dd379Sjsg * just an unordered stack of free regions. This could easily be improved if
377f4dd379Sjsg * an RB-tree is used instead. At least if we expect heavy fragmentation.
38746fbbdbSjsg *
39746fbbdbSjsg * Aligned allocations can also see improvement.
40746fbbdbSjsg *
41746fbbdbSjsg * Authors:
42746fbbdbSjsg * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
43746fbbdbSjsg */
44746fbbdbSjsg
453253c27bSkettenis #include <linux/export.h>
467f4dd379Sjsg #include <linux/interval_tree_generic.h>
47c349dbc7Sjsg #include <linux/seq_file.h>
48c349dbc7Sjsg #include <linux/slab.h>
49c349dbc7Sjsg #include <linux/stacktrace.h>
50c349dbc7Sjsg
51c349dbc7Sjsg #include <drm/drm_mm.h>
52746fbbdbSjsg
533253c27bSkettenis /**
543253c27bSkettenis * DOC: Overview
554e088e8fSjsg *
563253c27bSkettenis * drm_mm provides a simple range allocator. The drivers are free to use the
573253c27bSkettenis * resource allocator from the linux core if it suits them, the upside of drm_mm
583253c27bSkettenis * is that it's in the DRM core. Which means that it's easier to extend for
593253c27bSkettenis * some of the crazier special purpose needs of gpus.
603253c27bSkettenis *
613253c27bSkettenis * The main data struct is &drm_mm, allocations are tracked in &drm_mm_node.
623253c27bSkettenis * Drivers are free to embed either of them into their own suitable
637f4dd379Sjsg * datastructures. drm_mm itself will not do any memory allocations of its own,
647f4dd379Sjsg * so if drivers choose not to embed nodes they need to still allocate them
653253c27bSkettenis * themselves.
663253c27bSkettenis *
673253c27bSkettenis * The range allocator also supports reservation of preallocated blocks. This is
683253c27bSkettenis * useful for taking over initial mode setting configurations from the firmware,
693253c27bSkettenis * where an object needs to be created which exactly matches the firmware's
703253c27bSkettenis * scanout target. As long as the range is still free it can be inserted anytime
713253c27bSkettenis * after the allocator is initialized, which helps with avoiding looped
727f4dd379Sjsg * dependencies in the driver load sequence.
733253c27bSkettenis *
743253c27bSkettenis * drm_mm maintains a stack of most recently freed holes, which of all
753253c27bSkettenis * simplistic datastructures seems to be a fairly decent approach to clustering
763253c27bSkettenis * allocations and avoiding too much fragmentation. This means free space
773253c27bSkettenis * searches are O(num_holes). Given that all the fancy features drm_mm supports
783253c27bSkettenis * something better would be fairly complex and since gfx thrashing is a fairly
793253c27bSkettenis * steep cliff not a real concern. Removing a node again is O(1).
803253c27bSkettenis *
813253c27bSkettenis * drm_mm supports a few features: Alignment and range restrictions can be
823253c27bSkettenis * supplied. Furthermore every &drm_mm_node has a color value (which is just an
837f4dd379Sjsg * opaque unsigned long) which in conjunction with a driver callback can be used
843253c27bSkettenis * to implement sophisticated placement restrictions. The i915 DRM driver uses
853253c27bSkettenis * this to implement guard pages between incompatible caching domains in the
863253c27bSkettenis * graphics TT.
873253c27bSkettenis *
887f4dd379Sjsg * Two behaviors are supported for searching and allocating: bottom-up and
897f4dd379Sjsg * top-down. The default is bottom-up. Top-down allocation can be used if the
907f4dd379Sjsg * memory area has different restrictions, or just to reduce fragmentation.
913253c27bSkettenis *
923253c27bSkettenis * Finally iteration helpers to walk all nodes and all holes are provided as are
933253c27bSkettenis * some basic allocator dumpers for debugging.
947f4dd379Sjsg *
957f4dd379Sjsg * Note that this range allocator is not thread-safe, drivers need to protect
9648cf9467Sjsg * modifications with their own locking. The idea behind this is that for a full
977f4dd379Sjsg * memory manager additional data needs to be protected anyway, hence internal
987f4dd379Sjsg * locking would be fully redundant.
994e088e8fSjsg */
100746fbbdbSjsg
1017f4dd379Sjsg #ifdef CONFIG_DRM_DEBUG_MM
1027f4dd379Sjsg #include <linux/stackdepot.h>
1037f4dd379Sjsg
1047f4dd379Sjsg #define STACKDEPTH 32
1057f4dd379Sjsg #define BUFSZ 4096
1067f4dd379Sjsg
save_stack(struct drm_mm_node * node)1077f4dd379Sjsg static noinline void save_stack(struct drm_mm_node *node)
1087f4dd379Sjsg {
1097f4dd379Sjsg unsigned long entries[STACKDEPTH];
11048cf9467Sjsg unsigned int n;
1117f4dd379Sjsg
11248cf9467Sjsg n = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
1137f4dd379Sjsg
1147f4dd379Sjsg /* May be called under spinlock, so avoid sleeping */
11548cf9467Sjsg node->stack = stack_depot_save(entries, n, GFP_NOWAIT);
1167f4dd379Sjsg }
1177f4dd379Sjsg
show_leaks(struct drm_mm * mm)1187f4dd379Sjsg static void show_leaks(struct drm_mm *mm)
1197f4dd379Sjsg {
1207f4dd379Sjsg struct drm_mm_node *node;
12148cf9467Sjsg unsigned long *entries;
12248cf9467Sjsg unsigned int nr_entries;
1237f4dd379Sjsg char *buf;
1247f4dd379Sjsg
1257f4dd379Sjsg buf = kmalloc(BUFSZ, GFP_KERNEL);
1267f4dd379Sjsg if (!buf)
1277f4dd379Sjsg return;
1287f4dd379Sjsg
1297f4dd379Sjsg list_for_each_entry(node, drm_mm_nodes(mm), node_list) {
1307f4dd379Sjsg if (!node->stack) {
1317f4dd379Sjsg DRM_ERROR("node [%08llx + %08llx]: unknown owner\n",
1327f4dd379Sjsg node->start, node->size);
1337f4dd379Sjsg continue;
1347f4dd379Sjsg }
1357f4dd379Sjsg
13648cf9467Sjsg nr_entries = stack_depot_fetch(node->stack, &entries);
13748cf9467Sjsg stack_trace_snprint(buf, BUFSZ, entries, nr_entries, 0);
1387f4dd379Sjsg DRM_ERROR("node [%08llx + %08llx]: inserted at\n%s",
1397f4dd379Sjsg node->start, node->size, buf);
1407f4dd379Sjsg }
1417f4dd379Sjsg
1427f4dd379Sjsg kfree(buf);
1437f4dd379Sjsg }
1447f4dd379Sjsg
1457f4dd379Sjsg #undef STACKDEPTH
1467f4dd379Sjsg #undef BUFSZ
1477f4dd379Sjsg #else
save_stack(struct drm_mm_node * node)1487f4dd379Sjsg static void save_stack(struct drm_mm_node *node) { }
show_leaks(struct drm_mm * mm)1497f4dd379Sjsg static void show_leaks(struct drm_mm *mm) { }
1507f4dd379Sjsg #endif
1517f4dd379Sjsg
1527f4dd379Sjsg #define START(node) ((node)->start)
1537f4dd379Sjsg #define LAST(node) ((node)->start + (node)->size - 1)
1547f4dd379Sjsg
1557f4dd379Sjsg #ifdef __linux__
INTERVAL_TREE_DEFINE(struct drm_mm_node,rb,u64,__subtree_last,START,LAST,static inline,drm_mm_interval_tree)1567f4dd379Sjsg INTERVAL_TREE_DEFINE(struct drm_mm_node, rb,
1577f4dd379Sjsg u64, __subtree_last,
1587f4dd379Sjsg START, LAST, static inline, drm_mm_interval_tree)
1597f4dd379Sjsg #else
16048cf9467Sjsg static struct drm_mm_node *
16148cf9467Sjsg drm_mm_interval_tree_iter_first(const struct rb_root_cached *root,
16248cf9467Sjsg uint64_t start, uint64_t last)
1637f4dd379Sjsg {
1647f4dd379Sjsg struct drm_mm_node *node;
16548cf9467Sjsg struct rb_node *rb;
1667f4dd379Sjsg
16748cf9467Sjsg for (rb = rb_first_cached(root); rb; rb = rb_next(rb)) {
16848cf9467Sjsg node = rb_entry(rb, typeof(*node), rb);
1697f4dd379Sjsg if (LAST(node) >= start && START(node) <= last)
1707f4dd379Sjsg return node;
1717f4dd379Sjsg }
1727f4dd379Sjsg return NULL;
1737f4dd379Sjsg }
17448cf9467Sjsg
17548cf9467Sjsg static void
17648cf9467Sjsg drm_mm_interval_tree_remove(struct drm_mm_node *node,
17748cf9467Sjsg struct rb_root_cached *root)
17848cf9467Sjsg {
17948cf9467Sjsg rb_erase_cached(&node->rb, root);
18048cf9467Sjsg }
1817f4dd379Sjsg #endif
1827f4dd379Sjsg
1837f4dd379Sjsg struct drm_mm_node *
1847f4dd379Sjsg __drm_mm_interval_first(const struct drm_mm *mm, u64 start, u64 last)
1857f4dd379Sjsg {
18648cf9467Sjsg return drm_mm_interval_tree_iter_first((struct rb_root_cached *)&mm->interval_tree,
18748cf9467Sjsg start, last) ?: (struct drm_mm_node *)&mm->head_node;
1887f4dd379Sjsg }
1897f4dd379Sjsg EXPORT_SYMBOL(__drm_mm_interval_first);
1907f4dd379Sjsg
drm_mm_interval_tree_add_node(struct drm_mm_node * hole_node,struct drm_mm_node * node)1917f4dd379Sjsg static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node,
1927f4dd379Sjsg struct drm_mm_node *node)
1937f4dd379Sjsg {
1947f4dd379Sjsg struct drm_mm *mm = hole_node->mm;
1957f4dd379Sjsg struct rb_node **link, *rb;
1967f4dd379Sjsg struct drm_mm_node *parent;
19748cf9467Sjsg bool leftmost;
1987f4dd379Sjsg
1997f4dd379Sjsg node->__subtree_last = LAST(node);
2007f4dd379Sjsg
20148cf9467Sjsg if (drm_mm_node_allocated(hole_node)) {
2027f4dd379Sjsg rb = &hole_node->rb;
2037f4dd379Sjsg while (rb) {
2047f4dd379Sjsg parent = rb_entry(rb, struct drm_mm_node, rb);
2057f4dd379Sjsg if (parent->__subtree_last >= node->__subtree_last)
2067f4dd379Sjsg break;
2077f4dd379Sjsg
2087f4dd379Sjsg parent->__subtree_last = node->__subtree_last;
2097f4dd379Sjsg rb = rb_parent(rb);
2107f4dd379Sjsg }
2117f4dd379Sjsg
2127f4dd379Sjsg rb = &hole_node->rb;
2137f4dd379Sjsg link = &hole_node->rb.rb_right;
21448cf9467Sjsg leftmost = false;
2157f4dd379Sjsg } else {
2167f4dd379Sjsg rb = NULL;
21748cf9467Sjsg link = &mm->interval_tree.rb_root.rb_node;
21848cf9467Sjsg leftmost = true;
2197f4dd379Sjsg }
2207f4dd379Sjsg
2217f4dd379Sjsg while (*link) {
2227f4dd379Sjsg rb = *link;
2237f4dd379Sjsg parent = rb_entry(rb, struct drm_mm_node, rb);
2247f4dd379Sjsg if (parent->__subtree_last < node->__subtree_last)
2257f4dd379Sjsg parent->__subtree_last = node->__subtree_last;
22648cf9467Sjsg if (node->start < parent->start) {
2277f4dd379Sjsg link = &parent->rb.rb_left;
22848cf9467Sjsg } else {
2297f4dd379Sjsg link = &parent->rb.rb_right;
23048cf9467Sjsg leftmost = false;
23148cf9467Sjsg }
2327f4dd379Sjsg }
2337f4dd379Sjsg
2347f4dd379Sjsg rb_link_node(&node->rb, rb, link);
23548cf9467Sjsg #ifdef notyet
23648cf9467Sjsg rb_insert_augmented_cached(&node->rb, &mm->interval_tree, leftmost,
2377f4dd379Sjsg &drm_mm_interval_tree_augment);
23848cf9467Sjsg #else
23948cf9467Sjsg rb_insert_color_cached(&node->rb, &mm->interval_tree, leftmost);
2407f4dd379Sjsg #endif
24148cf9467Sjsg }
2427f4dd379Sjsg
243*9e16f3f2Sjsg #define DRM_RB_INSERT(root, member, expr) do { \
244*9e16f3f2Sjsg struct rb_node **link = &root.rb_node, *rb = NULL; \
245*9e16f3f2Sjsg u64 x = expr(node); \
246*9e16f3f2Sjsg while (*link) { \
247*9e16f3f2Sjsg rb = *link; \
248*9e16f3f2Sjsg if (x < expr(rb_entry(rb, struct drm_mm_node, member))) \
249*9e16f3f2Sjsg link = &rb->rb_left; \
250*9e16f3f2Sjsg else \
251*9e16f3f2Sjsg link = &rb->rb_right; \
252*9e16f3f2Sjsg } \
253*9e16f3f2Sjsg rb_link_node(&node->member, rb, link); \
254*9e16f3f2Sjsg rb_insert_color(&node->member, &root); \
255*9e16f3f2Sjsg } while (0)
256*9e16f3f2Sjsg
25748cf9467Sjsg #define HOLE_SIZE(NODE) ((NODE)->hole_size)
25848cf9467Sjsg #define HOLE_ADDR(NODE) (__drm_mm_hole_node_start(NODE))
25948cf9467Sjsg
rb_to_hole_size(struct rb_node * rb)26048cf9467Sjsg static u64 rb_to_hole_size(struct rb_node *rb)
261746fbbdbSjsg {
26248cf9467Sjsg return rb_entry(rb, struct drm_mm_node, rb_hole_size)->hole_size;
26348cf9467Sjsg }
264746fbbdbSjsg
insert_hole_size(struct rb_root_cached * root,struct drm_mm_node * node)26548cf9467Sjsg static void insert_hole_size(struct rb_root_cached *root,
26648cf9467Sjsg struct drm_mm_node *node)
26748cf9467Sjsg {
26848cf9467Sjsg struct rb_node **link = &root->rb_root.rb_node, *rb = NULL;
26948cf9467Sjsg u64 x = node->hole_size;
27048cf9467Sjsg bool first = true;
271746fbbdbSjsg
27248cf9467Sjsg while (*link) {
27348cf9467Sjsg rb = *link;
27448cf9467Sjsg if (x > rb_to_hole_size(rb)) {
27548cf9467Sjsg link = &rb->rb_left;
27648cf9467Sjsg } else {
27748cf9467Sjsg link = &rb->rb_right;
27848cf9467Sjsg first = false;
2794e088e8fSjsg }
2803253c27bSkettenis }
2813253c27bSkettenis
28248cf9467Sjsg rb_link_node(&node->rb_hole_size, rb, link);
28348cf9467Sjsg rb_insert_color_cached(&node->rb_hole_size, root, first);
2844e088e8fSjsg }
285746fbbdbSjsg
add_hole(struct drm_mm_node * node)28648cf9467Sjsg static void add_hole(struct drm_mm_node *node)
28748cf9467Sjsg {
28848cf9467Sjsg struct drm_mm *mm = node->mm;
289746fbbdbSjsg
29048cf9467Sjsg node->hole_size =
29148cf9467Sjsg __drm_mm_hole_node_end(node) - __drm_mm_hole_node_start(node);
29248cf9467Sjsg DRM_MM_BUG_ON(!drm_mm_hole_follows(node));
293746fbbdbSjsg
29448cf9467Sjsg insert_hole_size(&mm->holes_size, node);
295*9e16f3f2Sjsg DRM_RB_INSERT(mm->holes_addr, rb_hole_addr, HOLE_ADDR);
2967f4dd379Sjsg
297746fbbdbSjsg list_add(&node->hole_stack, &mm->hole_stack);
298746fbbdbSjsg }
2997f4dd379Sjsg
rm_hole(struct drm_mm_node * node)30048cf9467Sjsg static void rm_hole(struct drm_mm_node *node)
30148cf9467Sjsg {
30248cf9467Sjsg DRM_MM_BUG_ON(!drm_mm_hole_follows(node));
30348cf9467Sjsg
30448cf9467Sjsg list_del(&node->hole_stack);
30548cf9467Sjsg rb_erase_cached(&node->rb_hole_size, &node->mm->holes_size);
30648cf9467Sjsg rb_erase(&node->rb_hole_addr, &node->mm->holes_addr);
30748cf9467Sjsg node->hole_size = 0;
30848cf9467Sjsg
30948cf9467Sjsg DRM_MM_BUG_ON(drm_mm_hole_follows(node));
31048cf9467Sjsg }
31148cf9467Sjsg
rb_hole_size_to_node(struct rb_node * rb)31248cf9467Sjsg static inline struct drm_mm_node *rb_hole_size_to_node(struct rb_node *rb)
31348cf9467Sjsg {
31448cf9467Sjsg return rb_entry_safe(rb, struct drm_mm_node, rb_hole_size);
31548cf9467Sjsg }
31648cf9467Sjsg
rb_hole_addr_to_node(struct rb_node * rb)31748cf9467Sjsg static inline struct drm_mm_node *rb_hole_addr_to_node(struct rb_node *rb)
31848cf9467Sjsg {
31948cf9467Sjsg return rb_entry_safe(rb, struct drm_mm_node, rb_hole_addr);
32048cf9467Sjsg }
32148cf9467Sjsg
rb_hole_size(struct rb_node * rb)322*9e16f3f2Sjsg static inline u64 rb_hole_size(struct rb_node *rb)
323*9e16f3f2Sjsg {
324*9e16f3f2Sjsg return rb_entry(rb, struct drm_mm_node, rb_hole_size)->hole_size;
325*9e16f3f2Sjsg }
326*9e16f3f2Sjsg
best_hole(struct drm_mm * mm,u64 size)32748cf9467Sjsg static struct drm_mm_node *best_hole(struct drm_mm *mm, u64 size)
32848cf9467Sjsg {
32948cf9467Sjsg struct rb_node *rb = mm->holes_size.rb_root.rb_node;
33048cf9467Sjsg struct drm_mm_node *best = NULL;
33148cf9467Sjsg
33248cf9467Sjsg do {
33348cf9467Sjsg struct drm_mm_node *node =
33448cf9467Sjsg rb_entry(rb, struct drm_mm_node, rb_hole_size);
33548cf9467Sjsg
33648cf9467Sjsg if (size <= node->hole_size) {
33748cf9467Sjsg best = node;
33848cf9467Sjsg rb = rb->rb_right;
33948cf9467Sjsg } else {
34048cf9467Sjsg rb = rb->rb_left;
34148cf9467Sjsg }
34248cf9467Sjsg } while (rb);
34348cf9467Sjsg
34448cf9467Sjsg return best;
34548cf9467Sjsg }
34648cf9467Sjsg
find_hole(struct drm_mm * mm,u64 addr)347*9e16f3f2Sjsg static struct drm_mm_node *find_hole(struct drm_mm *mm, u64 addr)
34848cf9467Sjsg {
34948cf9467Sjsg struct rb_node *rb = mm->holes_addr.rb_node;
35048cf9467Sjsg struct drm_mm_node *node = NULL;
35148cf9467Sjsg
35248cf9467Sjsg while (rb) {
35348cf9467Sjsg u64 hole_start;
35448cf9467Sjsg
35548cf9467Sjsg node = rb_hole_addr_to_node(rb);
35648cf9467Sjsg hole_start = __drm_mm_hole_node_start(node);
35748cf9467Sjsg
35848cf9467Sjsg if (addr < hole_start)
35948cf9467Sjsg rb = node->rb_hole_addr.rb_left;
36048cf9467Sjsg else if (addr > hole_start + node->hole_size)
36148cf9467Sjsg rb = node->rb_hole_addr.rb_right;
36248cf9467Sjsg else
36348cf9467Sjsg break;
36448cf9467Sjsg }
36548cf9467Sjsg
36648cf9467Sjsg return node;
36748cf9467Sjsg }
36848cf9467Sjsg
36948cf9467Sjsg static struct drm_mm_node *
first_hole(struct drm_mm * mm,u64 start,u64 end,u64 size,enum drm_mm_insert_mode mode)37048cf9467Sjsg first_hole(struct drm_mm *mm,
37148cf9467Sjsg u64 start, u64 end, u64 size,
37248cf9467Sjsg enum drm_mm_insert_mode mode)
37348cf9467Sjsg {
37448cf9467Sjsg switch (mode) {
37548cf9467Sjsg default:
37648cf9467Sjsg case DRM_MM_INSERT_BEST:
37748cf9467Sjsg return best_hole(mm, size);
37848cf9467Sjsg
37948cf9467Sjsg case DRM_MM_INSERT_LOW:
380*9e16f3f2Sjsg return find_hole(mm, start);
38148cf9467Sjsg
38248cf9467Sjsg case DRM_MM_INSERT_HIGH:
383*9e16f3f2Sjsg return find_hole(mm, end);
38448cf9467Sjsg
38548cf9467Sjsg case DRM_MM_INSERT_EVICT:
38648cf9467Sjsg return list_first_entry_or_null(&mm->hole_stack,
38748cf9467Sjsg struct drm_mm_node,
38848cf9467Sjsg hole_stack);
38948cf9467Sjsg }
39048cf9467Sjsg }
39148cf9467Sjsg
39248cf9467Sjsg static struct drm_mm_node *
next_hole(struct drm_mm * mm,struct drm_mm_node * node,enum drm_mm_insert_mode mode)39348cf9467Sjsg next_hole(struct drm_mm *mm,
39448cf9467Sjsg struct drm_mm_node *node,
39548cf9467Sjsg enum drm_mm_insert_mode mode)
39648cf9467Sjsg {
39748cf9467Sjsg switch (mode) {
39848cf9467Sjsg default:
39948cf9467Sjsg case DRM_MM_INSERT_BEST:
40048cf9467Sjsg return rb_hole_size_to_node(rb_prev(&node->rb_hole_size));
40148cf9467Sjsg
40248cf9467Sjsg case DRM_MM_INSERT_LOW:
403*9e16f3f2Sjsg return rb_hole_addr_to_node(rb_next(&node->rb_hole_addr));
40448cf9467Sjsg
40548cf9467Sjsg case DRM_MM_INSERT_HIGH:
406*9e16f3f2Sjsg return rb_hole_addr_to_node(rb_prev(&node->rb_hole_addr));
40748cf9467Sjsg
40848cf9467Sjsg case DRM_MM_INSERT_EVICT:
40948cf9467Sjsg node = list_next_entry(node, hole_stack);
41048cf9467Sjsg return &node->hole_stack == &mm->hole_stack ? NULL : node;
41148cf9467Sjsg }
412746fbbdbSjsg }
413746fbbdbSjsg
4143253c27bSkettenis /**
4153253c27bSkettenis * drm_mm_reserve_node - insert an pre-initialized node
4163253c27bSkettenis * @mm: drm_mm allocator to insert @node into
4173253c27bSkettenis * @node: drm_mm_node to insert
4183253c27bSkettenis *
4197f4dd379Sjsg * This functions inserts an already set-up &drm_mm_node into the allocator,
4207f4dd379Sjsg * meaning that start, size and color must be set by the caller. All other
4217f4dd379Sjsg * fields must be cleared to 0. This is useful to initialize the allocator with
4227f4dd379Sjsg * preallocated objects which must be set-up before the range allocator can be
4237f4dd379Sjsg * set-up, e.g. when taking over a firmware framebuffer.
4243253c27bSkettenis *
4253253c27bSkettenis * Returns:
4263253c27bSkettenis * 0 on success, -ENOSPC if there's no hole where @node is.
4273253c27bSkettenis */
drm_mm_reserve_node(struct drm_mm * mm,struct drm_mm_node * node)428e1001332Skettenis int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
429e1001332Skettenis {
430e1001332Skettenis struct drm_mm_node *hole;
4317f4dd379Sjsg u64 hole_start, hole_end;
4327f4dd379Sjsg u64 adj_start, adj_end;
43348cf9467Sjsg u64 end;
434e1001332Skettenis
4353253c27bSkettenis end = node->start + node->size;
4367f4dd379Sjsg if (unlikely(end <= node->start))
4377f4dd379Sjsg return -ENOSPC;
4383253c27bSkettenis
439e1001332Skettenis /* Find the relevant hole to add our node to */
440*9e16f3f2Sjsg hole = find_hole(mm, node->start);
44148cf9467Sjsg if (!hole)
4427f4dd379Sjsg return -ENOSPC;
4437f4dd379Sjsg
4447f4dd379Sjsg adj_start = hole_start = __drm_mm_hole_node_start(hole);
44548cf9467Sjsg adj_end = hole_end = hole_start + hole->hole_size;
4467f4dd379Sjsg
4477f4dd379Sjsg if (mm->color_adjust)
4487f4dd379Sjsg mm->color_adjust(hole, node->color, &adj_start, &adj_end);
4497f4dd379Sjsg
4507f4dd379Sjsg if (adj_start > node->start || adj_end < end)
4517f4dd379Sjsg return -ENOSPC;
452e1001332Skettenis
453e1001332Skettenis node->mm = mm;
454e1001332Skettenis
45548cf9467Sjsg __set_bit(DRM_MM_NODE_ALLOCATED_BIT, &node->flags);
456e1001332Skettenis list_add(&node->node_list, &hole->node_list);
4577f4dd379Sjsg drm_mm_interval_tree_add_node(hole, node);
45848cf9467Sjsg node->hole_size = 0;
4597f4dd379Sjsg
46048cf9467Sjsg rm_hole(hole);
46148cf9467Sjsg if (node->start > hole_start)
46248cf9467Sjsg add_hole(hole);
46348cf9467Sjsg if (end < hole_end)
46448cf9467Sjsg add_hole(node);
465e1001332Skettenis
4667f4dd379Sjsg save_stack(node);
4677f4dd379Sjsg return 0;
468e1001332Skettenis }
469e1001332Skettenis EXPORT_SYMBOL(drm_mm_reserve_node);
470e1001332Skettenis
rb_to_hole_size_or_zero(struct rb_node * rb)47148cf9467Sjsg static u64 rb_to_hole_size_or_zero(struct rb_node *rb)
47248cf9467Sjsg {
47348cf9467Sjsg return rb ? rb_to_hole_size(rb) : 0;
47448cf9467Sjsg }
47548cf9467Sjsg
4764e088e8fSjsg /**
47748cf9467Sjsg * drm_mm_insert_node_in_range - ranged search for space and insert @node
4783253c27bSkettenis * @mm: drm_mm to allocate from
4793253c27bSkettenis * @node: preallocate node to insert
4803253c27bSkettenis * @size: size of the allocation
4813253c27bSkettenis * @alignment: alignment of the allocation
4823253c27bSkettenis * @color: opaque tag value to use for this node
48348cf9467Sjsg * @range_start: start of the allowed range for this node
48448cf9467Sjsg * @range_end: end of the allowed range for this node
48548cf9467Sjsg * @mode: fine-tune the allocation search and placement
4863253c27bSkettenis *
4877f4dd379Sjsg * The preallocated @node must be cleared to 0.
4883253c27bSkettenis *
4893253c27bSkettenis * Returns:
4903253c27bSkettenis * 0 on success, -ENOSPC if there's no suitable hole.
4914e088e8fSjsg */
drm_mm_insert_node_in_range(struct drm_mm * const mm,struct drm_mm_node * const node,u64 size,u64 alignment,unsigned long color,u64 range_start,u64 range_end,enum drm_mm_insert_mode mode)49248cf9467Sjsg int drm_mm_insert_node_in_range(struct drm_mm * const mm,
49348cf9467Sjsg struct drm_mm_node * const node,
4947f4dd379Sjsg u64 size, u64 alignment,
4953253c27bSkettenis unsigned long color,
49648cf9467Sjsg u64 range_start, u64 range_end,
49748cf9467Sjsg enum drm_mm_insert_mode mode)
4984e088e8fSjsg {
49948cf9467Sjsg struct drm_mm_node *hole;
50048cf9467Sjsg u64 remainder_mask;
50148cf9467Sjsg bool once;
5024e088e8fSjsg
50348cf9467Sjsg DRM_MM_BUG_ON(range_start > range_end);
5047f4dd379Sjsg
50548cf9467Sjsg if (unlikely(size == 0 || range_end - range_start < size))
5064e088e8fSjsg return -ENOSPC;
5074e088e8fSjsg
50848cf9467Sjsg if (rb_to_hole_size_or_zero(rb_first_cached(&mm->holes_size)) < size)
50948cf9467Sjsg return -ENOSPC;
51048cf9467Sjsg
51148cf9467Sjsg if (alignment <= 1)
51248cf9467Sjsg alignment = 0;
51348cf9467Sjsg
51448cf9467Sjsg once = mode & DRM_MM_INSERT_ONCE;
51548cf9467Sjsg mode &= ~DRM_MM_INSERT_ONCE;
51648cf9467Sjsg
51748cf9467Sjsg remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0;
51848cf9467Sjsg for (hole = first_hole(mm, range_start, range_end, size, mode);
51948cf9467Sjsg hole;
520*9e16f3f2Sjsg hole = once ? NULL : next_hole(mm, hole, mode)) {
52148cf9467Sjsg u64 hole_start = __drm_mm_hole_node_start(hole);
52248cf9467Sjsg u64 hole_end = hole_start + hole->hole_size;
52348cf9467Sjsg u64 adj_start, adj_end;
52448cf9467Sjsg u64 col_start, col_end;
52548cf9467Sjsg
52648cf9467Sjsg if (mode == DRM_MM_INSERT_LOW && hole_start >= range_end)
52748cf9467Sjsg break;
52848cf9467Sjsg
52948cf9467Sjsg if (mode == DRM_MM_INSERT_HIGH && hole_end <= range_start)
53048cf9467Sjsg break;
53148cf9467Sjsg
53248cf9467Sjsg col_start = hole_start;
53348cf9467Sjsg col_end = hole_end;
53448cf9467Sjsg if (mm->color_adjust)
53548cf9467Sjsg mm->color_adjust(hole, color, &col_start, &col_end);
53648cf9467Sjsg
53748cf9467Sjsg adj_start = max(col_start, range_start);
53848cf9467Sjsg adj_end = min(col_end, range_end);
53948cf9467Sjsg
54048cf9467Sjsg if (adj_end <= adj_start || adj_end - adj_start < size)
54148cf9467Sjsg continue;
54248cf9467Sjsg
54348cf9467Sjsg if (mode == DRM_MM_INSERT_HIGH)
54448cf9467Sjsg adj_start = adj_end - size;
54548cf9467Sjsg
54648cf9467Sjsg if (alignment) {
54748cf9467Sjsg u64 rem;
54848cf9467Sjsg
54948cf9467Sjsg if (likely(remainder_mask))
55048cf9467Sjsg rem = adj_start & remainder_mask;
55148cf9467Sjsg else
55248cf9467Sjsg div64_u64_rem(adj_start, alignment, &rem);
55348cf9467Sjsg if (rem) {
55448cf9467Sjsg adj_start -= rem;
55548cf9467Sjsg if (mode != DRM_MM_INSERT_HIGH)
55648cf9467Sjsg adj_start += alignment;
55748cf9467Sjsg
55848cf9467Sjsg if (adj_start < max(col_start, range_start) ||
55948cf9467Sjsg min(col_end, range_end) - adj_start < size)
56048cf9467Sjsg continue;
56148cf9467Sjsg
56248cf9467Sjsg if (adj_end <= adj_start ||
56348cf9467Sjsg adj_end - adj_start < size)
56448cf9467Sjsg continue;
56548cf9467Sjsg }
56648cf9467Sjsg }
56748cf9467Sjsg
56848cf9467Sjsg node->mm = mm;
56948cf9467Sjsg node->size = size;
57048cf9467Sjsg node->start = adj_start;
57148cf9467Sjsg node->color = color;
57248cf9467Sjsg node->hole_size = 0;
57348cf9467Sjsg
57448cf9467Sjsg __set_bit(DRM_MM_NODE_ALLOCATED_BIT, &node->flags);
57548cf9467Sjsg list_add(&node->node_list, &hole->node_list);
57648cf9467Sjsg drm_mm_interval_tree_add_node(hole, node);
57748cf9467Sjsg
57848cf9467Sjsg rm_hole(hole);
57948cf9467Sjsg if (adj_start > hole_start)
58048cf9467Sjsg add_hole(hole);
58148cf9467Sjsg if (adj_start + size < hole_end)
58248cf9467Sjsg add_hole(node);
58348cf9467Sjsg
58448cf9467Sjsg save_stack(node);
5854e088e8fSjsg return 0;
5864e088e8fSjsg }
58748cf9467Sjsg
58848cf9467Sjsg return -ENOSPC;
58948cf9467Sjsg }
59048cf9467Sjsg EXPORT_SYMBOL(drm_mm_insert_node_in_range);
59148cf9467Sjsg
drm_mm_node_scanned_block(const struct drm_mm_node * node)59248cf9467Sjsg static inline bool drm_mm_node_scanned_block(const struct drm_mm_node *node)
59348cf9467Sjsg {
59448cf9467Sjsg return test_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags);
59548cf9467Sjsg }
596746fbbdbSjsg
5974e088e8fSjsg /**
5983253c27bSkettenis * drm_mm_remove_node - Remove a memory node from the allocator.
5993253c27bSkettenis * @node: drm_mm_node to remove
6003253c27bSkettenis *
6013253c27bSkettenis * This just removes a node from its drm_mm allocator. The node does not need to
6023253c27bSkettenis * be cleared again before it can be re-inserted into this or any other drm_mm
6037f4dd379Sjsg * allocator. It is a bug to call this function on a unallocated node.
6044e088e8fSjsg */
drm_mm_remove_node(struct drm_mm_node * node)605746fbbdbSjsg void drm_mm_remove_node(struct drm_mm_node *node)
606746fbbdbSjsg {
607746fbbdbSjsg struct drm_mm *mm = node->mm;
608746fbbdbSjsg struct drm_mm_node *prev_node;
609746fbbdbSjsg
61048cf9467Sjsg DRM_MM_BUG_ON(!drm_mm_node_allocated(node));
61148cf9467Sjsg DRM_MM_BUG_ON(drm_mm_node_scanned_block(node));
612746fbbdbSjsg
61348cf9467Sjsg prev_node = list_prev_entry(node, node_list);
614746fbbdbSjsg
61548cf9467Sjsg if (drm_mm_hole_follows(node))
61648cf9467Sjsg rm_hole(node);
617e1001332Skettenis
6187f4dd379Sjsg drm_mm_interval_tree_remove(node, &mm->interval_tree);
619746fbbdbSjsg list_del(&node->node_list);
62048cf9467Sjsg
62148cf9467Sjsg if (drm_mm_hole_follows(prev_node))
62248cf9467Sjsg rm_hole(prev_node);
62348cf9467Sjsg add_hole(prev_node);
62448cf9467Sjsg
62548cf9467Sjsg clear_bit_unlock(DRM_MM_NODE_ALLOCATED_BIT, &node->flags);
626746fbbdbSjsg }
6274e088e8fSjsg EXPORT_SYMBOL(drm_mm_remove_node);
628746fbbdbSjsg
6294e088e8fSjsg /**
6303253c27bSkettenis * drm_mm_replace_node - move an allocation from @old to @new
6313253c27bSkettenis * @old: drm_mm_node to remove from the allocator
6323253c27bSkettenis * @new: drm_mm_node which should inherit @old's allocation
6333253c27bSkettenis *
6343253c27bSkettenis * This is useful for when drivers embed the drm_mm_node structure and hence
6353253c27bSkettenis * can't move allocations by reassigning pointers. It's a combination of remove
6363253c27bSkettenis * and insert with the guarantee that the allocation start will match.
6374e088e8fSjsg */
drm_mm_replace_node(struct drm_mm_node * old,struct drm_mm_node * new)638746fbbdbSjsg void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
639746fbbdbSjsg {
64048cf9467Sjsg struct drm_mm *mm = old->mm;
6417f4dd379Sjsg
64248cf9467Sjsg DRM_MM_BUG_ON(!drm_mm_node_allocated(old));
64348cf9467Sjsg
64448cf9467Sjsg *new = *old;
64548cf9467Sjsg
64648cf9467Sjsg __set_bit(DRM_MM_NODE_ALLOCATED_BIT, &new->flags);
647746fbbdbSjsg list_replace(&old->node_list, &new->node_list);
64848cf9467Sjsg rb_replace_node_cached(&old->rb, &new->rb, &mm->interval_tree);
649746fbbdbSjsg
65048cf9467Sjsg if (drm_mm_hole_follows(old)) {
65148cf9467Sjsg list_replace(&old->hole_stack, &new->hole_stack);
65248cf9467Sjsg rb_replace_node_cached(&old->rb_hole_size,
65348cf9467Sjsg &new->rb_hole_size,
65448cf9467Sjsg &mm->holes_size);
65548cf9467Sjsg rb_replace_node(&old->rb_hole_addr,
65648cf9467Sjsg &new->rb_hole_addr,
65748cf9467Sjsg &mm->holes_addr);
65848cf9467Sjsg }
65948cf9467Sjsg
66048cf9467Sjsg clear_bit_unlock(DRM_MM_NODE_ALLOCATED_BIT, &old->flags);
661746fbbdbSjsg }
6624e088e8fSjsg EXPORT_SYMBOL(drm_mm_replace_node);
663746fbbdbSjsg
6644e088e8fSjsg /**
6657f4dd379Sjsg * DOC: lru scan roster
6663253c27bSkettenis *
6673253c27bSkettenis * Very often GPUs need to have continuous allocations for a given object. When
6683253c27bSkettenis * evicting objects to make space for a new one it is therefore not most
6693253c27bSkettenis * efficient when we simply start to select all objects from the tail of an LRU
6703253c27bSkettenis * until there's a suitable hole: Especially for big objects or nodes that
6713253c27bSkettenis * otherwise have special allocation constraints there's a good chance we evict
6727f4dd379Sjsg * lots of (smaller) objects unnecessarily.
6733253c27bSkettenis *
6743253c27bSkettenis * The DRM range allocator supports this use-case through the scanning
6753253c27bSkettenis * interfaces. First a scan operation needs to be initialized with
6767f4dd379Sjsg * drm_mm_scan_init() or drm_mm_scan_init_with_range(). The driver adds
6777f4dd379Sjsg * objects to the roster, probably by walking an LRU list, but this can be
6787f4dd379Sjsg * freely implemented. Eviction candiates are added using
6797f4dd379Sjsg * drm_mm_scan_add_block() until a suitable hole is found or there are no
6807f4dd379Sjsg * further evictable objects. Eviction roster metadata is tracked in &struct
6817f4dd379Sjsg * drm_mm_scan.
6823253c27bSkettenis *
6837f4dd379Sjsg * The driver must walk through all objects again in exactly the reverse
6843253c27bSkettenis * order to restore the allocator state. Note that while the allocator is used
6853253c27bSkettenis * in the scan mode no other operation is allowed.
6863253c27bSkettenis *
6877f4dd379Sjsg * Finally the driver evicts all objects selected (drm_mm_scan_remove_block()
6887f4dd379Sjsg * reported true) in the scan, and any overlapping nodes after color adjustment
6897f4dd379Sjsg * (drm_mm_scan_color_evict()). Adding and removing an object is O(1), and
6907f4dd379Sjsg * since freeing a node is also O(1) the overall complexity is
6917f4dd379Sjsg * O(scanned_objects). So like the free stack which needs to be walked before a
6927f4dd379Sjsg * scan operation even begins this is linear in the number of objects. It
6937f4dd379Sjsg * doesn't seem to hurt too badly.
6943253c27bSkettenis */
6953253c27bSkettenis
6963253c27bSkettenis /**
6977f4dd379Sjsg * drm_mm_scan_init_with_range - initialize range-restricted lru scanning
6987f4dd379Sjsg * @scan: scan state
6993253c27bSkettenis * @mm: drm_mm to scan
7003253c27bSkettenis * @size: size of the allocation
7013253c27bSkettenis * @alignment: alignment of the allocation
7023253c27bSkettenis * @color: opaque tag value to use for the allocation
7033253c27bSkettenis * @start: start of the allowed range for the allocation
7043253c27bSkettenis * @end: end of the allowed range for the allocation
70548cf9467Sjsg * @mode: fine-tune the allocation search and placement
7064e088e8fSjsg *
7074e088e8fSjsg * This simply sets up the scanning routines with the parameters for the desired
7087f4dd379Sjsg * hole.
7094e088e8fSjsg *
7103253c27bSkettenis * Warning:
7113253c27bSkettenis * As long as the scan list is non-empty, no other operations than
7124e088e8fSjsg * adding/removing nodes to/from the scan list are allowed.
7134e088e8fSjsg */
drm_mm_scan_init_with_range(struct drm_mm_scan * scan,struct drm_mm * mm,u64 size,u64 alignment,unsigned long color,u64 start,u64 end,enum drm_mm_insert_mode mode)7147f4dd379Sjsg void drm_mm_scan_init_with_range(struct drm_mm_scan *scan,
7157f4dd379Sjsg struct drm_mm *mm,
7163253c27bSkettenis u64 size,
7177f4dd379Sjsg u64 alignment,
7184e088e8fSjsg unsigned long color,
7193253c27bSkettenis u64 start,
7207f4dd379Sjsg u64 end,
72148cf9467Sjsg enum drm_mm_insert_mode mode)
722746fbbdbSjsg {
7237f4dd379Sjsg DRM_MM_BUG_ON(start >= end);
7247f4dd379Sjsg DRM_MM_BUG_ON(!size || size > end - start);
7257f4dd379Sjsg DRM_MM_BUG_ON(mm->scan_active);
7267f4dd379Sjsg
7277f4dd379Sjsg scan->mm = mm;
7287f4dd379Sjsg
7297f4dd379Sjsg if (alignment <= 1)
7307f4dd379Sjsg alignment = 0;
7317f4dd379Sjsg
7327f4dd379Sjsg scan->color = color;
7337f4dd379Sjsg scan->alignment = alignment;
7347f4dd379Sjsg scan->remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0;
7357f4dd379Sjsg scan->size = size;
73648cf9467Sjsg scan->mode = mode;
7377f4dd379Sjsg
7387f4dd379Sjsg DRM_MM_BUG_ON(end <= start);
7397f4dd379Sjsg scan->range_start = start;
7407f4dd379Sjsg scan->range_end = end;
7417f4dd379Sjsg
7427f4dd379Sjsg scan->hit_start = U64_MAX;
7437f4dd379Sjsg scan->hit_end = 0;
744746fbbdbSjsg }
7457f4dd379Sjsg EXPORT_SYMBOL(drm_mm_scan_init_with_range);
746746fbbdbSjsg
7474e088e8fSjsg /**
7483253c27bSkettenis * drm_mm_scan_add_block - add a node to the scan list
7497f4dd379Sjsg * @scan: the active drm_mm scanner
7503253c27bSkettenis * @node: drm_mm_node to add
7513253c27bSkettenis *
7524e088e8fSjsg * Add a node to the scan list that might be freed to make space for the desired
7534e088e8fSjsg * hole.
7544e088e8fSjsg *
7553253c27bSkettenis * Returns:
7563253c27bSkettenis * True if a hole has been found, false otherwise.
7574e088e8fSjsg */
drm_mm_scan_add_block(struct drm_mm_scan * scan,struct drm_mm_node * node)7587f4dd379Sjsg bool drm_mm_scan_add_block(struct drm_mm_scan *scan,
7597f4dd379Sjsg struct drm_mm_node *node)
760746fbbdbSjsg {
7617f4dd379Sjsg struct drm_mm *mm = scan->mm;
7627f4dd379Sjsg struct drm_mm_node *hole;
7633253c27bSkettenis u64 hole_start, hole_end;
7647f4dd379Sjsg u64 col_start, col_end;
7653253c27bSkettenis u64 adj_start, adj_end;
766746fbbdbSjsg
7677f4dd379Sjsg DRM_MM_BUG_ON(node->mm != mm);
76848cf9467Sjsg DRM_MM_BUG_ON(!drm_mm_node_allocated(node));
76948cf9467Sjsg DRM_MM_BUG_ON(drm_mm_node_scanned_block(node));
77048cf9467Sjsg __set_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags);
7717f4dd379Sjsg mm->scan_active++;
772746fbbdbSjsg
7737f4dd379Sjsg /* Remove this block from the node_list so that we enlarge the hole
7747f4dd379Sjsg * (distance between the end of our previous node and the start of
7757f4dd379Sjsg * or next), without poisoning the link so that we can restore it
7767f4dd379Sjsg * later in drm_mm_scan_remove_block().
7777f4dd379Sjsg */
7787f4dd379Sjsg hole = list_prev_entry(node, node_list);
7797f4dd379Sjsg DRM_MM_BUG_ON(list_next_entry(hole, node_list) != node);
7807f4dd379Sjsg __list_del_entry(&node->node_list);
781746fbbdbSjsg
7827f4dd379Sjsg hole_start = __drm_mm_hole_node_start(hole);
7837f4dd379Sjsg hole_end = __drm_mm_hole_node_end(hole);
784746fbbdbSjsg
7857f4dd379Sjsg col_start = hole_start;
7867f4dd379Sjsg col_end = hole_end;
7874e088e8fSjsg if (mm->color_adjust)
7887f4dd379Sjsg mm->color_adjust(hole, scan->color, &col_start, &col_end);
7894e088e8fSjsg
7907f4dd379Sjsg adj_start = max(col_start, scan->range_start);
7917f4dd379Sjsg adj_end = min(col_end, scan->range_end);
7927f4dd379Sjsg if (adj_end <= adj_start || adj_end - adj_start < scan->size)
7937f4dd379Sjsg return false;
7947f4dd379Sjsg
79548cf9467Sjsg if (scan->mode == DRM_MM_INSERT_HIGH)
7967f4dd379Sjsg adj_start = adj_end - scan->size;
7977f4dd379Sjsg
7987f4dd379Sjsg if (scan->alignment) {
7997f4dd379Sjsg u64 rem;
8007f4dd379Sjsg
8017f4dd379Sjsg if (likely(scan->remainder_mask))
8027f4dd379Sjsg rem = adj_start & scan->remainder_mask;
8037f4dd379Sjsg else
8047f4dd379Sjsg div64_u64_rem(adj_start, scan->alignment, &rem);
8057f4dd379Sjsg if (rem) {
8067f4dd379Sjsg adj_start -= rem;
80748cf9467Sjsg if (scan->mode != DRM_MM_INSERT_HIGH)
8087f4dd379Sjsg adj_start += scan->alignment;
8097f4dd379Sjsg if (adj_start < max(col_start, scan->range_start) ||
8107f4dd379Sjsg min(col_end, scan->range_end) - adj_start < scan->size)
8117f4dd379Sjsg return false;
8127f4dd379Sjsg
8137f4dd379Sjsg if (adj_end <= adj_start ||
8147f4dd379Sjsg adj_end - adj_start < scan->size)
8157f4dd379Sjsg return false;
8167f4dd379Sjsg }
817746fbbdbSjsg }
818746fbbdbSjsg
8197f4dd379Sjsg scan->hit_start = adj_start;
8207f4dd379Sjsg scan->hit_end = adj_start + scan->size;
8217f4dd379Sjsg
8227f4dd379Sjsg DRM_MM_BUG_ON(scan->hit_start >= scan->hit_end);
8237f4dd379Sjsg DRM_MM_BUG_ON(scan->hit_start < hole_start);
8247f4dd379Sjsg DRM_MM_BUG_ON(scan->hit_end > hole_end);
8257f4dd379Sjsg
8267f4dd379Sjsg return true;
827746fbbdbSjsg }
8284e088e8fSjsg EXPORT_SYMBOL(drm_mm_scan_add_block);
829746fbbdbSjsg
8304e088e8fSjsg /**
8313253c27bSkettenis * drm_mm_scan_remove_block - remove a node from the scan list
8327f4dd379Sjsg * @scan: the active drm_mm scanner
8333253c27bSkettenis * @node: drm_mm_node to remove
8344e088e8fSjsg *
8357f4dd379Sjsg * Nodes **must** be removed in exactly the reverse order from the scan list as
8367f4dd379Sjsg * they have been added (e.g. using list_add() as they are added and then
8377f4dd379Sjsg * list_for_each() over that eviction list to remove), otherwise the internal
8387f4dd379Sjsg * state of the memory manager will be corrupted.
8394e088e8fSjsg *
8404e088e8fSjsg * When the scan list is empty, the selected memory nodes can be freed. An
8417f4dd379Sjsg * immediately following drm_mm_insert_node_in_range_generic() or one of the
8427f4dd379Sjsg * simpler versions of that function with !DRM_MM_SEARCH_BEST will then return
84348cf9467Sjsg * the just freed block (because it's at the top of the free_stack list).
8444e088e8fSjsg *
8453253c27bSkettenis * Returns:
8463253c27bSkettenis * True if this block should be evicted, false otherwise. Will always
8473253c27bSkettenis * return false when no hole has been found.
8484e088e8fSjsg */
drm_mm_scan_remove_block(struct drm_mm_scan * scan,struct drm_mm_node * node)8497f4dd379Sjsg bool drm_mm_scan_remove_block(struct drm_mm_scan *scan,
8507f4dd379Sjsg struct drm_mm_node *node)
851746fbbdbSjsg {
852746fbbdbSjsg struct drm_mm_node *prev_node;
853746fbbdbSjsg
8547f4dd379Sjsg DRM_MM_BUG_ON(node->mm != scan->mm);
85548cf9467Sjsg DRM_MM_BUG_ON(!drm_mm_node_scanned_block(node));
85648cf9467Sjsg __clear_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags);
857746fbbdbSjsg
8587f4dd379Sjsg DRM_MM_BUG_ON(!node->mm->scan_active);
8597f4dd379Sjsg node->mm->scan_active--;
860746fbbdbSjsg
8617f4dd379Sjsg /* During drm_mm_scan_add_block() we decoupled this node leaving
8627f4dd379Sjsg * its pointers intact. Now that the caller is walking back along
8637f4dd379Sjsg * the eviction list we can restore this block into its rightful
8647f4dd379Sjsg * place on the full node_list. To confirm that the caller is walking
8657f4dd379Sjsg * backwards correctly we check that prev_node->next == node->next,
8667f4dd379Sjsg * i.e. both believe the same node should be on the other side of the
8677f4dd379Sjsg * hole.
8687f4dd379Sjsg */
8697f4dd379Sjsg prev_node = list_prev_entry(node, node_list);
8707f4dd379Sjsg DRM_MM_BUG_ON(list_next_entry(prev_node, node_list) !=
8717f4dd379Sjsg list_next_entry(node, node_list));
872746fbbdbSjsg list_add(&node->node_list, &prev_node->node_list);
873746fbbdbSjsg
8747f4dd379Sjsg return (node->start + node->size > scan->hit_start &&
8757f4dd379Sjsg node->start < scan->hit_end);
876746fbbdbSjsg }
8774e088e8fSjsg EXPORT_SYMBOL(drm_mm_scan_remove_block);
878746fbbdbSjsg
8793253c27bSkettenis /**
8807f4dd379Sjsg * drm_mm_scan_color_evict - evict overlapping nodes on either side of hole
8817f4dd379Sjsg * @scan: drm_mm scan with target hole
8827f4dd379Sjsg *
8837f4dd379Sjsg * After completing an eviction scan and removing the selected nodes, we may
8847f4dd379Sjsg * need to remove a few more nodes from either side of the target hole if
8857f4dd379Sjsg * mm.color_adjust is being used.
8863253c27bSkettenis *
8873253c27bSkettenis * Returns:
8887f4dd379Sjsg * A node to evict, or NULL if there are no overlapping nodes.
8893253c27bSkettenis */
drm_mm_scan_color_evict(struct drm_mm_scan * scan)8907f4dd379Sjsg struct drm_mm_node *drm_mm_scan_color_evict(struct drm_mm_scan *scan)
891746fbbdbSjsg {
8927f4dd379Sjsg struct drm_mm *mm = scan->mm;
8937f4dd379Sjsg struct drm_mm_node *hole;
8947f4dd379Sjsg u64 hole_start, hole_end;
895746fbbdbSjsg
8967f4dd379Sjsg DRM_MM_BUG_ON(list_empty(&mm->hole_stack));
8977f4dd379Sjsg
8987f4dd379Sjsg if (!mm->color_adjust)
8997f4dd379Sjsg return NULL;
9007f4dd379Sjsg
90148cf9467Sjsg /*
90248cf9467Sjsg * The hole found during scanning should ideally be the first element
90348cf9467Sjsg * in the hole_stack list, but due to side-effects in the driver it
90448cf9467Sjsg * may not be.
90548cf9467Sjsg */
90648cf9467Sjsg list_for_each_entry(hole, &mm->hole_stack, hole_stack) {
9077f4dd379Sjsg hole_start = __drm_mm_hole_node_start(hole);
90848cf9467Sjsg hole_end = hole_start + hole->hole_size;
90948cf9467Sjsg
91048cf9467Sjsg if (hole_start <= scan->hit_start &&
91148cf9467Sjsg hole_end >= scan->hit_end)
91248cf9467Sjsg break;
91348cf9467Sjsg }
91448cf9467Sjsg
91548cf9467Sjsg /* We should only be called after we found the hole previously */
91648cf9467Sjsg DRM_MM_BUG_ON(&hole->hole_stack == &mm->hole_stack);
91748cf9467Sjsg if (unlikely(&hole->hole_stack == &mm->hole_stack))
91848cf9467Sjsg return NULL;
9197f4dd379Sjsg
9207f4dd379Sjsg DRM_MM_BUG_ON(hole_start > scan->hit_start);
9217f4dd379Sjsg DRM_MM_BUG_ON(hole_end < scan->hit_end);
9227f4dd379Sjsg
9237f4dd379Sjsg mm->color_adjust(hole, scan->color, &hole_start, &hole_end);
9247f4dd379Sjsg if (hole_start > scan->hit_start)
9257f4dd379Sjsg return hole;
9267f4dd379Sjsg if (hole_end < scan->hit_end)
9277f4dd379Sjsg return list_next_entry(hole, node_list);
9287f4dd379Sjsg
9297f4dd379Sjsg return NULL;
930746fbbdbSjsg }
9317f4dd379Sjsg EXPORT_SYMBOL(drm_mm_scan_color_evict);
932746fbbdbSjsg
9333253c27bSkettenis /**
9343253c27bSkettenis * drm_mm_init - initialize a drm-mm allocator
9353253c27bSkettenis * @mm: the drm_mm structure to initialize
9363253c27bSkettenis * @start: start of the range managed by @mm
9373253c27bSkettenis * @size: end of the range managed by @mm
9383253c27bSkettenis *
9393253c27bSkettenis * Note that @mm must be cleared to 0 before calling this function.
9403253c27bSkettenis */
drm_mm_init(struct drm_mm * mm,u64 start,u64 size)9413253c27bSkettenis void drm_mm_init(struct drm_mm *mm, u64 start, u64 size)
942746fbbdbSjsg {
9437f4dd379Sjsg DRM_MM_BUG_ON(start + size <= start);
9447f4dd379Sjsg
94548cf9467Sjsg mm->color_adjust = NULL;
94648cf9467Sjsg
947746fbbdbSjsg INIT_LIST_HEAD(&mm->hole_stack);
94848cf9467Sjsg mm->interval_tree = RB_ROOT_CACHED;
94948cf9467Sjsg mm->holes_size = RB_ROOT_CACHED;
95048cf9467Sjsg mm->holes_addr = RB_ROOT;
951746fbbdbSjsg
9524e088e8fSjsg /* Clever trick to avoid a special case in the free hole tracking. */
953746fbbdbSjsg INIT_LIST_HEAD(&mm->head_node.node_list);
95448cf9467Sjsg mm->head_node.flags = 0;
955746fbbdbSjsg mm->head_node.mm = mm;
956746fbbdbSjsg mm->head_node.start = start + size;
95748cf9467Sjsg mm->head_node.size = -size;
95848cf9467Sjsg add_hole(&mm->head_node);
959746fbbdbSjsg
96048cf9467Sjsg mm->scan_active = 0;
961746fbbdbSjsg }
9624e088e8fSjsg EXPORT_SYMBOL(drm_mm_init);
963746fbbdbSjsg
9643253c27bSkettenis /**
9653253c27bSkettenis * drm_mm_takedown - clean up a drm_mm allocator
9663253c27bSkettenis * @mm: drm_mm allocator to clean up
9673253c27bSkettenis *
9683253c27bSkettenis * Note that it is a bug to call this function on an allocator which is not
9693253c27bSkettenis * clean.
9703253c27bSkettenis */
drm_mm_takedown(struct drm_mm * mm)971746fbbdbSjsg void drm_mm_takedown(struct drm_mm *mm)
972746fbbdbSjsg {
9737f4dd379Sjsg if (WARN(!drm_mm_clean(mm),
9747f4dd379Sjsg "Memory manager not clean during takedown.\n"))
9757f4dd379Sjsg show_leaks(mm);
976746fbbdbSjsg }
9774e088e8fSjsg EXPORT_SYMBOL(drm_mm_takedown);
9784e088e8fSjsg
drm_mm_dump_hole(struct drm_printer * p,const struct drm_mm_node * entry)9797f4dd379Sjsg static u64 drm_mm_dump_hole(struct drm_printer *p, const struct drm_mm_node *entry)
9804e088e8fSjsg {
98148cf9467Sjsg u64 start, size;
9824e088e8fSjsg
98348cf9467Sjsg size = entry->hole_size;
98448cf9467Sjsg if (size) {
98548cf9467Sjsg start = drm_mm_hole_node_start(entry);
98648cf9467Sjsg drm_printf(p, "%#018llx-%#018llx: %llu: free\n",
98748cf9467Sjsg start, start + size, size);
9884e088e8fSjsg }
989e1001332Skettenis
99048cf9467Sjsg return size;
991e1001332Skettenis }
9923253c27bSkettenis /**
9937f4dd379Sjsg * drm_mm_print - print allocator state
9947f4dd379Sjsg * @mm: drm_mm allocator to print
9957f4dd379Sjsg * @p: DRM printer to use
9963253c27bSkettenis */
drm_mm_print(const struct drm_mm * mm,struct drm_printer * p)9977f4dd379Sjsg void drm_mm_print(const struct drm_mm *mm, struct drm_printer *p)
998e1001332Skettenis {
9997f4dd379Sjsg const struct drm_mm_node *entry;
10003253c27bSkettenis u64 total_used = 0, total_free = 0, total = 0;
1001e1001332Skettenis
10027f4dd379Sjsg total_free += drm_mm_dump_hole(p, &mm->head_node);
1003e1001332Skettenis
1004e1001332Skettenis drm_mm_for_each_node(entry, mm) {
10057f4dd379Sjsg drm_printf(p, "%#018llx-%#018llx: %llu: used\n", entry->start,
10063253c27bSkettenis entry->start + entry->size, entry->size);
1007e1001332Skettenis total_used += entry->size;
10087f4dd379Sjsg total_free += drm_mm_dump_hole(p, entry);
10094e088e8fSjsg }
10104e088e8fSjsg total = total_free + total_used;
10114e088e8fSjsg
10127f4dd379Sjsg drm_printf(p, "total: %llu, used %llu free %llu\n", total,
10134e088e8fSjsg total_used, total_free);
10144e088e8fSjsg }
10157f4dd379Sjsg EXPORT_SYMBOL(drm_mm_print);
1016