1746fbbdbSjsg /************************************************************************** 2746fbbdbSjsg * 3746fbbdbSjsg * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. 47f4dd379Sjsg * Copyright 2016 Intel Corporation 5746fbbdbSjsg * All Rights Reserved. 6746fbbdbSjsg * 7746fbbdbSjsg * Permission is hereby granted, free of charge, to any person obtaining a 8746fbbdbSjsg * copy of this software and associated documentation files (the 9746fbbdbSjsg * "Software"), to deal in the Software without restriction, including 10746fbbdbSjsg * without limitation the rights to use, copy, modify, merge, publish, 11746fbbdbSjsg * distribute, sub license, and/or sell copies of the Software, and to 12746fbbdbSjsg * permit persons to whom the Software is furnished to do so, subject to 13746fbbdbSjsg * the following conditions: 14746fbbdbSjsg * 15746fbbdbSjsg * The above copyright notice and this permission notice (including the 16746fbbdbSjsg * next paragraph) shall be included in all copies or substantial portions 17746fbbdbSjsg * of the Software. 18746fbbdbSjsg * 19746fbbdbSjsg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20746fbbdbSjsg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21746fbbdbSjsg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 22746fbbdbSjsg * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 23746fbbdbSjsg * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24746fbbdbSjsg * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 25746fbbdbSjsg * USE OR OTHER DEALINGS IN THE SOFTWARE. 26746fbbdbSjsg * 27746fbbdbSjsg * 28746fbbdbSjsg **************************************************************************/ 29746fbbdbSjsg 30746fbbdbSjsg /* 31746fbbdbSjsg * Generic simple memory manager implementation. Intended to be used as a base 32746fbbdbSjsg * class implementation for more advanced memory managers. 33746fbbdbSjsg * 34746fbbdbSjsg * Note that the algorithm used is quite simple and there might be substantial 357f4dd379Sjsg * performance gains if a smarter free list is implemented. Currently it is 367f4dd379Sjsg * just an unordered stack of free regions. This could easily be improved if 377f4dd379Sjsg * an RB-tree is used instead. At least if we expect heavy fragmentation. 38746fbbdbSjsg * 39746fbbdbSjsg * Aligned allocations can also see improvement. 40746fbbdbSjsg * 41746fbbdbSjsg * Authors: 42746fbbdbSjsg * Thomas Hellström <thomas-at-tungstengraphics-dot-com> 43746fbbdbSjsg */ 44746fbbdbSjsg 453253c27bSkettenis #include <linux/export.h> 467f4dd379Sjsg #include <linux/interval_tree_generic.h> 47c349dbc7Sjsg #include <linux/seq_file.h> 48c349dbc7Sjsg #include <linux/slab.h> 49c349dbc7Sjsg #include <linux/stacktrace.h> 50c349dbc7Sjsg 51c349dbc7Sjsg #include <drm/drm_mm.h> 52746fbbdbSjsg 533253c27bSkettenis /** 543253c27bSkettenis * DOC: Overview 554e088e8fSjsg * 563253c27bSkettenis * drm_mm provides a simple range allocator. The drivers are free to use the 573253c27bSkettenis * resource allocator from the linux core if it suits them, the upside of drm_mm 583253c27bSkettenis * is that it's in the DRM core. Which means that it's easier to extend for 593253c27bSkettenis * some of the crazier special purpose needs of gpus. 603253c27bSkettenis * 613253c27bSkettenis * The main data struct is &drm_mm, allocations are tracked in &drm_mm_node. 623253c27bSkettenis * Drivers are free to embed either of them into their own suitable 637f4dd379Sjsg * datastructures. drm_mm itself will not do any memory allocations of its own, 647f4dd379Sjsg * so if drivers choose not to embed nodes they need to still allocate them 653253c27bSkettenis * themselves. 663253c27bSkettenis * 673253c27bSkettenis * The range allocator also supports reservation of preallocated blocks. This is 683253c27bSkettenis * useful for taking over initial mode setting configurations from the firmware, 693253c27bSkettenis * where an object needs to be created which exactly matches the firmware's 703253c27bSkettenis * scanout target. As long as the range is still free it can be inserted anytime 713253c27bSkettenis * after the allocator is initialized, which helps with avoiding looped 727f4dd379Sjsg * dependencies in the driver load sequence. 733253c27bSkettenis * 743253c27bSkettenis * drm_mm maintains a stack of most recently freed holes, which of all 753253c27bSkettenis * simplistic datastructures seems to be a fairly decent approach to clustering 763253c27bSkettenis * allocations and avoiding too much fragmentation. This means free space 773253c27bSkettenis * searches are O(num_holes). Given that all the fancy features drm_mm supports 783253c27bSkettenis * something better would be fairly complex and since gfx thrashing is a fairly 793253c27bSkettenis * steep cliff not a real concern. Removing a node again is O(1). 803253c27bSkettenis * 813253c27bSkettenis * drm_mm supports a few features: Alignment and range restrictions can be 823253c27bSkettenis * supplied. Furthermore every &drm_mm_node has a color value (which is just an 837f4dd379Sjsg * opaque unsigned long) which in conjunction with a driver callback can be used 843253c27bSkettenis * to implement sophisticated placement restrictions. The i915 DRM driver uses 853253c27bSkettenis * this to implement guard pages between incompatible caching domains in the 863253c27bSkettenis * graphics TT. 873253c27bSkettenis * 887f4dd379Sjsg * Two behaviors are supported for searching and allocating: bottom-up and 897f4dd379Sjsg * top-down. The default is bottom-up. Top-down allocation can be used if the 907f4dd379Sjsg * memory area has different restrictions, or just to reduce fragmentation. 913253c27bSkettenis * 923253c27bSkettenis * Finally iteration helpers to walk all nodes and all holes are provided as are 933253c27bSkettenis * some basic allocator dumpers for debugging. 947f4dd379Sjsg * 957f4dd379Sjsg * Note that this range allocator is not thread-safe, drivers need to protect 9648cf9467Sjsg * modifications with their own locking. The idea behind this is that for a full 977f4dd379Sjsg * memory manager additional data needs to be protected anyway, hence internal 987f4dd379Sjsg * locking would be fully redundant. 994e088e8fSjsg */ 100746fbbdbSjsg 1017f4dd379Sjsg #ifdef CONFIG_DRM_DEBUG_MM 1027f4dd379Sjsg #include <linux/stackdepot.h> 1037f4dd379Sjsg 1047f4dd379Sjsg #define STACKDEPTH 32 1057f4dd379Sjsg #define BUFSZ 4096 1067f4dd379Sjsg 1077f4dd379Sjsg static noinline void save_stack(struct drm_mm_node *node) 1087f4dd379Sjsg { 1097f4dd379Sjsg unsigned long entries[STACKDEPTH]; 11048cf9467Sjsg unsigned int n; 1117f4dd379Sjsg 11248cf9467Sjsg n = stack_trace_save(entries, ARRAY_SIZE(entries), 1); 1137f4dd379Sjsg 1147f4dd379Sjsg /* May be called under spinlock, so avoid sleeping */ 11548cf9467Sjsg node->stack = stack_depot_save(entries, n, GFP_NOWAIT); 1167f4dd379Sjsg } 1177f4dd379Sjsg 1187f4dd379Sjsg static void show_leaks(struct drm_mm *mm) 1197f4dd379Sjsg { 1207f4dd379Sjsg struct drm_mm_node *node; 12148cf9467Sjsg unsigned long *entries; 12248cf9467Sjsg unsigned int nr_entries; 1237f4dd379Sjsg char *buf; 1247f4dd379Sjsg 1257f4dd379Sjsg buf = kmalloc(BUFSZ, GFP_KERNEL); 1267f4dd379Sjsg if (!buf) 1277f4dd379Sjsg return; 1287f4dd379Sjsg 1297f4dd379Sjsg list_for_each_entry(node, drm_mm_nodes(mm), node_list) { 1307f4dd379Sjsg if (!node->stack) { 1317f4dd379Sjsg DRM_ERROR("node [%08llx + %08llx]: unknown owner\n", 1327f4dd379Sjsg node->start, node->size); 1337f4dd379Sjsg continue; 1347f4dd379Sjsg } 1357f4dd379Sjsg 13648cf9467Sjsg nr_entries = stack_depot_fetch(node->stack, &entries); 13748cf9467Sjsg stack_trace_snprint(buf, BUFSZ, entries, nr_entries, 0); 1387f4dd379Sjsg DRM_ERROR("node [%08llx + %08llx]: inserted at\n%s", 1397f4dd379Sjsg node->start, node->size, buf); 1407f4dd379Sjsg } 1417f4dd379Sjsg 1427f4dd379Sjsg kfree(buf); 1437f4dd379Sjsg } 1447f4dd379Sjsg 1457f4dd379Sjsg #undef STACKDEPTH 1467f4dd379Sjsg #undef BUFSZ 1477f4dd379Sjsg #else 1487f4dd379Sjsg static void save_stack(struct drm_mm_node *node) { } 1497f4dd379Sjsg static void show_leaks(struct drm_mm *mm) { } 1507f4dd379Sjsg #endif 1517f4dd379Sjsg 1527f4dd379Sjsg #define START(node) ((node)->start) 1537f4dd379Sjsg #define LAST(node) ((node)->start + (node)->size - 1) 1547f4dd379Sjsg 1557f4dd379Sjsg #ifdef __linux__ 1567f4dd379Sjsg INTERVAL_TREE_DEFINE(struct drm_mm_node, rb, 1577f4dd379Sjsg u64, __subtree_last, 158*c9876c59Sjsg START, LAST, static inline __maybe_unused, drm_mm_interval_tree) 1597f4dd379Sjsg #else 16048cf9467Sjsg static struct drm_mm_node * 16148cf9467Sjsg drm_mm_interval_tree_iter_first(const struct rb_root_cached *root, 16248cf9467Sjsg uint64_t start, uint64_t last) 1637f4dd379Sjsg { 1647f4dd379Sjsg struct drm_mm_node *node; 16548cf9467Sjsg struct rb_node *rb; 1667f4dd379Sjsg 16748cf9467Sjsg for (rb = rb_first_cached(root); rb; rb = rb_next(rb)) { 16848cf9467Sjsg node = rb_entry(rb, typeof(*node), rb); 1697f4dd379Sjsg if (LAST(node) >= start && START(node) <= last) 1707f4dd379Sjsg return node; 1717f4dd379Sjsg } 1727f4dd379Sjsg return NULL; 1737f4dd379Sjsg } 17448cf9467Sjsg 17548cf9467Sjsg static void 17648cf9467Sjsg drm_mm_interval_tree_remove(struct drm_mm_node *node, 17748cf9467Sjsg struct rb_root_cached *root) 17848cf9467Sjsg { 17948cf9467Sjsg rb_erase_cached(&node->rb, root); 18048cf9467Sjsg } 1817f4dd379Sjsg #endif 1827f4dd379Sjsg 1837f4dd379Sjsg struct drm_mm_node * 1847f4dd379Sjsg __drm_mm_interval_first(const struct drm_mm *mm, u64 start, u64 last) 1857f4dd379Sjsg { 18648cf9467Sjsg return drm_mm_interval_tree_iter_first((struct rb_root_cached *)&mm->interval_tree, 18748cf9467Sjsg start, last) ?: (struct drm_mm_node *)&mm->head_node; 1887f4dd379Sjsg } 1897f4dd379Sjsg EXPORT_SYMBOL(__drm_mm_interval_first); 1907f4dd379Sjsg 1917f4dd379Sjsg static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node, 1927f4dd379Sjsg struct drm_mm_node *node) 1937f4dd379Sjsg { 1947f4dd379Sjsg struct drm_mm *mm = hole_node->mm; 1957f4dd379Sjsg struct rb_node **link, *rb; 1967f4dd379Sjsg struct drm_mm_node *parent; 19748cf9467Sjsg bool leftmost; 1987f4dd379Sjsg 1997f4dd379Sjsg node->__subtree_last = LAST(node); 2007f4dd379Sjsg 20148cf9467Sjsg if (drm_mm_node_allocated(hole_node)) { 2027f4dd379Sjsg rb = &hole_node->rb; 2037f4dd379Sjsg while (rb) { 2047f4dd379Sjsg parent = rb_entry(rb, struct drm_mm_node, rb); 2057f4dd379Sjsg if (parent->__subtree_last >= node->__subtree_last) 2067f4dd379Sjsg break; 2077f4dd379Sjsg 2087f4dd379Sjsg parent->__subtree_last = node->__subtree_last; 2097f4dd379Sjsg rb = rb_parent(rb); 2107f4dd379Sjsg } 2117f4dd379Sjsg 2127f4dd379Sjsg rb = &hole_node->rb; 2137f4dd379Sjsg link = &hole_node->rb.rb_right; 21448cf9467Sjsg leftmost = false; 2157f4dd379Sjsg } else { 2167f4dd379Sjsg rb = NULL; 21748cf9467Sjsg link = &mm->interval_tree.rb_root.rb_node; 21848cf9467Sjsg leftmost = true; 2197f4dd379Sjsg } 2207f4dd379Sjsg 2217f4dd379Sjsg while (*link) { 2227f4dd379Sjsg rb = *link; 2237f4dd379Sjsg parent = rb_entry(rb, struct drm_mm_node, rb); 2247f4dd379Sjsg if (parent->__subtree_last < node->__subtree_last) 2257f4dd379Sjsg parent->__subtree_last = node->__subtree_last; 22648cf9467Sjsg if (node->start < parent->start) { 2277f4dd379Sjsg link = &parent->rb.rb_left; 22848cf9467Sjsg } else { 2297f4dd379Sjsg link = &parent->rb.rb_right; 23048cf9467Sjsg leftmost = false; 23148cf9467Sjsg } 2327f4dd379Sjsg } 2337f4dd379Sjsg 2347f4dd379Sjsg rb_link_node(&node->rb, rb, link); 23548cf9467Sjsg #ifdef notyet 23648cf9467Sjsg rb_insert_augmented_cached(&node->rb, &mm->interval_tree, leftmost, 2377f4dd379Sjsg &drm_mm_interval_tree_augment); 23848cf9467Sjsg #else 23948cf9467Sjsg rb_insert_color_cached(&node->rb, &mm->interval_tree, leftmost); 2407f4dd379Sjsg #endif 24148cf9467Sjsg } 2427f4dd379Sjsg 2439e16f3f2Sjsg #define DRM_RB_INSERT(root, member, expr) do { \ 2449e16f3f2Sjsg struct rb_node **link = &root.rb_node, *rb = NULL; \ 2459e16f3f2Sjsg u64 x = expr(node); \ 2469e16f3f2Sjsg while (*link) { \ 2479e16f3f2Sjsg rb = *link; \ 2489e16f3f2Sjsg if (x < expr(rb_entry(rb, struct drm_mm_node, member))) \ 2499e16f3f2Sjsg link = &rb->rb_left; \ 2509e16f3f2Sjsg else \ 2519e16f3f2Sjsg link = &rb->rb_right; \ 2529e16f3f2Sjsg } \ 2539e16f3f2Sjsg rb_link_node(&node->member, rb, link); \ 2549e16f3f2Sjsg rb_insert_color(&node->member, &root); \ 2559e16f3f2Sjsg } while (0) 2569e16f3f2Sjsg 25748cf9467Sjsg #define HOLE_SIZE(NODE) ((NODE)->hole_size) 25848cf9467Sjsg #define HOLE_ADDR(NODE) (__drm_mm_hole_node_start(NODE)) 25948cf9467Sjsg 26048cf9467Sjsg static u64 rb_to_hole_size(struct rb_node *rb) 261746fbbdbSjsg { 26248cf9467Sjsg return rb_entry(rb, struct drm_mm_node, rb_hole_size)->hole_size; 26348cf9467Sjsg } 264746fbbdbSjsg 26548cf9467Sjsg static void insert_hole_size(struct rb_root_cached *root, 26648cf9467Sjsg struct drm_mm_node *node) 26748cf9467Sjsg { 26848cf9467Sjsg struct rb_node **link = &root->rb_root.rb_node, *rb = NULL; 26948cf9467Sjsg u64 x = node->hole_size; 27048cf9467Sjsg bool first = true; 271746fbbdbSjsg 27248cf9467Sjsg while (*link) { 27348cf9467Sjsg rb = *link; 27448cf9467Sjsg if (x > rb_to_hole_size(rb)) { 27548cf9467Sjsg link = &rb->rb_left; 27648cf9467Sjsg } else { 27748cf9467Sjsg link = &rb->rb_right; 27848cf9467Sjsg first = false; 2794e088e8fSjsg } 2803253c27bSkettenis } 2813253c27bSkettenis 28248cf9467Sjsg rb_link_node(&node->rb_hole_size, rb, link); 28348cf9467Sjsg rb_insert_color_cached(&node->rb_hole_size, root, first); 2844e088e8fSjsg } 285746fbbdbSjsg 28648cf9467Sjsg static void add_hole(struct drm_mm_node *node) 28748cf9467Sjsg { 28848cf9467Sjsg struct drm_mm *mm = node->mm; 289746fbbdbSjsg 29048cf9467Sjsg node->hole_size = 29148cf9467Sjsg __drm_mm_hole_node_end(node) - __drm_mm_hole_node_start(node); 29248cf9467Sjsg DRM_MM_BUG_ON(!drm_mm_hole_follows(node)); 293746fbbdbSjsg 29448cf9467Sjsg insert_hole_size(&mm->holes_size, node); 2959e16f3f2Sjsg DRM_RB_INSERT(mm->holes_addr, rb_hole_addr, HOLE_ADDR); 2967f4dd379Sjsg 297746fbbdbSjsg list_add(&node->hole_stack, &mm->hole_stack); 298746fbbdbSjsg } 2997f4dd379Sjsg 30048cf9467Sjsg static void rm_hole(struct drm_mm_node *node) 30148cf9467Sjsg { 30248cf9467Sjsg DRM_MM_BUG_ON(!drm_mm_hole_follows(node)); 30348cf9467Sjsg 30448cf9467Sjsg list_del(&node->hole_stack); 30548cf9467Sjsg rb_erase_cached(&node->rb_hole_size, &node->mm->holes_size); 30648cf9467Sjsg rb_erase(&node->rb_hole_addr, &node->mm->holes_addr); 30748cf9467Sjsg node->hole_size = 0; 30848cf9467Sjsg 30948cf9467Sjsg DRM_MM_BUG_ON(drm_mm_hole_follows(node)); 31048cf9467Sjsg } 31148cf9467Sjsg 31248cf9467Sjsg static inline struct drm_mm_node *rb_hole_size_to_node(struct rb_node *rb) 31348cf9467Sjsg { 31448cf9467Sjsg return rb_entry_safe(rb, struct drm_mm_node, rb_hole_size); 31548cf9467Sjsg } 31648cf9467Sjsg 31748cf9467Sjsg static inline struct drm_mm_node *rb_hole_addr_to_node(struct rb_node *rb) 31848cf9467Sjsg { 31948cf9467Sjsg return rb_entry_safe(rb, struct drm_mm_node, rb_hole_addr); 32048cf9467Sjsg } 32148cf9467Sjsg 3229e16f3f2Sjsg static inline u64 rb_hole_size(struct rb_node *rb) 3239e16f3f2Sjsg { 3249e16f3f2Sjsg return rb_entry(rb, struct drm_mm_node, rb_hole_size)->hole_size; 3259e16f3f2Sjsg } 3269e16f3f2Sjsg 32748cf9467Sjsg static struct drm_mm_node *best_hole(struct drm_mm *mm, u64 size) 32848cf9467Sjsg { 32948cf9467Sjsg struct rb_node *rb = mm->holes_size.rb_root.rb_node; 33048cf9467Sjsg struct drm_mm_node *best = NULL; 33148cf9467Sjsg 33248cf9467Sjsg do { 33348cf9467Sjsg struct drm_mm_node *node = 33448cf9467Sjsg rb_entry(rb, struct drm_mm_node, rb_hole_size); 33548cf9467Sjsg 33648cf9467Sjsg if (size <= node->hole_size) { 33748cf9467Sjsg best = node; 33848cf9467Sjsg rb = rb->rb_right; 33948cf9467Sjsg } else { 34048cf9467Sjsg rb = rb->rb_left; 34148cf9467Sjsg } 34248cf9467Sjsg } while (rb); 34348cf9467Sjsg 34448cf9467Sjsg return best; 34548cf9467Sjsg } 34648cf9467Sjsg 3479e16f3f2Sjsg static struct drm_mm_node *find_hole(struct drm_mm *mm, u64 addr) 34848cf9467Sjsg { 34948cf9467Sjsg struct rb_node *rb = mm->holes_addr.rb_node; 35048cf9467Sjsg struct drm_mm_node *node = NULL; 35148cf9467Sjsg 35248cf9467Sjsg while (rb) { 35348cf9467Sjsg u64 hole_start; 35448cf9467Sjsg 35548cf9467Sjsg node = rb_hole_addr_to_node(rb); 35648cf9467Sjsg hole_start = __drm_mm_hole_node_start(node); 35748cf9467Sjsg 35848cf9467Sjsg if (addr < hole_start) 35948cf9467Sjsg rb = node->rb_hole_addr.rb_left; 36048cf9467Sjsg else if (addr > hole_start + node->hole_size) 36148cf9467Sjsg rb = node->rb_hole_addr.rb_right; 36248cf9467Sjsg else 36348cf9467Sjsg break; 36448cf9467Sjsg } 36548cf9467Sjsg 36648cf9467Sjsg return node; 36748cf9467Sjsg } 36848cf9467Sjsg 36948cf9467Sjsg static struct drm_mm_node * 37048cf9467Sjsg first_hole(struct drm_mm *mm, 37148cf9467Sjsg u64 start, u64 end, u64 size, 37248cf9467Sjsg enum drm_mm_insert_mode mode) 37348cf9467Sjsg { 37448cf9467Sjsg switch (mode) { 37548cf9467Sjsg default: 37648cf9467Sjsg case DRM_MM_INSERT_BEST: 37748cf9467Sjsg return best_hole(mm, size); 37848cf9467Sjsg 37948cf9467Sjsg case DRM_MM_INSERT_LOW: 3809e16f3f2Sjsg return find_hole(mm, start); 38148cf9467Sjsg 38248cf9467Sjsg case DRM_MM_INSERT_HIGH: 3839e16f3f2Sjsg return find_hole(mm, end); 38448cf9467Sjsg 38548cf9467Sjsg case DRM_MM_INSERT_EVICT: 38648cf9467Sjsg return list_first_entry_or_null(&mm->hole_stack, 38748cf9467Sjsg struct drm_mm_node, 38848cf9467Sjsg hole_stack); 38948cf9467Sjsg } 39048cf9467Sjsg } 39148cf9467Sjsg 39248cf9467Sjsg static struct drm_mm_node * 39348cf9467Sjsg next_hole(struct drm_mm *mm, 39448cf9467Sjsg struct drm_mm_node *node, 39548cf9467Sjsg enum drm_mm_insert_mode mode) 39648cf9467Sjsg { 39748cf9467Sjsg switch (mode) { 39848cf9467Sjsg default: 39948cf9467Sjsg case DRM_MM_INSERT_BEST: 40048cf9467Sjsg return rb_hole_size_to_node(rb_prev(&node->rb_hole_size)); 40148cf9467Sjsg 40248cf9467Sjsg case DRM_MM_INSERT_LOW: 4039e16f3f2Sjsg return rb_hole_addr_to_node(rb_next(&node->rb_hole_addr)); 40448cf9467Sjsg 40548cf9467Sjsg case DRM_MM_INSERT_HIGH: 4069e16f3f2Sjsg return rb_hole_addr_to_node(rb_prev(&node->rb_hole_addr)); 40748cf9467Sjsg 40848cf9467Sjsg case DRM_MM_INSERT_EVICT: 40948cf9467Sjsg node = list_next_entry(node, hole_stack); 41048cf9467Sjsg return &node->hole_stack == &mm->hole_stack ? NULL : node; 41148cf9467Sjsg } 412746fbbdbSjsg } 413746fbbdbSjsg 4143253c27bSkettenis /** 4153253c27bSkettenis * drm_mm_reserve_node - insert an pre-initialized node 4163253c27bSkettenis * @mm: drm_mm allocator to insert @node into 4173253c27bSkettenis * @node: drm_mm_node to insert 4183253c27bSkettenis * 4197f4dd379Sjsg * This functions inserts an already set-up &drm_mm_node into the allocator, 4207f4dd379Sjsg * meaning that start, size and color must be set by the caller. All other 4217f4dd379Sjsg * fields must be cleared to 0. This is useful to initialize the allocator with 4227f4dd379Sjsg * preallocated objects which must be set-up before the range allocator can be 4237f4dd379Sjsg * set-up, e.g. when taking over a firmware framebuffer. 4243253c27bSkettenis * 4253253c27bSkettenis * Returns: 4263253c27bSkettenis * 0 on success, -ENOSPC if there's no hole where @node is. 4273253c27bSkettenis */ 428e1001332Skettenis int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) 429e1001332Skettenis { 430e1001332Skettenis struct drm_mm_node *hole; 4317f4dd379Sjsg u64 hole_start, hole_end; 4327f4dd379Sjsg u64 adj_start, adj_end; 43348cf9467Sjsg u64 end; 434e1001332Skettenis 4353253c27bSkettenis end = node->start + node->size; 4367f4dd379Sjsg if (unlikely(end <= node->start)) 4377f4dd379Sjsg return -ENOSPC; 4383253c27bSkettenis 439e1001332Skettenis /* Find the relevant hole to add our node to */ 4409e16f3f2Sjsg hole = find_hole(mm, node->start); 44148cf9467Sjsg if (!hole) 4427f4dd379Sjsg return -ENOSPC; 4437f4dd379Sjsg 4447f4dd379Sjsg adj_start = hole_start = __drm_mm_hole_node_start(hole); 44548cf9467Sjsg adj_end = hole_end = hole_start + hole->hole_size; 4467f4dd379Sjsg 4477f4dd379Sjsg if (mm->color_adjust) 4487f4dd379Sjsg mm->color_adjust(hole, node->color, &adj_start, &adj_end); 4497f4dd379Sjsg 4507f4dd379Sjsg if (adj_start > node->start || adj_end < end) 4517f4dd379Sjsg return -ENOSPC; 452e1001332Skettenis 453e1001332Skettenis node->mm = mm; 454e1001332Skettenis 45548cf9467Sjsg __set_bit(DRM_MM_NODE_ALLOCATED_BIT, &node->flags); 456e1001332Skettenis list_add(&node->node_list, &hole->node_list); 4577f4dd379Sjsg drm_mm_interval_tree_add_node(hole, node); 45848cf9467Sjsg node->hole_size = 0; 4597f4dd379Sjsg 46048cf9467Sjsg rm_hole(hole); 46148cf9467Sjsg if (node->start > hole_start) 46248cf9467Sjsg add_hole(hole); 46348cf9467Sjsg if (end < hole_end) 46448cf9467Sjsg add_hole(node); 465e1001332Skettenis 4667f4dd379Sjsg save_stack(node); 4677f4dd379Sjsg return 0; 468e1001332Skettenis } 469e1001332Skettenis EXPORT_SYMBOL(drm_mm_reserve_node); 470e1001332Skettenis 47148cf9467Sjsg static u64 rb_to_hole_size_or_zero(struct rb_node *rb) 47248cf9467Sjsg { 47348cf9467Sjsg return rb ? rb_to_hole_size(rb) : 0; 47448cf9467Sjsg } 47548cf9467Sjsg 4764e088e8fSjsg /** 47748cf9467Sjsg * drm_mm_insert_node_in_range - ranged search for space and insert @node 4783253c27bSkettenis * @mm: drm_mm to allocate from 4793253c27bSkettenis * @node: preallocate node to insert 4803253c27bSkettenis * @size: size of the allocation 4813253c27bSkettenis * @alignment: alignment of the allocation 4823253c27bSkettenis * @color: opaque tag value to use for this node 48348cf9467Sjsg * @range_start: start of the allowed range for this node 48448cf9467Sjsg * @range_end: end of the allowed range for this node 48548cf9467Sjsg * @mode: fine-tune the allocation search and placement 4863253c27bSkettenis * 4877f4dd379Sjsg * The preallocated @node must be cleared to 0. 4883253c27bSkettenis * 4893253c27bSkettenis * Returns: 4903253c27bSkettenis * 0 on success, -ENOSPC if there's no suitable hole. 4914e088e8fSjsg */ 49248cf9467Sjsg int drm_mm_insert_node_in_range(struct drm_mm * const mm, 49348cf9467Sjsg struct drm_mm_node * const node, 4947f4dd379Sjsg u64 size, u64 alignment, 4953253c27bSkettenis unsigned long color, 49648cf9467Sjsg u64 range_start, u64 range_end, 49748cf9467Sjsg enum drm_mm_insert_mode mode) 4984e088e8fSjsg { 49948cf9467Sjsg struct drm_mm_node *hole; 50048cf9467Sjsg u64 remainder_mask; 50148cf9467Sjsg bool once; 5024e088e8fSjsg 50348cf9467Sjsg DRM_MM_BUG_ON(range_start > range_end); 5047f4dd379Sjsg 50548cf9467Sjsg if (unlikely(size == 0 || range_end - range_start < size)) 5064e088e8fSjsg return -ENOSPC; 5074e088e8fSjsg 50848cf9467Sjsg if (rb_to_hole_size_or_zero(rb_first_cached(&mm->holes_size)) < size) 50948cf9467Sjsg return -ENOSPC; 51048cf9467Sjsg 51148cf9467Sjsg if (alignment <= 1) 51248cf9467Sjsg alignment = 0; 51348cf9467Sjsg 51448cf9467Sjsg once = mode & DRM_MM_INSERT_ONCE; 51548cf9467Sjsg mode &= ~DRM_MM_INSERT_ONCE; 51648cf9467Sjsg 51748cf9467Sjsg remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0; 51848cf9467Sjsg for (hole = first_hole(mm, range_start, range_end, size, mode); 51948cf9467Sjsg hole; 5209e16f3f2Sjsg hole = once ? NULL : next_hole(mm, hole, mode)) { 52148cf9467Sjsg u64 hole_start = __drm_mm_hole_node_start(hole); 52248cf9467Sjsg u64 hole_end = hole_start + hole->hole_size; 52348cf9467Sjsg u64 adj_start, adj_end; 52448cf9467Sjsg u64 col_start, col_end; 52548cf9467Sjsg 52648cf9467Sjsg if (mode == DRM_MM_INSERT_LOW && hole_start >= range_end) 52748cf9467Sjsg break; 52848cf9467Sjsg 52948cf9467Sjsg if (mode == DRM_MM_INSERT_HIGH && hole_end <= range_start) 53048cf9467Sjsg break; 53148cf9467Sjsg 53248cf9467Sjsg col_start = hole_start; 53348cf9467Sjsg col_end = hole_end; 53448cf9467Sjsg if (mm->color_adjust) 53548cf9467Sjsg mm->color_adjust(hole, color, &col_start, &col_end); 53648cf9467Sjsg 53748cf9467Sjsg adj_start = max(col_start, range_start); 53848cf9467Sjsg adj_end = min(col_end, range_end); 53948cf9467Sjsg 54048cf9467Sjsg if (adj_end <= adj_start || adj_end - adj_start < size) 54148cf9467Sjsg continue; 54248cf9467Sjsg 54348cf9467Sjsg if (mode == DRM_MM_INSERT_HIGH) 54448cf9467Sjsg adj_start = adj_end - size; 54548cf9467Sjsg 54648cf9467Sjsg if (alignment) { 54748cf9467Sjsg u64 rem; 54848cf9467Sjsg 54948cf9467Sjsg if (likely(remainder_mask)) 55048cf9467Sjsg rem = adj_start & remainder_mask; 55148cf9467Sjsg else 55248cf9467Sjsg div64_u64_rem(adj_start, alignment, &rem); 55348cf9467Sjsg if (rem) { 55448cf9467Sjsg adj_start -= rem; 55548cf9467Sjsg if (mode != DRM_MM_INSERT_HIGH) 55648cf9467Sjsg adj_start += alignment; 55748cf9467Sjsg 55848cf9467Sjsg if (adj_start < max(col_start, range_start) || 55948cf9467Sjsg min(col_end, range_end) - adj_start < size) 56048cf9467Sjsg continue; 56148cf9467Sjsg 56248cf9467Sjsg if (adj_end <= adj_start || 56348cf9467Sjsg adj_end - adj_start < size) 56448cf9467Sjsg continue; 56548cf9467Sjsg } 56648cf9467Sjsg } 56748cf9467Sjsg 56848cf9467Sjsg node->mm = mm; 56948cf9467Sjsg node->size = size; 57048cf9467Sjsg node->start = adj_start; 57148cf9467Sjsg node->color = color; 57248cf9467Sjsg node->hole_size = 0; 57348cf9467Sjsg 57448cf9467Sjsg __set_bit(DRM_MM_NODE_ALLOCATED_BIT, &node->flags); 57548cf9467Sjsg list_add(&node->node_list, &hole->node_list); 57648cf9467Sjsg drm_mm_interval_tree_add_node(hole, node); 57748cf9467Sjsg 57848cf9467Sjsg rm_hole(hole); 57948cf9467Sjsg if (adj_start > hole_start) 58048cf9467Sjsg add_hole(hole); 58148cf9467Sjsg if (adj_start + size < hole_end) 58248cf9467Sjsg add_hole(node); 58348cf9467Sjsg 58448cf9467Sjsg save_stack(node); 5854e088e8fSjsg return 0; 5864e088e8fSjsg } 58748cf9467Sjsg 58848cf9467Sjsg return -ENOSPC; 58948cf9467Sjsg } 59048cf9467Sjsg EXPORT_SYMBOL(drm_mm_insert_node_in_range); 59148cf9467Sjsg 59248cf9467Sjsg static inline bool drm_mm_node_scanned_block(const struct drm_mm_node *node) 59348cf9467Sjsg { 59448cf9467Sjsg return test_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags); 59548cf9467Sjsg } 596746fbbdbSjsg 5974e088e8fSjsg /** 5983253c27bSkettenis * drm_mm_remove_node - Remove a memory node from the allocator. 5993253c27bSkettenis * @node: drm_mm_node to remove 6003253c27bSkettenis * 6013253c27bSkettenis * This just removes a node from its drm_mm allocator. The node does not need to 6023253c27bSkettenis * be cleared again before it can be re-inserted into this or any other drm_mm 6037f4dd379Sjsg * allocator. It is a bug to call this function on a unallocated node. 6044e088e8fSjsg */ 605746fbbdbSjsg void drm_mm_remove_node(struct drm_mm_node *node) 606746fbbdbSjsg { 607746fbbdbSjsg struct drm_mm *mm = node->mm; 608746fbbdbSjsg struct drm_mm_node *prev_node; 609746fbbdbSjsg 61048cf9467Sjsg DRM_MM_BUG_ON(!drm_mm_node_allocated(node)); 61148cf9467Sjsg DRM_MM_BUG_ON(drm_mm_node_scanned_block(node)); 612746fbbdbSjsg 61348cf9467Sjsg prev_node = list_prev_entry(node, node_list); 614746fbbdbSjsg 61548cf9467Sjsg if (drm_mm_hole_follows(node)) 61648cf9467Sjsg rm_hole(node); 617e1001332Skettenis 6187f4dd379Sjsg drm_mm_interval_tree_remove(node, &mm->interval_tree); 619746fbbdbSjsg list_del(&node->node_list); 62048cf9467Sjsg 62148cf9467Sjsg if (drm_mm_hole_follows(prev_node)) 62248cf9467Sjsg rm_hole(prev_node); 62348cf9467Sjsg add_hole(prev_node); 62448cf9467Sjsg 62548cf9467Sjsg clear_bit_unlock(DRM_MM_NODE_ALLOCATED_BIT, &node->flags); 626746fbbdbSjsg } 6274e088e8fSjsg EXPORT_SYMBOL(drm_mm_remove_node); 628746fbbdbSjsg 6294e088e8fSjsg /** 6303253c27bSkettenis * drm_mm_replace_node - move an allocation from @old to @new 6313253c27bSkettenis * @old: drm_mm_node to remove from the allocator 6323253c27bSkettenis * @new: drm_mm_node which should inherit @old's allocation 6333253c27bSkettenis * 6343253c27bSkettenis * This is useful for when drivers embed the drm_mm_node structure and hence 6353253c27bSkettenis * can't move allocations by reassigning pointers. It's a combination of remove 6363253c27bSkettenis * and insert with the guarantee that the allocation start will match. 6374e088e8fSjsg */ 638746fbbdbSjsg void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) 639746fbbdbSjsg { 64048cf9467Sjsg struct drm_mm *mm = old->mm; 6417f4dd379Sjsg 64248cf9467Sjsg DRM_MM_BUG_ON(!drm_mm_node_allocated(old)); 64348cf9467Sjsg 64448cf9467Sjsg *new = *old; 64548cf9467Sjsg 64648cf9467Sjsg __set_bit(DRM_MM_NODE_ALLOCATED_BIT, &new->flags); 647746fbbdbSjsg list_replace(&old->node_list, &new->node_list); 64848cf9467Sjsg rb_replace_node_cached(&old->rb, &new->rb, &mm->interval_tree); 649746fbbdbSjsg 65048cf9467Sjsg if (drm_mm_hole_follows(old)) { 65148cf9467Sjsg list_replace(&old->hole_stack, &new->hole_stack); 65248cf9467Sjsg rb_replace_node_cached(&old->rb_hole_size, 65348cf9467Sjsg &new->rb_hole_size, 65448cf9467Sjsg &mm->holes_size); 65548cf9467Sjsg rb_replace_node(&old->rb_hole_addr, 65648cf9467Sjsg &new->rb_hole_addr, 65748cf9467Sjsg &mm->holes_addr); 65848cf9467Sjsg } 65948cf9467Sjsg 66048cf9467Sjsg clear_bit_unlock(DRM_MM_NODE_ALLOCATED_BIT, &old->flags); 661746fbbdbSjsg } 6624e088e8fSjsg EXPORT_SYMBOL(drm_mm_replace_node); 663746fbbdbSjsg 6644e088e8fSjsg /** 6657f4dd379Sjsg * DOC: lru scan roster 6663253c27bSkettenis * 6673253c27bSkettenis * Very often GPUs need to have continuous allocations for a given object. When 6683253c27bSkettenis * evicting objects to make space for a new one it is therefore not most 6693253c27bSkettenis * efficient when we simply start to select all objects from the tail of an LRU 6703253c27bSkettenis * until there's a suitable hole: Especially for big objects or nodes that 6713253c27bSkettenis * otherwise have special allocation constraints there's a good chance we evict 6727f4dd379Sjsg * lots of (smaller) objects unnecessarily. 6733253c27bSkettenis * 6743253c27bSkettenis * The DRM range allocator supports this use-case through the scanning 6753253c27bSkettenis * interfaces. First a scan operation needs to be initialized with 6767f4dd379Sjsg * drm_mm_scan_init() or drm_mm_scan_init_with_range(). The driver adds 6777f4dd379Sjsg * objects to the roster, probably by walking an LRU list, but this can be 6787f4dd379Sjsg * freely implemented. Eviction candiates are added using 6797f4dd379Sjsg * drm_mm_scan_add_block() until a suitable hole is found or there are no 6807f4dd379Sjsg * further evictable objects. Eviction roster metadata is tracked in &struct 6817f4dd379Sjsg * drm_mm_scan. 6823253c27bSkettenis * 6837f4dd379Sjsg * The driver must walk through all objects again in exactly the reverse 6843253c27bSkettenis * order to restore the allocator state. Note that while the allocator is used 6853253c27bSkettenis * in the scan mode no other operation is allowed. 6863253c27bSkettenis * 6877f4dd379Sjsg * Finally the driver evicts all objects selected (drm_mm_scan_remove_block() 6887f4dd379Sjsg * reported true) in the scan, and any overlapping nodes after color adjustment 6897f4dd379Sjsg * (drm_mm_scan_color_evict()). Adding and removing an object is O(1), and 6907f4dd379Sjsg * since freeing a node is also O(1) the overall complexity is 6917f4dd379Sjsg * O(scanned_objects). So like the free stack which needs to be walked before a 6927f4dd379Sjsg * scan operation even begins this is linear in the number of objects. It 6937f4dd379Sjsg * doesn't seem to hurt too badly. 6943253c27bSkettenis */ 6953253c27bSkettenis 6963253c27bSkettenis /** 6977f4dd379Sjsg * drm_mm_scan_init_with_range - initialize range-restricted lru scanning 6987f4dd379Sjsg * @scan: scan state 6993253c27bSkettenis * @mm: drm_mm to scan 7003253c27bSkettenis * @size: size of the allocation 7013253c27bSkettenis * @alignment: alignment of the allocation 7023253c27bSkettenis * @color: opaque tag value to use for the allocation 7033253c27bSkettenis * @start: start of the allowed range for the allocation 7043253c27bSkettenis * @end: end of the allowed range for the allocation 70548cf9467Sjsg * @mode: fine-tune the allocation search and placement 7064e088e8fSjsg * 7074e088e8fSjsg * This simply sets up the scanning routines with the parameters for the desired 7087f4dd379Sjsg * hole. 7094e088e8fSjsg * 7103253c27bSkettenis * Warning: 7113253c27bSkettenis * As long as the scan list is non-empty, no other operations than 7124e088e8fSjsg * adding/removing nodes to/from the scan list are allowed. 7134e088e8fSjsg */ 7147f4dd379Sjsg void drm_mm_scan_init_with_range(struct drm_mm_scan *scan, 7157f4dd379Sjsg struct drm_mm *mm, 7163253c27bSkettenis u64 size, 7177f4dd379Sjsg u64 alignment, 7184e088e8fSjsg unsigned long color, 7193253c27bSkettenis u64 start, 7207f4dd379Sjsg u64 end, 72148cf9467Sjsg enum drm_mm_insert_mode mode) 722746fbbdbSjsg { 7237f4dd379Sjsg DRM_MM_BUG_ON(start >= end); 7247f4dd379Sjsg DRM_MM_BUG_ON(!size || size > end - start); 7257f4dd379Sjsg DRM_MM_BUG_ON(mm->scan_active); 7267f4dd379Sjsg 7277f4dd379Sjsg scan->mm = mm; 7287f4dd379Sjsg 7297f4dd379Sjsg if (alignment <= 1) 7307f4dd379Sjsg alignment = 0; 7317f4dd379Sjsg 7327f4dd379Sjsg scan->color = color; 7337f4dd379Sjsg scan->alignment = alignment; 7347f4dd379Sjsg scan->remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0; 7357f4dd379Sjsg scan->size = size; 73648cf9467Sjsg scan->mode = mode; 7377f4dd379Sjsg 7387f4dd379Sjsg DRM_MM_BUG_ON(end <= start); 7397f4dd379Sjsg scan->range_start = start; 7407f4dd379Sjsg scan->range_end = end; 7417f4dd379Sjsg 7427f4dd379Sjsg scan->hit_start = U64_MAX; 7437f4dd379Sjsg scan->hit_end = 0; 744746fbbdbSjsg } 7457f4dd379Sjsg EXPORT_SYMBOL(drm_mm_scan_init_with_range); 746746fbbdbSjsg 7474e088e8fSjsg /** 7483253c27bSkettenis * drm_mm_scan_add_block - add a node to the scan list 7497f4dd379Sjsg * @scan: the active drm_mm scanner 7503253c27bSkettenis * @node: drm_mm_node to add 7513253c27bSkettenis * 7524e088e8fSjsg * Add a node to the scan list that might be freed to make space for the desired 7534e088e8fSjsg * hole. 7544e088e8fSjsg * 7553253c27bSkettenis * Returns: 7563253c27bSkettenis * True if a hole has been found, false otherwise. 7574e088e8fSjsg */ 7587f4dd379Sjsg bool drm_mm_scan_add_block(struct drm_mm_scan *scan, 7597f4dd379Sjsg struct drm_mm_node *node) 760746fbbdbSjsg { 7617f4dd379Sjsg struct drm_mm *mm = scan->mm; 7627f4dd379Sjsg struct drm_mm_node *hole; 7633253c27bSkettenis u64 hole_start, hole_end; 7647f4dd379Sjsg u64 col_start, col_end; 7653253c27bSkettenis u64 adj_start, adj_end; 766746fbbdbSjsg 7677f4dd379Sjsg DRM_MM_BUG_ON(node->mm != mm); 76848cf9467Sjsg DRM_MM_BUG_ON(!drm_mm_node_allocated(node)); 76948cf9467Sjsg DRM_MM_BUG_ON(drm_mm_node_scanned_block(node)); 77048cf9467Sjsg __set_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags); 7717f4dd379Sjsg mm->scan_active++; 772746fbbdbSjsg 7737f4dd379Sjsg /* Remove this block from the node_list so that we enlarge the hole 7747f4dd379Sjsg * (distance between the end of our previous node and the start of 7757f4dd379Sjsg * or next), without poisoning the link so that we can restore it 7767f4dd379Sjsg * later in drm_mm_scan_remove_block(). 7777f4dd379Sjsg */ 7787f4dd379Sjsg hole = list_prev_entry(node, node_list); 7797f4dd379Sjsg DRM_MM_BUG_ON(list_next_entry(hole, node_list) != node); 7807f4dd379Sjsg __list_del_entry(&node->node_list); 781746fbbdbSjsg 7827f4dd379Sjsg hole_start = __drm_mm_hole_node_start(hole); 7837f4dd379Sjsg hole_end = __drm_mm_hole_node_end(hole); 784746fbbdbSjsg 7857f4dd379Sjsg col_start = hole_start; 7867f4dd379Sjsg col_end = hole_end; 7874e088e8fSjsg if (mm->color_adjust) 7887f4dd379Sjsg mm->color_adjust(hole, scan->color, &col_start, &col_end); 7894e088e8fSjsg 7907f4dd379Sjsg adj_start = max(col_start, scan->range_start); 7917f4dd379Sjsg adj_end = min(col_end, scan->range_end); 7927f4dd379Sjsg if (adj_end <= adj_start || adj_end - adj_start < scan->size) 7937f4dd379Sjsg return false; 7947f4dd379Sjsg 79548cf9467Sjsg if (scan->mode == DRM_MM_INSERT_HIGH) 7967f4dd379Sjsg adj_start = adj_end - scan->size; 7977f4dd379Sjsg 7987f4dd379Sjsg if (scan->alignment) { 7997f4dd379Sjsg u64 rem; 8007f4dd379Sjsg 8017f4dd379Sjsg if (likely(scan->remainder_mask)) 8027f4dd379Sjsg rem = adj_start & scan->remainder_mask; 8037f4dd379Sjsg else 8047f4dd379Sjsg div64_u64_rem(adj_start, scan->alignment, &rem); 8057f4dd379Sjsg if (rem) { 8067f4dd379Sjsg adj_start -= rem; 80748cf9467Sjsg if (scan->mode != DRM_MM_INSERT_HIGH) 8087f4dd379Sjsg adj_start += scan->alignment; 8097f4dd379Sjsg if (adj_start < max(col_start, scan->range_start) || 8107f4dd379Sjsg min(col_end, scan->range_end) - adj_start < scan->size) 8117f4dd379Sjsg return false; 8127f4dd379Sjsg 8137f4dd379Sjsg if (adj_end <= adj_start || 8147f4dd379Sjsg adj_end - adj_start < scan->size) 8157f4dd379Sjsg return false; 8167f4dd379Sjsg } 817746fbbdbSjsg } 818746fbbdbSjsg 8197f4dd379Sjsg scan->hit_start = adj_start; 8207f4dd379Sjsg scan->hit_end = adj_start + scan->size; 8217f4dd379Sjsg 8227f4dd379Sjsg DRM_MM_BUG_ON(scan->hit_start >= scan->hit_end); 8237f4dd379Sjsg DRM_MM_BUG_ON(scan->hit_start < hole_start); 8247f4dd379Sjsg DRM_MM_BUG_ON(scan->hit_end > hole_end); 8257f4dd379Sjsg 8267f4dd379Sjsg return true; 827746fbbdbSjsg } 8284e088e8fSjsg EXPORT_SYMBOL(drm_mm_scan_add_block); 829746fbbdbSjsg 8304e088e8fSjsg /** 8313253c27bSkettenis * drm_mm_scan_remove_block - remove a node from the scan list 8327f4dd379Sjsg * @scan: the active drm_mm scanner 8333253c27bSkettenis * @node: drm_mm_node to remove 8344e088e8fSjsg * 8357f4dd379Sjsg * Nodes **must** be removed in exactly the reverse order from the scan list as 8367f4dd379Sjsg * they have been added (e.g. using list_add() as they are added and then 8377f4dd379Sjsg * list_for_each() over that eviction list to remove), otherwise the internal 8387f4dd379Sjsg * state of the memory manager will be corrupted. 8394e088e8fSjsg * 8404e088e8fSjsg * When the scan list is empty, the selected memory nodes can be freed. An 8417f4dd379Sjsg * immediately following drm_mm_insert_node_in_range_generic() or one of the 8427f4dd379Sjsg * simpler versions of that function with !DRM_MM_SEARCH_BEST will then return 84348cf9467Sjsg * the just freed block (because it's at the top of the free_stack list). 8444e088e8fSjsg * 8453253c27bSkettenis * Returns: 8463253c27bSkettenis * True if this block should be evicted, false otherwise. Will always 8473253c27bSkettenis * return false when no hole has been found. 8484e088e8fSjsg */ 8497f4dd379Sjsg bool drm_mm_scan_remove_block(struct drm_mm_scan *scan, 8507f4dd379Sjsg struct drm_mm_node *node) 851746fbbdbSjsg { 852746fbbdbSjsg struct drm_mm_node *prev_node; 853746fbbdbSjsg 8547f4dd379Sjsg DRM_MM_BUG_ON(node->mm != scan->mm); 85548cf9467Sjsg DRM_MM_BUG_ON(!drm_mm_node_scanned_block(node)); 85648cf9467Sjsg __clear_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags); 857746fbbdbSjsg 8587f4dd379Sjsg DRM_MM_BUG_ON(!node->mm->scan_active); 8597f4dd379Sjsg node->mm->scan_active--; 860746fbbdbSjsg 8617f4dd379Sjsg /* During drm_mm_scan_add_block() we decoupled this node leaving 8627f4dd379Sjsg * its pointers intact. Now that the caller is walking back along 8637f4dd379Sjsg * the eviction list we can restore this block into its rightful 8647f4dd379Sjsg * place on the full node_list. To confirm that the caller is walking 8657f4dd379Sjsg * backwards correctly we check that prev_node->next == node->next, 8667f4dd379Sjsg * i.e. both believe the same node should be on the other side of the 8677f4dd379Sjsg * hole. 8687f4dd379Sjsg */ 8697f4dd379Sjsg prev_node = list_prev_entry(node, node_list); 8707f4dd379Sjsg DRM_MM_BUG_ON(list_next_entry(prev_node, node_list) != 8717f4dd379Sjsg list_next_entry(node, node_list)); 872746fbbdbSjsg list_add(&node->node_list, &prev_node->node_list); 873746fbbdbSjsg 8747f4dd379Sjsg return (node->start + node->size > scan->hit_start && 8757f4dd379Sjsg node->start < scan->hit_end); 876746fbbdbSjsg } 8774e088e8fSjsg EXPORT_SYMBOL(drm_mm_scan_remove_block); 878746fbbdbSjsg 8793253c27bSkettenis /** 8807f4dd379Sjsg * drm_mm_scan_color_evict - evict overlapping nodes on either side of hole 8817f4dd379Sjsg * @scan: drm_mm scan with target hole 8827f4dd379Sjsg * 8837f4dd379Sjsg * After completing an eviction scan and removing the selected nodes, we may 8847f4dd379Sjsg * need to remove a few more nodes from either side of the target hole if 8857f4dd379Sjsg * mm.color_adjust is being used. 8863253c27bSkettenis * 8873253c27bSkettenis * Returns: 8887f4dd379Sjsg * A node to evict, or NULL if there are no overlapping nodes. 8893253c27bSkettenis */ 8907f4dd379Sjsg struct drm_mm_node *drm_mm_scan_color_evict(struct drm_mm_scan *scan) 891746fbbdbSjsg { 8927f4dd379Sjsg struct drm_mm *mm = scan->mm; 8937f4dd379Sjsg struct drm_mm_node *hole; 8947f4dd379Sjsg u64 hole_start, hole_end; 895746fbbdbSjsg 8967f4dd379Sjsg DRM_MM_BUG_ON(list_empty(&mm->hole_stack)); 8977f4dd379Sjsg 8987f4dd379Sjsg if (!mm->color_adjust) 8997f4dd379Sjsg return NULL; 9007f4dd379Sjsg 90148cf9467Sjsg /* 90248cf9467Sjsg * The hole found during scanning should ideally be the first element 90348cf9467Sjsg * in the hole_stack list, but due to side-effects in the driver it 90448cf9467Sjsg * may not be. 90548cf9467Sjsg */ 90648cf9467Sjsg list_for_each_entry(hole, &mm->hole_stack, hole_stack) { 9077f4dd379Sjsg hole_start = __drm_mm_hole_node_start(hole); 90848cf9467Sjsg hole_end = hole_start + hole->hole_size; 90948cf9467Sjsg 91048cf9467Sjsg if (hole_start <= scan->hit_start && 91148cf9467Sjsg hole_end >= scan->hit_end) 91248cf9467Sjsg break; 91348cf9467Sjsg } 91448cf9467Sjsg 91548cf9467Sjsg /* We should only be called after we found the hole previously */ 91648cf9467Sjsg DRM_MM_BUG_ON(&hole->hole_stack == &mm->hole_stack); 91748cf9467Sjsg if (unlikely(&hole->hole_stack == &mm->hole_stack)) 91848cf9467Sjsg return NULL; 9197f4dd379Sjsg 9207f4dd379Sjsg DRM_MM_BUG_ON(hole_start > scan->hit_start); 9217f4dd379Sjsg DRM_MM_BUG_ON(hole_end < scan->hit_end); 9227f4dd379Sjsg 9237f4dd379Sjsg mm->color_adjust(hole, scan->color, &hole_start, &hole_end); 9247f4dd379Sjsg if (hole_start > scan->hit_start) 9257f4dd379Sjsg return hole; 9267f4dd379Sjsg if (hole_end < scan->hit_end) 9277f4dd379Sjsg return list_next_entry(hole, node_list); 9287f4dd379Sjsg 9297f4dd379Sjsg return NULL; 930746fbbdbSjsg } 9317f4dd379Sjsg EXPORT_SYMBOL(drm_mm_scan_color_evict); 932746fbbdbSjsg 9333253c27bSkettenis /** 9343253c27bSkettenis * drm_mm_init - initialize a drm-mm allocator 9353253c27bSkettenis * @mm: the drm_mm structure to initialize 9363253c27bSkettenis * @start: start of the range managed by @mm 9373253c27bSkettenis * @size: end of the range managed by @mm 9383253c27bSkettenis * 9393253c27bSkettenis * Note that @mm must be cleared to 0 before calling this function. 9403253c27bSkettenis */ 9413253c27bSkettenis void drm_mm_init(struct drm_mm *mm, u64 start, u64 size) 942746fbbdbSjsg { 9437f4dd379Sjsg DRM_MM_BUG_ON(start + size <= start); 9447f4dd379Sjsg 94548cf9467Sjsg mm->color_adjust = NULL; 94648cf9467Sjsg 947746fbbdbSjsg INIT_LIST_HEAD(&mm->hole_stack); 94848cf9467Sjsg mm->interval_tree = RB_ROOT_CACHED; 94948cf9467Sjsg mm->holes_size = RB_ROOT_CACHED; 95048cf9467Sjsg mm->holes_addr = RB_ROOT; 951746fbbdbSjsg 9524e088e8fSjsg /* Clever trick to avoid a special case in the free hole tracking. */ 953746fbbdbSjsg INIT_LIST_HEAD(&mm->head_node.node_list); 95448cf9467Sjsg mm->head_node.flags = 0; 955746fbbdbSjsg mm->head_node.mm = mm; 956746fbbdbSjsg mm->head_node.start = start + size; 95748cf9467Sjsg mm->head_node.size = -size; 95848cf9467Sjsg add_hole(&mm->head_node); 959746fbbdbSjsg 96048cf9467Sjsg mm->scan_active = 0; 961746fbbdbSjsg } 9624e088e8fSjsg EXPORT_SYMBOL(drm_mm_init); 963746fbbdbSjsg 9643253c27bSkettenis /** 9653253c27bSkettenis * drm_mm_takedown - clean up a drm_mm allocator 9663253c27bSkettenis * @mm: drm_mm allocator to clean up 9673253c27bSkettenis * 9683253c27bSkettenis * Note that it is a bug to call this function on an allocator which is not 9693253c27bSkettenis * clean. 9703253c27bSkettenis */ 971746fbbdbSjsg void drm_mm_takedown(struct drm_mm *mm) 972746fbbdbSjsg { 9737f4dd379Sjsg if (WARN(!drm_mm_clean(mm), 9747f4dd379Sjsg "Memory manager not clean during takedown.\n")) 9757f4dd379Sjsg show_leaks(mm); 976746fbbdbSjsg } 9774e088e8fSjsg EXPORT_SYMBOL(drm_mm_takedown); 9784e088e8fSjsg 9797f4dd379Sjsg static u64 drm_mm_dump_hole(struct drm_printer *p, const struct drm_mm_node *entry) 9804e088e8fSjsg { 98148cf9467Sjsg u64 start, size; 9824e088e8fSjsg 98348cf9467Sjsg size = entry->hole_size; 98448cf9467Sjsg if (size) { 98548cf9467Sjsg start = drm_mm_hole_node_start(entry); 98648cf9467Sjsg drm_printf(p, "%#018llx-%#018llx: %llu: free\n", 98748cf9467Sjsg start, start + size, size); 9884e088e8fSjsg } 989e1001332Skettenis 99048cf9467Sjsg return size; 991e1001332Skettenis } 9923253c27bSkettenis /** 9937f4dd379Sjsg * drm_mm_print - print allocator state 9947f4dd379Sjsg * @mm: drm_mm allocator to print 9957f4dd379Sjsg * @p: DRM printer to use 9963253c27bSkettenis */ 9977f4dd379Sjsg void drm_mm_print(const struct drm_mm *mm, struct drm_printer *p) 998e1001332Skettenis { 9997f4dd379Sjsg const struct drm_mm_node *entry; 10003253c27bSkettenis u64 total_used = 0, total_free = 0, total = 0; 1001e1001332Skettenis 10027f4dd379Sjsg total_free += drm_mm_dump_hole(p, &mm->head_node); 1003e1001332Skettenis 1004e1001332Skettenis drm_mm_for_each_node(entry, mm) { 10057f4dd379Sjsg drm_printf(p, "%#018llx-%#018llx: %llu: used\n", entry->start, 10063253c27bSkettenis entry->start + entry->size, entry->size); 1007e1001332Skettenis total_used += entry->size; 10087f4dd379Sjsg total_free += drm_mm_dump_hole(p, entry); 10094e088e8fSjsg } 10104e088e8fSjsg total = total_free + total_used; 10114e088e8fSjsg 10127f4dd379Sjsg drm_printf(p, "total: %llu, used %llu free %llu\n", total, 10134e088e8fSjsg total_used, total_free); 10144e088e8fSjsg } 10157f4dd379Sjsg EXPORT_SYMBOL(drm_mm_print); 1016