xref: /dragonfly/sys/dev/drm/drm_mm.c (revision 9edbd4a0)
13f6063ccSDavid Shao /**************************************************************************
23f6063ccSDavid Shao  *
33f6063ccSDavid Shao  * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
43f6063ccSDavid Shao  * All Rights Reserved.
53f6063ccSDavid Shao  *
63f6063ccSDavid Shao  * Permission is hereby granted, free of charge, to any person obtaining a
73f6063ccSDavid Shao  * copy of this software and associated documentation files (the
83f6063ccSDavid Shao  * "Software"), to deal in the Software without restriction, including
93f6063ccSDavid Shao  * without limitation the rights to use, copy, modify, merge, publish,
103f6063ccSDavid Shao  * distribute, sub license, and/or sell copies of the Software, and to
113f6063ccSDavid Shao  * permit persons to whom the Software is furnished to do so, subject to
123f6063ccSDavid Shao  * the following conditions:
133f6063ccSDavid Shao  *
143f6063ccSDavid Shao  * The above copyright notice and this permission notice (including the
153f6063ccSDavid Shao  * next paragraph) shall be included in all copies or substantial portions
163f6063ccSDavid Shao  * of the Software.
173f6063ccSDavid Shao  *
183f6063ccSDavid Shao  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
193f6063ccSDavid Shao  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
203f6063ccSDavid Shao  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
213f6063ccSDavid Shao  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
223f6063ccSDavid Shao  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
233f6063ccSDavid Shao  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
243f6063ccSDavid Shao  * USE OR OTHER DEALINGS IN THE SOFTWARE.
253f6063ccSDavid Shao  *
26ea132f0fSFrançois Tigeot  *
273f6063ccSDavid Shao  **************************************************************************/
283f6063ccSDavid Shao 
293f6063ccSDavid Shao /*
303f6063ccSDavid Shao  * Generic simple memory manager implementation. Intended to be used as a base
313f6063ccSDavid Shao  * class implementation for more advanced memory managers.
323f6063ccSDavid Shao  *
333f6063ccSDavid Shao  * Note that the algorithm used is quite simple and there might be substantial
343f6063ccSDavid Shao  * performance gains if a smarter free list is implemented. Currently it is just an
353f6063ccSDavid Shao  * unordered stack of free regions. This could easily be improved if an RB-tree
363f6063ccSDavid Shao  * is used instead. At least if we expect heavy fragmentation.
373f6063ccSDavid Shao  *
383f6063ccSDavid Shao  * Aligned allocations can also see improvement.
393f6063ccSDavid Shao  *
403f6063ccSDavid Shao  * Authors:
413f6063ccSDavid Shao  * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
423f6063ccSDavid Shao  */
433f6063ccSDavid Shao 
4418e26a6dSFrançois Tigeot #include <drm/drmP.h>
4518e26a6dSFrançois Tigeot #include <drm/drm_mm.h>
46b6fbc077SFrançois Tigeot #include <linux/slab.h>
47*9edbd4a0SFrançois Tigeot #include <linux/seq_file.h>
48ea132f0fSFrançois Tigeot #include <linux/export.h>
493f6063ccSDavid Shao 
503f6063ccSDavid Shao #define MM_UNUSED_TARGET 4
513f6063ccSDavid Shao 
523f6063ccSDavid Shao static struct drm_mm_node *drm_mm_kmalloc(struct drm_mm *mm, int atomic)
533f6063ccSDavid Shao {
543f6063ccSDavid Shao 	struct drm_mm_node *child;
553f6063ccSDavid Shao 
56ea132f0fSFrançois Tigeot 	if (atomic)
57b6fbc077SFrançois Tigeot 		child = kzalloc(sizeof(*child), GFP_ATOMIC);
58ea132f0fSFrançois Tigeot 	else
59b6fbc077SFrançois Tigeot 		child = kzalloc(sizeof(*child), GFP_KERNEL);
603f6063ccSDavid Shao 
613f6063ccSDavid Shao 	if (unlikely(child == NULL)) {
62ea132f0fSFrançois Tigeot 		spin_lock(&mm->unused_lock);
633f6063ccSDavid Shao 		if (list_empty(&mm->unused_nodes))
643f6063ccSDavid Shao 			child = NULL;
653f6063ccSDavid Shao 		else {
663f6063ccSDavid Shao 			child =
673f6063ccSDavid Shao 			    list_entry(mm->unused_nodes.next,
685718399fSFrançois Tigeot 				       struct drm_mm_node, node_list);
695718399fSFrançois Tigeot 			list_del(&child->node_list);
703f6063ccSDavid Shao 			--mm->num_unused;
713f6063ccSDavid Shao 		}
72ea132f0fSFrançois Tigeot 		spin_unlock(&mm->unused_lock);
733f6063ccSDavid Shao 	}
743f6063ccSDavid Shao 	return child;
753f6063ccSDavid Shao }
763f6063ccSDavid Shao 
77ea132f0fSFrançois Tigeot /* drm_mm_pre_get() - pre allocate drm_mm_node structure
78ea132f0fSFrançois Tigeot  * drm_mm:	memory manager struct we are pre-allocating for
79ea132f0fSFrançois Tigeot  *
80ea132f0fSFrançois Tigeot  * Returns 0 on success or -ENOMEM if allocation fails.
81ea132f0fSFrançois Tigeot  */
823f6063ccSDavid Shao int drm_mm_pre_get(struct drm_mm *mm)
833f6063ccSDavid Shao {
843f6063ccSDavid Shao 	struct drm_mm_node *node;
853f6063ccSDavid Shao 
86ea132f0fSFrançois Tigeot 	spin_lock(&mm->unused_lock);
873f6063ccSDavid Shao 	while (mm->num_unused < MM_UNUSED_TARGET) {
88ea132f0fSFrançois Tigeot 		spin_unlock(&mm->unused_lock);
89b6fbc077SFrançois Tigeot 		node = kzalloc(sizeof(*node), GFP_KERNEL);
90ea132f0fSFrançois Tigeot 		spin_lock(&mm->unused_lock);
913f6063ccSDavid Shao 
923f6063ccSDavid Shao 		if (unlikely(node == NULL)) {
933f6063ccSDavid Shao 			int ret = (mm->num_unused < 2) ? -ENOMEM : 0;
94ea132f0fSFrançois Tigeot 			spin_unlock(&mm->unused_lock);
953f6063ccSDavid Shao 			return ret;
963f6063ccSDavid Shao 		}
973f6063ccSDavid Shao 		++mm->num_unused;
985718399fSFrançois Tigeot 		list_add_tail(&node->node_list, &mm->unused_nodes);
993f6063ccSDavid Shao 	}
100ea132f0fSFrançois Tigeot 	spin_unlock(&mm->unused_lock);
1013f6063ccSDavid Shao 	return 0;
1023f6063ccSDavid Shao }
103ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_pre_get);
1043f6063ccSDavid Shao 
1055718399fSFrançois Tigeot static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
1065718399fSFrançois Tigeot 				 struct drm_mm_node *node,
107ea132f0fSFrançois Tigeot 				 unsigned long size, unsigned alignment,
108ea132f0fSFrançois Tigeot 				 unsigned long color)
1093f6063ccSDavid Shao {
1105718399fSFrançois Tigeot 	struct drm_mm *mm = hole_node->mm;
1115718399fSFrançois Tigeot 	unsigned long hole_start = drm_mm_hole_node_start(hole_node);
1125718399fSFrançois Tigeot 	unsigned long hole_end = drm_mm_hole_node_end(hole_node);
113ea132f0fSFrançois Tigeot 	unsigned long adj_start = hole_start;
114ea132f0fSFrançois Tigeot 	unsigned long adj_end = hole_end;
1153f6063ccSDavid Shao 
116b6fbc077SFrançois Tigeot 	BUG_ON(node->allocated);
1173f6063ccSDavid Shao 
118ea132f0fSFrançois Tigeot 	if (mm->color_adjust)
119ea132f0fSFrançois Tigeot 		mm->color_adjust(hole_node, color, &adj_start, &adj_end);
1203f6063ccSDavid Shao 
121ea132f0fSFrançois Tigeot 	if (alignment) {
122ea132f0fSFrançois Tigeot 		unsigned tmp = adj_start % alignment;
123ea132f0fSFrançois Tigeot 		if (tmp)
124ea132f0fSFrançois Tigeot 			adj_start += alignment - tmp;
125ea132f0fSFrançois Tigeot 	}
126ea132f0fSFrançois Tigeot 
127ea132f0fSFrançois Tigeot 	if (adj_start == hole_start) {
1285718399fSFrançois Tigeot 		hole_node->hole_follows = 0;
129ea132f0fSFrançois Tigeot 		list_del(&hole_node->hole_stack);
130ea132f0fSFrançois Tigeot 	}
1313f6063ccSDavid Shao 
132ea132f0fSFrançois Tigeot 	node->start = adj_start;
1335718399fSFrançois Tigeot 	node->size = size;
1345718399fSFrançois Tigeot 	node->mm = mm;
135ea132f0fSFrançois Tigeot 	node->color = color;
1365718399fSFrançois Tigeot 	node->allocated = 1;
1373f6063ccSDavid Shao 
1385718399fSFrançois Tigeot 	INIT_LIST_HEAD(&node->hole_stack);
1395718399fSFrançois Tigeot 	list_add(&node->node_list, &hole_node->node_list);
1405718399fSFrançois Tigeot 
141ea132f0fSFrançois Tigeot 	BUG_ON(node->start + node->size > adj_end);
1425718399fSFrançois Tigeot 
143ea132f0fSFrançois Tigeot 	node->hole_follows = 0;
144b6fbc077SFrançois Tigeot 	if (__drm_mm_hole_node_start(node) < hole_end) {
1455718399fSFrançois Tigeot 		list_add(&node->hole_stack, &mm->hole_stack);
1465718399fSFrançois Tigeot 		node->hole_follows = 1;
1475718399fSFrançois Tigeot 	}
1483f6063ccSDavid Shao }
1493f6063ccSDavid Shao 
150*9edbd4a0SFrançois Tigeot int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
151b6fbc077SFrançois Tigeot {
152*9edbd4a0SFrançois Tigeot 	struct drm_mm_node *hole;
153*9edbd4a0SFrançois Tigeot 	unsigned long end = node->start + node->size;
154b6fbc077SFrançois Tigeot 	unsigned long hole_start;
155b6fbc077SFrançois Tigeot 	unsigned long hole_end;
156b6fbc077SFrançois Tigeot 
157*9edbd4a0SFrançois Tigeot 	BUG_ON(node == NULL);
158*9edbd4a0SFrançois Tigeot 
159*9edbd4a0SFrançois Tigeot 	/* Find the relevant hole to add our node to */
160b6fbc077SFrançois Tigeot 	drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
161*9edbd4a0SFrançois Tigeot 		if (hole_start > node->start || hole_end < end)
162b6fbc077SFrançois Tigeot 			continue;
163b6fbc077SFrançois Tigeot 
164b6fbc077SFrançois Tigeot 		node->mm = mm;
165b6fbc077SFrançois Tigeot 		node->allocated = 1;
166b6fbc077SFrançois Tigeot 
167b6fbc077SFrançois Tigeot 		INIT_LIST_HEAD(&node->hole_stack);
168b6fbc077SFrançois Tigeot 		list_add(&node->node_list, &hole->node_list);
169b6fbc077SFrançois Tigeot 
170*9edbd4a0SFrançois Tigeot 		if (node->start == hole_start) {
171b6fbc077SFrançois Tigeot 			hole->hole_follows = 0;
172b6fbc077SFrançois Tigeot 			list_del_init(&hole->hole_stack);
173b6fbc077SFrançois Tigeot 		}
174b6fbc077SFrançois Tigeot 
175b6fbc077SFrançois Tigeot 		node->hole_follows = 0;
176b6fbc077SFrançois Tigeot 		if (end != hole_end) {
177b6fbc077SFrançois Tigeot 			list_add(&node->hole_stack, &mm->hole_stack);
178b6fbc077SFrançois Tigeot 			node->hole_follows = 1;
179b6fbc077SFrançois Tigeot 		}
180b6fbc077SFrançois Tigeot 
181*9edbd4a0SFrançois Tigeot 		return 0;
182b6fbc077SFrançois Tigeot 	}
183b6fbc077SFrançois Tigeot 
184*9edbd4a0SFrançois Tigeot 	WARN(1, "no hole found for node 0x%lx + 0x%lx\n",
185*9edbd4a0SFrançois Tigeot 	     node->start, node->size);
186*9edbd4a0SFrançois Tigeot 	return -ENOSPC;
187b6fbc077SFrançois Tigeot }
188*9edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_mm_reserve_node);
189b6fbc077SFrançois Tigeot 
1905718399fSFrançois Tigeot struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node,
1913f6063ccSDavid Shao 					     unsigned long size,
1923f6063ccSDavid Shao 					     unsigned alignment,
193ea132f0fSFrançois Tigeot 					     unsigned long color,
1943f6063ccSDavid Shao 					     int atomic)
1953f6063ccSDavid Shao {
1965718399fSFrançois Tigeot 	struct drm_mm_node *node;
1973f6063ccSDavid Shao 
1985718399fSFrançois Tigeot 	node = drm_mm_kmalloc(hole_node->mm, atomic);
1995718399fSFrançois Tigeot 	if (unlikely(node == NULL))
2003f6063ccSDavid Shao 		return NULL;
2013f6063ccSDavid Shao 
202ea132f0fSFrançois Tigeot 	drm_mm_insert_helper(hole_node, node, size, alignment, color);
2033f6063ccSDavid Shao 
2043f6063ccSDavid Shao 	return node;
2053f6063ccSDavid Shao }
206ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_get_block_generic);
207ea132f0fSFrançois Tigeot 
208ea132f0fSFrançois Tigeot /**
209ea132f0fSFrançois Tigeot  * Search for free space and insert a preallocated memory node. Returns
210ea132f0fSFrançois Tigeot  * -ENOSPC if no suitable free area is available. The preallocated memory node
211ea132f0fSFrançois Tigeot  * must be cleared.
212ea132f0fSFrançois Tigeot  */
213ea132f0fSFrançois Tigeot int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node,
214ea132f0fSFrançois Tigeot 			       unsigned long size, unsigned alignment,
215*9edbd4a0SFrançois Tigeot 			       unsigned long color,
216*9edbd4a0SFrançois Tigeot 			       enum drm_mm_search_flags flags)
217ea132f0fSFrançois Tigeot {
218ea132f0fSFrançois Tigeot 	struct drm_mm_node *hole_node;
219ea132f0fSFrançois Tigeot 
220ea132f0fSFrançois Tigeot 	hole_node = drm_mm_search_free_generic(mm, size, alignment,
221*9edbd4a0SFrançois Tigeot 					       color, flags);
222ea132f0fSFrançois Tigeot 	if (!hole_node)
223ea132f0fSFrançois Tigeot 		return -ENOSPC;
224ea132f0fSFrançois Tigeot 
225ea132f0fSFrançois Tigeot 	drm_mm_insert_helper(hole_node, node, size, alignment, color);
226ea132f0fSFrançois Tigeot 	return 0;
227ea132f0fSFrançois Tigeot }
228ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_insert_node_generic);
2293f6063ccSDavid Shao 
2305718399fSFrançois Tigeot static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
2315718399fSFrançois Tigeot 				       struct drm_mm_node *node,
2325718399fSFrançois Tigeot 				       unsigned long size, unsigned alignment,
233ea132f0fSFrançois Tigeot 				       unsigned long color,
2345718399fSFrançois Tigeot 				       unsigned long start, unsigned long end)
2355718399fSFrançois Tigeot {
2365718399fSFrançois Tigeot 	struct drm_mm *mm = hole_node->mm;
2375718399fSFrançois Tigeot 	unsigned long hole_start = drm_mm_hole_node_start(hole_node);
2385718399fSFrançois Tigeot 	unsigned long hole_end = drm_mm_hole_node_end(hole_node);
239ea132f0fSFrançois Tigeot 	unsigned long adj_start = hole_start;
240ea132f0fSFrançois Tigeot 	unsigned long adj_end = hole_end;
2415718399fSFrançois Tigeot 
242ea132f0fSFrançois Tigeot 	BUG_ON(!hole_node->hole_follows || node->allocated);
2435718399fSFrançois Tigeot 
244ea132f0fSFrançois Tigeot 	if (adj_start < start)
245ea132f0fSFrançois Tigeot 		adj_start = start;
246ea132f0fSFrançois Tigeot 	if (adj_end > end)
247ea132f0fSFrançois Tigeot 		adj_end = end;
2485718399fSFrançois Tigeot 
249ea132f0fSFrançois Tigeot 	if (mm->color_adjust)
250ea132f0fSFrançois Tigeot 		mm->color_adjust(hole_node, color, &adj_start, &adj_end);
251ea132f0fSFrançois Tigeot 
252ea132f0fSFrançois Tigeot 	if (alignment) {
253ea132f0fSFrançois Tigeot 		unsigned tmp = adj_start % alignment;
2545718399fSFrançois Tigeot 		if (tmp)
255ea132f0fSFrançois Tigeot 			adj_start += alignment - tmp;
2565718399fSFrançois Tigeot 	}
2575718399fSFrançois Tigeot 
258ea132f0fSFrançois Tigeot 	if (adj_start == hole_start) {
259ea132f0fSFrançois Tigeot 		hole_node->hole_follows = 0;
260ea132f0fSFrançois Tigeot 		list_del(&hole_node->hole_stack);
261ea132f0fSFrançois Tigeot 	}
262ea132f0fSFrançois Tigeot 
263ea132f0fSFrançois Tigeot 	node->start = adj_start;
2645718399fSFrançois Tigeot 	node->size = size;
2655718399fSFrançois Tigeot 	node->mm = mm;
266ea132f0fSFrançois Tigeot 	node->color = color;
2675718399fSFrançois Tigeot 	node->allocated = 1;
2685718399fSFrançois Tigeot 
2695718399fSFrançois Tigeot 	INIT_LIST_HEAD(&node->hole_stack);
2705718399fSFrançois Tigeot 	list_add(&node->node_list, &hole_node->node_list);
2715718399fSFrançois Tigeot 
272ea132f0fSFrançois Tigeot 	BUG_ON(node->start + node->size > adj_end);
273ea132f0fSFrançois Tigeot 	BUG_ON(node->start + node->size > end);
2745718399fSFrançois Tigeot 
275ea132f0fSFrançois Tigeot 	node->hole_follows = 0;
276b6fbc077SFrançois Tigeot 	if (__drm_mm_hole_node_start(node) < hole_end) {
2775718399fSFrançois Tigeot 		list_add(&node->hole_stack, &mm->hole_stack);
2785718399fSFrançois Tigeot 		node->hole_follows = 1;
2795718399fSFrançois Tigeot 	}
2805718399fSFrançois Tigeot }
2815718399fSFrançois Tigeot 
2825718399fSFrançois Tigeot struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *hole_node,
2835718399fSFrançois Tigeot 						unsigned long size,
2845718399fSFrançois Tigeot 						unsigned alignment,
285ea132f0fSFrançois Tigeot 						unsigned long color,
2865718399fSFrançois Tigeot 						unsigned long start,
2875718399fSFrançois Tigeot 						unsigned long end,
2885718399fSFrançois Tigeot 						int atomic)
2895718399fSFrançois Tigeot {
2905718399fSFrançois Tigeot 	struct drm_mm_node *node;
2915718399fSFrançois Tigeot 
2925718399fSFrançois Tigeot 	node = drm_mm_kmalloc(hole_node->mm, atomic);
2935718399fSFrançois Tigeot 	if (unlikely(node == NULL))
2945718399fSFrançois Tigeot 		return NULL;
2955718399fSFrançois Tigeot 
296ea132f0fSFrançois Tigeot 	drm_mm_insert_helper_range(hole_node, node, size, alignment, color,
2975718399fSFrançois Tigeot 				   start, end);
2985718399fSFrançois Tigeot 
2995718399fSFrançois Tigeot 	return node;
3005718399fSFrançois Tigeot }
301ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_get_block_range_generic);
302ea132f0fSFrançois Tigeot 
303ea132f0fSFrançois Tigeot /**
304ea132f0fSFrançois Tigeot  * Search for free space and insert a preallocated memory node. Returns
305ea132f0fSFrançois Tigeot  * -ENOSPC if no suitable free area is available. This is for range
306ea132f0fSFrançois Tigeot  * restricted allocations. The preallocated memory node must be cleared.
307ea132f0fSFrançois Tigeot  */
308ea132f0fSFrançois Tigeot int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node,
309ea132f0fSFrançois Tigeot 					unsigned long size, unsigned alignment, unsigned long color,
310*9edbd4a0SFrançois Tigeot 					unsigned long start, unsigned long end,
311*9edbd4a0SFrançois Tigeot 					enum drm_mm_search_flags flags)
312ea132f0fSFrançois Tigeot {
313ea132f0fSFrançois Tigeot 	struct drm_mm_node *hole_node;
314ea132f0fSFrançois Tigeot 
315ea132f0fSFrançois Tigeot 	hole_node = drm_mm_search_free_in_range_generic(mm,
316ea132f0fSFrançois Tigeot 							size, alignment, color,
317*9edbd4a0SFrançois Tigeot 							start, end, flags);
318ea132f0fSFrançois Tigeot 	if (!hole_node)
319ea132f0fSFrançois Tigeot 		return -ENOSPC;
320ea132f0fSFrançois Tigeot 
321ea132f0fSFrançois Tigeot 	drm_mm_insert_helper_range(hole_node, node,
322ea132f0fSFrançois Tigeot 				   size, alignment, color,
323ea132f0fSFrançois Tigeot 				   start, end);
324ea132f0fSFrançois Tigeot 	return 0;
325ea132f0fSFrançois Tigeot }
326ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic);
3275718399fSFrançois Tigeot 
328ea132f0fSFrançois Tigeot /**
329ea132f0fSFrançois Tigeot  * Remove a memory node from the allocator.
330ea132f0fSFrançois Tigeot  */
3315718399fSFrançois Tigeot void drm_mm_remove_node(struct drm_mm_node *node)
3325718399fSFrançois Tigeot {
3335718399fSFrançois Tigeot 	struct drm_mm *mm = node->mm;
3345718399fSFrançois Tigeot 	struct drm_mm_node *prev_node;
3355718399fSFrançois Tigeot 
336*9edbd4a0SFrançois Tigeot 	if (WARN_ON(!node->allocated))
337*9edbd4a0SFrançois Tigeot 		return;
338*9edbd4a0SFrançois Tigeot 
339ea132f0fSFrançois Tigeot 	BUG_ON(node->scanned_block || node->scanned_prev_free
340ea132f0fSFrançois Tigeot 				   || node->scanned_next_free);
3415718399fSFrançois Tigeot 
3425718399fSFrançois Tigeot 	prev_node =
3435718399fSFrançois Tigeot 	    list_entry(node->node_list.prev, struct drm_mm_node, node_list);
3445718399fSFrançois Tigeot 
3455718399fSFrançois Tigeot 	if (node->hole_follows) {
346b6fbc077SFrançois Tigeot 		BUG_ON(__drm_mm_hole_node_start(node) ==
347b6fbc077SFrançois Tigeot 		       __drm_mm_hole_node_end(node));
3485718399fSFrançois Tigeot 		list_del(&node->hole_stack);
3495718399fSFrançois Tigeot 	} else
350b6fbc077SFrançois Tigeot 		BUG_ON(__drm_mm_hole_node_start(node) !=
351b6fbc077SFrançois Tigeot 		       __drm_mm_hole_node_end(node));
352b6fbc077SFrançois Tigeot 
3535718399fSFrançois Tigeot 
3545718399fSFrançois Tigeot 	if (!prev_node->hole_follows) {
3555718399fSFrançois Tigeot 		prev_node->hole_follows = 1;
3565718399fSFrançois Tigeot 		list_add(&prev_node->hole_stack, &mm->hole_stack);
3575718399fSFrançois Tigeot 	} else
3585718399fSFrançois Tigeot 		list_move(&prev_node->hole_stack, &mm->hole_stack);
3595718399fSFrançois Tigeot 
3605718399fSFrançois Tigeot 	list_del(&node->node_list);
3615718399fSFrançois Tigeot 	node->allocated = 0;
3625718399fSFrançois Tigeot }
363ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_remove_node);
3645718399fSFrançois Tigeot 
3653f6063ccSDavid Shao /*
366ea132f0fSFrançois Tigeot  * Remove a memory node from the allocator and free the allocated struct
367ea132f0fSFrançois Tigeot  * drm_mm_node. Only to be used on a struct drm_mm_node obtained by one of the
368ea132f0fSFrançois Tigeot  * drm_mm_get_block functions.
3693f6063ccSDavid Shao  */
3705718399fSFrançois Tigeot void drm_mm_put_block(struct drm_mm_node *node)
3713f6063ccSDavid Shao {
372ea132f0fSFrançois Tigeot 
3735718399fSFrançois Tigeot 	struct drm_mm *mm = node->mm;
3743f6063ccSDavid Shao 
3755718399fSFrançois Tigeot 	drm_mm_remove_node(node);
3763f6063ccSDavid Shao 
377ea132f0fSFrançois Tigeot 	spin_lock(&mm->unused_lock);
3783f6063ccSDavid Shao 	if (mm->num_unused < MM_UNUSED_TARGET) {
3795718399fSFrançois Tigeot 		list_add(&node->node_list, &mm->unused_nodes);
3803f6063ccSDavid Shao 		++mm->num_unused;
3813f6063ccSDavid Shao 	} else
382b6fbc077SFrançois Tigeot 		kfree(node);
383ea132f0fSFrançois Tigeot 	spin_unlock(&mm->unused_lock);
3843f6063ccSDavid Shao }
385ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_put_block);
3865718399fSFrançois Tigeot 
3875718399fSFrançois Tigeot static int check_free_hole(unsigned long start, unsigned long end,
3885718399fSFrançois Tigeot 			   unsigned long size, unsigned alignment)
3895718399fSFrançois Tigeot {
3905718399fSFrançois Tigeot 	if (end - start < size)
3915718399fSFrançois Tigeot 		return 0;
3925718399fSFrançois Tigeot 
3935718399fSFrançois Tigeot 	if (alignment) {
3945718399fSFrançois Tigeot 		unsigned tmp = start % alignment;
3955718399fSFrançois Tigeot 		if (tmp)
396ea132f0fSFrançois Tigeot 			start += alignment - tmp;
3973f6063ccSDavid Shao 	}
3985718399fSFrançois Tigeot 
399ea132f0fSFrançois Tigeot 	return end >= start + size;
4003f6063ccSDavid Shao }
4015718399fSFrançois Tigeot 
402ea132f0fSFrançois Tigeot struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
4035718399fSFrançois Tigeot 					       unsigned long size,
4045718399fSFrançois Tigeot 					       unsigned alignment,
405ea132f0fSFrançois Tigeot 					       unsigned long color,
406*9edbd4a0SFrançois Tigeot 					       enum drm_mm_search_flags flags)
4075718399fSFrançois Tigeot {
4085718399fSFrançois Tigeot 	struct drm_mm_node *entry;
4095718399fSFrançois Tigeot 	struct drm_mm_node *best;
410b6fbc077SFrançois Tigeot 	unsigned long adj_start;
411b6fbc077SFrançois Tigeot 	unsigned long adj_end;
4125718399fSFrançois Tigeot 	unsigned long best_size;
4135718399fSFrançois Tigeot 
414ea132f0fSFrançois Tigeot 	BUG_ON(mm->scanned_blocks);
4155718399fSFrançois Tigeot 
4165718399fSFrançois Tigeot 	best = NULL;
4175718399fSFrançois Tigeot 	best_size = ~0UL;
4185718399fSFrançois Tigeot 
419b6fbc077SFrançois Tigeot 	drm_mm_for_each_hole(entry, mm, adj_start, adj_end) {
420ea132f0fSFrançois Tigeot 		if (mm->color_adjust) {
421ea132f0fSFrançois Tigeot 			mm->color_adjust(entry, color, &adj_start, &adj_end);
422ea132f0fSFrançois Tigeot 			if (adj_end <= adj_start)
423ea132f0fSFrançois Tigeot 				continue;
424ea132f0fSFrançois Tigeot 		}
425ea132f0fSFrançois Tigeot 
4265718399fSFrançois Tigeot 		if (!check_free_hole(adj_start, adj_end, size, alignment))
4275718399fSFrançois Tigeot 			continue;
4285718399fSFrançois Tigeot 
429*9edbd4a0SFrançois Tigeot 		if (!(flags & DRM_MM_SEARCH_BEST))
4305718399fSFrançois Tigeot 			return entry;
4315718399fSFrançois Tigeot 
4325718399fSFrançois Tigeot 		if (entry->size < best_size) {
4335718399fSFrançois Tigeot 			best = entry;
4345718399fSFrançois Tigeot 			best_size = entry->size;
4355718399fSFrançois Tigeot 		}
4365718399fSFrançois Tigeot 	}
4375718399fSFrançois Tigeot 
4385718399fSFrançois Tigeot 	return best;
4395718399fSFrançois Tigeot }
440ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_search_free_generic);
4415718399fSFrançois Tigeot 
442ea132f0fSFrançois Tigeot struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
443ea132f0fSFrançois Tigeot 							unsigned long size,
444ea132f0fSFrançois Tigeot 							unsigned alignment,
445ea132f0fSFrançois Tigeot 							unsigned long color,
446ea132f0fSFrançois Tigeot 							unsigned long start,
447ea132f0fSFrançois Tigeot 							unsigned long end,
448*9edbd4a0SFrançois Tigeot 							enum drm_mm_search_flags flags)
449ea132f0fSFrançois Tigeot {
450ea132f0fSFrançois Tigeot 	struct drm_mm_node *entry;
451ea132f0fSFrançois Tigeot 	struct drm_mm_node *best;
452b6fbc077SFrançois Tigeot 	unsigned long adj_start;
453b6fbc077SFrançois Tigeot 	unsigned long adj_end;
454ea132f0fSFrançois Tigeot 	unsigned long best_size;
455ea132f0fSFrançois Tigeot 
456ea132f0fSFrançois Tigeot 	BUG_ON(mm->scanned_blocks);
457ea132f0fSFrançois Tigeot 
458ea132f0fSFrançois Tigeot 	best = NULL;
459ea132f0fSFrançois Tigeot 	best_size = ~0UL;
460ea132f0fSFrançois Tigeot 
461b6fbc077SFrançois Tigeot 	drm_mm_for_each_hole(entry, mm, adj_start, adj_end) {
462b6fbc077SFrançois Tigeot 		if (adj_start < start)
463b6fbc077SFrançois Tigeot 			adj_start = start;
464b6fbc077SFrançois Tigeot 		if (adj_end > end)
465b6fbc077SFrançois Tigeot 			adj_end = end;
466ea132f0fSFrançois Tigeot 
467ea132f0fSFrançois Tigeot 		if (mm->color_adjust) {
468ea132f0fSFrançois Tigeot 			mm->color_adjust(entry, color, &adj_start, &adj_end);
469ea132f0fSFrançois Tigeot 			if (adj_end <= adj_start)
470ea132f0fSFrançois Tigeot 				continue;
471ea132f0fSFrançois Tigeot 		}
472ea132f0fSFrançois Tigeot 
473ea132f0fSFrançois Tigeot 		if (!check_free_hole(adj_start, adj_end, size, alignment))
474ea132f0fSFrançois Tigeot 			continue;
475ea132f0fSFrançois Tigeot 
476*9edbd4a0SFrançois Tigeot 		if (!(flags & DRM_MM_SEARCH_BEST))
477ea132f0fSFrançois Tigeot 			return entry;
478ea132f0fSFrançois Tigeot 
479ea132f0fSFrançois Tigeot 		if (entry->size < best_size) {
480ea132f0fSFrançois Tigeot 			best = entry;
481ea132f0fSFrançois Tigeot 			best_size = entry->size;
482ea132f0fSFrançois Tigeot 		}
483ea132f0fSFrançois Tigeot 	}
484ea132f0fSFrançois Tigeot 
485ea132f0fSFrançois Tigeot 	return best;
486ea132f0fSFrançois Tigeot }
487ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_search_free_in_range_generic);
488ea132f0fSFrançois Tigeot 
489ea132f0fSFrançois Tigeot /**
490ea132f0fSFrançois Tigeot  * Moves an allocation. To be used with embedded struct drm_mm_node.
491ea132f0fSFrançois Tigeot  */
4925718399fSFrançois Tigeot void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
4935718399fSFrançois Tigeot {
4945718399fSFrançois Tigeot 	list_replace(&old->node_list, &new->node_list);
4955718399fSFrançois Tigeot 	list_replace(&old->hole_stack, &new->hole_stack);
4965718399fSFrançois Tigeot 	new->hole_follows = old->hole_follows;
4975718399fSFrançois Tigeot 	new->mm = old->mm;
4985718399fSFrançois Tigeot 	new->start = old->start;
4995718399fSFrançois Tigeot 	new->size = old->size;
500ea132f0fSFrançois Tigeot 	new->color = old->color;
5015718399fSFrançois Tigeot 
5025718399fSFrançois Tigeot 	old->allocated = 0;
5035718399fSFrançois Tigeot 	new->allocated = 1;
5045718399fSFrançois Tigeot }
505ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_replace_node);
5065718399fSFrançois Tigeot 
507ea132f0fSFrançois Tigeot /**
508ea132f0fSFrançois Tigeot  * Initializa lru scanning.
509ea132f0fSFrançois Tigeot  *
510ea132f0fSFrançois Tigeot  * This simply sets up the scanning routines with the parameters for the desired
511ea132f0fSFrançois Tigeot  * hole.
512ea132f0fSFrançois Tigeot  *
513ea132f0fSFrançois Tigeot  * Warning: As long as the scan list is non-empty, no other operations than
514ea132f0fSFrançois Tigeot  * adding/removing nodes to/from the scan list are allowed.
515ea132f0fSFrançois Tigeot  */
516ea132f0fSFrançois Tigeot void drm_mm_init_scan(struct drm_mm *mm,
517ea132f0fSFrançois Tigeot 		      unsigned long size,
518ea132f0fSFrançois Tigeot 		      unsigned alignment,
519ea132f0fSFrançois Tigeot 		      unsigned long color)
5205718399fSFrançois Tigeot {
521ea132f0fSFrançois Tigeot 	mm->scan_color = color;
5225718399fSFrançois Tigeot 	mm->scan_alignment = alignment;
5235718399fSFrançois Tigeot 	mm->scan_size = size;
5245718399fSFrançois Tigeot 	mm->scanned_blocks = 0;
5255718399fSFrançois Tigeot 	mm->scan_hit_start = 0;
526ea132f0fSFrançois Tigeot 	mm->scan_hit_end = 0;
5275718399fSFrançois Tigeot 	mm->scan_check_range = 0;
5285718399fSFrançois Tigeot 	mm->prev_scanned_node = NULL;
5295718399fSFrançois Tigeot }
530ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_init_scan);
5315718399fSFrançois Tigeot 
532ea132f0fSFrançois Tigeot /**
533ea132f0fSFrançois Tigeot  * Initializa lru scanning.
534ea132f0fSFrançois Tigeot  *
535ea132f0fSFrançois Tigeot  * This simply sets up the scanning routines with the parameters for the desired
536ea132f0fSFrançois Tigeot  * hole. This version is for range-restricted scans.
537ea132f0fSFrançois Tigeot  *
538ea132f0fSFrançois Tigeot  * Warning: As long as the scan list is non-empty, no other operations than
539ea132f0fSFrançois Tigeot  * adding/removing nodes to/from the scan list are allowed.
540ea132f0fSFrançois Tigeot  */
541ea132f0fSFrançois Tigeot void drm_mm_init_scan_with_range(struct drm_mm *mm,
542ea132f0fSFrançois Tigeot 				 unsigned long size,
5435718399fSFrançois Tigeot 				 unsigned alignment,
544ea132f0fSFrançois Tigeot 				 unsigned long color,
5455718399fSFrançois Tigeot 				 unsigned long start,
5465718399fSFrançois Tigeot 				 unsigned long end)
5475718399fSFrançois Tigeot {
548ea132f0fSFrançois Tigeot 	mm->scan_color = color;
5495718399fSFrançois Tigeot 	mm->scan_alignment = alignment;
5505718399fSFrançois Tigeot 	mm->scan_size = size;
5515718399fSFrançois Tigeot 	mm->scanned_blocks = 0;
5525718399fSFrançois Tigeot 	mm->scan_hit_start = 0;
553ea132f0fSFrançois Tigeot 	mm->scan_hit_end = 0;
5545718399fSFrançois Tigeot 	mm->scan_start = start;
5555718399fSFrançois Tigeot 	mm->scan_end = end;
5565718399fSFrançois Tigeot 	mm->scan_check_range = 1;
5575718399fSFrançois Tigeot 	mm->prev_scanned_node = NULL;
5585718399fSFrançois Tigeot }
559ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_init_scan_with_range);
5605718399fSFrançois Tigeot 
561ea132f0fSFrançois Tigeot /**
562ea132f0fSFrançois Tigeot  * Add a node to the scan list that might be freed to make space for the desired
563ea132f0fSFrançois Tigeot  * hole.
564ea132f0fSFrançois Tigeot  *
565ea132f0fSFrançois Tigeot  * Returns non-zero, if a hole has been found, zero otherwise.
566ea132f0fSFrançois Tigeot  */
5675718399fSFrançois Tigeot int drm_mm_scan_add_block(struct drm_mm_node *node)
5685718399fSFrançois Tigeot {
5695718399fSFrançois Tigeot 	struct drm_mm *mm = node->mm;
5705718399fSFrançois Tigeot 	struct drm_mm_node *prev_node;
5715718399fSFrançois Tigeot 	unsigned long hole_start, hole_end;
572ea132f0fSFrançois Tigeot 	unsigned long adj_start, adj_end;
5735718399fSFrançois Tigeot 
5745718399fSFrançois Tigeot 	mm->scanned_blocks++;
5755718399fSFrançois Tigeot 
576ea132f0fSFrançois Tigeot 	BUG_ON(node->scanned_block);
5775718399fSFrançois Tigeot 	node->scanned_block = 1;
5785718399fSFrançois Tigeot 
5795718399fSFrançois Tigeot 	prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
5805718399fSFrançois Tigeot 			       node_list);
5815718399fSFrançois Tigeot 
5825718399fSFrançois Tigeot 	node->scanned_preceeds_hole = prev_node->hole_follows;
5835718399fSFrançois Tigeot 	prev_node->hole_follows = 1;
5845718399fSFrançois Tigeot 	list_del(&node->node_list);
5855718399fSFrançois Tigeot 	node->node_list.prev = &prev_node->node_list;
5865718399fSFrançois Tigeot 	node->node_list.next = &mm->prev_scanned_node->node_list;
5875718399fSFrançois Tigeot 	mm->prev_scanned_node = node;
5885718399fSFrançois Tigeot 
589ea132f0fSFrançois Tigeot 	adj_start = hole_start = drm_mm_hole_node_start(prev_node);
590ea132f0fSFrançois Tigeot 	adj_end = hole_end = drm_mm_hole_node_end(prev_node);
591ea132f0fSFrançois Tigeot 
5925718399fSFrançois Tigeot 	if (mm->scan_check_range) {
593ea132f0fSFrançois Tigeot 		if (adj_start < mm->scan_start)
594ea132f0fSFrançois Tigeot 			adj_start = mm->scan_start;
595ea132f0fSFrançois Tigeot 		if (adj_end > mm->scan_end)
596ea132f0fSFrançois Tigeot 			adj_end = mm->scan_end;
5975718399fSFrançois Tigeot 	}
5985718399fSFrançois Tigeot 
599ea132f0fSFrançois Tigeot 	if (mm->color_adjust)
600ea132f0fSFrançois Tigeot 		mm->color_adjust(prev_node, mm->scan_color,
601ea132f0fSFrançois Tigeot 				 &adj_start, &adj_end);
602ea132f0fSFrançois Tigeot 
6035718399fSFrançois Tigeot 	if (check_free_hole(adj_start, adj_end,
6045718399fSFrançois Tigeot 			    mm->scan_size, mm->scan_alignment)) {
6055718399fSFrançois Tigeot 		mm->scan_hit_start = hole_start;
606ea132f0fSFrançois Tigeot 		mm->scan_hit_end = hole_end;
6075718399fSFrançois Tigeot 		return 1;
6085718399fSFrançois Tigeot 	}
6095718399fSFrançois Tigeot 
6105718399fSFrançois Tigeot 	return 0;
6115718399fSFrançois Tigeot }
612ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_scan_add_block);
6135718399fSFrançois Tigeot 
614ea132f0fSFrançois Tigeot /**
615ea132f0fSFrançois Tigeot  * Remove a node from the scan list.
616ea132f0fSFrançois Tigeot  *
617ea132f0fSFrançois Tigeot  * Nodes _must_ be removed in the exact same order from the scan list as they
618ea132f0fSFrançois Tigeot  * have been added, otherwise the internal state of the memory manager will be
619ea132f0fSFrançois Tigeot  * corrupted.
620ea132f0fSFrançois Tigeot  *
621ea132f0fSFrançois Tigeot  * When the scan list is empty, the selected memory nodes can be freed. An
622*9edbd4a0SFrançois Tigeot  * immediately following drm_mm_search_free with !DRM_MM_SEARCH_BEST will then
623*9edbd4a0SFrançois Tigeot  * return the just freed block (because its at the top of the free_stack list).
624ea132f0fSFrançois Tigeot  *
625ea132f0fSFrançois Tigeot  * Returns one if this block should be evicted, zero otherwise. Will always
626ea132f0fSFrançois Tigeot  * return zero when no hole has been found.
627ea132f0fSFrançois Tigeot  */
6285718399fSFrançois Tigeot int drm_mm_scan_remove_block(struct drm_mm_node *node)
6295718399fSFrançois Tigeot {
6305718399fSFrançois Tigeot 	struct drm_mm *mm = node->mm;
6315718399fSFrançois Tigeot 	struct drm_mm_node *prev_node;
6325718399fSFrançois Tigeot 
6335718399fSFrançois Tigeot 	mm->scanned_blocks--;
6345718399fSFrançois Tigeot 
635ea132f0fSFrançois Tigeot 	BUG_ON(!node->scanned_block);
6365718399fSFrançois Tigeot 	node->scanned_block = 0;
6375718399fSFrançois Tigeot 
6385718399fSFrançois Tigeot 	prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
6395718399fSFrançois Tigeot 			       node_list);
6405718399fSFrançois Tigeot 
6415718399fSFrançois Tigeot 	prev_node->hole_follows = node->scanned_preceeds_hole;
6425718399fSFrançois Tigeot 	list_add(&node->node_list, &prev_node->node_list);
6435718399fSFrançois Tigeot 
644ea132f0fSFrançois Tigeot 	 return (drm_mm_hole_node_end(node) > mm->scan_hit_start &&
645ea132f0fSFrançois Tigeot 		 node->start < mm->scan_hit_end);
6465718399fSFrançois Tigeot }
647ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_scan_remove_block);
6485718399fSFrançois Tigeot 
6493f6063ccSDavid Shao int drm_mm_clean(struct drm_mm * mm)
6503f6063ccSDavid Shao {
6515718399fSFrançois Tigeot 	struct list_head *head = &mm->head_node.node_list;
6523f6063ccSDavid Shao 
6533f6063ccSDavid Shao 	return (head->next->next == head);
6543f6063ccSDavid Shao }
655ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_clean);
6563f6063ccSDavid Shao 
657b6fbc077SFrançois Tigeot void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
6583f6063ccSDavid Shao {
6595718399fSFrançois Tigeot 	INIT_LIST_HEAD(&mm->hole_stack);
6603f6063ccSDavid Shao 	INIT_LIST_HEAD(&mm->unused_nodes);
6613f6063ccSDavid Shao 	mm->num_unused = 0;
6625718399fSFrançois Tigeot 	mm->scanned_blocks = 0;
663ba87a4abSSascha Wildner 	spin_init(&mm->unused_lock, "drmmminit");
6643f6063ccSDavid Shao 
665ea132f0fSFrançois Tigeot 	/* Clever trick to avoid a special case in the free hole tracking. */
6665718399fSFrançois Tigeot 	INIT_LIST_HEAD(&mm->head_node.node_list);
6675718399fSFrançois Tigeot 	INIT_LIST_HEAD(&mm->head_node.hole_stack);
6685718399fSFrançois Tigeot 	mm->head_node.hole_follows = 1;
6695718399fSFrançois Tigeot 	mm->head_node.scanned_block = 0;
6705718399fSFrançois Tigeot 	mm->head_node.scanned_prev_free = 0;
6715718399fSFrançois Tigeot 	mm->head_node.scanned_next_free = 0;
6725718399fSFrançois Tigeot 	mm->head_node.mm = mm;
6735718399fSFrançois Tigeot 	mm->head_node.start = start + size;
6745718399fSFrançois Tigeot 	mm->head_node.size = start - mm->head_node.start;
6755718399fSFrançois Tigeot 	list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack);
6765718399fSFrançois Tigeot 
677ea132f0fSFrançois Tigeot 	mm->color_adjust = NULL;
6783f6063ccSDavid Shao }
679ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_init);
6803f6063ccSDavid Shao 
6813f6063ccSDavid Shao void drm_mm_takedown(struct drm_mm * mm)
6823f6063ccSDavid Shao {
6835718399fSFrançois Tigeot 	struct drm_mm_node *entry, *next;
6843f6063ccSDavid Shao 
685*9edbd4a0SFrançois Tigeot 	if (!list_empty(&mm->head_node.node_list)) {
686*9edbd4a0SFrançois Tigeot 		DRM_ERROR("Memory manager not clean. Delaying takedown\n");
6873f6063ccSDavid Shao 		return;
6883f6063ccSDavid Shao 	}
6893f6063ccSDavid Shao 
690ea132f0fSFrançois Tigeot 	spin_lock(&mm->unused_lock);
6915718399fSFrançois Tigeot 	list_for_each_entry_safe(entry, next, &mm->unused_nodes, node_list) {
6925718399fSFrançois Tigeot 		list_del(&entry->node_list);
693b6fbc077SFrançois Tigeot 		kfree(entry);
6943f6063ccSDavid Shao 		--mm->num_unused;
6953f6063ccSDavid Shao 	}
696ea132f0fSFrançois Tigeot 	spin_unlock(&mm->unused_lock);
6973f6063ccSDavid Shao 
698ea132f0fSFrançois Tigeot 	BUG_ON(mm->num_unused != 0);
6993f6063ccSDavid Shao }
700ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_takedown);
7015718399fSFrançois Tigeot 
702*9edbd4a0SFrançois Tigeot void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
7035718399fSFrançois Tigeot {
704*9edbd4a0SFrançois Tigeot 	struct drm_mm_node *entry;
705*9edbd4a0SFrançois Tigeot 	unsigned long total_used = 0, total_free = 0, total = 0;
7065718399fSFrançois Tigeot 	unsigned long hole_start, hole_end, hole_size;
7075718399fSFrançois Tigeot 
708*9edbd4a0SFrançois Tigeot 	hole_start = drm_mm_hole_node_start(&mm->head_node);
709*9edbd4a0SFrançois Tigeot 	hole_end = drm_mm_hole_node_end(&mm->head_node);
710*9edbd4a0SFrançois Tigeot 	hole_size = hole_end - hole_start;
711*9edbd4a0SFrançois Tigeot 	if (hole_size)
712*9edbd4a0SFrançois Tigeot 		printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n",
713*9edbd4a0SFrançois Tigeot 			prefix, hole_start, hole_end,
714*9edbd4a0SFrançois Tigeot 			hole_size);
715*9edbd4a0SFrançois Tigeot 	total_free += hole_size;
716*9edbd4a0SFrançois Tigeot 
717*9edbd4a0SFrançois Tigeot 	drm_mm_for_each_node(entry, mm) {
718*9edbd4a0SFrançois Tigeot 		printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: used\n",
719*9edbd4a0SFrançois Tigeot 			prefix, entry->start, entry->start + entry->size,
720*9edbd4a0SFrançois Tigeot 			entry->size);
721*9edbd4a0SFrançois Tigeot 		total_used += entry->size;
722*9edbd4a0SFrançois Tigeot 
7235718399fSFrançois Tigeot 		if (entry->hole_follows) {
7245718399fSFrançois Tigeot 			hole_start = drm_mm_hole_node_start(entry);
7255718399fSFrançois Tigeot 			hole_end = drm_mm_hole_node_end(entry);
7265718399fSFrançois Tigeot 			hole_size = hole_end - hole_start;
727*9edbd4a0SFrançois Tigeot 			printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n",
7285718399fSFrançois Tigeot 				prefix, hole_start, hole_end,
7295718399fSFrançois Tigeot 				hole_size);
730*9edbd4a0SFrançois Tigeot 			total_free += hole_size;
7315718399fSFrançois Tigeot 		}
7325718399fSFrançois Tigeot 	}
7335718399fSFrançois Tigeot 	total = total_free + total_used;
7345718399fSFrançois Tigeot 
735*9edbd4a0SFrançois Tigeot 	printk(KERN_DEBUG "%s total: %lu, used %lu free %lu\n", prefix, total,
7365718399fSFrançois Tigeot 		total_used, total_free);
7375718399fSFrançois Tigeot }
738ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_debug_table);
739ea132f0fSFrançois Tigeot 
740ea132f0fSFrançois Tigeot #if defined(CONFIG_DEBUG_FS)
741b6fbc077SFrançois Tigeot static unsigned long drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *entry)
742ea132f0fSFrançois Tigeot {
743ea132f0fSFrançois Tigeot 	unsigned long hole_start, hole_end, hole_size;
744ea132f0fSFrançois Tigeot 
745ea132f0fSFrançois Tigeot 	if (entry->hole_follows) {
746ea132f0fSFrançois Tigeot 		hole_start = drm_mm_hole_node_start(entry);
747ea132f0fSFrançois Tigeot 		hole_end = drm_mm_hole_node_end(entry);
748ea132f0fSFrançois Tigeot 		hole_size = hole_end - hole_start;
749ea132f0fSFrançois Tigeot 		seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n",
750ea132f0fSFrançois Tigeot 				hole_start, hole_end, hole_size);
751b6fbc077SFrançois Tigeot 		return hole_size;
752ea132f0fSFrançois Tigeot 	}
753b6fbc077SFrançois Tigeot 
754b6fbc077SFrançois Tigeot 	return 0;
755b6fbc077SFrançois Tigeot }
756b6fbc077SFrançois Tigeot 
757b6fbc077SFrançois Tigeot int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
758b6fbc077SFrançois Tigeot {
759b6fbc077SFrançois Tigeot 	struct drm_mm_node *entry;
760b6fbc077SFrançois Tigeot 	unsigned long total_used = 0, total_free = 0, total = 0;
761b6fbc077SFrançois Tigeot 
762b6fbc077SFrançois Tigeot 	total_free += drm_mm_dump_hole(m, &mm->head_node);
763b6fbc077SFrançois Tigeot 
764b6fbc077SFrançois Tigeot 	drm_mm_for_each_node(entry, mm) {
765b6fbc077SFrançois Tigeot 		seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: used\n",
766b6fbc077SFrançois Tigeot 				entry->start, entry->start + entry->size,
767b6fbc077SFrançois Tigeot 				entry->size);
768b6fbc077SFrançois Tigeot 		total_used += entry->size;
769b6fbc077SFrançois Tigeot 		total_free += drm_mm_dump_hole(m, entry);
770ea132f0fSFrançois Tigeot 	}
771ea132f0fSFrançois Tigeot 	total = total_free + total_used;
772ea132f0fSFrançois Tigeot 
773ea132f0fSFrançois Tigeot 	seq_printf(m, "total: %lu, used %lu free %lu\n", total, total_used, total_free);
774ea132f0fSFrançois Tigeot 	return 0;
775ea132f0fSFrançois Tigeot }
776ea132f0fSFrançois Tigeot EXPORT_SYMBOL(drm_mm_dump_table);
777ea132f0fSFrançois Tigeot #endif
778