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