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