1*4882a593Smuzhiyun /**************************************************************************
2*4882a593Smuzhiyun *
3*4882a593Smuzhiyun * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
4*4882a593Smuzhiyun * Copyright 2016 Intel Corporation
5*4882a593Smuzhiyun * All Rights Reserved.
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Permission is hereby granted, free of charge, to any person obtaining a
8*4882a593Smuzhiyun * copy of this software and associated documentation files (the
9*4882a593Smuzhiyun * "Software"), to deal in the Software without restriction, including
10*4882a593Smuzhiyun * without limitation the rights to use, copy, modify, merge, publish,
11*4882a593Smuzhiyun * distribute, sub license, and/or sell copies of the Software, and to
12*4882a593Smuzhiyun * permit persons to whom the Software is furnished to do so, subject to
13*4882a593Smuzhiyun * the following conditions:
14*4882a593Smuzhiyun *
15*4882a593Smuzhiyun * The above copyright notice and this permission notice (including the
16*4882a593Smuzhiyun * next paragraph) shall be included in all copies or substantial portions
17*4882a593Smuzhiyun * of the Software.
18*4882a593Smuzhiyun *
19*4882a593Smuzhiyun * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20*4882a593Smuzhiyun * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21*4882a593Smuzhiyun * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
22*4882a593Smuzhiyun * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23*4882a593Smuzhiyun * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
24*4882a593Smuzhiyun * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
25*4882a593Smuzhiyun * USE OR OTHER DEALINGS IN THE SOFTWARE.
26*4882a593Smuzhiyun *
27*4882a593Smuzhiyun *
28*4882a593Smuzhiyun **************************************************************************/
29*4882a593Smuzhiyun
30*4882a593Smuzhiyun /*
31*4882a593Smuzhiyun * Generic simple memory manager implementation. Intended to be used as a base
32*4882a593Smuzhiyun * class implementation for more advanced memory managers.
33*4882a593Smuzhiyun *
34*4882a593Smuzhiyun * Note that the algorithm used is quite simple and there might be substantial
35*4882a593Smuzhiyun * performance gains if a smarter free list is implemented. Currently it is
36*4882a593Smuzhiyun * just an unordered stack of free regions. This could easily be improved if
37*4882a593Smuzhiyun * an RB-tree is used instead. At least if we expect heavy fragmentation.
38*4882a593Smuzhiyun *
39*4882a593Smuzhiyun * Aligned allocations can also see improvement.
40*4882a593Smuzhiyun *
41*4882a593Smuzhiyun * Authors:
42*4882a593Smuzhiyun * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
43*4882a593Smuzhiyun */
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun #include <linux/export.h>
46*4882a593Smuzhiyun #include <linux/interval_tree_generic.h>
47*4882a593Smuzhiyun #include <linux/seq_file.h>
48*4882a593Smuzhiyun #include <linux/slab.h>
49*4882a593Smuzhiyun #include <linux/stacktrace.h>
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun #include <drm/drm_mm.h>
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun /**
54*4882a593Smuzhiyun * DOC: Overview
55*4882a593Smuzhiyun *
56*4882a593Smuzhiyun * drm_mm provides a simple range allocator. The drivers are free to use the
57*4882a593Smuzhiyun * resource allocator from the linux core if it suits them, the upside of drm_mm
58*4882a593Smuzhiyun * is that it's in the DRM core. Which means that it's easier to extend for
59*4882a593Smuzhiyun * some of the crazier special purpose needs of gpus.
60*4882a593Smuzhiyun *
61*4882a593Smuzhiyun * The main data struct is &drm_mm, allocations are tracked in &drm_mm_node.
62*4882a593Smuzhiyun * Drivers are free to embed either of them into their own suitable
63*4882a593Smuzhiyun * datastructures. drm_mm itself will not do any memory allocations of its own,
64*4882a593Smuzhiyun * so if drivers choose not to embed nodes they need to still allocate them
65*4882a593Smuzhiyun * themselves.
66*4882a593Smuzhiyun *
67*4882a593Smuzhiyun * The range allocator also supports reservation of preallocated blocks. This is
68*4882a593Smuzhiyun * useful for taking over initial mode setting configurations from the firmware,
69*4882a593Smuzhiyun * where an object needs to be created which exactly matches the firmware's
70*4882a593Smuzhiyun * scanout target. As long as the range is still free it can be inserted anytime
71*4882a593Smuzhiyun * after the allocator is initialized, which helps with avoiding looped
72*4882a593Smuzhiyun * dependencies in the driver load sequence.
73*4882a593Smuzhiyun *
74*4882a593Smuzhiyun * drm_mm maintains a stack of most recently freed holes, which of all
75*4882a593Smuzhiyun * simplistic datastructures seems to be a fairly decent approach to clustering
76*4882a593Smuzhiyun * allocations and avoiding too much fragmentation. This means free space
77*4882a593Smuzhiyun * searches are O(num_holes). Given that all the fancy features drm_mm supports
78*4882a593Smuzhiyun * something better would be fairly complex and since gfx thrashing is a fairly
79*4882a593Smuzhiyun * steep cliff not a real concern. Removing a node again is O(1).
80*4882a593Smuzhiyun *
81*4882a593Smuzhiyun * drm_mm supports a few features: Alignment and range restrictions can be
82*4882a593Smuzhiyun * supplied. Furthermore every &drm_mm_node has a color value (which is just an
83*4882a593Smuzhiyun * opaque unsigned long) which in conjunction with a driver callback can be used
84*4882a593Smuzhiyun * to implement sophisticated placement restrictions. The i915 DRM driver uses
85*4882a593Smuzhiyun * this to implement guard pages between incompatible caching domains in the
86*4882a593Smuzhiyun * graphics TT.
87*4882a593Smuzhiyun *
88*4882a593Smuzhiyun * Two behaviors are supported for searching and allocating: bottom-up and
89*4882a593Smuzhiyun * top-down. The default is bottom-up. Top-down allocation can be used if the
90*4882a593Smuzhiyun * memory area has different restrictions, or just to reduce fragmentation.
91*4882a593Smuzhiyun *
92*4882a593Smuzhiyun * Finally iteration helpers to walk all nodes and all holes are provided as are
93*4882a593Smuzhiyun * some basic allocator dumpers for debugging.
94*4882a593Smuzhiyun *
95*4882a593Smuzhiyun * Note that this range allocator is not thread-safe, drivers need to protect
96*4882a593Smuzhiyun * modifications with their own locking. The idea behind this is that for a full
97*4882a593Smuzhiyun * memory manager additional data needs to be protected anyway, hence internal
98*4882a593Smuzhiyun * locking would be fully redundant.
99*4882a593Smuzhiyun */
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun #ifdef CONFIG_DRM_DEBUG_MM
102*4882a593Smuzhiyun #include <linux/stackdepot.h>
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun #define STACKDEPTH 32
105*4882a593Smuzhiyun #define BUFSZ 4096
106*4882a593Smuzhiyun
save_stack(struct drm_mm_node * node)107*4882a593Smuzhiyun static noinline void save_stack(struct drm_mm_node *node)
108*4882a593Smuzhiyun {
109*4882a593Smuzhiyun unsigned long entries[STACKDEPTH];
110*4882a593Smuzhiyun unsigned int n;
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun n = stack_trace_save(entries, ARRAY_SIZE(entries), 1);
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /* May be called under spinlock, so avoid sleeping */
115*4882a593Smuzhiyun node->stack = stack_depot_save(entries, n, GFP_NOWAIT);
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun
show_leaks(struct drm_mm * mm)118*4882a593Smuzhiyun static void show_leaks(struct drm_mm *mm)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun struct drm_mm_node *node;
121*4882a593Smuzhiyun unsigned long *entries;
122*4882a593Smuzhiyun unsigned int nr_entries;
123*4882a593Smuzhiyun char *buf;
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun buf = kmalloc(BUFSZ, GFP_KERNEL);
126*4882a593Smuzhiyun if (!buf)
127*4882a593Smuzhiyun return;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun list_for_each_entry(node, drm_mm_nodes(mm), node_list) {
130*4882a593Smuzhiyun if (!node->stack) {
131*4882a593Smuzhiyun DRM_ERROR("node [%08llx + %08llx]: unknown owner\n",
132*4882a593Smuzhiyun node->start, node->size);
133*4882a593Smuzhiyun continue;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun nr_entries = stack_depot_fetch(node->stack, &entries);
137*4882a593Smuzhiyun stack_trace_snprint(buf, BUFSZ, entries, nr_entries, 0);
138*4882a593Smuzhiyun DRM_ERROR("node [%08llx + %08llx]: inserted at\n%s",
139*4882a593Smuzhiyun node->start, node->size, buf);
140*4882a593Smuzhiyun }
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun kfree(buf);
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun #undef STACKDEPTH
146*4882a593Smuzhiyun #undef BUFSZ
147*4882a593Smuzhiyun #else
save_stack(struct drm_mm_node * node)148*4882a593Smuzhiyun static void save_stack(struct drm_mm_node *node) { }
show_leaks(struct drm_mm * mm)149*4882a593Smuzhiyun static void show_leaks(struct drm_mm *mm) { }
150*4882a593Smuzhiyun #endif
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun #define START(node) ((node)->start)
153*4882a593Smuzhiyun #define LAST(node) ((node)->start + (node)->size - 1)
154*4882a593Smuzhiyun
INTERVAL_TREE_DEFINE(struct drm_mm_node,rb,u64,__subtree_last,START,LAST,static inline,drm_mm_interval_tree)155*4882a593Smuzhiyun INTERVAL_TREE_DEFINE(struct drm_mm_node, rb,
156*4882a593Smuzhiyun u64, __subtree_last,
157*4882a593Smuzhiyun START, LAST, static inline, drm_mm_interval_tree)
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun struct drm_mm_node *
160*4882a593Smuzhiyun __drm_mm_interval_first(const struct drm_mm *mm, u64 start, u64 last)
161*4882a593Smuzhiyun {
162*4882a593Smuzhiyun return drm_mm_interval_tree_iter_first((struct rb_root_cached *)&mm->interval_tree,
163*4882a593Smuzhiyun start, last) ?: (struct drm_mm_node *)&mm->head_node;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun EXPORT_SYMBOL(__drm_mm_interval_first);
166*4882a593Smuzhiyun
drm_mm_interval_tree_add_node(struct drm_mm_node * hole_node,struct drm_mm_node * node)167*4882a593Smuzhiyun static void drm_mm_interval_tree_add_node(struct drm_mm_node *hole_node,
168*4882a593Smuzhiyun struct drm_mm_node *node)
169*4882a593Smuzhiyun {
170*4882a593Smuzhiyun struct drm_mm *mm = hole_node->mm;
171*4882a593Smuzhiyun struct rb_node **link, *rb;
172*4882a593Smuzhiyun struct drm_mm_node *parent;
173*4882a593Smuzhiyun bool leftmost;
174*4882a593Smuzhiyun
175*4882a593Smuzhiyun node->__subtree_last = LAST(node);
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun if (drm_mm_node_allocated(hole_node)) {
178*4882a593Smuzhiyun rb = &hole_node->rb;
179*4882a593Smuzhiyun while (rb) {
180*4882a593Smuzhiyun parent = rb_entry(rb, struct drm_mm_node, rb);
181*4882a593Smuzhiyun if (parent->__subtree_last >= node->__subtree_last)
182*4882a593Smuzhiyun break;
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun parent->__subtree_last = node->__subtree_last;
185*4882a593Smuzhiyun rb = rb_parent(rb);
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun rb = &hole_node->rb;
189*4882a593Smuzhiyun link = &hole_node->rb.rb_right;
190*4882a593Smuzhiyun leftmost = false;
191*4882a593Smuzhiyun } else {
192*4882a593Smuzhiyun rb = NULL;
193*4882a593Smuzhiyun link = &mm->interval_tree.rb_root.rb_node;
194*4882a593Smuzhiyun leftmost = true;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun
197*4882a593Smuzhiyun while (*link) {
198*4882a593Smuzhiyun rb = *link;
199*4882a593Smuzhiyun parent = rb_entry(rb, struct drm_mm_node, rb);
200*4882a593Smuzhiyun if (parent->__subtree_last < node->__subtree_last)
201*4882a593Smuzhiyun parent->__subtree_last = node->__subtree_last;
202*4882a593Smuzhiyun if (node->start < parent->start) {
203*4882a593Smuzhiyun link = &parent->rb.rb_left;
204*4882a593Smuzhiyun } else {
205*4882a593Smuzhiyun link = &parent->rb.rb_right;
206*4882a593Smuzhiyun leftmost = false;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun rb_link_node(&node->rb, rb, link);
211*4882a593Smuzhiyun rb_insert_augmented_cached(&node->rb, &mm->interval_tree, leftmost,
212*4882a593Smuzhiyun &drm_mm_interval_tree_augment);
213*4882a593Smuzhiyun }
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun #define HOLE_SIZE(NODE) ((NODE)->hole_size)
216*4882a593Smuzhiyun #define HOLE_ADDR(NODE) (__drm_mm_hole_node_start(NODE))
217*4882a593Smuzhiyun
rb_to_hole_size(struct rb_node * rb)218*4882a593Smuzhiyun static u64 rb_to_hole_size(struct rb_node *rb)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun return rb_entry(rb, struct drm_mm_node, rb_hole_size)->hole_size;
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun
insert_hole_size(struct rb_root_cached * root,struct drm_mm_node * node)223*4882a593Smuzhiyun static void insert_hole_size(struct rb_root_cached *root,
224*4882a593Smuzhiyun struct drm_mm_node *node)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun struct rb_node **link = &root->rb_root.rb_node, *rb = NULL;
227*4882a593Smuzhiyun u64 x = node->hole_size;
228*4882a593Smuzhiyun bool first = true;
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun while (*link) {
231*4882a593Smuzhiyun rb = *link;
232*4882a593Smuzhiyun if (x > rb_to_hole_size(rb)) {
233*4882a593Smuzhiyun link = &rb->rb_left;
234*4882a593Smuzhiyun } else {
235*4882a593Smuzhiyun link = &rb->rb_right;
236*4882a593Smuzhiyun first = false;
237*4882a593Smuzhiyun }
238*4882a593Smuzhiyun }
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun rb_link_node(&node->rb_hole_size, rb, link);
241*4882a593Smuzhiyun rb_insert_color_cached(&node->rb_hole_size, root, first);
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun
RB_DECLARE_CALLBACKS_MAX(static,augment_callbacks,struct drm_mm_node,rb_hole_addr,u64,subtree_max_hole,HOLE_SIZE)244*4882a593Smuzhiyun RB_DECLARE_CALLBACKS_MAX(static, augment_callbacks,
245*4882a593Smuzhiyun struct drm_mm_node, rb_hole_addr,
246*4882a593Smuzhiyun u64, subtree_max_hole, HOLE_SIZE)
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun static void insert_hole_addr(struct rb_root *root, struct drm_mm_node *node)
249*4882a593Smuzhiyun {
250*4882a593Smuzhiyun struct rb_node **link = &root->rb_node, *rb_parent = NULL;
251*4882a593Smuzhiyun u64 start = HOLE_ADDR(node), subtree_max_hole = node->subtree_max_hole;
252*4882a593Smuzhiyun struct drm_mm_node *parent;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun while (*link) {
255*4882a593Smuzhiyun rb_parent = *link;
256*4882a593Smuzhiyun parent = rb_entry(rb_parent, struct drm_mm_node, rb_hole_addr);
257*4882a593Smuzhiyun if (parent->subtree_max_hole < subtree_max_hole)
258*4882a593Smuzhiyun parent->subtree_max_hole = subtree_max_hole;
259*4882a593Smuzhiyun if (start < HOLE_ADDR(parent))
260*4882a593Smuzhiyun link = &parent->rb_hole_addr.rb_left;
261*4882a593Smuzhiyun else
262*4882a593Smuzhiyun link = &parent->rb_hole_addr.rb_right;
263*4882a593Smuzhiyun }
264*4882a593Smuzhiyun
265*4882a593Smuzhiyun rb_link_node(&node->rb_hole_addr, rb_parent, link);
266*4882a593Smuzhiyun rb_insert_augmented(&node->rb_hole_addr, root, &augment_callbacks);
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun
add_hole(struct drm_mm_node * node)269*4882a593Smuzhiyun static void add_hole(struct drm_mm_node *node)
270*4882a593Smuzhiyun {
271*4882a593Smuzhiyun struct drm_mm *mm = node->mm;
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun node->hole_size =
274*4882a593Smuzhiyun __drm_mm_hole_node_end(node) - __drm_mm_hole_node_start(node);
275*4882a593Smuzhiyun node->subtree_max_hole = node->hole_size;
276*4882a593Smuzhiyun DRM_MM_BUG_ON(!drm_mm_hole_follows(node));
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun insert_hole_size(&mm->holes_size, node);
279*4882a593Smuzhiyun insert_hole_addr(&mm->holes_addr, node);
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun list_add(&node->hole_stack, &mm->hole_stack);
282*4882a593Smuzhiyun }
283*4882a593Smuzhiyun
rm_hole(struct drm_mm_node * node)284*4882a593Smuzhiyun static void rm_hole(struct drm_mm_node *node)
285*4882a593Smuzhiyun {
286*4882a593Smuzhiyun DRM_MM_BUG_ON(!drm_mm_hole_follows(node));
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun list_del(&node->hole_stack);
289*4882a593Smuzhiyun rb_erase_cached(&node->rb_hole_size, &node->mm->holes_size);
290*4882a593Smuzhiyun rb_erase_augmented(&node->rb_hole_addr, &node->mm->holes_addr,
291*4882a593Smuzhiyun &augment_callbacks);
292*4882a593Smuzhiyun node->hole_size = 0;
293*4882a593Smuzhiyun node->subtree_max_hole = 0;
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun DRM_MM_BUG_ON(drm_mm_hole_follows(node));
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun
rb_hole_size_to_node(struct rb_node * rb)298*4882a593Smuzhiyun static inline struct drm_mm_node *rb_hole_size_to_node(struct rb_node *rb)
299*4882a593Smuzhiyun {
300*4882a593Smuzhiyun return rb_entry_safe(rb, struct drm_mm_node, rb_hole_size);
301*4882a593Smuzhiyun }
302*4882a593Smuzhiyun
rb_hole_addr_to_node(struct rb_node * rb)303*4882a593Smuzhiyun static inline struct drm_mm_node *rb_hole_addr_to_node(struct rb_node *rb)
304*4882a593Smuzhiyun {
305*4882a593Smuzhiyun return rb_entry_safe(rb, struct drm_mm_node, rb_hole_addr);
306*4882a593Smuzhiyun }
307*4882a593Smuzhiyun
best_hole(struct drm_mm * mm,u64 size)308*4882a593Smuzhiyun static struct drm_mm_node *best_hole(struct drm_mm *mm, u64 size)
309*4882a593Smuzhiyun {
310*4882a593Smuzhiyun struct rb_node *rb = mm->holes_size.rb_root.rb_node;
311*4882a593Smuzhiyun struct drm_mm_node *best = NULL;
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun do {
314*4882a593Smuzhiyun struct drm_mm_node *node =
315*4882a593Smuzhiyun rb_entry(rb, struct drm_mm_node, rb_hole_size);
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun if (size <= node->hole_size) {
318*4882a593Smuzhiyun best = node;
319*4882a593Smuzhiyun rb = rb->rb_right;
320*4882a593Smuzhiyun } else {
321*4882a593Smuzhiyun rb = rb->rb_left;
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun } while (rb);
324*4882a593Smuzhiyun
325*4882a593Smuzhiyun return best;
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun
usable_hole_addr(struct rb_node * rb,u64 size)328*4882a593Smuzhiyun static bool usable_hole_addr(struct rb_node *rb, u64 size)
329*4882a593Smuzhiyun {
330*4882a593Smuzhiyun return rb && rb_hole_addr_to_node(rb)->subtree_max_hole >= size;
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun
find_hole_addr(struct drm_mm * mm,u64 addr,u64 size)333*4882a593Smuzhiyun static struct drm_mm_node *find_hole_addr(struct drm_mm *mm, u64 addr, u64 size)
334*4882a593Smuzhiyun {
335*4882a593Smuzhiyun struct rb_node *rb = mm->holes_addr.rb_node;
336*4882a593Smuzhiyun struct drm_mm_node *node = NULL;
337*4882a593Smuzhiyun
338*4882a593Smuzhiyun while (rb) {
339*4882a593Smuzhiyun u64 hole_start;
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun if (!usable_hole_addr(rb, size))
342*4882a593Smuzhiyun break;
343*4882a593Smuzhiyun
344*4882a593Smuzhiyun node = rb_hole_addr_to_node(rb);
345*4882a593Smuzhiyun hole_start = __drm_mm_hole_node_start(node);
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun if (addr < hole_start)
348*4882a593Smuzhiyun rb = node->rb_hole_addr.rb_left;
349*4882a593Smuzhiyun else if (addr > hole_start + node->hole_size)
350*4882a593Smuzhiyun rb = node->rb_hole_addr.rb_right;
351*4882a593Smuzhiyun else
352*4882a593Smuzhiyun break;
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun
355*4882a593Smuzhiyun return node;
356*4882a593Smuzhiyun }
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun static struct drm_mm_node *
first_hole(struct drm_mm * mm,u64 start,u64 end,u64 size,enum drm_mm_insert_mode mode)359*4882a593Smuzhiyun first_hole(struct drm_mm *mm,
360*4882a593Smuzhiyun u64 start, u64 end, u64 size,
361*4882a593Smuzhiyun enum drm_mm_insert_mode mode)
362*4882a593Smuzhiyun {
363*4882a593Smuzhiyun switch (mode) {
364*4882a593Smuzhiyun default:
365*4882a593Smuzhiyun case DRM_MM_INSERT_BEST:
366*4882a593Smuzhiyun return best_hole(mm, size);
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun case DRM_MM_INSERT_LOW:
369*4882a593Smuzhiyun return find_hole_addr(mm, start, size);
370*4882a593Smuzhiyun
371*4882a593Smuzhiyun case DRM_MM_INSERT_HIGH:
372*4882a593Smuzhiyun return find_hole_addr(mm, end, size);
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun case DRM_MM_INSERT_EVICT:
375*4882a593Smuzhiyun return list_first_entry_or_null(&mm->hole_stack,
376*4882a593Smuzhiyun struct drm_mm_node,
377*4882a593Smuzhiyun hole_stack);
378*4882a593Smuzhiyun }
379*4882a593Smuzhiyun }
380*4882a593Smuzhiyun
381*4882a593Smuzhiyun /**
382*4882a593Smuzhiyun * DECLARE_NEXT_HOLE_ADDR - macro to declare next hole functions
383*4882a593Smuzhiyun * @name: name of function to declare
384*4882a593Smuzhiyun * @first: first rb member to traverse (either rb_left or rb_right).
385*4882a593Smuzhiyun * @last: last rb member to traverse (either rb_right or rb_left).
386*4882a593Smuzhiyun *
387*4882a593Smuzhiyun * This macro declares a function to return the next hole of the addr rb tree.
388*4882a593Smuzhiyun * While traversing the tree we take the searched size into account and only
389*4882a593Smuzhiyun * visit branches with potential big enough holes.
390*4882a593Smuzhiyun */
391*4882a593Smuzhiyun
392*4882a593Smuzhiyun #define DECLARE_NEXT_HOLE_ADDR(name, first, last) \
393*4882a593Smuzhiyun static struct drm_mm_node *name(struct drm_mm_node *entry, u64 size) \
394*4882a593Smuzhiyun { \
395*4882a593Smuzhiyun struct rb_node *parent, *node = &entry->rb_hole_addr; \
396*4882a593Smuzhiyun \
397*4882a593Smuzhiyun if (!entry || RB_EMPTY_NODE(node)) \
398*4882a593Smuzhiyun return NULL; \
399*4882a593Smuzhiyun \
400*4882a593Smuzhiyun if (usable_hole_addr(node->first, size)) { \
401*4882a593Smuzhiyun node = node->first; \
402*4882a593Smuzhiyun while (usable_hole_addr(node->last, size)) \
403*4882a593Smuzhiyun node = node->last; \
404*4882a593Smuzhiyun return rb_hole_addr_to_node(node); \
405*4882a593Smuzhiyun } \
406*4882a593Smuzhiyun \
407*4882a593Smuzhiyun while ((parent = rb_parent(node)) && node == parent->first) \
408*4882a593Smuzhiyun node = parent; \
409*4882a593Smuzhiyun \
410*4882a593Smuzhiyun return rb_hole_addr_to_node(parent); \
411*4882a593Smuzhiyun }
412*4882a593Smuzhiyun
DECLARE_NEXT_HOLE_ADDR(next_hole_high_addr,rb_left,rb_right)413*4882a593Smuzhiyun DECLARE_NEXT_HOLE_ADDR(next_hole_high_addr, rb_left, rb_right)
414*4882a593Smuzhiyun DECLARE_NEXT_HOLE_ADDR(next_hole_low_addr, rb_right, rb_left)
415*4882a593Smuzhiyun
416*4882a593Smuzhiyun static struct drm_mm_node *
417*4882a593Smuzhiyun next_hole(struct drm_mm *mm,
418*4882a593Smuzhiyun struct drm_mm_node *node,
419*4882a593Smuzhiyun u64 size,
420*4882a593Smuzhiyun enum drm_mm_insert_mode mode)
421*4882a593Smuzhiyun {
422*4882a593Smuzhiyun switch (mode) {
423*4882a593Smuzhiyun default:
424*4882a593Smuzhiyun case DRM_MM_INSERT_BEST:
425*4882a593Smuzhiyun return rb_hole_size_to_node(rb_prev(&node->rb_hole_size));
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun case DRM_MM_INSERT_LOW:
428*4882a593Smuzhiyun return next_hole_low_addr(node, size);
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun case DRM_MM_INSERT_HIGH:
431*4882a593Smuzhiyun return next_hole_high_addr(node, size);
432*4882a593Smuzhiyun
433*4882a593Smuzhiyun case DRM_MM_INSERT_EVICT:
434*4882a593Smuzhiyun node = list_next_entry(node, hole_stack);
435*4882a593Smuzhiyun return &node->hole_stack == &mm->hole_stack ? NULL : node;
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun }
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun /**
440*4882a593Smuzhiyun * drm_mm_reserve_node - insert an pre-initialized node
441*4882a593Smuzhiyun * @mm: drm_mm allocator to insert @node into
442*4882a593Smuzhiyun * @node: drm_mm_node to insert
443*4882a593Smuzhiyun *
444*4882a593Smuzhiyun * This functions inserts an already set-up &drm_mm_node into the allocator,
445*4882a593Smuzhiyun * meaning that start, size and color must be set by the caller. All other
446*4882a593Smuzhiyun * fields must be cleared to 0. This is useful to initialize the allocator with
447*4882a593Smuzhiyun * preallocated objects which must be set-up before the range allocator can be
448*4882a593Smuzhiyun * set-up, e.g. when taking over a firmware framebuffer.
449*4882a593Smuzhiyun *
450*4882a593Smuzhiyun * Returns:
451*4882a593Smuzhiyun * 0 on success, -ENOSPC if there's no hole where @node is.
452*4882a593Smuzhiyun */
drm_mm_reserve_node(struct drm_mm * mm,struct drm_mm_node * node)453*4882a593Smuzhiyun int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
454*4882a593Smuzhiyun {
455*4882a593Smuzhiyun struct drm_mm_node *hole;
456*4882a593Smuzhiyun u64 hole_start, hole_end;
457*4882a593Smuzhiyun u64 adj_start, adj_end;
458*4882a593Smuzhiyun u64 end;
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun end = node->start + node->size;
461*4882a593Smuzhiyun if (unlikely(end <= node->start))
462*4882a593Smuzhiyun return -ENOSPC;
463*4882a593Smuzhiyun
464*4882a593Smuzhiyun /* Find the relevant hole to add our node to */
465*4882a593Smuzhiyun hole = find_hole_addr(mm, node->start, 0);
466*4882a593Smuzhiyun if (!hole)
467*4882a593Smuzhiyun return -ENOSPC;
468*4882a593Smuzhiyun
469*4882a593Smuzhiyun adj_start = hole_start = __drm_mm_hole_node_start(hole);
470*4882a593Smuzhiyun adj_end = hole_end = hole_start + hole->hole_size;
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun if (mm->color_adjust)
473*4882a593Smuzhiyun mm->color_adjust(hole, node->color, &adj_start, &adj_end);
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun if (adj_start > node->start || adj_end < end)
476*4882a593Smuzhiyun return -ENOSPC;
477*4882a593Smuzhiyun
478*4882a593Smuzhiyun node->mm = mm;
479*4882a593Smuzhiyun
480*4882a593Smuzhiyun __set_bit(DRM_MM_NODE_ALLOCATED_BIT, &node->flags);
481*4882a593Smuzhiyun list_add(&node->node_list, &hole->node_list);
482*4882a593Smuzhiyun drm_mm_interval_tree_add_node(hole, node);
483*4882a593Smuzhiyun node->hole_size = 0;
484*4882a593Smuzhiyun
485*4882a593Smuzhiyun rm_hole(hole);
486*4882a593Smuzhiyun if (node->start > hole_start)
487*4882a593Smuzhiyun add_hole(hole);
488*4882a593Smuzhiyun if (end < hole_end)
489*4882a593Smuzhiyun add_hole(node);
490*4882a593Smuzhiyun
491*4882a593Smuzhiyun save_stack(node);
492*4882a593Smuzhiyun return 0;
493*4882a593Smuzhiyun }
494*4882a593Smuzhiyun EXPORT_SYMBOL(drm_mm_reserve_node);
495*4882a593Smuzhiyun
rb_to_hole_size_or_zero(struct rb_node * rb)496*4882a593Smuzhiyun static u64 rb_to_hole_size_or_zero(struct rb_node *rb)
497*4882a593Smuzhiyun {
498*4882a593Smuzhiyun return rb ? rb_to_hole_size(rb) : 0;
499*4882a593Smuzhiyun }
500*4882a593Smuzhiyun
501*4882a593Smuzhiyun /**
502*4882a593Smuzhiyun * drm_mm_insert_node_in_range - ranged search for space and insert @node
503*4882a593Smuzhiyun * @mm: drm_mm to allocate from
504*4882a593Smuzhiyun * @node: preallocate node to insert
505*4882a593Smuzhiyun * @size: size of the allocation
506*4882a593Smuzhiyun * @alignment: alignment of the allocation
507*4882a593Smuzhiyun * @color: opaque tag value to use for this node
508*4882a593Smuzhiyun * @range_start: start of the allowed range for this node
509*4882a593Smuzhiyun * @range_end: end of the allowed range for this node
510*4882a593Smuzhiyun * @mode: fine-tune the allocation search and placement
511*4882a593Smuzhiyun *
512*4882a593Smuzhiyun * The preallocated @node must be cleared to 0.
513*4882a593Smuzhiyun *
514*4882a593Smuzhiyun * Returns:
515*4882a593Smuzhiyun * 0 on success, -ENOSPC if there's no suitable hole.
516*4882a593Smuzhiyun */
drm_mm_insert_node_in_range(struct drm_mm * const mm,struct drm_mm_node * const node,u64 size,u64 alignment,unsigned long color,u64 range_start,u64 range_end,enum drm_mm_insert_mode mode)517*4882a593Smuzhiyun int drm_mm_insert_node_in_range(struct drm_mm * const mm,
518*4882a593Smuzhiyun struct drm_mm_node * const node,
519*4882a593Smuzhiyun u64 size, u64 alignment,
520*4882a593Smuzhiyun unsigned long color,
521*4882a593Smuzhiyun u64 range_start, u64 range_end,
522*4882a593Smuzhiyun enum drm_mm_insert_mode mode)
523*4882a593Smuzhiyun {
524*4882a593Smuzhiyun struct drm_mm_node *hole;
525*4882a593Smuzhiyun u64 remainder_mask;
526*4882a593Smuzhiyun bool once;
527*4882a593Smuzhiyun
528*4882a593Smuzhiyun DRM_MM_BUG_ON(range_start > range_end);
529*4882a593Smuzhiyun
530*4882a593Smuzhiyun if (unlikely(size == 0 || range_end - range_start < size))
531*4882a593Smuzhiyun return -ENOSPC;
532*4882a593Smuzhiyun
533*4882a593Smuzhiyun if (rb_to_hole_size_or_zero(rb_first_cached(&mm->holes_size)) < size)
534*4882a593Smuzhiyun return -ENOSPC;
535*4882a593Smuzhiyun
536*4882a593Smuzhiyun if (alignment <= 1)
537*4882a593Smuzhiyun alignment = 0;
538*4882a593Smuzhiyun
539*4882a593Smuzhiyun once = mode & DRM_MM_INSERT_ONCE;
540*4882a593Smuzhiyun mode &= ~DRM_MM_INSERT_ONCE;
541*4882a593Smuzhiyun
542*4882a593Smuzhiyun remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0;
543*4882a593Smuzhiyun for (hole = first_hole(mm, range_start, range_end, size, mode);
544*4882a593Smuzhiyun hole;
545*4882a593Smuzhiyun hole = once ? NULL : next_hole(mm, hole, size, mode)) {
546*4882a593Smuzhiyun u64 hole_start = __drm_mm_hole_node_start(hole);
547*4882a593Smuzhiyun u64 hole_end = hole_start + hole->hole_size;
548*4882a593Smuzhiyun u64 adj_start, adj_end;
549*4882a593Smuzhiyun u64 col_start, col_end;
550*4882a593Smuzhiyun
551*4882a593Smuzhiyun if (mode == DRM_MM_INSERT_LOW && hole_start >= range_end)
552*4882a593Smuzhiyun break;
553*4882a593Smuzhiyun
554*4882a593Smuzhiyun if (mode == DRM_MM_INSERT_HIGH && hole_end <= range_start)
555*4882a593Smuzhiyun break;
556*4882a593Smuzhiyun
557*4882a593Smuzhiyun col_start = hole_start;
558*4882a593Smuzhiyun col_end = hole_end;
559*4882a593Smuzhiyun if (mm->color_adjust)
560*4882a593Smuzhiyun mm->color_adjust(hole, color, &col_start, &col_end);
561*4882a593Smuzhiyun
562*4882a593Smuzhiyun adj_start = max(col_start, range_start);
563*4882a593Smuzhiyun adj_end = min(col_end, range_end);
564*4882a593Smuzhiyun
565*4882a593Smuzhiyun if (adj_end <= adj_start || adj_end - adj_start < size)
566*4882a593Smuzhiyun continue;
567*4882a593Smuzhiyun
568*4882a593Smuzhiyun if (mode == DRM_MM_INSERT_HIGH)
569*4882a593Smuzhiyun adj_start = adj_end - size;
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun if (alignment) {
572*4882a593Smuzhiyun u64 rem;
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun if (likely(remainder_mask))
575*4882a593Smuzhiyun rem = adj_start & remainder_mask;
576*4882a593Smuzhiyun else
577*4882a593Smuzhiyun div64_u64_rem(adj_start, alignment, &rem);
578*4882a593Smuzhiyun if (rem) {
579*4882a593Smuzhiyun adj_start -= rem;
580*4882a593Smuzhiyun if (mode != DRM_MM_INSERT_HIGH)
581*4882a593Smuzhiyun adj_start += alignment;
582*4882a593Smuzhiyun
583*4882a593Smuzhiyun if (adj_start < max(col_start, range_start) ||
584*4882a593Smuzhiyun min(col_end, range_end) - adj_start < size)
585*4882a593Smuzhiyun continue;
586*4882a593Smuzhiyun
587*4882a593Smuzhiyun if (adj_end <= adj_start ||
588*4882a593Smuzhiyun adj_end - adj_start < size)
589*4882a593Smuzhiyun continue;
590*4882a593Smuzhiyun }
591*4882a593Smuzhiyun }
592*4882a593Smuzhiyun
593*4882a593Smuzhiyun node->mm = mm;
594*4882a593Smuzhiyun node->size = size;
595*4882a593Smuzhiyun node->start = adj_start;
596*4882a593Smuzhiyun node->color = color;
597*4882a593Smuzhiyun node->hole_size = 0;
598*4882a593Smuzhiyun
599*4882a593Smuzhiyun __set_bit(DRM_MM_NODE_ALLOCATED_BIT, &node->flags);
600*4882a593Smuzhiyun list_add(&node->node_list, &hole->node_list);
601*4882a593Smuzhiyun drm_mm_interval_tree_add_node(hole, node);
602*4882a593Smuzhiyun
603*4882a593Smuzhiyun rm_hole(hole);
604*4882a593Smuzhiyun if (adj_start > hole_start)
605*4882a593Smuzhiyun add_hole(hole);
606*4882a593Smuzhiyun if (adj_start + size < hole_end)
607*4882a593Smuzhiyun add_hole(node);
608*4882a593Smuzhiyun
609*4882a593Smuzhiyun save_stack(node);
610*4882a593Smuzhiyun return 0;
611*4882a593Smuzhiyun }
612*4882a593Smuzhiyun
613*4882a593Smuzhiyun return -ENOSPC;
614*4882a593Smuzhiyun }
615*4882a593Smuzhiyun EXPORT_SYMBOL(drm_mm_insert_node_in_range);
616*4882a593Smuzhiyun
drm_mm_node_scanned_block(const struct drm_mm_node * node)617*4882a593Smuzhiyun static inline bool drm_mm_node_scanned_block(const struct drm_mm_node *node)
618*4882a593Smuzhiyun {
619*4882a593Smuzhiyun return test_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags);
620*4882a593Smuzhiyun }
621*4882a593Smuzhiyun
622*4882a593Smuzhiyun /**
623*4882a593Smuzhiyun * drm_mm_remove_node - Remove a memory node from the allocator.
624*4882a593Smuzhiyun * @node: drm_mm_node to remove
625*4882a593Smuzhiyun *
626*4882a593Smuzhiyun * This just removes a node from its drm_mm allocator. The node does not need to
627*4882a593Smuzhiyun * be cleared again before it can be re-inserted into this or any other drm_mm
628*4882a593Smuzhiyun * allocator. It is a bug to call this function on a unallocated node.
629*4882a593Smuzhiyun */
drm_mm_remove_node(struct drm_mm_node * node)630*4882a593Smuzhiyun void drm_mm_remove_node(struct drm_mm_node *node)
631*4882a593Smuzhiyun {
632*4882a593Smuzhiyun struct drm_mm *mm = node->mm;
633*4882a593Smuzhiyun struct drm_mm_node *prev_node;
634*4882a593Smuzhiyun
635*4882a593Smuzhiyun DRM_MM_BUG_ON(!drm_mm_node_allocated(node));
636*4882a593Smuzhiyun DRM_MM_BUG_ON(drm_mm_node_scanned_block(node));
637*4882a593Smuzhiyun
638*4882a593Smuzhiyun prev_node = list_prev_entry(node, node_list);
639*4882a593Smuzhiyun
640*4882a593Smuzhiyun if (drm_mm_hole_follows(node))
641*4882a593Smuzhiyun rm_hole(node);
642*4882a593Smuzhiyun
643*4882a593Smuzhiyun drm_mm_interval_tree_remove(node, &mm->interval_tree);
644*4882a593Smuzhiyun list_del(&node->node_list);
645*4882a593Smuzhiyun
646*4882a593Smuzhiyun if (drm_mm_hole_follows(prev_node))
647*4882a593Smuzhiyun rm_hole(prev_node);
648*4882a593Smuzhiyun add_hole(prev_node);
649*4882a593Smuzhiyun
650*4882a593Smuzhiyun clear_bit_unlock(DRM_MM_NODE_ALLOCATED_BIT, &node->flags);
651*4882a593Smuzhiyun }
652*4882a593Smuzhiyun EXPORT_SYMBOL(drm_mm_remove_node);
653*4882a593Smuzhiyun
654*4882a593Smuzhiyun /**
655*4882a593Smuzhiyun * drm_mm_replace_node - move an allocation from @old to @new
656*4882a593Smuzhiyun * @old: drm_mm_node to remove from the allocator
657*4882a593Smuzhiyun * @new: drm_mm_node which should inherit @old's allocation
658*4882a593Smuzhiyun *
659*4882a593Smuzhiyun * This is useful for when drivers embed the drm_mm_node structure and hence
660*4882a593Smuzhiyun * can't move allocations by reassigning pointers. It's a combination of remove
661*4882a593Smuzhiyun * and insert with the guarantee that the allocation start will match.
662*4882a593Smuzhiyun */
drm_mm_replace_node(struct drm_mm_node * old,struct drm_mm_node * new)663*4882a593Smuzhiyun void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
664*4882a593Smuzhiyun {
665*4882a593Smuzhiyun struct drm_mm *mm = old->mm;
666*4882a593Smuzhiyun
667*4882a593Smuzhiyun DRM_MM_BUG_ON(!drm_mm_node_allocated(old));
668*4882a593Smuzhiyun
669*4882a593Smuzhiyun *new = *old;
670*4882a593Smuzhiyun
671*4882a593Smuzhiyun __set_bit(DRM_MM_NODE_ALLOCATED_BIT, &new->flags);
672*4882a593Smuzhiyun list_replace(&old->node_list, &new->node_list);
673*4882a593Smuzhiyun rb_replace_node_cached(&old->rb, &new->rb, &mm->interval_tree);
674*4882a593Smuzhiyun
675*4882a593Smuzhiyun if (drm_mm_hole_follows(old)) {
676*4882a593Smuzhiyun list_replace(&old->hole_stack, &new->hole_stack);
677*4882a593Smuzhiyun rb_replace_node_cached(&old->rb_hole_size,
678*4882a593Smuzhiyun &new->rb_hole_size,
679*4882a593Smuzhiyun &mm->holes_size);
680*4882a593Smuzhiyun rb_replace_node(&old->rb_hole_addr,
681*4882a593Smuzhiyun &new->rb_hole_addr,
682*4882a593Smuzhiyun &mm->holes_addr);
683*4882a593Smuzhiyun }
684*4882a593Smuzhiyun
685*4882a593Smuzhiyun clear_bit_unlock(DRM_MM_NODE_ALLOCATED_BIT, &old->flags);
686*4882a593Smuzhiyun }
687*4882a593Smuzhiyun EXPORT_SYMBOL(drm_mm_replace_node);
688*4882a593Smuzhiyun
689*4882a593Smuzhiyun /**
690*4882a593Smuzhiyun * DOC: lru scan roster
691*4882a593Smuzhiyun *
692*4882a593Smuzhiyun * Very often GPUs need to have continuous allocations for a given object. When
693*4882a593Smuzhiyun * evicting objects to make space for a new one it is therefore not most
694*4882a593Smuzhiyun * efficient when we simply start to select all objects from the tail of an LRU
695*4882a593Smuzhiyun * until there's a suitable hole: Especially for big objects or nodes that
696*4882a593Smuzhiyun * otherwise have special allocation constraints there's a good chance we evict
697*4882a593Smuzhiyun * lots of (smaller) objects unnecessarily.
698*4882a593Smuzhiyun *
699*4882a593Smuzhiyun * The DRM range allocator supports this use-case through the scanning
700*4882a593Smuzhiyun * interfaces. First a scan operation needs to be initialized with
701*4882a593Smuzhiyun * drm_mm_scan_init() or drm_mm_scan_init_with_range(). The driver adds
702*4882a593Smuzhiyun * objects to the roster, probably by walking an LRU list, but this can be
703*4882a593Smuzhiyun * freely implemented. Eviction candiates are added using
704*4882a593Smuzhiyun * drm_mm_scan_add_block() until a suitable hole is found or there are no
705*4882a593Smuzhiyun * further evictable objects. Eviction roster metadata is tracked in &struct
706*4882a593Smuzhiyun * drm_mm_scan.
707*4882a593Smuzhiyun *
708*4882a593Smuzhiyun * The driver must walk through all objects again in exactly the reverse
709*4882a593Smuzhiyun * order to restore the allocator state. Note that while the allocator is used
710*4882a593Smuzhiyun * in the scan mode no other operation is allowed.
711*4882a593Smuzhiyun *
712*4882a593Smuzhiyun * Finally the driver evicts all objects selected (drm_mm_scan_remove_block()
713*4882a593Smuzhiyun * reported true) in the scan, and any overlapping nodes after color adjustment
714*4882a593Smuzhiyun * (drm_mm_scan_color_evict()). Adding and removing an object is O(1), and
715*4882a593Smuzhiyun * since freeing a node is also O(1) the overall complexity is
716*4882a593Smuzhiyun * O(scanned_objects). So like the free stack which needs to be walked before a
717*4882a593Smuzhiyun * scan operation even begins this is linear in the number of objects. It
718*4882a593Smuzhiyun * doesn't seem to hurt too badly.
719*4882a593Smuzhiyun */
720*4882a593Smuzhiyun
721*4882a593Smuzhiyun /**
722*4882a593Smuzhiyun * drm_mm_scan_init_with_range - initialize range-restricted lru scanning
723*4882a593Smuzhiyun * @scan: scan state
724*4882a593Smuzhiyun * @mm: drm_mm to scan
725*4882a593Smuzhiyun * @size: size of the allocation
726*4882a593Smuzhiyun * @alignment: alignment of the allocation
727*4882a593Smuzhiyun * @color: opaque tag value to use for the allocation
728*4882a593Smuzhiyun * @start: start of the allowed range for the allocation
729*4882a593Smuzhiyun * @end: end of the allowed range for the allocation
730*4882a593Smuzhiyun * @mode: fine-tune the allocation search and placement
731*4882a593Smuzhiyun *
732*4882a593Smuzhiyun * This simply sets up the scanning routines with the parameters for the desired
733*4882a593Smuzhiyun * hole.
734*4882a593Smuzhiyun *
735*4882a593Smuzhiyun * Warning:
736*4882a593Smuzhiyun * As long as the scan list is non-empty, no other operations than
737*4882a593Smuzhiyun * adding/removing nodes to/from the scan list are allowed.
738*4882a593Smuzhiyun */
drm_mm_scan_init_with_range(struct drm_mm_scan * scan,struct drm_mm * mm,u64 size,u64 alignment,unsigned long color,u64 start,u64 end,enum drm_mm_insert_mode mode)739*4882a593Smuzhiyun void drm_mm_scan_init_with_range(struct drm_mm_scan *scan,
740*4882a593Smuzhiyun struct drm_mm *mm,
741*4882a593Smuzhiyun u64 size,
742*4882a593Smuzhiyun u64 alignment,
743*4882a593Smuzhiyun unsigned long color,
744*4882a593Smuzhiyun u64 start,
745*4882a593Smuzhiyun u64 end,
746*4882a593Smuzhiyun enum drm_mm_insert_mode mode)
747*4882a593Smuzhiyun {
748*4882a593Smuzhiyun DRM_MM_BUG_ON(start >= end);
749*4882a593Smuzhiyun DRM_MM_BUG_ON(!size || size > end - start);
750*4882a593Smuzhiyun DRM_MM_BUG_ON(mm->scan_active);
751*4882a593Smuzhiyun
752*4882a593Smuzhiyun scan->mm = mm;
753*4882a593Smuzhiyun
754*4882a593Smuzhiyun if (alignment <= 1)
755*4882a593Smuzhiyun alignment = 0;
756*4882a593Smuzhiyun
757*4882a593Smuzhiyun scan->color = color;
758*4882a593Smuzhiyun scan->alignment = alignment;
759*4882a593Smuzhiyun scan->remainder_mask = is_power_of_2(alignment) ? alignment - 1 : 0;
760*4882a593Smuzhiyun scan->size = size;
761*4882a593Smuzhiyun scan->mode = mode;
762*4882a593Smuzhiyun
763*4882a593Smuzhiyun DRM_MM_BUG_ON(end <= start);
764*4882a593Smuzhiyun scan->range_start = start;
765*4882a593Smuzhiyun scan->range_end = end;
766*4882a593Smuzhiyun
767*4882a593Smuzhiyun scan->hit_start = U64_MAX;
768*4882a593Smuzhiyun scan->hit_end = 0;
769*4882a593Smuzhiyun }
770*4882a593Smuzhiyun EXPORT_SYMBOL(drm_mm_scan_init_with_range);
771*4882a593Smuzhiyun
772*4882a593Smuzhiyun /**
773*4882a593Smuzhiyun * drm_mm_scan_add_block - add a node to the scan list
774*4882a593Smuzhiyun * @scan: the active drm_mm scanner
775*4882a593Smuzhiyun * @node: drm_mm_node to add
776*4882a593Smuzhiyun *
777*4882a593Smuzhiyun * Add a node to the scan list that might be freed to make space for the desired
778*4882a593Smuzhiyun * hole.
779*4882a593Smuzhiyun *
780*4882a593Smuzhiyun * Returns:
781*4882a593Smuzhiyun * True if a hole has been found, false otherwise.
782*4882a593Smuzhiyun */
drm_mm_scan_add_block(struct drm_mm_scan * scan,struct drm_mm_node * node)783*4882a593Smuzhiyun bool drm_mm_scan_add_block(struct drm_mm_scan *scan,
784*4882a593Smuzhiyun struct drm_mm_node *node)
785*4882a593Smuzhiyun {
786*4882a593Smuzhiyun struct drm_mm *mm = scan->mm;
787*4882a593Smuzhiyun struct drm_mm_node *hole;
788*4882a593Smuzhiyun u64 hole_start, hole_end;
789*4882a593Smuzhiyun u64 col_start, col_end;
790*4882a593Smuzhiyun u64 adj_start, adj_end;
791*4882a593Smuzhiyun
792*4882a593Smuzhiyun DRM_MM_BUG_ON(node->mm != mm);
793*4882a593Smuzhiyun DRM_MM_BUG_ON(!drm_mm_node_allocated(node));
794*4882a593Smuzhiyun DRM_MM_BUG_ON(drm_mm_node_scanned_block(node));
795*4882a593Smuzhiyun __set_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags);
796*4882a593Smuzhiyun mm->scan_active++;
797*4882a593Smuzhiyun
798*4882a593Smuzhiyun /* Remove this block from the node_list so that we enlarge the hole
799*4882a593Smuzhiyun * (distance between the end of our previous node and the start of
800*4882a593Smuzhiyun * or next), without poisoning the link so that we can restore it
801*4882a593Smuzhiyun * later in drm_mm_scan_remove_block().
802*4882a593Smuzhiyun */
803*4882a593Smuzhiyun hole = list_prev_entry(node, node_list);
804*4882a593Smuzhiyun DRM_MM_BUG_ON(list_next_entry(hole, node_list) != node);
805*4882a593Smuzhiyun __list_del_entry(&node->node_list);
806*4882a593Smuzhiyun
807*4882a593Smuzhiyun hole_start = __drm_mm_hole_node_start(hole);
808*4882a593Smuzhiyun hole_end = __drm_mm_hole_node_end(hole);
809*4882a593Smuzhiyun
810*4882a593Smuzhiyun col_start = hole_start;
811*4882a593Smuzhiyun col_end = hole_end;
812*4882a593Smuzhiyun if (mm->color_adjust)
813*4882a593Smuzhiyun mm->color_adjust(hole, scan->color, &col_start, &col_end);
814*4882a593Smuzhiyun
815*4882a593Smuzhiyun adj_start = max(col_start, scan->range_start);
816*4882a593Smuzhiyun adj_end = min(col_end, scan->range_end);
817*4882a593Smuzhiyun if (adj_end <= adj_start || adj_end - adj_start < scan->size)
818*4882a593Smuzhiyun return false;
819*4882a593Smuzhiyun
820*4882a593Smuzhiyun if (scan->mode == DRM_MM_INSERT_HIGH)
821*4882a593Smuzhiyun adj_start = adj_end - scan->size;
822*4882a593Smuzhiyun
823*4882a593Smuzhiyun if (scan->alignment) {
824*4882a593Smuzhiyun u64 rem;
825*4882a593Smuzhiyun
826*4882a593Smuzhiyun if (likely(scan->remainder_mask))
827*4882a593Smuzhiyun rem = adj_start & scan->remainder_mask;
828*4882a593Smuzhiyun else
829*4882a593Smuzhiyun div64_u64_rem(adj_start, scan->alignment, &rem);
830*4882a593Smuzhiyun if (rem) {
831*4882a593Smuzhiyun adj_start -= rem;
832*4882a593Smuzhiyun if (scan->mode != DRM_MM_INSERT_HIGH)
833*4882a593Smuzhiyun adj_start += scan->alignment;
834*4882a593Smuzhiyun if (adj_start < max(col_start, scan->range_start) ||
835*4882a593Smuzhiyun min(col_end, scan->range_end) - adj_start < scan->size)
836*4882a593Smuzhiyun return false;
837*4882a593Smuzhiyun
838*4882a593Smuzhiyun if (adj_end <= adj_start ||
839*4882a593Smuzhiyun adj_end - adj_start < scan->size)
840*4882a593Smuzhiyun return false;
841*4882a593Smuzhiyun }
842*4882a593Smuzhiyun }
843*4882a593Smuzhiyun
844*4882a593Smuzhiyun scan->hit_start = adj_start;
845*4882a593Smuzhiyun scan->hit_end = adj_start + scan->size;
846*4882a593Smuzhiyun
847*4882a593Smuzhiyun DRM_MM_BUG_ON(scan->hit_start >= scan->hit_end);
848*4882a593Smuzhiyun DRM_MM_BUG_ON(scan->hit_start < hole_start);
849*4882a593Smuzhiyun DRM_MM_BUG_ON(scan->hit_end > hole_end);
850*4882a593Smuzhiyun
851*4882a593Smuzhiyun return true;
852*4882a593Smuzhiyun }
853*4882a593Smuzhiyun EXPORT_SYMBOL(drm_mm_scan_add_block);
854*4882a593Smuzhiyun
855*4882a593Smuzhiyun /**
856*4882a593Smuzhiyun * drm_mm_scan_remove_block - remove a node from the scan list
857*4882a593Smuzhiyun * @scan: the active drm_mm scanner
858*4882a593Smuzhiyun * @node: drm_mm_node to remove
859*4882a593Smuzhiyun *
860*4882a593Smuzhiyun * Nodes **must** be removed in exactly the reverse order from the scan list as
861*4882a593Smuzhiyun * they have been added (e.g. using list_add() as they are added and then
862*4882a593Smuzhiyun * list_for_each() over that eviction list to remove), otherwise the internal
863*4882a593Smuzhiyun * state of the memory manager will be corrupted.
864*4882a593Smuzhiyun *
865*4882a593Smuzhiyun * When the scan list is empty, the selected memory nodes can be freed. An
866*4882a593Smuzhiyun * immediately following drm_mm_insert_node_in_range_generic() or one of the
867*4882a593Smuzhiyun * simpler versions of that function with !DRM_MM_SEARCH_BEST will then return
868*4882a593Smuzhiyun * the just freed block (because it's at the top of the free_stack list).
869*4882a593Smuzhiyun *
870*4882a593Smuzhiyun * Returns:
871*4882a593Smuzhiyun * True if this block should be evicted, false otherwise. Will always
872*4882a593Smuzhiyun * return false when no hole has been found.
873*4882a593Smuzhiyun */
drm_mm_scan_remove_block(struct drm_mm_scan * scan,struct drm_mm_node * node)874*4882a593Smuzhiyun bool drm_mm_scan_remove_block(struct drm_mm_scan *scan,
875*4882a593Smuzhiyun struct drm_mm_node *node)
876*4882a593Smuzhiyun {
877*4882a593Smuzhiyun struct drm_mm_node *prev_node;
878*4882a593Smuzhiyun
879*4882a593Smuzhiyun DRM_MM_BUG_ON(node->mm != scan->mm);
880*4882a593Smuzhiyun DRM_MM_BUG_ON(!drm_mm_node_scanned_block(node));
881*4882a593Smuzhiyun __clear_bit(DRM_MM_NODE_SCANNED_BIT, &node->flags);
882*4882a593Smuzhiyun
883*4882a593Smuzhiyun DRM_MM_BUG_ON(!node->mm->scan_active);
884*4882a593Smuzhiyun node->mm->scan_active--;
885*4882a593Smuzhiyun
886*4882a593Smuzhiyun /* During drm_mm_scan_add_block() we decoupled this node leaving
887*4882a593Smuzhiyun * its pointers intact. Now that the caller is walking back along
888*4882a593Smuzhiyun * the eviction list we can restore this block into its rightful
889*4882a593Smuzhiyun * place on the full node_list. To confirm that the caller is walking
890*4882a593Smuzhiyun * backwards correctly we check that prev_node->next == node->next,
891*4882a593Smuzhiyun * i.e. both believe the same node should be on the other side of the
892*4882a593Smuzhiyun * hole.
893*4882a593Smuzhiyun */
894*4882a593Smuzhiyun prev_node = list_prev_entry(node, node_list);
895*4882a593Smuzhiyun DRM_MM_BUG_ON(list_next_entry(prev_node, node_list) !=
896*4882a593Smuzhiyun list_next_entry(node, node_list));
897*4882a593Smuzhiyun list_add(&node->node_list, &prev_node->node_list);
898*4882a593Smuzhiyun
899*4882a593Smuzhiyun return (node->start + node->size > scan->hit_start &&
900*4882a593Smuzhiyun node->start < scan->hit_end);
901*4882a593Smuzhiyun }
902*4882a593Smuzhiyun EXPORT_SYMBOL(drm_mm_scan_remove_block);
903*4882a593Smuzhiyun
904*4882a593Smuzhiyun /**
905*4882a593Smuzhiyun * drm_mm_scan_color_evict - evict overlapping nodes on either side of hole
906*4882a593Smuzhiyun * @scan: drm_mm scan with target hole
907*4882a593Smuzhiyun *
908*4882a593Smuzhiyun * After completing an eviction scan and removing the selected nodes, we may
909*4882a593Smuzhiyun * need to remove a few more nodes from either side of the target hole if
910*4882a593Smuzhiyun * mm.color_adjust is being used.
911*4882a593Smuzhiyun *
912*4882a593Smuzhiyun * Returns:
913*4882a593Smuzhiyun * A node to evict, or NULL if there are no overlapping nodes.
914*4882a593Smuzhiyun */
drm_mm_scan_color_evict(struct drm_mm_scan * scan)915*4882a593Smuzhiyun struct drm_mm_node *drm_mm_scan_color_evict(struct drm_mm_scan *scan)
916*4882a593Smuzhiyun {
917*4882a593Smuzhiyun struct drm_mm *mm = scan->mm;
918*4882a593Smuzhiyun struct drm_mm_node *hole;
919*4882a593Smuzhiyun u64 hole_start, hole_end;
920*4882a593Smuzhiyun
921*4882a593Smuzhiyun DRM_MM_BUG_ON(list_empty(&mm->hole_stack));
922*4882a593Smuzhiyun
923*4882a593Smuzhiyun if (!mm->color_adjust)
924*4882a593Smuzhiyun return NULL;
925*4882a593Smuzhiyun
926*4882a593Smuzhiyun /*
927*4882a593Smuzhiyun * The hole found during scanning should ideally be the first element
928*4882a593Smuzhiyun * in the hole_stack list, but due to side-effects in the driver it
929*4882a593Smuzhiyun * may not be.
930*4882a593Smuzhiyun */
931*4882a593Smuzhiyun list_for_each_entry(hole, &mm->hole_stack, hole_stack) {
932*4882a593Smuzhiyun hole_start = __drm_mm_hole_node_start(hole);
933*4882a593Smuzhiyun hole_end = hole_start + hole->hole_size;
934*4882a593Smuzhiyun
935*4882a593Smuzhiyun if (hole_start <= scan->hit_start &&
936*4882a593Smuzhiyun hole_end >= scan->hit_end)
937*4882a593Smuzhiyun break;
938*4882a593Smuzhiyun }
939*4882a593Smuzhiyun
940*4882a593Smuzhiyun /* We should only be called after we found the hole previously */
941*4882a593Smuzhiyun DRM_MM_BUG_ON(&hole->hole_stack == &mm->hole_stack);
942*4882a593Smuzhiyun if (unlikely(&hole->hole_stack == &mm->hole_stack))
943*4882a593Smuzhiyun return NULL;
944*4882a593Smuzhiyun
945*4882a593Smuzhiyun DRM_MM_BUG_ON(hole_start > scan->hit_start);
946*4882a593Smuzhiyun DRM_MM_BUG_ON(hole_end < scan->hit_end);
947*4882a593Smuzhiyun
948*4882a593Smuzhiyun mm->color_adjust(hole, scan->color, &hole_start, &hole_end);
949*4882a593Smuzhiyun if (hole_start > scan->hit_start)
950*4882a593Smuzhiyun return hole;
951*4882a593Smuzhiyun if (hole_end < scan->hit_end)
952*4882a593Smuzhiyun return list_next_entry(hole, node_list);
953*4882a593Smuzhiyun
954*4882a593Smuzhiyun return NULL;
955*4882a593Smuzhiyun }
956*4882a593Smuzhiyun EXPORT_SYMBOL(drm_mm_scan_color_evict);
957*4882a593Smuzhiyun
958*4882a593Smuzhiyun /**
959*4882a593Smuzhiyun * drm_mm_init - initialize a drm-mm allocator
960*4882a593Smuzhiyun * @mm: the drm_mm structure to initialize
961*4882a593Smuzhiyun * @start: start of the range managed by @mm
962*4882a593Smuzhiyun * @size: end of the range managed by @mm
963*4882a593Smuzhiyun *
964*4882a593Smuzhiyun * Note that @mm must be cleared to 0 before calling this function.
965*4882a593Smuzhiyun */
drm_mm_init(struct drm_mm * mm,u64 start,u64 size)966*4882a593Smuzhiyun void drm_mm_init(struct drm_mm *mm, u64 start, u64 size)
967*4882a593Smuzhiyun {
968*4882a593Smuzhiyun DRM_MM_BUG_ON(start + size <= start);
969*4882a593Smuzhiyun
970*4882a593Smuzhiyun mm->color_adjust = NULL;
971*4882a593Smuzhiyun
972*4882a593Smuzhiyun INIT_LIST_HEAD(&mm->hole_stack);
973*4882a593Smuzhiyun mm->interval_tree = RB_ROOT_CACHED;
974*4882a593Smuzhiyun mm->holes_size = RB_ROOT_CACHED;
975*4882a593Smuzhiyun mm->holes_addr = RB_ROOT;
976*4882a593Smuzhiyun
977*4882a593Smuzhiyun /* Clever trick to avoid a special case in the free hole tracking. */
978*4882a593Smuzhiyun INIT_LIST_HEAD(&mm->head_node.node_list);
979*4882a593Smuzhiyun mm->head_node.flags = 0;
980*4882a593Smuzhiyun mm->head_node.mm = mm;
981*4882a593Smuzhiyun mm->head_node.start = start + size;
982*4882a593Smuzhiyun mm->head_node.size = -size;
983*4882a593Smuzhiyun add_hole(&mm->head_node);
984*4882a593Smuzhiyun
985*4882a593Smuzhiyun mm->scan_active = 0;
986*4882a593Smuzhiyun }
987*4882a593Smuzhiyun EXPORT_SYMBOL(drm_mm_init);
988*4882a593Smuzhiyun
989*4882a593Smuzhiyun /**
990*4882a593Smuzhiyun * drm_mm_takedown - clean up a drm_mm allocator
991*4882a593Smuzhiyun * @mm: drm_mm allocator to clean up
992*4882a593Smuzhiyun *
993*4882a593Smuzhiyun * Note that it is a bug to call this function on an allocator which is not
994*4882a593Smuzhiyun * clean.
995*4882a593Smuzhiyun */
drm_mm_takedown(struct drm_mm * mm)996*4882a593Smuzhiyun void drm_mm_takedown(struct drm_mm *mm)
997*4882a593Smuzhiyun {
998*4882a593Smuzhiyun if (WARN(!drm_mm_clean(mm),
999*4882a593Smuzhiyun "Memory manager not clean during takedown.\n"))
1000*4882a593Smuzhiyun show_leaks(mm);
1001*4882a593Smuzhiyun }
1002*4882a593Smuzhiyun EXPORT_SYMBOL(drm_mm_takedown);
1003*4882a593Smuzhiyun
drm_mm_dump_hole(struct drm_printer * p,const struct drm_mm_node * entry)1004*4882a593Smuzhiyun static u64 drm_mm_dump_hole(struct drm_printer *p, const struct drm_mm_node *entry)
1005*4882a593Smuzhiyun {
1006*4882a593Smuzhiyun u64 start, size;
1007*4882a593Smuzhiyun
1008*4882a593Smuzhiyun size = entry->hole_size;
1009*4882a593Smuzhiyun if (size) {
1010*4882a593Smuzhiyun start = drm_mm_hole_node_start(entry);
1011*4882a593Smuzhiyun drm_printf(p, "%#018llx-%#018llx: %llu: free\n",
1012*4882a593Smuzhiyun start, start + size, size);
1013*4882a593Smuzhiyun }
1014*4882a593Smuzhiyun
1015*4882a593Smuzhiyun return size;
1016*4882a593Smuzhiyun }
1017*4882a593Smuzhiyun /**
1018*4882a593Smuzhiyun * drm_mm_print - print allocator state
1019*4882a593Smuzhiyun * @mm: drm_mm allocator to print
1020*4882a593Smuzhiyun * @p: DRM printer to use
1021*4882a593Smuzhiyun */
drm_mm_print(const struct drm_mm * mm,struct drm_printer * p)1022*4882a593Smuzhiyun void drm_mm_print(const struct drm_mm *mm, struct drm_printer *p)
1023*4882a593Smuzhiyun {
1024*4882a593Smuzhiyun const struct drm_mm_node *entry;
1025*4882a593Smuzhiyun u64 total_used = 0, total_free = 0, total = 0;
1026*4882a593Smuzhiyun
1027*4882a593Smuzhiyun total_free += drm_mm_dump_hole(p, &mm->head_node);
1028*4882a593Smuzhiyun
1029*4882a593Smuzhiyun drm_mm_for_each_node(entry, mm) {
1030*4882a593Smuzhiyun drm_printf(p, "%#018llx-%#018llx: %llu: used\n", entry->start,
1031*4882a593Smuzhiyun entry->start + entry->size, entry->size);
1032*4882a593Smuzhiyun total_used += entry->size;
1033*4882a593Smuzhiyun total_free += drm_mm_dump_hole(p, entry);
1034*4882a593Smuzhiyun }
1035*4882a593Smuzhiyun total = total_free + total_used;
1036*4882a593Smuzhiyun
1037*4882a593Smuzhiyun drm_printf(p, "total: %llu, used %llu free %llu\n", total,
1038*4882a593Smuzhiyun total_used, total_free);
1039*4882a593Smuzhiyun }
1040*4882a593Smuzhiyun EXPORT_SYMBOL(drm_mm_print);
1041