162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Test cases for the drm_mm range manager
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2022 Arthur Grillo <arthur.grillo@usp.br>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <kunit/test.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/prime_numbers.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include <linux/random.h>
1362306a36Sopenharmony_ci#include <linux/vmalloc.h>
1462306a36Sopenharmony_ci#include <linux/ktime.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <drm/drm_mm.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "../lib/drm_random.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic unsigned int random_seed;
2162306a36Sopenharmony_cistatic unsigned int max_iterations = 8192;
2262306a36Sopenharmony_cistatic unsigned int max_prime = 128;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cienum {
2562306a36Sopenharmony_ci	BEST,
2662306a36Sopenharmony_ci	BOTTOMUP,
2762306a36Sopenharmony_ci	TOPDOWN,
2862306a36Sopenharmony_ci	EVICT,
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic const struct insert_mode {
3262306a36Sopenharmony_ci	const char *name;
3362306a36Sopenharmony_ci	enum drm_mm_insert_mode mode;
3462306a36Sopenharmony_ci} insert_modes[] = {
3562306a36Sopenharmony_ci	[BEST] = { "best", DRM_MM_INSERT_BEST },
3662306a36Sopenharmony_ci	[BOTTOMUP] = { "bottom-up", DRM_MM_INSERT_LOW },
3762306a36Sopenharmony_ci	[TOPDOWN] = { "top-down", DRM_MM_INSERT_HIGH },
3862306a36Sopenharmony_ci	[EVICT] = { "evict", DRM_MM_INSERT_EVICT },
3962306a36Sopenharmony_ci	{}
4062306a36Sopenharmony_ci}, evict_modes[] = {
4162306a36Sopenharmony_ci	{ "bottom-up", DRM_MM_INSERT_LOW },
4262306a36Sopenharmony_ci	{ "top-down", DRM_MM_INSERT_HIGH },
4362306a36Sopenharmony_ci	{}
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic bool assert_no_holes(struct kunit *test, const struct drm_mm *mm)
4762306a36Sopenharmony_ci{
4862306a36Sopenharmony_ci	struct drm_mm_node *hole;
4962306a36Sopenharmony_ci	u64 hole_start, __always_unused hole_end;
5062306a36Sopenharmony_ci	unsigned long count;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	count = 0;
5362306a36Sopenharmony_ci	drm_mm_for_each_hole(hole, mm, hole_start, hole_end)
5462306a36Sopenharmony_ci		count++;
5562306a36Sopenharmony_ci	if (count) {
5662306a36Sopenharmony_ci		KUNIT_FAIL(test,
5762306a36Sopenharmony_ci			   "Expected to find no holes (after reserve), found %lu instead\n", count);
5862306a36Sopenharmony_ci		return false;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	drm_mm_for_each_node(hole, mm) {
6262306a36Sopenharmony_ci		if (drm_mm_hole_follows(hole)) {
6362306a36Sopenharmony_ci			KUNIT_FAIL(test, "Hole follows node, expected none!\n");
6462306a36Sopenharmony_ci			return false;
6562306a36Sopenharmony_ci		}
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return true;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic bool assert_one_hole(struct kunit *test, const struct drm_mm *mm, u64 start, u64 end)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct drm_mm_node *hole;
7462306a36Sopenharmony_ci	u64 hole_start, hole_end;
7562306a36Sopenharmony_ci	unsigned long count;
7662306a36Sopenharmony_ci	bool ok = true;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	if (end <= start)
7962306a36Sopenharmony_ci		return true;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	count = 0;
8262306a36Sopenharmony_ci	drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
8362306a36Sopenharmony_ci		if (start != hole_start || end != hole_end) {
8462306a36Sopenharmony_ci			if (ok)
8562306a36Sopenharmony_ci				KUNIT_FAIL(test,
8662306a36Sopenharmony_ci					   "empty mm has incorrect hole, found (%llx, %llx), expect (%llx, %llx)\n",
8762306a36Sopenharmony_ci					   hole_start, hole_end, start, end);
8862306a36Sopenharmony_ci			ok = false;
8962306a36Sopenharmony_ci		}
9062306a36Sopenharmony_ci		count++;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci	if (count != 1) {
9362306a36Sopenharmony_ci		KUNIT_FAIL(test, "Expected to find one hole, found %lu instead\n", count);
9462306a36Sopenharmony_ci		ok = false;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return ok;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic bool assert_continuous(struct kunit *test, const struct drm_mm *mm, u64 size)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct drm_mm_node *node, *check, *found;
10362306a36Sopenharmony_ci	unsigned long n;
10462306a36Sopenharmony_ci	u64 addr;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (!assert_no_holes(test, mm))
10762306a36Sopenharmony_ci		return false;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	n = 0;
11062306a36Sopenharmony_ci	addr = 0;
11162306a36Sopenharmony_ci	drm_mm_for_each_node(node, mm) {
11262306a36Sopenharmony_ci		if (node->start != addr) {
11362306a36Sopenharmony_ci			KUNIT_FAIL(test, "node[%ld] list out of order, expected %llx found %llx\n",
11462306a36Sopenharmony_ci				   n, addr, node->start);
11562306a36Sopenharmony_ci			return false;
11662306a36Sopenharmony_ci		}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci		if (node->size != size) {
11962306a36Sopenharmony_ci			KUNIT_FAIL(test, "node[%ld].size incorrect, expected %llx, found %llx\n",
12062306a36Sopenharmony_ci				   n, size, node->size);
12162306a36Sopenharmony_ci			return false;
12262306a36Sopenharmony_ci		}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci		if (drm_mm_hole_follows(node)) {
12562306a36Sopenharmony_ci			KUNIT_FAIL(test, "node[%ld] is followed by a hole!\n", n);
12662306a36Sopenharmony_ci			return false;
12762306a36Sopenharmony_ci		}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		found = NULL;
13062306a36Sopenharmony_ci		drm_mm_for_each_node_in_range(check, mm, addr, addr + size) {
13162306a36Sopenharmony_ci			if (node != check) {
13262306a36Sopenharmony_ci				KUNIT_FAIL(test,
13362306a36Sopenharmony_ci					   "lookup return wrong node, expected start %llx, found %llx\n",
13462306a36Sopenharmony_ci					   node->start, check->start);
13562306a36Sopenharmony_ci				return false;
13662306a36Sopenharmony_ci			}
13762306a36Sopenharmony_ci			found = check;
13862306a36Sopenharmony_ci		}
13962306a36Sopenharmony_ci		if (!found) {
14062306a36Sopenharmony_ci			KUNIT_FAIL(test, "lookup failed for node %llx + %llx\n", addr, size);
14162306a36Sopenharmony_ci			return false;
14262306a36Sopenharmony_ci		}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci		addr += size;
14562306a36Sopenharmony_ci		n++;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return true;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic u64 misalignment(struct drm_mm_node *node, u64 alignment)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	u64 rem;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	if (!alignment)
15662306a36Sopenharmony_ci		return 0;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	div64_u64_rem(node->start, alignment, &rem);
15962306a36Sopenharmony_ci	return rem;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic bool assert_node(struct kunit *test, struct drm_mm_node *node, struct drm_mm *mm,
16362306a36Sopenharmony_ci			u64 size, u64 alignment, unsigned long color)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	bool ok = true;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (!drm_mm_node_allocated(node) || node->mm != mm) {
16862306a36Sopenharmony_ci		KUNIT_FAIL(test, "node not allocated\n");
16962306a36Sopenharmony_ci		ok = false;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	if (node->size != size) {
17362306a36Sopenharmony_ci		KUNIT_FAIL(test, "node has wrong size, found %llu, expected %llu\n",
17462306a36Sopenharmony_ci			   node->size, size);
17562306a36Sopenharmony_ci		ok = false;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	if (misalignment(node, alignment)) {
17962306a36Sopenharmony_ci		KUNIT_FAIL(test,
18062306a36Sopenharmony_ci			   "node is misaligned, start %llx rem %llu, expected alignment %llu\n",
18162306a36Sopenharmony_ci			   node->start, misalignment(node, alignment), alignment);
18262306a36Sopenharmony_ci		ok = false;
18362306a36Sopenharmony_ci	}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (node->color != color) {
18662306a36Sopenharmony_ci		KUNIT_FAIL(test, "node has wrong color, found %lu, expected %lu\n",
18762306a36Sopenharmony_ci			   node->color, color);
18862306a36Sopenharmony_ci		ok = false;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	return ok;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic void drm_test_mm_init(struct kunit *test)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	const unsigned int size = 4096;
19762306a36Sopenharmony_ci	struct drm_mm mm;
19862306a36Sopenharmony_ci	struct drm_mm_node tmp;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* Start with some simple checks on initialising the struct drm_mm */
20162306a36Sopenharmony_ci	memset(&mm, 0, sizeof(mm));
20262306a36Sopenharmony_ci	KUNIT_ASSERT_FALSE_MSG(test, drm_mm_initialized(&mm),
20362306a36Sopenharmony_ci			       "zeroed mm claims to be initialized\n");
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	memset(&mm, 0xff, sizeof(mm));
20662306a36Sopenharmony_ci	drm_mm_init(&mm, 0, size);
20762306a36Sopenharmony_ci	if (!drm_mm_initialized(&mm)) {
20862306a36Sopenharmony_ci		KUNIT_FAIL(test, "mm claims not to be initialized\n");
20962306a36Sopenharmony_ci		goto out;
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (!drm_mm_clean(&mm)) {
21362306a36Sopenharmony_ci		KUNIT_FAIL(test, "mm not empty on creation\n");
21462306a36Sopenharmony_ci		goto out;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	/* After creation, it should all be one massive hole */
21862306a36Sopenharmony_ci	if (!assert_one_hole(test, &mm, 0, size)) {
21962306a36Sopenharmony_ci		KUNIT_FAIL(test, "");
22062306a36Sopenharmony_ci		goto out;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	memset(&tmp, 0, sizeof(tmp));
22462306a36Sopenharmony_ci	tmp.start = 0;
22562306a36Sopenharmony_ci	tmp.size = size;
22662306a36Sopenharmony_ci	if (drm_mm_reserve_node(&mm, &tmp)) {
22762306a36Sopenharmony_ci		KUNIT_FAIL(test, "failed to reserve whole drm_mm\n");
22862306a36Sopenharmony_ci		goto out;
22962306a36Sopenharmony_ci	}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	/* After filling the range entirely, there should be no holes */
23262306a36Sopenharmony_ci	if (!assert_no_holes(test, &mm)) {
23362306a36Sopenharmony_ci		KUNIT_FAIL(test, "");
23462306a36Sopenharmony_ci		goto out;
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	/* And then after emptying it again, the massive hole should be back */
23862306a36Sopenharmony_ci	drm_mm_remove_node(&tmp);
23962306a36Sopenharmony_ci	if (!assert_one_hole(test, &mm, 0, size)) {
24062306a36Sopenharmony_ci		KUNIT_FAIL(test, "");
24162306a36Sopenharmony_ci		goto out;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ciout:
24562306a36Sopenharmony_ci	drm_mm_takedown(&mm);
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic void drm_test_mm_debug(struct kunit *test)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	struct drm_mm mm;
25162306a36Sopenharmony_ci	struct drm_mm_node nodes[2];
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	/* Create a small drm_mm with a couple of nodes and a few holes, and
25462306a36Sopenharmony_ci	 * check that the debug iterator doesn't explode over a trivial drm_mm.
25562306a36Sopenharmony_ci	 */
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	drm_mm_init(&mm, 0, 4096);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	memset(nodes, 0, sizeof(nodes));
26062306a36Sopenharmony_ci	nodes[0].start = 512;
26162306a36Sopenharmony_ci	nodes[0].size = 1024;
26262306a36Sopenharmony_ci	KUNIT_ASSERT_FALSE_MSG(test, drm_mm_reserve_node(&mm, &nodes[0]),
26362306a36Sopenharmony_ci			       "failed to reserve node[0] {start=%lld, size=%lld)\n",
26462306a36Sopenharmony_ci			       nodes[0].start, nodes[0].size);
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	nodes[1].size = 1024;
26762306a36Sopenharmony_ci	nodes[1].start = 4096 - 512 - nodes[1].size;
26862306a36Sopenharmony_ci	KUNIT_ASSERT_FALSE_MSG(test, drm_mm_reserve_node(&mm, &nodes[1]),
26962306a36Sopenharmony_ci			       "failed to reserve node[0] {start=%lld, size=%lld)\n",
27062306a36Sopenharmony_ci			       nodes[0].start, nodes[0].size);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic struct drm_mm_node *set_node(struct drm_mm_node *node,
27462306a36Sopenharmony_ci				    u64 start, u64 size)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	node->start = start;
27762306a36Sopenharmony_ci	node->size = size;
27862306a36Sopenharmony_ci	return node;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_cistatic bool expect_reserve_fail(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *node)
28262306a36Sopenharmony_ci{
28362306a36Sopenharmony_ci	int err;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	err = drm_mm_reserve_node(mm, node);
28662306a36Sopenharmony_ci	if (likely(err == -ENOSPC))
28762306a36Sopenharmony_ci		return true;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if (!err) {
29062306a36Sopenharmony_ci		KUNIT_FAIL(test, "impossible reserve succeeded, node %llu + %llu\n",
29162306a36Sopenharmony_ci			   node->start, node->size);
29262306a36Sopenharmony_ci		drm_mm_remove_node(node);
29362306a36Sopenharmony_ci	} else {
29462306a36Sopenharmony_ci		KUNIT_FAIL(test,
29562306a36Sopenharmony_ci			   "impossible reserve failed with wrong error %d [expected %d], node %llu + %llu\n",
29662306a36Sopenharmony_ci		       err, -ENOSPC, node->start, node->size);
29762306a36Sopenharmony_ci	}
29862306a36Sopenharmony_ci	return false;
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistatic bool noinline_for_stack check_reserve_boundaries(struct kunit *test, struct drm_mm *mm,
30262306a36Sopenharmony_ci							unsigned int count,
30362306a36Sopenharmony_ci							u64 size)
30462306a36Sopenharmony_ci{
30562306a36Sopenharmony_ci	const struct boundary {
30662306a36Sopenharmony_ci		u64 start, size;
30762306a36Sopenharmony_ci		const char *name;
30862306a36Sopenharmony_ci	} boundaries[] = {
30962306a36Sopenharmony_ci#define B(st, sz) { (st), (sz), "{ " #st ", " #sz "}" }
31062306a36Sopenharmony_ci		B(0, 0),
31162306a36Sopenharmony_ci		B(-size, 0),
31262306a36Sopenharmony_ci		B(size, 0),
31362306a36Sopenharmony_ci		B(size * count, 0),
31462306a36Sopenharmony_ci		B(-size, size),
31562306a36Sopenharmony_ci		B(-size, -size),
31662306a36Sopenharmony_ci		B(-size, 2 * size),
31762306a36Sopenharmony_ci		B(0, -size),
31862306a36Sopenharmony_ci		B(size, -size),
31962306a36Sopenharmony_ci		B(count * size, size),
32062306a36Sopenharmony_ci		B(count * size, -size),
32162306a36Sopenharmony_ci		B(count * size, count * size),
32262306a36Sopenharmony_ci		B(count * size, -count * size),
32362306a36Sopenharmony_ci		B(count * size, -(count + 1) * size),
32462306a36Sopenharmony_ci		B((count + 1) * size, size),
32562306a36Sopenharmony_ci		B((count + 1) * size, -size),
32662306a36Sopenharmony_ci		B((count + 1) * size, -2 * size),
32762306a36Sopenharmony_ci#undef B
32862306a36Sopenharmony_ci	};
32962306a36Sopenharmony_ci	struct drm_mm_node tmp = {};
33062306a36Sopenharmony_ci	int n;
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	for (n = 0; n < ARRAY_SIZE(boundaries); n++) {
33362306a36Sopenharmony_ci		if (!expect_reserve_fail(test, mm, set_node(&tmp, boundaries[n].start,
33462306a36Sopenharmony_ci							    boundaries[n].size))) {
33562306a36Sopenharmony_ci			KUNIT_FAIL(test, "boundary[%d:%s] failed, count=%u, size=%lld\n",
33662306a36Sopenharmony_ci				   n, boundaries[n].name, count, size);
33762306a36Sopenharmony_ci			return false;
33862306a36Sopenharmony_ci		}
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	return true;
34262306a36Sopenharmony_ci}
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cistatic int __drm_test_mm_reserve(struct kunit *test, unsigned int count, u64 size)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	DRM_RND_STATE(prng, random_seed);
34762306a36Sopenharmony_ci	struct drm_mm mm;
34862306a36Sopenharmony_ci	struct drm_mm_node tmp, *nodes, *node, *next;
34962306a36Sopenharmony_ci	unsigned int *order, n, m, o = 0;
35062306a36Sopenharmony_ci	int ret, err;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	/* For exercising drm_mm_reserve_node(), we want to check that
35362306a36Sopenharmony_ci	 * reservations outside of the drm_mm range are rejected, and to
35462306a36Sopenharmony_ci	 * overlapping and otherwise already occupied ranges. Afterwards,
35562306a36Sopenharmony_ci	 * the tree and nodes should be intact.
35662306a36Sopenharmony_ci	 */
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	DRM_MM_BUG_ON(!count);
35962306a36Sopenharmony_ci	DRM_MM_BUG_ON(!size);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	ret = -ENOMEM;
36262306a36Sopenharmony_ci	order = drm_random_order(count, &prng);
36362306a36Sopenharmony_ci	if (!order)
36462306a36Sopenharmony_ci		goto err;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	nodes = vzalloc(array_size(count, sizeof(*nodes)));
36762306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE(test, nodes);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	ret = -EINVAL;
37062306a36Sopenharmony_ci	drm_mm_init(&mm, 0, count * size);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (!check_reserve_boundaries(test, &mm, count, size))
37362306a36Sopenharmony_ci		goto out;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	for (n = 0; n < count; n++) {
37662306a36Sopenharmony_ci		nodes[n].start = order[n] * size;
37762306a36Sopenharmony_ci		nodes[n].size = size;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci		err = drm_mm_reserve_node(&mm, &nodes[n]);
38062306a36Sopenharmony_ci		if (err) {
38162306a36Sopenharmony_ci			KUNIT_FAIL(test, "reserve failed, step %d, start %llu\n",
38262306a36Sopenharmony_ci				   n, nodes[n].start);
38362306a36Sopenharmony_ci			ret = err;
38462306a36Sopenharmony_ci			goto out;
38562306a36Sopenharmony_ci		}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		if (!drm_mm_node_allocated(&nodes[n])) {
38862306a36Sopenharmony_ci			KUNIT_FAIL(test, "reserved node not allocated! step %d, start %llu\n",
38962306a36Sopenharmony_ci				   n, nodes[n].start);
39062306a36Sopenharmony_ci			goto out;
39162306a36Sopenharmony_ci		}
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		if (!expect_reserve_fail(test, &mm, &nodes[n]))
39462306a36Sopenharmony_ci			goto out;
39562306a36Sopenharmony_ci	}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	/* After random insertion the nodes should be in order */
39862306a36Sopenharmony_ci	if (!assert_continuous(test, &mm, size))
39962306a36Sopenharmony_ci		goto out;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	/* Repeated use should then fail */
40262306a36Sopenharmony_ci	drm_random_reorder(order, count, &prng);
40362306a36Sopenharmony_ci	for (n = 0; n < count; n++) {
40462306a36Sopenharmony_ci		if (!expect_reserve_fail(test, &mm, set_node(&tmp, order[n] * size, 1)))
40562306a36Sopenharmony_ci			goto out;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci		/* Remove and reinsert should work */
40862306a36Sopenharmony_ci		drm_mm_remove_node(&nodes[order[n]]);
40962306a36Sopenharmony_ci		err = drm_mm_reserve_node(&mm, &nodes[order[n]]);
41062306a36Sopenharmony_ci		if (err) {
41162306a36Sopenharmony_ci			KUNIT_FAIL(test, "reserve failed, step %d, start %llu\n",
41262306a36Sopenharmony_ci				   n, nodes[n].start);
41362306a36Sopenharmony_ci			ret = err;
41462306a36Sopenharmony_ci			goto out;
41562306a36Sopenharmony_ci		}
41662306a36Sopenharmony_ci	}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	if (!assert_continuous(test, &mm, size))
41962306a36Sopenharmony_ci		goto out;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	/* Overlapping use should then fail */
42262306a36Sopenharmony_ci	for (n = 0; n < count; n++) {
42362306a36Sopenharmony_ci		if (!expect_reserve_fail(test, &mm, set_node(&tmp, 0, size * count)))
42462306a36Sopenharmony_ci			goto out;
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci	for (n = 0; n < count; n++) {
42762306a36Sopenharmony_ci		if (!expect_reserve_fail(test, &mm, set_node(&tmp, size * n, size * (count - n))))
42862306a36Sopenharmony_ci			goto out;
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	/* Remove several, reinsert, check full */
43262306a36Sopenharmony_ci	for_each_prime_number(n, min(max_prime, count)) {
43362306a36Sopenharmony_ci		for (m = 0; m < n; m++) {
43462306a36Sopenharmony_ci			node = &nodes[order[(o + m) % count]];
43562306a36Sopenharmony_ci			drm_mm_remove_node(node);
43662306a36Sopenharmony_ci		}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci		for (m = 0; m < n; m++) {
43962306a36Sopenharmony_ci			node = &nodes[order[(o + m) % count]];
44062306a36Sopenharmony_ci			err = drm_mm_reserve_node(&mm, node);
44162306a36Sopenharmony_ci			if (err) {
44262306a36Sopenharmony_ci				KUNIT_FAIL(test, "reserve failed, step %d/%d, start %llu\n",
44362306a36Sopenharmony_ci					   m, n, node->start);
44462306a36Sopenharmony_ci				ret = err;
44562306a36Sopenharmony_ci				goto out;
44662306a36Sopenharmony_ci			}
44762306a36Sopenharmony_ci		}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci		o += n;
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci		if (!assert_continuous(test, &mm, size))
45262306a36Sopenharmony_ci			goto out;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	ret = 0;
45662306a36Sopenharmony_ciout:
45762306a36Sopenharmony_ci	drm_mm_for_each_node_safe(node, next, &mm)
45862306a36Sopenharmony_ci		drm_mm_remove_node(node);
45962306a36Sopenharmony_ci	drm_mm_takedown(&mm);
46062306a36Sopenharmony_ci	vfree(nodes);
46162306a36Sopenharmony_ci	kfree(order);
46262306a36Sopenharmony_cierr:
46362306a36Sopenharmony_ci	return ret;
46462306a36Sopenharmony_ci}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_cistatic void drm_test_mm_reserve(struct kunit *test)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	const unsigned int count = min_t(unsigned int, BIT(10), max_iterations);
46962306a36Sopenharmony_ci	int n;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	for_each_prime_number_from(n, 1, 54) {
47262306a36Sopenharmony_ci		u64 size = BIT_ULL(n);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_reserve(test, count, size - 1));
47562306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_reserve(test, count, size));
47662306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_reserve(test, count, size + 1));
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci		cond_resched();
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic bool expect_insert(struct kunit *test, struct drm_mm *mm,
48362306a36Sopenharmony_ci			  struct drm_mm_node *node, u64 size, u64 alignment, unsigned long color,
48462306a36Sopenharmony_ci			const struct insert_mode *mode)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	int err;
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci	err = drm_mm_insert_node_generic(mm, node,
48962306a36Sopenharmony_ci					 size, alignment, color,
49062306a36Sopenharmony_ci					 mode->mode);
49162306a36Sopenharmony_ci	if (err) {
49262306a36Sopenharmony_ci		KUNIT_FAIL(test,
49362306a36Sopenharmony_ci			   "insert (size=%llu, alignment=%llu, color=%lu, mode=%s) failed with err=%d\n",
49462306a36Sopenharmony_ci			   size, alignment, color, mode->name, err);
49562306a36Sopenharmony_ci		return false;
49662306a36Sopenharmony_ci	}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci	if (!assert_node(test, node, mm, size, alignment, color)) {
49962306a36Sopenharmony_ci		drm_mm_remove_node(node);
50062306a36Sopenharmony_ci		return false;
50162306a36Sopenharmony_ci	}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci	return true;
50462306a36Sopenharmony_ci}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_cistatic bool expect_insert_fail(struct kunit *test, struct drm_mm *mm, u64 size)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	struct drm_mm_node tmp = {};
50962306a36Sopenharmony_ci	int err;
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	err = drm_mm_insert_node(mm, &tmp, size);
51262306a36Sopenharmony_ci	if (likely(err == -ENOSPC))
51362306a36Sopenharmony_ci		return true;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (!err) {
51662306a36Sopenharmony_ci		KUNIT_FAIL(test, "impossible insert succeeded, node %llu + %llu\n",
51762306a36Sopenharmony_ci			   tmp.start, tmp.size);
51862306a36Sopenharmony_ci		drm_mm_remove_node(&tmp);
51962306a36Sopenharmony_ci	} else {
52062306a36Sopenharmony_ci		KUNIT_FAIL(test,
52162306a36Sopenharmony_ci			   "impossible insert failed with wrong error %d [expected %d], size %llu\n",
52262306a36Sopenharmony_ci			   err, -ENOSPC, size);
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci	return false;
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic int __drm_test_mm_insert(struct kunit *test, unsigned int count, u64 size, bool replace)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	DRM_RND_STATE(prng, random_seed);
53062306a36Sopenharmony_ci	const struct insert_mode *mode;
53162306a36Sopenharmony_ci	struct drm_mm mm;
53262306a36Sopenharmony_ci	struct drm_mm_node *nodes, *node, *next;
53362306a36Sopenharmony_ci	unsigned int *order, n, m, o = 0;
53462306a36Sopenharmony_ci	int ret;
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	/* Fill a range with lots of nodes, check it doesn't fail too early */
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	DRM_MM_BUG_ON(!count);
53962306a36Sopenharmony_ci	DRM_MM_BUG_ON(!size);
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	ret = -ENOMEM;
54262306a36Sopenharmony_ci	nodes = vmalloc(array_size(count, sizeof(*nodes)));
54362306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE(test, nodes);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	order = drm_random_order(count, &prng);
54662306a36Sopenharmony_ci	if (!order)
54762306a36Sopenharmony_ci		goto err_nodes;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	ret = -EINVAL;
55062306a36Sopenharmony_ci	drm_mm_init(&mm, 0, count * size);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	for (mode = insert_modes; mode->name; mode++) {
55362306a36Sopenharmony_ci		for (n = 0; n < count; n++) {
55462306a36Sopenharmony_ci			struct drm_mm_node tmp;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci			node = replace ? &tmp : &nodes[n];
55762306a36Sopenharmony_ci			memset(node, 0, sizeof(*node));
55862306a36Sopenharmony_ci			if (!expect_insert(test, &mm, node, size, 0, n, mode)) {
55962306a36Sopenharmony_ci				KUNIT_FAIL(test, "%s insert failed, size %llu step %d\n",
56062306a36Sopenharmony_ci					   mode->name, size, n);
56162306a36Sopenharmony_ci				goto out;
56262306a36Sopenharmony_ci			}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci			if (replace) {
56562306a36Sopenharmony_ci				drm_mm_replace_node(&tmp, &nodes[n]);
56662306a36Sopenharmony_ci				if (drm_mm_node_allocated(&tmp)) {
56762306a36Sopenharmony_ci					KUNIT_FAIL(test,
56862306a36Sopenharmony_ci						   "replaced old-node still allocated! step %d\n",
56962306a36Sopenharmony_ci						   n);
57062306a36Sopenharmony_ci					goto out;
57162306a36Sopenharmony_ci				}
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci				if (!assert_node(test, &nodes[n], &mm, size, 0, n)) {
57462306a36Sopenharmony_ci					KUNIT_FAIL(test,
57562306a36Sopenharmony_ci						   "replaced node did not inherit parameters, size %llu step %d\n",
57662306a36Sopenharmony_ci						   size, n);
57762306a36Sopenharmony_ci					goto out;
57862306a36Sopenharmony_ci				}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci				if (tmp.start != nodes[n].start) {
58162306a36Sopenharmony_ci					KUNIT_FAIL(test,
58262306a36Sopenharmony_ci						   "replaced node mismatch location expected [%llx + %llx], found [%llx + %llx]\n",
58362306a36Sopenharmony_ci						   tmp.start, size, nodes[n].start, nodes[n].size);
58462306a36Sopenharmony_ci					goto out;
58562306a36Sopenharmony_ci				}
58662306a36Sopenharmony_ci			}
58762306a36Sopenharmony_ci		}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		/* After random insertion the nodes should be in order */
59062306a36Sopenharmony_ci		if (!assert_continuous(test, &mm, size))
59162306a36Sopenharmony_ci			goto out;
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci		/* Repeated use should then fail */
59462306a36Sopenharmony_ci		if (!expect_insert_fail(test, &mm, size))
59562306a36Sopenharmony_ci			goto out;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		/* Remove one and reinsert, as the only hole it should refill itself */
59862306a36Sopenharmony_ci		for (n = 0; n < count; n++) {
59962306a36Sopenharmony_ci			u64 addr = nodes[n].start;
60062306a36Sopenharmony_ci
60162306a36Sopenharmony_ci			drm_mm_remove_node(&nodes[n]);
60262306a36Sopenharmony_ci			if (!expect_insert(test, &mm, &nodes[n], size, 0, n, mode)) {
60362306a36Sopenharmony_ci				KUNIT_FAIL(test, "%s reinsert failed, size %llu step %d\n",
60462306a36Sopenharmony_ci					   mode->name, size, n);
60562306a36Sopenharmony_ci				goto out;
60662306a36Sopenharmony_ci			}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci			if (nodes[n].start != addr) {
60962306a36Sopenharmony_ci				KUNIT_FAIL(test,
61062306a36Sopenharmony_ci					   "%s reinsert node moved, step %d, expected %llx, found %llx\n",
61162306a36Sopenharmony_ci					   mode->name, n, addr, nodes[n].start);
61262306a36Sopenharmony_ci				goto out;
61362306a36Sopenharmony_ci			}
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci			if (!assert_continuous(test, &mm, size))
61662306a36Sopenharmony_ci				goto out;
61762306a36Sopenharmony_ci		}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci		/* Remove several, reinsert, check full */
62062306a36Sopenharmony_ci		for_each_prime_number(n, min(max_prime, count)) {
62162306a36Sopenharmony_ci			for (m = 0; m < n; m++) {
62262306a36Sopenharmony_ci				node = &nodes[order[(o + m) % count]];
62362306a36Sopenharmony_ci				drm_mm_remove_node(node);
62462306a36Sopenharmony_ci			}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci			for (m = 0; m < n; m++) {
62762306a36Sopenharmony_ci				node = &nodes[order[(o + m) % count]];
62862306a36Sopenharmony_ci				if (!expect_insert(test, &mm, node, size, 0, n, mode)) {
62962306a36Sopenharmony_ci					KUNIT_FAIL(test,
63062306a36Sopenharmony_ci						   "%s multiple reinsert failed, size %llu step %d\n",
63162306a36Sopenharmony_ci							   mode->name, size, n);
63262306a36Sopenharmony_ci					goto out;
63362306a36Sopenharmony_ci				}
63462306a36Sopenharmony_ci			}
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci			o += n;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci			if (!assert_continuous(test, &mm, size))
63962306a36Sopenharmony_ci				goto out;
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci			if (!expect_insert_fail(test, &mm, size))
64262306a36Sopenharmony_ci				goto out;
64362306a36Sopenharmony_ci		}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci		drm_mm_for_each_node_safe(node, next, &mm)
64662306a36Sopenharmony_ci			drm_mm_remove_node(node);
64762306a36Sopenharmony_ci		DRM_MM_BUG_ON(!drm_mm_clean(&mm));
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci		cond_resched();
65062306a36Sopenharmony_ci	}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	ret = 0;
65362306a36Sopenharmony_ciout:
65462306a36Sopenharmony_ci	drm_mm_for_each_node_safe(node, next, &mm)
65562306a36Sopenharmony_ci		drm_mm_remove_node(node);
65662306a36Sopenharmony_ci	drm_mm_takedown(&mm);
65762306a36Sopenharmony_ci	kfree(order);
65862306a36Sopenharmony_cierr_nodes:
65962306a36Sopenharmony_ci	vfree(nodes);
66062306a36Sopenharmony_ci	return ret;
66162306a36Sopenharmony_ci}
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_cistatic void drm_test_mm_insert(struct kunit *test)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	const unsigned int count = min_t(unsigned int, BIT(10), max_iterations);
66662306a36Sopenharmony_ci	unsigned int n;
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	for_each_prime_number_from(n, 1, 54) {
66962306a36Sopenharmony_ci		u64 size = BIT_ULL(n);
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size - 1, false));
67262306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size, false));
67362306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size + 1, false));
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci		cond_resched();
67662306a36Sopenharmony_ci	}
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_cistatic void drm_test_mm_replace(struct kunit *test)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	const unsigned int count = min_t(unsigned int, BIT(10), max_iterations);
68262306a36Sopenharmony_ci	unsigned int n;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	/* Reuse __drm_test_mm_insert to exercise replacement by inserting a dummy node,
68562306a36Sopenharmony_ci	 * then replacing it with the intended node. We want to check that
68662306a36Sopenharmony_ci	 * the tree is intact and all the information we need is carried
68762306a36Sopenharmony_ci	 * across to the target node.
68862306a36Sopenharmony_ci	 */
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	for_each_prime_number_from(n, 1, 54) {
69162306a36Sopenharmony_ci		u64 size = BIT_ULL(n);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size - 1, true));
69462306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size, true));
69562306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size + 1, true));
69662306a36Sopenharmony_ci
69762306a36Sopenharmony_ci		cond_resched();
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_cistatic bool expect_insert_in_range(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *node,
70262306a36Sopenharmony_ci				   u64 size, u64 alignment, unsigned long color,
70362306a36Sopenharmony_ci				   u64 range_start, u64 range_end, const struct insert_mode *mode)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	int err;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	err = drm_mm_insert_node_in_range(mm, node,
70862306a36Sopenharmony_ci					  size, alignment, color,
70962306a36Sopenharmony_ci					  range_start, range_end,
71062306a36Sopenharmony_ci					  mode->mode);
71162306a36Sopenharmony_ci	if (err) {
71262306a36Sopenharmony_ci		KUNIT_FAIL(test,
71362306a36Sopenharmony_ci			   "insert (size=%llu, alignment=%llu, color=%lu, mode=%s) nto range [%llx, %llx] failed with err=%d\n",
71462306a36Sopenharmony_ci				   size, alignment, color, mode->name,
71562306a36Sopenharmony_ci				   range_start, range_end, err);
71662306a36Sopenharmony_ci		return false;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	if (!assert_node(test, node, mm, size, alignment, color)) {
72062306a36Sopenharmony_ci		drm_mm_remove_node(node);
72162306a36Sopenharmony_ci		return false;
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	return true;
72562306a36Sopenharmony_ci}
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_cistatic bool expect_insert_in_range_fail(struct kunit *test, struct drm_mm *mm,
72862306a36Sopenharmony_ci					u64 size, u64 range_start, u64 range_end)
72962306a36Sopenharmony_ci{
73062306a36Sopenharmony_ci	struct drm_mm_node tmp = {};
73162306a36Sopenharmony_ci	int err;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	err = drm_mm_insert_node_in_range(mm, &tmp, size, 0, 0, range_start, range_end,
73462306a36Sopenharmony_ci					  0);
73562306a36Sopenharmony_ci	if (likely(err == -ENOSPC))
73662306a36Sopenharmony_ci		return true;
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	if (!err) {
73962306a36Sopenharmony_ci		KUNIT_FAIL(test,
74062306a36Sopenharmony_ci			   "impossible insert succeeded, node %llx + %llu, range [%llx, %llx]\n",
74162306a36Sopenharmony_ci				   tmp.start, tmp.size, range_start, range_end);
74262306a36Sopenharmony_ci		drm_mm_remove_node(&tmp);
74362306a36Sopenharmony_ci	} else {
74462306a36Sopenharmony_ci		KUNIT_FAIL(test,
74562306a36Sopenharmony_ci			   "impossible insert failed with wrong error %d [expected %d], size %llu, range [%llx, %llx]\n",
74662306a36Sopenharmony_ci				   err, -ENOSPC, size, range_start, range_end);
74762306a36Sopenharmony_ci	}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	return false;
75062306a36Sopenharmony_ci}
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_cistatic bool assert_contiguous_in_range(struct kunit *test, struct drm_mm *mm,
75362306a36Sopenharmony_ci				       u64 size, u64 start, u64 end)
75462306a36Sopenharmony_ci{
75562306a36Sopenharmony_ci	struct drm_mm_node *node;
75662306a36Sopenharmony_ci	unsigned int n;
75762306a36Sopenharmony_ci
75862306a36Sopenharmony_ci	if (!expect_insert_in_range_fail(test, mm, size, start, end))
75962306a36Sopenharmony_ci		return false;
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	n = div64_u64(start + size - 1, size);
76262306a36Sopenharmony_ci	drm_mm_for_each_node(node, mm) {
76362306a36Sopenharmony_ci		if (node->start < start || node->start + node->size > end) {
76462306a36Sopenharmony_ci			KUNIT_FAIL(test,
76562306a36Sopenharmony_ci				   "node %d out of range, address [%llx + %llu], range [%llx, %llx]\n",
76662306a36Sopenharmony_ci					   n, node->start, node->start + node->size, start, end);
76762306a36Sopenharmony_ci			return false;
76862306a36Sopenharmony_ci		}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci		if (node->start != n * size) {
77162306a36Sopenharmony_ci			KUNIT_FAIL(test, "node %d out of order, expected start %llx, found %llx\n",
77262306a36Sopenharmony_ci				   n, n * size, node->start);
77362306a36Sopenharmony_ci			return false;
77462306a36Sopenharmony_ci		}
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci		if (node->size != size) {
77762306a36Sopenharmony_ci			KUNIT_FAIL(test, "node %d has wrong size, expected size %llx, found %llx\n",
77862306a36Sopenharmony_ci				   n, size, node->size);
77962306a36Sopenharmony_ci			return false;
78062306a36Sopenharmony_ci		}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci		if (drm_mm_hole_follows(node) && drm_mm_hole_node_end(node) < end) {
78362306a36Sopenharmony_ci			KUNIT_FAIL(test, "node %d is followed by a hole!\n", n);
78462306a36Sopenharmony_ci			return false;
78562306a36Sopenharmony_ci		}
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci		n++;
78862306a36Sopenharmony_ci	}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	if (start > 0) {
79162306a36Sopenharmony_ci		node = __drm_mm_interval_first(mm, 0, start - 1);
79262306a36Sopenharmony_ci		if (drm_mm_node_allocated(node)) {
79362306a36Sopenharmony_ci			KUNIT_FAIL(test, "node before start: node=%llx+%llu, start=%llx\n",
79462306a36Sopenharmony_ci				   node->start, node->size, start);
79562306a36Sopenharmony_ci			return false;
79662306a36Sopenharmony_ci		}
79762306a36Sopenharmony_ci	}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	if (end < U64_MAX) {
80062306a36Sopenharmony_ci		node = __drm_mm_interval_first(mm, end, U64_MAX);
80162306a36Sopenharmony_ci		if (drm_mm_node_allocated(node)) {
80262306a36Sopenharmony_ci			KUNIT_FAIL(test, "node after end: node=%llx+%llu, end=%llx\n",
80362306a36Sopenharmony_ci				   node->start, node->size, end);
80462306a36Sopenharmony_ci			return false;
80562306a36Sopenharmony_ci		}
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	return true;
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_cistatic int __drm_test_mm_insert_range(struct kunit *test, unsigned int count, u64 size,
81262306a36Sopenharmony_ci				      u64 start, u64 end)
81362306a36Sopenharmony_ci{
81462306a36Sopenharmony_ci	const struct insert_mode *mode;
81562306a36Sopenharmony_ci	struct drm_mm mm;
81662306a36Sopenharmony_ci	struct drm_mm_node *nodes, *node, *next;
81762306a36Sopenharmony_ci	unsigned int n, start_n, end_n;
81862306a36Sopenharmony_ci	int ret;
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	DRM_MM_BUG_ON(!count);
82162306a36Sopenharmony_ci	DRM_MM_BUG_ON(!size);
82262306a36Sopenharmony_ci	DRM_MM_BUG_ON(end <= start);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci	/* Very similar to __drm_test_mm_insert(), but now instead of populating the
82562306a36Sopenharmony_ci	 * full range of the drm_mm, we try to fill a small portion of it.
82662306a36Sopenharmony_ci	 */
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	ret = -ENOMEM;
82962306a36Sopenharmony_ci	nodes = vzalloc(array_size(count, sizeof(*nodes)));
83062306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE(test, nodes);
83162306a36Sopenharmony_ci
83262306a36Sopenharmony_ci	ret = -EINVAL;
83362306a36Sopenharmony_ci	drm_mm_init(&mm, 0, count * size);
83462306a36Sopenharmony_ci
83562306a36Sopenharmony_ci	start_n = div64_u64(start + size - 1, size);
83662306a36Sopenharmony_ci	end_n = div64_u64(end - size, size);
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	for (mode = insert_modes; mode->name; mode++) {
83962306a36Sopenharmony_ci		for (n = start_n; n <= end_n; n++) {
84062306a36Sopenharmony_ci			if (!expect_insert_in_range(test, &mm, &nodes[n], size, size, n,
84162306a36Sopenharmony_ci						    start, end, mode)) {
84262306a36Sopenharmony_ci				KUNIT_FAIL(test,
84362306a36Sopenharmony_ci					   "%s insert failed, size %llu, step %d [%d, %d], range [%llx, %llx]\n",
84462306a36Sopenharmony_ci						   mode->name, size, n, start_n, end_n, start, end);
84562306a36Sopenharmony_ci				goto out;
84662306a36Sopenharmony_ci			}
84762306a36Sopenharmony_ci		}
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci		if (!assert_contiguous_in_range(test, &mm, size, start, end)) {
85062306a36Sopenharmony_ci			KUNIT_FAIL(test,
85162306a36Sopenharmony_ci				   "%s: range [%llx, %llx] not full after initialisation, size=%llu\n",
85262306a36Sopenharmony_ci				   mode->name, start, end, size);
85362306a36Sopenharmony_ci			goto out;
85462306a36Sopenharmony_ci		}
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci		/* Remove one and reinsert, it should refill itself */
85762306a36Sopenharmony_ci		for (n = start_n; n <= end_n; n++) {
85862306a36Sopenharmony_ci			u64 addr = nodes[n].start;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci			drm_mm_remove_node(&nodes[n]);
86162306a36Sopenharmony_ci			if (!expect_insert_in_range(test, &mm, &nodes[n], size, size, n,
86262306a36Sopenharmony_ci						    start, end, mode)) {
86362306a36Sopenharmony_ci				KUNIT_FAIL(test, "%s reinsert failed, step %d\n", mode->name, n);
86462306a36Sopenharmony_ci				goto out;
86562306a36Sopenharmony_ci			}
86662306a36Sopenharmony_ci
86762306a36Sopenharmony_ci			if (nodes[n].start != addr) {
86862306a36Sopenharmony_ci				KUNIT_FAIL(test,
86962306a36Sopenharmony_ci					   "%s reinsert node moved, step %d, expected %llx, found %llx\n",
87062306a36Sopenharmony_ci					   mode->name, n, addr, nodes[n].start);
87162306a36Sopenharmony_ci				goto out;
87262306a36Sopenharmony_ci			}
87362306a36Sopenharmony_ci		}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci		if (!assert_contiguous_in_range(test, &mm, size, start, end)) {
87662306a36Sopenharmony_ci			KUNIT_FAIL(test,
87762306a36Sopenharmony_ci				   "%s: range [%llx, %llx] not full after reinsertion, size=%llu\n",
87862306a36Sopenharmony_ci				   mode->name, start, end, size);
87962306a36Sopenharmony_ci			goto out;
88062306a36Sopenharmony_ci		}
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci		drm_mm_for_each_node_safe(node, next, &mm)
88362306a36Sopenharmony_ci			drm_mm_remove_node(node);
88462306a36Sopenharmony_ci		DRM_MM_BUG_ON(!drm_mm_clean(&mm));
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci		cond_resched();
88762306a36Sopenharmony_ci	}
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	ret = 0;
89062306a36Sopenharmony_ciout:
89162306a36Sopenharmony_ci	drm_mm_for_each_node_safe(node, next, &mm)
89262306a36Sopenharmony_ci		drm_mm_remove_node(node);
89362306a36Sopenharmony_ci	drm_mm_takedown(&mm);
89462306a36Sopenharmony_ci	vfree(nodes);
89562306a36Sopenharmony_ci	return ret;
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ci
89862306a36Sopenharmony_cistatic int insert_outside_range(struct kunit *test)
89962306a36Sopenharmony_ci{
90062306a36Sopenharmony_ci	struct drm_mm mm;
90162306a36Sopenharmony_ci	const unsigned int start = 1024;
90262306a36Sopenharmony_ci	const unsigned int end = 2048;
90362306a36Sopenharmony_ci	const unsigned int size = end - start;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	drm_mm_init(&mm, start, size);
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_ci	if (!expect_insert_in_range_fail(test, &mm, 1, 0, start))
90862306a36Sopenharmony_ci		return -EINVAL;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	if (!expect_insert_in_range_fail(test, &mm, size,
91162306a36Sopenharmony_ci					 start - size / 2, start + (size + 1) / 2))
91262306a36Sopenharmony_ci		return -EINVAL;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	if (!expect_insert_in_range_fail(test, &mm, size,
91562306a36Sopenharmony_ci					 end - (size + 1) / 2, end + size / 2))
91662306a36Sopenharmony_ci		return -EINVAL;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (!expect_insert_in_range_fail(test, &mm, 1, end, end + size))
91962306a36Sopenharmony_ci		return -EINVAL;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	drm_mm_takedown(&mm);
92262306a36Sopenharmony_ci	return 0;
92362306a36Sopenharmony_ci}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_cistatic void drm_test_mm_insert_range(struct kunit *test)
92662306a36Sopenharmony_ci{
92762306a36Sopenharmony_ci	const unsigned int count = min_t(unsigned int, BIT(13), max_iterations);
92862306a36Sopenharmony_ci	unsigned int n;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	/* Check that requests outside the bounds of drm_mm are rejected. */
93162306a36Sopenharmony_ci	KUNIT_ASSERT_FALSE(test, insert_outside_range(test));
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	for_each_prime_number_from(n, 1, 50) {
93462306a36Sopenharmony_ci		const u64 size = BIT_ULL(n);
93562306a36Sopenharmony_ci		const u64 max = count * size;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, 0, max));
93862306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, 1, max));
93962306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, 0, max - 1));
94062306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, 0, max / 2));
94162306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size,
94262306a36Sopenharmony_ci								    max / 2, max));
94362306a36Sopenharmony_ci		KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size,
94462306a36Sopenharmony_ci								    max / 4 + 1, 3 * max / 4 - 1));
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_ci		cond_resched();
94762306a36Sopenharmony_ci	}
94862306a36Sopenharmony_ci}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_cistatic int prepare_frag(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *nodes,
95162306a36Sopenharmony_ci			unsigned int num_insert, const struct insert_mode *mode)
95262306a36Sopenharmony_ci{
95362306a36Sopenharmony_ci	unsigned int size = 4096;
95462306a36Sopenharmony_ci	unsigned int i;
95562306a36Sopenharmony_ci
95662306a36Sopenharmony_ci	for (i = 0; i < num_insert; i++) {
95762306a36Sopenharmony_ci		if (!expect_insert(test, mm, &nodes[i], size, 0, i, mode) != 0) {
95862306a36Sopenharmony_ci			KUNIT_FAIL(test, "%s insert failed\n", mode->name);
95962306a36Sopenharmony_ci			return -EINVAL;
96062306a36Sopenharmony_ci		}
96162306a36Sopenharmony_ci	}
96262306a36Sopenharmony_ci
96362306a36Sopenharmony_ci	/* introduce fragmentation by freeing every other node */
96462306a36Sopenharmony_ci	for (i = 0; i < num_insert; i++) {
96562306a36Sopenharmony_ci		if (i % 2 == 0)
96662306a36Sopenharmony_ci			drm_mm_remove_node(&nodes[i]);
96762306a36Sopenharmony_ci	}
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	return 0;
97062306a36Sopenharmony_ci}
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistatic u64 get_insert_time(struct kunit *test, struct drm_mm *mm,
97362306a36Sopenharmony_ci			   unsigned int num_insert, struct drm_mm_node *nodes,
97462306a36Sopenharmony_ci			   const struct insert_mode *mode)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	unsigned int size = 8192;
97762306a36Sopenharmony_ci	ktime_t start;
97862306a36Sopenharmony_ci	unsigned int i;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	start = ktime_get();
98162306a36Sopenharmony_ci	for (i = 0; i < num_insert; i++) {
98262306a36Sopenharmony_ci		if (!expect_insert(test, mm, &nodes[i], size, 0, i, mode) != 0) {
98362306a36Sopenharmony_ci			KUNIT_FAIL(test, "%s insert failed\n", mode->name);
98462306a36Sopenharmony_ci			return 0;
98562306a36Sopenharmony_ci		}
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	return ktime_to_ns(ktime_sub(ktime_get(), start));
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cistatic void drm_test_mm_frag(struct kunit *test)
99262306a36Sopenharmony_ci{
99362306a36Sopenharmony_ci	struct drm_mm mm;
99462306a36Sopenharmony_ci	const struct insert_mode *mode;
99562306a36Sopenharmony_ci	struct drm_mm_node *nodes, *node, *next;
99662306a36Sopenharmony_ci	unsigned int insert_size = 10000;
99762306a36Sopenharmony_ci	unsigned int scale_factor = 4;
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	/* We need 4 * insert_size nodes to hold intermediate allocated
100062306a36Sopenharmony_ci	 * drm_mm nodes.
100162306a36Sopenharmony_ci	 * 1 times for prepare_frag()
100262306a36Sopenharmony_ci	 * 1 times for get_insert_time()
100362306a36Sopenharmony_ci	 * 2 times for get_insert_time()
100462306a36Sopenharmony_ci	 */
100562306a36Sopenharmony_ci	nodes = vzalloc(array_size(insert_size * 4, sizeof(*nodes)));
100662306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE(test, nodes);
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	/* For BOTTOMUP and TOPDOWN, we first fragment the
100962306a36Sopenharmony_ci	 * address space using prepare_frag() and then try to verify
101062306a36Sopenharmony_ci	 * that insertions scale quadratically from 10k to 20k insertions
101162306a36Sopenharmony_ci	 */
101262306a36Sopenharmony_ci	drm_mm_init(&mm, 1, U64_MAX - 2);
101362306a36Sopenharmony_ci	for (mode = insert_modes; mode->name; mode++) {
101462306a36Sopenharmony_ci		u64 insert_time1, insert_time2;
101562306a36Sopenharmony_ci
101662306a36Sopenharmony_ci		if (mode->mode != DRM_MM_INSERT_LOW &&
101762306a36Sopenharmony_ci		    mode->mode != DRM_MM_INSERT_HIGH)
101862306a36Sopenharmony_ci			continue;
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci		if (prepare_frag(test, &mm, nodes, insert_size, mode))
102162306a36Sopenharmony_ci			goto err;
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci		insert_time1 = get_insert_time(test, &mm, insert_size,
102462306a36Sopenharmony_ci					       nodes + insert_size, mode);
102562306a36Sopenharmony_ci		if (insert_time1 == 0)
102662306a36Sopenharmony_ci			goto err;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci		insert_time2 = get_insert_time(test, &mm, (insert_size * 2),
102962306a36Sopenharmony_ci					       nodes + insert_size * 2, mode);
103062306a36Sopenharmony_ci		if (insert_time2 == 0)
103162306a36Sopenharmony_ci			goto err;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci		kunit_info(test, "%s fragmented insert of %u and %u insertions took %llu and %llu nsecs\n",
103462306a36Sopenharmony_ci			   mode->name, insert_size, insert_size * 2, insert_time1, insert_time2);
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci		if (insert_time2 > (scale_factor * insert_time1)) {
103762306a36Sopenharmony_ci			KUNIT_FAIL(test, "%s fragmented insert took %llu nsecs more\n",
103862306a36Sopenharmony_ci				   mode->name, insert_time2 - (scale_factor * insert_time1));
103962306a36Sopenharmony_ci			goto err;
104062306a36Sopenharmony_ci		}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci		drm_mm_for_each_node_safe(node, next, &mm)
104362306a36Sopenharmony_ci			drm_mm_remove_node(node);
104462306a36Sopenharmony_ci	}
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_cierr:
104762306a36Sopenharmony_ci	drm_mm_for_each_node_safe(node, next, &mm)
104862306a36Sopenharmony_ci		drm_mm_remove_node(node);
104962306a36Sopenharmony_ci	drm_mm_takedown(&mm);
105062306a36Sopenharmony_ci	vfree(nodes);
105162306a36Sopenharmony_ci}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_cistatic void drm_test_mm_align(struct kunit *test)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	const struct insert_mode *mode;
105662306a36Sopenharmony_ci	const unsigned int max_count = min(8192u, max_prime);
105762306a36Sopenharmony_ci	struct drm_mm mm;
105862306a36Sopenharmony_ci	struct drm_mm_node *nodes, *node, *next;
105962306a36Sopenharmony_ci	unsigned int prime;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	/* For each of the possible insertion modes, we pick a few
106262306a36Sopenharmony_ci	 * arbitrary alignments and check that the inserted node
106362306a36Sopenharmony_ci	 * meets our requirements.
106462306a36Sopenharmony_ci	 */
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_ci	nodes = vzalloc(array_size(max_count, sizeof(*nodes)));
106762306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE(test, nodes);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	drm_mm_init(&mm, 1, U64_MAX - 2);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	for (mode = insert_modes; mode->name; mode++) {
107262306a36Sopenharmony_ci		unsigned int i = 0;
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci		for_each_prime_number_from(prime, 1, max_count) {
107562306a36Sopenharmony_ci			u64 size = next_prime_number(prime);
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci			if (!expect_insert(test, &mm, &nodes[i], size, prime, i, mode)) {
107862306a36Sopenharmony_ci				KUNIT_FAIL(test, "%s insert failed with alignment=%d",
107962306a36Sopenharmony_ci					   mode->name, prime);
108062306a36Sopenharmony_ci				goto out;
108162306a36Sopenharmony_ci			}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci			i++;
108462306a36Sopenharmony_ci		}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci		drm_mm_for_each_node_safe(node, next, &mm)
108762306a36Sopenharmony_ci			drm_mm_remove_node(node);
108862306a36Sopenharmony_ci		DRM_MM_BUG_ON(!drm_mm_clean(&mm));
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		cond_resched();
109162306a36Sopenharmony_ci	}
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ciout:
109462306a36Sopenharmony_ci	drm_mm_for_each_node_safe(node, next, &mm)
109562306a36Sopenharmony_ci		drm_mm_remove_node(node);
109662306a36Sopenharmony_ci	drm_mm_takedown(&mm);
109762306a36Sopenharmony_ci	vfree(nodes);
109862306a36Sopenharmony_ci}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_cistatic void drm_test_mm_align_pot(struct kunit *test, int max)
110162306a36Sopenharmony_ci{
110262306a36Sopenharmony_ci	struct drm_mm mm;
110362306a36Sopenharmony_ci	struct drm_mm_node *node, *next;
110462306a36Sopenharmony_ci	int bit;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	/* Check that we can align to the full u64 address space */
110762306a36Sopenharmony_ci
110862306a36Sopenharmony_ci	drm_mm_init(&mm, 1, U64_MAX - 2);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	for (bit = max - 1; bit; bit--) {
111162306a36Sopenharmony_ci		u64 align, size;
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci		node = kzalloc(sizeof(*node), GFP_KERNEL);
111462306a36Sopenharmony_ci		if (!node) {
111562306a36Sopenharmony_ci			KUNIT_FAIL(test, "failed to allocate node");
111662306a36Sopenharmony_ci			goto out;
111762306a36Sopenharmony_ci		}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci		align = BIT_ULL(bit);
112062306a36Sopenharmony_ci		size = BIT_ULL(bit - 1) + 1;
112162306a36Sopenharmony_ci		if (!expect_insert(test, &mm, node, size, align, bit, &insert_modes[0])) {
112262306a36Sopenharmony_ci			KUNIT_FAIL(test, "insert failed with alignment=%llx [%d]", align, bit);
112362306a36Sopenharmony_ci			goto out;
112462306a36Sopenharmony_ci		}
112562306a36Sopenharmony_ci
112662306a36Sopenharmony_ci		cond_resched();
112762306a36Sopenharmony_ci	}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ciout:
113062306a36Sopenharmony_ci	drm_mm_for_each_node_safe(node, next, &mm) {
113162306a36Sopenharmony_ci		drm_mm_remove_node(node);
113262306a36Sopenharmony_ci		kfree(node);
113362306a36Sopenharmony_ci	}
113462306a36Sopenharmony_ci	drm_mm_takedown(&mm);
113562306a36Sopenharmony_ci}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_cistatic void drm_test_mm_align32(struct kunit *test)
113862306a36Sopenharmony_ci{
113962306a36Sopenharmony_ci	drm_test_mm_align_pot(test, 32);
114062306a36Sopenharmony_ci}
114162306a36Sopenharmony_ci
114262306a36Sopenharmony_cistatic void drm_test_mm_align64(struct kunit *test)
114362306a36Sopenharmony_ci{
114462306a36Sopenharmony_ci	drm_test_mm_align_pot(test, 64);
114562306a36Sopenharmony_ci}
114662306a36Sopenharmony_ci
114762306a36Sopenharmony_cistatic void show_scan(struct kunit *test, const struct drm_mm_scan *scan)
114862306a36Sopenharmony_ci{
114962306a36Sopenharmony_ci	kunit_info(test, "scan: hit [%llx, %llx], size=%lld, align=%lld, color=%ld\n",
115062306a36Sopenharmony_ci		   scan->hit_start, scan->hit_end, scan->size, scan->alignment, scan->color);
115162306a36Sopenharmony_ci}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_cistatic void show_holes(struct kunit *test, const struct drm_mm *mm, int count)
115462306a36Sopenharmony_ci{
115562306a36Sopenharmony_ci	u64 hole_start, hole_end;
115662306a36Sopenharmony_ci	struct drm_mm_node *hole;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
115962306a36Sopenharmony_ci		struct drm_mm_node *next = list_next_entry(hole, node_list);
116062306a36Sopenharmony_ci		const char *node1 = NULL, *node2 = NULL;
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci		if (drm_mm_node_allocated(hole))
116362306a36Sopenharmony_ci			node1 = kasprintf(GFP_KERNEL, "[%llx + %lld, color=%ld], ",
116462306a36Sopenharmony_ci					  hole->start, hole->size, hole->color);
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci		if (drm_mm_node_allocated(next))
116762306a36Sopenharmony_ci			node2 = kasprintf(GFP_KERNEL, ", [%llx + %lld, color=%ld]",
116862306a36Sopenharmony_ci					  next->start, next->size, next->color);
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci		kunit_info(test, "%sHole [%llx - %llx, size %lld]%s\n", node1,
117162306a36Sopenharmony_ci			   hole_start, hole_end, hole_end - hole_start, node2);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci		kfree(node2);
117462306a36Sopenharmony_ci		kfree(node1);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci		if (!--count)
117762306a36Sopenharmony_ci			break;
117862306a36Sopenharmony_ci	}
117962306a36Sopenharmony_ci}
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_cistruct evict_node {
118262306a36Sopenharmony_ci	struct drm_mm_node node;
118362306a36Sopenharmony_ci	struct list_head link;
118462306a36Sopenharmony_ci};
118562306a36Sopenharmony_ci
118662306a36Sopenharmony_cistatic bool evict_nodes(struct kunit *test, struct drm_mm_scan *scan,
118762306a36Sopenharmony_ci			struct evict_node *nodes, unsigned int *order, unsigned int count,
118862306a36Sopenharmony_ci			bool use_color, struct list_head *evict_list)
118962306a36Sopenharmony_ci{
119062306a36Sopenharmony_ci	struct evict_node *e, *en;
119162306a36Sopenharmony_ci	unsigned int i;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
119462306a36Sopenharmony_ci		e = &nodes[order ? order[i] : i];
119562306a36Sopenharmony_ci		list_add(&e->link, evict_list);
119662306a36Sopenharmony_ci		if (drm_mm_scan_add_block(scan, &e->node))
119762306a36Sopenharmony_ci			break;
119862306a36Sopenharmony_ci	}
119962306a36Sopenharmony_ci	list_for_each_entry_safe(e, en, evict_list, link) {
120062306a36Sopenharmony_ci		if (!drm_mm_scan_remove_block(scan, &e->node))
120162306a36Sopenharmony_ci			list_del(&e->link);
120262306a36Sopenharmony_ci	}
120362306a36Sopenharmony_ci	if (list_empty(evict_list)) {
120462306a36Sopenharmony_ci		KUNIT_FAIL(test,
120562306a36Sopenharmony_ci			   "Failed to find eviction: size=%lld [avail=%d], align=%lld (color=%lu)\n",
120662306a36Sopenharmony_ci			   scan->size, count, scan->alignment, scan->color);
120762306a36Sopenharmony_ci		return false;
120862306a36Sopenharmony_ci	}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	list_for_each_entry(e, evict_list, link)
121162306a36Sopenharmony_ci		drm_mm_remove_node(&e->node);
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	if (use_color) {
121462306a36Sopenharmony_ci		struct drm_mm_node *node;
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_ci		while ((node = drm_mm_scan_color_evict(scan))) {
121762306a36Sopenharmony_ci			e = container_of(node, typeof(*e), node);
121862306a36Sopenharmony_ci			drm_mm_remove_node(&e->node);
121962306a36Sopenharmony_ci			list_add(&e->link, evict_list);
122062306a36Sopenharmony_ci		}
122162306a36Sopenharmony_ci	} else {
122262306a36Sopenharmony_ci		if (drm_mm_scan_color_evict(scan)) {
122362306a36Sopenharmony_ci			KUNIT_FAIL(test,
122462306a36Sopenharmony_ci				   "drm_mm_scan_color_evict unexpectedly reported overlapping nodes!\n");
122562306a36Sopenharmony_ci			return false;
122662306a36Sopenharmony_ci		}
122762306a36Sopenharmony_ci	}
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	return true;
123062306a36Sopenharmony_ci}
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_cistatic bool evict_nothing(struct kunit *test, struct drm_mm *mm,
123362306a36Sopenharmony_ci			  unsigned int total_size, struct evict_node *nodes)
123462306a36Sopenharmony_ci{
123562306a36Sopenharmony_ci	struct drm_mm_scan scan;
123662306a36Sopenharmony_ci	LIST_HEAD(evict_list);
123762306a36Sopenharmony_ci	struct evict_node *e;
123862306a36Sopenharmony_ci	struct drm_mm_node *node;
123962306a36Sopenharmony_ci	unsigned int n;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	drm_mm_scan_init(&scan, mm, 1, 0, 0, 0);
124262306a36Sopenharmony_ci	for (n = 0; n < total_size; n++) {
124362306a36Sopenharmony_ci		e = &nodes[n];
124462306a36Sopenharmony_ci		list_add(&e->link, &evict_list);
124562306a36Sopenharmony_ci		drm_mm_scan_add_block(&scan, &e->node);
124662306a36Sopenharmony_ci	}
124762306a36Sopenharmony_ci	list_for_each_entry(e, &evict_list, link)
124862306a36Sopenharmony_ci		drm_mm_scan_remove_block(&scan, &e->node);
124962306a36Sopenharmony_ci
125062306a36Sopenharmony_ci	for (n = 0; n < total_size; n++) {
125162306a36Sopenharmony_ci		e = &nodes[n];
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci		if (!drm_mm_node_allocated(&e->node)) {
125462306a36Sopenharmony_ci			KUNIT_FAIL(test, "node[%d] no longer allocated!\n", n);
125562306a36Sopenharmony_ci			return false;
125662306a36Sopenharmony_ci		}
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci		e->link.next = NULL;
125962306a36Sopenharmony_ci	}
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	drm_mm_for_each_node(node, mm) {
126262306a36Sopenharmony_ci		e = container_of(node, typeof(*e), node);
126362306a36Sopenharmony_ci		e->link.next = &e->link;
126462306a36Sopenharmony_ci	}
126562306a36Sopenharmony_ci
126662306a36Sopenharmony_ci	for (n = 0; n < total_size; n++) {
126762306a36Sopenharmony_ci		e = &nodes[n];
126862306a36Sopenharmony_ci
126962306a36Sopenharmony_ci		if (!e->link.next) {
127062306a36Sopenharmony_ci			KUNIT_FAIL(test, "node[%d] no longer connected!\n", n);
127162306a36Sopenharmony_ci			return false;
127262306a36Sopenharmony_ci		}
127362306a36Sopenharmony_ci	}
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	return assert_continuous(test, mm, nodes[0].node.size);
127662306a36Sopenharmony_ci}
127762306a36Sopenharmony_ci
127862306a36Sopenharmony_cistatic bool evict_everything(struct kunit *test, struct drm_mm *mm,
127962306a36Sopenharmony_ci			     unsigned int total_size, struct evict_node *nodes)
128062306a36Sopenharmony_ci{
128162306a36Sopenharmony_ci	struct drm_mm_scan scan;
128262306a36Sopenharmony_ci	LIST_HEAD(evict_list);
128362306a36Sopenharmony_ci	struct evict_node *e;
128462306a36Sopenharmony_ci	unsigned int n;
128562306a36Sopenharmony_ci	int err;
128662306a36Sopenharmony_ci
128762306a36Sopenharmony_ci	drm_mm_scan_init(&scan, mm, total_size, 0, 0, 0);
128862306a36Sopenharmony_ci	for (n = 0; n < total_size; n++) {
128962306a36Sopenharmony_ci		e = &nodes[n];
129062306a36Sopenharmony_ci		list_add(&e->link, &evict_list);
129162306a36Sopenharmony_ci		if (drm_mm_scan_add_block(&scan, &e->node))
129262306a36Sopenharmony_ci			break;
129362306a36Sopenharmony_ci	}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	err = 0;
129662306a36Sopenharmony_ci	list_for_each_entry(e, &evict_list, link) {
129762306a36Sopenharmony_ci		if (!drm_mm_scan_remove_block(&scan, &e->node)) {
129862306a36Sopenharmony_ci			if (!err) {
129962306a36Sopenharmony_ci				KUNIT_FAIL(test, "Node %lld not marked for eviction!\n",
130062306a36Sopenharmony_ci					   e->node.start);
130162306a36Sopenharmony_ci				err = -EINVAL;
130262306a36Sopenharmony_ci			}
130362306a36Sopenharmony_ci		}
130462306a36Sopenharmony_ci	}
130562306a36Sopenharmony_ci	if (err)
130662306a36Sopenharmony_ci		return false;
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	list_for_each_entry(e, &evict_list, link)
130962306a36Sopenharmony_ci		drm_mm_remove_node(&e->node);
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_ci	if (!assert_one_hole(test, mm, 0, total_size))
131262306a36Sopenharmony_ci		return false;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci	list_for_each_entry(e, &evict_list, link) {
131562306a36Sopenharmony_ci		err = drm_mm_reserve_node(mm, &e->node);
131662306a36Sopenharmony_ci		if (err) {
131762306a36Sopenharmony_ci			KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n",
131862306a36Sopenharmony_ci				   e->node.start);
131962306a36Sopenharmony_ci			return false;
132062306a36Sopenharmony_ci		}
132162306a36Sopenharmony_ci	}
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	return assert_continuous(test, mm, nodes[0].node.size);
132462306a36Sopenharmony_ci}
132562306a36Sopenharmony_ci
132662306a36Sopenharmony_cistatic int evict_something(struct kunit *test, struct drm_mm *mm,
132762306a36Sopenharmony_ci			   u64 range_start, u64 range_end, struct evict_node *nodes,
132862306a36Sopenharmony_ci			   unsigned int *order, unsigned int count, unsigned int size,
132962306a36Sopenharmony_ci			   unsigned int alignment, const struct insert_mode *mode)
133062306a36Sopenharmony_ci{
133162306a36Sopenharmony_ci	struct drm_mm_scan scan;
133262306a36Sopenharmony_ci	LIST_HEAD(evict_list);
133362306a36Sopenharmony_ci	struct evict_node *e;
133462306a36Sopenharmony_ci	struct drm_mm_node tmp;
133562306a36Sopenharmony_ci	int err;
133662306a36Sopenharmony_ci
133762306a36Sopenharmony_ci	drm_mm_scan_init_with_range(&scan, mm, size, alignment, 0, range_start,
133862306a36Sopenharmony_ci				    range_end, mode->mode);
133962306a36Sopenharmony_ci	if (!evict_nodes(test, &scan, nodes, order, count, false, &evict_list))
134062306a36Sopenharmony_ci		return -EINVAL;
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	memset(&tmp, 0, sizeof(tmp));
134362306a36Sopenharmony_ci	err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, 0,
134462306a36Sopenharmony_ci					 DRM_MM_INSERT_EVICT);
134562306a36Sopenharmony_ci	if (err) {
134662306a36Sopenharmony_ci		KUNIT_FAIL(test, "Failed to insert into eviction hole: size=%d, align=%d\n",
134762306a36Sopenharmony_ci			   size, alignment);
134862306a36Sopenharmony_ci		show_scan(test, &scan);
134962306a36Sopenharmony_ci		show_holes(test, mm, 3);
135062306a36Sopenharmony_ci		return err;
135162306a36Sopenharmony_ci	}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	if (tmp.start < range_start || tmp.start + tmp.size > range_end) {
135462306a36Sopenharmony_ci		KUNIT_FAIL(test,
135562306a36Sopenharmony_ci			   "Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n",
135662306a36Sopenharmony_ci			   tmp.start, tmp.size, range_start, range_end);
135762306a36Sopenharmony_ci		err = -EINVAL;
135862306a36Sopenharmony_ci	}
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	if (!assert_node(test, &tmp, mm, size, alignment, 0) ||
136162306a36Sopenharmony_ci	    drm_mm_hole_follows(&tmp)) {
136262306a36Sopenharmony_ci		KUNIT_FAIL(test,
136362306a36Sopenharmony_ci			   "Inserted did not fill the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx, hole-follows?=%d\n",
136462306a36Sopenharmony_ci			   tmp.size, size, alignment, misalignment(&tmp, alignment),
136562306a36Sopenharmony_ci			   tmp.start, drm_mm_hole_follows(&tmp));
136662306a36Sopenharmony_ci		err = -EINVAL;
136762306a36Sopenharmony_ci	}
136862306a36Sopenharmony_ci
136962306a36Sopenharmony_ci	drm_mm_remove_node(&tmp);
137062306a36Sopenharmony_ci	if (err)
137162306a36Sopenharmony_ci		return err;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	list_for_each_entry(e, &evict_list, link) {
137462306a36Sopenharmony_ci		err = drm_mm_reserve_node(mm, &e->node);
137562306a36Sopenharmony_ci		if (err) {
137662306a36Sopenharmony_ci			KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n",
137762306a36Sopenharmony_ci				   e->node.start);
137862306a36Sopenharmony_ci			return err;
137962306a36Sopenharmony_ci		}
138062306a36Sopenharmony_ci	}
138162306a36Sopenharmony_ci
138262306a36Sopenharmony_ci	if (!assert_continuous(test, mm, nodes[0].node.size)) {
138362306a36Sopenharmony_ci		KUNIT_FAIL(test, "range is no longer continuous\n");
138462306a36Sopenharmony_ci		return -EINVAL;
138562306a36Sopenharmony_ci	}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	return 0;
138862306a36Sopenharmony_ci}
138962306a36Sopenharmony_ci
139062306a36Sopenharmony_cistatic void drm_test_mm_evict(struct kunit *test)
139162306a36Sopenharmony_ci{
139262306a36Sopenharmony_ci	DRM_RND_STATE(prng, random_seed);
139362306a36Sopenharmony_ci	const unsigned int size = 8192;
139462306a36Sopenharmony_ci	const struct insert_mode *mode;
139562306a36Sopenharmony_ci	struct drm_mm mm;
139662306a36Sopenharmony_ci	struct evict_node *nodes;
139762306a36Sopenharmony_ci	struct drm_mm_node *node, *next;
139862306a36Sopenharmony_ci	unsigned int *order, n;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	/* Here we populate a full drm_mm and then try and insert a new node
140162306a36Sopenharmony_ci	 * by evicting other nodes in a random order. The drm_mm_scan should
140262306a36Sopenharmony_ci	 * pick the first matching hole it finds from the random list. We
140362306a36Sopenharmony_ci	 * repeat that for different allocation strategies, alignments and
140462306a36Sopenharmony_ci	 * sizes to try and stress the hole finder.
140562306a36Sopenharmony_ci	 */
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	nodes = vzalloc(array_size(size, sizeof(*nodes)));
140862306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE(test, nodes);
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	order = drm_random_order(size, &prng);
141162306a36Sopenharmony_ci	if (!order)
141262306a36Sopenharmony_ci		goto err_nodes;
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_ci	drm_mm_init(&mm, 0, size);
141562306a36Sopenharmony_ci	for (n = 0; n < size; n++) {
141662306a36Sopenharmony_ci		if (drm_mm_insert_node(&mm, &nodes[n].node, 1)) {
141762306a36Sopenharmony_ci			KUNIT_FAIL(test, "insert failed, step %d\n", n);
141862306a36Sopenharmony_ci			goto out;
141962306a36Sopenharmony_ci		}
142062306a36Sopenharmony_ci	}
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_ci	/* First check that using the scanner doesn't break the mm */
142362306a36Sopenharmony_ci	if (!evict_nothing(test, &mm, size, nodes)) {
142462306a36Sopenharmony_ci		KUNIT_FAIL(test, "evict_nothing() failed\n");
142562306a36Sopenharmony_ci		goto out;
142662306a36Sopenharmony_ci	}
142762306a36Sopenharmony_ci	if (!evict_everything(test, &mm, size, nodes)) {
142862306a36Sopenharmony_ci		KUNIT_FAIL(test, "evict_everything() failed\n");
142962306a36Sopenharmony_ci		goto out;
143062306a36Sopenharmony_ci	}
143162306a36Sopenharmony_ci
143262306a36Sopenharmony_ci	for (mode = evict_modes; mode->name; mode++) {
143362306a36Sopenharmony_ci		for (n = 1; n <= size; n <<= 1) {
143462306a36Sopenharmony_ci			drm_random_reorder(order, size, &prng);
143562306a36Sopenharmony_ci			if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size, n, 1,
143662306a36Sopenharmony_ci					    mode)) {
143762306a36Sopenharmony_ci				KUNIT_FAIL(test, "%s evict_something(size=%u) failed\n",
143862306a36Sopenharmony_ci					   mode->name, n);
143962306a36Sopenharmony_ci				goto out;
144062306a36Sopenharmony_ci			}
144162306a36Sopenharmony_ci		}
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci		for (n = 1; n < size; n <<= 1) {
144462306a36Sopenharmony_ci			drm_random_reorder(order, size, &prng);
144562306a36Sopenharmony_ci			if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size,
144662306a36Sopenharmony_ci					    size / 2, n, mode)) {
144762306a36Sopenharmony_ci				KUNIT_FAIL(test,
144862306a36Sopenharmony_ci					   "%s evict_something(size=%u, alignment=%u) failed\n",
144962306a36Sopenharmony_ci					   mode->name, size / 2, n);
145062306a36Sopenharmony_ci				goto out;
145162306a36Sopenharmony_ci			}
145262306a36Sopenharmony_ci		}
145362306a36Sopenharmony_ci
145462306a36Sopenharmony_ci		for_each_prime_number_from(n, 1, min(size, max_prime)) {
145562306a36Sopenharmony_ci			unsigned int nsize = (size - n + 1) / 2;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci			DRM_MM_BUG_ON(!nsize);
145862306a36Sopenharmony_ci
145962306a36Sopenharmony_ci			drm_random_reorder(order, size, &prng);
146062306a36Sopenharmony_ci			if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size,
146162306a36Sopenharmony_ci					    nsize, n, mode)) {
146262306a36Sopenharmony_ci				KUNIT_FAIL(test,
146362306a36Sopenharmony_ci					   "%s evict_something(size=%u, alignment=%u) failed\n",
146462306a36Sopenharmony_ci					   mode->name, nsize, n);
146562306a36Sopenharmony_ci				goto out;
146662306a36Sopenharmony_ci			}
146762306a36Sopenharmony_ci		}
146862306a36Sopenharmony_ci
146962306a36Sopenharmony_ci		cond_resched();
147062306a36Sopenharmony_ci	}
147162306a36Sopenharmony_ci
147262306a36Sopenharmony_ciout:
147362306a36Sopenharmony_ci	drm_mm_for_each_node_safe(node, next, &mm)
147462306a36Sopenharmony_ci		drm_mm_remove_node(node);
147562306a36Sopenharmony_ci	drm_mm_takedown(&mm);
147662306a36Sopenharmony_ci	kfree(order);
147762306a36Sopenharmony_cierr_nodes:
147862306a36Sopenharmony_ci	vfree(nodes);
147962306a36Sopenharmony_ci}
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_cistatic void drm_test_mm_evict_range(struct kunit *test)
148262306a36Sopenharmony_ci{
148362306a36Sopenharmony_ci	DRM_RND_STATE(prng, random_seed);
148462306a36Sopenharmony_ci	const unsigned int size = 8192;
148562306a36Sopenharmony_ci	const unsigned int range_size = size / 2;
148662306a36Sopenharmony_ci	const unsigned int range_start = size / 4;
148762306a36Sopenharmony_ci	const unsigned int range_end = range_start + range_size;
148862306a36Sopenharmony_ci	const struct insert_mode *mode;
148962306a36Sopenharmony_ci	struct drm_mm mm;
149062306a36Sopenharmony_ci	struct evict_node *nodes;
149162306a36Sopenharmony_ci	struct drm_mm_node *node, *next;
149262306a36Sopenharmony_ci	unsigned int *order, n;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci	/* Like drm_test_mm_evict() but now we are limiting the search to a
149562306a36Sopenharmony_ci	 * small portion of the full drm_mm.
149662306a36Sopenharmony_ci	 */
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	nodes = vzalloc(array_size(size, sizeof(*nodes)));
149962306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE(test, nodes);
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	order = drm_random_order(size, &prng);
150262306a36Sopenharmony_ci	if (!order)
150362306a36Sopenharmony_ci		goto err_nodes;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	drm_mm_init(&mm, 0, size);
150662306a36Sopenharmony_ci	for (n = 0; n < size; n++) {
150762306a36Sopenharmony_ci		if (drm_mm_insert_node(&mm, &nodes[n].node, 1)) {
150862306a36Sopenharmony_ci			KUNIT_FAIL(test, "insert failed, step %d\n", n);
150962306a36Sopenharmony_ci			goto out;
151062306a36Sopenharmony_ci		}
151162306a36Sopenharmony_ci	}
151262306a36Sopenharmony_ci
151362306a36Sopenharmony_ci	for (mode = evict_modes; mode->name; mode++) {
151462306a36Sopenharmony_ci		for (n = 1; n <= range_size; n <<= 1) {
151562306a36Sopenharmony_ci			drm_random_reorder(order, size, &prng);
151662306a36Sopenharmony_ci			if (evict_something(test, &mm, range_start, range_end, nodes,
151762306a36Sopenharmony_ci					    order, size, n, 1, mode)) {
151862306a36Sopenharmony_ci				KUNIT_FAIL(test,
151962306a36Sopenharmony_ci					   "%s evict_something(size=%u) failed with range [%u, %u]\n",
152062306a36Sopenharmony_ci					   mode->name, n, range_start, range_end);
152162306a36Sopenharmony_ci				goto out;
152262306a36Sopenharmony_ci			}
152362306a36Sopenharmony_ci		}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci		for (n = 1; n <= range_size; n <<= 1) {
152662306a36Sopenharmony_ci			drm_random_reorder(order, size, &prng);
152762306a36Sopenharmony_ci			if (evict_something(test, &mm, range_start, range_end, nodes,
152862306a36Sopenharmony_ci					    order, size, range_size / 2, n, mode)) {
152962306a36Sopenharmony_ci				KUNIT_FAIL(test,
153062306a36Sopenharmony_ci					   "%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n",
153162306a36Sopenharmony_ci					   mode->name, range_size / 2, n, range_start, range_end);
153262306a36Sopenharmony_ci				goto out;
153362306a36Sopenharmony_ci			}
153462306a36Sopenharmony_ci		}
153562306a36Sopenharmony_ci
153662306a36Sopenharmony_ci		for_each_prime_number_from(n, 1, min(range_size, max_prime)) {
153762306a36Sopenharmony_ci			unsigned int nsize = (range_size - n + 1) / 2;
153862306a36Sopenharmony_ci
153962306a36Sopenharmony_ci			DRM_MM_BUG_ON(!nsize);
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci			drm_random_reorder(order, size, &prng);
154262306a36Sopenharmony_ci			if (evict_something(test, &mm, range_start, range_end, nodes,
154362306a36Sopenharmony_ci					    order, size, nsize, n, mode)) {
154462306a36Sopenharmony_ci				KUNIT_FAIL(test,
154562306a36Sopenharmony_ci					   "%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n",
154662306a36Sopenharmony_ci					   mode->name, nsize, n, range_start, range_end);
154762306a36Sopenharmony_ci				goto out;
154862306a36Sopenharmony_ci			}
154962306a36Sopenharmony_ci		}
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci		cond_resched();
155262306a36Sopenharmony_ci	}
155362306a36Sopenharmony_ci
155462306a36Sopenharmony_ciout:
155562306a36Sopenharmony_ci	drm_mm_for_each_node_safe(node, next, &mm)
155662306a36Sopenharmony_ci		drm_mm_remove_node(node);
155762306a36Sopenharmony_ci	drm_mm_takedown(&mm);
155862306a36Sopenharmony_ci	kfree(order);
155962306a36Sopenharmony_cierr_nodes:
156062306a36Sopenharmony_ci	vfree(nodes);
156162306a36Sopenharmony_ci}
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_cistatic unsigned int node_index(const struct drm_mm_node *node)
156462306a36Sopenharmony_ci{
156562306a36Sopenharmony_ci	return div64_u64(node->start, node->size);
156662306a36Sopenharmony_ci}
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_cistatic void drm_test_mm_topdown(struct kunit *test)
156962306a36Sopenharmony_ci{
157062306a36Sopenharmony_ci	const struct insert_mode *topdown = &insert_modes[TOPDOWN];
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	DRM_RND_STATE(prng, random_seed);
157362306a36Sopenharmony_ci	const unsigned int count = 8192;
157462306a36Sopenharmony_ci	unsigned int size;
157562306a36Sopenharmony_ci	unsigned long *bitmap;
157662306a36Sopenharmony_ci	struct drm_mm mm;
157762306a36Sopenharmony_ci	struct drm_mm_node *nodes, *node, *next;
157862306a36Sopenharmony_ci	unsigned int *order, n, m, o = 0;
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	/* When allocating top-down, we expect to be returned a node
158162306a36Sopenharmony_ci	 * from a suitable hole at the top of the drm_mm. We check that
158262306a36Sopenharmony_ci	 * the returned node does match the highest available slot.
158362306a36Sopenharmony_ci	 */
158462306a36Sopenharmony_ci
158562306a36Sopenharmony_ci	nodes = vzalloc(array_size(count, sizeof(*nodes)));
158662306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE(test, nodes);
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	bitmap = bitmap_zalloc(count, GFP_KERNEL);
158962306a36Sopenharmony_ci	if (!bitmap)
159062306a36Sopenharmony_ci		goto err_nodes;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	order = drm_random_order(count, &prng);
159362306a36Sopenharmony_ci	if (!order)
159462306a36Sopenharmony_ci		goto err_bitmap;
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	for (size = 1; size <= 64; size <<= 1) {
159762306a36Sopenharmony_ci		drm_mm_init(&mm, 0, size * count);
159862306a36Sopenharmony_ci		for (n = 0; n < count; n++) {
159962306a36Sopenharmony_ci			if (!expect_insert(test, &mm, &nodes[n], size, 0, n, topdown)) {
160062306a36Sopenharmony_ci				KUNIT_FAIL(test, "insert failed, size %u step %d\n", size, n);
160162306a36Sopenharmony_ci				goto out;
160262306a36Sopenharmony_ci			}
160362306a36Sopenharmony_ci
160462306a36Sopenharmony_ci			if (drm_mm_hole_follows(&nodes[n])) {
160562306a36Sopenharmony_ci				KUNIT_FAIL(test,
160662306a36Sopenharmony_ci					   "hole after topdown insert %d, start=%llx\n, size=%u",
160762306a36Sopenharmony_ci					   n, nodes[n].start, size);
160862306a36Sopenharmony_ci				goto out;
160962306a36Sopenharmony_ci			}
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci			if (!assert_one_hole(test, &mm, 0, size * (count - n - 1)))
161262306a36Sopenharmony_ci				goto out;
161362306a36Sopenharmony_ci		}
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci		if (!assert_continuous(test, &mm, size))
161662306a36Sopenharmony_ci			goto out;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci		drm_random_reorder(order, count, &prng);
161962306a36Sopenharmony_ci		for_each_prime_number_from(n, 1, min(count, max_prime)) {
162062306a36Sopenharmony_ci			for (m = 0; m < n; m++) {
162162306a36Sopenharmony_ci				node = &nodes[order[(o + m) % count]];
162262306a36Sopenharmony_ci				drm_mm_remove_node(node);
162362306a36Sopenharmony_ci				__set_bit(node_index(node), bitmap);
162462306a36Sopenharmony_ci			}
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_ci			for (m = 0; m < n; m++) {
162762306a36Sopenharmony_ci				unsigned int last;
162862306a36Sopenharmony_ci
162962306a36Sopenharmony_ci				node = &nodes[order[(o + m) % count]];
163062306a36Sopenharmony_ci				if (!expect_insert(test, &mm, node, size, 0, 0, topdown)) {
163162306a36Sopenharmony_ci					KUNIT_FAIL(test, "insert failed, step %d/%d\n", m, n);
163262306a36Sopenharmony_ci					goto out;
163362306a36Sopenharmony_ci				}
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci				if (drm_mm_hole_follows(node)) {
163662306a36Sopenharmony_ci					KUNIT_FAIL(test,
163762306a36Sopenharmony_ci						   "hole after topdown insert %d/%d, start=%llx\n",
163862306a36Sopenharmony_ci						   m, n, node->start);
163962306a36Sopenharmony_ci					goto out;
164062306a36Sopenharmony_ci				}
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci				last = find_last_bit(bitmap, count);
164362306a36Sopenharmony_ci				if (node_index(node) != last) {
164462306a36Sopenharmony_ci					KUNIT_FAIL(test,
164562306a36Sopenharmony_ci						   "node %d/%d, size %d, not inserted into upmost hole, expected %d, found %d\n",
164662306a36Sopenharmony_ci						   m, n, size, last, node_index(node));
164762306a36Sopenharmony_ci					goto out;
164862306a36Sopenharmony_ci				}
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci				__clear_bit(last, bitmap);
165162306a36Sopenharmony_ci			}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci			DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count);
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci			o += n;
165662306a36Sopenharmony_ci		}
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci		drm_mm_for_each_node_safe(node, next, &mm)
165962306a36Sopenharmony_ci			drm_mm_remove_node(node);
166062306a36Sopenharmony_ci		DRM_MM_BUG_ON(!drm_mm_clean(&mm));
166162306a36Sopenharmony_ci		cond_resched();
166262306a36Sopenharmony_ci	}
166362306a36Sopenharmony_ci
166462306a36Sopenharmony_ciout:
166562306a36Sopenharmony_ci	drm_mm_for_each_node_safe(node, next, &mm)
166662306a36Sopenharmony_ci		drm_mm_remove_node(node);
166762306a36Sopenharmony_ci	drm_mm_takedown(&mm);
166862306a36Sopenharmony_ci	kfree(order);
166962306a36Sopenharmony_cierr_bitmap:
167062306a36Sopenharmony_ci	bitmap_free(bitmap);
167162306a36Sopenharmony_cierr_nodes:
167262306a36Sopenharmony_ci	vfree(nodes);
167362306a36Sopenharmony_ci}
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_cistatic void drm_test_mm_bottomup(struct kunit *test)
167662306a36Sopenharmony_ci{
167762306a36Sopenharmony_ci	const struct insert_mode *bottomup = &insert_modes[BOTTOMUP];
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	DRM_RND_STATE(prng, random_seed);
168062306a36Sopenharmony_ci	const unsigned int count = 8192;
168162306a36Sopenharmony_ci	unsigned int size;
168262306a36Sopenharmony_ci	unsigned long *bitmap;
168362306a36Sopenharmony_ci	struct drm_mm mm;
168462306a36Sopenharmony_ci	struct drm_mm_node *nodes, *node, *next;
168562306a36Sopenharmony_ci	unsigned int *order, n, m, o = 0;
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	/* Like drm_test_mm_topdown, but instead of searching for the last hole,
168862306a36Sopenharmony_ci	 * we search for the first.
168962306a36Sopenharmony_ci	 */
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci	nodes = vzalloc(array_size(count, sizeof(*nodes)));
169262306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE(test, nodes);
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_ci	bitmap = bitmap_zalloc(count, GFP_KERNEL);
169562306a36Sopenharmony_ci	if (!bitmap)
169662306a36Sopenharmony_ci		goto err_nodes;
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	order = drm_random_order(count, &prng);
169962306a36Sopenharmony_ci	if (!order)
170062306a36Sopenharmony_ci		goto err_bitmap;
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	for (size = 1; size <= 64; size <<= 1) {
170362306a36Sopenharmony_ci		drm_mm_init(&mm, 0, size * count);
170462306a36Sopenharmony_ci		for (n = 0; n < count; n++) {
170562306a36Sopenharmony_ci			if (!expect_insert(test, &mm, &nodes[n], size, 0, n, bottomup)) {
170662306a36Sopenharmony_ci				KUNIT_FAIL(test,
170762306a36Sopenharmony_ci					   "bottomup insert failed, size %u step %d\n", size, n);
170862306a36Sopenharmony_ci				goto out;
170962306a36Sopenharmony_ci			}
171062306a36Sopenharmony_ci
171162306a36Sopenharmony_ci			if (!assert_one_hole(test, &mm, size * (n + 1), size * count))
171262306a36Sopenharmony_ci				goto out;
171362306a36Sopenharmony_ci		}
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci		if (!assert_continuous(test, &mm, size))
171662306a36Sopenharmony_ci			goto out;
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_ci		drm_random_reorder(order, count, &prng);
171962306a36Sopenharmony_ci		for_each_prime_number_from(n, 1, min(count, max_prime)) {
172062306a36Sopenharmony_ci			for (m = 0; m < n; m++) {
172162306a36Sopenharmony_ci				node = &nodes[order[(o + m) % count]];
172262306a36Sopenharmony_ci				drm_mm_remove_node(node);
172362306a36Sopenharmony_ci				__set_bit(node_index(node), bitmap);
172462306a36Sopenharmony_ci			}
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci			for (m = 0; m < n; m++) {
172762306a36Sopenharmony_ci				unsigned int first;
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci				node = &nodes[order[(o + m) % count]];
173062306a36Sopenharmony_ci				if (!expect_insert(test, &mm, node, size, 0, 0, bottomup)) {
173162306a36Sopenharmony_ci					KUNIT_FAIL(test, "insert failed, step %d/%d\n", m, n);
173262306a36Sopenharmony_ci					goto out;
173362306a36Sopenharmony_ci				}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_ci				first = find_first_bit(bitmap, count);
173662306a36Sopenharmony_ci				if (node_index(node) != first) {
173762306a36Sopenharmony_ci					KUNIT_FAIL(test,
173862306a36Sopenharmony_ci						   "node %d/%d not inserted into bottom hole, expected %d, found %d\n",
173962306a36Sopenharmony_ci						   m, n, first, node_index(node));
174062306a36Sopenharmony_ci					goto out;
174162306a36Sopenharmony_ci				}
174262306a36Sopenharmony_ci				__clear_bit(first, bitmap);
174362306a36Sopenharmony_ci			}
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci			DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count);
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci			o += n;
174862306a36Sopenharmony_ci		}
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci		drm_mm_for_each_node_safe(node, next, &mm)
175162306a36Sopenharmony_ci			drm_mm_remove_node(node);
175262306a36Sopenharmony_ci		DRM_MM_BUG_ON(!drm_mm_clean(&mm));
175362306a36Sopenharmony_ci		cond_resched();
175462306a36Sopenharmony_ci	}
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ciout:
175762306a36Sopenharmony_ci	drm_mm_for_each_node_safe(node, next, &mm)
175862306a36Sopenharmony_ci		drm_mm_remove_node(node);
175962306a36Sopenharmony_ci	drm_mm_takedown(&mm);
176062306a36Sopenharmony_ci	kfree(order);
176162306a36Sopenharmony_cierr_bitmap:
176262306a36Sopenharmony_ci	bitmap_free(bitmap);
176362306a36Sopenharmony_cierr_nodes:
176462306a36Sopenharmony_ci	vfree(nodes);
176562306a36Sopenharmony_ci}
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_cistatic void drm_test_mm_once(struct kunit *test, unsigned int mode)
176862306a36Sopenharmony_ci{
176962306a36Sopenharmony_ci	struct drm_mm mm;
177062306a36Sopenharmony_ci	struct drm_mm_node rsvd_lo, rsvd_hi, node;
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	drm_mm_init(&mm, 0, 7);
177362306a36Sopenharmony_ci
177462306a36Sopenharmony_ci	memset(&rsvd_lo, 0, sizeof(rsvd_lo));
177562306a36Sopenharmony_ci	rsvd_lo.start = 1;
177662306a36Sopenharmony_ci	rsvd_lo.size = 1;
177762306a36Sopenharmony_ci	if (drm_mm_reserve_node(&mm, &rsvd_lo)) {
177862306a36Sopenharmony_ci		KUNIT_FAIL(test, "Could not reserve low node\n");
177962306a36Sopenharmony_ci		goto err;
178062306a36Sopenharmony_ci	}
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci	memset(&rsvd_hi, 0, sizeof(rsvd_hi));
178362306a36Sopenharmony_ci	rsvd_hi.start = 5;
178462306a36Sopenharmony_ci	rsvd_hi.size = 1;
178562306a36Sopenharmony_ci	if (drm_mm_reserve_node(&mm, &rsvd_hi)) {
178662306a36Sopenharmony_ci		KUNIT_FAIL(test, "Could not reserve low node\n");
178762306a36Sopenharmony_ci		goto err_lo;
178862306a36Sopenharmony_ci	}
178962306a36Sopenharmony_ci
179062306a36Sopenharmony_ci	if (!drm_mm_hole_follows(&rsvd_lo) || !drm_mm_hole_follows(&rsvd_hi)) {
179162306a36Sopenharmony_ci		KUNIT_FAIL(test, "Expected a hole after lo and high nodes!\n");
179262306a36Sopenharmony_ci		goto err_hi;
179362306a36Sopenharmony_ci	}
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	memset(&node, 0, sizeof(node));
179662306a36Sopenharmony_ci	if (drm_mm_insert_node_generic(&mm, &node, 2, 0, 0, mode)) {
179762306a36Sopenharmony_ci		KUNIT_FAIL(test, "Could not insert the node into the available hole!\n");
179862306a36Sopenharmony_ci		goto err_hi;
179962306a36Sopenharmony_ci	}
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	drm_mm_remove_node(&node);
180262306a36Sopenharmony_cierr_hi:
180362306a36Sopenharmony_ci	drm_mm_remove_node(&rsvd_hi);
180462306a36Sopenharmony_cierr_lo:
180562306a36Sopenharmony_ci	drm_mm_remove_node(&rsvd_lo);
180662306a36Sopenharmony_cierr:
180762306a36Sopenharmony_ci	drm_mm_takedown(&mm);
180862306a36Sopenharmony_ci}
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_cistatic void drm_test_mm_lowest(struct kunit *test)
181162306a36Sopenharmony_ci{
181262306a36Sopenharmony_ci	drm_test_mm_once(test, DRM_MM_INSERT_LOW);
181362306a36Sopenharmony_ci}
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_cistatic void drm_test_mm_highest(struct kunit *test)
181662306a36Sopenharmony_ci{
181762306a36Sopenharmony_ci	drm_test_mm_once(test, DRM_MM_INSERT_HIGH);
181862306a36Sopenharmony_ci}
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_cistatic void separate_adjacent_colors(const struct drm_mm_node *node,
182162306a36Sopenharmony_ci				     unsigned long color, u64 *start, u64 *end)
182262306a36Sopenharmony_ci{
182362306a36Sopenharmony_ci	if (drm_mm_node_allocated(node) && node->color != color)
182462306a36Sopenharmony_ci		++*start;
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_ci	node = list_next_entry(node, node_list);
182762306a36Sopenharmony_ci	if (drm_mm_node_allocated(node) && node->color != color)
182862306a36Sopenharmony_ci		--*end;
182962306a36Sopenharmony_ci}
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_cistatic bool colors_abutt(struct kunit *test, const struct drm_mm_node *node)
183262306a36Sopenharmony_ci{
183362306a36Sopenharmony_ci	if (!drm_mm_hole_follows(node) &&
183462306a36Sopenharmony_ci	    drm_mm_node_allocated(list_next_entry(node, node_list))) {
183562306a36Sopenharmony_ci		KUNIT_FAIL(test, "colors abutt; %ld [%llx + %llx] is next to %ld [%llx + %llx]!\n",
183662306a36Sopenharmony_ci			   node->color, node->start, node->size,
183762306a36Sopenharmony_ci		       list_next_entry(node, node_list)->color,
183862306a36Sopenharmony_ci		       list_next_entry(node, node_list)->start,
183962306a36Sopenharmony_ci		       list_next_entry(node, node_list)->size);
184062306a36Sopenharmony_ci		return true;
184162306a36Sopenharmony_ci	}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci	return false;
184462306a36Sopenharmony_ci}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_cistatic void drm_test_mm_color(struct kunit *test)
184762306a36Sopenharmony_ci{
184862306a36Sopenharmony_ci	const unsigned int count = min(4096u, max_iterations);
184962306a36Sopenharmony_ci	const struct insert_mode *mode;
185062306a36Sopenharmony_ci	struct drm_mm mm;
185162306a36Sopenharmony_ci	struct drm_mm_node *node, *nn;
185262306a36Sopenharmony_ci	unsigned int n;
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	/* Color adjustment complicates everything. First we just check
185562306a36Sopenharmony_ci	 * that when we insert a node we apply any color_adjustment callback.
185662306a36Sopenharmony_ci	 * The callback we use should ensure that there is a gap between
185762306a36Sopenharmony_ci	 * any two nodes, and so after each insertion we check that those
185862306a36Sopenharmony_ci	 * holes are inserted and that they are preserved.
185962306a36Sopenharmony_ci	 */
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	drm_mm_init(&mm, 0, U64_MAX);
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	for (n = 1; n <= count; n++) {
186462306a36Sopenharmony_ci		node = kzalloc(sizeof(*node), GFP_KERNEL);
186562306a36Sopenharmony_ci		if (!node)
186662306a36Sopenharmony_ci			goto out;
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci		if (!expect_insert(test, &mm, node, n, 0, n, &insert_modes[0])) {
186962306a36Sopenharmony_ci			KUNIT_FAIL(test, "insert failed, step %d\n", n);
187062306a36Sopenharmony_ci			kfree(node);
187162306a36Sopenharmony_ci			goto out;
187262306a36Sopenharmony_ci		}
187362306a36Sopenharmony_ci	}
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	drm_mm_for_each_node_safe(node, nn, &mm) {
187662306a36Sopenharmony_ci		if (node->color != node->size) {
187762306a36Sopenharmony_ci			KUNIT_FAIL(test, "invalid color stored: expected %lld, found %ld\n",
187862306a36Sopenharmony_ci				   node->size, node->color);
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci			goto out;
188162306a36Sopenharmony_ci		}
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci		drm_mm_remove_node(node);
188462306a36Sopenharmony_ci		kfree(node);
188562306a36Sopenharmony_ci	}
188662306a36Sopenharmony_ci
188762306a36Sopenharmony_ci	/* Now, let's start experimenting with applying a color callback */
188862306a36Sopenharmony_ci	mm.color_adjust = separate_adjacent_colors;
188962306a36Sopenharmony_ci	for (mode = insert_modes; mode->name; mode++) {
189062306a36Sopenharmony_ci		u64 last;
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci		node = kzalloc(sizeof(*node), GFP_KERNEL);
189362306a36Sopenharmony_ci		if (!node)
189462306a36Sopenharmony_ci			goto out;
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci		node->size = 1 + 2 * count;
189762306a36Sopenharmony_ci		node->color = node->size;
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci		if (drm_mm_reserve_node(&mm, node)) {
190062306a36Sopenharmony_ci			KUNIT_FAIL(test, "initial reserve failed!\n");
190162306a36Sopenharmony_ci			goto out;
190262306a36Sopenharmony_ci		}
190362306a36Sopenharmony_ci
190462306a36Sopenharmony_ci		last = node->start + node->size;
190562306a36Sopenharmony_ci
190662306a36Sopenharmony_ci		for (n = 1; n <= count; n++) {
190762306a36Sopenharmony_ci			int rem;
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci			node = kzalloc(sizeof(*node), GFP_KERNEL);
191062306a36Sopenharmony_ci			if (!node)
191162306a36Sopenharmony_ci				goto out;
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci			node->start = last;
191462306a36Sopenharmony_ci			node->size = n + count;
191562306a36Sopenharmony_ci			node->color = node->size;
191662306a36Sopenharmony_ci
191762306a36Sopenharmony_ci			if (drm_mm_reserve_node(&mm, node) != -ENOSPC) {
191862306a36Sopenharmony_ci				KUNIT_FAIL(test, "reserve %d did not report color overlap!", n);
191962306a36Sopenharmony_ci				goto out;
192062306a36Sopenharmony_ci			}
192162306a36Sopenharmony_ci
192262306a36Sopenharmony_ci			node->start += n + 1;
192362306a36Sopenharmony_ci			rem = misalignment(node, n + count);
192462306a36Sopenharmony_ci			node->start += n + count - rem;
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci			if (drm_mm_reserve_node(&mm, node)) {
192762306a36Sopenharmony_ci				KUNIT_FAIL(test, "reserve %d failed", n);
192862306a36Sopenharmony_ci				goto out;
192962306a36Sopenharmony_ci			}
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci			last = node->start + node->size;
193262306a36Sopenharmony_ci		}
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci		for (n = 1; n <= count; n++) {
193562306a36Sopenharmony_ci			node = kzalloc(sizeof(*node), GFP_KERNEL);
193662306a36Sopenharmony_ci			if (!node)
193762306a36Sopenharmony_ci				goto out;
193862306a36Sopenharmony_ci
193962306a36Sopenharmony_ci			if (!expect_insert(test, &mm, node, n, n, n, mode)) {
194062306a36Sopenharmony_ci				KUNIT_FAIL(test, "%s insert failed, step %d\n", mode->name, n);
194162306a36Sopenharmony_ci				kfree(node);
194262306a36Sopenharmony_ci				goto out;
194362306a36Sopenharmony_ci			}
194462306a36Sopenharmony_ci		}
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci		drm_mm_for_each_node_safe(node, nn, &mm) {
194762306a36Sopenharmony_ci			u64 rem;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci			if (node->color != node->size) {
195062306a36Sopenharmony_ci				KUNIT_FAIL(test,
195162306a36Sopenharmony_ci					   "%s invalid color stored: expected %lld, found %ld\n",
195262306a36Sopenharmony_ci					   mode->name, node->size, node->color);
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci				goto out;
195562306a36Sopenharmony_ci			}
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci			if (colors_abutt(test, node))
195862306a36Sopenharmony_ci				goto out;
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci			div64_u64_rem(node->start, node->size, &rem);
196162306a36Sopenharmony_ci			if (rem) {
196262306a36Sopenharmony_ci				KUNIT_FAIL(test,
196362306a36Sopenharmony_ci					   "%s colored node misaligned, start=%llx expected alignment=%lld [rem=%lld]\n",
196462306a36Sopenharmony_ci					   mode->name, node->start, node->size, rem);
196562306a36Sopenharmony_ci				goto out;
196662306a36Sopenharmony_ci			}
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci			drm_mm_remove_node(node);
196962306a36Sopenharmony_ci			kfree(node);
197062306a36Sopenharmony_ci		}
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci		cond_resched();
197362306a36Sopenharmony_ci	}
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ciout:
197662306a36Sopenharmony_ci	drm_mm_for_each_node_safe(node, nn, &mm) {
197762306a36Sopenharmony_ci		drm_mm_remove_node(node);
197862306a36Sopenharmony_ci		kfree(node);
197962306a36Sopenharmony_ci	}
198062306a36Sopenharmony_ci	drm_mm_takedown(&mm);
198162306a36Sopenharmony_ci}
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_cistatic int evict_color(struct kunit *test, struct drm_mm *mm, u64 range_start,
198462306a36Sopenharmony_ci		       u64 range_end, struct evict_node *nodes, unsigned int *order,
198562306a36Sopenharmony_ci		unsigned int count, unsigned int size, unsigned int alignment,
198662306a36Sopenharmony_ci		unsigned long color, const struct insert_mode *mode)
198762306a36Sopenharmony_ci{
198862306a36Sopenharmony_ci	struct drm_mm_scan scan;
198962306a36Sopenharmony_ci	LIST_HEAD(evict_list);
199062306a36Sopenharmony_ci	struct evict_node *e;
199162306a36Sopenharmony_ci	struct drm_mm_node tmp;
199262306a36Sopenharmony_ci	int err;
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	drm_mm_scan_init_with_range(&scan, mm, size, alignment, color, range_start,
199562306a36Sopenharmony_ci				    range_end, mode->mode);
199662306a36Sopenharmony_ci	if (!evict_nodes(test, &scan, nodes, order, count, true, &evict_list))
199762306a36Sopenharmony_ci		return -EINVAL;
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci	memset(&tmp, 0, sizeof(tmp));
200062306a36Sopenharmony_ci	err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, color,
200162306a36Sopenharmony_ci					 DRM_MM_INSERT_EVICT);
200262306a36Sopenharmony_ci	if (err) {
200362306a36Sopenharmony_ci		KUNIT_FAIL(test,
200462306a36Sopenharmony_ci			   "Failed to insert into eviction hole: size=%d, align=%d, color=%lu, err=%d\n",
200562306a36Sopenharmony_ci			   size, alignment, color, err);
200662306a36Sopenharmony_ci		show_scan(test, &scan);
200762306a36Sopenharmony_ci		show_holes(test, mm, 3);
200862306a36Sopenharmony_ci		return err;
200962306a36Sopenharmony_ci	}
201062306a36Sopenharmony_ci
201162306a36Sopenharmony_ci	if (tmp.start < range_start || tmp.start + tmp.size > range_end) {
201262306a36Sopenharmony_ci		KUNIT_FAIL(test,
201362306a36Sopenharmony_ci			   "Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n",
201462306a36Sopenharmony_ci			   tmp.start, tmp.size, range_start, range_end);
201562306a36Sopenharmony_ci		err = -EINVAL;
201662306a36Sopenharmony_ci	}
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	if (colors_abutt(test, &tmp))
201962306a36Sopenharmony_ci		err = -EINVAL;
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	if (!assert_node(test, &tmp, mm, size, alignment, color)) {
202262306a36Sopenharmony_ci		KUNIT_FAIL(test,
202362306a36Sopenharmony_ci			   "Inserted did not fit the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx\n",
202462306a36Sopenharmony_ci			   tmp.size, size, alignment, misalignment(&tmp, alignment), tmp.start);
202562306a36Sopenharmony_ci		err = -EINVAL;
202662306a36Sopenharmony_ci	}
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci	drm_mm_remove_node(&tmp);
202962306a36Sopenharmony_ci	if (err)
203062306a36Sopenharmony_ci		return err;
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	list_for_each_entry(e, &evict_list, link) {
203362306a36Sopenharmony_ci		err = drm_mm_reserve_node(mm, &e->node);
203462306a36Sopenharmony_ci		if (err) {
203562306a36Sopenharmony_ci			KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n",
203662306a36Sopenharmony_ci				   e->node.start);
203762306a36Sopenharmony_ci			return err;
203862306a36Sopenharmony_ci		}
203962306a36Sopenharmony_ci	}
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	cond_resched();
204262306a36Sopenharmony_ci	return 0;
204362306a36Sopenharmony_ci}
204462306a36Sopenharmony_ci
204562306a36Sopenharmony_cistatic void drm_test_mm_color_evict(struct kunit *test)
204662306a36Sopenharmony_ci{
204762306a36Sopenharmony_ci	DRM_RND_STATE(prng, random_seed);
204862306a36Sopenharmony_ci	const unsigned int total_size = min(8192u, max_iterations);
204962306a36Sopenharmony_ci	const struct insert_mode *mode;
205062306a36Sopenharmony_ci	unsigned long color = 0;
205162306a36Sopenharmony_ci	struct drm_mm mm;
205262306a36Sopenharmony_ci	struct evict_node *nodes;
205362306a36Sopenharmony_ci	struct drm_mm_node *node, *next;
205462306a36Sopenharmony_ci	unsigned int *order, n;
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	/* Check that the drm_mm_scan also honours color adjustment when
205762306a36Sopenharmony_ci	 * choosing its victims to create a hole. Our color_adjust does not
205862306a36Sopenharmony_ci	 * allow two nodes to be placed together without an intervening hole
205962306a36Sopenharmony_ci	 * enlarging the set of victims that must be evicted.
206062306a36Sopenharmony_ci	 */
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	nodes = vzalloc(array_size(total_size, sizeof(*nodes)));
206362306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE(test, nodes);
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_ci	order = drm_random_order(total_size, &prng);
206662306a36Sopenharmony_ci	if (!order)
206762306a36Sopenharmony_ci		goto err_nodes;
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	drm_mm_init(&mm, 0, 2 * total_size - 1);
207062306a36Sopenharmony_ci	mm.color_adjust = separate_adjacent_colors;
207162306a36Sopenharmony_ci	for (n = 0; n < total_size; n++) {
207262306a36Sopenharmony_ci		if (!expect_insert(test, &mm, &nodes[n].node,
207362306a36Sopenharmony_ci				   1, 0, color++,
207462306a36Sopenharmony_ci				   &insert_modes[0])) {
207562306a36Sopenharmony_ci			KUNIT_FAIL(test, "insert failed, step %d\n", n);
207662306a36Sopenharmony_ci			goto out;
207762306a36Sopenharmony_ci		}
207862306a36Sopenharmony_ci	}
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	for (mode = evict_modes; mode->name; mode++) {
208162306a36Sopenharmony_ci		for (n = 1; n <= total_size; n <<= 1) {
208262306a36Sopenharmony_ci			drm_random_reorder(order, total_size, &prng);
208362306a36Sopenharmony_ci			if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size,
208462306a36Sopenharmony_ci					n, 1, color++, mode)) {
208562306a36Sopenharmony_ci				KUNIT_FAIL(test, "%s evict_color(size=%u) failed\n", mode->name, n);
208662306a36Sopenharmony_ci				goto out;
208762306a36Sopenharmony_ci			}
208862306a36Sopenharmony_ci		}
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci		for (n = 1; n < total_size; n <<= 1) {
209162306a36Sopenharmony_ci			drm_random_reorder(order, total_size, &prng);
209262306a36Sopenharmony_ci			if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size,
209362306a36Sopenharmony_ci					total_size / 2, n, color++, mode)) {
209462306a36Sopenharmony_ci				KUNIT_FAIL(test, "%s evict_color(size=%u, alignment=%u) failed\n",
209562306a36Sopenharmony_ci					   mode->name, total_size / 2, n);
209662306a36Sopenharmony_ci				goto out;
209762306a36Sopenharmony_ci			}
209862306a36Sopenharmony_ci		}
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci		for_each_prime_number_from(n, 1, min(total_size, max_prime)) {
210162306a36Sopenharmony_ci			unsigned int nsize = (total_size - n + 1) / 2;
210262306a36Sopenharmony_ci
210362306a36Sopenharmony_ci			DRM_MM_BUG_ON(!nsize);
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci			drm_random_reorder(order, total_size, &prng);
210662306a36Sopenharmony_ci			if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size,
210762306a36Sopenharmony_ci					nsize, n, color++, mode)) {
210862306a36Sopenharmony_ci				KUNIT_FAIL(test, "%s evict_color(size=%u, alignment=%u) failed\n",
210962306a36Sopenharmony_ci					   mode->name, nsize, n);
211062306a36Sopenharmony_ci				goto out;
211162306a36Sopenharmony_ci			}
211262306a36Sopenharmony_ci		}
211362306a36Sopenharmony_ci
211462306a36Sopenharmony_ci		cond_resched();
211562306a36Sopenharmony_ci	}
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ciout:
211862306a36Sopenharmony_ci	drm_mm_for_each_node_safe(node, next, &mm)
211962306a36Sopenharmony_ci		drm_mm_remove_node(node);
212062306a36Sopenharmony_ci	drm_mm_takedown(&mm);
212162306a36Sopenharmony_ci	kfree(order);
212262306a36Sopenharmony_cierr_nodes:
212362306a36Sopenharmony_ci	vfree(nodes);
212462306a36Sopenharmony_ci}
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_cistatic void drm_test_mm_color_evict_range(struct kunit *test)
212762306a36Sopenharmony_ci{
212862306a36Sopenharmony_ci	DRM_RND_STATE(prng, random_seed);
212962306a36Sopenharmony_ci	const unsigned int total_size = 8192;
213062306a36Sopenharmony_ci	const unsigned int range_size = total_size / 2;
213162306a36Sopenharmony_ci	const unsigned int range_start = total_size / 4;
213262306a36Sopenharmony_ci	const unsigned int range_end = range_start + range_size;
213362306a36Sopenharmony_ci	const struct insert_mode *mode;
213462306a36Sopenharmony_ci	unsigned long color = 0;
213562306a36Sopenharmony_ci	struct drm_mm mm;
213662306a36Sopenharmony_ci	struct evict_node *nodes;
213762306a36Sopenharmony_ci	struct drm_mm_node *node, *next;
213862306a36Sopenharmony_ci	unsigned int *order, n;
213962306a36Sopenharmony_ci
214062306a36Sopenharmony_ci	/* Like drm_test_mm_color_evict(), but limited to small portion of the full
214162306a36Sopenharmony_ci	 * drm_mm range.
214262306a36Sopenharmony_ci	 */
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_ci	nodes = vzalloc(array_size(total_size, sizeof(*nodes)));
214562306a36Sopenharmony_ci	KUNIT_ASSERT_TRUE(test, nodes);
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci	order = drm_random_order(total_size, &prng);
214862306a36Sopenharmony_ci	if (!order)
214962306a36Sopenharmony_ci		goto err_nodes;
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_ci	drm_mm_init(&mm, 0, 2 * total_size - 1);
215262306a36Sopenharmony_ci	mm.color_adjust = separate_adjacent_colors;
215362306a36Sopenharmony_ci	for (n = 0; n < total_size; n++) {
215462306a36Sopenharmony_ci		if (!expect_insert(test, &mm, &nodes[n].node,
215562306a36Sopenharmony_ci				   1, 0, color++,
215662306a36Sopenharmony_ci				   &insert_modes[0])) {
215762306a36Sopenharmony_ci			KUNIT_FAIL(test, "insert failed, step %d\n", n);
215862306a36Sopenharmony_ci			goto out;
215962306a36Sopenharmony_ci		}
216062306a36Sopenharmony_ci	}
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci	for (mode = evict_modes; mode->name; mode++) {
216362306a36Sopenharmony_ci		for (n = 1; n <= range_size; n <<= 1) {
216462306a36Sopenharmony_ci			drm_random_reorder(order, range_size, &prng);
216562306a36Sopenharmony_ci			if (evict_color(test, &mm, range_start, range_end, nodes, order,
216662306a36Sopenharmony_ci					total_size, n, 1, color++, mode)) {
216762306a36Sopenharmony_ci				KUNIT_FAIL(test,
216862306a36Sopenharmony_ci					   "%s evict_color(size=%u) failed for range [%x, %x]\n",
216962306a36Sopenharmony_ci						mode->name, n, range_start, range_end);
217062306a36Sopenharmony_ci				goto out;
217162306a36Sopenharmony_ci			}
217262306a36Sopenharmony_ci		}
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci		for (n = 1; n < range_size; n <<= 1) {
217562306a36Sopenharmony_ci			drm_random_reorder(order, total_size, &prng);
217662306a36Sopenharmony_ci			if (evict_color(test, &mm, range_start, range_end, nodes, order,
217762306a36Sopenharmony_ci					total_size, range_size / 2, n, color++, mode)) {
217862306a36Sopenharmony_ci				KUNIT_FAIL(test,
217962306a36Sopenharmony_ci					   "%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n",
218062306a36Sopenharmony_ci					   mode->name, total_size / 2, n, range_start, range_end);
218162306a36Sopenharmony_ci				goto out;
218262306a36Sopenharmony_ci			}
218362306a36Sopenharmony_ci		}
218462306a36Sopenharmony_ci
218562306a36Sopenharmony_ci		for_each_prime_number_from(n, 1, min(range_size, max_prime)) {
218662306a36Sopenharmony_ci			unsigned int nsize = (range_size - n + 1) / 2;
218762306a36Sopenharmony_ci
218862306a36Sopenharmony_ci			DRM_MM_BUG_ON(!nsize);
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci			drm_random_reorder(order, total_size, &prng);
219162306a36Sopenharmony_ci			if (evict_color(test, &mm, range_start, range_end, nodes, order,
219262306a36Sopenharmony_ci					total_size, nsize, n, color++, mode)) {
219362306a36Sopenharmony_ci				KUNIT_FAIL(test,
219462306a36Sopenharmony_ci					   "%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n",
219562306a36Sopenharmony_ci					   mode->name, nsize, n, range_start, range_end);
219662306a36Sopenharmony_ci				goto out;
219762306a36Sopenharmony_ci			}
219862306a36Sopenharmony_ci		}
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci		cond_resched();
220162306a36Sopenharmony_ci	}
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ciout:
220462306a36Sopenharmony_ci	drm_mm_for_each_node_safe(node, next, &mm)
220562306a36Sopenharmony_ci		drm_mm_remove_node(node);
220662306a36Sopenharmony_ci	drm_mm_takedown(&mm);
220762306a36Sopenharmony_ci	kfree(order);
220862306a36Sopenharmony_cierr_nodes:
220962306a36Sopenharmony_ci	vfree(nodes);
221062306a36Sopenharmony_ci}
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_cistatic int drm_mm_suite_init(struct kunit_suite *suite)
221362306a36Sopenharmony_ci{
221462306a36Sopenharmony_ci	while (!random_seed)
221562306a36Sopenharmony_ci		random_seed = get_random_u32();
221662306a36Sopenharmony_ci
221762306a36Sopenharmony_ci	kunit_info(suite,
221862306a36Sopenharmony_ci		   "Testing DRM range manager, with random_seed=0x%x max_iterations=%u max_prime=%u\n",
221962306a36Sopenharmony_ci		   random_seed, max_iterations, max_prime);
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci	return 0;
222262306a36Sopenharmony_ci}
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_cimodule_param(random_seed, uint, 0400);
222562306a36Sopenharmony_cimodule_param(max_iterations, uint, 0400);
222662306a36Sopenharmony_cimodule_param(max_prime, uint, 0400);
222762306a36Sopenharmony_ci
222862306a36Sopenharmony_cistatic struct kunit_case drm_mm_tests[] = {
222962306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_init),
223062306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_debug),
223162306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_reserve),
223262306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_insert),
223362306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_replace),
223462306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_insert_range),
223562306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_frag),
223662306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_align),
223762306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_align32),
223862306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_align64),
223962306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_evict),
224062306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_evict_range),
224162306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_topdown),
224262306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_bottomup),
224362306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_lowest),
224462306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_highest),
224562306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_color),
224662306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_color_evict),
224762306a36Sopenharmony_ci	KUNIT_CASE(drm_test_mm_color_evict_range),
224862306a36Sopenharmony_ci	{}
224962306a36Sopenharmony_ci};
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_cistatic struct kunit_suite drm_mm_test_suite = {
225262306a36Sopenharmony_ci	.name = "drm_mm",
225362306a36Sopenharmony_ci	.suite_init = drm_mm_suite_init,
225462306a36Sopenharmony_ci	.test_cases = drm_mm_tests,
225562306a36Sopenharmony_ci};
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_cikunit_test_suite(drm_mm_test_suite);
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ciMODULE_AUTHOR("Intel Corporation");
226062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2261