1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45#include <drm/drmP.h>
46#include <drm/drm_mm.h>
47#include <linux/slab.h>
48#include <linux/seq_file.h>
49#include <linux/export.h>
50#include <linux/interval_tree_generic.h>
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100#ifdef CONFIG_DRM_DEBUG_MM
101#include <linux/stackdepot.h>
102
103#define STACKDEPTH 32
104#define BUFSZ 4096
105
106static noinline void save_stack(struct drm_mm_node *node)
107{
108 unsigned long entries[STACKDEPTH];
109 struct stack_trace trace = {
110 .entries = entries,
111 .max_entries = STACKDEPTH,
112 .skip = 1
113 };
114
115 save_stack_trace(&trace);
116 if (trace.nr_entries != 0 &&
117 trace.entries[trace.nr_entries-1] == ULONG_MAX)
118 trace.nr_entries--;
119
120
121 node->stack = depot_save_stack(&trace, GFP_NOWAIT);
122}
123
124static void show_leaks(struct drm_mm *mm)
125{
126 struct drm_mm_node *node;
127 unsigned long entries[STACKDEPTH];
128 char *buf;
129
130 buf = kmalloc(BUFSZ, GFP_KERNEL);
131 if (!buf)
132 return;
133
134 list_for_each_entry(node, drm_mm_nodes(mm), node_list) {
135 struct stack_trace trace = {
136 .entries = entries,
137 .max_entries = STACKDEPTH
138 };
139
140 if (!node->stack) {
141 DRM_ERROR("node [%08llx + %08llx]: unknown owner\n",
142 node->start, node->size);
143 continue;
144 }
145
146 depot_fetch_stack(node->stack, &trace);
147 snprint_stack_trace(buf, BUFSZ, &trace, 0);
148 DRM_ERROR("node [%08llx + %08llx]: inserted at\n%s",
149 node->start, node->size, buf);
150 }
151
152 kfree(buf);
153}
154
155#undef STACKDEPTH
156#undef BUFSZ
157#else
158static void save_stack(struct drm_mm_node *node) { }
159static void show_leaks(struct drm_mm *mm) { }
160#endif
161
162#define START(node) ((node)->start)
163#define LAST(node) ((node)->start + (node)->size - 1)
164
165INTERVAL_TREE_DEFINE(struct drm_mm_node, rb,
166 u64, __subtree_last,
167 START, LAST, static inline, drm_mm_interval_tree)
168
169struct drm_mm_node *
170__drm_mm_interval_first(const struct drm_mm *mm, u64 start, u64 last)
171{
172 return drm_mm_interval_tree_iter_first((struct rb_root_cached *)&mm->interval_tree,
173 start, last) ?: (struct drm_mm_node *)&mm->head_node;
174}
175EXPORT_SYMBOL(__drm_mm_interval_first);
176
177static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node,
178 struct drm_mm_node *node)
179{
180 struct drm_mm *mm = hole_node->mm;
181 struct rb_node **link, *rb;
182 struct drm_mm_node *parent;
183 bool leftmost;
184
185 node->__subtree_last = LAST(node);
186
187 if (hole_node->allocated) {
188 rb = &hole_node->rb;
189 while (rb) {
190 parent = rb_entry(rb, struct drm_mm_node, rb);
191 if (parent->__subtree_last >= node->__subtree_last)
192 break;
193
194 parent->__subtree_last = node->__subtree_last;
195 rb = rb_parent(rb);
196 }
197
198 rb = &hole_node->rb;
199 link = &hole_node->rb.rb_right;
200 leftmost = false;
201 } else {
202 rb = NULL;
203 link = &mm->interval_tree.rb_root.rb_node;
204 leftmost = true;
205 }
206
207 while (*link) {
208 rb = *link;
209 parent = rb_entry(rb, struct drm_mm_node, rb);
210 if (parent->__subtree_last < node->__subtree_last)
211 parent->__subtree_last = node->__subtree_last;
212 if (node->start < parent->start) {
213 link = &parent->rb.rb_left;
214 } else {
215 link = &parent->rb.rb_right;
216 leftmost = false;
217 }
218 }
219
220 rb_link_node(&node->rb, rb, link);
221 rb_insert_augmented_cached(&node->rb, &mm->interval_tree, leftmost,
222 &drm_mm_interval_tree_augment);
223}
224
225#define RB_INSERT(root, member, expr) do { \
226 struct rb_node **link = &root.rb_node, *rb = NULL; \
227 u64 x = expr(node); \
228 while (*link) { \
229 rb = *link; \
230 if (x < expr(rb_entry(rb, struct drm_mm_node, member))) \
231 link = &rb->rb_left; \
232 else \
233 link = &rb->rb_right; \
234 } \
235 rb_link_node(&node->member, rb, link); \
236 rb_insert_color(&node->member, &root); \
237} while (0)
238
239#define HOLE_SIZE(NODE) ((NODE)->hole_size)
240#define HOLE_ADDR(NODE) (__drm_mm_hole_node_start(NODE))
241
242static void add_hole(struct drm_mm_node *node)
243{
244 struct drm_mm *mm = node->mm;
245
246 node->hole_size =
247 __drm_mm_hole_node_end(node) - __drm_mm_hole_node_start(node);
248 DRM_MM_BUG_ON(!drm_mm_hole_follows(node));
249
250 RB_INSERT(mm->holes_size, rb_hole_size, HOLE_SIZE);
251 RB_INSERT(mm->holes_addr, rb_hole_addr, HOLE_ADDR);
252
253 list_add(&node->hole_stack, &mm->hole_stack);
254}
255
256static void rm_hole(struct drm_mm_node *node)
257{
258 DRM_MM_BUG_ON(!drm_mm_hole_follows(node));
259
260 list_del(&node->hole_stack);
261 rb_erase(&node->rb_hole_size, &node->mm->holes_size);
262 rb_erase(&node->rb_hole_addr, &node->mm->holes_addr);
263 node->hole_size = 0;
264
265 DRM_MM_BUG_ON(drm_mm_hole_follows(node));
266}
267
268static inline struct drm_mm_node *rb_hole_size_to_node(struct rb_node *rb)
269{
270 return rb_entry_safe(rb, struct drm_mm_node, rb_hole_size);
271}
272
273static inline struct drm_mm_node *rb_hole_addr_to_node(struct rb_node *rb)
274{
275 return rb_entry_safe(rb, struct drm_mm_node, rb_hole_addr);
276}
277
278static inline u64 rb_hole_size(struct rb_node *rb)
279{
280 return rb_entry(rb, struct drm_mm_node, rb_hole_size)->hole_size;
281}
282
283static struct drm_mm_node *best_hole(struct drm_mm *mm, u64 size)
284{
285 struct rb_node *best = NULL;
286 struct rb_node **link = &mm->holes_size.rb_node;
287
288 while (*link) {
289 struct rb_node *rb = *link;
290
291 if (size <= rb_hole_size(rb)) {
292 link = &rb->rb_left;
293 best = rb;
294 } else {
295 link = &rb->rb_right;
296 }
297 }
298
299 return rb_hole_size_to_node(best);
300}
301
302static struct drm_mm_node *find_hole(struct drm_mm *mm, u64 addr)
303{
304 struct drm_mm_node *node = NULL;
305 struct rb_node **link = &mm->holes_addr.rb_node;
306
307 while (*link) {
308 u64 hole_start;
309
310 node = rb_hole_addr_to_node(*link);
311 hole_start = __drm_mm_hole_node_start(node);
312
313 if (addr < hole_start)
314 link = &node->rb_hole_addr.rb_left;
315 else if (addr > hole_start + node->hole_size)
316 link = &node->rb_hole_addr.rb_right;
317 else
318 break;
319 }
320
321 return node;
322}
323
324static struct drm_mm_node *
325first_hole(struct drm_mm *mm,
326 u64 start, u64 end, u64 size,
327 enum drm_mm_insert_mode mode)
328{
329 if (RB_EMPTY_ROOT(&mm->holes_size))
330 return NULL;
331
332 switch (mode) {
333 default:
334 case DRM_MM_INSERT_BEST:
335 return best_hole(mm, size);
336
337 case DRM_MM_INSERT_LOW:
338 return find_hole(mm, start);
339
340 case DRM_MM_INSERT_HIGH:
341 return find_hole(mm, end);
342
343 case DRM_MM_INSERT_EVICT:
344 return list_first_entry_or_null(&mm->hole_stack,
345 struct drm_mm_node,
346 hole_stack);
347 }
348}
349
350static struct drm_mm_node *
351next_hole(struct drm_mm *mm,
352 struct drm_mm_node *node,
353 enum drm_mm_insert_mode mode)
354{
355 switch (mode) {
356 default:
357 case DRM_MM_INSERT_BEST:
358 return rb_hole_size_to_node(rb_next(&node->rb_hole_size));
359
360 case DRM_MM_INSERT_LOW:
361 return rb_hole_addr_to_node(rb_next(&node->rb_hole_addr));
362
363 case DRM_MM_INSERT_HIGH:
364 return rb_hole_addr_to_node(rb_prev(&node->rb_hole_addr));
365
366 case DRM_MM_INSERT_EVICT:
367 node = list_next_entry(node, hole_stack);
368 return &node->hole_stack == &mm->hole_stack ? NULL : node;
369 }
370}
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
387{
388 u64 end = node->start + node->size;
389 struct drm_mm_node *hole;
390 u64 hole_start, hole_end;
391 u64 adj_start, adj_end;
392
393 end = node->start + node->size;
394 if (unlikely(end <= node->start))
395 return -ENOSPC;
396
397
398 hole = find_hole(mm, node->start);
399 if (!hole)
400 return -ENOSPC;
401
402 adj_start = hole_start = __drm_mm_hole_node_start(hole);
403 adj_end = hole_end = hole_start + hole->hole_size;
404
405 if (mm->color_adjust)
406 mm->color_adjust(hole, node->color, &adj_start, &adj_end);
407
408 if (adj_start > node->start || adj_end < end)
409 return -ENOSPC;
410
411 node->mm = mm;
412
413 list_add(&node->node_list, &hole->node_list);
414 drm_mm_interval_tree_add_node(hole, node);
415 node->allocated = true;
416 node->hole_size = 0;
417
418 rm_hole(hole);
419 if (node->start > hole_start)
420 add_hole(hole);
421 if (end < hole_end)
422 add_hole(node);
423
424 save_stack(node);
425 return 0;
426}
427EXPORT_SYMBOL(drm_mm_reserve_node);
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445int drm_mm_insert_node_in_range(struct drm_mm * const mm,
446 struct drm_mm_node * const node,
447 u64 size, u64 alignment,
448 unsigned long color,
449 u64 range_start, u64 range_end,
450 enum drm_mm_insert_mode mode)
451{
452 struct drm_mm_node *hole;
453 u64 remainder_mask;
454
455 DRM_MM_BUG_ON(range_start >= range_end);
456
457 if (unlikely(size == 0 || range_end - range_start < size))
458 return -ENOSPC;
459
460 if (alignment <= 1)
461 alignment = 0;
462
463 remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0;
464 for (hole = first_hole(mm, range_start, range_end, size, mode); hole;
465 hole = next_hole(mm, hole, mode)) {
466 u64 hole_start = __drm_mm_hole_node_start(hole);
467 u64 hole_end = hole_start + hole->hole_size;
468 u64 adj_start, adj_end;
469 u64 col_start, col_end;
470
471 if (mode == DRM_MM_INSERT_LOW && hole_start >= range_end)
472 break;
473
474 if (mode == DRM_MM_INSERT_HIGH && hole_end <= range_start)
475 break;
476
477 col_start = hole_start;
478 col_end = hole_end;
479 if (mm->color_adjust)
480 mm->color_adjust(hole, color, &col_start, &col_end);
481
482 adj_start = max(col_start, range_start);
483 adj_end = min(col_end, range_end);
484
485 if (adj_end <= adj_start || adj_end - adj_start < size)
486 continue;
487
488 if (mode == DRM_MM_INSERT_HIGH)
489 adj_start = adj_end - size;
490
491 if (alignment) {
492 u64 rem;
493
494 if (likely(remainder_mask))
495 rem = adj_start & remainder_mask;
496 else
497 div64_u64_rem(adj_start, alignment, &rem);
498 if (rem) {
499 adj_start -= rem;
500 if (mode != DRM_MM_INSERT_HIGH)
501 adj_start += alignment;
502
503 if (adj_start < max(col_start, range_start) ||
504 min(col_end, range_end) - adj_start < size)
505 continue;
506
507 if (adj_end <= adj_start ||
508 adj_end - adj_start < size)
509 continue;
510 }
511 }
512
513 node->mm = mm;
514 node->size = size;
515 node->start = adj_start;
516 node->color = color;
517 node->hole_size = 0;
518
519 list_add(&node->node_list, &hole->node_list);
520 drm_mm_interval_tree_add_node(hole, node);
521 node->allocated = true;
522
523 rm_hole(hole);
524 if (adj_start > hole_start)
525 add_hole(hole);
526 if (adj_start + size < hole_end)
527 add_hole(node);
528
529 save_stack(node);
530 return 0;
531 }
532
533 return -ENOSPC;
534}
535EXPORT_SYMBOL(drm_mm_insert_node_in_range);
536
537
538
539
540
541
542
543
544
545void drm_mm_remove_node(struct drm_mm_node *node)
546{
547 struct drm_mm *mm = node->mm;
548 struct drm_mm_node *prev_node;
549
550 DRM_MM_BUG_ON(!node->allocated);
551 DRM_MM_BUG_ON(node->scanned_block);
552
553 prev_node = list_prev_entry(node, node_list);
554
555 if (drm_mm_hole_follows(node))
556 rm_hole(node);
557
558 drm_mm_interval_tree_remove(node, &mm->interval_tree);
559 list_del(&node->node_list);
560 node->allocated = false;
561
562 if (drm_mm_hole_follows(prev_node))
563 rm_hole(prev_node);
564 add_hole(prev_node);
565}
566EXPORT_SYMBOL(drm_mm_remove_node);
567
568
569
570
571
572
573
574
575
576
577void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
578{
579 struct drm_mm *mm = old->mm;
580
581 DRM_MM_BUG_ON(!old->allocated);
582
583 *new = *old;
584
585 list_replace(&old->node_list, &new->node_list);
586 rb_replace_node_cached(&old->rb, &new->rb, &mm->interval_tree);
587
588 if (drm_mm_hole_follows(old)) {
589 list_replace(&old->hole_stack, &new->hole_stack);
590 rb_replace_node(&old->rb_hole_size,
591 &new->rb_hole_size,
592 &mm->holes_size);
593 rb_replace_node(&old->rb_hole_addr,
594 &new->rb_hole_addr,
595 &mm->holes_addr);
596 }
597
598 old->allocated = false;
599 new->allocated = true;
600}
601EXPORT_SYMBOL(drm_mm_replace_node);
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653void drm_mm_scan_init_with_range(struct drm_mm_scan *scan,
654 struct drm_mm *mm,
655 u64 size,
656 u64 alignment,
657 unsigned long color,
658 u64 start,
659 u64 end,
660 enum drm_mm_insert_mode mode)
661{
662 DRM_MM_BUG_ON(start >= end);
663 DRM_MM_BUG_ON(!size || size > end - start);
664 DRM_MM_BUG_ON(mm->scan_active);
665
666 scan->mm = mm;
667
668 if (alignment <= 1)
669 alignment = 0;
670
671 scan->color = color;
672 scan->alignment = alignment;
673 scan->remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0;
674 scan->size = size;
675 scan->mode = mode;
676
677 DRM_MM_BUG_ON(end <= start);
678 scan->range_start = start;
679 scan->range_end = end;
680
681 scan->hit_start = U64_MAX;
682 scan->hit_end = 0;
683}
684EXPORT_SYMBOL(drm_mm_scan_init_with_range);
685
686
687
688
689
690
691
692
693
694
695
696
697bool drm_mm_scan_add_block(struct drm_mm_scan *scan,
698 struct drm_mm_node *node)
699{
700 struct drm_mm *mm = scan->mm;
701 struct drm_mm_node *hole;
702 u64 hole_start, hole_end;
703 u64 col_start, col_end;
704 u64 adj_start, adj_end;
705
706 DRM_MM_BUG_ON(node->mm != mm);
707 DRM_MM_BUG_ON(!node->allocated);
708 DRM_MM_BUG_ON(node->scanned_block);
709 node->scanned_block = true;
710 mm->scan_active++;
711
712
713
714
715
716
717 hole = list_prev_entry(node, node_list);
718 DRM_MM_BUG_ON(list_next_entry(hole, node_list) != node);
719 __list_del_entry(&node->node_list);
720
721 hole_start = __drm_mm_hole_node_start(hole);
722 hole_end = __drm_mm_hole_node_end(hole);
723
724 col_start = hole_start;
725 col_end = hole_end;
726 if (mm->color_adjust)
727 mm->color_adjust(hole, scan->color, &col_start, &col_end);
728
729 adj_start = max(col_start, scan->range_start);
730 adj_end = min(col_end, scan->range_end);
731 if (adj_end <= adj_start || adj_end - adj_start < scan->size)
732 return false;
733
734 if (scan->mode == DRM_MM_INSERT_HIGH)
735 adj_start = adj_end - scan->size;
736
737 if (scan->alignment) {
738 u64 rem;
739
740 if (likely(scan->remainder_mask))
741 rem = adj_start & scan->remainder_mask;
742 else
743 div64_u64_rem(adj_start, scan->alignment, &rem);
744 if (rem) {
745 adj_start -= rem;
746 if (scan->mode != DRM_MM_INSERT_HIGH)
747 adj_start += scan->alignment;
748 if (adj_start < max(col_start, scan->range_start) ||
749 min(col_end, scan->range_end) - adj_start < scan->size)
750 return false;
751
752 if (adj_end <= adj_start ||
753 adj_end - adj_start < scan->size)
754 return false;
755 }
756 }
757
758 scan->hit_start = adj_start;
759 scan->hit_end = adj_start + scan->size;
760
761 DRM_MM_BUG_ON(scan->hit_start >= scan->hit_end);
762 DRM_MM_BUG_ON(scan->hit_start < hole_start);
763 DRM_MM_BUG_ON(scan->hit_end > hole_end);
764
765 return true;
766}
767EXPORT_SYMBOL(drm_mm_scan_add_block);
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788bool drm_mm_scan_remove_block(struct drm_mm_scan *scan,
789 struct drm_mm_node *node)
790{
791 struct drm_mm_node *prev_node;
792
793 DRM_MM_BUG_ON(node->mm != scan->mm);
794 DRM_MM_BUG_ON(!node->scanned_block);
795 node->scanned_block = false;
796
797 DRM_MM_BUG_ON(!node->mm->scan_active);
798 node->mm->scan_active--;
799
800
801
802
803
804
805
806
807
808 prev_node = list_prev_entry(node, node_list);
809 DRM_MM_BUG_ON(list_next_entry(prev_node, node_list) !=
810 list_next_entry(node, node_list));
811 list_add(&node->node_list, &prev_node->node_list);
812
813 return (node->start + node->size > scan->hit_start &&
814 node->start < scan->hit_end);
815}
816EXPORT_SYMBOL(drm_mm_scan_remove_block);
817
818
819
820
821
822
823
824
825
826
827
828
829struct drm_mm_node *drm_mm_scan_color_evict(struct drm_mm_scan *scan)
830{
831 struct drm_mm *mm = scan->mm;
832 struct drm_mm_node *hole;
833 u64 hole_start, hole_end;
834
835 DRM_MM_BUG_ON(list_empty(&mm->hole_stack));
836
837 if (!mm->color_adjust)
838 return NULL;
839
840
841
842
843
844
845 list_for_each_entry(hole, &mm->hole_stack, hole_stack) {
846 hole_start = __drm_mm_hole_node_start(hole);
847 hole_end = hole_start + hole->hole_size;
848
849 if (hole_start <= scan->hit_start &&
850 hole_end >= scan->hit_end)
851 break;
852 }
853
854
855 DRM_MM_BUG_ON(&hole->hole_stack == &mm->hole_stack);
856 if (unlikely(&hole->hole_stack == &mm->hole_stack))
857 return NULL;
858
859 DRM_MM_BUG_ON(hole_start > scan->hit_start);
860 DRM_MM_BUG_ON(hole_end < scan->hit_end);
861
862 mm->color_adjust(hole, scan->color, &hole_start, &hole_end);
863 if (hole_start > scan->hit_start)
864 return hole;
865 if (hole_end < scan->hit_end)
866 return list_next_entry(hole, node_list);
867
868 return NULL;
869}
870EXPORT_SYMBOL(drm_mm_scan_color_evict);
871
872
873
874
875
876
877
878
879
880void drm_mm_init(struct drm_mm *mm, u64 start, u64 size)
881{
882 DRM_MM_BUG_ON(start + size <= start);
883
884 mm->color_adjust = NULL;
885
886 INIT_LIST_HEAD(&mm->hole_stack);
887 mm->interval_tree = RB_ROOT_CACHED;
888 mm->holes_size = RB_ROOT;
889 mm->holes_addr = RB_ROOT;
890
891
892 INIT_LIST_HEAD(&mm->head_node.node_list);
893 mm->head_node.allocated = false;
894 mm->head_node.mm = mm;
895 mm->head_node.start = start + size;
896 mm->head_node.size = -size;
897 add_hole(&mm->head_node);
898
899 mm->scan_active = 0;
900}
901EXPORT_SYMBOL(drm_mm_init);
902
903
904
905
906
907
908
909
910void drm_mm_takedown(struct drm_mm *mm)
911{
912 if (WARN(!drm_mm_clean(mm),
913 "Memory manager not clean during takedown.\n"))
914 show_leaks(mm);
915}
916EXPORT_SYMBOL(drm_mm_takedown);
917
918static u64 drm_mm_dump_hole(struct drm_printer *p, const struct drm_mm_node *entry)
919{
920 u64 start, size;
921
922 size = entry->hole_size;
923 if (size) {
924 start = drm_mm_hole_node_start(entry);
925 drm_printf(p, "%#018llx-%#018llx: %llu: free\n",
926 start, start + size, size);
927 }
928
929 return size;
930}
931
932
933
934
935
936void drm_mm_print(const struct drm_mm *mm, struct drm_printer *p)
937{
938 const struct drm_mm_node *entry;
939 u64 total_used = 0, total_free = 0, total = 0;
940
941 total_free += drm_mm_dump_hole(p, &mm->head_node);
942
943 drm_mm_for_each_node(entry, mm) {
944 drm_printf(p, "%#018llx-%#018llx: %llu: used\n", entry->start,
945 entry->start + entry->size, entry->size);
946 total_used += entry->size;
947 total_free += drm_mm_dump_hole(p, entry);
948 }
949 total = total_free + total_used;
950
951 drm_printf(p, "total: %llu, used %llu free %llu\n", total,
952 total_used, total_free);
953}
954EXPORT_SYMBOL(drm_mm_print);
955