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