xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/drm_mm.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /**************************************************************************
2  *
3  * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
22  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24  * USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  *
27  **************************************************************************/
28 
29 /*
30  * Generic simple memory manager implementation. Intended to be used as a base
31  * class implementation for more advanced memory managers.
32  *
33  * Note that the algorithm used is quite simple and there might be substantial
34  * performance gains if a smarter free list is implemented. Currently it is just an
35  * unordered stack of free regions. This could easily be improved if an RB-tree
36  * is used instead. At least if we expect heavy fragmentation.
37  *
38  * Aligned allocations can also see improvement.
39  *
40  * Authors:
41  * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
42  */
43 
44 #include <drm/drmP.h>
45 #include <drm/drm_mm.h>
46 #include <linux/slab.h>
47 #include <linux/seq_file.h>
48 #include <linux/export.h>
49 #include <linux/printk.h>
50 #include <asm/bug.h>
51 
52 #define MM_UNUSED_TARGET 4
53 
54 static struct drm_mm_node *drm_mm_kmalloc(struct drm_mm *mm, int atomic)
55 {
56 	struct drm_mm_node *child;
57 
58 	if (atomic)
59 		child = kzalloc(sizeof(*child), GFP_ATOMIC);
60 	else
61 		child = kzalloc(sizeof(*child), GFP_KERNEL);
62 
63 	if (unlikely(child == NULL)) {
64 		spin_lock(&mm->unused_lock);
65 		if (list_empty(&mm->unused_nodes))
66 			child = NULL;
67 		else {
68 			child =
69 			    list_entry(mm->unused_nodes.next,
70 				       struct drm_mm_node, node_list);
71 			list_del(&child->node_list);
72 			--mm->num_unused;
73 		}
74 		spin_unlock(&mm->unused_lock);
75 	}
76 	return child;
77 }
78 
79 /* drm_mm_pre_get() - pre allocate drm_mm_node structure
80  * drm_mm:	memory manager struct we are pre-allocating for
81  *
82  * Returns 0 on success or -ENOMEM if allocation fails.
83  */
84 int drm_mm_pre_get(struct drm_mm *mm)
85 {
86 	struct drm_mm_node *node;
87 
88 	spin_lock(&mm->unused_lock);
89 	while (mm->num_unused < MM_UNUSED_TARGET) {
90 		spin_unlock(&mm->unused_lock);
91 		node = kzalloc(sizeof(*node), GFP_KERNEL);
92 		spin_lock(&mm->unused_lock);
93 
94 		if (unlikely(node == NULL)) {
95 			int ret = (mm->num_unused < 2) ? -ENOMEM : 0;
96 			spin_unlock(&mm->unused_lock);
97 			return ret;
98 		}
99 		++mm->num_unused;
100 		list_add_tail(&node->node_list, &mm->unused_nodes);
101 	}
102 	spin_unlock(&mm->unused_lock);
103 	return 0;
104 }
105 EXPORT_SYMBOL(drm_mm_pre_get);
106 
107 static inline unsigned long drm_mm_hole_node_start(struct drm_mm_node *hole_node)
108 {
109 	return hole_node->start + hole_node->size;
110 }
111 
112 static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node)
113 {
114 	struct drm_mm_node *next_node =
115 		list_entry(hole_node->node_list.next, struct drm_mm_node,
116 			   node_list);
117 
118 	return next_node->start;
119 }
120 
121 static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
122 				 struct drm_mm_node *node,
123 				 unsigned long size, unsigned alignment,
124 				 unsigned long color)
125 {
126 	struct drm_mm *mm = hole_node->mm;
127 	unsigned long hole_start = drm_mm_hole_node_start(hole_node);
128 	unsigned long hole_end = drm_mm_hole_node_end(hole_node);
129 	unsigned long adj_start = hole_start;
130 	unsigned long adj_end = hole_end;
131 
132 	BUG_ON(!hole_node->hole_follows || node->allocated);
133 
134 	if (mm->color_adjust)
135 		mm->color_adjust(hole_node, color, &adj_start, &adj_end);
136 
137 	if (alignment) {
138 		unsigned tmp = adj_start % alignment;
139 		if (tmp)
140 			adj_start += alignment - tmp;
141 	}
142 
143 	if (adj_start == hole_start) {
144 		hole_node->hole_follows = 0;
145 		list_del(&hole_node->hole_stack);
146 	}
147 
148 	node->start = adj_start;
149 	node->size = size;
150 	node->mm = mm;
151 	node->color = color;
152 	node->allocated = 1;
153 
154 	INIT_LIST_HEAD(&node->hole_stack);
155 	list_add(&node->node_list, &hole_node->node_list);
156 
157 	BUG_ON(node->start + node->size > adj_end);
158 
159 	node->hole_follows = 0;
160 	if (node->start + node->size < hole_end) {
161 		list_add(&node->hole_stack, &mm->hole_stack);
162 		node->hole_follows = 1;
163 	}
164 }
165 
166 struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node,
167 					     unsigned long size,
168 					     unsigned alignment,
169 					     unsigned long color,
170 					     int atomic)
171 {
172 	struct drm_mm_node *node;
173 
174 	node = drm_mm_kmalloc(hole_node->mm, atomic);
175 	if (unlikely(node == NULL))
176 		return NULL;
177 
178 	drm_mm_insert_helper(hole_node, node, size, alignment, color);
179 
180 	return node;
181 }
182 EXPORT_SYMBOL(drm_mm_get_block_generic);
183 
184 /**
185  * Search for free space and insert a preallocated memory node. Returns
186  * -ENOSPC if no suitable free area is available. The preallocated memory node
187  * must be cleared.
188  */
189 int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node,
190 			       unsigned long size, unsigned alignment,
191 			       unsigned long color)
192 {
193 	struct drm_mm_node *hole_node;
194 
195 	hole_node = drm_mm_search_free_generic(mm, size, alignment,
196 					       color, 0);
197 	if (!hole_node)
198 		return -ENOSPC;
199 
200 	drm_mm_insert_helper(hole_node, node, size, alignment, color);
201 	return 0;
202 }
203 EXPORT_SYMBOL(drm_mm_insert_node_generic);
204 
205 int drm_mm_insert_node(struct drm_mm *mm, struct drm_mm_node *node,
206 		       unsigned long size, unsigned alignment)
207 {
208 	return drm_mm_insert_node_generic(mm, node, size, alignment, 0);
209 }
210 EXPORT_SYMBOL(drm_mm_insert_node);
211 
212 static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
213 				       struct drm_mm_node *node,
214 				       unsigned long size, unsigned alignment,
215 				       unsigned long color,
216 				       unsigned long start, unsigned long end)
217 {
218 	struct drm_mm *mm = hole_node->mm;
219 	unsigned long hole_start = drm_mm_hole_node_start(hole_node);
220 	unsigned long hole_end = drm_mm_hole_node_end(hole_node);
221 	unsigned long adj_start = hole_start;
222 	unsigned long adj_end = hole_end;
223 
224 	BUG_ON(!hole_node->hole_follows || node->allocated);
225 
226 	if (adj_start < start)
227 		adj_start = start;
228 	if (adj_end > end)
229 		adj_end = end;
230 
231 	if (mm->color_adjust)
232 		mm->color_adjust(hole_node, color, &adj_start, &adj_end);
233 
234 	if (alignment) {
235 		unsigned tmp = adj_start % alignment;
236 		if (tmp)
237 			adj_start += alignment - tmp;
238 	}
239 
240 	if (adj_start == hole_start) {
241 		hole_node->hole_follows = 0;
242 		list_del(&hole_node->hole_stack);
243 	}
244 
245 	node->start = adj_start;
246 	node->size = size;
247 	node->mm = mm;
248 	node->color = color;
249 	node->allocated = 1;
250 
251 	INIT_LIST_HEAD(&node->hole_stack);
252 	list_add(&node->node_list, &hole_node->node_list);
253 
254 	BUG_ON(node->start + node->size > adj_end);
255 	BUG_ON(node->start + node->size > end);
256 
257 	node->hole_follows = 0;
258 	if (node->start + node->size < hole_end) {
259 		list_add(&node->hole_stack, &mm->hole_stack);
260 		node->hole_follows = 1;
261 	}
262 }
263 
264 struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *hole_node,
265 						unsigned long size,
266 						unsigned alignment,
267 						unsigned long color,
268 						unsigned long start,
269 						unsigned long end,
270 						int atomic)
271 {
272 	struct drm_mm_node *node;
273 
274 	node = drm_mm_kmalloc(hole_node->mm, atomic);
275 	if (unlikely(node == NULL))
276 		return NULL;
277 
278 	drm_mm_insert_helper_range(hole_node, node, size, alignment, color,
279 				   start, end);
280 
281 	return node;
282 }
283 EXPORT_SYMBOL(drm_mm_get_block_range_generic);
284 
285 /**
286  * Search for free space and insert a preallocated memory node. Returns
287  * -ENOSPC if no suitable free area is available. This is for range
288  * restricted allocations. The preallocated memory node must be cleared.
289  */
290 int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node,
291 					unsigned long size, unsigned alignment, unsigned long color,
292 					unsigned long start, unsigned long end)
293 {
294 	struct drm_mm_node *hole_node;
295 
296 	hole_node = drm_mm_search_free_in_range_generic(mm,
297 							size, alignment, color,
298 							start, end, 0);
299 	if (!hole_node)
300 		return -ENOSPC;
301 
302 	drm_mm_insert_helper_range(hole_node, node,
303 				   size, alignment, color,
304 				   start, end);
305 	return 0;
306 }
307 EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic);
308 
309 int drm_mm_insert_node_in_range(struct drm_mm *mm, struct drm_mm_node *node,
310 				unsigned long size, unsigned alignment,
311 				unsigned long start, unsigned long end)
312 {
313 	return drm_mm_insert_node_in_range_generic(mm, node, size, alignment, 0, start, end);
314 }
315 EXPORT_SYMBOL(drm_mm_insert_node_in_range);
316 
317 /**
318  * Remove a memory node from the allocator.
319  */
320 void drm_mm_remove_node(struct drm_mm_node *node)
321 {
322 	struct drm_mm *mm = node->mm;
323 	struct drm_mm_node *prev_node;
324 
325 	BUG_ON(node->scanned_block || node->scanned_prev_free
326 				   || node->scanned_next_free);
327 
328 	prev_node =
329 	    list_entry(node->node_list.prev, struct drm_mm_node, node_list);
330 
331 	if (node->hole_follows) {
332 		BUG_ON(drm_mm_hole_node_start(node)
333 				== drm_mm_hole_node_end(node));
334 		list_del(&node->hole_stack);
335 	} else
336 		BUG_ON(drm_mm_hole_node_start(node)
337 				!= drm_mm_hole_node_end(node));
338 
339 	if (!prev_node->hole_follows) {
340 		prev_node->hole_follows = 1;
341 		list_add(&prev_node->hole_stack, &mm->hole_stack);
342 	} else
343 		list_move(&prev_node->hole_stack, &mm->hole_stack);
344 
345 	list_del(&node->node_list);
346 	node->allocated = 0;
347 }
348 EXPORT_SYMBOL(drm_mm_remove_node);
349 
350 /*
351  * Remove a memory node from the allocator and free the allocated struct
352  * drm_mm_node. Only to be used on a struct drm_mm_node obtained by one of the
353  * drm_mm_get_block functions.
354  */
355 void drm_mm_put_block(struct drm_mm_node *node)
356 {
357 
358 	struct drm_mm *mm = node->mm;
359 
360 	drm_mm_remove_node(node);
361 
362 	spin_lock(&mm->unused_lock);
363 	if (mm->num_unused < MM_UNUSED_TARGET) {
364 		list_add(&node->node_list, &mm->unused_nodes);
365 		++mm->num_unused;
366 	} else
367 		kfree(node);
368 	spin_unlock(&mm->unused_lock);
369 }
370 EXPORT_SYMBOL(drm_mm_put_block);
371 
372 static int check_free_hole(unsigned long start, unsigned long end,
373 			   unsigned long size, unsigned alignment)
374 {
375 	if (end - start < size)
376 		return 0;
377 
378 	if (alignment) {
379 		unsigned tmp = start % alignment;
380 		if (tmp)
381 			start += alignment - tmp;
382 	}
383 
384 	return end >= start + size;
385 }
386 
387 struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
388 					       unsigned long size,
389 					       unsigned alignment,
390 					       unsigned long color,
391 					       bool best_match)
392 {
393 	struct drm_mm_node *entry;
394 	struct drm_mm_node *best;
395 	unsigned long best_size;
396 
397 	BUG_ON(mm->scanned_blocks);
398 
399 	best = NULL;
400 	best_size = ~0UL;
401 
402 	list_for_each_entry(entry, &mm->hole_stack, hole_stack) {
403 		unsigned long adj_start = drm_mm_hole_node_start(entry);
404 		unsigned long adj_end = drm_mm_hole_node_end(entry);
405 
406 		if (mm->color_adjust) {
407 			mm->color_adjust(entry, color, &adj_start, &adj_end);
408 			if (adj_end <= adj_start)
409 				continue;
410 		}
411 
412 		BUG_ON(!entry->hole_follows);
413 		if (!check_free_hole(adj_start, adj_end, size, alignment))
414 			continue;
415 
416 		if (!best_match)
417 			return entry;
418 
419 		if (entry->size < best_size) {
420 			best = entry;
421 			best_size = entry->size;
422 		}
423 	}
424 
425 	return best;
426 }
427 EXPORT_SYMBOL(drm_mm_search_free_generic);
428 
429 struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
430 							unsigned long size,
431 							unsigned alignment,
432 							unsigned long color,
433 							unsigned long start,
434 							unsigned long end,
435 							bool best_match)
436 {
437 	struct drm_mm_node *entry;
438 	struct drm_mm_node *best;
439 	unsigned long best_size;
440 
441 	BUG_ON(mm->scanned_blocks);
442 
443 	best = NULL;
444 	best_size = ~0UL;
445 
446 	list_for_each_entry(entry, &mm->hole_stack, hole_stack) {
447 		unsigned long adj_start = drm_mm_hole_node_start(entry) < start ?
448 			start : drm_mm_hole_node_start(entry);
449 		unsigned long adj_end = drm_mm_hole_node_end(entry) > end ?
450 			end : drm_mm_hole_node_end(entry);
451 
452 		BUG_ON(!entry->hole_follows);
453 
454 		if (mm->color_adjust) {
455 			mm->color_adjust(entry, color, &adj_start, &adj_end);
456 			if (adj_end <= adj_start)
457 				continue;
458 		}
459 
460 		if (!check_free_hole(adj_start, adj_end, size, alignment))
461 			continue;
462 
463 		if (!best_match)
464 			return entry;
465 
466 		if (entry->size < best_size) {
467 			best = entry;
468 			best_size = entry->size;
469 		}
470 	}
471 
472 	return best;
473 }
474 EXPORT_SYMBOL(drm_mm_search_free_in_range_generic);
475 
476 /**
477  * Moves an allocation. To be used with embedded struct drm_mm_node.
478  */
479 void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
480 {
481 	list_replace(&old->node_list, &new->node_list);
482 	list_replace(&old->hole_stack, &new->hole_stack);
483 	new->hole_follows = old->hole_follows;
484 	new->mm = old->mm;
485 	new->start = old->start;
486 	new->size = old->size;
487 	new->color = old->color;
488 
489 	old->allocated = 0;
490 	new->allocated = 1;
491 }
492 EXPORT_SYMBOL(drm_mm_replace_node);
493 
494 /**
495  * Initializa lru scanning.
496  *
497  * This simply sets up the scanning routines with the parameters for the desired
498  * hole.
499  *
500  * Warning: As long as the scan list is non-empty, no other operations than
501  * adding/removing nodes to/from the scan list are allowed.
502  */
503 void drm_mm_init_scan(struct drm_mm *mm,
504 		      unsigned long size,
505 		      unsigned alignment,
506 		      unsigned long color)
507 {
508 	mm->scan_color = color;
509 	mm->scan_alignment = alignment;
510 	mm->scan_size = size;
511 	mm->scanned_blocks = 0;
512 	mm->scan_hit_start = 0;
513 	mm->scan_hit_end = 0;
514 	mm->scan_check_range = 0;
515 	mm->prev_scanned_node = NULL;
516 }
517 EXPORT_SYMBOL(drm_mm_init_scan);
518 
519 /**
520  * Initializa lru scanning.
521  *
522  * This simply sets up the scanning routines with the parameters for the desired
523  * hole. This version is for range-restricted scans.
524  *
525  * Warning: As long as the scan list is non-empty, no other operations than
526  * adding/removing nodes to/from the scan list are allowed.
527  */
528 void drm_mm_init_scan_with_range(struct drm_mm *mm,
529 				 unsigned long size,
530 				 unsigned alignment,
531 				 unsigned long color,
532 				 unsigned long start,
533 				 unsigned long end)
534 {
535 	mm->scan_color = color;
536 	mm->scan_alignment = alignment;
537 	mm->scan_size = size;
538 	mm->scanned_blocks = 0;
539 	mm->scan_hit_start = 0;
540 	mm->scan_hit_end = 0;
541 	mm->scan_start = start;
542 	mm->scan_end = end;
543 	mm->scan_check_range = 1;
544 	mm->prev_scanned_node = NULL;
545 }
546 EXPORT_SYMBOL(drm_mm_init_scan_with_range);
547 
548 /**
549  * Add a node to the scan list that might be freed to make space for the desired
550  * hole.
551  *
552  * Returns non-zero, if a hole has been found, zero otherwise.
553  */
554 int drm_mm_scan_add_block(struct drm_mm_node *node)
555 {
556 	struct drm_mm *mm = node->mm;
557 	struct drm_mm_node *prev_node;
558 	unsigned long hole_start, hole_end;
559 	unsigned long adj_start, adj_end;
560 
561 	mm->scanned_blocks++;
562 
563 	BUG_ON(node->scanned_block);
564 	node->scanned_block = 1;
565 
566 	prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
567 			       node_list);
568 
569 	node->scanned_preceeds_hole = prev_node->hole_follows;
570 	prev_node->hole_follows = 1;
571 	list_del(&node->node_list);
572 	node->node_list.prev = &prev_node->node_list;
573 	node->node_list.next = &mm->prev_scanned_node->node_list;
574 	mm->prev_scanned_node = node;
575 
576 	adj_start = hole_start = drm_mm_hole_node_start(prev_node);
577 	adj_end = hole_end = drm_mm_hole_node_end(prev_node);
578 
579 	if (mm->scan_check_range) {
580 		if (adj_start < mm->scan_start)
581 			adj_start = mm->scan_start;
582 		if (adj_end > mm->scan_end)
583 			adj_end = mm->scan_end;
584 	}
585 
586 	if (mm->color_adjust)
587 		mm->color_adjust(prev_node, mm->scan_color,
588 				 &adj_start, &adj_end);
589 
590 	if (check_free_hole(adj_start, adj_end,
591 			    mm->scan_size, mm->scan_alignment)) {
592 		mm->scan_hit_start = hole_start;
593 		mm->scan_hit_end = hole_end;
594 		return 1;
595 	}
596 
597 	return 0;
598 }
599 EXPORT_SYMBOL(drm_mm_scan_add_block);
600 
601 /**
602  * Remove a node from the scan list.
603  *
604  * Nodes _must_ be removed in the exact same order from the scan list as they
605  * have been added, otherwise the internal state of the memory manager will be
606  * corrupted.
607  *
608  * When the scan list is empty, the selected memory nodes can be freed. An
609  * immediately following drm_mm_search_free with best_match = 0 will then return
610  * the just freed block (because its at the top of the free_stack list).
611  *
612  * Returns one if this block should be evicted, zero otherwise. Will always
613  * return zero when no hole has been found.
614  */
615 int drm_mm_scan_remove_block(struct drm_mm_node *node)
616 {
617 	struct drm_mm *mm = node->mm;
618 	struct drm_mm_node *prev_node;
619 
620 	mm->scanned_blocks--;
621 
622 	BUG_ON(!node->scanned_block);
623 	node->scanned_block = 0;
624 
625 	prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
626 			       node_list);
627 
628 	prev_node->hole_follows = node->scanned_preceeds_hole;
629 	list_add(&node->node_list, &prev_node->node_list);
630 
631 	 return (drm_mm_hole_node_end(node) > mm->scan_hit_start &&
632 		 node->start < mm->scan_hit_end);
633 }
634 EXPORT_SYMBOL(drm_mm_scan_remove_block);
635 
636 int drm_mm_clean(struct drm_mm * mm)
637 {
638 	struct list_head *head = &mm->head_node.node_list;
639 
640 	return (head->next->next == head);
641 }
642 EXPORT_SYMBOL(drm_mm_clean);
643 
644 int drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
645 {
646 	INIT_LIST_HEAD(&mm->hole_stack);
647 	INIT_LIST_HEAD(&mm->unused_nodes);
648 	mm->num_unused = 0;
649 	mm->scanned_blocks = 0;
650 	spin_lock_init(&mm->unused_lock);
651 
652 	/* Clever trick to avoid a special case in the free hole tracking. */
653 	INIT_LIST_HEAD(&mm->head_node.node_list);
654 	INIT_LIST_HEAD(&mm->head_node.hole_stack);
655 	mm->head_node.hole_follows = 1;
656 	mm->head_node.scanned_block = 0;
657 	mm->head_node.scanned_prev_free = 0;
658 	mm->head_node.scanned_next_free = 0;
659 	mm->head_node.mm = mm;
660 	mm->head_node.start = start + size;
661 	mm->head_node.size = start - mm->head_node.start;
662 	list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack);
663 
664 	mm->color_adjust = NULL;
665 
666 	return 0;
667 }
668 EXPORT_SYMBOL(drm_mm_init);
669 
670 void drm_mm_takedown(struct drm_mm * mm)
671 {
672 	struct drm_mm_node *entry, *next;
673 
674 	BUG_ON(!list_empty(&mm->head_node.node_list));
675 
676 	spin_lock(&mm->unused_lock);
677 	list_for_each_entry_safe(entry, next, &mm->unused_nodes, node_list) {
678 		list_del(&entry->node_list);
679 		kfree(entry);
680 		--mm->num_unused;
681 	}
682 	spin_unlock(&mm->unused_lock);
683 
684 	/*
685 	 * XXX The locking above can't be right -- either it is
686 	 * unnecessary or it is insufficient.
687 	 */
688 	spin_lock_destroy(&mm->unused_lock);
689 
690 	BUG_ON(mm->num_unused != 0);
691 }
692 EXPORT_SYMBOL(drm_mm_takedown);
693 
694 void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
695 {
696 	struct drm_mm_node *entry;
697 	unsigned long total_used = 0, total_free = 0, total = 0;
698 	unsigned long hole_start, hole_end, hole_size;
699 
700 	hole_start = drm_mm_hole_node_start(&mm->head_node);
701 	hole_end = drm_mm_hole_node_end(&mm->head_node);
702 	hole_size = hole_end - hole_start;
703 	if (hole_size)
704 		printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n",
705 			prefix, hole_start, hole_end,
706 			hole_size);
707 	total_free += hole_size;
708 
709 	drm_mm_for_each_node(entry, mm) {
710 		printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: used\n",
711 			prefix, entry->start, entry->start + entry->size,
712 			entry->size);
713 		total_used += entry->size;
714 
715 		if (entry->hole_follows) {
716 			hole_start = drm_mm_hole_node_start(entry);
717 			hole_end = drm_mm_hole_node_end(entry);
718 			hole_size = hole_end - hole_start;
719 			printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n",
720 				prefix, hole_start, hole_end,
721 				hole_size);
722 			total_free += hole_size;
723 		}
724 	}
725 	total = total_free + total_used;
726 
727 	printk(KERN_DEBUG "%s total: %lu, used %lu free %lu\n", prefix, total,
728 		total_used, total_free);
729 }
730 EXPORT_SYMBOL(drm_mm_debug_table);
731 
732 #if defined(CONFIG_DEBUG_FS)
733 int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
734 {
735 	struct drm_mm_node *entry;
736 	unsigned long total_used = 0, total_free = 0, total = 0;
737 	unsigned long hole_start, hole_end, hole_size;
738 
739 	hole_start = drm_mm_hole_node_start(&mm->head_node);
740 	hole_end = drm_mm_hole_node_end(&mm->head_node);
741 	hole_size = hole_end - hole_start;
742 	if (hole_size)
743 		seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n",
744 				hole_start, hole_end, hole_size);
745 	total_free += hole_size;
746 
747 	drm_mm_for_each_node(entry, mm) {
748 		seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: used\n",
749 				entry->start, entry->start + entry->size,
750 				entry->size);
751 		total_used += entry->size;
752 		if (entry->hole_follows) {
753 			hole_start = drm_mm_hole_node_start(entry);
754 			hole_end = drm_mm_hole_node_end(entry);
755 			hole_size = hole_end - hole_start;
756 			seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n",
757 					hole_start, hole_end, hole_size);
758 			total_free += hole_size;
759 		}
760 	}
761 	total = total_free + total_used;
762 
763 	seq_printf(m, "total: %lu, used %lu free %lu\n", total, total_used, total_free);
764 	return 0;
765 }
766 EXPORT_SYMBOL(drm_mm_dump_table);
767 #endif
768