xref: /openbsd/sys/dev/pci/drm/drm_mm.c (revision 9e16f3f2)
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