aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_mm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_mm.c')
-rw-r--r--drivers/gpu/drm/drm_mm.c598
1 files changed, 347 insertions, 251 deletions
diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
index a6bfc302ed90..add1737dae0d 100644
--- a/drivers/gpu/drm/drm_mm.c
+++ b/drivers/gpu/drm/drm_mm.c
@@ -64,8 +64,8 @@ static struct drm_mm_node *drm_mm_kmalloc(struct drm_mm *mm, int atomic)
64 else { 64 else {
65 child = 65 child =
66 list_entry(mm->unused_nodes.next, 66 list_entry(mm->unused_nodes.next,
67 struct drm_mm_node, free_stack); 67 struct drm_mm_node, node_list);
68 list_del(&child->free_stack); 68 list_del(&child->node_list);
69 --mm->num_unused; 69 --mm->num_unused;
70 } 70 }
71 spin_unlock(&mm->unused_lock); 71 spin_unlock(&mm->unused_lock);
@@ -94,195 +94,242 @@ int drm_mm_pre_get(struct drm_mm *mm)
94 return ret; 94 return ret;
95 } 95 }
96 ++mm->num_unused; 96 ++mm->num_unused;
97 list_add_tail(&node->free_stack, &mm->unused_nodes); 97 list_add_tail(&node->node_list, &mm->unused_nodes);
98 } 98 }
99 spin_unlock(&mm->unused_lock); 99 spin_unlock(&mm->unused_lock);
100 return 0; 100 return 0;
101} 101}
102EXPORT_SYMBOL(drm_mm_pre_get); 102EXPORT_SYMBOL(drm_mm_pre_get);
103 103
104static int drm_mm_create_tail_node(struct drm_mm *mm, 104static inline unsigned long drm_mm_hole_node_start(struct drm_mm_node *hole_node)
105 unsigned long start,
106 unsigned long size, int atomic)
107{ 105{
108 struct drm_mm_node *child; 106 return hole_node->start + hole_node->size;
109 107}
110 child = drm_mm_kmalloc(mm, atomic);
111 if (unlikely(child == NULL))
112 return -ENOMEM;
113
114 child->free = 1;
115 child->size = size;
116 child->start = start;
117 child->mm = mm;
118 108
119 list_add_tail(&child->node_list, &mm->node_list); 109static inline unsigned long drm_mm_hole_node_end(struct drm_mm_node *hole_node)
120 list_add_tail(&child->free_stack, &mm->free_stack); 110{
111 struct drm_mm_node *next_node =
112 list_entry(hole_node->node_list.next, struct drm_mm_node,
113 node_list);
121 114
122 return 0; 115 return next_node->start;
123} 116}
124 117
125static struct drm_mm_node *drm_mm_split_at_start(struct drm_mm_node *parent, 118static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
126 unsigned long size, 119 struct drm_mm_node *node,
127 int atomic) 120 unsigned long size, unsigned alignment)
128{ 121{
129 struct drm_mm_node *child; 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);
130 126
131 child = drm_mm_kmalloc(parent->mm, atomic); 127 BUG_ON(!hole_node->hole_follows || node->allocated);
132 if (unlikely(child == NULL))
133 return NULL;
134 128
135 INIT_LIST_HEAD(&child->free_stack); 129 if (alignment)
130 tmp = hole_start % alignment;
136 131
137 child->size = size; 132 if (!tmp) {
138 child->start = parent->start; 133 hole_node->hole_follows = 0;
139 child->mm = parent->mm; 134 list_del_init(&hole_node->hole_stack);
135 } else
136 wasted = alignment - tmp;
140 137
141 list_add_tail(&child->node_list, &parent->node_list); 138 node->start = hole_start + wasted;
142 INIT_LIST_HEAD(&child->free_stack); 139 node->size = size;
140 node->mm = mm;
141 node->allocated = 1;
143 142
144 parent->size -= size; 143 INIT_LIST_HEAD(&node->hole_stack);
145 parent->start += size; 144 list_add(&node->node_list, &hole_node->node_list);
146 return child;
147}
148 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}
149 155
150struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node, 156struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node,
151 unsigned long size, 157 unsigned long size,
152 unsigned alignment, 158 unsigned alignment,
153 int atomic) 159 int atomic)
154{ 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;
155 166
156 struct drm_mm_node *align_splitoff = NULL; 167 drm_mm_insert_helper(hole_node, node, size, alignment);
157 unsigned tmp = 0;
158 168
159 if (alignment) 169 return node;
160 tmp = node->start % alignment; 170}
171EXPORT_SYMBOL(drm_mm_get_block_generic);
161 172
162 if (tmp) { 173/**
163 align_splitoff = 174 * Search for free space and insert a preallocated memory node. Returns
164 drm_mm_split_at_start(node, alignment - tmp, atomic); 175 * -ENOSPC if no suitable free area is available. The preallocated memory node
165 if (unlikely(align_splitoff == NULL)) 176 * must be cleared.
166 return NULL; 177 */
167 } 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;
168 182
169 if (node->size == size) { 183 hole_node = drm_mm_search_free(mm, size, alignment, 0);
170 list_del_init(&node->free_stack); 184 if (!hole_node)
171 node->free = 0; 185 return -ENOSPC;
172 } else {
173 node = drm_mm_split_at_start(node, size, atomic);
174 }
175 186
176 if (align_splitoff) 187 drm_mm_insert_helper(hole_node, node, size, alignment);
177 drm_mm_put_block(align_splitoff);
178 188
179 return node; 189 return 0;
180} 190}
181EXPORT_SYMBOL(drm_mm_get_block_generic); 191EXPORT_SYMBOL(drm_mm_insert_node);
182 192
183struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *node, 193static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
184 unsigned long size, 194 struct drm_mm_node *node,
185 unsigned alignment, 195 unsigned long size, unsigned alignment,
186 unsigned long start, 196 unsigned long start, unsigned long end)
187 unsigned long end,
188 int atomic)
189{ 197{
190 struct drm_mm_node *align_splitoff = NULL; 198 struct drm_mm *mm = hole_node->mm;
191 unsigned tmp = 0; 199 unsigned long tmp = 0, wasted = 0;
192 unsigned 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);
193 202
194 if (node->start < start) 203 BUG_ON(!hole_node->hole_follows || node->allocated);
195 wasted += start - node->start; 204
205 if (hole_start < start)
206 wasted += start - hole_start;
196 if (alignment) 207 if (alignment)
197 tmp = ((node->start + wasted) % alignment); 208 tmp = (hole_start + wasted) % alignment;
198 209
199 if (tmp) 210 if (tmp)
200 wasted += alignment - tmp; 211 wasted += alignment - tmp;
201 if (wasted) { 212
202 align_splitoff = drm_mm_split_at_start(node, wasted, atomic); 213 if (!wasted) {
203 if (unlikely(align_splitoff == NULL)) 214 hole_node->hole_follows = 0;
204 return NULL; 215 list_del_init(&hole_node->hole_stack);
205 } 216 }
206 217
207 if (node->size == size) { 218 node->start = hole_start + wasted;
208 list_del_init(&node->free_stack); 219 node->size = size;
209 node->free = 0; 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;
210 } else { 232 } else {
211 node = drm_mm_split_at_start(node, size, atomic); 233 node->hole_follows = 0;
212 } 234 }
235}
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;
213 245
214 if (align_splitoff) 246 node = drm_mm_kmalloc(hole_node->mm, atomic);
215 drm_mm_put_block(align_splitoff); 247 if (unlikely(node == NULL))
248 return NULL;
249
250 drm_mm_insert_helper_range(hole_node, node, size, alignment,
251 start, end);
216 252
217 return node; 253 return node;
218} 254}
219EXPORT_SYMBOL(drm_mm_get_block_range_generic); 255EXPORT_SYMBOL(drm_mm_get_block_range_generic);
220 256
221/* 257/**
222 * Put a block. Merge with the previous and / or next block if they are free. 258 * Search for free space and insert a preallocated memory node. Returns
223 * Otherwise add to the free stack. 259 * -ENOSPC if no suitable free area is available. This is for range
260 * restricted allocations. The preallocated memory node must be cleared.
224 */ 261 */
225 262int drm_mm_insert_node_in_range(struct drm_mm *mm, struct drm_mm_node *node,
226void drm_mm_put_block(struct drm_mm_node *cur) 263 unsigned long size, unsigned alignment,
264 unsigned long start, unsigned long end)
227{ 265{
266 struct drm_mm_node *hole_node;
228 267
229 struct drm_mm *mm = cur->mm; 268 hole_node = drm_mm_search_free_in_range(mm, size, alignment,
230 struct list_head *cur_head = &cur->node_list; 269 start, end, 0);
231 struct list_head *root_head = &mm->node_list; 270 if (!hole_node)
232 struct drm_mm_node *prev_node = NULL; 271 return -ENOSPC;
233 struct drm_mm_node *next_node;
234 272
235 int merged = 0; 273 drm_mm_insert_helper_range(hole_node, node, size, alignment,
274 start, end);
236 275
237 BUG_ON(cur->scanned_block || cur->scanned_prev_free 276 return 0;
238 || cur->scanned_next_free); 277}
278EXPORT_SYMBOL(drm_mm_insert_node_in_range);
239 279
240 if (cur_head->prev != root_head) { 280/**
241 prev_node = 281 * Remove a memory node from the allocator.
242 list_entry(cur_head->prev, struct drm_mm_node, node_list); 282 */
243 if (prev_node->free) { 283void drm_mm_remove_node(struct drm_mm_node *node)
244 prev_node->size += cur->size; 284{
245 merged = 1; 285 struct drm_mm *mm = node->mm;
246 } 286 struct drm_mm_node *prev_node;
247 } 287
248 if (cur_head->next != root_head) { 288 BUG_ON(node->scanned_block || node->scanned_prev_free
249 next_node = 289 || node->scanned_next_free);
250 list_entry(cur_head->next, struct drm_mm_node, node_list); 290
251 if (next_node->free) { 291 prev_node =
252 if (merged) { 292 list_entry(node->node_list.prev, struct drm_mm_node, node_list);
253 prev_node->size += next_node->size; 293
254 list_del(&next_node->node_list); 294 if (node->hole_follows) {
255 list_del(&next_node->free_stack); 295 BUG_ON(drm_mm_hole_node_start(node)
256 spin_lock(&mm->unused_lock); 296 == drm_mm_hole_node_end(node));
257 if (mm->num_unused < MM_UNUSED_TARGET) { 297 list_del(&node->hole_stack);
258 list_add(&next_node->free_stack, 298 } else
259 &mm->unused_nodes); 299 BUG_ON(drm_mm_hole_node_start(node)
260 ++mm->num_unused; 300 != drm_mm_hole_node_end(node));
261 } else 301
262 kfree(next_node); 302 if (!prev_node->hole_follows) {
263 spin_unlock(&mm->unused_lock); 303 prev_node->hole_follows = 1;
264 } else { 304 list_add(&prev_node->hole_stack, &mm->hole_stack);
265 next_node->size += cur->size; 305 } else
266 next_node->start = cur->start; 306 list_move(&prev_node->hole_stack, &mm->hole_stack);
267 merged = 1; 307
268 } 308 list_del(&node->node_list);
269 } 309 node->allocated = 0;
270 }
271 if (!merged) {
272 cur->free = 1;
273 list_add(&cur->free_stack, &mm->free_stack);
274 } else {
275 list_del(&cur->node_list);
276 spin_lock(&mm->unused_lock);
277 if (mm->num_unused < MM_UNUSED_TARGET) {
278 list_add(&cur->free_stack, &mm->unused_nodes);
279 ++mm->num_unused;
280 } else
281 kfree(cur);
282 spin_unlock(&mm->unused_lock);
283 }
284} 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);
285 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}
286EXPORT_SYMBOL(drm_mm_put_block); 333EXPORT_SYMBOL(drm_mm_put_block);
287 334
288static int check_free_hole(unsigned long start, unsigned long end, 335static int check_free_hole(unsigned long start, unsigned long end,
@@ -319,8 +366,10 @@ struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
319 best = NULL; 366 best = NULL;
320 best_size = ~0UL; 367 best_size = ~0UL;
321 368
322 list_for_each_entry(entry, &mm->free_stack, free_stack) { 369 list_for_each_entry(entry, &mm->hole_stack, hole_stack) {
323 if (!check_free_hole(entry->start, entry->start + entry->size, 370 BUG_ON(!entry->hole_follows);
371 if (!check_free_hole(drm_mm_hole_node_start(entry),
372 drm_mm_hole_node_end(entry),
324 size, alignment)) 373 size, alignment))
325 continue; 374 continue;
326 375
@@ -353,12 +402,13 @@ struct drm_mm_node *drm_mm_search_free_in_range(const struct drm_mm *mm,
353 best = NULL; 402 best = NULL;
354 best_size = ~0UL; 403 best_size = ~0UL;
355 404
356 list_for_each_entry(entry, &mm->free_stack, free_stack) { 405 list_for_each_entry(entry, &mm->hole_stack, hole_stack) {
357 unsigned long adj_start = entry->start < start ? 406 unsigned long adj_start = drm_mm_hole_node_start(entry) < start ?
358 start : entry->start; 407 start : drm_mm_hole_node_start(entry);
359 unsigned long adj_end = entry->start + entry->size > end ? 408 unsigned long adj_end = drm_mm_hole_node_end(entry) > end ?
360 end : entry->start + entry->size; 409 end : drm_mm_hole_node_end(entry);
361 410
411 BUG_ON(!entry->hole_follows);
362 if (!check_free_hole(adj_start, adj_end, size, alignment)) 412 if (!check_free_hole(adj_start, adj_end, size, alignment))
363 continue; 413 continue;
364 414
@@ -376,6 +426,23 @@ struct drm_mm_node *drm_mm_search_free_in_range(const struct drm_mm *mm,
376EXPORT_SYMBOL(drm_mm_search_free_in_range); 426EXPORT_SYMBOL(drm_mm_search_free_in_range);
377 427
378/** 428/**
429 * 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/**
379 * Initializa lru scanning. 446 * Initializa lru scanning.
380 * 447 *
381 * This simply sets up the scanning routines with the parameters for the desired 448 * This simply sets up the scanning routines with the parameters for the desired
@@ -392,10 +459,38 @@ void drm_mm_init_scan(struct drm_mm *mm, unsigned long size,
392 mm->scanned_blocks = 0; 459 mm->scanned_blocks = 0;
393 mm->scan_hit_start = 0; 460 mm->scan_hit_start = 0;
394 mm->scan_hit_size = 0; 461 mm->scan_hit_size = 0;
462 mm->scan_check_range = 0;
463 mm->prev_scanned_node = NULL;
395} 464}
396EXPORT_SYMBOL(drm_mm_init_scan); 465EXPORT_SYMBOL(drm_mm_init_scan);
397 466
398/** 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 */
476void 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}
491EXPORT_SYMBOL(drm_mm_init_scan_with_range);
492
493/**
399 * Add a node to the scan list that might be freed to make space for the desired 494 * Add a node to the scan list that might be freed to make space for the desired
400 * hole. 495 * hole.
401 * 496 *
@@ -404,58 +499,42 @@ EXPORT_SYMBOL(drm_mm_init_scan);
404int drm_mm_scan_add_block(struct drm_mm_node *node) 499int drm_mm_scan_add_block(struct drm_mm_node *node)
405{ 500{
406 struct drm_mm *mm = node->mm; 501 struct drm_mm *mm = node->mm;
407 struct list_head *prev_free, *next_free; 502 struct drm_mm_node *prev_node;
408 struct drm_mm_node *prev_node, *next_node; 503 unsigned long hole_start, hole_end;
504 unsigned long adj_start;
505 unsigned long adj_end;
409 506
410 mm->scanned_blocks++; 507 mm->scanned_blocks++;
411 508
412 prev_free = next_free = NULL; 509 BUG_ON(node->scanned_block);
413
414 BUG_ON(node->free);
415 node->scanned_block = 1; 510 node->scanned_block = 1;
416 node->free = 1;
417
418 if (node->node_list.prev != &mm->node_list) {
419 prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
420 node_list);
421
422 if (prev_node->free) {
423 list_del(&prev_node->node_list);
424
425 node->start = prev_node->start;
426 node->size += prev_node->size;
427
428 prev_node->scanned_prev_free = 1;
429
430 prev_free = &prev_node->free_stack;
431 }
432 }
433
434 if (node->node_list.next != &mm->node_list) {
435 next_node = list_entry(node->node_list.next, struct drm_mm_node,
436 node_list);
437 511
438 if (next_node->free) { 512 prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
439 list_del(&next_node->node_list); 513 node_list);
440 514
441 node->size += next_node->size; 515 node->scanned_preceeds_hole = prev_node->hole_follows;
442 516 prev_node->hole_follows = 1;
443 next_node->scanned_next_free = 1; 517 list_del(&node->node_list);
444 518 node->node_list.prev = &prev_node->node_list;
445 next_free = &next_node->free_stack; 519 node->node_list.next = &mm->prev_scanned_node->node_list;
446 } 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;
447 } 532 }
448 533
449 /* The free_stack list is not used for allocated objects, so these two 534 if (check_free_hole(adj_start , adj_end,
450 * pointers can be abused (as long as no allocations in this memory
451 * manager happens). */
452 node->free_stack.prev = prev_free;
453 node->free_stack.next = next_free;
454
455 if (check_free_hole(node->start, node->start + node->size,
456 mm->scan_size, mm->scan_alignment)) { 535 mm->scan_size, mm->scan_alignment)) {
457 mm->scan_hit_start = node->start; 536 mm->scan_hit_start = hole_start;
458 mm->scan_hit_size = node->size; 537 mm->scan_hit_size = hole_end;
459 538
460 return 1; 539 return 1;
461 } 540 }
@@ -481,39 +560,19 @@ EXPORT_SYMBOL(drm_mm_scan_add_block);
481int drm_mm_scan_remove_block(struct drm_mm_node *node) 560int drm_mm_scan_remove_block(struct drm_mm_node *node)
482{ 561{
483 struct drm_mm *mm = node->mm; 562 struct drm_mm *mm = node->mm;
484 struct drm_mm_node *prev_node, *next_node; 563 struct drm_mm_node *prev_node;
485 564
486 mm->scanned_blocks--; 565 mm->scanned_blocks--;
487 566
488 BUG_ON(!node->scanned_block); 567 BUG_ON(!node->scanned_block);
489 node->scanned_block = 0; 568 node->scanned_block = 0;
490 node->free = 0;
491 569
492 prev_node = list_entry(node->free_stack.prev, struct drm_mm_node, 570 prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
493 free_stack); 571 node_list);
494 next_node = list_entry(node->free_stack.next, struct drm_mm_node,
495 free_stack);
496 572
497 if (prev_node) { 573 prev_node->hole_follows = node->scanned_preceeds_hole;
498 BUG_ON(!prev_node->scanned_prev_free); 574 INIT_LIST_HEAD(&node->node_list);
499 prev_node->scanned_prev_free = 0; 575 list_add(&node->node_list, &prev_node->node_list);
500
501 list_add_tail(&prev_node->node_list, &node->node_list);
502
503 node->start = prev_node->start + prev_node->size;
504 node->size -= prev_node->size;
505 }
506
507 if (next_node) {
508 BUG_ON(!next_node->scanned_next_free);
509 next_node->scanned_next_free = 0;
510
511 list_add(&next_node->node_list, &node->node_list);
512
513 node->size -= next_node->size;
514 }
515
516 INIT_LIST_HEAD(&node->free_stack);
517 576
518 /* Only need to check for containement because start&size for the 577 /* Only need to check for containement because start&size for the
519 * complete resulting free block (not just the desired part) is 578 * complete resulting free block (not just the desired part) is
@@ -530,7 +589,7 @@ EXPORT_SYMBOL(drm_mm_scan_remove_block);
530 589
531int drm_mm_clean(struct drm_mm * mm) 590int drm_mm_clean(struct drm_mm * mm)
532{ 591{
533 struct list_head *head = &mm->node_list; 592 struct list_head *head = &mm->head_node.node_list;
534 593
535 return (head->next->next == head); 594 return (head->next->next == head);
536} 595}
@@ -538,38 +597,40 @@ EXPORT_SYMBOL(drm_mm_clean);
538 597
539int drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) 598int drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size)
540{ 599{
541 INIT_LIST_HEAD(&mm->node_list); 600 INIT_LIST_HEAD(&mm->hole_stack);
542 INIT_LIST_HEAD(&mm->free_stack);
543 INIT_LIST_HEAD(&mm->unused_nodes); 601 INIT_LIST_HEAD(&mm->unused_nodes);
544 mm->num_unused = 0; 602 mm->num_unused = 0;
545 mm->scanned_blocks = 0; 603 mm->scanned_blocks = 0;
546 spin_lock_init(&mm->unused_lock); 604 spin_lock_init(&mm->unused_lock);
547 605
548 return drm_mm_create_tail_node(mm, start, size, 0); 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;
549} 619}
550EXPORT_SYMBOL(drm_mm_init); 620EXPORT_SYMBOL(drm_mm_init);
551 621
552void drm_mm_takedown(struct drm_mm * mm) 622void drm_mm_takedown(struct drm_mm * mm)
553{ 623{
554 struct list_head *bnode = mm->free_stack.next; 624 struct drm_mm_node *entry, *next;
555 struct drm_mm_node *entry;
556 struct drm_mm_node *next;
557
558 entry = list_entry(bnode, struct drm_mm_node, free_stack);
559 625
560 if (entry->node_list.next != &mm->node_list || 626 if (!list_empty(&mm->head_node.node_list)) {
561 entry->free_stack.next != &mm->free_stack) {
562 DRM_ERROR("Memory manager not clean. Delaying takedown\n"); 627 DRM_ERROR("Memory manager not clean. Delaying takedown\n");
563 return; 628 return;
564 } 629 }
565 630
566 list_del(&entry->free_stack);
567 list_del(&entry->node_list);
568 kfree(entry);
569
570 spin_lock(&mm->unused_lock); 631 spin_lock(&mm->unused_lock);
571 list_for_each_entry_safe(entry, next, &mm->unused_nodes, free_stack) { 632 list_for_each_entry_safe(entry, next, &mm->unused_nodes, node_list) {
572 list_del(&entry->free_stack); 633 list_del(&entry->node_list);
573 kfree(entry); 634 kfree(entry);
574 --mm->num_unused; 635 --mm->num_unused;
575 } 636 }
@@ -582,19 +643,37 @@ EXPORT_SYMBOL(drm_mm_takedown);
582void drm_mm_debug_table(struct drm_mm *mm, const char *prefix) 643void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
583{ 644{
584 struct drm_mm_node *entry; 645 struct drm_mm_node *entry;
585 int total_used = 0, total_free = 0, total = 0; 646 unsigned long total_used = 0, total_free = 0, total = 0;
586 647 unsigned long hole_start, hole_end, hole_size;
587 list_for_each_entry(entry, &mm->node_list, node_list) { 648
588 printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8ld: %s\n", 649 hole_start = drm_mm_hole_node_start(&mm->head_node);
650 hole_end = drm_mm_hole_node_end(&mm->head_node);
651 hole_size = hole_end - hole_start;
652 if (hole_size)
653 printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n",
654 prefix, hole_start, hole_end,
655 hole_size);
656 total_free += hole_size;
657
658 drm_mm_for_each_node(entry, mm) {
659 printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: used\n",
589 prefix, entry->start, entry->start + entry->size, 660 prefix, entry->start, entry->start + entry->size,
590 entry->size, entry->free ? "free" : "used"); 661 entry->size);
591 total += entry->size; 662 total_used += entry->size;
592 if (entry->free) 663
593 total_free += entry->size; 664 if (entry->hole_follows) {
594 else 665 hole_start = drm_mm_hole_node_start(entry);
595 total_used += entry->size; 666 hole_end = drm_mm_hole_node_end(entry);
667 hole_size = hole_end - hole_start;
668 printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8lu: free\n",
669 prefix, hole_start, hole_end,
670 hole_size);
671 total_free += hole_size;
672 }
596 } 673 }
597 printk(KERN_DEBUG "%s total: %d, used %d free %d\n", prefix, total, 674 total = total_free + total_used;
675
676 printk(KERN_DEBUG "%s total: %lu, used %lu free %lu\n", prefix, total,
598 total_used, total_free); 677 total_used, total_free);
599} 678}
600EXPORT_SYMBOL(drm_mm_debug_table); 679EXPORT_SYMBOL(drm_mm_debug_table);
@@ -603,17 +682,34 @@ EXPORT_SYMBOL(drm_mm_debug_table);
603int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm) 682int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
604{ 683{
605 struct drm_mm_node *entry; 684 struct drm_mm_node *entry;
606 int total_used = 0, total_free = 0, total = 0; 685 unsigned long total_used = 0, total_free = 0, total = 0;
607 686 unsigned long hole_start, hole_end, hole_size;
608 list_for_each_entry(entry, &mm->node_list, node_list) { 687
609 seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: %s\n", entry->start, entry->start + entry->size, entry->size, entry->free ? "free" : "used"); 688 hole_start = drm_mm_hole_node_start(&mm->head_node);
610 total += entry->size; 689 hole_end = drm_mm_hole_node_end(&mm->head_node);
611 if (entry->free) 690 hole_size = hole_end - hole_start;
612 total_free += entry->size; 691 if (hole_size)
613 else 692 seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n",
614 total_used += entry->size; 693 hole_start, hole_end, hole_size);
694 total_free += hole_size;
695
696 drm_mm_for_each_node(entry, mm) {
697 seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: used\n",
698 entry->start, entry->start + entry->size,
699 entry->size);
700 total_used += entry->size;
701 if (entry->hole_follows) {
702 hole_start = drm_mm_hole_node_start(&mm->head_node);
703 hole_end = drm_mm_hole_node_end(&mm->head_node);
704 hole_size = hole_end - hole_start;
705 seq_printf(m, "0x%08lx-0x%08lx: 0x%08lx: free\n",
706 hole_start, hole_end, hole_size);
707 total_free += hole_size;
708 }
615 } 709 }
616 seq_printf(m, "total: %d, used %d free %d\n", total, total_used, total_free); 710 total = total_free + total_used;
711
712 seq_printf(m, "total: %lu, used %lu free %lu\n", total, total_used, total_free);
617 return 0; 713 return 0;
618} 714}
619EXPORT_SYMBOL(drm_mm_dump_table); 715EXPORT_SYMBOL(drm_mm_dump_table);