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