162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright © 2017 Intel Corporation
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
562306a36Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
662306a36Sopenharmony_ci * to deal in the Software without restriction, including without limitation
762306a36Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
862306a36Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
962306a36Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * The above copyright notice and this permission notice (including the next
1262306a36Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
1362306a36Sopenharmony_ci * Software.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1662306a36Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1762306a36Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1862306a36Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1962306a36Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2062306a36Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
2162306a36Sopenharmony_ci * IN THE SOFTWARE.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#include "../i915_selftest.h"
2662306a36Sopenharmony_ci#include "i915_random.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic char *
2962306a36Sopenharmony_ci__sync_print(struct i915_syncmap *p,
3062306a36Sopenharmony_ci	     char *buf, unsigned long *sz,
3162306a36Sopenharmony_ci	     unsigned int depth,
3262306a36Sopenharmony_ci	     unsigned int last,
3362306a36Sopenharmony_ci	     unsigned int idx)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	unsigned long len;
3662306a36Sopenharmony_ci	unsigned int i, X;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	if (depth) {
3962306a36Sopenharmony_ci		unsigned int d;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci		for (d = 0; d < depth - 1; d++) {
4262306a36Sopenharmony_ci			if (last & BIT(depth - d - 1))
4362306a36Sopenharmony_ci				len = scnprintf(buf, *sz, "|   ");
4462306a36Sopenharmony_ci			else
4562306a36Sopenharmony_ci				len = scnprintf(buf, *sz, "    ");
4662306a36Sopenharmony_ci			buf += len;
4762306a36Sopenharmony_ci			*sz -= len;
4862306a36Sopenharmony_ci		}
4962306a36Sopenharmony_ci		len = scnprintf(buf, *sz, "%x-> ", idx);
5062306a36Sopenharmony_ci		buf += len;
5162306a36Sopenharmony_ci		*sz -= len;
5262306a36Sopenharmony_ci	}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	/* We mark bits after the prefix as "X" */
5562306a36Sopenharmony_ci	len = scnprintf(buf, *sz, "0x%016llx", p->prefix << p->height << SHIFT);
5662306a36Sopenharmony_ci	buf += len;
5762306a36Sopenharmony_ci	*sz -= len;
5862306a36Sopenharmony_ci	X = (p->height + SHIFT) / 4;
5962306a36Sopenharmony_ci	scnprintf(buf - X, *sz + X, "%*s", X, "XXXXXXXXXXXXXXXXX");
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (!p->height) {
6262306a36Sopenharmony_ci		for_each_set_bit(i, (unsigned long *)&p->bitmap, KSYNCMAP) {
6362306a36Sopenharmony_ci			len = scnprintf(buf, *sz, " %x:%x,",
6462306a36Sopenharmony_ci					i, __sync_seqno(p)[i]);
6562306a36Sopenharmony_ci			buf += len;
6662306a36Sopenharmony_ci			*sz -= len;
6762306a36Sopenharmony_ci		}
6862306a36Sopenharmony_ci		buf -= 1;
6962306a36Sopenharmony_ci		*sz += 1;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	len = scnprintf(buf, *sz, "\n");
7362306a36Sopenharmony_ci	buf += len;
7462306a36Sopenharmony_ci	*sz -= len;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (p->height) {
7762306a36Sopenharmony_ci		for_each_set_bit(i, (unsigned long *)&p->bitmap, KSYNCMAP) {
7862306a36Sopenharmony_ci			buf = __sync_print(__sync_child(p)[i], buf, sz,
7962306a36Sopenharmony_ci					   depth + 1,
8062306a36Sopenharmony_ci					   last << 1 | !!(p->bitmap >> (i + 1)),
8162306a36Sopenharmony_ci					   i);
8262306a36Sopenharmony_ci		}
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return buf;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic bool
8962306a36Sopenharmony_cii915_syncmap_print_to_buf(struct i915_syncmap *p, char *buf, unsigned long sz)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	if (!p)
9262306a36Sopenharmony_ci		return false;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	while (p->parent)
9562306a36Sopenharmony_ci		p = p->parent;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	__sync_print(p, buf, &sz, 0, 1, 0);
9862306a36Sopenharmony_ci	return true;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic int check_syncmap_free(struct i915_syncmap **sync)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	i915_syncmap_free(sync);
10462306a36Sopenharmony_ci	if (*sync) {
10562306a36Sopenharmony_ci		pr_err("sync not cleared after free\n");
10662306a36Sopenharmony_ci		return -EINVAL;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return 0;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic int dump_syncmap(struct i915_syncmap *sync, int err)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	char *buf;
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (!err)
11762306a36Sopenharmony_ci		return check_syncmap_free(&sync);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
12062306a36Sopenharmony_ci	if (!buf)
12162306a36Sopenharmony_ci		goto skip;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (i915_syncmap_print_to_buf(sync, buf, PAGE_SIZE))
12462306a36Sopenharmony_ci		pr_err("%s", buf);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	kfree(buf);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ciskip:
12962306a36Sopenharmony_ci	i915_syncmap_free(&sync);
13062306a36Sopenharmony_ci	return err;
13162306a36Sopenharmony_ci}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cistatic int igt_syncmap_init(void *arg)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct i915_syncmap *sync = (void *)~0ul;
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	/*
13862306a36Sopenharmony_ci	 * Cursory check that we can initialise a random pointer and transform
13962306a36Sopenharmony_ci	 * it into the root pointer of a syncmap.
14062306a36Sopenharmony_ci	 */
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	i915_syncmap_init(&sync);
14362306a36Sopenharmony_ci	return check_syncmap_free(&sync);
14462306a36Sopenharmony_ci}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_cistatic int check_seqno(struct i915_syncmap *leaf, unsigned int idx, u32 seqno)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	if (leaf->height) {
14962306a36Sopenharmony_ci		pr_err("%s: not a leaf, height is %d\n",
15062306a36Sopenharmony_ci		       __func__, leaf->height);
15162306a36Sopenharmony_ci		return -EINVAL;
15262306a36Sopenharmony_ci	}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (__sync_seqno(leaf)[idx] != seqno) {
15562306a36Sopenharmony_ci		pr_err("%s: seqno[%d], found %x, expected %x\n",
15662306a36Sopenharmony_ci		       __func__, idx, __sync_seqno(leaf)[idx], seqno);
15762306a36Sopenharmony_ci		return -EINVAL;
15862306a36Sopenharmony_ci	}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	return 0;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int check_one(struct i915_syncmap **sync, u64 context, u32 seqno)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	int err;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	err = i915_syncmap_set(sync, context, seqno);
16862306a36Sopenharmony_ci	if (err)
16962306a36Sopenharmony_ci		return err;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if ((*sync)->height) {
17262306a36Sopenharmony_ci		pr_err("Inserting first context=%llx did not return leaf (height=%d, prefix=%llx\n",
17362306a36Sopenharmony_ci		       context, (*sync)->height, (*sync)->prefix);
17462306a36Sopenharmony_ci		return -EINVAL;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	if ((*sync)->parent) {
17862306a36Sopenharmony_ci		pr_err("Inserting first context=%llx created branches!\n",
17962306a36Sopenharmony_ci		       context);
18062306a36Sopenharmony_ci		return -EINVAL;
18162306a36Sopenharmony_ci	}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	if (hweight32((*sync)->bitmap) != 1) {
18462306a36Sopenharmony_ci		pr_err("First bitmap does not contain a single entry, found %x (count=%d)!\n",
18562306a36Sopenharmony_ci		       (*sync)->bitmap, hweight32((*sync)->bitmap));
18662306a36Sopenharmony_ci		return -EINVAL;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	err = check_seqno((*sync), ilog2((*sync)->bitmap), seqno);
19062306a36Sopenharmony_ci	if (err)
19162306a36Sopenharmony_ci		return err;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	if (!i915_syncmap_is_later(sync, context, seqno)) {
19462306a36Sopenharmony_ci		pr_err("Lookup of first context=%llx/seqno=%x failed!\n",
19562306a36Sopenharmony_ci		       context, seqno);
19662306a36Sopenharmony_ci		return -EINVAL;
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	return 0;
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cistatic int igt_syncmap_one(void *arg)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	I915_RND_STATE(prng);
20562306a36Sopenharmony_ci	IGT_TIMEOUT(end_time);
20662306a36Sopenharmony_ci	struct i915_syncmap *sync;
20762306a36Sopenharmony_ci	unsigned long max = 1;
20862306a36Sopenharmony_ci	int err;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/*
21162306a36Sopenharmony_ci	 * Check that inserting a new id, creates a leaf and only that leaf.
21262306a36Sopenharmony_ci	 */
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	i915_syncmap_init(&sync);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	do {
21762306a36Sopenharmony_ci		u64 context = i915_prandom_u64_state(&prng);
21862306a36Sopenharmony_ci		unsigned long loop;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		err = check_syncmap_free(&sync);
22162306a36Sopenharmony_ci		if (err)
22262306a36Sopenharmony_ci			goto out;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		for (loop = 0; loop <= max; loop++) {
22562306a36Sopenharmony_ci			err = check_one(&sync, context,
22662306a36Sopenharmony_ci					prandom_u32_state(&prng));
22762306a36Sopenharmony_ci			if (err)
22862306a36Sopenharmony_ci				goto out;
22962306a36Sopenharmony_ci		}
23062306a36Sopenharmony_ci		max++;
23162306a36Sopenharmony_ci	} while (!__igt_timeout(end_time, NULL));
23262306a36Sopenharmony_ci	pr_debug("%s: Completed %lu single insertions\n",
23362306a36Sopenharmony_ci		 __func__, max * (max - 1) / 2);
23462306a36Sopenharmony_ciout:
23562306a36Sopenharmony_ci	return dump_syncmap(sync, err);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic int check_leaf(struct i915_syncmap **sync, u64 context, u32 seqno)
23962306a36Sopenharmony_ci{
24062306a36Sopenharmony_ci	int err;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	err = i915_syncmap_set(sync, context, seqno);
24362306a36Sopenharmony_ci	if (err)
24462306a36Sopenharmony_ci		return err;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if ((*sync)->height) {
24762306a36Sopenharmony_ci		pr_err("Inserting context=%llx did not return leaf (height=%d, prefix=%llx\n",
24862306a36Sopenharmony_ci		       context, (*sync)->height, (*sync)->prefix);
24962306a36Sopenharmony_ci		return -EINVAL;
25062306a36Sopenharmony_ci	}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	if (hweight32((*sync)->bitmap) != 1) {
25362306a36Sopenharmony_ci		pr_err("First entry into leaf (context=%llx) does not contain a single entry, found %x (count=%d)!\n",
25462306a36Sopenharmony_ci		       context, (*sync)->bitmap, hweight32((*sync)->bitmap));
25562306a36Sopenharmony_ci		return -EINVAL;
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	err = check_seqno((*sync), ilog2((*sync)->bitmap), seqno);
25962306a36Sopenharmony_ci	if (err)
26062306a36Sopenharmony_ci		return err;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (!i915_syncmap_is_later(sync, context, seqno)) {
26362306a36Sopenharmony_ci		pr_err("Lookup of first entry context=%llx/seqno=%x failed!\n",
26462306a36Sopenharmony_ci		       context, seqno);
26562306a36Sopenharmony_ci		return -EINVAL;
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	return 0;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_cistatic int igt_syncmap_join_above(void *arg)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct i915_syncmap *sync;
27462306a36Sopenharmony_ci	unsigned int pass, order;
27562306a36Sopenharmony_ci	int err;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	i915_syncmap_init(&sync);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/*
28062306a36Sopenharmony_ci	 * When we have a new id that doesn't fit inside the existing tree,
28162306a36Sopenharmony_ci	 * we need to add a new layer above.
28262306a36Sopenharmony_ci	 *
28362306a36Sopenharmony_ci	 * 1: 0x00000001
28462306a36Sopenharmony_ci	 * 2: 0x00000010
28562306a36Sopenharmony_ci	 * 3: 0x00000100
28662306a36Sopenharmony_ci	 * 4: 0x00001000
28762306a36Sopenharmony_ci	 * ...
28862306a36Sopenharmony_ci	 * Each pass the common prefix shrinks and we have to insert a join.
28962306a36Sopenharmony_ci	 * Each join will only contain two branches, the latest of which
29062306a36Sopenharmony_ci	 * is always a leaf.
29162306a36Sopenharmony_ci	 *
29262306a36Sopenharmony_ci	 * If we then reuse the same set of contexts, we expect to build an
29362306a36Sopenharmony_ci	 * identical tree.
29462306a36Sopenharmony_ci	 */
29562306a36Sopenharmony_ci	for (pass = 0; pass < 3; pass++) {
29662306a36Sopenharmony_ci		for (order = 0; order < 64; order += SHIFT) {
29762306a36Sopenharmony_ci			u64 context = BIT_ULL(order);
29862306a36Sopenharmony_ci			struct i915_syncmap *join;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci			err = check_leaf(&sync, context, 0);
30162306a36Sopenharmony_ci			if (err)
30262306a36Sopenharmony_ci				goto out;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci			join = sync->parent;
30562306a36Sopenharmony_ci			if (!join) /* very first insert will have no parents */
30662306a36Sopenharmony_ci				continue;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci			if (!join->height) {
30962306a36Sopenharmony_ci				pr_err("Parent with no height!\n");
31062306a36Sopenharmony_ci				err = -EINVAL;
31162306a36Sopenharmony_ci				goto out;
31262306a36Sopenharmony_ci			}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci			if (hweight32(join->bitmap) != 2) {
31562306a36Sopenharmony_ci				pr_err("Join does not have 2 children: %x (%d)\n",
31662306a36Sopenharmony_ci				       join->bitmap, hweight32(join->bitmap));
31762306a36Sopenharmony_ci				err = -EINVAL;
31862306a36Sopenharmony_ci				goto out;
31962306a36Sopenharmony_ci			}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci			if (__sync_child(join)[__sync_branch_idx(join, context)] != sync) {
32262306a36Sopenharmony_ci				pr_err("Leaf misplaced in parent!\n");
32362306a36Sopenharmony_ci				err = -EINVAL;
32462306a36Sopenharmony_ci				goto out;
32562306a36Sopenharmony_ci			}
32662306a36Sopenharmony_ci		}
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ciout:
32962306a36Sopenharmony_ci	return dump_syncmap(sync, err);
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic int igt_syncmap_join_below(void *arg)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	struct i915_syncmap *sync;
33562306a36Sopenharmony_ci	unsigned int step, order, idx;
33662306a36Sopenharmony_ci	int err = -ENODEV;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	i915_syncmap_init(&sync);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/*
34162306a36Sopenharmony_ci	 * Check that we can split a compacted branch by replacing it with
34262306a36Sopenharmony_ci	 * a join.
34362306a36Sopenharmony_ci	 */
34462306a36Sopenharmony_ci	for (step = 0; step < KSYNCMAP; step++) {
34562306a36Sopenharmony_ci		for (order = 64 - SHIFT; order > 0; order -= SHIFT) {
34662306a36Sopenharmony_ci			u64 context = step * BIT_ULL(order);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci			err = i915_syncmap_set(&sync, context, 0);
34962306a36Sopenharmony_ci			if (err)
35062306a36Sopenharmony_ci				goto out;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci			if (sync->height) {
35362306a36Sopenharmony_ci				pr_err("Inserting context=%llx (order=%d, step=%d) did not return leaf (height=%d, prefix=%llx\n",
35462306a36Sopenharmony_ci				       context, order, step, sync->height, sync->prefix);
35562306a36Sopenharmony_ci				err = -EINVAL;
35662306a36Sopenharmony_ci				goto out;
35762306a36Sopenharmony_ci			}
35862306a36Sopenharmony_ci		}
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	for (step = 0; step < KSYNCMAP; step++) {
36262306a36Sopenharmony_ci		for (order = SHIFT; order < 64; order += SHIFT) {
36362306a36Sopenharmony_ci			u64 context = step * BIT_ULL(order);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci			if (!i915_syncmap_is_later(&sync, context, 0)) {
36662306a36Sopenharmony_ci				pr_err("1: context %llx (order=%d, step=%d) not found\n",
36762306a36Sopenharmony_ci				       context, order, step);
36862306a36Sopenharmony_ci				err = -EINVAL;
36962306a36Sopenharmony_ci				goto out;
37062306a36Sopenharmony_ci			}
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci			for (idx = 1; idx < KSYNCMAP; idx++) {
37362306a36Sopenharmony_ci				if (i915_syncmap_is_later(&sync, context + idx, 0)) {
37462306a36Sopenharmony_ci					pr_err("1: context %llx (order=%d, step=%d) should not exist\n",
37562306a36Sopenharmony_ci					       context + idx, order, step);
37662306a36Sopenharmony_ci					err = -EINVAL;
37762306a36Sopenharmony_ci					goto out;
37862306a36Sopenharmony_ci				}
37962306a36Sopenharmony_ci			}
38062306a36Sopenharmony_ci		}
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	for (order = SHIFT; order < 64; order += SHIFT) {
38462306a36Sopenharmony_ci		for (step = 0; step < KSYNCMAP; step++) {
38562306a36Sopenharmony_ci			u64 context = step * BIT_ULL(order);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci			if (!i915_syncmap_is_later(&sync, context, 0)) {
38862306a36Sopenharmony_ci				pr_err("2: context %llx (order=%d, step=%d) not found\n",
38962306a36Sopenharmony_ci				       context, order, step);
39062306a36Sopenharmony_ci				err = -EINVAL;
39162306a36Sopenharmony_ci				goto out;
39262306a36Sopenharmony_ci			}
39362306a36Sopenharmony_ci		}
39462306a36Sopenharmony_ci	}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ciout:
39762306a36Sopenharmony_ci	return dump_syncmap(sync, err);
39862306a36Sopenharmony_ci}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_cistatic int igt_syncmap_neighbours(void *arg)
40162306a36Sopenharmony_ci{
40262306a36Sopenharmony_ci	I915_RND_STATE(prng);
40362306a36Sopenharmony_ci	IGT_TIMEOUT(end_time);
40462306a36Sopenharmony_ci	struct i915_syncmap *sync;
40562306a36Sopenharmony_ci	int err = -ENODEV;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/*
40862306a36Sopenharmony_ci	 * Each leaf holds KSYNCMAP seqno. Check that when we create KSYNCMAP
40962306a36Sopenharmony_ci	 * neighbouring ids, they all fit into the same leaf.
41062306a36Sopenharmony_ci	 */
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	i915_syncmap_init(&sync);
41362306a36Sopenharmony_ci	do {
41462306a36Sopenharmony_ci		u64 context = i915_prandom_u64_state(&prng) & ~MASK;
41562306a36Sopenharmony_ci		unsigned int idx;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci		if (i915_syncmap_is_later(&sync, context, 0)) /* Skip repeats */
41862306a36Sopenharmony_ci			continue;
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci		for (idx = 0; idx < KSYNCMAP; idx++) {
42162306a36Sopenharmony_ci			err = i915_syncmap_set(&sync, context + idx, 0);
42262306a36Sopenharmony_ci			if (err)
42362306a36Sopenharmony_ci				goto out;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci			if (sync->height) {
42662306a36Sopenharmony_ci				pr_err("Inserting context=%llx did not return leaf (height=%d, prefix=%llx\n",
42762306a36Sopenharmony_ci				       context, sync->height, sync->prefix);
42862306a36Sopenharmony_ci				err = -EINVAL;
42962306a36Sopenharmony_ci				goto out;
43062306a36Sopenharmony_ci			}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci			if (sync->bitmap != BIT(idx + 1) - 1) {
43362306a36Sopenharmony_ci				pr_err("Inserting neighbouring context=0x%llx+%d, did not fit into the same leaf bitmap=%x (%d), expected %lx (%d)\n",
43462306a36Sopenharmony_ci				       context, idx,
43562306a36Sopenharmony_ci				       sync->bitmap, hweight32(sync->bitmap),
43662306a36Sopenharmony_ci				       BIT(idx + 1) - 1, idx + 1);
43762306a36Sopenharmony_ci				err = -EINVAL;
43862306a36Sopenharmony_ci				goto out;
43962306a36Sopenharmony_ci			}
44062306a36Sopenharmony_ci		}
44162306a36Sopenharmony_ci	} while (!__igt_timeout(end_time, NULL));
44262306a36Sopenharmony_ciout:
44362306a36Sopenharmony_ci	return dump_syncmap(sync, err);
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic int igt_syncmap_compact(void *arg)
44762306a36Sopenharmony_ci{
44862306a36Sopenharmony_ci	struct i915_syncmap *sync;
44962306a36Sopenharmony_ci	unsigned int idx, order;
45062306a36Sopenharmony_ci	int err = -ENODEV;
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	i915_syncmap_init(&sync);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	/*
45562306a36Sopenharmony_ci	 * The syncmap are "space efficient" compressed radix trees - any
45662306a36Sopenharmony_ci	 * branch with only one child is skipped and replaced by the child.
45762306a36Sopenharmony_ci	 *
45862306a36Sopenharmony_ci	 * If we construct a tree with ids that are neighbouring at a non-zero
45962306a36Sopenharmony_ci	 * height, we form a join but each child of that join is directly a
46062306a36Sopenharmony_ci	 * leaf holding the single id.
46162306a36Sopenharmony_ci	 */
46262306a36Sopenharmony_ci	for (order = SHIFT; order < 64; order += SHIFT) {
46362306a36Sopenharmony_ci		err = check_syncmap_free(&sync);
46462306a36Sopenharmony_ci		if (err)
46562306a36Sopenharmony_ci			goto out;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci		/* Create neighbours in the parent */
46862306a36Sopenharmony_ci		for (idx = 0; idx < KSYNCMAP; idx++) {
46962306a36Sopenharmony_ci			u64 context = idx * BIT_ULL(order) + idx;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci			err = i915_syncmap_set(&sync, context, 0);
47262306a36Sopenharmony_ci			if (err)
47362306a36Sopenharmony_ci				goto out;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci			if (sync->height) {
47662306a36Sopenharmony_ci				pr_err("Inserting context=%llx (order=%d, idx=%d) did not return leaf (height=%d, prefix=%llx\n",
47762306a36Sopenharmony_ci				       context, order, idx,
47862306a36Sopenharmony_ci				       sync->height, sync->prefix);
47962306a36Sopenharmony_ci				err = -EINVAL;
48062306a36Sopenharmony_ci				goto out;
48162306a36Sopenharmony_ci			}
48262306a36Sopenharmony_ci		}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		sync = sync->parent;
48562306a36Sopenharmony_ci		if (sync->parent) {
48662306a36Sopenharmony_ci			pr_err("Parent (join) of last leaf was not the sync!\n");
48762306a36Sopenharmony_ci			err = -EINVAL;
48862306a36Sopenharmony_ci			goto out;
48962306a36Sopenharmony_ci		}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		if (sync->height != order) {
49262306a36Sopenharmony_ci			pr_err("Join does not have the expected height, found %d, expected %d\n",
49362306a36Sopenharmony_ci			       sync->height, order);
49462306a36Sopenharmony_ci			err = -EINVAL;
49562306a36Sopenharmony_ci			goto out;
49662306a36Sopenharmony_ci		}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		if (sync->bitmap != BIT(KSYNCMAP) - 1) {
49962306a36Sopenharmony_ci			pr_err("Join is not full!, found %x (%d) expected %lx (%d)\n",
50062306a36Sopenharmony_ci			       sync->bitmap, hweight32(sync->bitmap),
50162306a36Sopenharmony_ci			       BIT(KSYNCMAP) - 1, KSYNCMAP);
50262306a36Sopenharmony_ci			err = -EINVAL;
50362306a36Sopenharmony_ci			goto out;
50462306a36Sopenharmony_ci		}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci		/* Each of our children should be a leaf */
50762306a36Sopenharmony_ci		for (idx = 0; idx < KSYNCMAP; idx++) {
50862306a36Sopenharmony_ci			struct i915_syncmap *leaf = __sync_child(sync)[idx];
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci			if (leaf->height) {
51162306a36Sopenharmony_ci				pr_err("Child %d is a not leaf!\n", idx);
51262306a36Sopenharmony_ci				err = -EINVAL;
51362306a36Sopenharmony_ci				goto out;
51462306a36Sopenharmony_ci			}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci			if (leaf->parent != sync) {
51762306a36Sopenharmony_ci				pr_err("Child %d is not attached to us!\n",
51862306a36Sopenharmony_ci				       idx);
51962306a36Sopenharmony_ci				err = -EINVAL;
52062306a36Sopenharmony_ci				goto out;
52162306a36Sopenharmony_ci			}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci			if (!is_power_of_2(leaf->bitmap)) {
52462306a36Sopenharmony_ci				pr_err("Child %d holds more than one id, found %x (%d)\n",
52562306a36Sopenharmony_ci				       idx, leaf->bitmap, hweight32(leaf->bitmap));
52662306a36Sopenharmony_ci				err = -EINVAL;
52762306a36Sopenharmony_ci				goto out;
52862306a36Sopenharmony_ci			}
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci			if (leaf->bitmap != BIT(idx)) {
53162306a36Sopenharmony_ci				pr_err("Child %d has wrong seqno idx, found %d, expected %d\n",
53262306a36Sopenharmony_ci				       idx, ilog2(leaf->bitmap), idx);
53362306a36Sopenharmony_ci				err = -EINVAL;
53462306a36Sopenharmony_ci				goto out;
53562306a36Sopenharmony_ci			}
53662306a36Sopenharmony_ci		}
53762306a36Sopenharmony_ci	}
53862306a36Sopenharmony_ciout:
53962306a36Sopenharmony_ci	return dump_syncmap(sync, err);
54062306a36Sopenharmony_ci}
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_cistatic int igt_syncmap_random(void *arg)
54362306a36Sopenharmony_ci{
54462306a36Sopenharmony_ci	I915_RND_STATE(prng);
54562306a36Sopenharmony_ci	IGT_TIMEOUT(end_time);
54662306a36Sopenharmony_ci	struct i915_syncmap *sync;
54762306a36Sopenharmony_ci	unsigned long count, phase, i;
54862306a36Sopenharmony_ci	u32 seqno;
54962306a36Sopenharmony_ci	int err;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	i915_syncmap_init(&sync);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	/*
55462306a36Sopenharmony_ci	 * Having tried to test the individual operations within i915_syncmap,
55562306a36Sopenharmony_ci	 * run a smoketest exploring the entire u64 space with random
55662306a36Sopenharmony_ci	 * insertions.
55762306a36Sopenharmony_ci	 */
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	count = 0;
56062306a36Sopenharmony_ci	phase = jiffies + HZ/100 + 1;
56162306a36Sopenharmony_ci	do {
56262306a36Sopenharmony_ci		u64 context = i915_prandom_u64_state(&prng);
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci		err = i915_syncmap_set(&sync, context, 0);
56562306a36Sopenharmony_ci		if (err)
56662306a36Sopenharmony_ci			goto out;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci		count++;
56962306a36Sopenharmony_ci	} while (!time_after(jiffies, phase));
57062306a36Sopenharmony_ci	seqno = 0;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	phase = 0;
57362306a36Sopenharmony_ci	do {
57462306a36Sopenharmony_ci		I915_RND_STATE(ctx);
57562306a36Sopenharmony_ci		u32 last_seqno = seqno;
57662306a36Sopenharmony_ci		bool expect;
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_ci		seqno = prandom_u32_state(&prng);
57962306a36Sopenharmony_ci		expect = seqno_later(last_seqno, seqno);
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci		for (i = 0; i < count; i++) {
58262306a36Sopenharmony_ci			u64 context = i915_prandom_u64_state(&ctx);
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci			if (i915_syncmap_is_later(&sync, context, seqno) != expect) {
58562306a36Sopenharmony_ci				pr_err("context=%llu, last=%u this=%u did not match expectation (%d)\n",
58662306a36Sopenharmony_ci				       context, last_seqno, seqno, expect);
58762306a36Sopenharmony_ci				err = -EINVAL;
58862306a36Sopenharmony_ci				goto out;
58962306a36Sopenharmony_ci			}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci			err = i915_syncmap_set(&sync, context, seqno);
59262306a36Sopenharmony_ci			if (err)
59362306a36Sopenharmony_ci				goto out;
59462306a36Sopenharmony_ci		}
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci		phase++;
59762306a36Sopenharmony_ci	} while (!__igt_timeout(end_time, NULL));
59862306a36Sopenharmony_ci	pr_debug("Completed %lu passes, each of %lu contexts\n", phase, count);
59962306a36Sopenharmony_ciout:
60062306a36Sopenharmony_ci	return dump_syncmap(sync, err);
60162306a36Sopenharmony_ci}
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ciint i915_syncmap_mock_selftests(void)
60462306a36Sopenharmony_ci{
60562306a36Sopenharmony_ci	static const struct i915_subtest tests[] = {
60662306a36Sopenharmony_ci		SUBTEST(igt_syncmap_init),
60762306a36Sopenharmony_ci		SUBTEST(igt_syncmap_one),
60862306a36Sopenharmony_ci		SUBTEST(igt_syncmap_join_above),
60962306a36Sopenharmony_ci		SUBTEST(igt_syncmap_join_below),
61062306a36Sopenharmony_ci		SUBTEST(igt_syncmap_neighbours),
61162306a36Sopenharmony_ci		SUBTEST(igt_syncmap_compact),
61262306a36Sopenharmony_ci		SUBTEST(igt_syncmap_random),
61362306a36Sopenharmony_ci	};
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	return i915_subtests(tests, NULL);
61662306a36Sopenharmony_ci}
617