drm_mm.c revision 1.3.28.1 1 1.3.28.1 pgoyette /* $NetBSD: drm_mm.c,v 1.3.28.1 2018/09/06 06:56:09 pgoyette Exp $ */
2 1.3.28.1 pgoyette
3 1.1 riastrad /**************************************************************************
4 1.1 riastrad *
5 1.1 riastrad * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
6 1.1 riastrad * All Rights Reserved.
7 1.1 riastrad *
8 1.1 riastrad * Permission is hereby granted, free of charge, to any person obtaining a
9 1.1 riastrad * copy of this software and associated documentation files (the
10 1.1 riastrad * "Software"), to deal in the Software without restriction, including
11 1.1 riastrad * without limitation the rights to use, copy, modify, merge, publish,
12 1.1 riastrad * distribute, sub license, and/or sell copies of the Software, and to
13 1.1 riastrad * permit persons to whom the Software is furnished to do so, subject to
14 1.1 riastrad * the following conditions:
15 1.1 riastrad *
16 1.1 riastrad * The above copyright notice and this permission notice (including the
17 1.1 riastrad * next paragraph) shall be included in all copies or substantial portions
18 1.1 riastrad * of the Software.
19 1.1 riastrad *
20 1.1 riastrad * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 1.1 riastrad * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 1.1 riastrad * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
23 1.1 riastrad * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
24 1.1 riastrad * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
25 1.1 riastrad * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
26 1.1 riastrad * USE OR OTHER DEALINGS IN THE SOFTWARE.
27 1.1 riastrad *
28 1.1 riastrad *
29 1.1 riastrad **************************************************************************/
30 1.1 riastrad
31 1.1 riastrad /*
32 1.1 riastrad * Generic simple memory manager implementation. Intended to be used as a base
33 1.1 riastrad * class implementation for more advanced memory managers.
34 1.1 riastrad *
35 1.1 riastrad * Note that the algorithm used is quite simple and there might be substantial
36 1.1 riastrad * performance gains if a smarter free list is implemented. Currently it is just an
37 1.1 riastrad * unordered stack of free regions. This could easily be improved if an RB-tree
38 1.1 riastrad * is used instead. At least if we expect heavy fragmentation.
39 1.1 riastrad *
40 1.1 riastrad * Aligned allocations can also see improvement.
41 1.1 riastrad *
42 1.1 riastrad * Authors:
43 1.1 riastrad * Thomas Hellstrm <thomas-at-tungstengraphics-dot-com>
44 1.1 riastrad */
45 1.1 riastrad
46 1.3.28.1 pgoyette #include <sys/cdefs.h>
47 1.3.28.1 pgoyette __KERNEL_RCSID(0, "$NetBSD: drm_mm.c,v 1.3.28.1 2018/09/06 06:56:09 pgoyette Exp $");
48 1.3.28.1 pgoyette
49 1.1 riastrad #include <drm/drmP.h>
50 1.1 riastrad #include <drm/drm_mm.h>
51 1.1 riastrad #include <linux/slab.h>
52 1.1 riastrad #include <linux/seq_file.h>
53 1.1 riastrad #include <linux/export.h>
54 1.2 riastrad #include <linux/printk.h>
55 1.2 riastrad #include <asm/bug.h>
56 1.3.28.1 pgoyette #include <asm/div64.h>
57 1.1 riastrad
58 1.3 riastrad /**
59 1.3 riastrad * DOC: Overview
60 1.3 riastrad *
61 1.3 riastrad * drm_mm provides a simple range allocator. The drivers are free to use the
62 1.3 riastrad * resource allocator from the linux core if it suits them, the upside of drm_mm
63 1.3 riastrad * is that it's in the DRM core. Which means that it's easier to extend for
64 1.3 riastrad * some of the crazier special purpose needs of gpus.
65 1.3 riastrad *
66 1.3 riastrad * The main data struct is &drm_mm, allocations are tracked in &drm_mm_node.
67 1.3 riastrad * Drivers are free to embed either of them into their own suitable
68 1.3 riastrad * datastructures. drm_mm itself will not do any allocations of its own, so if
69 1.3 riastrad * drivers choose not to embed nodes they need to still allocate them
70 1.3 riastrad * themselves.
71 1.3 riastrad *
72 1.3 riastrad * The range allocator also supports reservation of preallocated blocks. This is
73 1.3 riastrad * useful for taking over initial mode setting configurations from the firmware,
74 1.3 riastrad * where an object needs to be created which exactly matches the firmware's
75 1.3 riastrad * scanout target. As long as the range is still free it can be inserted anytime
76 1.3 riastrad * after the allocator is initialized, which helps with avoiding looped
77 1.3 riastrad * depencies in the driver load sequence.
78 1.3 riastrad *
79 1.3 riastrad * drm_mm maintains a stack of most recently freed holes, which of all
80 1.3 riastrad * simplistic datastructures seems to be a fairly decent approach to clustering
81 1.3 riastrad * allocations and avoiding too much fragmentation. This means free space
82 1.3 riastrad * searches are O(num_holes). Given that all the fancy features drm_mm supports
83 1.3 riastrad * something better would be fairly complex and since gfx thrashing is a fairly
84 1.3 riastrad * steep cliff not a real concern. Removing a node again is O(1).
85 1.3 riastrad *
86 1.3 riastrad * drm_mm supports a few features: Alignment and range restrictions can be
87 1.3 riastrad * supplied. Further more every &drm_mm_node has a color value (which is just an
88 1.3 riastrad * opaqua unsigned long) which in conjunction with a driver callback can be used
89 1.3 riastrad * to implement sophisticated placement restrictions. The i915 DRM driver uses
90 1.3 riastrad * this to implement guard pages between incompatible caching domains in the
91 1.3 riastrad * graphics TT.
92 1.3 riastrad *
93 1.3 riastrad * Two behaviors are supported for searching and allocating: bottom-up and top-down.
94 1.3 riastrad * The default is bottom-up. Top-down allocation can be used if the memory area
95 1.3 riastrad * has different restrictions, or just to reduce fragmentation.
96 1.1 riastrad *
97 1.3 riastrad * Finally iteration helpers to walk all nodes and all holes are provided as are
98 1.3 riastrad * some basic allocator dumpers for debugging.
99 1.1 riastrad */
100 1.1 riastrad
101 1.3 riastrad static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
102 1.3.28.1 pgoyette u64 size,
103 1.3 riastrad unsigned alignment,
104 1.3 riastrad unsigned long color,
105 1.3 riastrad enum drm_mm_search_flags flags);
106 1.3 riastrad static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
107 1.3.28.1 pgoyette u64 size,
108 1.3 riastrad unsigned alignment,
109 1.3 riastrad unsigned long color,
110 1.3.28.1 pgoyette u64 start,
111 1.3.28.1 pgoyette u64 end,
112 1.3 riastrad enum drm_mm_search_flags flags);
113 1.1 riastrad
114 1.1 riastrad static void drm_mm_insert_helper(struct drm_mm_node *hole_node,
115 1.1 riastrad struct drm_mm_node *node,
116 1.3.28.1 pgoyette u64 size, unsigned alignment,
117 1.3 riastrad unsigned long color,
118 1.3 riastrad enum drm_mm_allocator_flags flags)
119 1.1 riastrad {
120 1.1 riastrad struct drm_mm *mm = hole_node->mm;
121 1.3.28.1 pgoyette u64 hole_start = drm_mm_hole_node_start(hole_node);
122 1.3.28.1 pgoyette u64 hole_end = drm_mm_hole_node_end(hole_node);
123 1.3.28.1 pgoyette u64 adj_start = hole_start;
124 1.3.28.1 pgoyette u64 adj_end = hole_end;
125 1.1 riastrad
126 1.3 riastrad BUG_ON(node->allocated);
127 1.1 riastrad
128 1.1 riastrad if (mm->color_adjust)
129 1.1 riastrad mm->color_adjust(hole_node, color, &adj_start, &adj_end);
130 1.1 riastrad
131 1.3 riastrad if (flags & DRM_MM_CREATE_TOP)
132 1.3 riastrad adj_start = adj_end - size;
133 1.3 riastrad
134 1.1 riastrad if (alignment) {
135 1.3.28.1 pgoyette u64 tmp = adj_start;
136 1.3.28.1 pgoyette unsigned rem;
137 1.3.28.1 pgoyette
138 1.3.28.1 pgoyette rem = do_div(tmp, alignment);
139 1.3.28.1 pgoyette if (rem) {
140 1.3 riastrad if (flags & DRM_MM_CREATE_TOP)
141 1.3.28.1 pgoyette adj_start -= rem;
142 1.3 riastrad else
143 1.3.28.1 pgoyette adj_start += alignment - rem;
144 1.3 riastrad }
145 1.1 riastrad }
146 1.1 riastrad
147 1.3 riastrad BUG_ON(adj_start < hole_start);
148 1.3 riastrad BUG_ON(adj_end > hole_end);
149 1.3 riastrad
150 1.1 riastrad if (adj_start == hole_start) {
151 1.1 riastrad hole_node->hole_follows = 0;
152 1.1 riastrad list_del(&hole_node->hole_stack);
153 1.1 riastrad }
154 1.1 riastrad
155 1.1 riastrad node->start = adj_start;
156 1.1 riastrad node->size = size;
157 1.1 riastrad node->mm = mm;
158 1.1 riastrad node->color = color;
159 1.1 riastrad node->allocated = 1;
160 1.1 riastrad
161 1.1 riastrad INIT_LIST_HEAD(&node->hole_stack);
162 1.1 riastrad list_add(&node->node_list, &hole_node->node_list);
163 1.1 riastrad
164 1.1 riastrad BUG_ON(node->start + node->size > adj_end);
165 1.1 riastrad
166 1.1 riastrad node->hole_follows = 0;
167 1.3 riastrad if (__drm_mm_hole_node_start(node) < hole_end) {
168 1.1 riastrad list_add(&node->hole_stack, &mm->hole_stack);
169 1.1 riastrad node->hole_follows = 1;
170 1.1 riastrad }
171 1.1 riastrad }
172 1.1 riastrad
173 1.3 riastrad /**
174 1.3 riastrad * drm_mm_reserve_node - insert an pre-initialized node
175 1.3 riastrad * @mm: drm_mm allocator to insert @node into
176 1.3 riastrad * @node: drm_mm_node to insert
177 1.3 riastrad *
178 1.3 riastrad * This functions inserts an already set-up drm_mm_node into the allocator,
179 1.3 riastrad * meaning that start, size and color must be set by the caller. This is useful
180 1.3 riastrad * to initialize the allocator with preallocated objects which must be set-up
181 1.3 riastrad * before the range allocator can be set-up, e.g. when taking over a firmware
182 1.3 riastrad * framebuffer.
183 1.3 riastrad *
184 1.3 riastrad * Returns:
185 1.3 riastrad * 0 on success, -ENOSPC if there's no hole where @node is.
186 1.3 riastrad */
187 1.3 riastrad int drm_mm_reserve_node(struct drm_mm *mm, struct drm_mm_node *node)
188 1.1 riastrad {
189 1.3 riastrad struct drm_mm_node *hole;
190 1.3.28.1 pgoyette u64 end = node->start + node->size;
191 1.3.28.1 pgoyette u64 hole_start;
192 1.3.28.1 pgoyette u64 hole_end;
193 1.3 riastrad
194 1.3 riastrad BUG_ON(node == NULL);
195 1.3 riastrad
196 1.3 riastrad /* Find the relevant hole to add our node to */
197 1.3 riastrad drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
198 1.3 riastrad if (hole_start > node->start || hole_end < end)
199 1.3 riastrad continue;
200 1.1 riastrad
201 1.3 riastrad node->mm = mm;
202 1.3 riastrad node->allocated = 1;
203 1.1 riastrad
204 1.3 riastrad INIT_LIST_HEAD(&node->hole_stack);
205 1.3 riastrad list_add(&node->node_list, &hole->node_list);
206 1.1 riastrad
207 1.3 riastrad if (node->start == hole_start) {
208 1.3 riastrad hole->hole_follows = 0;
209 1.3 riastrad list_del_init(&hole->hole_stack);
210 1.3 riastrad }
211 1.3 riastrad
212 1.3 riastrad node->hole_follows = 0;
213 1.3 riastrad if (end != hole_end) {
214 1.3 riastrad list_add(&node->hole_stack, &mm->hole_stack);
215 1.3 riastrad node->hole_follows = 1;
216 1.3 riastrad }
217 1.3 riastrad
218 1.3 riastrad return 0;
219 1.3 riastrad }
220 1.3 riastrad
221 1.3 riastrad return -ENOSPC;
222 1.1 riastrad }
223 1.3 riastrad EXPORT_SYMBOL(drm_mm_reserve_node);
224 1.1 riastrad
225 1.1 riastrad /**
226 1.3 riastrad * drm_mm_insert_node_generic - search for space and insert @node
227 1.3 riastrad * @mm: drm_mm to allocate from
228 1.3 riastrad * @node: preallocate node to insert
229 1.3 riastrad * @size: size of the allocation
230 1.3 riastrad * @alignment: alignment of the allocation
231 1.3 riastrad * @color: opaque tag value to use for this node
232 1.3 riastrad * @sflags: flags to fine-tune the allocation search
233 1.3 riastrad * @aflags: flags to fine-tune the allocation behavior
234 1.3 riastrad *
235 1.3 riastrad * The preallocated node must be cleared to 0.
236 1.3 riastrad *
237 1.3 riastrad * Returns:
238 1.3 riastrad * 0 on success, -ENOSPC if there's no suitable hole.
239 1.1 riastrad */
240 1.1 riastrad int drm_mm_insert_node_generic(struct drm_mm *mm, struct drm_mm_node *node,
241 1.3.28.1 pgoyette u64 size, unsigned alignment,
242 1.3 riastrad unsigned long color,
243 1.3 riastrad enum drm_mm_search_flags sflags,
244 1.3 riastrad enum drm_mm_allocator_flags aflags)
245 1.1 riastrad {
246 1.1 riastrad struct drm_mm_node *hole_node;
247 1.1 riastrad
248 1.1 riastrad hole_node = drm_mm_search_free_generic(mm, size, alignment,
249 1.3 riastrad color, sflags);
250 1.1 riastrad if (!hole_node)
251 1.1 riastrad return -ENOSPC;
252 1.1 riastrad
253 1.3 riastrad drm_mm_insert_helper(hole_node, node, size, alignment, color, aflags);
254 1.1 riastrad return 0;
255 1.1 riastrad }
256 1.1 riastrad EXPORT_SYMBOL(drm_mm_insert_node_generic);
257 1.1 riastrad
258 1.1 riastrad static void drm_mm_insert_helper_range(struct drm_mm_node *hole_node,
259 1.1 riastrad struct drm_mm_node *node,
260 1.3.28.1 pgoyette u64 size, unsigned alignment,
261 1.1 riastrad unsigned long color,
262 1.3.28.1 pgoyette u64 start, u64 end,
263 1.3 riastrad enum drm_mm_allocator_flags flags)
264 1.1 riastrad {
265 1.1 riastrad struct drm_mm *mm = hole_node->mm;
266 1.3.28.1 pgoyette u64 hole_start = drm_mm_hole_node_start(hole_node);
267 1.3.28.1 pgoyette u64 hole_end = drm_mm_hole_node_end(hole_node);
268 1.3.28.1 pgoyette u64 adj_start = hole_start;
269 1.3.28.1 pgoyette u64 adj_end = hole_end;
270 1.1 riastrad
271 1.1 riastrad BUG_ON(!hole_node->hole_follows || node->allocated);
272 1.1 riastrad
273 1.3.28.1 pgoyette if (mm->color_adjust)
274 1.3.28.1 pgoyette mm->color_adjust(hole_node, color, &adj_start, &adj_end);
275 1.3.28.1 pgoyette
276 1.3.28.1 pgoyette adj_start = max(adj_start, start);
277 1.3.28.1 pgoyette adj_end = min(adj_end, end);
278 1.1 riastrad
279 1.3 riastrad if (flags & DRM_MM_CREATE_TOP)
280 1.3 riastrad adj_start = adj_end - size;
281 1.3 riastrad
282 1.1 riastrad if (alignment) {
283 1.3.28.1 pgoyette u64 tmp = adj_start;
284 1.3.28.1 pgoyette unsigned rem;
285 1.3.28.1 pgoyette
286 1.3.28.1 pgoyette rem = do_div(tmp, alignment);
287 1.3.28.1 pgoyette if (rem) {
288 1.3 riastrad if (flags & DRM_MM_CREATE_TOP)
289 1.3.28.1 pgoyette adj_start -= rem;
290 1.3 riastrad else
291 1.3.28.1 pgoyette adj_start += alignment - rem;
292 1.3 riastrad }
293 1.1 riastrad }
294 1.1 riastrad
295 1.1 riastrad if (adj_start == hole_start) {
296 1.1 riastrad hole_node->hole_follows = 0;
297 1.1 riastrad list_del(&hole_node->hole_stack);
298 1.1 riastrad }
299 1.1 riastrad
300 1.1 riastrad node->start = adj_start;
301 1.1 riastrad node->size = size;
302 1.1 riastrad node->mm = mm;
303 1.1 riastrad node->color = color;
304 1.1 riastrad node->allocated = 1;
305 1.1 riastrad
306 1.1 riastrad INIT_LIST_HEAD(&node->hole_stack);
307 1.1 riastrad list_add(&node->node_list, &hole_node->node_list);
308 1.1 riastrad
309 1.3 riastrad BUG_ON(node->start < start);
310 1.3 riastrad BUG_ON(node->start < adj_start);
311 1.1 riastrad BUG_ON(node->start + node->size > adj_end);
312 1.1 riastrad BUG_ON(node->start + node->size > end);
313 1.1 riastrad
314 1.1 riastrad node->hole_follows = 0;
315 1.3 riastrad if (__drm_mm_hole_node_start(node) < hole_end) {
316 1.1 riastrad list_add(&node->hole_stack, &mm->hole_stack);
317 1.1 riastrad node->hole_follows = 1;
318 1.1 riastrad }
319 1.1 riastrad }
320 1.1 riastrad
321 1.1 riastrad /**
322 1.3 riastrad * drm_mm_insert_node_in_range_generic - ranged search for space and insert @node
323 1.3 riastrad * @mm: drm_mm to allocate from
324 1.3 riastrad * @node: preallocate node to insert
325 1.3 riastrad * @size: size of the allocation
326 1.3 riastrad * @alignment: alignment of the allocation
327 1.3 riastrad * @color: opaque tag value to use for this node
328 1.3 riastrad * @start: start of the allowed range for this node
329 1.3 riastrad * @end: end of the allowed range for this node
330 1.3 riastrad * @sflags: flags to fine-tune the allocation search
331 1.3 riastrad * @aflags: flags to fine-tune the allocation behavior
332 1.3 riastrad *
333 1.3 riastrad * The preallocated node must be cleared to 0.
334 1.3 riastrad *
335 1.3 riastrad * Returns:
336 1.3 riastrad * 0 on success, -ENOSPC if there's no suitable hole.
337 1.1 riastrad */
338 1.1 riastrad int drm_mm_insert_node_in_range_generic(struct drm_mm *mm, struct drm_mm_node *node,
339 1.3.28.1 pgoyette u64 size, unsigned alignment,
340 1.3 riastrad unsigned long color,
341 1.3.28.1 pgoyette u64 start, u64 end,
342 1.3 riastrad enum drm_mm_search_flags sflags,
343 1.3 riastrad enum drm_mm_allocator_flags aflags)
344 1.1 riastrad {
345 1.1 riastrad struct drm_mm_node *hole_node;
346 1.1 riastrad
347 1.1 riastrad hole_node = drm_mm_search_free_in_range_generic(mm,
348 1.1 riastrad size, alignment, color,
349 1.3 riastrad start, end, sflags);
350 1.1 riastrad if (!hole_node)
351 1.1 riastrad return -ENOSPC;
352 1.1 riastrad
353 1.1 riastrad drm_mm_insert_helper_range(hole_node, node,
354 1.1 riastrad size, alignment, color,
355 1.3 riastrad start, end, aflags);
356 1.1 riastrad return 0;
357 1.1 riastrad }
358 1.1 riastrad EXPORT_SYMBOL(drm_mm_insert_node_in_range_generic);
359 1.1 riastrad
360 1.1 riastrad /**
361 1.3 riastrad * drm_mm_remove_node - Remove a memory node from the allocator.
362 1.3 riastrad * @node: drm_mm_node to remove
363 1.3 riastrad *
364 1.3 riastrad * This just removes a node from its drm_mm allocator. The node does not need to
365 1.3 riastrad * be cleared again before it can be re-inserted into this or any other drm_mm
366 1.3 riastrad * allocator. It is a bug to call this function on a un-allocated node.
367 1.1 riastrad */
368 1.1 riastrad void drm_mm_remove_node(struct drm_mm_node *node)
369 1.1 riastrad {
370 1.1 riastrad struct drm_mm *mm = node->mm;
371 1.1 riastrad struct drm_mm_node *prev_node;
372 1.1 riastrad
373 1.3 riastrad if (WARN_ON(!node->allocated))
374 1.3 riastrad return;
375 1.3 riastrad
376 1.1 riastrad BUG_ON(node->scanned_block || node->scanned_prev_free
377 1.1 riastrad || node->scanned_next_free);
378 1.1 riastrad
379 1.1 riastrad prev_node =
380 1.1 riastrad list_entry(node->node_list.prev, struct drm_mm_node, node_list);
381 1.1 riastrad
382 1.1 riastrad if (node->hole_follows) {
383 1.3 riastrad BUG_ON(__drm_mm_hole_node_start(node) ==
384 1.3 riastrad __drm_mm_hole_node_end(node));
385 1.1 riastrad list_del(&node->hole_stack);
386 1.1 riastrad } else
387 1.3 riastrad BUG_ON(__drm_mm_hole_node_start(node) !=
388 1.3 riastrad __drm_mm_hole_node_end(node));
389 1.3 riastrad
390 1.1 riastrad
391 1.1 riastrad if (!prev_node->hole_follows) {
392 1.1 riastrad prev_node->hole_follows = 1;
393 1.1 riastrad list_add(&prev_node->hole_stack, &mm->hole_stack);
394 1.1 riastrad } else
395 1.1 riastrad list_move(&prev_node->hole_stack, &mm->hole_stack);
396 1.1 riastrad
397 1.1 riastrad list_del(&node->node_list);
398 1.1 riastrad node->allocated = 0;
399 1.1 riastrad }
400 1.1 riastrad EXPORT_SYMBOL(drm_mm_remove_node);
401 1.1 riastrad
402 1.3.28.1 pgoyette static int check_free_hole(u64 start, u64 end, u64 size, unsigned alignment)
403 1.1 riastrad {
404 1.1 riastrad if (end - start < size)
405 1.1 riastrad return 0;
406 1.1 riastrad
407 1.1 riastrad if (alignment) {
408 1.3.28.1 pgoyette u64 tmp = start;
409 1.3.28.1 pgoyette unsigned rem;
410 1.3.28.1 pgoyette
411 1.3.28.1 pgoyette rem = do_div(tmp, alignment);
412 1.3.28.1 pgoyette if (rem)
413 1.3.28.1 pgoyette start += alignment - rem;
414 1.1 riastrad }
415 1.1 riastrad
416 1.1 riastrad return end >= start + size;
417 1.1 riastrad }
418 1.1 riastrad
419 1.3 riastrad static struct drm_mm_node *drm_mm_search_free_generic(const struct drm_mm *mm,
420 1.3.28.1 pgoyette u64 size,
421 1.3 riastrad unsigned alignment,
422 1.3 riastrad unsigned long color,
423 1.3 riastrad enum drm_mm_search_flags flags)
424 1.1 riastrad {
425 1.1 riastrad struct drm_mm_node *entry;
426 1.1 riastrad struct drm_mm_node *best;
427 1.3.28.1 pgoyette u64 adj_start;
428 1.3.28.1 pgoyette u64 adj_end;
429 1.3.28.1 pgoyette u64 best_size;
430 1.1 riastrad
431 1.1 riastrad BUG_ON(mm->scanned_blocks);
432 1.1 riastrad
433 1.1 riastrad best = NULL;
434 1.1 riastrad best_size = ~0UL;
435 1.1 riastrad
436 1.3 riastrad __drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
437 1.3 riastrad flags & DRM_MM_SEARCH_BELOW) {
438 1.3.28.1 pgoyette u64 hole_size = adj_end - adj_start;
439 1.1 riastrad
440 1.1 riastrad if (mm->color_adjust) {
441 1.1 riastrad mm->color_adjust(entry, color, &adj_start, &adj_end);
442 1.1 riastrad if (adj_end <= adj_start)
443 1.1 riastrad continue;
444 1.1 riastrad }
445 1.1 riastrad
446 1.1 riastrad if (!check_free_hole(adj_start, adj_end, size, alignment))
447 1.1 riastrad continue;
448 1.1 riastrad
449 1.3 riastrad if (!(flags & DRM_MM_SEARCH_BEST))
450 1.1 riastrad return entry;
451 1.1 riastrad
452 1.3 riastrad if (hole_size < best_size) {
453 1.1 riastrad best = entry;
454 1.3 riastrad best_size = hole_size;
455 1.1 riastrad }
456 1.1 riastrad }
457 1.1 riastrad
458 1.1 riastrad return best;
459 1.1 riastrad }
460 1.1 riastrad
461 1.3 riastrad static struct drm_mm_node *drm_mm_search_free_in_range_generic(const struct drm_mm *mm,
462 1.3.28.1 pgoyette u64 size,
463 1.1 riastrad unsigned alignment,
464 1.1 riastrad unsigned long color,
465 1.3.28.1 pgoyette u64 start,
466 1.3.28.1 pgoyette u64 end,
467 1.3 riastrad enum drm_mm_search_flags flags)
468 1.1 riastrad {
469 1.1 riastrad struct drm_mm_node *entry;
470 1.1 riastrad struct drm_mm_node *best;
471 1.3.28.1 pgoyette u64 adj_start;
472 1.3.28.1 pgoyette u64 adj_end;
473 1.3.28.1 pgoyette u64 best_size;
474 1.1 riastrad
475 1.1 riastrad BUG_ON(mm->scanned_blocks);
476 1.1 riastrad
477 1.1 riastrad best = NULL;
478 1.1 riastrad best_size = ~0UL;
479 1.1 riastrad
480 1.3 riastrad __drm_mm_for_each_hole(entry, mm, adj_start, adj_end,
481 1.3 riastrad flags & DRM_MM_SEARCH_BELOW) {
482 1.3.28.1 pgoyette u64 hole_size = adj_end - adj_start;
483 1.1 riastrad
484 1.1 riastrad if (mm->color_adjust) {
485 1.1 riastrad mm->color_adjust(entry, color, &adj_start, &adj_end);
486 1.1 riastrad if (adj_end <= adj_start)
487 1.1 riastrad continue;
488 1.1 riastrad }
489 1.1 riastrad
490 1.3.28.1 pgoyette adj_start = max(adj_start, start);
491 1.3.28.1 pgoyette adj_end = min(adj_end, end);
492 1.3.28.1 pgoyette
493 1.1 riastrad if (!check_free_hole(adj_start, adj_end, size, alignment))
494 1.1 riastrad continue;
495 1.1 riastrad
496 1.3 riastrad if (!(flags & DRM_MM_SEARCH_BEST))
497 1.1 riastrad return entry;
498 1.1 riastrad
499 1.3 riastrad if (hole_size < best_size) {
500 1.1 riastrad best = entry;
501 1.3 riastrad best_size = hole_size;
502 1.1 riastrad }
503 1.1 riastrad }
504 1.1 riastrad
505 1.1 riastrad return best;
506 1.1 riastrad }
507 1.1 riastrad
508 1.1 riastrad /**
509 1.3 riastrad * drm_mm_replace_node - move an allocation from @old to @new
510 1.3 riastrad * @old: drm_mm_node to remove from the allocator
511 1.3 riastrad * @new: drm_mm_node which should inherit @old's allocation
512 1.3 riastrad *
513 1.3 riastrad * This is useful for when drivers embed the drm_mm_node structure and hence
514 1.3 riastrad * can't move allocations by reassigning pointers. It's a combination of remove
515 1.3 riastrad * and insert with the guarantee that the allocation start will match.
516 1.1 riastrad */
517 1.1 riastrad void drm_mm_replace_node(struct drm_mm_node *old, struct drm_mm_node *new)
518 1.1 riastrad {
519 1.1 riastrad list_replace(&old->node_list, &new->node_list);
520 1.1 riastrad list_replace(&old->hole_stack, &new->hole_stack);
521 1.1 riastrad new->hole_follows = old->hole_follows;
522 1.1 riastrad new->mm = old->mm;
523 1.1 riastrad new->start = old->start;
524 1.1 riastrad new->size = old->size;
525 1.1 riastrad new->color = old->color;
526 1.1 riastrad
527 1.1 riastrad old->allocated = 0;
528 1.1 riastrad new->allocated = 1;
529 1.1 riastrad }
530 1.1 riastrad EXPORT_SYMBOL(drm_mm_replace_node);
531 1.1 riastrad
532 1.1 riastrad /**
533 1.3 riastrad * DOC: lru scan roaster
534 1.3 riastrad *
535 1.3 riastrad * Very often GPUs need to have continuous allocations for a given object. When
536 1.3 riastrad * evicting objects to make space for a new one it is therefore not most
537 1.3 riastrad * efficient when we simply start to select all objects from the tail of an LRU
538 1.3 riastrad * until there's a suitable hole: Especially for big objects or nodes that
539 1.3 riastrad * otherwise have special allocation constraints there's a good chance we evict
540 1.3 riastrad * lots of (smaller) objects unecessarily.
541 1.3 riastrad *
542 1.3 riastrad * The DRM range allocator supports this use-case through the scanning
543 1.3 riastrad * interfaces. First a scan operation needs to be initialized with
544 1.3 riastrad * drm_mm_init_scan() or drm_mm_init_scan_with_range(). The the driver adds
545 1.3 riastrad * objects to the roaster (probably by walking an LRU list, but this can be
546 1.3 riastrad * freely implemented) until a suitable hole is found or there's no further
547 1.3 riastrad * evitable object.
548 1.3 riastrad *
549 1.3 riastrad * The the driver must walk through all objects again in exactly the reverse
550 1.3 riastrad * order to restore the allocator state. Note that while the allocator is used
551 1.3 riastrad * in the scan mode no other operation is allowed.
552 1.3 riastrad *
553 1.3 riastrad * Finally the driver evicts all objects selected in the scan. Adding and
554 1.3 riastrad * removing an object is O(1), and since freeing a node is also O(1) the overall
555 1.3 riastrad * complexity is O(scanned_objects). So like the free stack which needs to be
556 1.3 riastrad * walked before a scan operation even begins this is linear in the number of
557 1.3 riastrad * objects. It doesn't seem to hurt badly.
558 1.3 riastrad */
559 1.3 riastrad
560 1.3 riastrad /**
561 1.3 riastrad * drm_mm_init_scan - initialize lru scanning
562 1.3 riastrad * @mm: drm_mm to scan
563 1.3 riastrad * @size: size of the allocation
564 1.3 riastrad * @alignment: alignment of the allocation
565 1.3 riastrad * @color: opaque tag value to use for the allocation
566 1.1 riastrad *
567 1.1 riastrad * This simply sets up the scanning routines with the parameters for the desired
568 1.3 riastrad * hole. Note that there's no need to specify allocation flags, since they only
569 1.3 riastrad * change the place a node is allocated from within a suitable hole.
570 1.1 riastrad *
571 1.3 riastrad * Warning:
572 1.3 riastrad * As long as the scan list is non-empty, no other operations than
573 1.1 riastrad * adding/removing nodes to/from the scan list are allowed.
574 1.1 riastrad */
575 1.1 riastrad void drm_mm_init_scan(struct drm_mm *mm,
576 1.3.28.1 pgoyette u64 size,
577 1.1 riastrad unsigned alignment,
578 1.1 riastrad unsigned long color)
579 1.1 riastrad {
580 1.1 riastrad mm->scan_color = color;
581 1.1 riastrad mm->scan_alignment = alignment;
582 1.1 riastrad mm->scan_size = size;
583 1.1 riastrad mm->scanned_blocks = 0;
584 1.1 riastrad mm->scan_hit_start = 0;
585 1.1 riastrad mm->scan_hit_end = 0;
586 1.1 riastrad mm->scan_check_range = 0;
587 1.1 riastrad mm->prev_scanned_node = NULL;
588 1.1 riastrad }
589 1.1 riastrad EXPORT_SYMBOL(drm_mm_init_scan);
590 1.1 riastrad
591 1.1 riastrad /**
592 1.3 riastrad * drm_mm_init_scan - initialize range-restricted lru scanning
593 1.3 riastrad * @mm: drm_mm to scan
594 1.3 riastrad * @size: size of the allocation
595 1.3 riastrad * @alignment: alignment of the allocation
596 1.3 riastrad * @color: opaque tag value to use for the allocation
597 1.3 riastrad * @start: start of the allowed range for the allocation
598 1.3 riastrad * @end: end of the allowed range for the allocation
599 1.1 riastrad *
600 1.1 riastrad * This simply sets up the scanning routines with the parameters for the desired
601 1.3 riastrad * hole. Note that there's no need to specify allocation flags, since they only
602 1.3 riastrad * change the place a node is allocated from within a suitable hole.
603 1.1 riastrad *
604 1.3 riastrad * Warning:
605 1.3 riastrad * As long as the scan list is non-empty, no other operations than
606 1.1 riastrad * adding/removing nodes to/from the scan list are allowed.
607 1.1 riastrad */
608 1.1 riastrad void drm_mm_init_scan_with_range(struct drm_mm *mm,
609 1.3.28.1 pgoyette u64 size,
610 1.1 riastrad unsigned alignment,
611 1.1 riastrad unsigned long color,
612 1.3.28.1 pgoyette u64 start,
613 1.3.28.1 pgoyette u64 end)
614 1.1 riastrad {
615 1.1 riastrad mm->scan_color = color;
616 1.1 riastrad mm->scan_alignment = alignment;
617 1.1 riastrad mm->scan_size = size;
618 1.1 riastrad mm->scanned_blocks = 0;
619 1.1 riastrad mm->scan_hit_start = 0;
620 1.1 riastrad mm->scan_hit_end = 0;
621 1.1 riastrad mm->scan_start = start;
622 1.1 riastrad mm->scan_end = end;
623 1.1 riastrad mm->scan_check_range = 1;
624 1.1 riastrad mm->prev_scanned_node = NULL;
625 1.1 riastrad }
626 1.1 riastrad EXPORT_SYMBOL(drm_mm_init_scan_with_range);
627 1.1 riastrad
628 1.1 riastrad /**
629 1.3 riastrad * drm_mm_scan_add_block - add a node to the scan list
630 1.3 riastrad * @node: drm_mm_node to add
631 1.3 riastrad *
632 1.1 riastrad * Add a node to the scan list that might be freed to make space for the desired
633 1.1 riastrad * hole.
634 1.1 riastrad *
635 1.3 riastrad * Returns:
636 1.3 riastrad * True if a hole has been found, false otherwise.
637 1.1 riastrad */
638 1.3 riastrad bool drm_mm_scan_add_block(struct drm_mm_node *node)
639 1.1 riastrad {
640 1.1 riastrad struct drm_mm *mm = node->mm;
641 1.1 riastrad struct drm_mm_node *prev_node;
642 1.3.28.1 pgoyette u64 hole_start, hole_end;
643 1.3.28.1 pgoyette u64 adj_start, adj_end;
644 1.1 riastrad
645 1.1 riastrad mm->scanned_blocks++;
646 1.1 riastrad
647 1.1 riastrad BUG_ON(node->scanned_block);
648 1.1 riastrad node->scanned_block = 1;
649 1.1 riastrad
650 1.1 riastrad prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
651 1.1 riastrad node_list);
652 1.1 riastrad
653 1.1 riastrad node->scanned_preceeds_hole = prev_node->hole_follows;
654 1.1 riastrad prev_node->hole_follows = 1;
655 1.1 riastrad list_del(&node->node_list);
656 1.1 riastrad node->node_list.prev = &prev_node->node_list;
657 1.1 riastrad node->node_list.next = &mm->prev_scanned_node->node_list;
658 1.1 riastrad mm->prev_scanned_node = node;
659 1.1 riastrad
660 1.1 riastrad adj_start = hole_start = drm_mm_hole_node_start(prev_node);
661 1.1 riastrad adj_end = hole_end = drm_mm_hole_node_end(prev_node);
662 1.1 riastrad
663 1.1 riastrad if (mm->scan_check_range) {
664 1.1 riastrad if (adj_start < mm->scan_start)
665 1.1 riastrad adj_start = mm->scan_start;
666 1.1 riastrad if (adj_end > mm->scan_end)
667 1.1 riastrad adj_end = mm->scan_end;
668 1.1 riastrad }
669 1.1 riastrad
670 1.1 riastrad if (mm->color_adjust)
671 1.1 riastrad mm->color_adjust(prev_node, mm->scan_color,
672 1.1 riastrad &adj_start, &adj_end);
673 1.1 riastrad
674 1.1 riastrad if (check_free_hole(adj_start, adj_end,
675 1.1 riastrad mm->scan_size, mm->scan_alignment)) {
676 1.1 riastrad mm->scan_hit_start = hole_start;
677 1.1 riastrad mm->scan_hit_end = hole_end;
678 1.3 riastrad return true;
679 1.1 riastrad }
680 1.1 riastrad
681 1.3 riastrad return false;
682 1.1 riastrad }
683 1.1 riastrad EXPORT_SYMBOL(drm_mm_scan_add_block);
684 1.1 riastrad
685 1.1 riastrad /**
686 1.3 riastrad * drm_mm_scan_remove_block - remove a node from the scan list
687 1.3 riastrad * @node: drm_mm_node to remove
688 1.1 riastrad *
689 1.1 riastrad * Nodes _must_ be removed in the exact same order from the scan list as they
690 1.1 riastrad * have been added, otherwise the internal state of the memory manager will be
691 1.1 riastrad * corrupted.
692 1.1 riastrad *
693 1.1 riastrad * When the scan list is empty, the selected memory nodes can be freed. An
694 1.3 riastrad * immediately following drm_mm_search_free with !DRM_MM_SEARCH_BEST will then
695 1.3 riastrad * return the just freed block (because its at the top of the free_stack list).
696 1.1 riastrad *
697 1.3 riastrad * Returns:
698 1.3 riastrad * True if this block should be evicted, false otherwise. Will always
699 1.3 riastrad * return false when no hole has been found.
700 1.1 riastrad */
701 1.3 riastrad bool drm_mm_scan_remove_block(struct drm_mm_node *node)
702 1.1 riastrad {
703 1.1 riastrad struct drm_mm *mm = node->mm;
704 1.1 riastrad struct drm_mm_node *prev_node;
705 1.1 riastrad
706 1.1 riastrad mm->scanned_blocks--;
707 1.1 riastrad
708 1.1 riastrad BUG_ON(!node->scanned_block);
709 1.1 riastrad node->scanned_block = 0;
710 1.1 riastrad
711 1.1 riastrad prev_node = list_entry(node->node_list.prev, struct drm_mm_node,
712 1.1 riastrad node_list);
713 1.1 riastrad
714 1.1 riastrad prev_node->hole_follows = node->scanned_preceeds_hole;
715 1.1 riastrad list_add(&node->node_list, &prev_node->node_list);
716 1.1 riastrad
717 1.1 riastrad return (drm_mm_hole_node_end(node) > mm->scan_hit_start &&
718 1.1 riastrad node->start < mm->scan_hit_end);
719 1.1 riastrad }
720 1.1 riastrad EXPORT_SYMBOL(drm_mm_scan_remove_block);
721 1.1 riastrad
722 1.3 riastrad /**
723 1.3 riastrad * drm_mm_clean - checks whether an allocator is clean
724 1.3 riastrad * @mm: drm_mm allocator to check
725 1.3 riastrad *
726 1.3 riastrad * Returns:
727 1.3 riastrad * True if the allocator is completely free, false if there's still a node
728 1.3 riastrad * allocated in it.
729 1.3 riastrad */
730 1.3 riastrad bool drm_mm_clean(struct drm_mm * mm)
731 1.1 riastrad {
732 1.1 riastrad struct list_head *head = &mm->head_node.node_list;
733 1.1 riastrad
734 1.1 riastrad return (head->next->next == head);
735 1.1 riastrad }
736 1.1 riastrad EXPORT_SYMBOL(drm_mm_clean);
737 1.1 riastrad
738 1.3 riastrad /**
739 1.3 riastrad * drm_mm_init - initialize a drm-mm allocator
740 1.3 riastrad * @mm: the drm_mm structure to initialize
741 1.3 riastrad * @start: start of the range managed by @mm
742 1.3 riastrad * @size: end of the range managed by @mm
743 1.3 riastrad *
744 1.3 riastrad * Note that @mm must be cleared to 0 before calling this function.
745 1.3 riastrad */
746 1.3.28.1 pgoyette void drm_mm_init(struct drm_mm * mm, u64 start, u64 size)
747 1.1 riastrad {
748 1.1 riastrad INIT_LIST_HEAD(&mm->hole_stack);
749 1.1 riastrad mm->scanned_blocks = 0;
750 1.1 riastrad
751 1.1 riastrad /* Clever trick to avoid a special case in the free hole tracking. */
752 1.1 riastrad INIT_LIST_HEAD(&mm->head_node.node_list);
753 1.1 riastrad INIT_LIST_HEAD(&mm->head_node.hole_stack);
754 1.1 riastrad mm->head_node.hole_follows = 1;
755 1.1 riastrad mm->head_node.scanned_block = 0;
756 1.1 riastrad mm->head_node.scanned_prev_free = 0;
757 1.1 riastrad mm->head_node.scanned_next_free = 0;
758 1.1 riastrad mm->head_node.mm = mm;
759 1.1 riastrad mm->head_node.start = start + size;
760 1.1 riastrad mm->head_node.size = start - mm->head_node.start;
761 1.1 riastrad list_add_tail(&mm->head_node.hole_stack, &mm->hole_stack);
762 1.1 riastrad
763 1.1 riastrad mm->color_adjust = NULL;
764 1.1 riastrad }
765 1.1 riastrad EXPORT_SYMBOL(drm_mm_init);
766 1.1 riastrad
767 1.3 riastrad /**
768 1.3 riastrad * drm_mm_takedown - clean up a drm_mm allocator
769 1.3 riastrad * @mm: drm_mm allocator to clean up
770 1.3 riastrad *
771 1.3 riastrad * Note that it is a bug to call this function on an allocator which is not
772 1.3 riastrad * clean.
773 1.3 riastrad */
774 1.1 riastrad void drm_mm_takedown(struct drm_mm * mm)
775 1.1 riastrad {
776 1.3 riastrad WARN(!list_empty(&mm->head_node.node_list),
777 1.3 riastrad "Memory manager not clean during takedown.\n");
778 1.3 riastrad }
779 1.3 riastrad EXPORT_SYMBOL(drm_mm_takedown);
780 1.1 riastrad
781 1.3.28.1 pgoyette static u64 drm_mm_debug_hole(struct drm_mm_node *entry,
782 1.3.28.1 pgoyette const char *prefix)
783 1.3 riastrad {
784 1.3.28.1 pgoyette u64 hole_start, hole_end, hole_size;
785 1.1 riastrad
786 1.3 riastrad if (entry->hole_follows) {
787 1.3 riastrad hole_start = drm_mm_hole_node_start(entry);
788 1.3 riastrad hole_end = drm_mm_hole_node_end(entry);
789 1.3 riastrad hole_size = hole_end - hole_start;
790 1.3.28.1 pgoyette pr_debug("%s %#"PRIx64"-%#"PRIx64": %"PRIu64": free\n", prefix, hole_start,
791 1.3.28.1 pgoyette hole_end, hole_size);
792 1.3 riastrad return hole_size;
793 1.1 riastrad }
794 1.1 riastrad
795 1.3 riastrad return 0;
796 1.1 riastrad }
797 1.1 riastrad
798 1.3 riastrad /**
799 1.3 riastrad * drm_mm_debug_table - dump allocator state to dmesg
800 1.3 riastrad * @mm: drm_mm allocator to dump
801 1.3 riastrad * @prefix: prefix to use for dumping to dmesg
802 1.3 riastrad */
803 1.1 riastrad void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
804 1.1 riastrad {
805 1.1 riastrad struct drm_mm_node *entry;
806 1.3.28.1 pgoyette u64 total_used = 0, total_free = 0, total = 0;
807 1.1 riastrad
808 1.3 riastrad total_free += drm_mm_debug_hole(&mm->head_node, prefix);
809 1.1 riastrad
810 1.1 riastrad drm_mm_for_each_node(entry, mm) {
811 1.3.28.1 pgoyette pr_debug("%s %#"PRIx64"-%#"PRIx64": %"PRIu64": used\n", prefix, entry->start,
812 1.3.28.1 pgoyette entry->start + entry->size, entry->size);
813 1.1 riastrad total_used += entry->size;
814 1.3 riastrad total_free += drm_mm_debug_hole(entry, prefix);
815 1.1 riastrad }
816 1.1 riastrad total = total_free + total_used;
817 1.1 riastrad
818 1.3.28.1 pgoyette pr_debug("%s total: %"PRIu64", used %"PRIu64" free %"PRIu64"\n", prefix, total,
819 1.3.28.1 pgoyette total_used, total_free);
820 1.1 riastrad }
821 1.1 riastrad EXPORT_SYMBOL(drm_mm_debug_table);
822 1.1 riastrad
823 1.1 riastrad #if defined(CONFIG_DEBUG_FS)
824 1.3.28.1 pgoyette static u64 drm_mm_dump_hole(struct seq_file *m, struct drm_mm_node *entry)
825 1.3 riastrad {
826 1.3.28.1 pgoyette u64 hole_start, hole_end, hole_size;
827 1.3 riastrad
828 1.3 riastrad if (entry->hole_follows) {
829 1.3 riastrad hole_start = drm_mm_hole_node_start(entry);
830 1.3 riastrad hole_end = drm_mm_hole_node_end(entry);
831 1.3 riastrad hole_size = hole_end - hole_start;
832 1.3.28.1 pgoyette seq_printf(m, "%#018llx-%#018llx: %llu: free\n", hole_start,
833 1.3.28.1 pgoyette hole_end, hole_size);
834 1.3 riastrad return hole_size;
835 1.3 riastrad }
836 1.3 riastrad
837 1.3 riastrad return 0;
838 1.3 riastrad }
839 1.3 riastrad
840 1.3 riastrad /**
841 1.3 riastrad * drm_mm_dump_table - dump allocator state to a seq_file
842 1.3 riastrad * @m: seq_file to dump to
843 1.3 riastrad * @mm: drm_mm allocator to dump
844 1.3 riastrad */
845 1.1 riastrad int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
846 1.1 riastrad {
847 1.1 riastrad struct drm_mm_node *entry;
848 1.3.28.1 pgoyette u64 total_used = 0, total_free = 0, total = 0;
849 1.1 riastrad
850 1.3 riastrad total_free += drm_mm_dump_hole(m, &mm->head_node);
851 1.1 riastrad
852 1.1 riastrad drm_mm_for_each_node(entry, mm) {
853 1.3.28.1 pgoyette seq_printf(m, "%#018llx-%#018llx: %llu: used\n", entry->start,
854 1.3.28.1 pgoyette entry->start + entry->size, entry->size);
855 1.1 riastrad total_used += entry->size;
856 1.3 riastrad total_free += drm_mm_dump_hole(m, entry);
857 1.1 riastrad }
858 1.1 riastrad total = total_free + total_used;
859 1.1 riastrad
860 1.3.28.1 pgoyette seq_printf(m, "total: %llu, used %llu free %llu\n", total,
861 1.3.28.1 pgoyette total_used, total_free);
862 1.1 riastrad return 0;
863 1.1 riastrad }
864 1.1 riastrad EXPORT_SYMBOL(drm_mm_dump_table);
865 1.1 riastrad #endif
866