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