Subversion Repositories Kolibri OS

Rev

Rev 1430 | Rev 3192 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

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