xref: /linux/drivers/gpu/drm/i915/selftests/i915_syncmap.c (revision bd628c1bed7902ec1f24ba0fe70758949146abbe)
1 /*
2  * Copyright © 2017 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21  * IN THE SOFTWARE.
22  *
23  */
24 
25 #include "../i915_selftest.h"
26 #include "i915_random.h"
27 
28 static char *
29 __sync_print(struct i915_syncmap *p,
30 	     char *buf, unsigned long *sz,
31 	     unsigned int depth,
32 	     unsigned int last,
33 	     unsigned int idx)
34 {
35 	unsigned long len;
36 	unsigned int i, X;
37 
38 	if (depth) {
39 		unsigned int d;
40 
41 		for (d = 0; d < depth - 1; d++) {
42 			if (last & BIT(depth - d - 1))
43 				len = scnprintf(buf, *sz, "|   ");
44 			else
45 				len = scnprintf(buf, *sz, "    ");
46 			buf += len;
47 			*sz -= len;
48 		}
49 		len = scnprintf(buf, *sz, "%x-> ", idx);
50 		buf += len;
51 		*sz -= len;
52 	}
53 
54 	/* We mark bits after the prefix as "X" */
55 	len = scnprintf(buf, *sz, "0x%016llx", p->prefix << p->height << SHIFT);
56 	buf += len;
57 	*sz -= len;
58 	X = (p->height + SHIFT) / 4;
59 	scnprintf(buf - X, *sz + X, "%*s", X, "XXXXXXXXXXXXXXXXX");
60 
61 	if (!p->height) {
62 		for_each_set_bit(i, (unsigned long *)&p->bitmap, KSYNCMAP) {
63 			len = scnprintf(buf, *sz, " %x:%x,",
64 					i, __sync_seqno(p)[i]);
65 			buf += len;
66 			*sz -= len;
67 		}
68 		buf -= 1;
69 		*sz += 1;
70 	}
71 
72 	len = scnprintf(buf, *sz, "\n");
73 	buf += len;
74 	*sz -= len;
75 
76 	if (p->height) {
77 		for_each_set_bit(i, (unsigned long *)&p->bitmap, KSYNCMAP) {
78 			buf = __sync_print(__sync_child(p)[i], buf, sz,
79 					   depth + 1,
80 					   last << 1 | !!(p->bitmap >> (i + 1)),
81 					   i);
82 		}
83 	}
84 
85 	return buf;
86 }
87 
88 static bool
89 i915_syncmap_print_to_buf(struct i915_syncmap *p, char *buf, unsigned long sz)
90 {
91 	if (!p)
92 		return false;
93 
94 	while (p->parent)
95 		p = p->parent;
96 
97 	__sync_print(p, buf, &sz, 0, 1, 0);
98 	return true;
99 }
100 
101 static int check_syncmap_free(struct i915_syncmap **sync)
102 {
103 	i915_syncmap_free(sync);
104 	if (*sync) {
105 		pr_err("sync not cleared after free\n");
106 		return -EINVAL;
107 	}
108 
109 	return 0;
110 }
111 
112 static int dump_syncmap(struct i915_syncmap *sync, int err)
113 {
114 	char *buf;
115 
116 	if (!err)
117 		return check_syncmap_free(&sync);
118 
119 	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
120 	if (!buf)
121 		goto skip;
122 
123 	if (i915_syncmap_print_to_buf(sync, buf, PAGE_SIZE))
124 		pr_err("%s", buf);
125 
126 	kfree(buf);
127 
128 skip:
129 	i915_syncmap_free(&sync);
130 	return err;
131 }
132 
133 static int igt_syncmap_init(void *arg)
134 {
135 	struct i915_syncmap *sync = (void *)~0ul;
136 
137 	/*
138 	 * Cursory check that we can initialise a random pointer and transform
139 	 * it into the root pointer of a syncmap.
140 	 */
141 
142 	i915_syncmap_init(&sync);
143 	return check_syncmap_free(&sync);
144 }
145 
146 static int check_seqno(struct i915_syncmap *leaf, unsigned int idx, u32 seqno)
147 {
148 	if (leaf->height) {
149 		pr_err("%s: not a leaf, height is %d\n",
150 		       __func__, leaf->height);
151 		return -EINVAL;
152 	}
153 
154 	if (__sync_seqno(leaf)[idx] != seqno) {
155 		pr_err("%s: seqno[%d], found %x, expected %x\n",
156 		       __func__, idx, __sync_seqno(leaf)[idx], seqno);
157 		return -EINVAL;
158 	}
159 
160 	return 0;
161 }
162 
163 static int check_one(struct i915_syncmap **sync, u64 context, u32 seqno)
164 {
165 	int err;
166 
167 	err = i915_syncmap_set(sync, context, seqno);
168 	if (err)
169 		return err;
170 
171 	if ((*sync)->height) {
172 		pr_err("Inserting first context=%llx did not return leaf (height=%d, prefix=%llx\n",
173 		       context, (*sync)->height, (*sync)->prefix);
174 		return -EINVAL;
175 	}
176 
177 	if ((*sync)->parent) {
178 		pr_err("Inserting first context=%llx created branches!\n",
179 		       context);
180 		return -EINVAL;
181 	}
182 
183 	if (hweight32((*sync)->bitmap) != 1) {
184 		pr_err("First bitmap does not contain a single entry, found %x (count=%d)!\n",
185 		       (*sync)->bitmap, hweight32((*sync)->bitmap));
186 		return -EINVAL;
187 	}
188 
189 	err = check_seqno((*sync), ilog2((*sync)->bitmap), seqno);
190 	if (err)
191 		return err;
192 
193 	if (!i915_syncmap_is_later(sync, context, seqno)) {
194 		pr_err("Lookup of first context=%llx/seqno=%x failed!\n",
195 		       context, seqno);
196 		return -EINVAL;
197 	}
198 
199 	return 0;
200 }
201 
202 static int igt_syncmap_one(void *arg)
203 {
204 	I915_RND_STATE(prng);
205 	IGT_TIMEOUT(end_time);
206 	struct i915_syncmap *sync;
207 	unsigned long max = 1;
208 	int err;
209 
210 	/*
211 	 * Check that inserting a new id, creates a leaf and only that leaf.
212 	 */
213 
214 	i915_syncmap_init(&sync);
215 
216 	do {
217 		u64 context = i915_prandom_u64_state(&prng);
218 		unsigned long loop;
219 
220 		err = check_syncmap_free(&sync);
221 		if (err)
222 			goto out;
223 
224 		for (loop = 0; loop <= max; loop++) {
225 			err = check_one(&sync, context,
226 					prandom_u32_state(&prng));
227 			if (err)
228 				goto out;
229 		}
230 		max++;
231 	} while (!__igt_timeout(end_time, NULL));
232 	pr_debug("%s: Completed %lu single insertions\n",
233 		 __func__, max * (max - 1) / 2);
234 out:
235 	return dump_syncmap(sync, err);
236 }
237 
238 static int check_leaf(struct i915_syncmap **sync, u64 context, u32 seqno)
239 {
240 	int err;
241 
242 	err = i915_syncmap_set(sync, context, seqno);
243 	if (err)
244 		return err;
245 
246 	if ((*sync)->height) {
247 		pr_err("Inserting context=%llx did not return leaf (height=%d, prefix=%llx\n",
248 		       context, (*sync)->height, (*sync)->prefix);
249 		return -EINVAL;
250 	}
251 
252 	if (hweight32((*sync)->bitmap) != 1) {
253 		pr_err("First entry into leaf (context=%llx) does not contain a single entry, found %x (count=%d)!\n",
254 		       context, (*sync)->bitmap, hweight32((*sync)->bitmap));
255 		return -EINVAL;
256 	}
257 
258 	err = check_seqno((*sync), ilog2((*sync)->bitmap), seqno);
259 	if (err)
260 		return err;
261 
262 	if (!i915_syncmap_is_later(sync, context, seqno)) {
263 		pr_err("Lookup of first entry context=%llx/seqno=%x failed!\n",
264 		       context, seqno);
265 		return -EINVAL;
266 	}
267 
268 	return 0;
269 }
270 
271 static int igt_syncmap_join_above(void *arg)
272 {
273 	struct i915_syncmap *sync;
274 	unsigned int pass, order;
275 	int err;
276 
277 	i915_syncmap_init(&sync);
278 
279 	/*
280 	 * When we have a new id that doesn't fit inside the existing tree,
281 	 * we need to add a new layer above.
282 	 *
283 	 * 1: 0x00000001
284 	 * 2: 0x00000010
285 	 * 3: 0x00000100
286 	 * 4: 0x00001000
287 	 * ...
288 	 * Each pass the common prefix shrinks and we have to insert a join.
289 	 * Each join will only contain two branches, the latest of which
290 	 * is always a leaf.
291 	 *
292 	 * If we then reuse the same set of contexts, we expect to build an
293 	 * identical tree.
294 	 */
295 	for (pass = 0; pass < 3; pass++) {
296 		for (order = 0; order < 64; order += SHIFT) {
297 			u64 context = BIT_ULL(order);
298 			struct i915_syncmap *join;
299 
300 			err = check_leaf(&sync, context, 0);
301 			if (err)
302 				goto out;
303 
304 			join = sync->parent;
305 			if (!join) /* very first insert will have no parents */
306 				continue;
307 
308 			if (!join->height) {
309 				pr_err("Parent with no height!\n");
310 				err = -EINVAL;
311 				goto out;
312 			}
313 
314 			if (hweight32(join->bitmap) != 2) {
315 				pr_err("Join does not have 2 children: %x (%d)\n",
316 				       join->bitmap, hweight32(join->bitmap));
317 				err = -EINVAL;
318 				goto out;
319 			}
320 
321 			if (__sync_child(join)[__sync_branch_idx(join, context)] != sync) {
322 				pr_err("Leaf misplaced in parent!\n");
323 				err = -EINVAL;
324 				goto out;
325 			}
326 		}
327 	}
328 out:
329 	return dump_syncmap(sync, err);
330 }
331 
332 static int igt_syncmap_join_below(void *arg)
333 {
334 	struct i915_syncmap *sync;
335 	unsigned int step, order, idx;
336 	int err = -ENODEV;
337 
338 	i915_syncmap_init(&sync);
339 
340 	/*
341 	 * Check that we can split a compacted branch by replacing it with
342 	 * a join.
343 	 */
344 	for (step = 0; step < KSYNCMAP; step++) {
345 		for (order = 64 - SHIFT; order > 0; order -= SHIFT) {
346 			u64 context = step * BIT_ULL(order);
347 
348 			err = i915_syncmap_set(&sync, context, 0);
349 			if (err)
350 				goto out;
351 
352 			if (sync->height) {
353 				pr_err("Inserting context=%llx (order=%d, step=%d) did not return leaf (height=%d, prefix=%llx\n",
354 				       context, order, step, sync->height, sync->prefix);
355 				err = -EINVAL;
356 				goto out;
357 			}
358 		}
359 	}
360 
361 	for (step = 0; step < KSYNCMAP; step++) {
362 		for (order = SHIFT; order < 64; order += SHIFT) {
363 			u64 context = step * BIT_ULL(order);
364 
365 			if (!i915_syncmap_is_later(&sync, context, 0)) {
366 				pr_err("1: context %llx (order=%d, step=%d) not found\n",
367 				       context, order, step);
368 				err = -EINVAL;
369 				goto out;
370 			}
371 
372 			for (idx = 1; idx < KSYNCMAP; idx++) {
373 				if (i915_syncmap_is_later(&sync, context + idx, 0)) {
374 					pr_err("1: context %llx (order=%d, step=%d) should not exist\n",
375 					       context + idx, order, step);
376 					err = -EINVAL;
377 					goto out;
378 				}
379 			}
380 		}
381 	}
382 
383 	for (order = SHIFT; order < 64; order += SHIFT) {
384 		for (step = 0; step < KSYNCMAP; step++) {
385 			u64 context = step * BIT_ULL(order);
386 
387 			if (!i915_syncmap_is_later(&sync, context, 0)) {
388 				pr_err("2: context %llx (order=%d, step=%d) not found\n",
389 				       context, order, step);
390 				err = -EINVAL;
391 				goto out;
392 			}
393 		}
394 	}
395 
396 out:
397 	return dump_syncmap(sync, err);
398 }
399 
400 static int igt_syncmap_neighbours(void *arg)
401 {
402 	I915_RND_STATE(prng);
403 	IGT_TIMEOUT(end_time);
404 	struct i915_syncmap *sync;
405 	int err = -ENODEV;
406 
407 	/*
408 	 * Each leaf holds KSYNCMAP seqno. Check that when we create KSYNCMAP
409 	 * neighbouring ids, they all fit into the same leaf.
410 	 */
411 
412 	i915_syncmap_init(&sync);
413 	do {
414 		u64 context = i915_prandom_u64_state(&prng) & ~MASK;
415 		unsigned int idx;
416 
417 		if (i915_syncmap_is_later(&sync, context, 0)) /* Skip repeats */
418 			continue;
419 
420 		for (idx = 0; idx < KSYNCMAP; idx++) {
421 			err = i915_syncmap_set(&sync, context + idx, 0);
422 			if (err)
423 				goto out;
424 
425 			if (sync->height) {
426 				pr_err("Inserting context=%llx did not return leaf (height=%d, prefix=%llx\n",
427 				       context, sync->height, sync->prefix);
428 				err = -EINVAL;
429 				goto out;
430 			}
431 
432 			if (sync->bitmap != BIT(idx + 1) - 1) {
433 				pr_err("Inserting neighbouring context=0x%llx+%d, did not fit into the same leaf bitmap=%x (%d), expected %lx (%d)\n",
434 				       context, idx,
435 				       sync->bitmap, hweight32(sync->bitmap),
436 				       BIT(idx + 1) - 1, idx + 1);
437 				err = -EINVAL;
438 				goto out;
439 			}
440 		}
441 	} while (!__igt_timeout(end_time, NULL));
442 out:
443 	return dump_syncmap(sync, err);
444 }
445 
446 static int igt_syncmap_compact(void *arg)
447 {
448 	struct i915_syncmap *sync;
449 	unsigned int idx, order;
450 	int err = -ENODEV;
451 
452 	i915_syncmap_init(&sync);
453 
454 	/*
455 	 * The syncmap are "space efficient" compressed radix trees - any
456 	 * branch with only one child is skipped and replaced by the child.
457 	 *
458 	 * If we construct a tree with ids that are neighbouring at a non-zero
459 	 * height, we form a join but each child of that join is directly a
460 	 * leaf holding the single id.
461 	 */
462 	for (order = SHIFT; order < 64; order += SHIFT) {
463 		err = check_syncmap_free(&sync);
464 		if (err)
465 			goto out;
466 
467 		/* Create neighbours in the parent */
468 		for (idx = 0; idx < KSYNCMAP; idx++) {
469 			u64 context = idx * BIT_ULL(order) + idx;
470 
471 			err = i915_syncmap_set(&sync, context, 0);
472 			if (err)
473 				goto out;
474 
475 			if (sync->height) {
476 				pr_err("Inserting context=%llx (order=%d, idx=%d) did not return leaf (height=%d, prefix=%llx\n",
477 				       context, order, idx,
478 				       sync->height, sync->prefix);
479 				err = -EINVAL;
480 				goto out;
481 			}
482 		}
483 
484 		sync = sync->parent;
485 		if (sync->parent) {
486 			pr_err("Parent (join) of last leaf was not the sync!\n");
487 			err = -EINVAL;
488 			goto out;
489 		}
490 
491 		if (sync->height != order) {
492 			pr_err("Join does not have the expected height, found %d, expected %d\n",
493 			       sync->height, order);
494 			err = -EINVAL;
495 			goto out;
496 		}
497 
498 		if (sync->bitmap != BIT(KSYNCMAP) - 1) {
499 			pr_err("Join is not full!, found %x (%d) expected %lx (%d)\n",
500 			       sync->bitmap, hweight32(sync->bitmap),
501 			       BIT(KSYNCMAP) - 1, KSYNCMAP);
502 			err = -EINVAL;
503 			goto out;
504 		}
505 
506 		/* Each of our children should be a leaf */
507 		for (idx = 0; idx < KSYNCMAP; idx++) {
508 			struct i915_syncmap *leaf = __sync_child(sync)[idx];
509 
510 			if (leaf->height) {
511 				pr_err("Child %d is a not leaf!\n", idx);
512 				err = -EINVAL;
513 				goto out;
514 			}
515 
516 			if (leaf->parent != sync) {
517 				pr_err("Child %d is not attached to us!\n",
518 				       idx);
519 				err = -EINVAL;
520 				goto out;
521 			}
522 
523 			if (!is_power_of_2(leaf->bitmap)) {
524 				pr_err("Child %d holds more than one id, found %x (%d)\n",
525 				       idx, leaf->bitmap, hweight32(leaf->bitmap));
526 				err = -EINVAL;
527 				goto out;
528 			}
529 
530 			if (leaf->bitmap != BIT(idx)) {
531 				pr_err("Child %d has wrong seqno idx, found %d, expected %d\n",
532 				       idx, ilog2(leaf->bitmap), idx);
533 				err = -EINVAL;
534 				goto out;
535 			}
536 		}
537 	}
538 out:
539 	return dump_syncmap(sync, err);
540 }
541 
542 static int igt_syncmap_random(void *arg)
543 {
544 	I915_RND_STATE(prng);
545 	IGT_TIMEOUT(end_time);
546 	struct i915_syncmap *sync;
547 	unsigned long count, phase, i;
548 	u32 seqno;
549 	int err;
550 
551 	i915_syncmap_init(&sync);
552 
553 	/*
554 	 * Having tried to test the individual operations within i915_syncmap,
555 	 * run a smoketest exploring the entire u64 space with random
556 	 * insertions.
557 	 */
558 
559 	count = 0;
560 	phase = jiffies + HZ/100 + 1;
561 	do {
562 		u64 context = i915_prandom_u64_state(&prng);
563 
564 		err = i915_syncmap_set(&sync, context, 0);
565 		if (err)
566 			goto out;
567 
568 		count++;
569 	} while (!time_after(jiffies, phase));
570 	seqno = 0;
571 
572 	phase = 0;
573 	do {
574 		I915_RND_STATE(ctx);
575 		u32 last_seqno = seqno;
576 		bool expect;
577 
578 		seqno = prandom_u32_state(&prng);
579 		expect = seqno_later(last_seqno, seqno);
580 
581 		for (i = 0; i < count; i++) {
582 			u64 context = i915_prandom_u64_state(&ctx);
583 
584 			if (i915_syncmap_is_later(&sync, context, seqno) != expect) {
585 				pr_err("context=%llu, last=%u this=%u did not match expectation (%d)\n",
586 				       context, last_seqno, seqno, expect);
587 				err = -EINVAL;
588 				goto out;
589 			}
590 
591 			err = i915_syncmap_set(&sync, context, seqno);
592 			if (err)
593 				goto out;
594 		}
595 
596 		phase++;
597 	} while (!__igt_timeout(end_time, NULL));
598 	pr_debug("Completed %lu passes, each of %lu contexts\n", phase, count);
599 out:
600 	return dump_syncmap(sync, err);
601 }
602 
603 int i915_syncmap_mock_selftests(void)
604 {
605 	static const struct i915_subtest tests[] = {
606 		SUBTEST(igt_syncmap_init),
607 		SUBTEST(igt_syncmap_one),
608 		SUBTEST(igt_syncmap_join_above),
609 		SUBTEST(igt_syncmap_join_below),
610 		SUBTEST(igt_syncmap_neighbours),
611 		SUBTEST(igt_syncmap_compact),
612 		SUBTEST(igt_syncmap_random),
613 	};
614 
615 	return i915_subtests(tests, NULL);
616 }
617