Rev 4075 | Rev 5060 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 4075 | Rev 4104 | ||
---|---|---|---|
Line 47... | Line 47... | ||
47 | #include |
47 | #include |
48 | #include |
48 | #include |
Line 49... | Line 49... | ||
49 | 49 | ||
Line 50... | Line 50... | ||
50 | #define MM_UNUSED_TARGET 4 |
50 | #define MM_UNUSED_TARGET 4 |
51 | - | ||
52 | static struct drm_mm_node *drm_mm_kmalloc(struct drm_mm *mm, int atomic) |
51 | |
53 | { |
- | |
54 | struct drm_mm_node *child; |
- | |
55 | - | ||
56 | if (atomic) |
- | |
57 | child = kzalloc(sizeof(*child), GFP_ATOMIC); |
- | |
58 | else |
- | |
59 | child = kzalloc(sizeof(*child), GFP_KERNEL); |
52 | static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, |
60 | - | ||
61 | if (unlikely(child == NULL)) { |
- | |
62 | spin_lock(&mm->unused_lock); |
53 | unsigned long size, |
63 | if (list_empty(&mm->unused_nodes)) |
- | |
64 | child = NULL; |
- | |
65 | else { |
- | |
66 | child = |
54 | unsigned alignment, |
67 | list_entry(mm->unused_nodes.next, |
- | |
68 | struct drm_mm_node, node_list); |
- | |
69 | list_del(&child->node_list); |
- | |
70 | --mm->num_unused; |
- | |
71 | } |
- | |
72 | spin_unlock(&mm->unused_lock); |
- | |
73 | } |
- | |
74 | return child; |
- | |
75 | } |
- | |
76 | - | ||
77 | /* drm_mm_pre_get() - pre allocate drm_mm_node structure |
- | |
78 | * drm_mm: memory manager struct we are pre-allocating for |
- | |
79 | * |
- | |
80 | * Returns 0 on success or -ENOMEM if allocation fails. |
55 | unsigned long color, |
81 | */ |
- | |
82 | int drm_mm_pre_get(struct drm_mm *mm) |
56 | enum drm_mm_search_flags flags); |
83 | { |
- | |
84 | struct drm_mm_node *node; |
- | |
85 | - | ||
86 | spin_lock(&mm->unused_lock); |
- | |
87 | while (mm->num_unused < MM_UNUSED_TARGET) { |
- | |
88 | spin_unlock(&mm->unused_lock); |
- | |
89 | node = kzalloc(sizeof(*node), GFP_KERNEL); |
- | |
90 | spin_lock(&mm->unused_lock); |
57 | static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, |
91 | - | ||
92 | if (unlikely(node == NULL)) { |
58 | unsigned long size, |
93 | int ret = (mm->num_unused < 2) ? -ENOMEM : 0; |
59 | unsigned alignment, |
94 | spin_unlock(&mm->unused_lock); |
- | |
95 | return ret; |
60 | unsigned long color, |
96 | } |
- | |
97 | ++mm->num_unused; |
- | |
98 | list_add_tail(&node->node_list, &mm->unused_nodes); |
- | |
99 | } |
- | |
100 | spin_unlock(&mm->unused_lock); |
- | |
101 | return 0; |
61 | unsigned long start, |
Line 102... | Line 62... | ||
102 | } |
62 | unsigned long end, |
103 | EXPORT_SYMBOL(drm_mm_pre_get); |
63 | enum drm_mm_search_flags flags); |
104 | 64 | ||
105 | static void drm_mm_insert_helper(struct drm_mm_node *hole_node, |
65 | static void drm_mm_insert_helper(struct drm_mm_node *hole_node, |
Line 145... | Line 105... | ||
145 | list_add(&node->hole_stack, &mm->hole_stack); |
105 | list_add(&node->hole_stack, &mm->hole_stack); |
146 | node->hole_follows = 1; |
106 | node->hole_follows = 1; |
147 | } |
107 | } |
148 | } |
108 | } |
Line 149... | Line 109... | ||
149 | 109 | ||
150 | struct drm_mm_node *drm_mm_create_block(struct drm_mm *mm, |
- | |
151 | unsigned long start, |
- | |
152 | unsigned long size, |
- | |
153 | bool atomic) |
110 | int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) |
154 | { |
111 | { |
155 | struct drm_mm_node *hole, *node; |
112 | struct drm_mm_node *hole; |
156 | unsigned long end = start + size; |
113 | unsigned long end = node->start + node->size; |
157 | unsigned long hole_start; |
114 | unsigned long hole_start; |
Line -... | Line 115... | ||
- | 115 | unsigned long hole_end; |
|
- | 116 | ||
- | 117 | BUG_ON(node == NULL); |
|
158 | unsigned long hole_end; |
118 | |
159 | 119 | /* Find the relevant hole to add our node to */ |
|
160 | drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { |
120 | drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { |
Line 161... | Line -... | ||
161 | if (hole_start > start || hole_end < end) |
- | |
162 | continue; |
- | |
163 | - | ||
164 | node = drm_mm_kmalloc(mm, atomic); |
- | |
165 | if (unlikely(node == NULL)) |
- | |
166 | return NULL; |
- | |
167 | 121 | if (hole_start > node->start || hole_end < end) |
|
168 | node->start = start; |
122 | continue; |
Line 169... | Line 123... | ||
169 | node->size = size; |
123 | |
170 | node->mm = mm; |
124 | node->mm = mm; |
Line 171... | Line 125... | ||
171 | node->allocated = 1; |
125 | node->allocated = 1; |
172 | 126 | ||
173 | INIT_LIST_HEAD(&node->hole_stack); |
127 | INIT_LIST_HEAD(&node->hole_stack); |
174 | list_add(&node->node_list, &hole->node_list); |
128 | list_add(&node->node_list, &hole->node_list); |
Line 175... | Line 129... | ||
175 | 129 | ||
176 | if (start == hole_start) { |
130 | if (node->start == hole_start) { |
177 | hole->hole_follows = 0; |
131 | hole->hole_follows = 0; |
178 | list_del_init(&hole->hole_stack); |
132 | list_del_init(&hole->hole_stack); |
179 | } |
133 | } |
Line 180... | Line 134... | ||
180 | 134 | ||
181 | node->hole_follows = 0; |
- | |
182 | if (end != hole_end) { |
- | |
183 | list_add(&node->hole_stack, &mm->hole_stack); |
- | |
184 | node->hole_follows = 1; |
- | |
185 | } |
135 | node->hole_follows = 0; |
186 | - | ||
187 | return node; |
- | |
188 | } |
- | |
189 | - | ||
190 | WARN(1, "no hole found for block 0x%lx + 0x%lx\n", start, size); |
- | |
191 | return NULL; |
- | |
192 | } |
- | |
193 | EXPORT_SYMBOL(drm_mm_create_block); |
- | |
194 | - | ||
195 | struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *hole_node, |
- | |
196 | unsigned long size, |
- | |
197 | unsigned alignment, |
- | |
198 | unsigned long color, |
- | |
Line 199... | Line 136... | ||
199 | int atomic) |
136 | if (end != hole_end) { |
200 | { |
- | |
- | 137 | list_add(&node->hole_stack, &mm->hole_stack); |
|
201 | struct drm_mm_node *node; |
138 | node->hole_follows = 1; |
202 | 139 | } |
|
203 | node = drm_mm_kmalloc(hole_node->mm, atomic); |
140 | |
Line 204... | Line 141... | ||
204 | if (unlikely(node == NULL)) |
141 | return 0; |
205 | return NULL; |
142 | } |
206 | 143 | ||
207 | drm_mm_insert_helper(hole_node, node, size, alignment, color); |
144 | WARN(1, "no hole found for node 0x%lx + 0x%lx\n", |
208 | 145 | node->start, node->size); |
|
209 | return node; |
146 | return -ENOSPC; |
210 | } |
147 | } |
211 | EXPORT_SYMBOL(drm_mm_get_block_generic); |
148 | EXPORT_SYMBOL(drm_mm_reserve_node); |
- | 149 | ||
212 | 150 | /** |
|
213 | /** |
151 | * Search for free space and insert a preallocated memory node. Returns |
Line 214... | Line 152... | ||
214 | * Search for free space and insert a preallocated memory node. Returns |
152 | * -ENOSPC if no suitable free area is available. The preallocated memory node |
215 | * -ENOSPC if no suitable free area is available. The preallocated memory node |
153 | * must be cleared. |
216 | * must be cleared. |
154 | */ |
217 | */ |
155 | int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, |
Line 218... | Line 156... | ||
218 | int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node, |
156 | unsigned long size, unsigned alignment, |
219 | unsigned long size, unsigned alignment, |
157 | unsigned long color, |
220 | unsigned long color) |
158 | enum drm_mm_search_flags flags) |
221 | { |
159 | { |
Line 222... | Line -... | ||
222 | struct drm_mm_node *hole_node; |
- | |
223 | - | ||
224 | hole_node = drm_mm_search_free_generic(mm, size, alignment, |
- | |
225 | color, 0); |
- | |
226 | if (!hole_node) |
- | |
227 | return -ENOSPC; |
- | |
228 | - | ||
229 | drm_mm_insert_helper(hole_node, node, size, alignment, color); |
160 | struct drm_mm_node *hole_node; |
230 | return 0; |
161 | |
231 | } |
162 | hole_node = drm_mm_search_free_generic(mm, size, alignment, |
232 | EXPORT_SYMBOL(drm_mm_insert_node_generic); |
163 | color, flags); |
233 | 164 | if (!hole_node) |
|
Line 288... | Line 219... | ||
288 | list_add(&node->hole_stack, &mm->hole_stack); |
219 | list_add(&node->hole_stack, &mm->hole_stack); |
289 | node->hole_follows = 1; |
220 | node->hole_follows = 1; |
290 | } |
221 | } |
291 | } |
222 | } |
Line 292... | Line -... | ||
292 | - | ||
293 | struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *hole_node, |
- | |
294 | unsigned long size, |
- | |
295 | unsigned alignment, |
- | |
296 | unsigned long color, |
- | |
297 | unsigned long start, |
- | |
298 | unsigned long end, |
- | |
299 | int atomic) |
- | |
300 | { |
- | |
301 | struct drm_mm_node *node; |
- | |
302 | - | ||
303 | node = drm_mm_kmalloc(hole_node->mm, atomic); |
- | |
304 | if (unlikely(node == NULL)) |
- | |
305 | return NULL; |
- | |
306 | - | ||
307 | drm_mm_insert_helper_range(hole_node, node, size, alignment, color, |
- | |
308 | start, end); |
- | |
309 | - | ||
310 | return node; |
- | |
311 | } |
- | |
312 | EXPORT_SYMBOL(drm_mm_get_block_range_generic); |
- | |
313 | 223 | ||
314 | /** |
224 | /** |
315 | * Search for free space and insert a preallocated memory node. Returns |
225 | * Search for free space and insert a preallocated memory node. Returns |
316 | * -ENOSPC if no suitable free area is available. This is for range |
226 | * -ENOSPC if no suitable free area is available. This is for range |
317 | * restricted allocations. The preallocated memory node must be cleared. |
227 | * restricted allocations. The preallocated memory node must be cleared. |
318 | */ |
228 | */ |
319 | int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node, |
229 | int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node, |
320 | unsigned long size, unsigned alignment, unsigned long color, |
230 | unsigned long size, unsigned alignment, unsigned long color, |
- | 231 | unsigned long start, unsigned long end, |
|
321 | unsigned long start, unsigned long end) |
232 | enum drm_mm_search_flags flags) |
322 | { |
233 | { |
Line 323... | Line 234... | ||
323 | struct drm_mm_node *hole_node; |
234 | struct drm_mm_node *hole_node; |
324 | 235 | ||
325 | hole_node = drm_mm_search_free_in_range_generic(mm, |
236 | hole_node = drm_mm_search_free_in_range_generic(mm, |
326 | size, alignment, color, |
237 | size, alignment, color, |
327 | start, end, 0); |
238 | start, end, flags); |
Line 328... | Line 239... | ||
328 | if (!hole_node) |
239 | if (!hole_node) |
329 | return -ENOSPC; |
240 | return -ENOSPC; |
330 | 241 | ||
331 | drm_mm_insert_helper_range(hole_node, node, |
242 | drm_mm_insert_helper_range(hole_node, node, |
332 | size, alignment, color, |
243 | size, alignment, color, |
333 | start, end); |
244 | start, end); |
Line 334... | Line -... | ||
334 | return 0; |
- | |
335 | } |
- | |
336 | EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic); |
- | |
337 | - | ||
338 | int drm_mm_insert_node_in_range(struct drm_mm *mm, struct drm_mm_node *node, |
- | |
339 | unsigned long size, unsigned alignment, |
- | |
340 | unsigned long start, unsigned long end) |
- | |
341 | { |
- | |
342 | return drm_mm_insert_node_in_range_generic(mm, node, size, alignment, 0, start, end); |
245 | return 0; |
343 | } |
246 | } |
344 | EXPORT_SYMBOL(drm_mm_insert_node_in_range); |
247 | EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic); |
345 | 248 | ||
346 | /** |
249 | /** |
347 | * Remove a memory node from the allocator. |
250 | * Remove a memory node from the allocator. |
348 | */ |
251 | */ |
Line -... | Line 252... | ||
- | 252 | void drm_mm_remove_node(struct drm_mm_node *node) |
|
- | 253 | { |
|
- | 254 | struct drm_mm *mm = node->mm; |
|
349 | void drm_mm_remove_node(struct drm_mm_node *node) |
255 | struct drm_mm_node *prev_node; |
350 | { |
256 | |
Line 351... | Line 257... | ||
351 | struct drm_mm *mm = node->mm; |
257 | if (WARN_ON(!node->allocated)) |
352 | struct drm_mm_node *prev_node; |
258 | return; |
Line 375... | Line 281... | ||
375 | list_del(&node->node_list); |
281 | list_del(&node->node_list); |
376 | node->allocated = 0; |
282 | node->allocated = 0; |
377 | } |
283 | } |
378 | EXPORT_SYMBOL(drm_mm_remove_node); |
284 | EXPORT_SYMBOL(drm_mm_remove_node); |
Line 379... | Line -... | ||
379 | - | ||
380 | /* |
- | |
381 | * Remove a memory node from the allocator and free the allocated struct |
- | |
382 | * drm_mm_node. Only to be used on a struct drm_mm_node obtained by one of the |
- | |
383 | * drm_mm_get_block functions. |
- | |
384 | */ |
- | |
385 | void drm_mm_put_block(struct drm_mm_node *node) |
- | |
386 | { |
- | |
387 | - | ||
388 | struct drm_mm *mm = node->mm; |
- | |
389 | - | ||
390 | drm_mm_remove_node(node); |
- | |
391 | - | ||
392 | spin_lock(&mm->unused_lock); |
- | |
393 | if (mm->num_unused < MM_UNUSED_TARGET) { |
- | |
394 | list_add(&node->node_list, &mm->unused_nodes); |
- | |
395 | ++mm->num_unused; |
- | |
396 | } else |
- | |
397 | kfree(node); |
- | |
398 | spin_unlock(&mm->unused_lock); |
- | |
399 | } |
- | |
400 | EXPORT_SYMBOL(drm_mm_put_block); |
- | |
401 | 285 | ||
402 | static int check_free_hole(unsigned long start, unsigned long end, |
286 | static int check_free_hole(unsigned long start, unsigned long end, |
403 | unsigned long size, unsigned alignment) |
287 | unsigned long size, unsigned alignment) |
404 | { |
288 | { |
405 | if (end - start < size) |
289 | if (end - start < size) |
Line 412... | Line 296... | ||
412 | } |
296 | } |
Line 413... | Line 297... | ||
413 | 297 | ||
414 | return end >= start + size; |
298 | return end >= start + size; |
Line 415... | Line 299... | ||
415 | } |
299 | } |
416 | 300 | ||
417 | struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, |
301 | static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm, |
418 | unsigned long size, |
302 | unsigned long size, |
419 | unsigned alignment, |
303 | unsigned alignment, |
420 | unsigned long color, |
304 | unsigned long color, |
421 | bool best_match) |
305 | enum drm_mm_search_flags flags) |
422 | { |
306 | { |
423 | struct drm_mm_node *entry; |
307 | struct drm_mm_node *entry; |
424 | struct drm_mm_node *best; |
308 | struct drm_mm_node *best; |
Line 439... | Line 323... | ||
439 | } |
323 | } |
Line 440... | Line 324... | ||
440 | 324 | ||
441 | if (!check_free_hole(adj_start, adj_end, size, alignment)) |
325 | if (!check_free_hole(adj_start, adj_end, size, alignment)) |
Line 442... | Line 326... | ||
442 | continue; |
326 | continue; |
443 | 327 | ||
Line 444... | Line 328... | ||
444 | if (!best_match) |
328 | if (!(flags & DRM_MM_SEARCH_BEST)) |
445 | return entry; |
329 | return entry; |
446 | 330 | ||
447 | if (entry->size < best_size) { |
331 | if (entry->size < best_size) { |
448 | best = entry; |
332 | best = entry; |
Line 449... | Line 333... | ||
449 | best_size = entry->size; |
333 | best_size = entry->size; |
450 | } |
334 | } |
451 | } |
- | |
Line 452... | Line 335... | ||
452 | 335 | } |
|
453 | return best; |
336 | |
454 | } |
337 | return best; |
455 | EXPORT_SYMBOL(drm_mm_search_free_generic); |
338 | } |
456 | 339 | ||
457 | struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, |
340 | static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm, |
458 | unsigned long size, |
341 | unsigned long size, |
459 | unsigned alignment, |
342 | unsigned alignment, |
460 | unsigned long color, |
343 | unsigned long color, |
461 | unsigned long start, |
344 | unsigned long start, |
462 | unsigned long end, |
345 | unsigned long end, |
463 | bool best_match) |
346 | enum drm_mm_search_flags flags) |
Line 486... | Line 369... | ||
486 | } |
369 | } |
Line 487... | Line 370... | ||
487 | 370 | ||
488 | if (!check_free_hole(adj_start, adj_end, size, alignment)) |
371 | if (!check_free_hole(adj_start, adj_end, size, alignment)) |
Line 489... | Line 372... | ||
489 | continue; |
372 | continue; |
490 | 373 | ||
Line 491... | Line 374... | ||
491 | if (!best_match) |
374 | if (!(flags & DRM_MM_SEARCH_BEST)) |
492 | return entry; |
375 | return entry; |
493 | 376 | ||
494 | if (entry->size < best_size) { |
377 | if (entry->size < best_size) { |
495 | best = entry; |
378 | best = entry; |
Line 496... | Line 379... | ||
496 | best_size = entry->size; |
379 | best_size = entry->size; |
497 | } |
380 | } |
498 | } |
- | |
Line 499... | Line 381... | ||
499 | 381 | } |
|
500 | return best; |
382 | |
501 | } |
383 | return best; |
502 | EXPORT_SYMBOL(drm_mm_search_free_in_range_generic); |
384 | } |
Line 632... | Line 514... | ||
632 | * Nodes _must_ be removed in the exact same order from the scan list as they |
514 | * Nodes _must_ be removed in the exact same order from the scan list as they |
633 | * have been added, otherwise the internal state of the memory manager will be |
515 | * have been added, otherwise the internal state of the memory manager will be |
634 | * corrupted. |
516 | * corrupted. |
635 | * |
517 | * |
636 | * When the scan list is empty, the selected memory nodes can be freed. An |
518 | * When the scan list is empty, the selected memory nodes can be freed. An |
637 | * immediately following drm_mm_search_free with best_match = 0 will then return |
519 | * immediately following drm_mm_search_free with !DRM_MM_SEARCH_BEST will then |
638 | * the just freed block (because its at the top of the free_stack list). |
520 | * return the just freed block (because its at the top of the free_stack list). |
639 | * |
521 | * |
640 | * Returns one if this block should be evicted, zero otherwise. Will always |
522 | * Returns one if this block should be evicted, zero otherwise. Will always |
641 | * return zero when no hole has been found. |
523 | * return zero when no hole has been found. |
642 | */ |
524 | */ |
643 | int drm_mm_scan_remove_block(struct drm_mm_node *node) |
525 | int drm_mm_scan_remove_block(struct drm_mm_node *node) |
Line 670... | Line 552... | ||
670 | EXPORT_SYMBOL(drm_mm_clean); |
552 | EXPORT_SYMBOL(drm_mm_clean); |
Line 671... | Line 553... | ||
671 | 553 | ||
672 | void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) |
554 | void drm_mm_init(struct drm_mm * mm, unsigned long start, unsigned long size) |
673 | { |
555 | { |
674 | INIT_LIST_HEAD(&mm->hole_stack); |
- | |
675 | INIT_LIST_HEAD(&mm->unused_nodes); |
- | |
676 | mm->num_unused = 0; |
556 | INIT_LIST_HEAD(&mm->hole_stack); |
677 | mm->scanned_blocks = 0; |
- | |
Line 678... | Line 557... | ||
678 | spin_lock_init(&mm->unused_lock); |
557 | mm->scanned_blocks = 0; |
679 | 558 | ||
680 | /* Clever trick to avoid a special case in the free hole tracking. */ |
559 | /* Clever trick to avoid a special case in the free hole tracking. */ |
681 | INIT_LIST_HEAD(&mm->head_node.node_list); |
560 | INIT_LIST_HEAD(&mm->head_node.node_list); |
Line 693... | Line 572... | ||
693 | } |
572 | } |
694 | EXPORT_SYMBOL(drm_mm_init); |
573 | EXPORT_SYMBOL(drm_mm_init); |
Line 695... | Line 574... | ||
695 | 574 | ||
696 | void drm_mm_takedown(struct drm_mm * mm) |
575 | void drm_mm_takedown(struct drm_mm * mm) |
697 | { |
- | |
698 | struct drm_mm_node *entry, *next; |
- | |
699 | 576 | { |
|
700 | if (WARN(!list_empty(&mm->head_node.node_list), |
577 | WARN(!list_empty(&mm->head_node.node_list), |
701 | "Memory manager not clean. Delaying takedown\n")) { |
- | |
702 | return; |
- | |
703 | } |
- | |
704 | - | ||
705 | spin_lock(&mm->unused_lock); |
- | |
706 | list_for_each_entry_safe(entry, next, &mm->unused_nodes, node_list) { |
- | |
707 | list_del(&entry->node_list); |
- | |
708 | kfree(entry); |
- | |
709 | --mm->num_unused; |
- | |
710 | } |
- | |
711 | spin_unlock(&mm->unused_lock); |
- | |
712 | - | ||
713 | BUG_ON(mm->num_unused != 0); |
578 | "Memory manager not clean during takedown.\n"); |
714 | } |
579 | } |
Line 715... | Line 580... | ||
715 | EXPORT_SYMBOL(drm_mm_takedown); |
580 | EXPORT_SYMBOL(drm_mm_takedown); |
716 | 581 |