162306a36Sopenharmony_ci/************************************************************************** 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. 462306a36Sopenharmony_ci * Copyright 2016 Intel Corporation 562306a36Sopenharmony_ci * All Rights Reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 862306a36Sopenharmony_ci * copy of this software and associated documentation files (the 962306a36Sopenharmony_ci * "Software"), to deal in the Software without restriction, including 1062306a36Sopenharmony_ci * without limitation the rights to use, copy, modify, merge, publish, 1162306a36Sopenharmony_ci * distribute, sub license, and/or sell copies of the Software, and to 1262306a36Sopenharmony_ci * permit persons to whom the Software is furnished to do so, subject to 1362306a36Sopenharmony_ci * the following conditions: 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the 1662306a36Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions 1762306a36Sopenharmony_ci * of the Software. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 2062306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 2162306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 2262306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 2362306a36Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 2462306a36Sopenharmony_ci * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 2562306a36Sopenharmony_ci * USE OR OTHER DEALINGS IN THE SOFTWARE. 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * 2862306a36Sopenharmony_ci **************************************************************************/ 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * Generic simple memory manager implementation. Intended to be used as a base 3262306a36Sopenharmony_ci * class implementation for more advanced memory managers. 3362306a36Sopenharmony_ci * 3462306a36Sopenharmony_ci * Note that the algorithm used is quite simple and there might be substantial 3562306a36Sopenharmony_ci * performance gains if a smarter free list is implemented. Currently it is 3662306a36Sopenharmony_ci * just an unordered stack of free regions. This could easily be improved if 3762306a36Sopenharmony_ci * an RB-tree is used instead. At least if we expect heavy fragmentation. 3862306a36Sopenharmony_ci * 3962306a36Sopenharmony_ci * Aligned allocations can also see improvement. 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * Authors: 4262306a36Sopenharmony_ci * Thomas Hellström <thomas-at-tungstengraphics-dot-com> 4362306a36Sopenharmony_ci */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#include <linux/export.h> 4662306a36Sopenharmony_ci#include <linux/interval_tree_generic.h> 4762306a36Sopenharmony_ci#include <linux/seq_file.h> 4862306a36Sopenharmony_ci#include <linux/slab.h> 4962306a36Sopenharmony_ci#include <linux/stacktrace.h> 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#include <drm/drm_mm.h> 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/** 5462306a36Sopenharmony_ci * DOC: Overview 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * drm_mm provides a simple range allocator. The drivers are free to use the 5762306a36Sopenharmony_ci * resource allocator from the linux core if it suits them, the upside of drm_mm 5862306a36Sopenharmony_ci * is that it's in the DRM core. Which means that it's easier to extend for 5962306a36Sopenharmony_ci * some of the crazier special purpose needs of gpus. 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * The main data struct is &drm_mm, allocations are tracked in &drm_mm_node. 6262306a36Sopenharmony_ci * Drivers are free to embed either of them into their own suitable 6362306a36Sopenharmony_ci * datastructures. drm_mm itself will not do any memory allocations of its own, 6462306a36Sopenharmony_ci * so if drivers choose not to embed nodes they need to still allocate them 6562306a36Sopenharmony_ci * themselves. 6662306a36Sopenharmony_ci * 6762306a36Sopenharmony_ci * The range allocator also supports reservation of preallocated blocks. This is 6862306a36Sopenharmony_ci * useful for taking over initial mode setting configurations from the firmware, 6962306a36Sopenharmony_ci * where an object needs to be created which exactly matches the firmware's 7062306a36Sopenharmony_ci * scanout target. As long as the range is still free it can be inserted anytime 7162306a36Sopenharmony_ci * after the allocator is initialized, which helps with avoiding looped 7262306a36Sopenharmony_ci * dependencies in the driver load sequence. 7362306a36Sopenharmony_ci * 7462306a36Sopenharmony_ci * drm_mm maintains a stack of most recently freed holes, which of all 7562306a36Sopenharmony_ci * simplistic datastructures seems to be a fairly decent approach to clustering 7662306a36Sopenharmony_ci * allocations and avoiding too much fragmentation. This means free space 7762306a36Sopenharmony_ci * searches are O(num_holes). Given that all the fancy features drm_mm supports 7862306a36Sopenharmony_ci * something better would be fairly complex and since gfx thrashing is a fairly 7962306a36Sopenharmony_ci * steep cliff not a real concern. Removing a node again is O(1). 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci * drm_mm supports a few features: Alignment and range restrictions can be 8262306a36Sopenharmony_ci * supplied. Furthermore every &drm_mm_node has a color value (which is just an 8362306a36Sopenharmony_ci * opaque unsigned long) which in conjunction with a driver callback can be used 8462306a36Sopenharmony_ci * to implement sophisticated placement restrictions. The i915 DRM driver uses 8562306a36Sopenharmony_ci * this to implement guard pages between incompatible caching domains in the 8662306a36Sopenharmony_ci * graphics TT. 8762306a36Sopenharmony_ci * 8862306a36Sopenharmony_ci * Two behaviors are supported for searching and allocating: bottom-up and 8962306a36Sopenharmony_ci * top-down. The default is bottom-up. Top-down allocation can be used if the 9062306a36Sopenharmony_ci * memory area has different restrictions, or just to reduce fragmentation. 9162306a36Sopenharmony_ci * 9262306a36Sopenharmony_ci * Finally iteration helpers to walk all nodes and all holes are provided as are 9362306a36Sopenharmony_ci * some basic allocator dumpers for debugging. 9462306a36Sopenharmony_ci * 9562306a36Sopenharmony_ci * Note that this range allocator is not thread-safe, drivers need to protect 9662306a36Sopenharmony_ci * modifications with their own locking. The idea behind this is that for a full 9762306a36Sopenharmony_ci * memory manager additional data needs to be protected anyway, hence internal 9862306a36Sopenharmony_ci * locking would be fully redundant. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#ifdef CONFIG_DRM_DEBUG_MM 10262306a36Sopenharmony_ci#include <linux/stackdepot.h> 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define STACKDEPTH 32 10562306a36Sopenharmony_ci#define BUFSZ 4096 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic noinline void save_stack(struct drm_mm_node *node) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci unsigned long entries[STACKDEPTH]; 11062306a36Sopenharmony_ci unsigned int n; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci n = stack_trace_save(entries, ARRAY_SIZE(entries), 1); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* May be called under spinlock, so avoid sleeping */ 11562306a36Sopenharmony_ci node->stack = stack_depot_save(entries, n, GFP_NOWAIT); 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic void show_leaks(struct drm_mm *mm) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct drm_mm_node *node; 12162306a36Sopenharmony_ci char *buf; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci buf = kmalloc(BUFSZ, GFP_KERNEL); 12462306a36Sopenharmony_ci if (!buf) 12562306a36Sopenharmony_ci return; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci list_for_each_entry(node, drm_mm_nodes(mm), node_list) { 12862306a36Sopenharmony_ci if (!node->stack) { 12962306a36Sopenharmony_ci DRM_ERROR("node [%08llx + %08llx]: unknown owner\n", 13062306a36Sopenharmony_ci node->start, node->size); 13162306a36Sopenharmony_ci continue; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci stack_depot_snprint(node->stack, buf, BUFSZ, 0); 13562306a36Sopenharmony_ci DRM_ERROR("node [%08llx + %08llx]: inserted at\n%s", 13662306a36Sopenharmony_ci node->start, node->size, buf); 13762306a36Sopenharmony_ci } 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci kfree(buf); 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci#undef STACKDEPTH 14362306a36Sopenharmony_ci#undef BUFSZ 14462306a36Sopenharmony_ci#else 14562306a36Sopenharmony_cistatic void save_stack(struct drm_mm_node *node) { } 14662306a36Sopenharmony_cistatic void show_leaks(struct drm_mm *mm) { } 14762306a36Sopenharmony_ci#endif 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci#define START(node) ((node)->start) 15062306a36Sopenharmony_ci#define LAST(node) ((node)->start + (node)->size - 1) 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ciINTERVAL_TREE_DEFINE(struct drm_mm_node, rb, 15362306a36Sopenharmony_ci u64, __subtree_last, 15462306a36Sopenharmony_ci START, LAST, static inline, drm_mm_interval_tree) 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_cistruct drm_mm_node * 15762306a36Sopenharmony_ci__drm_mm_interval_first(const struct drm_mm *mm, u64 start, u64 last) 15862306a36Sopenharmony_ci{ 15962306a36Sopenharmony_ci return drm_mm_interval_tree_iter_first((struct rb_root_cached *)&mm->interval_tree, 16062306a36Sopenharmony_ci start, last) ?: (struct drm_mm_node *)&mm->head_node; 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ciEXPORT_SYMBOL(__drm_mm_interval_first); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node, 16562306a36Sopenharmony_ci struct drm_mm_node *node) 16662306a36Sopenharmony_ci{ 16762306a36Sopenharmony_ci struct drm_mm *mm = hole_node->mm; 16862306a36Sopenharmony_ci struct rb_node **link, *rb; 16962306a36Sopenharmony_ci struct drm_mm_node *parent; 17062306a36Sopenharmony_ci bool leftmost; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci node->__subtree_last = LAST(node); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (drm_mm_node_allocated(hole_node)) { 17562306a36Sopenharmony_ci rb = &hole_node->rb; 17662306a36Sopenharmony_ci while (rb) { 17762306a36Sopenharmony_ci parent = rb_entry(rb, struct drm_mm_node, rb); 17862306a36Sopenharmony_ci if (parent->__subtree_last >= node->__subtree_last) 17962306a36Sopenharmony_ci break; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci parent->__subtree_last = node->__subtree_last; 18262306a36Sopenharmony_ci rb = rb_parent(rb); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci rb = &hole_node->rb; 18662306a36Sopenharmony_ci link = &hole_node->rb.rb_right; 18762306a36Sopenharmony_ci leftmost = false; 18862306a36Sopenharmony_ci } else { 18962306a36Sopenharmony_ci rb = NULL; 19062306a36Sopenharmony_ci link = &mm->interval_tree.rb_root.rb_node; 19162306a36Sopenharmony_ci leftmost = true; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci while (*link) { 19562306a36Sopenharmony_ci rb = *link; 19662306a36Sopenharmony_ci parent = rb_entry(rb, struct drm_mm_node, rb); 19762306a36Sopenharmony_ci if (parent->__subtree_last < node->__subtree_last) 19862306a36Sopenharmony_ci parent->__subtree_last = node->__subtree_last; 19962306a36Sopenharmony_ci if (node->start < parent->start) { 20062306a36Sopenharmony_ci link = &parent->rb.rb_left; 20162306a36Sopenharmony_ci } else { 20262306a36Sopenharmony_ci link = &parent->rb.rb_right; 20362306a36Sopenharmony_ci leftmost = false; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci rb_link_node(&node->rb, rb, link); 20862306a36Sopenharmony_ci rb_insert_augmented_cached(&node->rb, &mm->interval_tree, leftmost, 20962306a36Sopenharmony_ci &drm_mm_interval_tree_augment); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci#define HOLE_SIZE(NODE) ((NODE)->hole_size) 21362306a36Sopenharmony_ci#define HOLE_ADDR(NODE) (__drm_mm_hole_node_start(NODE)) 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_cistatic u64 rb_to_hole_size(struct rb_node *rb) 21662306a36Sopenharmony_ci{ 21762306a36Sopenharmony_ci return rb_entry(rb, struct drm_mm_node, rb_hole_size)->hole_size; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void insert_hole_size(struct rb_root_cached *root, 22162306a36Sopenharmony_ci struct drm_mm_node *node) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct rb_node **link = &root->rb_root.rb_node, *rb = NULL; 22462306a36Sopenharmony_ci u64 x = node->hole_size; 22562306a36Sopenharmony_ci bool first = true; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci while (*link) { 22862306a36Sopenharmony_ci rb = *link; 22962306a36Sopenharmony_ci if (x > rb_to_hole_size(rb)) { 23062306a36Sopenharmony_ci link = &rb->rb_left; 23162306a36Sopenharmony_ci } else { 23262306a36Sopenharmony_ci link = &rb->rb_right; 23362306a36Sopenharmony_ci first = false; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci rb_link_node(&node->rb_hole_size, rb, link); 23862306a36Sopenharmony_ci rb_insert_color_cached(&node->rb_hole_size, root, first); 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ciRB_DECLARE_CALLBACKS_MAX(static, augment_callbacks, 24262306a36Sopenharmony_ci struct drm_mm_node, rb_hole_addr, 24362306a36Sopenharmony_ci u64, subtree_max_hole, HOLE_SIZE) 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_cistatic void insert_hole_addr(struct rb_root *root, struct drm_mm_node *node) 24662306a36Sopenharmony_ci{ 24762306a36Sopenharmony_ci struct rb_node **link = &root->rb_node, *rb_parent = NULL; 24862306a36Sopenharmony_ci u64 start = HOLE_ADDR(node), subtree_max_hole = node->subtree_max_hole; 24962306a36Sopenharmony_ci struct drm_mm_node *parent; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci while (*link) { 25262306a36Sopenharmony_ci rb_parent = *link; 25362306a36Sopenharmony_ci parent = rb_entry(rb_parent, struct drm_mm_node, rb_hole_addr); 25462306a36Sopenharmony_ci if (parent->subtree_max_hole < subtree_max_hole) 25562306a36Sopenharmony_ci parent->subtree_max_hole = subtree_max_hole; 25662306a36Sopenharmony_ci if (start < HOLE_ADDR(parent)) 25762306a36Sopenharmony_ci link = &parent->rb_hole_addr.rb_left; 25862306a36Sopenharmony_ci else 25962306a36Sopenharmony_ci link = &parent->rb_hole_addr.rb_right; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci rb_link_node(&node->rb_hole_addr, rb_parent, link); 26362306a36Sopenharmony_ci rb_insert_augmented(&node->rb_hole_addr, root, &augment_callbacks); 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic void add_hole(struct drm_mm_node *node) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct drm_mm *mm = node->mm; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci node->hole_size = 27162306a36Sopenharmony_ci __drm_mm_hole_node_end(node) - __drm_mm_hole_node_start(node); 27262306a36Sopenharmony_ci node->subtree_max_hole = node->hole_size; 27362306a36Sopenharmony_ci DRM_MM_BUG_ON(!drm_mm_hole_follows(node)); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci insert_hole_size(&mm->holes_size, node); 27662306a36Sopenharmony_ci insert_hole_addr(&mm->holes_addr, node); 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci list_add(&node->hole_stack, &mm->hole_stack); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic void rm_hole(struct drm_mm_node *node) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci DRM_MM_BUG_ON(!drm_mm_hole_follows(node)); 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci list_del(&node->hole_stack); 28662306a36Sopenharmony_ci rb_erase_cached(&node->rb_hole_size, &node->mm->holes_size); 28762306a36Sopenharmony_ci rb_erase_augmented(&node->rb_hole_addr, &node->mm->holes_addr, 28862306a36Sopenharmony_ci &augment_callbacks); 28962306a36Sopenharmony_ci node->hole_size = 0; 29062306a36Sopenharmony_ci node->subtree_max_hole = 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci DRM_MM_BUG_ON(drm_mm_hole_follows(node)); 29362306a36Sopenharmony_ci} 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_cistatic inline struct drm_mm_node *rb_hole_size_to_node(struct rb_node *rb) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci return rb_entry_safe(rb, struct drm_mm_node, rb_hole_size); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic inline struct drm_mm_node *rb_hole_addr_to_node(struct rb_node *rb) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci return rb_entry_safe(rb, struct drm_mm_node, rb_hole_addr); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic struct drm_mm_node *best_hole(struct drm_mm *mm, u64 size) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct rb_node *rb = mm->holes_size.rb_root.rb_node; 30862306a36Sopenharmony_ci struct drm_mm_node *best = NULL; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci do { 31162306a36Sopenharmony_ci struct drm_mm_node *node = 31262306a36Sopenharmony_ci rb_entry(rb, struct drm_mm_node, rb_hole_size); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (size <= node->hole_size) { 31562306a36Sopenharmony_ci best = node; 31662306a36Sopenharmony_ci rb = rb->rb_right; 31762306a36Sopenharmony_ci } else { 31862306a36Sopenharmony_ci rb = rb->rb_left; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci } while (rb); 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci return best; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic bool usable_hole_addr(struct rb_node *rb, u64 size) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci return rb && rb_hole_addr_to_node(rb)->subtree_max_hole >= size; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic struct drm_mm_node *find_hole_addr(struct drm_mm *mm, u64 addr, u64 size) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct rb_node *rb = mm->holes_addr.rb_node; 33362306a36Sopenharmony_ci struct drm_mm_node *node = NULL; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci while (rb) { 33662306a36Sopenharmony_ci u64 hole_start; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (!usable_hole_addr(rb, size)) 33962306a36Sopenharmony_ci break; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci node = rb_hole_addr_to_node(rb); 34262306a36Sopenharmony_ci hole_start = __drm_mm_hole_node_start(node); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (addr < hole_start) 34562306a36Sopenharmony_ci rb = node->rb_hole_addr.rb_left; 34662306a36Sopenharmony_ci else if (addr > hole_start + node->hole_size) 34762306a36Sopenharmony_ci rb = node->rb_hole_addr.rb_right; 34862306a36Sopenharmony_ci else 34962306a36Sopenharmony_ci break; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return node; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic struct drm_mm_node * 35662306a36Sopenharmony_cifirst_hole(struct drm_mm *mm, 35762306a36Sopenharmony_ci u64 start, u64 end, u64 size, 35862306a36Sopenharmony_ci enum drm_mm_insert_mode mode) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci switch (mode) { 36162306a36Sopenharmony_ci default: 36262306a36Sopenharmony_ci case DRM_MM_INSERT_BEST: 36362306a36Sopenharmony_ci return best_hole(mm, size); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci case DRM_MM_INSERT_LOW: 36662306a36Sopenharmony_ci return find_hole_addr(mm, start, size); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci case DRM_MM_INSERT_HIGH: 36962306a36Sopenharmony_ci return find_hole_addr(mm, end, size); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci case DRM_MM_INSERT_EVICT: 37262306a36Sopenharmony_ci return list_first_entry_or_null(&mm->hole_stack, 37362306a36Sopenharmony_ci struct drm_mm_node, 37462306a36Sopenharmony_ci hole_stack); 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci/** 37962306a36Sopenharmony_ci * DECLARE_NEXT_HOLE_ADDR - macro to declare next hole functions 38062306a36Sopenharmony_ci * @name: name of function to declare 38162306a36Sopenharmony_ci * @first: first rb member to traverse (either rb_left or rb_right). 38262306a36Sopenharmony_ci * @last: last rb member to traverse (either rb_right or rb_left). 38362306a36Sopenharmony_ci * 38462306a36Sopenharmony_ci * This macro declares a function to return the next hole of the addr rb tree. 38562306a36Sopenharmony_ci * While traversing the tree we take the searched size into account and only 38662306a36Sopenharmony_ci * visit branches with potential big enough holes. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci#define DECLARE_NEXT_HOLE_ADDR(name, first, last) \ 39062306a36Sopenharmony_cistatic struct drm_mm_node *name(struct drm_mm_node *entry, u64 size) \ 39162306a36Sopenharmony_ci{ \ 39262306a36Sopenharmony_ci struct rb_node *parent, *node = &entry->rb_hole_addr; \ 39362306a36Sopenharmony_ci \ 39462306a36Sopenharmony_ci if (!entry || RB_EMPTY_NODE(node)) \ 39562306a36Sopenharmony_ci return NULL; \ 39662306a36Sopenharmony_ci \ 39762306a36Sopenharmony_ci if (usable_hole_addr(node->first, size)) { \ 39862306a36Sopenharmony_ci node = node->first; \ 39962306a36Sopenharmony_ci while (usable_hole_addr(node->last, size)) \ 40062306a36Sopenharmony_ci node = node->last; \ 40162306a36Sopenharmony_ci return rb_hole_addr_to_node(node); \ 40262306a36Sopenharmony_ci } \ 40362306a36Sopenharmony_ci \ 40462306a36Sopenharmony_ci while ((parent = rb_parent(node)) && node == parent->first) \ 40562306a36Sopenharmony_ci node = parent; \ 40662306a36Sopenharmony_ci \ 40762306a36Sopenharmony_ci return rb_hole_addr_to_node(parent); \ 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_ciDECLARE_NEXT_HOLE_ADDR(next_hole_high_addr, rb_left, rb_right) 41162306a36Sopenharmony_ciDECLARE_NEXT_HOLE_ADDR(next_hole_low_addr, rb_right, rb_left) 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic struct drm_mm_node * 41462306a36Sopenharmony_cinext_hole(struct drm_mm *mm, 41562306a36Sopenharmony_ci struct drm_mm_node *node, 41662306a36Sopenharmony_ci u64 size, 41762306a36Sopenharmony_ci enum drm_mm_insert_mode mode) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci switch (mode) { 42062306a36Sopenharmony_ci default: 42162306a36Sopenharmony_ci case DRM_MM_INSERT_BEST: 42262306a36Sopenharmony_ci return rb_hole_size_to_node(rb_prev(&node->rb_hole_size)); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci case DRM_MM_INSERT_LOW: 42562306a36Sopenharmony_ci return next_hole_low_addr(node, size); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci case DRM_MM_INSERT_HIGH: 42862306a36Sopenharmony_ci return next_hole_high_addr(node, size); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci case DRM_MM_INSERT_EVICT: 43162306a36Sopenharmony_ci node = list_next_entry(node, hole_stack); 43262306a36Sopenharmony_ci return &node->hole_stack == &mm->hole_stack ? NULL : node; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci/** 43762306a36Sopenharmony_ci * drm_mm_reserve_node - insert an pre-initialized node 43862306a36Sopenharmony_ci * @mm: drm_mm allocator to insert @node into 43962306a36Sopenharmony_ci * @node: drm_mm_node to insert 44062306a36Sopenharmony_ci * 44162306a36Sopenharmony_ci * This functions inserts an already set-up &drm_mm_node into the allocator, 44262306a36Sopenharmony_ci * meaning that start, size and color must be set by the caller. All other 44362306a36Sopenharmony_ci * fields must be cleared to 0. This is useful to initialize the allocator with 44462306a36Sopenharmony_ci * preallocated objects which must be set-up before the range allocator can be 44562306a36Sopenharmony_ci * set-up, e.g. when taking over a firmware framebuffer. 44662306a36Sopenharmony_ci * 44762306a36Sopenharmony_ci * Returns: 44862306a36Sopenharmony_ci * 0 on success, -ENOSPC if there's no hole where @node is. 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_ciint drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct drm_mm_node *hole; 45362306a36Sopenharmony_ci u64 hole_start, hole_end; 45462306a36Sopenharmony_ci u64 adj_start, adj_end; 45562306a36Sopenharmony_ci u64 end; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci end = node->start + node->size; 45862306a36Sopenharmony_ci if (unlikely(end <= node->start)) 45962306a36Sopenharmony_ci return -ENOSPC; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* Find the relevant hole to add our node to */ 46262306a36Sopenharmony_ci hole = find_hole_addr(mm, node->start, 0); 46362306a36Sopenharmony_ci if (!hole) 46462306a36Sopenharmony_ci return -ENOSPC; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci adj_start = hole_start = __drm_mm_hole_node_start(hole); 46762306a36Sopenharmony_ci adj_end = hole_end = hole_start + hole->hole_size; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci if (mm->color_adjust) 47062306a36Sopenharmony_ci mm->color_adjust(hole, node->color, &adj_start, &adj_end); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (adj_start > node->start || adj_end < end) 47362306a36Sopenharmony_ci return -ENOSPC; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci node->mm = mm; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci __set_bit(DRM_MM_NODE_ALLOCATED_BIT, &node->flags); 47862306a36Sopenharmony_ci list_add(&node->node_list, &hole->node_list); 47962306a36Sopenharmony_ci drm_mm_interval_tree_add_node(hole, node); 48062306a36Sopenharmony_ci node->hole_size = 0; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci rm_hole(hole); 48362306a36Sopenharmony_ci if (node->start > hole_start) 48462306a36Sopenharmony_ci add_hole(hole); 48562306a36Sopenharmony_ci if (end < hole_end) 48662306a36Sopenharmony_ci add_hole(node); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci save_stack(node); 48962306a36Sopenharmony_ci return 0; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_mm_reserve_node); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_cistatic u64 rb_to_hole_size_or_zero(struct rb_node *rb) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci return rb ? rb_to_hole_size(rb) : 0; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci/** 49962306a36Sopenharmony_ci * drm_mm_insert_node_in_range - ranged search for space and insert @node 50062306a36Sopenharmony_ci * @mm: drm_mm to allocate from 50162306a36Sopenharmony_ci * @node: preallocate node to insert 50262306a36Sopenharmony_ci * @size: size of the allocation 50362306a36Sopenharmony_ci * @alignment: alignment of the allocation 50462306a36Sopenharmony_ci * @color: opaque tag value to use for this node 50562306a36Sopenharmony_ci * @range_start: start of the allowed range for this node 50662306a36Sopenharmony_ci * @range_end: end of the allowed range for this node 50762306a36Sopenharmony_ci * @mode: fine-tune the allocation search and placement 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * The preallocated @node must be cleared to 0. 51062306a36Sopenharmony_ci * 51162306a36Sopenharmony_ci * Returns: 51262306a36Sopenharmony_ci * 0 on success, -ENOSPC if there's no suitable hole. 51362306a36Sopenharmony_ci */ 51462306a36Sopenharmony_ciint drm_mm_insert_node_in_range(struct drm_mm * const mm, 51562306a36Sopenharmony_ci struct drm_mm_node * const node, 51662306a36Sopenharmony_ci u64 size, u64 alignment, 51762306a36Sopenharmony_ci unsigned long color, 51862306a36Sopenharmony_ci u64 range_start, u64 range_end, 51962306a36Sopenharmony_ci enum drm_mm_insert_mode mode) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct drm_mm_node *hole; 52262306a36Sopenharmony_ci u64 remainder_mask; 52362306a36Sopenharmony_ci bool once; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci DRM_MM_BUG_ON(range_start > range_end); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (unlikely(size == 0 || range_end - range_start < size)) 52862306a36Sopenharmony_ci return -ENOSPC; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (rb_to_hole_size_or_zero(rb_first_cached(&mm->holes_size)) < size) 53162306a36Sopenharmony_ci return -ENOSPC; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci if (alignment <= 1) 53462306a36Sopenharmony_ci alignment = 0; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci once = mode & DRM_MM_INSERT_ONCE; 53762306a36Sopenharmony_ci mode &= ~DRM_MM_INSERT_ONCE; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0; 54062306a36Sopenharmony_ci for (hole = first_hole(mm, range_start, range_end, size, mode); 54162306a36Sopenharmony_ci hole; 54262306a36Sopenharmony_ci hole = once ? NULL : next_hole(mm, hole, size, mode)) { 54362306a36Sopenharmony_ci u64 hole_start = __drm_mm_hole_node_start(hole); 54462306a36Sopenharmony_ci u64 hole_end = hole_start + hole->hole_size; 54562306a36Sopenharmony_ci u64 adj_start, adj_end; 54662306a36Sopenharmony_ci u64 col_start, col_end; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci if (mode == DRM_MM_INSERT_LOW && hole_start >= range_end) 54962306a36Sopenharmony_ci break; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci if (mode == DRM_MM_INSERT_HIGH && hole_end <= range_start) 55262306a36Sopenharmony_ci break; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci col_start = hole_start; 55562306a36Sopenharmony_ci col_end = hole_end; 55662306a36Sopenharmony_ci if (mm->color_adjust) 55762306a36Sopenharmony_ci mm->color_adjust(hole, color, &col_start, &col_end); 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci adj_start = max(col_start, range_start); 56062306a36Sopenharmony_ci adj_end = min(col_end, range_end); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci if (adj_end <= adj_start || adj_end - adj_start < size) 56362306a36Sopenharmony_ci continue; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (mode == DRM_MM_INSERT_HIGH) 56662306a36Sopenharmony_ci adj_start = adj_end - size; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci if (alignment) { 56962306a36Sopenharmony_ci u64 rem; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (likely(remainder_mask)) 57262306a36Sopenharmony_ci rem = adj_start & remainder_mask; 57362306a36Sopenharmony_ci else 57462306a36Sopenharmony_ci div64_u64_rem(adj_start, alignment, &rem); 57562306a36Sopenharmony_ci if (rem) { 57662306a36Sopenharmony_ci adj_start -= rem; 57762306a36Sopenharmony_ci if (mode != DRM_MM_INSERT_HIGH) 57862306a36Sopenharmony_ci adj_start += alignment; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (adj_start < max(col_start, range_start) || 58162306a36Sopenharmony_ci min(col_end, range_end) - adj_start < size) 58262306a36Sopenharmony_ci continue; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci if (adj_end <= adj_start || 58562306a36Sopenharmony_ci adj_end - adj_start < size) 58662306a36Sopenharmony_ci continue; 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci node->mm = mm; 59162306a36Sopenharmony_ci node->size = size; 59262306a36Sopenharmony_ci node->start = adj_start; 59362306a36Sopenharmony_ci node->color = color; 59462306a36Sopenharmony_ci node->hole_size = 0; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci __set_bit(DRM_MM_NODE_ALLOCATED_BIT, &node->flags); 59762306a36Sopenharmony_ci list_add(&node->node_list, &hole->node_list); 59862306a36Sopenharmony_ci drm_mm_interval_tree_add_node(hole, node); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci rm_hole(hole); 60162306a36Sopenharmony_ci if (adj_start > hole_start) 60262306a36Sopenharmony_ci add_hole(hole); 60362306a36Sopenharmony_ci if (adj_start + size < hole_end) 60462306a36Sopenharmony_ci add_hole(node); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci save_stack(node); 60762306a36Sopenharmony_ci return 0; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci return -ENOSPC; 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_mm_insert_node_in_range); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic inline bool drm_mm_node_scanned_block(const struct drm_mm_node *node) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci return test_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags); 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci/** 62062306a36Sopenharmony_ci * drm_mm_remove_node - Remove a memory node from the allocator. 62162306a36Sopenharmony_ci * @node: drm_mm_node to remove 62262306a36Sopenharmony_ci * 62362306a36Sopenharmony_ci * This just removes a node from its drm_mm allocator. The node does not need to 62462306a36Sopenharmony_ci * be cleared again before it can be re-inserted into this or any other drm_mm 62562306a36Sopenharmony_ci * allocator. It is a bug to call this function on a unallocated node. 62662306a36Sopenharmony_ci */ 62762306a36Sopenharmony_civoid drm_mm_remove_node(struct drm_mm_node *node) 62862306a36Sopenharmony_ci{ 62962306a36Sopenharmony_ci struct drm_mm *mm = node->mm; 63062306a36Sopenharmony_ci struct drm_mm_node *prev_node; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci DRM_MM_BUG_ON(!drm_mm_node_allocated(node)); 63362306a36Sopenharmony_ci DRM_MM_BUG_ON(drm_mm_node_scanned_block(node)); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci prev_node = list_prev_entry(node, node_list); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci if (drm_mm_hole_follows(node)) 63862306a36Sopenharmony_ci rm_hole(node); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci drm_mm_interval_tree_remove(node, &mm->interval_tree); 64162306a36Sopenharmony_ci list_del(&node->node_list); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (drm_mm_hole_follows(prev_node)) 64462306a36Sopenharmony_ci rm_hole(prev_node); 64562306a36Sopenharmony_ci add_hole(prev_node); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci clear_bit_unlock(DRM_MM_NODE_ALLOCATED_BIT, &node->flags); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_mm_remove_node); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci/** 65262306a36Sopenharmony_ci * drm_mm_replace_node - move an allocation from @old to @new 65362306a36Sopenharmony_ci * @old: drm_mm_node to remove from the allocator 65462306a36Sopenharmony_ci * @new: drm_mm_node which should inherit @old's allocation 65562306a36Sopenharmony_ci * 65662306a36Sopenharmony_ci * This is useful for when drivers embed the drm_mm_node structure and hence 65762306a36Sopenharmony_ci * can't move allocations by reassigning pointers. It's a combination of remove 65862306a36Sopenharmony_ci * and insert with the guarantee that the allocation start will match. 65962306a36Sopenharmony_ci */ 66062306a36Sopenharmony_civoid drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new) 66162306a36Sopenharmony_ci{ 66262306a36Sopenharmony_ci struct drm_mm *mm = old->mm; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci DRM_MM_BUG_ON(!drm_mm_node_allocated(old)); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci *new = *old; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci __set_bit(DRM_MM_NODE_ALLOCATED_BIT, &new->flags); 66962306a36Sopenharmony_ci list_replace(&old->node_list, &new->node_list); 67062306a36Sopenharmony_ci rb_replace_node_cached(&old->rb, &new->rb, &mm->interval_tree); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci if (drm_mm_hole_follows(old)) { 67362306a36Sopenharmony_ci list_replace(&old->hole_stack, &new->hole_stack); 67462306a36Sopenharmony_ci rb_replace_node_cached(&old->rb_hole_size, 67562306a36Sopenharmony_ci &new->rb_hole_size, 67662306a36Sopenharmony_ci &mm->holes_size); 67762306a36Sopenharmony_ci rb_replace_node(&old->rb_hole_addr, 67862306a36Sopenharmony_ci &new->rb_hole_addr, 67962306a36Sopenharmony_ci &mm->holes_addr); 68062306a36Sopenharmony_ci } 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci clear_bit_unlock(DRM_MM_NODE_ALLOCATED_BIT, &old->flags); 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_mm_replace_node); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci/** 68762306a36Sopenharmony_ci * DOC: lru scan roster 68862306a36Sopenharmony_ci * 68962306a36Sopenharmony_ci * Very often GPUs need to have continuous allocations for a given object. When 69062306a36Sopenharmony_ci * evicting objects to make space for a new one it is therefore not most 69162306a36Sopenharmony_ci * efficient when we simply start to select all objects from the tail of an LRU 69262306a36Sopenharmony_ci * until there's a suitable hole: Especially for big objects or nodes that 69362306a36Sopenharmony_ci * otherwise have special allocation constraints there's a good chance we evict 69462306a36Sopenharmony_ci * lots of (smaller) objects unnecessarily. 69562306a36Sopenharmony_ci * 69662306a36Sopenharmony_ci * The DRM range allocator supports this use-case through the scanning 69762306a36Sopenharmony_ci * interfaces. First a scan operation needs to be initialized with 69862306a36Sopenharmony_ci * drm_mm_scan_init() or drm_mm_scan_init_with_range(). The driver adds 69962306a36Sopenharmony_ci * objects to the roster, probably by walking an LRU list, but this can be 70062306a36Sopenharmony_ci * freely implemented. Eviction candidates are added using 70162306a36Sopenharmony_ci * drm_mm_scan_add_block() until a suitable hole is found or there are no 70262306a36Sopenharmony_ci * further evictable objects. Eviction roster metadata is tracked in &struct 70362306a36Sopenharmony_ci * drm_mm_scan. 70462306a36Sopenharmony_ci * 70562306a36Sopenharmony_ci * The driver must walk through all objects again in exactly the reverse 70662306a36Sopenharmony_ci * order to restore the allocator state. Note that while the allocator is used 70762306a36Sopenharmony_ci * in the scan mode no other operation is allowed. 70862306a36Sopenharmony_ci * 70962306a36Sopenharmony_ci * Finally the driver evicts all objects selected (drm_mm_scan_remove_block() 71062306a36Sopenharmony_ci * reported true) in the scan, and any overlapping nodes after color adjustment 71162306a36Sopenharmony_ci * (drm_mm_scan_color_evict()). Adding and removing an object is O(1), and 71262306a36Sopenharmony_ci * since freeing a node is also O(1) the overall complexity is 71362306a36Sopenharmony_ci * O(scanned_objects). So like the free stack which needs to be walked before a 71462306a36Sopenharmony_ci * scan operation even begins this is linear in the number of objects. It 71562306a36Sopenharmony_ci * doesn't seem to hurt too badly. 71662306a36Sopenharmony_ci */ 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci/** 71962306a36Sopenharmony_ci * drm_mm_scan_init_with_range - initialize range-restricted lru scanning 72062306a36Sopenharmony_ci * @scan: scan state 72162306a36Sopenharmony_ci * @mm: drm_mm to scan 72262306a36Sopenharmony_ci * @size: size of the allocation 72362306a36Sopenharmony_ci * @alignment: alignment of the allocation 72462306a36Sopenharmony_ci * @color: opaque tag value to use for the allocation 72562306a36Sopenharmony_ci * @start: start of the allowed range for the allocation 72662306a36Sopenharmony_ci * @end: end of the allowed range for the allocation 72762306a36Sopenharmony_ci * @mode: fine-tune the allocation search and placement 72862306a36Sopenharmony_ci * 72962306a36Sopenharmony_ci * This simply sets up the scanning routines with the parameters for the desired 73062306a36Sopenharmony_ci * hole. 73162306a36Sopenharmony_ci * 73262306a36Sopenharmony_ci * Warning: 73362306a36Sopenharmony_ci * As long as the scan list is non-empty, no other operations than 73462306a36Sopenharmony_ci * adding/removing nodes to/from the scan list are allowed. 73562306a36Sopenharmony_ci */ 73662306a36Sopenharmony_civoid drm_mm_scan_init_with_range(struct drm_mm_scan *scan, 73762306a36Sopenharmony_ci struct drm_mm *mm, 73862306a36Sopenharmony_ci u64 size, 73962306a36Sopenharmony_ci u64 alignment, 74062306a36Sopenharmony_ci unsigned long color, 74162306a36Sopenharmony_ci u64 start, 74262306a36Sopenharmony_ci u64 end, 74362306a36Sopenharmony_ci enum drm_mm_insert_mode mode) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci DRM_MM_BUG_ON(start >= end); 74662306a36Sopenharmony_ci DRM_MM_BUG_ON(!size || size > end - start); 74762306a36Sopenharmony_ci DRM_MM_BUG_ON(mm->scan_active); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci scan->mm = mm; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci if (alignment <= 1) 75262306a36Sopenharmony_ci alignment = 0; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci scan->color = color; 75562306a36Sopenharmony_ci scan->alignment = alignment; 75662306a36Sopenharmony_ci scan->remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0; 75762306a36Sopenharmony_ci scan->size = size; 75862306a36Sopenharmony_ci scan->mode = mode; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci DRM_MM_BUG_ON(end <= start); 76162306a36Sopenharmony_ci scan->range_start = start; 76262306a36Sopenharmony_ci scan->range_end = end; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci scan->hit_start = U64_MAX; 76562306a36Sopenharmony_ci scan->hit_end = 0; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_mm_scan_init_with_range); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci/** 77062306a36Sopenharmony_ci * drm_mm_scan_add_block - add a node to the scan list 77162306a36Sopenharmony_ci * @scan: the active drm_mm scanner 77262306a36Sopenharmony_ci * @node: drm_mm_node to add 77362306a36Sopenharmony_ci * 77462306a36Sopenharmony_ci * Add a node to the scan list that might be freed to make space for the desired 77562306a36Sopenharmony_ci * hole. 77662306a36Sopenharmony_ci * 77762306a36Sopenharmony_ci * Returns: 77862306a36Sopenharmony_ci * True if a hole has been found, false otherwise. 77962306a36Sopenharmony_ci */ 78062306a36Sopenharmony_cibool drm_mm_scan_add_block(struct drm_mm_scan *scan, 78162306a36Sopenharmony_ci struct drm_mm_node *node) 78262306a36Sopenharmony_ci{ 78362306a36Sopenharmony_ci struct drm_mm *mm = scan->mm; 78462306a36Sopenharmony_ci struct drm_mm_node *hole; 78562306a36Sopenharmony_ci u64 hole_start, hole_end; 78662306a36Sopenharmony_ci u64 col_start, col_end; 78762306a36Sopenharmony_ci u64 adj_start, adj_end; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci DRM_MM_BUG_ON(node->mm != mm); 79062306a36Sopenharmony_ci DRM_MM_BUG_ON(!drm_mm_node_allocated(node)); 79162306a36Sopenharmony_ci DRM_MM_BUG_ON(drm_mm_node_scanned_block(node)); 79262306a36Sopenharmony_ci __set_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags); 79362306a36Sopenharmony_ci mm->scan_active++; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci /* Remove this block from the node_list so that we enlarge the hole 79662306a36Sopenharmony_ci * (distance between the end of our previous node and the start of 79762306a36Sopenharmony_ci * or next), without poisoning the link so that we can restore it 79862306a36Sopenharmony_ci * later in drm_mm_scan_remove_block(). 79962306a36Sopenharmony_ci */ 80062306a36Sopenharmony_ci hole = list_prev_entry(node, node_list); 80162306a36Sopenharmony_ci DRM_MM_BUG_ON(list_next_entry(hole, node_list) != node); 80262306a36Sopenharmony_ci __list_del_entry(&node->node_list); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci hole_start = __drm_mm_hole_node_start(hole); 80562306a36Sopenharmony_ci hole_end = __drm_mm_hole_node_end(hole); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci col_start = hole_start; 80862306a36Sopenharmony_ci col_end = hole_end; 80962306a36Sopenharmony_ci if (mm->color_adjust) 81062306a36Sopenharmony_ci mm->color_adjust(hole, scan->color, &col_start, &col_end); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci adj_start = max(col_start, scan->range_start); 81362306a36Sopenharmony_ci adj_end = min(col_end, scan->range_end); 81462306a36Sopenharmony_ci if (adj_end <= adj_start || adj_end - adj_start < scan->size) 81562306a36Sopenharmony_ci return false; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci if (scan->mode == DRM_MM_INSERT_HIGH) 81862306a36Sopenharmony_ci adj_start = adj_end - scan->size; 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci if (scan->alignment) { 82162306a36Sopenharmony_ci u64 rem; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (likely(scan->remainder_mask)) 82462306a36Sopenharmony_ci rem = adj_start & scan->remainder_mask; 82562306a36Sopenharmony_ci else 82662306a36Sopenharmony_ci div64_u64_rem(adj_start, scan->alignment, &rem); 82762306a36Sopenharmony_ci if (rem) { 82862306a36Sopenharmony_ci adj_start -= rem; 82962306a36Sopenharmony_ci if (scan->mode != DRM_MM_INSERT_HIGH) 83062306a36Sopenharmony_ci adj_start += scan->alignment; 83162306a36Sopenharmony_ci if (adj_start < max(col_start, scan->range_start) || 83262306a36Sopenharmony_ci min(col_end, scan->range_end) - adj_start < scan->size) 83362306a36Sopenharmony_ci return false; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (adj_end <= adj_start || 83662306a36Sopenharmony_ci adj_end - adj_start < scan->size) 83762306a36Sopenharmony_ci return false; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci } 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci scan->hit_start = adj_start; 84262306a36Sopenharmony_ci scan->hit_end = adj_start + scan->size; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci DRM_MM_BUG_ON(scan->hit_start >= scan->hit_end); 84562306a36Sopenharmony_ci DRM_MM_BUG_ON(scan->hit_start < hole_start); 84662306a36Sopenharmony_ci DRM_MM_BUG_ON(scan->hit_end > hole_end); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci return true; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_mm_scan_add_block); 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci/** 85362306a36Sopenharmony_ci * drm_mm_scan_remove_block - remove a node from the scan list 85462306a36Sopenharmony_ci * @scan: the active drm_mm scanner 85562306a36Sopenharmony_ci * @node: drm_mm_node to remove 85662306a36Sopenharmony_ci * 85762306a36Sopenharmony_ci * Nodes **must** be removed in exactly the reverse order from the scan list as 85862306a36Sopenharmony_ci * they have been added (e.g. using list_add() as they are added and then 85962306a36Sopenharmony_ci * list_for_each() over that eviction list to remove), otherwise the internal 86062306a36Sopenharmony_ci * state of the memory manager will be corrupted. 86162306a36Sopenharmony_ci * 86262306a36Sopenharmony_ci * When the scan list is empty, the selected memory nodes can be freed. An 86362306a36Sopenharmony_ci * immediately following drm_mm_insert_node_in_range_generic() or one of the 86462306a36Sopenharmony_ci * simpler versions of that function with !DRM_MM_SEARCH_BEST will then return 86562306a36Sopenharmony_ci * the just freed block (because it's at the top of the free_stack list). 86662306a36Sopenharmony_ci * 86762306a36Sopenharmony_ci * Returns: 86862306a36Sopenharmony_ci * True if this block should be evicted, false otherwise. Will always 86962306a36Sopenharmony_ci * return false when no hole has been found. 87062306a36Sopenharmony_ci */ 87162306a36Sopenharmony_cibool drm_mm_scan_remove_block(struct drm_mm_scan *scan, 87262306a36Sopenharmony_ci struct drm_mm_node *node) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci struct drm_mm_node *prev_node; 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci DRM_MM_BUG_ON(node->mm != scan->mm); 87762306a36Sopenharmony_ci DRM_MM_BUG_ON(!drm_mm_node_scanned_block(node)); 87862306a36Sopenharmony_ci __clear_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci DRM_MM_BUG_ON(!node->mm->scan_active); 88162306a36Sopenharmony_ci node->mm->scan_active--; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci /* During drm_mm_scan_add_block() we decoupled this node leaving 88462306a36Sopenharmony_ci * its pointers intact. Now that the caller is walking back along 88562306a36Sopenharmony_ci * the eviction list we can restore this block into its rightful 88662306a36Sopenharmony_ci * place on the full node_list. To confirm that the caller is walking 88762306a36Sopenharmony_ci * backwards correctly we check that prev_node->next == node->next, 88862306a36Sopenharmony_ci * i.e. both believe the same node should be on the other side of the 88962306a36Sopenharmony_ci * hole. 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_ci prev_node = list_prev_entry(node, node_list); 89262306a36Sopenharmony_ci DRM_MM_BUG_ON(list_next_entry(prev_node, node_list) != 89362306a36Sopenharmony_ci list_next_entry(node, node_list)); 89462306a36Sopenharmony_ci list_add(&node->node_list, &prev_node->node_list); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci return (node->start + node->size > scan->hit_start && 89762306a36Sopenharmony_ci node->start < scan->hit_end); 89862306a36Sopenharmony_ci} 89962306a36Sopenharmony_ciEXPORT_SYMBOL(drm_mm_scan_remove_block); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci/** 90262306a36Sopenharmony_ci * drm_mm_scan_color_evict - evict overlapping nodes on either side of hole 90362306a36Sopenharmony_ci * @scan: drm_mm scan with target hole 90462306a36Sopenharmony_ci * 90562306a36Sopenharmony_ci * After completing an eviction scan and removing the selected nodes, we may 90662306a36Sopenharmony_ci * need to remove a few more nodes from either side of the target hole if 90762306a36Sopenharmony_ci * mm.color_adjust is being used. 90862306a36Sopenharmony_ci * 90962306a36Sopenharmony_ci * Returns: 91062306a36Sopenharmony_ci * A node to evict, or NULL if there are no overlapping nodes. 91162306a36Sopenharmony_ci */ 91262306a36Sopenharmony_cistruct drm_mm_node *drm_mm_scan_color_evict(struct drm_mm_scan *scan) 91362306a36Sopenharmony_ci{ 91462306a36Sopenharmony_ci struct drm_mm *mm = scan->mm; 91562306a36Sopenharmony_ci struct drm_mm_node *hole; 91662306a36Sopenharmony_ci u64 hole_start, hole_end; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci DRM_MM_BUG_ON(list_empty(&mm->hole_stack)); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (!mm->color_adjust) 92162306a36Sopenharmony_ci return NULL; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci /* 92462306a36Sopenharmony_ci * The hole found during scanning should ideally be the first element 92562306a36Sopenharmony_ci * in the hole_stack list, but due to side-effects in the driver it 92662306a36Sopenharmony_ci * may not be. 92762306a36Sopenharmony_ci */ 92862306a36Sopenharmony_ci list_for_each_entry(hole, &mm->hole_stack, hole_stack) { 92962306a36Sopenharmony_ci hole_start = __drm_mm_hole_node_start(hole); 93062306a36Sopenharmony_ci hole_end = hole_start + hole->hole_size; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci if (hole_start <= scan->hit_start && 93362306a36Sopenharmony_ci hole_end >= scan->hit_end) 93462306a36Sopenharmony_ci break; 93562306a36Sopenharmony_ci } 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci /* We should only be called after we found the hole previously */ 93862306a36Sopenharmony_ci DRM_MM_BUG_ON(&hole->hole_stack == &mm->hole_stack); 93962306a36Sopenharmony_ci if (unlikely(&hole->hole_stack == &mm->hole_stack)) 94062306a36Sopenharmony_ci return NULL; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci DRM_MM_BUG_ON(hole_start > scan->hit_start); 94362306a36Sopenharmony_ci DRM_MM_BUG_ON(hole_end < scan->hit_end); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci mm->color_adjust(hole, scan->color, &hole_start, &hole_end); 94662306a36Sopenharmony_ci if (hole_start > scan->hit_start) 94762306a36Sopenharmony_ci return hole; 94862306a36Sopenharmony_ci if (hole_end < scan->hit_end) 94962306a36Sopenharmony_ci return list_next_entry(hole, node_list); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci return NULL; 95262306a36Sopenharmony_ci} 95362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_mm_scan_color_evict); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci/** 95662306a36Sopenharmony_ci * drm_mm_init - initialize a drm-mm allocator 95762306a36Sopenharmony_ci * @mm: the drm_mm structure to initialize 95862306a36Sopenharmony_ci * @start: start of the range managed by @mm 95962306a36Sopenharmony_ci * @size: end of the range managed by @mm 96062306a36Sopenharmony_ci * 96162306a36Sopenharmony_ci * Note that @mm must be cleared to 0 before calling this function. 96262306a36Sopenharmony_ci */ 96362306a36Sopenharmony_civoid drm_mm_init(struct drm_mm *mm, u64 start, u64 size) 96462306a36Sopenharmony_ci{ 96562306a36Sopenharmony_ci DRM_MM_BUG_ON(start + size <= start); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci mm->color_adjust = NULL; 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci INIT_LIST_HEAD(&mm->hole_stack); 97062306a36Sopenharmony_ci mm->interval_tree = RB_ROOT_CACHED; 97162306a36Sopenharmony_ci mm->holes_size = RB_ROOT_CACHED; 97262306a36Sopenharmony_ci mm->holes_addr = RB_ROOT; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci /* Clever trick to avoid a special case in the free hole tracking. */ 97562306a36Sopenharmony_ci INIT_LIST_HEAD(&mm->head_node.node_list); 97662306a36Sopenharmony_ci mm->head_node.flags = 0; 97762306a36Sopenharmony_ci mm->head_node.mm = mm; 97862306a36Sopenharmony_ci mm->head_node.start = start + size; 97962306a36Sopenharmony_ci mm->head_node.size = -size; 98062306a36Sopenharmony_ci add_hole(&mm->head_node); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci mm->scan_active = 0; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci#ifdef CONFIG_DRM_DEBUG_MM 98562306a36Sopenharmony_ci stack_depot_init(); 98662306a36Sopenharmony_ci#endif 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ciEXPORT_SYMBOL(drm_mm_init); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci/** 99162306a36Sopenharmony_ci * drm_mm_takedown - clean up a drm_mm allocator 99262306a36Sopenharmony_ci * @mm: drm_mm allocator to clean up 99362306a36Sopenharmony_ci * 99462306a36Sopenharmony_ci * Note that it is a bug to call this function on an allocator which is not 99562306a36Sopenharmony_ci * clean. 99662306a36Sopenharmony_ci */ 99762306a36Sopenharmony_civoid drm_mm_takedown(struct drm_mm *mm) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci if (WARN(!drm_mm_clean(mm), 100062306a36Sopenharmony_ci "Memory manager not clean during takedown.\n")) 100162306a36Sopenharmony_ci show_leaks(mm); 100262306a36Sopenharmony_ci} 100362306a36Sopenharmony_ciEXPORT_SYMBOL(drm_mm_takedown); 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_cistatic u64 drm_mm_dump_hole(struct drm_printer *p, const struct drm_mm_node *entry) 100662306a36Sopenharmony_ci{ 100762306a36Sopenharmony_ci u64 start, size; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci size = entry->hole_size; 101062306a36Sopenharmony_ci if (size) { 101162306a36Sopenharmony_ci start = drm_mm_hole_node_start(entry); 101262306a36Sopenharmony_ci drm_printf(p, "%#018llx-%#018llx: %llu: free\n", 101362306a36Sopenharmony_ci start, start + size, size); 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci return size; 101762306a36Sopenharmony_ci} 101862306a36Sopenharmony_ci/** 101962306a36Sopenharmony_ci * drm_mm_print - print allocator state 102062306a36Sopenharmony_ci * @mm: drm_mm allocator to print 102162306a36Sopenharmony_ci * @p: DRM printer to use 102262306a36Sopenharmony_ci */ 102362306a36Sopenharmony_civoid drm_mm_print(const struct drm_mm *mm, struct drm_printer *p) 102462306a36Sopenharmony_ci{ 102562306a36Sopenharmony_ci const struct drm_mm_node *entry; 102662306a36Sopenharmony_ci u64 total_used = 0, total_free = 0, total = 0; 102762306a36Sopenharmony_ci 102862306a36Sopenharmony_ci total_free += drm_mm_dump_hole(p, &mm->head_node); 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci drm_mm_for_each_node(entry, mm) { 103162306a36Sopenharmony_ci drm_printf(p, "%#018llx-%#018llx: %llu: used\n", entry->start, 103262306a36Sopenharmony_ci entry->start + entry->size, entry->size); 103362306a36Sopenharmony_ci total_used += entry->size; 103462306a36Sopenharmony_ci total_free += drm_mm_dump_hole(p, entry); 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci total = total_free + total_used; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci drm_printf(p, "total: %llu, used %llu free %llu\n", total, 103962306a36Sopenharmony_ci total_used, total_free); 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_mm_print); 1042