blob: d6432f9e49c1d0903f922d91c6fae2dcaefb59ac [file] [log] [blame]
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +10001/**************************************************************************
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:
Jan Engelhardt96de0e22007-10-19 23:21:04 +020041 * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +100042 */
43
44#include "drmP.h"
Jerome Glisse249d6042009-04-08 17:11:16 +020045#include "drm_mm.h"
Thomas Hellstrom1d584202007-01-08 22:25:47 +110046#include <linux/slab.h>
Dave Airliefa8a1232009-08-26 13:13:37 +100047#include <linux/seq_file.h>
Thomas Hellstrom1d584202007-01-08 22:25:47 +110048
Jerome Glisse249d6042009-04-08 17:11:16 +020049#define MM_UNUSED_TARGET 4
50
Jerome Glisse249d6042009-04-08 17:11:16 +020051static struct drm_mm_node *drm_mm_kmalloc(struct drm_mm *mm, int atomic)
Thomas Hellstrom1d584202007-01-08 22:25:47 +110052{
Dave Airlie55910512007-07-11 16:53:40 +100053 struct drm_mm_node *child;
Thomas Hellstrom1d584202007-01-08 22:25:47 +110054
Jerome Glisse249d6042009-04-08 17:11:16 +020055 if (atomic)
Daniel Vetter709ea972010-07-02 15:02:16 +010056 child = kzalloc(sizeof(*child), GFP_ATOMIC);
Jerome Glisse249d6042009-04-08 17:11:16 +020057 else
Daniel Vetter709ea972010-07-02 15:02:16 +010058 child = kzalloc(sizeof(*child), GFP_KERNEL);
Jerome Glisse249d6042009-04-08 17:11:16 +020059
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,
Daniel Vetterea7b1dd2011-02-18 17:59:12 +010067 struct drm_mm_node, node_list);
68 list_del(&child->node_list);
Jerome Glisse249d6042009-04-08 17:11:16 +020069 --mm->num_unused;
70 }
71 spin_unlock(&mm->unused_lock);
72 }
73 return child;
74}
75
Jerome Glissea698cf32009-11-13 20:56:58 +010076/* 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 */
Jerome Glisse249d6042009-04-08 17:11:16 +020081int 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);
Daniel Vetter709ea972010-07-02 15:02:16 +010088 node = kzalloc(sizeof(*node), GFP_KERNEL);
Jerome Glisse249d6042009-04-08 17:11:16 +020089 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;
Daniel Vetterea7b1dd2011-02-18 17:59:12 +010097 list_add_tail(&node->node_list, &mm->unused_nodes);
Jerome Glisse249d6042009-04-08 17:11:16 +020098 }
99 spin_unlock(&mm->unused_lock);
100 return 0;
101}
102EXPORT_SYMBOL(drm_mm_pre_get);
103
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100104static inline unsigned long drm_mm_hole_node_start(struct drm_mm_node *hole_node)
Jerome Glisse249d6042009-04-08 17:11:16 +0200105{
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100106 return hole_node->start + hole_node->size;
Thomas Hellstrom1d584202007-01-08 22:25:47 +1100107}
108
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100109static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node)
Thomas Hellstrom1d584202007-01-08 22:25:47 +1100110{
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100111 struct drm_mm_node *next_node =
112 list_entry(hole_node->node_list.next, struct drm_mm_node,
113 node_list);
Thomas Hellstrom1d584202007-01-08 22:25:47 +1100114
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100115 return next_node->start;
Thomas Hellstrom1d584202007-01-08 22:25:47 +1100116}
117
Daniel Vetter9fc935d2011-02-18 17:59:13 +0100118static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
119 struct drm_mm_node *node,
120 unsigned long size, unsigned alignment)
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000121{
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100122 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
Daniel Vetterb0b7af12011-02-18 17:59:14 +0100127 BUG_ON(!hole_node->hole_follows || node->allocated);
128
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000129 if (alignment)
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100130 tmp = hole_start % alignment;
Thomas Hellstrom1d584202007-01-08 22:25:47 +1100131
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100132 if (!tmp) {
133 hole_node->hole_follows = 0;
134 list_del_init(&hole_node->hole_stack);
135 } else
136 wasted = alignment - tmp;
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000137
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100138 node->start = hole_start + wasted;
139 node->size = size;
140 node->mm = mm;
Daniel Vetterb0b7af12011-02-18 17:59:14 +0100141 node->allocated = 1;
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100142
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;
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000151 } else {
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100152 node->hole_follows = 0;
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000153 }
Daniel Vetter9fc935d2011-02-18 17:59:13 +0100154}
155
156struct 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
Daniel Vetter9fc935d2011-02-18 17:59:13 +0100163 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);
Thomas Hellstrom1d584202007-01-08 22:25:47 +1100168
Chris Wilsone6c03c52009-05-22 14:14:22 +0100169 return node;
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000170}
Thomas Hellstrom89579f72009-06-17 12:29:56 +0200171EXPORT_SYMBOL(drm_mm_get_block_generic);
Jerome Glisse249d6042009-04-08 17:11:16 +0200172
Daniel Vetterb0b7af12011-02-18 17:59:14 +0100173/**
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 */
178int 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}
191EXPORT_SYMBOL(drm_mm_insert_node);
192
Daniel Vetter9fc935d2011-02-18 17:59:13 +0100193static 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)
Jerome Glissea2e68e92009-12-07 15:52:56 +0100197{
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100198 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);
Jerome Glissea2e68e92009-12-07 15:52:56 +0100202
Daniel Vetterb0b7af12011-02-18 17:59:14 +0100203 BUG_ON(!hole_node->hole_follows || node->allocated);
204
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100205 if (hole_start < start)
206 wasted += start - hole_start;
Jerome Glissea2e68e92009-12-07 15:52:56 +0100207 if (alignment)
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100208 tmp = (hole_start + wasted) % alignment;
Jerome Glissea2e68e92009-12-07 15:52:56 +0100209
210 if (tmp)
211 wasted += alignment - tmp;
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100212
213 if (!wasted) {
214 hole_node->hole_follows = 0;
215 list_del_init(&hole_node->hole_stack);
Jerome Glissea2e68e92009-12-07 15:52:56 +0100216 }
217
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100218 node->start = hole_start + wasted;
219 node->size = size;
220 node->mm = mm;
Daniel Vetterb0b7af12011-02-18 17:59:14 +0100221 node->allocated = 1;
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100222
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;
Jerome Glissea2e68e92009-12-07 15:52:56 +0100232 } else {
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100233 node->hole_follows = 0;
Jerome Glissea2e68e92009-12-07 15:52:56 +0100234 }
Daniel Vetter9fc935d2011-02-18 17:59:13 +0100235}
236
237struct 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
Daniel Vetter9fc935d2011-02-18 17:59:13 +0100246 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);
Jerome Glissea2e68e92009-12-07 15:52:56 +0100252
Jerome Glissea2e68e92009-12-07 15:52:56 +0100253 return node;
254}
255EXPORT_SYMBOL(drm_mm_get_block_range_generic);
256
Daniel Vetterb0b7af12011-02-18 17:59:14 +0100257/**
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.
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000261 */
Daniel Vetterb0b7af12011-02-18 17:59:14 +0100262int 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)
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000265{
Daniel Vetterb0b7af12011-02-18 17:59:14 +0100266 struct drm_mm_node *hole_node;
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000267
Daniel Vetterb0b7af12011-02-18 17:59:14 +0100268 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}
278EXPORT_SYMBOL(drm_mm_insert_node_in_range);
279
280/**
281 * Remove a memory node from the allocator.
282 */
283void drm_mm_remove_node(struct drm_mm_node *node)
284{
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100285 struct drm_mm *mm = node->mm;
286 struct drm_mm_node *prev_node;
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000287
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100288 BUG_ON(node->scanned_block || node->scanned_prev_free
289 || node->scanned_next_free);
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000290
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100291 prev_node =
292 list_entry(node->node_list.prev, struct drm_mm_node, node_list);
Daniel Vetter709ea972010-07-02 15:02:16 +0100293
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100294 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);
Daniel Vetterb0b7af12011-02-18 17:59:14 +0100309 node->allocated = 0;
310}
311EXPORT_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 */
318void 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
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100325 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);
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000332}
Eric Anholt673a3942008-07-30 12:06:12 -0700333EXPORT_SYMBOL(drm_mm_put_block);
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000334
Daniel Vetter75214732010-08-26 21:44:17 +0200335static int check_free_hole(unsigned long start, unsigned long end,
336 unsigned long size, unsigned alignment)
Daniel Vetter7a6b2892010-07-02 15:02:15 +0100337{
338 unsigned wasted = 0;
339
Daniel Vetter75214732010-08-26 21:44:17 +0200340 if (end - start < size)
Daniel Vetter7a6b2892010-07-02 15:02:15 +0100341 return 0;
342
343 if (alignment) {
Daniel Vetter75214732010-08-26 21:44:17 +0200344 unsigned tmp = start % alignment;
Daniel Vetter7a6b2892010-07-02 15:02:15 +0100345 if (tmp)
346 wasted = alignment - tmp;
347 }
348
Daniel Vetter75214732010-08-26 21:44:17 +0200349 if (end >= start + size + wasted) {
Daniel Vetter7a6b2892010-07-02 15:02:15 +0100350 return 1;
351 }
352
353 return 0;
354}
355
Jerome Glisse249d6042009-04-08 17:11:16 +0200356struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
357 unsigned long size,
358 unsigned alignment, int best_match)
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000359{
Dave Airlie55910512007-07-11 16:53:40 +1000360 struct drm_mm_node *entry;
361 struct drm_mm_node *best;
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000362 unsigned long best_size;
363
Daniel Vetter709ea972010-07-02 15:02:16 +0100364 BUG_ON(mm->scanned_blocks);
365
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000366 best = NULL;
367 best_size = ~0UL;
368
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100369 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),
Daniel Vetter75214732010-08-26 21:44:17 +0200373 size, alignment))
Thomas Hellstrom1d584202007-01-08 22:25:47 +1100374 continue;
375
Daniel Vetter7a6b2892010-07-02 15:02:15 +0100376 if (!best_match)
377 return entry;
Thomas Hellstrom1d584202007-01-08 22:25:47 +1100378
Daniel Vetter7a6b2892010-07-02 15:02:15 +0100379 if (entry->size < best_size) {
380 best = entry;
381 best_size = entry->size;
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000382 }
383 }
384
385 return best;
386}
Jerome Glisse249d6042009-04-08 17:11:16 +0200387EXPORT_SYMBOL(drm_mm_search_free);
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000388
Jerome Glissea2e68e92009-12-07 15:52:56 +0100389struct 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{
Jerome Glissea2e68e92009-12-07 15:52:56 +0100396 struct drm_mm_node *entry;
397 struct drm_mm_node *best;
398 unsigned long best_size;
Jerome Glissea2e68e92009-12-07 15:52:56 +0100399
Daniel Vetter709ea972010-07-02 15:02:16 +0100400 BUG_ON(mm->scanned_blocks);
401
Jerome Glissea2e68e92009-12-07 15:52:56 +0100402 best = NULL;
403 best_size = ~0UL;
404
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100405 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);
Jerome Glissea2e68e92009-12-07 15:52:56 +0100410
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100411 BUG_ON(!entry->hole_follows);
Daniel Vetter75214732010-08-26 21:44:17 +0200412 if (!check_free_hole(adj_start, adj_end, size, alignment))
Daniel Vetter7a6b2892010-07-02 15:02:15 +0100413 continue;
Jerome Glissea2e68e92009-12-07 15:52:56 +0100414
Daniel Vetter7a6b2892010-07-02 15:02:15 +0100415 if (!best_match)
416 return entry;
Jerome Glissea2e68e92009-12-07 15:52:56 +0100417
Daniel Vetter7a6b2892010-07-02 15:02:15 +0100418 if (entry->size < best_size) {
419 best = entry;
420 best_size = entry->size;
Jerome Glissea2e68e92009-12-07 15:52:56 +0100421 }
422 }
423
424 return best;
425}
426EXPORT_SYMBOL(drm_mm_search_free_in_range);
427
Daniel Vetter709ea972010-07-02 15:02:16 +0100428/**
Daniel Vetterb0b7af12011-02-18 17:59:14 +0100429 * Moves an allocation. To be used with embedded struct drm_mm_node.
430 */
431void 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->node_list, &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}
443EXPORT_SYMBOL(drm_mm_replace_node);
444
445/**
Daniel Vetter709ea972010-07-02 15:02:16 +0100446 * 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 */
454void 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;
Daniel Vetterd935cc62010-09-16 15:13:11 +0200462 mm->scan_check_range = 0;
Daniel Vetter709ea972010-07-02 15:02:16 +0100463}
464EXPORT_SYMBOL(drm_mm_init_scan);
465
466/**
Daniel Vetterd935cc62010-09-16 15:13:11 +0200467 * Initializa lru scanning.
468 *
469 * This simply sets up the scanning routines with the parameters for the desired
470 * hole. This version is for range-restricted scans.
471 *
472 * Warning: As long as the scan list is non-empty, no other operations than
473 * adding/removing nodes to/from the scan list are allowed.
474 */
475void drm_mm_init_scan_with_range(struct drm_mm *mm, unsigned long size,
476 unsigned alignment,
477 unsigned long start,
478 unsigned long end)
479{
480 mm->scan_alignment = alignment;
481 mm->scan_size = size;
482 mm->scanned_blocks = 0;
483 mm->scan_hit_start = 0;
484 mm->scan_hit_size = 0;
485 mm->scan_start = start;
486 mm->scan_end = end;
487 mm->scan_check_range = 1;
488}
489EXPORT_SYMBOL(drm_mm_init_scan_with_range);
490
491/**
Daniel Vetter709ea972010-07-02 15:02:16 +0100492 * Add a node to the scan list that might be freed to make space for the desired
493 * hole.
494 *
495 * Returns non-zero, if a hole has been found, zero otherwise.
496 */
497int drm_mm_scan_add_block(struct drm_mm_node *node)
498{
499 struct drm_mm *mm = node->mm;
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100500 struct drm_mm_node *prev_node;
501 unsigned long hole_start, hole_end;
Daniel Vetterd935cc62010-09-16 15:13:11 +0200502 unsigned long adj_start;
503 unsigned long adj_end;
Daniel Vetter709ea972010-07-02 15:02:16 +0100504
505 mm->scanned_blocks++;
506
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100507 BUG_ON(node->scanned_block);
Daniel Vetter709ea972010-07-02 15:02:16 +0100508 node->scanned_block = 1;
Daniel Vetter709ea972010-07-02 15:02:16 +0100509
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100510 prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
511 node_list);
Daniel Vetter709ea972010-07-02 15:02:16 +0100512
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100513 node->scanned_preceeds_hole = prev_node->hole_follows;
514 prev_node->hole_follows = 1;
515 list_del(&node->node_list);
516 node->node_list.prev = &prev_node->node_list;
Daniel Vetter709ea972010-07-02 15:02:16 +0100517
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100518 hole_start = drm_mm_hole_node_start(prev_node);
519 hole_end = drm_mm_hole_node_end(prev_node);
Daniel Vetterd935cc62010-09-16 15:13:11 +0200520 if (mm->scan_check_range) {
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100521 adj_start = hole_start < mm->scan_start ?
522 mm->scan_start : hole_start;
523 adj_end = hole_end > mm->scan_end ?
524 mm->scan_end : hole_end;
Daniel Vetterd935cc62010-09-16 15:13:11 +0200525 } else {
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100526 adj_start = hole_start;
527 adj_end = hole_end;
Daniel Vetterd935cc62010-09-16 15:13:11 +0200528 }
529
530 if (check_free_hole(adj_start , adj_end,
Daniel Vetter75214732010-08-26 21:44:17 +0200531 mm->scan_size, mm->scan_alignment)) {
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100532 mm->scan_hit_start = hole_start;
533 mm->scan_hit_size = hole_end;
Daniel Vetter709ea972010-07-02 15:02:16 +0100534
535 return 1;
536 }
537
538 return 0;
539}
540EXPORT_SYMBOL(drm_mm_scan_add_block);
541
542/**
543 * Remove a node from the scan list.
544 *
545 * Nodes _must_ be removed in the exact same order from the scan list as they
546 * have been added, otherwise the internal state of the memory manager will be
547 * corrupted.
548 *
549 * When the scan list is empty, the selected memory nodes can be freed. An
550 * immediatly following drm_mm_search_free with best_match = 0 will then return
551 * the just freed block (because its at the top of the free_stack list).
552 *
553 * Returns one if this block should be evicted, zero otherwise. Will always
554 * return zero when no hole has been found.
555 */
556int drm_mm_scan_remove_block(struct drm_mm_node *node)
557{
558 struct drm_mm *mm = node->mm;
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100559 struct drm_mm_node *prev_node;
Daniel Vetter709ea972010-07-02 15:02:16 +0100560
561 mm->scanned_blocks--;
562
563 BUG_ON(!node->scanned_block);
564 node->scanned_block = 0;
Daniel Vetter709ea972010-07-02 15:02:16 +0100565
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100566 prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
567 node_list);
Daniel Vetter709ea972010-07-02 15:02:16 +0100568
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100569 prev_node->hole_follows = node->scanned_preceeds_hole;
570 INIT_LIST_HEAD(&node->node_list);
571 list_add(&node->node_list, &prev_node->node_list);
Daniel Vetter709ea972010-07-02 15:02:16 +0100572
573 /* Only need to check for containement because start&size for the
574 * complete resulting free block (not just the desired part) is
575 * stored. */
576 if (node->start >= mm->scan_hit_start &&
577 node->start + node->size
578 <= mm->scan_hit_start + mm->scan_hit_size) {
579 return 1;
580 }
581
582 return 0;
583}
584EXPORT_SYMBOL(drm_mm_scan_remove_block);
585
Dave Airlie55910512007-07-11 16:53:40 +1000586int drm_mm_clean(struct drm_mm * mm)
Thomas Hellstrom1d584202007-01-08 22:25:47 +1100587{
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100588 struct list_head *head = &mm->head_node.node_list;
Thomas Hellstrom1d584202007-01-08 22:25:47 +1100589
590 return (head->next->next == head);
591}
Jerome Glisse249d6042009-04-08 17:11:16 +0200592EXPORT_SYMBOL(drm_mm_clean);
Thomas Hellstrom1d584202007-01-08 22:25:47 +1100593
Dave Airlie55910512007-07-11 16:53:40 +1000594int drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000595{
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100596 INIT_LIST_HEAD(&mm->hole_stack);
Jerome Glisse249d6042009-04-08 17:11:16 +0200597 INIT_LIST_HEAD(&mm->unused_nodes);
598 mm->num_unused = 0;
Daniel Vetter709ea972010-07-02 15:02:16 +0100599 mm->scanned_blocks = 0;
Jerome Glisse249d6042009-04-08 17:11:16 +0200600 spin_lock_init(&mm->unused_lock);
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000601
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100602 /* Clever trick to avoid a special case in the free hole tracking. */
603 INIT_LIST_HEAD(&mm->head_node.node_list);
604 INIT_LIST_HEAD(&mm->head_node.hole_stack);
605 mm->head_node.hole_follows = 1;
606 mm->head_node.scanned_block = 0;
607 mm->head_node.scanned_prev_free = 0;
608 mm->head_node.scanned_next_free = 0;
609 mm->head_node.mm = mm;
610 mm->head_node.start = start + size;
611 mm->head_node.size = start - mm->head_node.start;
612 list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack);
613
614 return 0;
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000615}
Eric Anholt673a3942008-07-30 12:06:12 -0700616EXPORT_SYMBOL(drm_mm_init);
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000617
Dave Airlie55910512007-07-11 16:53:40 +1000618void drm_mm_takedown(struct drm_mm * mm)
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000619{
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100620 struct drm_mm_node *entry, *next;
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000621
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100622 if (!list_empty(&mm->head_node.node_list)) {
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000623 DRM_ERROR("Memory manager not clean. Delaying takedown\n");
624 return;
625 }
626
Jerome Glisse249d6042009-04-08 17:11:16 +0200627 spin_lock(&mm->unused_lock);
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100628 list_for_each_entry_safe(entry, next, &mm->unused_nodes, node_list) {
629 list_del(&entry->node_list);
Jerome Glisse249d6042009-04-08 17:11:16 +0200630 kfree(entry);
631 --mm->num_unused;
632 }
633 spin_unlock(&mm->unused_lock);
634
635 BUG_ON(mm->num_unused != 0);
Thomas Hellstrom3a1bd922006-08-07 21:30:28 +1000636}
Dave Airlief453ba02008-11-07 14:05:41 -0800637EXPORT_SYMBOL(drm_mm_takedown);
Dave Airliefa8a1232009-08-26 13:13:37 +1000638
Jerome Glisse99d7e482009-12-09 21:55:09 +0100639void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
640{
641 struct drm_mm_node *entry;
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100642 unsigned long total_used = 0, total_free = 0, total = 0;
643 unsigned long hole_start, hole_end, hole_size;
Jerome Glisse99d7e482009-12-09 21:55:09 +0100644
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100645 hole_start = drm_mm_hole_node_start(&mm->head_node);
646 hole_end = drm_mm_hole_node_end(&mm->head_node);
647 hole_size = hole_end - hole_start;
648 if (hole_size)
649 printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n",
650 prefix, hole_start, hole_end,
651 hole_size);
652 total_free += hole_size;
653
654 drm_mm_for_each_node(entry, mm) {
655 printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: used\n",
Jerome Glisse99d7e482009-12-09 21:55:09 +0100656 prefix, entry->start, entry->start + entry->size,
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100657 entry->size);
658 total_used += entry->size;
659
660 if (entry->hole_follows) {
661 hole_start = drm_mm_hole_node_start(entry);
662 hole_end = drm_mm_hole_node_end(entry);
663 hole_size = hole_end - hole_start;
664 printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n",
665 prefix, hole_start, hole_end,
666 hole_size);
667 total_free += hole_size;
668 }
Jerome Glisse99d7e482009-12-09 21:55:09 +0100669 }
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100670 total = total_free + total_used;
671
672 printk(KERN_DEBUG "%s total: %lu, used %lu free %lu\n", prefix, total,
Jerome Glisse99d7e482009-12-09 21:55:09 +0100673 total_used, total_free);
674}
675EXPORT_SYMBOL(drm_mm_debug_table);
676
Dave Airliefa8a1232009-08-26 13:13:37 +1000677#if defined(CONFIG_DEBUG_FS)
678int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
679{
680 struct drm_mm_node *entry;
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100681 unsigned long total_used = 0, total_free = 0, total = 0;
682 unsigned long hole_start, hole_end, hole_size;
Dave Airliefa8a1232009-08-26 13:13:37 +1000683
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100684 hole_start = drm_mm_hole_node_start(&mm->head_node);
685 hole_end = drm_mm_hole_node_end(&mm->head_node);
686 hole_size = hole_end - hole_start;
687 if (hole_size)
688 seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n",
689 hole_start, hole_end, hole_size);
690 total_free += hole_size;
691
692 drm_mm_for_each_node(entry, mm) {
693 seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: used\n",
694 entry->start, entry->start + entry->size,
695 entry->size);
696 total_used += entry->size;
697 if (entry->hole_follows) {
698 hole_start = drm_mm_hole_node_start(&mm->head_node);
699 hole_end = drm_mm_hole_node_end(&mm->head_node);
700 hole_size = hole_end - hole_start;
701 seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n",
702 hole_start, hole_end, hole_size);
703 total_free += hole_size;
704 }
Dave Airliefa8a1232009-08-26 13:13:37 +1000705 }
Daniel Vetterea7b1dd2011-02-18 17:59:12 +0100706 total = total_free + total_used;
707
708 seq_printf(m, "total: %lu, used %lu free %lu\n", total, total_used, total_free);
Dave Airliefa8a1232009-08-26 13:13:37 +1000709 return 0;
710}
711EXPORT_SYMBOL(drm_mm_dump_table);
712#endif