blob: 789a810d66aa20363141364b2bb25cc0686f08c1 [file] [log] [blame]
Chris Wilson50f00332016-12-22 08:36:09 +00001/*
2 * Test cases for the drm_mm range manager
3 */
4
5#define pr_fmt(fmt) "drm_mm: " fmt
6
7#include <linux/module.h>
8#include <linux/prime_numbers.h>
9#include <linux/slab.h>
10#include <linux/random.h>
11#include <linux/vmalloc.h>
12
13#include <drm/drm_mm.h>
14
15#include "../lib/drm_random.h"
16
17#define TESTS "drm_mm_selftests.h"
18#include "drm_selftest.h"
19
20static unsigned int random_seed;
21static unsigned int max_iterations = 8192;
22static unsigned int max_prime = 128;
23
24static int igt_sanitycheck(void *ignored)
25{
26 pr_info("%s - ok!\n", __func__);
27 return 0;
28}
29
Chris Wilson393b50f2016-12-22 08:36:10 +000030static bool assert_no_holes(const struct drm_mm *mm)
31{
32 struct drm_mm_node *hole;
33 u64 hole_start, hole_end;
34 unsigned long count;
35
36 count = 0;
37 drm_mm_for_each_hole(hole, mm, hole_start, hole_end)
38 count++;
39 if (count) {
40 pr_err("Expected to find no holes (after reserve), found %lu instead\n", count);
41 return false;
42 }
43
44 drm_mm_for_each_node(hole, mm) {
45 if (hole->hole_follows) {
46 pr_err("Hole follows node, expected none!\n");
47 return false;
48 }
49 }
50
51 return true;
52}
53
54static bool assert_one_hole(const struct drm_mm *mm, u64 start, u64 end)
55{
56 struct drm_mm_node *hole;
57 u64 hole_start, hole_end;
58 unsigned long count;
59 bool ok = true;
60
61 if (end <= start)
62 return true;
63
64 count = 0;
65 drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
66 if (start != hole_start || end != hole_end) {
67 if (ok)
68 pr_err("empty mm has incorrect hole, found (%llx, %llx), expect (%llx, %llx)\n",
69 hole_start, hole_end,
70 start, end);
71 ok = false;
72 }
73 count++;
74 }
75 if (count != 1) {
76 pr_err("Expected to find one hole, found %lu instead\n", count);
77 ok = false;
78 }
79
80 return ok;
81}
82
Chris Wilson900537d2016-12-22 08:36:12 +000083static bool assert_continuous(const struct drm_mm *mm, u64 size)
84{
85 struct drm_mm_node *node, *check, *found;
86 unsigned long n;
87 u64 addr;
88
89 if (!assert_no_holes(mm))
90 return false;
91
92 n = 0;
93 addr = 0;
94 drm_mm_for_each_node(node, mm) {
95 if (node->start != addr) {
96 pr_err("node[%ld] list out of order, expected %llx found %llx\n",
97 n, addr, node->start);
98 return false;
99 }
100
101 if (node->size != size) {
102 pr_err("node[%ld].size incorrect, expected %llx, found %llx\n",
103 n, size, node->size);
104 return false;
105 }
106
107 if (node->hole_follows) {
108 pr_err("node[%ld] is followed by a hole!\n", n);
109 return false;
110 }
111
112 found = NULL;
113 drm_mm_for_each_node_in_range(check, mm, addr, addr + size) {
114 if (node != check) {
115 pr_err("lookup return wrong node, expected start %llx, found %llx\n",
116 node->start, check->start);
117 return false;
118 }
119 found = check;
120 }
121 if (!found) {
122 pr_err("lookup failed for node %llx + %llx\n",
123 addr, size);
124 return false;
125 }
126
127 addr += size;
128 n++;
129 }
130
131 return true;
132}
133
Chris Wilson393b50f2016-12-22 08:36:10 +0000134static int igt_init(void *ignored)
135{
136 const unsigned int size = 4096;
137 struct drm_mm mm;
138 struct drm_mm_node tmp;
139 int ret = -EINVAL;
140
141 /* Start with some simple checks on initialising the struct drm_mm */
142 memset(&mm, 0, sizeof(mm));
143 if (drm_mm_initialized(&mm)) {
144 pr_err("zeroed mm claims to be initialized\n");
145 return ret;
146 }
147
148 memset(&mm, 0xff, sizeof(mm));
149 drm_mm_init(&mm, 0, size);
150 if (!drm_mm_initialized(&mm)) {
151 pr_err("mm claims not to be initialized\n");
152 goto out;
153 }
154
155 if (!drm_mm_clean(&mm)) {
156 pr_err("mm not empty on creation\n");
157 goto out;
158 }
159
160 /* After creation, it should all be one massive hole */
161 if (!assert_one_hole(&mm, 0, size)) {
162 ret = -EINVAL;
163 goto out;
164 }
165
166 memset(&tmp, 0, sizeof(tmp));
167 tmp.start = 0;
168 tmp.size = size;
169 ret = drm_mm_reserve_node(&mm, &tmp);
170 if (ret) {
171 pr_err("failed to reserve whole drm_mm\n");
172 goto out;
173 }
174
175 /* After filling the range entirely, there should be no holes */
176 if (!assert_no_holes(&mm)) {
177 ret = -EINVAL;
178 goto out;
179 }
180
181 /* And then after emptying it again, the massive hole should be back */
182 drm_mm_remove_node(&tmp);
183 if (!assert_one_hole(&mm, 0, size)) {
184 ret = -EINVAL;
185 goto out;
186 }
187
188out:
189 if (ret)
190 drm_mm_debug_table(&mm, __func__);
191 drm_mm_takedown(&mm);
192 return ret;
193}
194
Chris Wilson06df8ac2016-12-22 08:36:11 +0000195static int igt_debug(void *ignored)
196{
197 struct drm_mm mm;
198 struct drm_mm_node nodes[2];
199 int ret;
200
201 /* Create a small drm_mm with a couple of nodes and a few holes, and
202 * check that the debug iterator doesn't explode over a trivial drm_mm.
203 */
204
205 drm_mm_init(&mm, 0, 4096);
206
207 memset(nodes, 0, sizeof(nodes));
208 nodes[0].start = 512;
209 nodes[0].size = 1024;
210 ret = drm_mm_reserve_node(&mm, &nodes[0]);
211 if (ret) {
212 pr_err("failed to reserve node[0] {start=%lld, size=%lld)\n",
213 nodes[0].start, nodes[0].size);
214 return ret;
215 }
216
217 nodes[1].size = 1024;
218 nodes[1].start = 4096 - 512 - nodes[1].size;
219 ret = drm_mm_reserve_node(&mm, &nodes[1]);
220 if (ret) {
221 pr_err("failed to reserve node[1] {start=%lld, size=%lld)\n",
222 nodes[1].start, nodes[1].size);
223 return ret;
224 }
225
226 drm_mm_debug_table(&mm, __func__);
227 return 0;
228}
229
Chris Wilson900537d2016-12-22 08:36:12 +0000230static struct drm_mm_node *set_node(struct drm_mm_node *node,
231 u64 start, u64 size)
232{
233 node->start = start;
234 node->size = size;
235 return node;
236}
237
238static bool expect_reserve_fail(struct drm_mm *mm, struct drm_mm_node *node)
239{
240 int err;
241
242 err = drm_mm_reserve_node(mm, node);
243 if (likely(err == -ENOSPC))
244 return true;
245
246 if (!err) {
247 pr_err("impossible reserve succeeded, node %llu + %llu\n",
248 node->start, node->size);
249 drm_mm_remove_node(node);
250 } else {
251 pr_err("impossible reserve failed with wrong error %d [expected %d], node %llu + %llu\n",
252 err, -ENOSPC, node->start, node->size);
253 }
254 return false;
255}
256
257static bool check_reserve_boundaries(struct drm_mm *mm,
258 unsigned int count,
259 u64 size)
260{
261 const struct boundary {
262 u64 start, size;
263 const char *name;
264 } boundaries[] = {
265#define B(st, sz) { (st), (sz), "{ " #st ", " #sz "}" }
266 B(0, 0),
267 B(-size, 0),
268 B(size, 0),
269 B(size * count, 0),
270 B(-size, size),
271 B(-size, -size),
272 B(-size, 2*size),
273 B(0, -size),
274 B(size, -size),
275 B(count*size, size),
276 B(count*size, -size),
277 B(count*size, count*size),
278 B(count*size, -count*size),
279 B(count*size, -(count+1)*size),
280 B((count+1)*size, size),
281 B((count+1)*size, -size),
282 B((count+1)*size, -2*size),
283#undef B
284 };
285 struct drm_mm_node tmp = {};
286 int n;
287
288 for (n = 0; n < ARRAY_SIZE(boundaries); n++) {
289 if (!expect_reserve_fail(mm,
290 set_node(&tmp,
291 boundaries[n].start,
292 boundaries[n].size))) {
293 pr_err("boundary[%d:%s] failed, count=%u, size=%lld\n",
294 n, boundaries[n].name, count, size);
295 return false;
296 }
297 }
298
299 return true;
300}
301
302static int __igt_reserve(unsigned int count, u64 size)
303{
304 DRM_RND_STATE(prng, random_seed);
305 struct drm_mm mm;
306 struct drm_mm_node tmp, *nodes, *node, *next;
307 unsigned int *order, n, m, o = 0;
308 int ret, err;
309
310 /* For exercising drm_mm_reserve_node(), we want to check that
311 * reservations outside of the drm_mm range are rejected, and to
312 * overlapping and otherwise already occupied ranges. Afterwards,
313 * the tree and nodes should be intact.
314 */
315
316 DRM_MM_BUG_ON(!count);
317 DRM_MM_BUG_ON(!size);
318
319 ret = -ENOMEM;
320 order = drm_random_order(count, &prng);
321 if (!order)
322 goto err;
323
324 nodes = vzalloc(sizeof(*nodes) * count);
325 if (!nodes)
326 goto err_order;
327
328 ret = -EINVAL;
329 drm_mm_init(&mm, 0, count * size);
330
331 if (!check_reserve_boundaries(&mm, count, size))
332 goto out;
333
334 for (n = 0; n < count; n++) {
335 nodes[n].start = order[n] * size;
336 nodes[n].size = size;
337
338 err = drm_mm_reserve_node(&mm, &nodes[n]);
339 if (err) {
340 pr_err("reserve failed, step %d, start %llu\n",
341 n, nodes[n].start);
342 ret = err;
343 goto out;
344 }
345
346 if (!drm_mm_node_allocated(&nodes[n])) {
347 pr_err("reserved node not allocated! step %d, start %llu\n",
348 n, nodes[n].start);
349 goto out;
350 }
351
352 if (!expect_reserve_fail(&mm, &nodes[n]))
353 goto out;
354 }
355
356 /* After random insertion the nodes should be in order */
357 if (!assert_continuous(&mm, size))
358 goto out;
359
360 /* Repeated use should then fail */
361 drm_random_reorder(order, count, &prng);
362 for (n = 0; n < count; n++) {
363 if (!expect_reserve_fail(&mm,
364 set_node(&tmp, order[n] * size, 1)))
365 goto out;
366
367 /* Remove and reinsert should work */
368 drm_mm_remove_node(&nodes[order[n]]);
369 err = drm_mm_reserve_node(&mm, &nodes[order[n]]);
370 if (err) {
371 pr_err("reserve failed, step %d, start %llu\n",
372 n, nodes[n].start);
373 ret = err;
374 goto out;
375 }
376 }
377
378 if (!assert_continuous(&mm, size))
379 goto out;
380
381 /* Overlapping use should then fail */
382 for (n = 0; n < count; n++) {
383 if (!expect_reserve_fail(&mm, set_node(&tmp, 0, size*count)))
384 goto out;
385 }
386 for (n = 0; n < count; n++) {
387 if (!expect_reserve_fail(&mm,
388 set_node(&tmp,
389 size * n,
390 size * (count - n))))
391 goto out;
392 }
393
394 /* Remove several, reinsert, check full */
395 for_each_prime_number(n, min(max_prime, count)) {
396 for (m = 0; m < n; m++) {
397 node = &nodes[order[(o + m) % count]];
398 drm_mm_remove_node(node);
399 }
400
401 for (m = 0; m < n; m++) {
402 node = &nodes[order[(o + m) % count]];
403 err = drm_mm_reserve_node(&mm, node);
404 if (err) {
405 pr_err("reserve failed, step %d/%d, start %llu\n",
406 m, n, node->start);
407 ret = err;
408 goto out;
409 }
410 }
411
412 o += n;
413
414 if (!assert_continuous(&mm, size))
415 goto out;
416 }
417
418 ret = 0;
419out:
420 drm_mm_for_each_node_safe(node, next, &mm)
421 drm_mm_remove_node(node);
422 drm_mm_takedown(&mm);
423 vfree(nodes);
424err_order:
425 kfree(order);
426err:
427 return ret;
428}
429
430static int igt_reserve(void *ignored)
431{
432 const unsigned int count = min_t(unsigned int, BIT(10), max_iterations);
433 int n, ret;
434
435 for_each_prime_number_from(n, 1, 54) {
436 u64 size = BIT_ULL(n);
437
438 ret = __igt_reserve(count, size - 1);
439 if (ret)
440 return ret;
441
442 ret = __igt_reserve(count, size);
443 if (ret)
444 return ret;
445
446 ret = __igt_reserve(count, size + 1);
447 if (ret)
448 return ret;
449 }
450
451 return 0;
452}
453
Chris Wilson50f00332016-12-22 08:36:09 +0000454#include "drm_selftest.c"
455
456static int __init test_drm_mm_init(void)
457{
458 int err;
459
460 while (!random_seed)
461 random_seed = get_random_int();
462
463 pr_info("Testing DRM range manger (struct drm_mm), with random_seed=0x%x max_iterations=%u max_prime=%u\n",
464 random_seed, max_iterations, max_prime);
465 err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL);
466
467 return err > 0 ? 0 : err;
468}
469
470static void __exit test_drm_mm_exit(void)
471{
472}
473
474module_init(test_drm_mm_init);
475module_exit(test_drm_mm_exit);
476
477module_param(random_seed, uint, 0400);
478module_param(max_iterations, uint, 0400);
479module_param(max_prime, uint, 0400);
480
481MODULE_AUTHOR("Intel Corporation");
482MODULE_LICENSE("GPL");