Home | History | Annotate | Line # | Download | only in drm
drm_mm.c revision 1.3.28.1
      1  1.3.28.1  pgoyette /*	$NetBSD: drm_mm.c,v 1.3.28.1 2018/09/06 06:56:09 pgoyette Exp $	*/
      2  1.3.28.1  pgoyette 
      3       1.1  riastrad /**************************************************************************
      4       1.1  riastrad  *
      5       1.1  riastrad  * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
      6       1.1  riastrad  * All Rights Reserved.
      7       1.1  riastrad  *
      8       1.1  riastrad  * Permission is hereby granted, free of charge, to any person obtaining a
      9       1.1  riastrad  * copy of this software and associated documentation files (the
     10       1.1  riastrad  * "Software"), to deal in the Software without restriction, including
     11       1.1  riastrad  * without limitation the rights to use, copy, modify, merge, publish,
     12       1.1  riastrad  * distribute, sub license, and/or sell copies of the Software, and to
     13       1.1  riastrad  * permit persons to whom the Software is furnished to do so, subject to
     14       1.1  riastrad  * the following conditions:
     15       1.1  riastrad  *
     16       1.1  riastrad  * The above copyright notice and this permission notice (including the
     17       1.1  riastrad  * next paragraph) shall be included in all copies or substantial portions
     18       1.1  riastrad  * of the Software.
     19       1.1  riastrad  *
     20       1.1  riastrad  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     21       1.1  riastrad  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     22       1.1  riastrad  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
     23       1.1  riastrad  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
     24       1.1  riastrad  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
     25       1.1  riastrad  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
     26       1.1  riastrad  * USE OR OTHER DEALINGS IN THE SOFTWARE.
     27       1.1  riastrad  *
     28       1.1  riastrad  *
     29       1.1  riastrad  **************************************************************************/
     30       1.1  riastrad 
     31       1.1  riastrad /*
     32       1.1  riastrad  * Generic simple memory manager implementation. Intended to be used as a base
     33       1.1  riastrad  * class implementation for more advanced memory managers.
     34       1.1  riastrad  *
     35       1.1  riastrad  * Note that the algorithm used is quite simple and there might be substantial
     36       1.1  riastrad  * performance gains if a smarter free list is implemented. Currently it is just an
     37       1.1  riastrad  * unordered stack of free regions. This could easily be improved if an RB-tree
     38       1.1  riastrad  * is used instead. At least if we expect heavy fragmentation.
     39       1.1  riastrad  *
     40       1.1  riastrad  * Aligned allocations can also see improvement.
     41       1.1  riastrad  *
     42       1.1  riastrad  * Authors:
     43       1.1  riastrad  * Thomas Hellstrm <thomas-at-tungstengraphics-dot-com>
     44       1.1  riastrad  */
     45       1.1  riastrad 
     46  1.3.28.1  pgoyette #include <sys/cdefs.h>
     47  1.3.28.1  pgoyette __KERNEL_RCSID(0, "$NetBSD: drm_mm.c,v 1.3.28.1 2018/09/06 06:56:09 pgoyette Exp $");
     48  1.3.28.1  pgoyette 
     49       1.1  riastrad #include <drm/drmP.h>
     50       1.1  riastrad #include <drm/drm_mm.h>
     51       1.1  riastrad #include <linux/slab.h>
     52       1.1  riastrad #include <linux/seq_file.h>
     53       1.1  riastrad #include <linux/export.h>
     54       1.2  riastrad #include <linux/printk.h>
     55       1.2  riastrad #include <asm/bug.h>
     56  1.3.28.1  pgoyette #include <asm/div64.h>
     57       1.1  riastrad 
     58       1.3  riastrad /**
     59       1.3  riastrad  * DOC: Overview
     60       1.3  riastrad  *
     61       1.3  riastrad  * drm_mm provides a simple range allocator. The drivers are free to use the
     62       1.3  riastrad  * resource allocator from the linux core if it suits them, the upside of drm_mm
     63       1.3  riastrad  * is that it's in the DRM core. Which means that it's easier to extend for
     64       1.3  riastrad  * some of the crazier special purpose needs of gpus.
     65       1.3  riastrad  *
     66       1.3  riastrad  * The main data struct is &drm_mm, allocations are tracked in &drm_mm_node.
     67       1.3  riastrad  * Drivers are free to embed either of them into their own suitable
     68       1.3  riastrad  * datastructures. drm_mm itself will not do any allocations of its own, so if
     69       1.3  riastrad  * drivers choose not to embed nodes they need to still allocate them
     70       1.3  riastrad  * themselves.
     71       1.3  riastrad  *
     72       1.3  riastrad  * The range allocator also supports reservation of preallocated blocks. This is
     73       1.3  riastrad  * useful for taking over initial mode setting configurations from the firmware,
     74       1.3  riastrad  * where an object needs to be created which exactly matches the firmware's
     75       1.3  riastrad  * scanout target. As long as the range is still free it can be inserted anytime
     76       1.3  riastrad  * after the allocator is initialized, which helps with avoiding looped
     77       1.3  riastrad  * depencies in the driver load sequence.
     78       1.3  riastrad  *
     79       1.3  riastrad  * drm_mm maintains a stack of most recently freed holes, which of all
     80       1.3  riastrad  * simplistic datastructures seems to be a fairly decent approach to clustering
     81       1.3  riastrad  * allocations and avoiding too much fragmentation. This means free space
     82       1.3  riastrad  * searches are O(num_holes). Given that all the fancy features drm_mm supports
     83       1.3  riastrad  * something better would be fairly complex and since gfx thrashing is a fairly
     84       1.3  riastrad  * steep cliff not a real concern. Removing a node again is O(1).
     85       1.3  riastrad  *
     86       1.3  riastrad  * drm_mm supports a few features: Alignment and range restrictions can be
     87       1.3  riastrad  * supplied. Further more every &drm_mm_node has a color value (which is just an
     88       1.3  riastrad  * opaqua unsigned long) which in conjunction with a driver callback can be used
     89       1.3  riastrad  * to implement sophisticated placement restrictions. The i915 DRM driver uses
     90       1.3  riastrad  * this to implement guard pages between incompatible caching domains in the
     91       1.3  riastrad  * graphics TT.
     92       1.3  riastrad  *
     93       1.3  riastrad  * Two behaviors are supported for searching and allocating: bottom-up and top-down.
     94       1.3  riastrad  * The default is bottom-up. Top-down allocation can be used if the memory area
     95       1.3  riastrad  * has different restrictions, or just to reduce fragmentation.
     96       1.1  riastrad  *
     97       1.3  riastrad  * Finally iteration helpers to walk all nodes and all holes are provided as are
     98       1.3  riastrad  * some basic allocator dumpers for debugging.
     99       1.1  riastrad  */
    100       1.1  riastrad 
    101       1.3  riastrad static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
    102  1.3.28.1  pgoyette 						u64 size,
    103       1.3  riastrad 						unsigned alignment,
    104       1.3  riastrad 						unsigned long color,
    105       1.3  riastrad 						enum drm_mm_search_flags flags);
    106       1.3  riastrad static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
    107  1.3.28.1  pgoyette 						u64 size,
    108       1.3  riastrad 						unsigned alignment,
    109       1.3  riastrad 						unsigned long color,
    110  1.3.28.1  pgoyette 						u64 start,
    111  1.3.28.1  pgoyette 						u64 end,
    112       1.3  riastrad 						enum drm_mm_search_flags flags);
    113       1.1  riastrad 
    114       1.1  riastrad static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
    115       1.1  riastrad 				 struct drm_mm_node *node,
    116  1.3.28.1  pgoyette 				 u64 size, unsigned alignment,
    117       1.3  riastrad 				 unsigned long color,
    118       1.3  riastrad 				 enum drm_mm_allocator_flags flags)
    119       1.1  riastrad {
    120       1.1  riastrad 	struct drm_mm *mm = hole_node->mm;
    121  1.3.28.1  pgoyette 	u64 hole_start = drm_mm_hole_node_start(hole_node);
    122  1.3.28.1  pgoyette 	u64 hole_end = drm_mm_hole_node_end(hole_node);
    123  1.3.28.1  pgoyette 	u64 adj_start = hole_start;
    124  1.3.28.1  pgoyette 	u64 adj_end = hole_end;
    125       1.1  riastrad 
    126       1.3  riastrad 	BUG_ON(node->allocated);
    127       1.1  riastrad 
    128       1.1  riastrad 	if (mm->color_adjust)
    129       1.1  riastrad 		mm->color_adjust(hole_node, color, &adj_start, &adj_end);
    130       1.1  riastrad 
    131       1.3  riastrad 	if (flags & DRM_MM_CREATE_TOP)
    132       1.3  riastrad 		adj_start = adj_end - size;
    133       1.3  riastrad 
    134       1.1  riastrad 	if (alignment) {
    135  1.3.28.1  pgoyette 		u64 tmp = adj_start;
    136  1.3.28.1  pgoyette 		unsigned rem;
    137  1.3.28.1  pgoyette 
    138  1.3.28.1  pgoyette 		rem = do_div(tmp, alignment);
    139  1.3.28.1  pgoyette 		if (rem) {
    140       1.3  riastrad 			if (flags & DRM_MM_CREATE_TOP)
    141  1.3.28.1  pgoyette 				adj_start -= rem;
    142       1.3  riastrad 			else
    143  1.3.28.1  pgoyette 				adj_start += alignment - rem;
    144       1.3  riastrad 		}
    145       1.1  riastrad 	}
    146       1.1  riastrad 
    147       1.3  riastrad 	BUG_ON(adj_start < hole_start);
    148       1.3  riastrad 	BUG_ON(adj_end > hole_end);
    149       1.3  riastrad 
    150       1.1  riastrad 	if (adj_start == hole_start) {
    151       1.1  riastrad 		hole_node->hole_follows = 0;
    152       1.1  riastrad 		list_del(&hole_node->hole_stack);
    153       1.1  riastrad 	}
    154       1.1  riastrad 
    155       1.1  riastrad 	node->start = adj_start;
    156       1.1  riastrad 	node->size = size;
    157       1.1  riastrad 	node->mm = mm;
    158       1.1  riastrad 	node->color = color;
    159       1.1  riastrad 	node->allocated = 1;
    160       1.1  riastrad 
    161       1.1  riastrad 	INIT_LIST_HEAD(&node->hole_stack);
    162       1.1  riastrad 	list_add(&node->node_list, &hole_node->node_list);
    163       1.1  riastrad 
    164       1.1  riastrad 	BUG_ON(node->start + node->size > adj_end);
    165       1.1  riastrad 
    166       1.1  riastrad 	node->hole_follows = 0;
    167       1.3  riastrad 	if (__drm_mm_hole_node_start(node) < hole_end) {
    168       1.1  riastrad 		list_add(&node->hole_stack, &mm->hole_stack);
    169       1.1  riastrad 		node->hole_follows = 1;
    170       1.1  riastrad 	}
    171       1.1  riastrad }
    172       1.1  riastrad 
    173       1.3  riastrad /**
    174       1.3  riastrad  * drm_mm_reserve_node - insert an pre-initialized node
    175       1.3  riastrad  * @mm: drm_mm allocator to insert @node into
    176       1.3  riastrad  * @node: drm_mm_node to insert
    177       1.3  riastrad  *
    178       1.3  riastrad  * This functions inserts an already set-up drm_mm_node into the allocator,
    179       1.3  riastrad  * meaning that start, size and color must be set by the caller. This is useful
    180       1.3  riastrad  * to initialize the allocator with preallocated objects which must be set-up
    181       1.3  riastrad  * before the range allocator can be set-up, e.g. when taking over a firmware
    182       1.3  riastrad  * framebuffer.
    183       1.3  riastrad  *
    184       1.3  riastrad  * Returns:
    185       1.3  riastrad  * 0 on success, -ENOSPC if there's no hole where @node is.
    186       1.3  riastrad  */
    187       1.3  riastrad int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
    188       1.1  riastrad {
    189       1.3  riastrad 	struct drm_mm_node *hole;
    190  1.3.28.1  pgoyette 	u64 end = node->start + node->size;
    191  1.3.28.1  pgoyette 	u64 hole_start;
    192  1.3.28.1  pgoyette 	u64 hole_end;
    193       1.3  riastrad 
    194       1.3  riastrad 	BUG_ON(node == NULL);
    195       1.3  riastrad 
    196       1.3  riastrad 	/* Find the relevant hole to add our node to */
    197       1.3  riastrad 	drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
    198       1.3  riastrad 		if (hole_start > node->start || hole_end < end)
    199       1.3  riastrad 			continue;
    200       1.1  riastrad 
    201       1.3  riastrad 		node->mm = mm;
    202       1.3  riastrad 		node->allocated = 1;
    203       1.1  riastrad 
    204       1.3  riastrad 		INIT_LIST_HEAD(&node->hole_stack);
    205       1.3  riastrad 		list_add(&node->node_list, &hole->node_list);
    206       1.1  riastrad 
    207       1.3  riastrad 		if (node->start == hole_start) {
    208       1.3  riastrad 			hole->hole_follows = 0;
    209       1.3  riastrad 			list_del_init(&hole->hole_stack);
    210       1.3  riastrad 		}
    211       1.3  riastrad 
    212       1.3  riastrad 		node->hole_follows = 0;
    213       1.3  riastrad 		if (end != hole_end) {
    214       1.3  riastrad 			list_add(&node->hole_stack, &mm->hole_stack);
    215       1.3  riastrad 			node->hole_follows = 1;
    216       1.3  riastrad 		}
    217       1.3  riastrad 
    218       1.3  riastrad 		return 0;
    219       1.3  riastrad 	}
    220       1.3  riastrad 
    221       1.3  riastrad 	return -ENOSPC;
    222       1.1  riastrad }
    223       1.3  riastrad EXPORT_SYMBOL(drm_mm_reserve_node);
    224       1.1  riastrad 
    225       1.1  riastrad /**
    226       1.3  riastrad  * drm_mm_insert_node_generic - search for space and insert @node
    227       1.3  riastrad  * @mm: drm_mm to allocate from
    228       1.3  riastrad  * @node: preallocate node to insert
    229       1.3  riastrad  * @size: size of the allocation
    230       1.3  riastrad  * @alignment: alignment of the allocation
    231       1.3  riastrad  * @color: opaque tag value to use for this node
    232       1.3  riastrad  * @sflags: flags to fine-tune the allocation search
    233       1.3  riastrad  * @aflags: flags to fine-tune the allocation behavior
    234       1.3  riastrad  *
    235       1.3  riastrad  * The preallocated node must be cleared to 0.
    236       1.3  riastrad  *
    237       1.3  riastrad  * Returns:
    238       1.3  riastrad  * 0 on success, -ENOSPC if there's no suitable hole.
    239       1.1  riastrad  */
    240       1.1  riastrad int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node,
    241  1.3.28.1  pgoyette 			       u64 size, unsigned alignment,
    242       1.3  riastrad 			       unsigned long color,
    243       1.3  riastrad 			       enum drm_mm_search_flags sflags,
    244       1.3  riastrad 			       enum drm_mm_allocator_flags aflags)
    245       1.1  riastrad {
    246       1.1  riastrad 	struct drm_mm_node *hole_node;
    247       1.1  riastrad 
    248       1.1  riastrad 	hole_node = drm_mm_search_free_generic(mm, size, alignment,
    249       1.3  riastrad 					       color, sflags);
    250       1.1  riastrad 	if (!hole_node)
    251       1.1  riastrad 		return -ENOSPC;
    252       1.1  riastrad 
    253       1.3  riastrad 	drm_mm_insert_helper(hole_node, node, size, alignment, color, aflags);
    254       1.1  riastrad 	return 0;
    255       1.1  riastrad }
    256       1.1  riastrad EXPORT_SYMBOL(drm_mm_insert_node_generic);
    257       1.1  riastrad 
    258       1.1  riastrad static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
    259       1.1  riastrad 				       struct drm_mm_node *node,
    260  1.3.28.1  pgoyette 				       u64 size, unsigned alignment,
    261       1.1  riastrad 				       unsigned long color,
    262  1.3.28.1  pgoyette 				       u64 start, u64 end,
    263       1.3  riastrad 				       enum drm_mm_allocator_flags flags)
    264       1.1  riastrad {
    265       1.1  riastrad 	struct drm_mm *mm = hole_node->mm;
    266  1.3.28.1  pgoyette 	u64 hole_start = drm_mm_hole_node_start(hole_node);
    267  1.3.28.1  pgoyette 	u64 hole_end = drm_mm_hole_node_end(hole_node);
    268  1.3.28.1  pgoyette 	u64 adj_start = hole_start;
    269  1.3.28.1  pgoyette 	u64 adj_end = hole_end;
    270       1.1  riastrad 
    271       1.1  riastrad 	BUG_ON(!hole_node->hole_follows || node->allocated);
    272       1.1  riastrad 
    273  1.3.28.1  pgoyette 	if (mm->color_adjust)
    274  1.3.28.1  pgoyette 		mm->color_adjust(hole_node, color, &adj_start, &adj_end);
    275  1.3.28.1  pgoyette 
    276  1.3.28.1  pgoyette 	adj_start = max(adj_start, start);
    277  1.3.28.1  pgoyette 	adj_end = min(adj_end, end);
    278       1.1  riastrad 
    279       1.3  riastrad 	if (flags & DRM_MM_CREATE_TOP)
    280       1.3  riastrad 		adj_start = adj_end - size;
    281       1.3  riastrad 
    282       1.1  riastrad 	if (alignment) {
    283  1.3.28.1  pgoyette 		u64 tmp = adj_start;
    284  1.3.28.1  pgoyette 		unsigned rem;
    285  1.3.28.1  pgoyette 
    286  1.3.28.1  pgoyette 		rem = do_div(tmp, alignment);
    287  1.3.28.1  pgoyette 		if (rem) {
    288       1.3  riastrad 			if (flags & DRM_MM_CREATE_TOP)
    289  1.3.28.1  pgoyette 				adj_start -= rem;
    290       1.3  riastrad 			else
    291  1.3.28.1  pgoyette 				adj_start += alignment - rem;
    292       1.3  riastrad 		}
    293       1.1  riastrad 	}
    294       1.1  riastrad 
    295       1.1  riastrad 	if (adj_start == hole_start) {
    296       1.1  riastrad 		hole_node->hole_follows = 0;
    297       1.1  riastrad 		list_del(&hole_node->hole_stack);
    298       1.1  riastrad 	}
    299       1.1  riastrad 
    300       1.1  riastrad 	node->start = adj_start;
    301       1.1  riastrad 	node->size = size;
    302       1.1  riastrad 	node->mm = mm;
    303       1.1  riastrad 	node->color = color;
    304       1.1  riastrad 	node->allocated = 1;
    305       1.1  riastrad 
    306       1.1  riastrad 	INIT_LIST_HEAD(&node->hole_stack);
    307       1.1  riastrad 	list_add(&node->node_list, &hole_node->node_list);
    308       1.1  riastrad 
    309       1.3  riastrad 	BUG_ON(node->start < start);
    310       1.3  riastrad 	BUG_ON(node->start < adj_start);
    311       1.1  riastrad 	BUG_ON(node->start + node->size > adj_end);
    312       1.1  riastrad 	BUG_ON(node->start + node->size > end);
    313       1.1  riastrad 
    314       1.1  riastrad 	node->hole_follows = 0;
    315       1.3  riastrad 	if (__drm_mm_hole_node_start(node) < hole_end) {
    316       1.1  riastrad 		list_add(&node->hole_stack, &mm->hole_stack);
    317       1.1  riastrad 		node->hole_follows = 1;
    318       1.1  riastrad 	}
    319       1.1  riastrad }
    320       1.1  riastrad 
    321       1.1  riastrad /**
    322       1.3  riastrad  * drm_mm_insert_node_in_range_generic - ranged search for space and insert @node
    323       1.3  riastrad  * @mm: drm_mm to allocate from
    324       1.3  riastrad  * @node: preallocate node to insert
    325       1.3  riastrad  * @size: size of the allocation
    326       1.3  riastrad  * @alignment: alignment of the allocation
    327       1.3  riastrad  * @color: opaque tag value to use for this node
    328       1.3  riastrad  * @start: start of the allowed range for this node
    329       1.3  riastrad  * @end: end of the allowed range for this node
    330       1.3  riastrad  * @sflags: flags to fine-tune the allocation search
    331       1.3  riastrad  * @aflags: flags to fine-tune the allocation behavior
    332       1.3  riastrad  *
    333       1.3  riastrad  * The preallocated node must be cleared to 0.
    334       1.3  riastrad  *
    335       1.3  riastrad  * Returns:
    336       1.3  riastrad  * 0 on success, -ENOSPC if there's no suitable hole.
    337       1.1  riastrad  */
    338       1.1  riastrad int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node,
    339  1.3.28.1  pgoyette 					u64 size, unsigned alignment,
    340       1.3  riastrad 					unsigned long color,
    341  1.3.28.1  pgoyette 					u64 start, u64 end,
    342       1.3  riastrad 					enum drm_mm_search_flags sflags,
    343       1.3  riastrad 					enum drm_mm_allocator_flags aflags)
    344       1.1  riastrad {
    345       1.1  riastrad 	struct drm_mm_node *hole_node;
    346       1.1  riastrad 
    347       1.1  riastrad 	hole_node = drm_mm_search_free_in_range_generic(mm,
    348       1.1  riastrad 							size, alignment, color,
    349       1.3  riastrad 							start, end, sflags);
    350       1.1  riastrad 	if (!hole_node)
    351       1.1  riastrad 		return -ENOSPC;
    352       1.1  riastrad 
    353       1.1  riastrad 	drm_mm_insert_helper_range(hole_node, node,
    354       1.1  riastrad 				   size, alignment, color,
    355       1.3  riastrad 				   start, end, aflags);
    356       1.1  riastrad 	return 0;
    357       1.1  riastrad }
    358       1.1  riastrad EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic);
    359       1.1  riastrad 
    360       1.1  riastrad /**
    361       1.3  riastrad  * drm_mm_remove_node - Remove a memory node from the allocator.
    362       1.3  riastrad  * @node: drm_mm_node to remove
    363       1.3  riastrad  *
    364       1.3  riastrad  * This just removes a node from its drm_mm allocator. The node does not need to
    365       1.3  riastrad  * be cleared again before it can be re-inserted into this or any other drm_mm
    366       1.3  riastrad  * allocator. It is a bug to call this function on a un-allocated node.
    367       1.1  riastrad  */
    368       1.1  riastrad void drm_mm_remove_node(struct drm_mm_node *node)
    369       1.1  riastrad {
    370       1.1  riastrad 	struct drm_mm *mm = node->mm;
    371       1.1  riastrad 	struct drm_mm_node *prev_node;
    372       1.1  riastrad 
    373       1.3  riastrad 	if (WARN_ON(!node->allocated))
    374       1.3  riastrad 		return;
    375       1.3  riastrad 
    376       1.1  riastrad 	BUG_ON(node->scanned_block || node->scanned_prev_free
    377       1.1  riastrad 				   || node->scanned_next_free);
    378       1.1  riastrad 
    379       1.1  riastrad 	prev_node =
    380       1.1  riastrad 	    list_entry(node->node_list.prev, struct drm_mm_node, node_list);
    381       1.1  riastrad 
    382       1.1  riastrad 	if (node->hole_follows) {
    383       1.3  riastrad 		BUG_ON(__drm_mm_hole_node_start(node) ==
    384       1.3  riastrad 		       __drm_mm_hole_node_end(node));
    385       1.1  riastrad 		list_del(&node->hole_stack);
    386       1.1  riastrad 	} else
    387       1.3  riastrad 		BUG_ON(__drm_mm_hole_node_start(node) !=
    388       1.3  riastrad 		       __drm_mm_hole_node_end(node));
    389       1.3  riastrad 
    390       1.1  riastrad 
    391       1.1  riastrad 	if (!prev_node->hole_follows) {
    392       1.1  riastrad 		prev_node->hole_follows = 1;
    393       1.1  riastrad 		list_add(&prev_node->hole_stack, &mm->hole_stack);
    394       1.1  riastrad 	} else
    395       1.1  riastrad 		list_move(&prev_node->hole_stack, &mm->hole_stack);
    396       1.1  riastrad 
    397       1.1  riastrad 	list_del(&node->node_list);
    398       1.1  riastrad 	node->allocated = 0;
    399       1.1  riastrad }
    400       1.1  riastrad EXPORT_SYMBOL(drm_mm_remove_node);
    401       1.1  riastrad 
    402  1.3.28.1  pgoyette static int check_free_hole(u64 start, u64 end, u64 size, unsigned alignment)
    403       1.1  riastrad {
    404       1.1  riastrad 	if (end - start < size)
    405       1.1  riastrad 		return 0;
    406       1.1  riastrad 
    407       1.1  riastrad 	if (alignment) {
    408  1.3.28.1  pgoyette 		u64 tmp = start;
    409  1.3.28.1  pgoyette 		unsigned rem;
    410  1.3.28.1  pgoyette 
    411  1.3.28.1  pgoyette 		rem = do_div(tmp, alignment);
    412  1.3.28.1  pgoyette 		if (rem)
    413  1.3.28.1  pgoyette 			start += alignment - rem;
    414       1.1  riastrad 	}
    415       1.1  riastrad 
    416       1.1  riastrad 	return end >= start + size;
    417       1.1  riastrad }
    418       1.1  riastrad 
    419       1.3  riastrad static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
    420  1.3.28.1  pgoyette 						      u64 size,
    421       1.3  riastrad 						      unsigned alignment,
    422       1.3  riastrad 						      unsigned long color,
    423       1.3  riastrad 						      enum drm_mm_search_flags flags)
    424       1.1  riastrad {
    425       1.1  riastrad 	struct drm_mm_node *entry;
    426       1.1  riastrad 	struct drm_mm_node *best;
    427  1.3.28.1  pgoyette 	u64 adj_start;
    428  1.3.28.1  pgoyette 	u64 adj_end;
    429  1.3.28.1  pgoyette 	u64 best_size;
    430       1.1  riastrad 
    431       1.1  riastrad 	BUG_ON(mm->scanned_blocks);
    432       1.1  riastrad 
    433       1.1  riastrad 	best = NULL;
    434       1.1  riastrad 	best_size = ~0UL;
    435       1.1  riastrad 
    436       1.3  riastrad 	__drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
    437       1.3  riastrad 			       flags & DRM_MM_SEARCH_BELOW) {
    438  1.3.28.1  pgoyette 		u64 hole_size = adj_end - adj_start;
    439       1.1  riastrad 
    440       1.1  riastrad 		if (mm->color_adjust) {
    441       1.1  riastrad 			mm->color_adjust(entry, color, &adj_start, &adj_end);
    442       1.1  riastrad 			if (adj_end <= adj_start)
    443       1.1  riastrad 				continue;
    444       1.1  riastrad 		}
    445       1.1  riastrad 
    446       1.1  riastrad 		if (!check_free_hole(adj_start, adj_end, size, alignment))
    447       1.1  riastrad 			continue;
    448       1.1  riastrad 
    449       1.3  riastrad 		if (!(flags & DRM_MM_SEARCH_BEST))
    450       1.1  riastrad 			return entry;
    451       1.1  riastrad 
    452       1.3  riastrad 		if (hole_size < best_size) {
    453       1.1  riastrad 			best = entry;
    454       1.3  riastrad 			best_size = hole_size;
    455       1.1  riastrad 		}
    456       1.1  riastrad 	}
    457       1.1  riastrad 
    458       1.1  riastrad 	return best;
    459       1.1  riastrad }
    460       1.1  riastrad 
    461       1.3  riastrad static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
    462  1.3.28.1  pgoyette 							u64 size,
    463       1.1  riastrad 							unsigned alignment,
    464       1.1  riastrad 							unsigned long color,
    465  1.3.28.1  pgoyette 							u64 start,
    466  1.3.28.1  pgoyette 							u64 end,
    467       1.3  riastrad 							enum drm_mm_search_flags flags)
    468       1.1  riastrad {
    469       1.1  riastrad 	struct drm_mm_node *entry;
    470       1.1  riastrad 	struct drm_mm_node *best;
    471  1.3.28.1  pgoyette 	u64 adj_start;
    472  1.3.28.1  pgoyette 	u64 adj_end;
    473  1.3.28.1  pgoyette 	u64 best_size;
    474       1.1  riastrad 
    475       1.1  riastrad 	BUG_ON(mm->scanned_blocks);
    476       1.1  riastrad 
    477       1.1  riastrad 	best = NULL;
    478       1.1  riastrad 	best_size = ~0UL;
    479       1.1  riastrad 
    480       1.3  riastrad 	__drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
    481       1.3  riastrad 			       flags & DRM_MM_SEARCH_BELOW) {
    482  1.3.28.1  pgoyette 		u64 hole_size = adj_end - adj_start;
    483       1.1  riastrad 
    484       1.1  riastrad 		if (mm->color_adjust) {
    485       1.1  riastrad 			mm->color_adjust(entry, color, &adj_start, &adj_end);
    486       1.1  riastrad 			if (adj_end <= adj_start)
    487       1.1  riastrad 				continue;
    488       1.1  riastrad 		}
    489       1.1  riastrad 
    490  1.3.28.1  pgoyette 		adj_start = max(adj_start, start);
    491  1.3.28.1  pgoyette 		adj_end = min(adj_end, end);
    492  1.3.28.1  pgoyette 
    493       1.1  riastrad 		if (!check_free_hole(adj_start, adj_end, size, alignment))
    494       1.1  riastrad 			continue;
    495       1.1  riastrad 
    496       1.3  riastrad 		if (!(flags & DRM_MM_SEARCH_BEST))
    497       1.1  riastrad 			return entry;
    498       1.1  riastrad 
    499       1.3  riastrad 		if (hole_size < best_size) {
    500       1.1  riastrad 			best = entry;
    501       1.3  riastrad 			best_size = hole_size;
    502       1.1  riastrad 		}
    503       1.1  riastrad 	}
    504       1.1  riastrad 
    505       1.1  riastrad 	return best;
    506       1.1  riastrad }
    507       1.1  riastrad 
    508       1.1  riastrad /**
    509       1.3  riastrad  * drm_mm_replace_node - move an allocation from @old to @new
    510       1.3  riastrad  * @old: drm_mm_node to remove from the allocator
    511       1.3  riastrad  * @new: drm_mm_node which should inherit @old's allocation
    512       1.3  riastrad  *
    513       1.3  riastrad  * This is useful for when drivers embed the drm_mm_node structure and hence
    514       1.3  riastrad  * can't move allocations by reassigning pointers. It's a combination of remove
    515       1.3  riastrad  * and insert with the guarantee that the allocation start will match.
    516       1.1  riastrad  */
    517       1.1  riastrad void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
    518       1.1  riastrad {
    519       1.1  riastrad 	list_replace(&old->node_list, &new->node_list);
    520       1.1  riastrad 	list_replace(&old->hole_stack, &new->hole_stack);
    521       1.1  riastrad 	new->hole_follows = old->hole_follows;
    522       1.1  riastrad 	new->mm = old->mm;
    523       1.1  riastrad 	new->start = old->start;
    524       1.1  riastrad 	new->size = old->size;
    525       1.1  riastrad 	new->color = old->color;
    526       1.1  riastrad 
    527       1.1  riastrad 	old->allocated = 0;
    528       1.1  riastrad 	new->allocated = 1;
    529       1.1  riastrad }
    530       1.1  riastrad EXPORT_SYMBOL(drm_mm_replace_node);
    531       1.1  riastrad 
    532       1.1  riastrad /**
    533       1.3  riastrad  * DOC: lru scan roaster
    534       1.3  riastrad  *
    535       1.3  riastrad  * Very often GPUs need to have continuous allocations for a given object. When
    536       1.3  riastrad  * evicting objects to make space for a new one it is therefore not most
    537       1.3  riastrad  * efficient when we simply start to select all objects from the tail of an LRU
    538       1.3  riastrad  * until there's a suitable hole: Especially for big objects or nodes that
    539       1.3  riastrad  * otherwise have special allocation constraints there's a good chance we evict
    540       1.3  riastrad  * lots of (smaller) objects unecessarily.
    541       1.3  riastrad  *
    542       1.3  riastrad  * The DRM range allocator supports this use-case through the scanning
    543       1.3  riastrad  * interfaces. First a scan operation needs to be initialized with
    544       1.3  riastrad  * drm_mm_init_scan() or drm_mm_init_scan_with_range(). The the driver adds
    545       1.3  riastrad  * objects to the roaster (probably by walking an LRU list, but this can be
    546       1.3  riastrad  * freely implemented) until a suitable hole is found or there's no further
    547       1.3  riastrad  * evitable object.
    548       1.3  riastrad  *
    549       1.3  riastrad  * The the driver must walk through all objects again in exactly the reverse
    550       1.3  riastrad  * order to restore the allocator state. Note that while the allocator is used
    551       1.3  riastrad  * in the scan mode no other operation is allowed.
    552       1.3  riastrad  *
    553       1.3  riastrad  * Finally the driver evicts all objects selected in the scan. Adding and
    554       1.3  riastrad  * removing an object is O(1), and since freeing a node is also O(1) the overall
    555       1.3  riastrad  * complexity is O(scanned_objects). So like the free stack which needs to be
    556       1.3  riastrad  * walked before a scan operation even begins this is linear in the number of
    557       1.3  riastrad  * objects. It doesn't seem to hurt badly.
    558       1.3  riastrad  */
    559       1.3  riastrad 
    560       1.3  riastrad /**
    561       1.3  riastrad  * drm_mm_init_scan - initialize lru scanning
    562       1.3  riastrad  * @mm: drm_mm to scan
    563       1.3  riastrad  * @size: size of the allocation
    564       1.3  riastrad  * @alignment: alignment of the allocation
    565       1.3  riastrad  * @color: opaque tag value to use for the allocation
    566       1.1  riastrad  *
    567       1.1  riastrad  * This simply sets up the scanning routines with the parameters for the desired
    568       1.3  riastrad  * hole. Note that there's no need to specify allocation flags, since they only
    569       1.3  riastrad  * change the place a node is allocated from within a suitable hole.
    570       1.1  riastrad  *
    571       1.3  riastrad  * Warning:
    572       1.3  riastrad  * As long as the scan list is non-empty, no other operations than
    573       1.1  riastrad  * adding/removing nodes to/from the scan list are allowed.
    574       1.1  riastrad  */
    575       1.1  riastrad void drm_mm_init_scan(struct drm_mm *mm,
    576  1.3.28.1  pgoyette 		      u64 size,
    577       1.1  riastrad 		      unsigned alignment,
    578       1.1  riastrad 		      unsigned long color)
    579       1.1  riastrad {
    580       1.1  riastrad 	mm->scan_color = color;
    581       1.1  riastrad 	mm->scan_alignment = alignment;
    582       1.1  riastrad 	mm->scan_size = size;
    583       1.1  riastrad 	mm->scanned_blocks = 0;
    584       1.1  riastrad 	mm->scan_hit_start = 0;
    585       1.1  riastrad 	mm->scan_hit_end = 0;
    586       1.1  riastrad 	mm->scan_check_range = 0;
    587       1.1  riastrad 	mm->prev_scanned_node = NULL;
    588       1.1  riastrad }
    589       1.1  riastrad EXPORT_SYMBOL(drm_mm_init_scan);
    590       1.1  riastrad 
    591       1.1  riastrad /**
    592       1.3  riastrad  * drm_mm_init_scan - initialize range-restricted lru scanning
    593       1.3  riastrad  * @mm: drm_mm to scan
    594       1.3  riastrad  * @size: size of the allocation
    595       1.3  riastrad  * @alignment: alignment of the allocation
    596       1.3  riastrad  * @color: opaque tag value to use for the allocation
    597       1.3  riastrad  * @start: start of the allowed range for the allocation
    598       1.3  riastrad  * @end: end of the allowed range for the allocation
    599       1.1  riastrad  *
    600       1.1  riastrad  * This simply sets up the scanning routines with the parameters for the desired
    601       1.3  riastrad  * hole. Note that there's no need to specify allocation flags, since they only
    602       1.3  riastrad  * change the place a node is allocated from within a suitable hole.
    603       1.1  riastrad  *
    604       1.3  riastrad  * Warning:
    605       1.3  riastrad  * As long as the scan list is non-empty, no other operations than
    606       1.1  riastrad  * adding/removing nodes to/from the scan list are allowed.
    607       1.1  riastrad  */
    608       1.1  riastrad void drm_mm_init_scan_with_range(struct drm_mm *mm,
    609  1.3.28.1  pgoyette 				 u64 size,
    610       1.1  riastrad 				 unsigned alignment,
    611       1.1  riastrad 				 unsigned long color,
    612  1.3.28.1  pgoyette 				 u64 start,
    613  1.3.28.1  pgoyette 				 u64 end)
    614       1.1  riastrad {
    615       1.1  riastrad 	mm->scan_color = color;
    616       1.1  riastrad 	mm->scan_alignment = alignment;
    617       1.1  riastrad 	mm->scan_size = size;
    618       1.1  riastrad 	mm->scanned_blocks = 0;
    619       1.1  riastrad 	mm->scan_hit_start = 0;
    620       1.1  riastrad 	mm->scan_hit_end = 0;
    621       1.1  riastrad 	mm->scan_start = start;
    622       1.1  riastrad 	mm->scan_end = end;
    623       1.1  riastrad 	mm->scan_check_range = 1;
    624       1.1  riastrad 	mm->prev_scanned_node = NULL;
    625       1.1  riastrad }
    626       1.1  riastrad EXPORT_SYMBOL(drm_mm_init_scan_with_range);
    627       1.1  riastrad 
    628       1.1  riastrad /**
    629       1.3  riastrad  * drm_mm_scan_add_block - add a node to the scan list
    630       1.3  riastrad  * @node: drm_mm_node to add
    631       1.3  riastrad  *
    632       1.1  riastrad  * Add a node to the scan list that might be freed to make space for the desired
    633       1.1  riastrad  * hole.
    634       1.1  riastrad  *
    635       1.3  riastrad  * Returns:
    636       1.3  riastrad  * True if a hole has been found, false otherwise.
    637       1.1  riastrad  */
    638       1.3  riastrad bool drm_mm_scan_add_block(struct drm_mm_node *node)
    639       1.1  riastrad {
    640       1.1  riastrad 	struct drm_mm *mm = node->mm;
    641       1.1  riastrad 	struct drm_mm_node *prev_node;
    642  1.3.28.1  pgoyette 	u64 hole_start, hole_end;
    643  1.3.28.1  pgoyette 	u64 adj_start, adj_end;
    644       1.1  riastrad 
    645       1.1  riastrad 	mm->scanned_blocks++;
    646       1.1  riastrad 
    647       1.1  riastrad 	BUG_ON(node->scanned_block);
    648       1.1  riastrad 	node->scanned_block = 1;
    649       1.1  riastrad 
    650       1.1  riastrad 	prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
    651       1.1  riastrad 			       node_list);
    652       1.1  riastrad 
    653       1.1  riastrad 	node->scanned_preceeds_hole = prev_node->hole_follows;
    654       1.1  riastrad 	prev_node->hole_follows = 1;
    655       1.1  riastrad 	list_del(&node->node_list);
    656       1.1  riastrad 	node->node_list.prev = &prev_node->node_list;
    657       1.1  riastrad 	node->node_list.next = &mm->prev_scanned_node->node_list;
    658       1.1  riastrad 	mm->prev_scanned_node = node;
    659       1.1  riastrad 
    660       1.1  riastrad 	adj_start = hole_start = drm_mm_hole_node_start(prev_node);
    661       1.1  riastrad 	adj_end = hole_end = drm_mm_hole_node_end(prev_node);
    662       1.1  riastrad 
    663       1.1  riastrad 	if (mm->scan_check_range) {
    664       1.1  riastrad 		if (adj_start < mm->scan_start)
    665       1.1  riastrad 			adj_start = mm->scan_start;
    666       1.1  riastrad 		if (adj_end > mm->scan_end)
    667       1.1  riastrad 			adj_end = mm->scan_end;
    668       1.1  riastrad 	}
    669       1.1  riastrad 
    670       1.1  riastrad 	if (mm->color_adjust)
    671       1.1  riastrad 		mm->color_adjust(prev_node, mm->scan_color,
    672       1.1  riastrad 				 &adj_start, &adj_end);
    673       1.1  riastrad 
    674       1.1  riastrad 	if (check_free_hole(adj_start, adj_end,
    675       1.1  riastrad 			    mm->scan_size, mm->scan_alignment)) {
    676       1.1  riastrad 		mm->scan_hit_start = hole_start;
    677       1.1  riastrad 		mm->scan_hit_end = hole_end;
    678       1.3  riastrad 		return true;
    679       1.1  riastrad 	}
    680       1.1  riastrad 
    681       1.3  riastrad 	return false;
    682       1.1  riastrad }
    683       1.1  riastrad EXPORT_SYMBOL(drm_mm_scan_add_block);
    684       1.1  riastrad 
    685       1.1  riastrad /**
    686       1.3  riastrad  * drm_mm_scan_remove_block - remove a node from the scan list
    687       1.3  riastrad  * @node: drm_mm_node to remove
    688       1.1  riastrad  *
    689       1.1  riastrad  * Nodes _must_ be removed in the exact same order from the scan list as they
    690       1.1  riastrad  * have been added, otherwise the internal state of the memory manager will be
    691       1.1  riastrad  * corrupted.
    692       1.1  riastrad  *
    693       1.1  riastrad  * When the scan list is empty, the selected memory nodes can be freed. An
    694       1.3  riastrad  * immediately following drm_mm_search_free with !DRM_MM_SEARCH_BEST will then
    695       1.3  riastrad  * return the just freed block (because its at the top of the free_stack list).
    696       1.1  riastrad  *
    697       1.3  riastrad  * Returns:
    698       1.3  riastrad  * True if this block should be evicted, false otherwise. Will always
    699       1.3  riastrad  * return false when no hole has been found.
    700       1.1  riastrad  */
    701       1.3  riastrad bool drm_mm_scan_remove_block(struct drm_mm_node *node)
    702       1.1  riastrad {
    703       1.1  riastrad 	struct drm_mm *mm = node->mm;
    704       1.1  riastrad 	struct drm_mm_node *prev_node;
    705       1.1  riastrad 
    706       1.1  riastrad 	mm->scanned_blocks--;
    707       1.1  riastrad 
    708       1.1  riastrad 	BUG_ON(!node->scanned_block);
    709       1.1  riastrad 	node->scanned_block = 0;
    710       1.1  riastrad 
    711       1.1  riastrad 	prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
    712       1.1  riastrad 			       node_list);
    713       1.1  riastrad 
    714       1.1  riastrad 	prev_node->hole_follows = node->scanned_preceeds_hole;
    715       1.1  riastrad 	list_add(&node->node_list, &prev_node->node_list);
    716       1.1  riastrad 
    717       1.1  riastrad 	 return (drm_mm_hole_node_end(node) > mm->scan_hit_start &&
    718       1.1  riastrad 		 node->start < mm->scan_hit_end);
    719       1.1  riastrad }
    720       1.1  riastrad EXPORT_SYMBOL(drm_mm_scan_remove_block);
    721       1.1  riastrad 
    722       1.3  riastrad /**
    723       1.3  riastrad  * drm_mm_clean - checks whether an allocator is clean
    724       1.3  riastrad  * @mm: drm_mm allocator to check
    725       1.3  riastrad  *
    726       1.3  riastrad  * Returns:
    727       1.3  riastrad  * True if the allocator is completely free, false if there's still a node
    728       1.3  riastrad  * allocated in it.
    729       1.3  riastrad  */
    730       1.3  riastrad bool drm_mm_clean(struct drm_mm * mm)
    731       1.1  riastrad {
    732       1.1  riastrad 	struct list_head *head = &mm->head_node.node_list;
    733       1.1  riastrad 
    734       1.1  riastrad 	return (head->next->next == head);
    735       1.1  riastrad }
    736       1.1  riastrad EXPORT_SYMBOL(drm_mm_clean);
    737       1.1  riastrad 
    738       1.3  riastrad /**
    739       1.3  riastrad  * drm_mm_init - initialize a drm-mm allocator
    740       1.3  riastrad  * @mm: the drm_mm structure to initialize
    741       1.3  riastrad  * @start: start of the range managed by @mm
    742       1.3  riastrad  * @size: end of the range managed by @mm
    743       1.3  riastrad  *
    744       1.3  riastrad  * Note that @mm must be cleared to 0 before calling this function.
    745       1.3  riastrad  */
    746  1.3.28.1  pgoyette void drm_mm_init(struct drm_mm * mm, u64 start, u64 size)
    747       1.1  riastrad {
    748       1.1  riastrad 	INIT_LIST_HEAD(&mm->hole_stack);
    749       1.1  riastrad 	mm->scanned_blocks = 0;
    750       1.1  riastrad 
    751       1.1  riastrad 	/* Clever trick to avoid a special case in the free hole tracking. */
    752       1.1  riastrad 	INIT_LIST_HEAD(&mm->head_node.node_list);
    753       1.1  riastrad 	INIT_LIST_HEAD(&mm->head_node.hole_stack);
    754       1.1  riastrad 	mm->head_node.hole_follows = 1;
    755       1.1  riastrad 	mm->head_node.scanned_block = 0;
    756       1.1  riastrad 	mm->head_node.scanned_prev_free = 0;
    757       1.1  riastrad 	mm->head_node.scanned_next_free = 0;
    758       1.1  riastrad 	mm->head_node.mm = mm;
    759       1.1  riastrad 	mm->head_node.start = start + size;
    760       1.1  riastrad 	mm->head_node.size = start - mm->head_node.start;
    761       1.1  riastrad 	list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack);
    762       1.1  riastrad 
    763       1.1  riastrad 	mm->color_adjust = NULL;
    764       1.1  riastrad }
    765       1.1  riastrad EXPORT_SYMBOL(drm_mm_init);
    766       1.1  riastrad 
    767       1.3  riastrad /**
    768       1.3  riastrad  * drm_mm_takedown - clean up a drm_mm allocator
    769       1.3  riastrad  * @mm: drm_mm allocator to clean up
    770       1.3  riastrad  *
    771       1.3  riastrad  * Note that it is a bug to call this function on an allocator which is not
    772       1.3  riastrad  * clean.
    773       1.3  riastrad  */
    774       1.1  riastrad void drm_mm_takedown(struct drm_mm * mm)
    775       1.1  riastrad {
    776       1.3  riastrad 	WARN(!list_empty(&mm->head_node.node_list),
    777       1.3  riastrad 	     "Memory manager not clean during takedown.\n");
    778       1.3  riastrad }
    779       1.3  riastrad EXPORT_SYMBOL(drm_mm_takedown);
    780       1.1  riastrad 
    781  1.3.28.1  pgoyette static u64 drm_mm_debug_hole(struct drm_mm_node *entry,
    782  1.3.28.1  pgoyette 				     const char *prefix)
    783       1.3  riastrad {
    784  1.3.28.1  pgoyette 	u64 hole_start, hole_end, hole_size;
    785       1.1  riastrad 
    786       1.3  riastrad 	if (entry->hole_follows) {
    787       1.3  riastrad 		hole_start = drm_mm_hole_node_start(entry);
    788       1.3  riastrad 		hole_end = drm_mm_hole_node_end(entry);
    789       1.3  riastrad 		hole_size = hole_end - hole_start;
    790  1.3.28.1  pgoyette 		pr_debug("%s %#"PRIx64"-%#"PRIx64": %"PRIu64": free\n", prefix, hole_start,
    791  1.3.28.1  pgoyette 			 hole_end, hole_size);
    792       1.3  riastrad 		return hole_size;
    793       1.1  riastrad 	}
    794       1.1  riastrad 
    795       1.3  riastrad 	return 0;
    796       1.1  riastrad }
    797       1.1  riastrad 
    798       1.3  riastrad /**
    799       1.3  riastrad  * drm_mm_debug_table - dump allocator state to dmesg
    800       1.3  riastrad  * @mm: drm_mm allocator to dump
    801       1.3  riastrad  * @prefix: prefix to use for dumping to dmesg
    802       1.3  riastrad  */
    803       1.1  riastrad void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
    804       1.1  riastrad {
    805       1.1  riastrad 	struct drm_mm_node *entry;
    806  1.3.28.1  pgoyette 	u64 total_used = 0, total_free = 0, total = 0;
    807       1.1  riastrad 
    808       1.3  riastrad 	total_free += drm_mm_debug_hole(&mm->head_node, prefix);
    809       1.1  riastrad 
    810       1.1  riastrad 	drm_mm_for_each_node(entry, mm) {
    811  1.3.28.1  pgoyette 		pr_debug("%s %#"PRIx64"-%#"PRIx64": %"PRIu64": used\n", prefix, entry->start,
    812  1.3.28.1  pgoyette 			 entry->start + entry->size, entry->size);
    813       1.1  riastrad 		total_used += entry->size;
    814       1.3  riastrad 		total_free += drm_mm_debug_hole(entry, prefix);
    815       1.1  riastrad 	}
    816       1.1  riastrad 	total = total_free + total_used;
    817       1.1  riastrad 
    818  1.3.28.1  pgoyette 	pr_debug("%s total: %"PRIu64", used %"PRIu64" free %"PRIu64"\n", prefix, total,
    819  1.3.28.1  pgoyette 		 total_used, total_free);
    820       1.1  riastrad }
    821       1.1  riastrad EXPORT_SYMBOL(drm_mm_debug_table);
    822       1.1  riastrad 
    823       1.1  riastrad #if defined(CONFIG_DEBUG_FS)
    824  1.3.28.1  pgoyette static u64 drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *entry)
    825       1.3  riastrad {
    826  1.3.28.1  pgoyette 	u64 hole_start, hole_end, hole_size;
    827       1.3  riastrad 
    828       1.3  riastrad 	if (entry->hole_follows) {
    829       1.3  riastrad 		hole_start = drm_mm_hole_node_start(entry);
    830       1.3  riastrad 		hole_end = drm_mm_hole_node_end(entry);
    831       1.3  riastrad 		hole_size = hole_end - hole_start;
    832  1.3.28.1  pgoyette 		seq_printf(m, "%#018llx-%#018llx: %llu: free\n", hole_start,
    833  1.3.28.1  pgoyette 			   hole_end, hole_size);
    834       1.3  riastrad 		return hole_size;
    835       1.3  riastrad 	}
    836       1.3  riastrad 
    837       1.3  riastrad 	return 0;
    838       1.3  riastrad }
    839       1.3  riastrad 
    840       1.3  riastrad /**
    841       1.3  riastrad  * drm_mm_dump_table - dump allocator state to a seq_file
    842       1.3  riastrad  * @m: seq_file to dump to
    843       1.3  riastrad  * @mm: drm_mm allocator to dump
    844       1.3  riastrad  */
    845       1.1  riastrad int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
    846       1.1  riastrad {
    847       1.1  riastrad 	struct drm_mm_node *entry;
    848  1.3.28.1  pgoyette 	u64 total_used = 0, total_free = 0, total = 0;
    849       1.1  riastrad 
    850       1.3  riastrad 	total_free += drm_mm_dump_hole(m, &mm->head_node);
    851       1.1  riastrad 
    852       1.1  riastrad 	drm_mm_for_each_node(entry, mm) {
    853  1.3.28.1  pgoyette 		seq_printf(m, "%#018llx-%#018llx: %llu: used\n", entry->start,
    854  1.3.28.1  pgoyette 			   entry->start + entry->size, entry->size);
    855       1.1  riastrad 		total_used += entry->size;
    856       1.3  riastrad 		total_free += drm_mm_dump_hole(m, entry);
    857       1.1  riastrad 	}
    858       1.1  riastrad 	total = total_free + total_used;
    859       1.1  riastrad 
    860  1.3.28.1  pgoyette 	seq_printf(m, "total: %llu, used %llu free %llu\n", total,
    861  1.3.28.1  pgoyette 		   total_used, total_free);
    862       1.1  riastrad 	return 0;
    863       1.1  riastrad }
    864       1.1  riastrad EXPORT_SYMBOL(drm_mm_dump_table);
    865       1.1  riastrad #endif
    866