Commit fc8d29e2 authored by Arthur Grillo's avatar Arthur Grillo Committed by Javier Martinez Canillas

drm: selftest: convert drm_mm selftest to KUnit

Considering the current adoption of the KUnit framework, convert the
DRM mm selftest to the KUnit API.
Signed-off-by: default avatarArthur Grillo <arthur.grillo@usp.br>
Tested-by: default avatarDavid Gow <davidgow@google.com>
Acked-by: default avatarDaniel Latypov <dlatypov@google.com>
Reviewed-by: default avatarJavier Martinez Canillas <javierm@redhat.com>
Signed-off-by: default avatarMaíra Canal <maira.canal@usp.br>
Signed-off-by: default avatarJavier Martinez Canillas <javierm@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220708203052.236290-10-maira.canal@usp.br
parent 932da861
...@@ -617,17 +617,6 @@ Contact: Javier Martinez Canillas <javierm@redhat.com> ...@@ -617,17 +617,6 @@ Contact: Javier Martinez Canillas <javierm@redhat.com>
Level: Intermediate Level: Intermediate
Convert Kernel Selftests (kselftest) to KUnit tests when appropriate
--------------------------------------------------------------------
Many of the `Kselftest <https://www.kernel.org/doc/html/latest/dev-tools/kselftest.html>`_
tests in DRM could be converted to Kunit tests instead, since that framework
is more suitable for unit testing.
Contact: Javier Martinez Canillas <javierm@redhat.com>
Level: Starter
Enable trinity for DRM Enable trinity for DRM
---------------------- ----------------------
......
...@@ -50,26 +50,6 @@ config DRM_DEBUG_MM ...@@ -50,26 +50,6 @@ config DRM_DEBUG_MM
If in doubt, say "N". If in doubt, say "N".
config DRM_DEBUG_SELFTEST
tristate "kselftests for DRM"
depends on DRM
depends on DEBUG_KERNEL
select PRIME_NUMBERS
select DRM_DISPLAY_DP_HELPER
select DRM_DISPLAY_HELPER
select DRM_LIB_RANDOM
select DRM_KMS_HELPER
select DRM_BUDDY
select DRM_EXPORT_FOR_TESTS if m
default n
help
This option provides kernel modules that can be used to run
various selftests on parts of the DRM api. This option is not
useful for distributions or general kernels, but only for kernel
developers working on DRM and associated drivers.
If in doubt, say "N".
config DRM_KUNIT_TEST config DRM_KUNIT_TEST
tristate "KUnit tests for DRM" if !KUNIT_ALL_TESTS tristate "KUnit tests for DRM" if !KUNIT_ALL_TESTS
depends on DRM && KUNIT depends on DRM && KUNIT
......
...@@ -75,7 +75,6 @@ obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o ...@@ -75,7 +75,6 @@ obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
# Drivers and the rest # Drivers and the rest
# #
obj-$(CONFIG_DRM_DEBUG_SELFTEST) += selftests/
obj-$(CONFIG_DRM_KUNIT_TEST) += tests/ obj-$(CONFIG_DRM_KUNIT_TEST) += tests/
obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o obj-$(CONFIG_DRM_MIPI_DBI) += drm_mipi_dbi.o
......
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_DRM_DEBUG_SELFTEST) += test-drm_mm.o
/* SPDX-License-Identifier: GPL-2.0 */
/* List each unit test as selftest(name, function)
*
* The name is used as both an enum and expanded as igt__name to create
* a module parameter. It must be unique and legal for a C identifier.
*
* Tests are executed in order by igt/drm_mm
*/
selftest(sanitycheck, igt_sanitycheck) /* keep first (selfcheck for igt) */
selftest(init, igt_init)
selftest(debug, igt_debug)
selftest(reserve, igt_reserve)
selftest(insert, igt_insert)
selftest(replace, igt_replace)
selftest(insert_range, igt_insert_range)
selftest(align, igt_align)
selftest(frag, igt_frag)
selftest(align32, igt_align32)
selftest(align64, igt_align64)
selftest(evict, igt_evict)
selftest(evict_range, igt_evict_range)
selftest(bottomup, igt_bottomup)
selftest(lowest, igt_lowest)
selftest(topdown, igt_topdown)
selftest(highest, igt_highest)
selftest(color, igt_color)
selftest(color_evict, igt_color_evict)
selftest(color_evict_range, igt_color_evict_range)
/*
* Copyright © 2016 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <linux/compiler.h>
#define selftest(name, func) __idx_##name,
enum {
#include TESTS
};
#undef selftest
#define selftest(n, f) [__idx_##n] = { .name = #n, .func = f },
static struct drm_selftest {
bool enabled;
const char *name;
int (*func)(void *);
} selftests[] = {
#include TESTS
};
#undef selftest
/* Embed the line number into the parameter name so that we can order tests */
#define param(n) __PASTE(igt__, __PASTE(__PASTE(__LINE__, __), n))
#define selftest_0(n, func, id) \
module_param_named(id, selftests[__idx_##n].enabled, bool, 0400);
#define selftest(n, func) selftest_0(n, func, param(n))
#include TESTS
#undef selftest
static void set_default_test_all(struct drm_selftest *st, unsigned long count)
{
unsigned long i;
for (i = 0; i < count; i++)
if (st[i].enabled)
return;
for (i = 0; i < count; i++)
st[i].enabled = true;
}
static int run_selftests(struct drm_selftest *st,
unsigned long count,
void *data)
{
int err = 0;
set_default_test_all(st, count);
/* Tests are listed in natural order in drm_*_selftests.h */
for (; count--; st++) {
if (!st->enabled)
continue;
pr_debug("drm: Running %s\n", st->name);
err = st->func(data);
if (err)
break;
}
if (WARN(err > 0 || err == -ENOTTY,
"%s returned %d, conflicting with selftest's magic values!\n",
st->name, err))
err = -1;
rcu_barrier();
return err;
}
static int __maybe_unused
__drm_subtests(const char *caller,
const struct drm_subtest *st,
int count,
void *data)
{
int err;
for (; count--; st++) {
pr_debug("Running %s/%s\n", caller, st->name);
err = st->func(data);
if (err) {
pr_err("%s: %s failed with error %d\n",
caller, st->name, err);
return err;
}
}
return 0;
}
/*
* Copyright © 2016 Intel Corporation
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef __DRM_SELFTEST_H__
#define __DRM_SELFTEST_H__
struct drm_subtest {
int (*func)(void *data);
const char *name;
};
static int __drm_subtests(const char *caller,
const struct drm_subtest *st,
int count,
void *data);
#define drm_subtests(T, data) \
__drm_subtests(__func__, T, ARRAY_SIZE(T), data)
#define SUBTEST(x) { x, #x }
#endif /* __DRM_SELFTEST_H__ */
...@@ -2,4 +2,4 @@ ...@@ -2,4 +2,4 @@
obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o drm_damage_helper_test.o \ obj-$(CONFIG_DRM_KUNIT_TEST) += drm_format_helper_test.o drm_damage_helper_test.o \
drm_cmdline_parser_test.o drm_rect_test.o drm_format_test.o drm_plane_helper_test.o \ drm_cmdline_parser_test.o drm_rect_test.o drm_format_test.o drm_plane_helper_test.o \
drm_dp_mst_helper_test.o drm_framebuffer_test.o drm_buddy_test.o drm_dp_mst_helper_test.o drm_framebuffer_test.o drm_buddy_test.o drm_mm_test.o
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
/* /*
* Test cases for the drm_mm range manager * Test cases for the drm_mm range manager
*
* Copyright (c) 2022 Arthur Grillo <arthur.grillo@usp.br>
*/ */
#define pr_fmt(fmt) "drm_mm: " fmt #include <kunit/test.h>
#include <linux/module.h>
#include <linux/prime_numbers.h> #include <linux/prime_numbers.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/random.h> #include <linux/random.h>
...@@ -16,9 +17,6 @@ ...@@ -16,9 +17,6 @@
#include "../lib/drm_random.h" #include "../lib/drm_random.h"
#define TESTS "drm_mm_selftests.h"
#include "drm_selftest.h"
static unsigned int random_seed; static unsigned int random_seed;
static unsigned int max_iterations = 8192; static unsigned int max_iterations = 8192;
static unsigned int max_prime = 128; static unsigned int max_prime = 128;
...@@ -45,13 +43,7 @@ static const struct insert_mode { ...@@ -45,13 +43,7 @@ static const struct insert_mode {
{} {}
}; };
static int igt_sanitycheck(void *ignored) static bool assert_no_holes(struct kunit *test, const struct drm_mm *mm)
{
pr_info("%s - ok!\n", __func__);
return 0;
}
static bool assert_no_holes(const struct drm_mm *mm)
{ {
struct drm_mm_node *hole; struct drm_mm_node *hole;
u64 hole_start, __always_unused hole_end; u64 hole_start, __always_unused hole_end;
...@@ -61,13 +53,14 @@ static bool assert_no_holes(const struct drm_mm *mm) ...@@ -61,13 +53,14 @@ static bool assert_no_holes(const struct drm_mm *mm)
drm_mm_for_each_hole(hole, mm, hole_start, hole_end) drm_mm_for_each_hole(hole, mm, hole_start, hole_end)
count++; count++;
if (count) { if (count) {
pr_err("Expected to find no holes (after reserve), found %lu instead\n", count); KUNIT_FAIL(test,
"Expected to find no holes (after reserve), found %lu instead\n", count);
return false; return false;
} }
drm_mm_for_each_node(hole, mm) { drm_mm_for_each_node(hole, mm) {
if (drm_mm_hole_follows(hole)) { if (drm_mm_hole_follows(hole)) {
pr_err("Hole follows node, expected none!\n"); KUNIT_FAIL(test, "Hole follows node, expected none!\n");
return false; return false;
} }
} }
...@@ -75,7 +68,7 @@ static bool assert_no_holes(const struct drm_mm *mm) ...@@ -75,7 +68,7 @@ static bool assert_no_holes(const struct drm_mm *mm)
return true; return true;
} }
static bool assert_one_hole(const struct drm_mm *mm, u64 start, u64 end) static bool assert_one_hole(struct kunit *test, const struct drm_mm *mm, u64 start, u64 end)
{ {
struct drm_mm_node *hole; struct drm_mm_node *hole;
u64 hole_start, hole_end; u64 hole_start, hole_end;
...@@ -89,62 +82,62 @@ static bool assert_one_hole(const struct drm_mm *mm, u64 start, u64 end) ...@@ -89,62 +82,62 @@ static bool assert_one_hole(const struct drm_mm *mm, u64 start, u64 end)
drm_mm_for_each_hole(hole, mm, hole_start, hole_end) { drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
if (start != hole_start || end != hole_end) { if (start != hole_start || end != hole_end) {
if (ok) if (ok)
pr_err("empty mm has incorrect hole, found (%llx, %llx), expect (%llx, %llx)\n", KUNIT_FAIL(test,
hole_start, hole_end, "empty mm has incorrect hole, found (%llx, %llx), expect (%llx, %llx)\n",
start, end); hole_start, hole_end, start, end);
ok = false; ok = false;
} }
count++; count++;
} }
if (count != 1) { if (count != 1) {
pr_err("Expected to find one hole, found %lu instead\n", count); KUNIT_FAIL(test, "Expected to find one hole, found %lu instead\n", count);
ok = false; ok = false;
} }
return ok; return ok;
} }
static bool assert_continuous(const struct drm_mm *mm, u64 size) static bool assert_continuous(struct kunit *test, const struct drm_mm *mm, u64 size)
{ {
struct drm_mm_node *node, *check, *found; struct drm_mm_node *node, *check, *found;
unsigned long n; unsigned long n;
u64 addr; u64 addr;
if (!assert_no_holes(mm)) if (!assert_no_holes(test, mm))
return false; return false;
n = 0; n = 0;
addr = 0; addr = 0;
drm_mm_for_each_node(node, mm) { drm_mm_for_each_node(node, mm) {
if (node->start != addr) { if (node->start != addr) {
pr_err("node[%ld] list out of order, expected %llx found %llx\n", KUNIT_FAIL(test, "node[%ld] list out of order, expected %llx found %llx\n",
n, addr, node->start); n, addr, node->start);
return false; return false;
} }
if (node->size != size) { if (node->size != size) {
pr_err("node[%ld].size incorrect, expected %llx, found %llx\n", KUNIT_FAIL(test, "node[%ld].size incorrect, expected %llx, found %llx\n",
n, size, node->size); n, size, node->size);
return false; return false;
} }
if (drm_mm_hole_follows(node)) { if (drm_mm_hole_follows(node)) {
pr_err("node[%ld] is followed by a hole!\n", n); KUNIT_FAIL(test, "node[%ld] is followed by a hole!\n", n);
return false; return false;
} }
found = NULL; found = NULL;
drm_mm_for_each_node_in_range(check, mm, addr, addr + size) { drm_mm_for_each_node_in_range(check, mm, addr, addr + size) {
if (node != check) { if (node != check) {
pr_err("lookup return wrong node, expected start %llx, found %llx\n", KUNIT_FAIL(test,
node->start, check->start); "lookup return wrong node, expected start %llx, found %llx\n",
node->start, check->start);
return false; return false;
} }
found = check; found = check;
} }
if (!found) { if (!found) {
pr_err("lookup failed for node %llx + %llx\n", KUNIT_FAIL(test, "lookup failed for node %llx + %llx\n", addr, size);
addr, size);
return false; return false;
} }
...@@ -166,107 +159,96 @@ static u64 misalignment(struct drm_mm_node *node, u64 alignment) ...@@ -166,107 +159,96 @@ static u64 misalignment(struct drm_mm_node *node, u64 alignment)
return rem; return rem;
} }
static bool assert_node(struct drm_mm_node *node, struct drm_mm *mm, static bool assert_node(struct kunit *test, struct drm_mm_node *node, struct drm_mm *mm,
u64 size, u64 alignment, unsigned long color) u64 size, u64 alignment, unsigned long color)
{ {
bool ok = true; bool ok = true;
if (!drm_mm_node_allocated(node) || node->mm != mm) { if (!drm_mm_node_allocated(node) || node->mm != mm) {
pr_err("node not allocated\n"); KUNIT_FAIL(test, "node not allocated\n");
ok = false; ok = false;
} }
if (node->size != size) { if (node->size != size) {
pr_err("node has wrong size, found %llu, expected %llu\n", KUNIT_FAIL(test, "node has wrong size, found %llu, expected %llu\n",
node->size, size); node->size, size);
ok = false; ok = false;
} }
if (misalignment(node, alignment)) { if (misalignment(node, alignment)) {
pr_err("node is misaligned, start %llx rem %llu, expected alignment %llu\n", KUNIT_FAIL(test,
node->start, misalignment(node, alignment), alignment); "node is misaligned, start %llx rem %llu, expected alignment %llu\n",
node->start, misalignment(node, alignment), alignment);
ok = false; ok = false;
} }
if (node->color != color) { if (node->color != color) {
pr_err("node has wrong color, found %lu, expected %lu\n", KUNIT_FAIL(test, "node has wrong color, found %lu, expected %lu\n",
node->color, color); node->color, color);
ok = false; ok = false;
} }
return ok; return ok;
} }
#define show_mm(mm) do { \ static void igt_mm_init(struct kunit *test)
struct drm_printer __p = drm_debug_printer(__func__); \
drm_mm_print((mm), &__p); } while (0)
static int igt_init(void *ignored)
{ {
const unsigned int size = 4096; const unsigned int size = 4096;
struct drm_mm mm; struct drm_mm mm;
struct drm_mm_node tmp; struct drm_mm_node tmp;
int ret = -EINVAL;
/* Start with some simple checks on initialising the struct drm_mm */ /* Start with some simple checks on initialising the struct drm_mm */
memset(&mm, 0, sizeof(mm)); memset(&mm, 0, sizeof(mm));
if (drm_mm_initialized(&mm)) { KUNIT_ASSERT_FALSE_MSG(test, drm_mm_initialized(&mm),
pr_err("zeroed mm claims to be initialized\n"); "zeroed mm claims to be initialized\n");
return ret;
}
memset(&mm, 0xff, sizeof(mm)); memset(&mm, 0xff, sizeof(mm));
drm_mm_init(&mm, 0, size); drm_mm_init(&mm, 0, size);
if (!drm_mm_initialized(&mm)) { if (!drm_mm_initialized(&mm)) {
pr_err("mm claims not to be initialized\n"); KUNIT_FAIL(test, "mm claims not to be initialized\n");
goto out; goto out;
} }
if (!drm_mm_clean(&mm)) { if (!drm_mm_clean(&mm)) {
pr_err("mm not empty on creation\n"); KUNIT_FAIL(test, "mm not empty on creation\n");
goto out; goto out;
} }
/* After creation, it should all be one massive hole */ /* After creation, it should all be one massive hole */
if (!assert_one_hole(&mm, 0, size)) { if (!assert_one_hole(test, &mm, 0, size)) {
ret = -EINVAL; KUNIT_FAIL(test, "");
goto out; goto out;
} }
memset(&tmp, 0, sizeof(tmp)); memset(&tmp, 0, sizeof(tmp));
tmp.start = 0; tmp.start = 0;
tmp.size = size; tmp.size = size;
ret = drm_mm_reserve_node(&mm, &tmp); if (drm_mm_reserve_node(&mm, &tmp)) {
if (ret) { KUNIT_FAIL(test, "failed to reserve whole drm_mm\n");
pr_err("failed to reserve whole drm_mm\n");
goto out; goto out;
} }
/* After filling the range entirely, there should be no holes */ /* After filling the range entirely, there should be no holes */
if (!assert_no_holes(&mm)) { if (!assert_no_holes(test, &mm)) {
ret = -EINVAL; KUNIT_FAIL(test, "");
goto out; goto out;
} }
/* And then after emptying it again, the massive hole should be back */ /* And then after emptying it again, the massive hole should be back */
drm_mm_remove_node(&tmp); drm_mm_remove_node(&tmp);
if (!assert_one_hole(&mm, 0, size)) { if (!assert_one_hole(test, &mm, 0, size)) {
ret = -EINVAL; KUNIT_FAIL(test, "");
goto out; goto out;
} }
out: out:
if (ret)
show_mm(&mm);
drm_mm_takedown(&mm); drm_mm_takedown(&mm);
return ret;
} }
static int igt_debug(void *ignored) static void igt_mm_debug(struct kunit *test)
{ {
struct drm_mm mm; struct drm_mm mm;
struct drm_mm_node nodes[2]; struct drm_mm_node nodes[2];
int ret;
/* Create a small drm_mm with a couple of nodes and a few holes, and /* Create a small drm_mm with a couple of nodes and a few holes, and
* check that the debug iterator doesn't explode over a trivial drm_mm. * check that the debug iterator doesn't explode over a trivial drm_mm.
...@@ -277,24 +259,15 @@ static int igt_debug(void *ignored) ...@@ -277,24 +259,15 @@ static int igt_debug(void *ignored)
memset(nodes, 0, sizeof(nodes)); memset(nodes, 0, sizeof(nodes));
nodes[0].start = 512; nodes[0].start = 512;
nodes[0].size = 1024; nodes[0].size = 1024;
ret = drm_mm_reserve_node(&mm, &nodes[0]); KUNIT_ASSERT_FALSE_MSG(test, drm_mm_reserve_node(&mm, &nodes[0]),
if (ret) { "failed to reserve node[0] {start=%lld, size=%lld)\n",
pr_err("failed to reserve node[0] {start=%lld, size=%lld)\n", nodes[0].start, nodes[0].size);
nodes[0].start, nodes[0].size);
return ret;
}
nodes[1].size = 1024; nodes[1].size = 1024;
nodes[1].start = 4096 - 512 - nodes[1].size; nodes[1].start = 4096 - 512 - nodes[1].size;
ret = drm_mm_reserve_node(&mm, &nodes[1]); KUNIT_ASSERT_FALSE_MSG(test, drm_mm_reserve_node(&mm, &nodes[1]),
if (ret) { "failed to reserve node[0] {start=%lld, size=%lld)\n",
pr_err("failed to reserve node[1] {start=%lld, size=%lld)\n", nodes[0].start, nodes[0].size);
nodes[1].start, nodes[1].size);
return ret;
}
show_mm(&mm);
return 0;
} }
static struct drm_mm_node *set_node(struct drm_mm_node *node, static struct drm_mm_node *set_node(struct drm_mm_node *node,
...@@ -305,7 +278,7 @@ static struct drm_mm_node *set_node(struct drm_mm_node *node, ...@@ -305,7 +278,7 @@ static struct drm_mm_node *set_node(struct drm_mm_node *node,
return node; return node;
} }
static bool expect_reserve_fail(struct drm_mm *mm, struct drm_mm_node *node) static bool expect_reserve_fail(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *node)
{ {
int err; int err;
...@@ -314,17 +287,18 @@ static bool expect_reserve_fail(struct drm_mm *mm, struct drm_mm_node *node) ...@@ -314,17 +287,18 @@ static bool expect_reserve_fail(struct drm_mm *mm, struct drm_mm_node *node)
return true; return true;
if (!err) { if (!err) {
pr_err("impossible reserve succeeded, node %llu + %llu\n", KUNIT_FAIL(test, "impossible reserve succeeded, node %llu + %llu\n",
node->start, node->size); node->start, node->size);
drm_mm_remove_node(node); drm_mm_remove_node(node);
} else { } else {
pr_err("impossible reserve failed with wrong error %d [expected %d], node %llu + %llu\n", KUNIT_FAIL(test,
"impossible reserve failed with wrong error %d [expected %d], node %llu + %llu\n",
err, -ENOSPC, node->start, node->size); err, -ENOSPC, node->start, node->size);
} }
return false; return false;
} }
static bool check_reserve_boundaries(struct drm_mm *mm, static bool check_reserve_boundaries(struct kunit *test, struct drm_mm *mm,
unsigned int count, unsigned int count,
u64 size) u64 size)
{ {
...@@ -339,29 +313,27 @@ static bool check_reserve_boundaries(struct drm_mm *mm, ...@@ -339,29 +313,27 @@ static bool check_reserve_boundaries(struct drm_mm *mm,
B(size * count, 0), B(size * count, 0),
B(-size, size), B(-size, size),
B(-size, -size), B(-size, -size),
B(-size, 2*size), B(-size, 2 * size),
B(0, -size), B(0, -size),
B(size, -size), B(size, -size),
B(count*size, size), B(count * size, size),
B(count*size, -size), B(count * size, -size),
B(count*size, count*size), B(count * size, count * size),
B(count*size, -count*size), B(count * size, -count * size),
B(count*size, -(count+1)*size), B(count * size, -(count + 1) * size),
B((count+1)*size, size), B((count + 1) * size, size),
B((count+1)*size, -size), B((count + 1) * size, -size),
B((count+1)*size, -2*size), B((count + 1) * size, -2 * size),
#undef B #undef B
}; };
struct drm_mm_node tmp = {}; struct drm_mm_node tmp = {};
int n; int n;
for (n = 0; n < ARRAY_SIZE(boundaries); n++) { for (n = 0; n < ARRAY_SIZE(boundaries); n++) {
if (!expect_reserve_fail(mm, if (!expect_reserve_fail(test, mm, set_node(&tmp, boundaries[n].start,
set_node(&tmp, boundaries[n].size))) {
boundaries[n].start, KUNIT_FAIL(test, "boundary[%d:%s] failed, count=%u, size=%lld\n",
boundaries[n].size))) { n, boundaries[n].name, count, size);
pr_err("boundary[%d:%s] failed, count=%u, size=%lld\n",
n, boundaries[n].name, count, size);
return false; return false;
} }
} }
...@@ -369,7 +341,7 @@ static bool check_reserve_boundaries(struct drm_mm *mm, ...@@ -369,7 +341,7 @@ static bool check_reserve_boundaries(struct drm_mm *mm,
return true; return true;
} }
static int __igt_reserve(unsigned int count, u64 size) static int __igt_reserve(struct kunit *test, unsigned int count, u64 size)
{ {
DRM_RND_STATE(prng, random_seed); DRM_RND_STATE(prng, random_seed);
struct drm_mm mm; struct drm_mm mm;
...@@ -377,7 +349,7 @@ static int __igt_reserve(unsigned int count, u64 size) ...@@ -377,7 +349,7 @@ static int __igt_reserve(unsigned int count, u64 size)
unsigned int *order, n, m, o = 0; unsigned int *order, n, m, o = 0;
int ret, err; int ret, err;
/* For exercising drm_mm_reserve_node(), we want to check that /* For exercising drm_mm_reserve_node(struct kunit *test, ), we want to check that
* reservations outside of the drm_mm range are rejected, and to * reservations outside of the drm_mm range are rejected, and to
* overlapping and otherwise already occupied ranges. Afterwards, * overlapping and otherwise already occupied ranges. Afterwards,
* the tree and nodes should be intact. * the tree and nodes should be intact.
...@@ -392,13 +364,12 @@ static int __igt_reserve(unsigned int count, u64 size) ...@@ -392,13 +364,12 @@ static int __igt_reserve(unsigned int count, u64 size)
goto err; goto err;
nodes = vzalloc(array_size(count, sizeof(*nodes))); nodes = vzalloc(array_size(count, sizeof(*nodes)));
if (!nodes) KUNIT_ASSERT_TRUE(test, nodes);
goto err_order;
ret = -EINVAL; ret = -EINVAL;
drm_mm_init(&mm, 0, count * size); drm_mm_init(&mm, 0, count * size);
if (!check_reserve_boundaries(&mm, count, size)) if (!check_reserve_boundaries(test, &mm, count, size))
goto out; goto out;
for (n = 0; n < count; n++) { for (n = 0; n < count; n++) {
...@@ -407,57 +378,53 @@ static int __igt_reserve(unsigned int count, u64 size) ...@@ -407,57 +378,53 @@ static int __igt_reserve(unsigned int count, u64 size)
err = drm_mm_reserve_node(&mm, &nodes[n]); err = drm_mm_reserve_node(&mm, &nodes[n]);
if (err) { if (err) {
pr_err("reserve failed, step %d, start %llu\n", KUNIT_FAIL(test, "reserve failed, step %d, start %llu\n",
n, nodes[n].start); n, nodes[n].start);
ret = err; ret = err;
goto out; goto out;
} }
if (!drm_mm_node_allocated(&nodes[n])) { if (!drm_mm_node_allocated(&nodes[n])) {
pr_err("reserved node not allocated! step %d, start %llu\n", KUNIT_FAIL(test, "reserved node not allocated! step %d, start %llu\n",
n, nodes[n].start); n, nodes[n].start);
goto out; goto out;
} }
if (!expect_reserve_fail(&mm, &nodes[n])) if (!expect_reserve_fail(test, &mm, &nodes[n]))
goto out; goto out;
} }
/* After random insertion the nodes should be in order */ /* After random insertion the nodes should be in order */
if (!assert_continuous(&mm, size)) if (!assert_continuous(test, &mm, size))
goto out; goto out;
/* Repeated use should then fail */ /* Repeated use should then fail */
drm_random_reorder(order, count, &prng); drm_random_reorder(order, count, &prng);
for (n = 0; n < count; n++) { for (n = 0; n < count; n++) {
if (!expect_reserve_fail(&mm, if (!expect_reserve_fail(test, &mm, set_node(&tmp, order[n] * size, 1)))
set_node(&tmp, order[n] * size, 1)))
goto out; goto out;
/* Remove and reinsert should work */ /* Remove and reinsert should work */
drm_mm_remove_node(&nodes[order[n]]); drm_mm_remove_node(&nodes[order[n]]);
err = drm_mm_reserve_node(&mm, &nodes[order[n]]); err = drm_mm_reserve_node(&mm, &nodes[order[n]]);
if (err) { if (err) {
pr_err("reserve failed, step %d, start %llu\n", KUNIT_FAIL(test, "reserve failed, step %d, start %llu\n",
n, nodes[n].start); n, nodes[n].start);
ret = err; ret = err;
goto out; goto out;
} }
} }
if (!assert_continuous(&mm, size)) if (!assert_continuous(test, &mm, size))
goto out; goto out;
/* Overlapping use should then fail */ /* Overlapping use should then fail */
for (n = 0; n < count; n++) { for (n = 0; n < count; n++) {
if (!expect_reserve_fail(&mm, set_node(&tmp, 0, size*count))) if (!expect_reserve_fail(test, &mm, set_node(&tmp, 0, size * count)))
goto out; goto out;
} }
for (n = 0; n < count; n++) { for (n = 0; n < count; n++) {
if (!expect_reserve_fail(&mm, if (!expect_reserve_fail(test, &mm, set_node(&tmp, size * n, size * (count - n))))
set_node(&tmp,
size * n,
size * (count - n))))
goto out; goto out;
} }
...@@ -472,8 +439,8 @@ static int __igt_reserve(unsigned int count, u64 size) ...@@ -472,8 +439,8 @@ static int __igt_reserve(unsigned int count, u64 size)
node = &nodes[order[(o + m) % count]]; node = &nodes[order[(o + m) % count]];
err = drm_mm_reserve_node(&mm, node); err = drm_mm_reserve_node(&mm, node);
if (err) { if (err) {
pr_err("reserve failed, step %d/%d, start %llu\n", KUNIT_FAIL(test, "reserve failed, step %d/%d, start %llu\n",
m, n, node->start); m, n, node->start);
ret = err; ret = err;
goto out; goto out;
} }
...@@ -481,7 +448,7 @@ static int __igt_reserve(unsigned int count, u64 size) ...@@ -481,7 +448,7 @@ static int __igt_reserve(unsigned int count, u64 size)
o += n; o += n;
if (!assert_continuous(&mm, size)) if (!assert_continuous(test, &mm, size))
goto out; goto out;
} }
...@@ -491,41 +458,30 @@ static int __igt_reserve(unsigned int count, u64 size) ...@@ -491,41 +458,30 @@ static int __igt_reserve(unsigned int count, u64 size)
drm_mm_remove_node(node); drm_mm_remove_node(node);
drm_mm_takedown(&mm); drm_mm_takedown(&mm);
vfree(nodes); vfree(nodes);
err_order:
kfree(order); kfree(order);
err: err:
return ret; return ret;
} }
static int igt_reserve(void *ignored) static void igt_mm_reserve(struct kunit *test)
{ {
const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); const unsigned int count = min_t(unsigned int, BIT(10), max_iterations);
int n, ret; int n;
for_each_prime_number_from(n, 1, 54) { for_each_prime_number_from(n, 1, 54) {
u64 size = BIT_ULL(n); u64 size = BIT_ULL(n);
ret = __igt_reserve(count, size - 1); KUNIT_ASSERT_FALSE(test, __igt_reserve(test, count, size - 1));
if (ret) KUNIT_ASSERT_FALSE(test, __igt_reserve(test, count, size));
return ret; KUNIT_ASSERT_FALSE(test, __igt_reserve(test, count, size + 1));
ret = __igt_reserve(count, size);
if (ret)
return ret;
ret = __igt_reserve(count, size + 1);
if (ret)
return ret;
cond_resched(); cond_resched();
} }
return 0;
} }
static bool expect_insert(struct drm_mm *mm, struct drm_mm_node *node, static bool expect_insert(struct kunit *test, struct drm_mm *mm,
u64 size, u64 alignment, unsigned long color, struct drm_mm_node *node, u64 size, u64 alignment, unsigned long color,
const struct insert_mode *mode) const struct insert_mode *mode)
{ {
int err; int err;
...@@ -533,12 +489,13 @@ static bool expect_insert(struct drm_mm *mm, struct drm_mm_node *node, ...@@ -533,12 +489,13 @@ static bool expect_insert(struct drm_mm *mm, struct drm_mm_node *node,
size, alignment, color, size, alignment, color,
mode->mode); mode->mode);
if (err) { if (err) {
pr_err("insert (size=%llu, alignment=%llu, color=%lu, mode=%s) failed with err=%d\n", KUNIT_FAIL(test,
size, alignment, color, mode->name, err); "insert (size=%llu, alignment=%llu, color=%lu, mode=%s) failed with err=%d\n",
size, alignment, color, mode->name, err);
return false; return false;
} }
if (!assert_node(node, mm, size, alignment, color)) { if (!assert_node(test, node, mm, size, alignment, color)) {
drm_mm_remove_node(node); drm_mm_remove_node(node);
return false; return false;
} }
...@@ -546,7 +503,7 @@ static bool expect_insert(struct drm_mm *mm, struct drm_mm_node *node, ...@@ -546,7 +503,7 @@ static bool expect_insert(struct drm_mm *mm, struct drm_mm_node *node,
return true; return true;
} }
static bool expect_insert_fail(struct drm_mm *mm, u64 size) static bool expect_insert_fail(struct kunit *test, struct drm_mm *mm, u64 size)
{ {
struct drm_mm_node tmp = {}; struct drm_mm_node tmp = {};
int err; int err;
...@@ -556,17 +513,18 @@ static bool expect_insert_fail(struct drm_mm *mm, u64 size) ...@@ -556,17 +513,18 @@ static bool expect_insert_fail(struct drm_mm *mm, u64 size)
return true; return true;
if (!err) { if (!err) {
pr_err("impossible insert succeeded, node %llu + %llu\n", KUNIT_FAIL(test, "impossible insert succeeded, node %llu + %llu\n",
tmp.start, tmp.size); tmp.start, tmp.size);
drm_mm_remove_node(&tmp); drm_mm_remove_node(&tmp);
} else { } else {
pr_err("impossible insert failed with wrong error %d [expected %d], size %llu\n", KUNIT_FAIL(test,
err, -ENOSPC, size); "impossible insert failed with wrong error %d [expected %d], size %llu\n",
err, -ENOSPC, size);
} }
return false; return false;
} }
static int __igt_insert(unsigned int count, u64 size, bool replace) static int __igt_insert(struct kunit *test, unsigned int count, u64 size, bool replace)
{ {
DRM_RND_STATE(prng, random_seed); DRM_RND_STATE(prng, random_seed);
const struct insert_mode *mode; const struct insert_mode *mode;
...@@ -582,8 +540,7 @@ static int __igt_insert(unsigned int count, u64 size, bool replace) ...@@ -582,8 +540,7 @@ static int __igt_insert(unsigned int count, u64 size, bool replace)
ret = -ENOMEM; ret = -ENOMEM;
nodes = vmalloc(array_size(count, sizeof(*nodes))); nodes = vmalloc(array_size(count, sizeof(*nodes)));
if (!nodes) KUNIT_ASSERT_TRUE(test, nodes);
goto err;
order = drm_random_order(count, &prng); order = drm_random_order(count, &prng);
if (!order) if (!order)
...@@ -598,41 +555,43 @@ static int __igt_insert(unsigned int count, u64 size, bool replace) ...@@ -598,41 +555,43 @@ static int __igt_insert(unsigned int count, u64 size, bool replace)
node = replace ? &tmp : &nodes[n]; node = replace ? &tmp : &nodes[n];
memset(node, 0, sizeof(*node)); memset(node, 0, sizeof(*node));
if (!expect_insert(&mm, node, size, 0, n, mode)) { if (!expect_insert(test, &mm, node, size, 0, n, mode)) {
pr_err("%s insert failed, size %llu step %d\n", KUNIT_FAIL(test, "%s insert failed, size %llu step %d\n",
mode->name, size, n); mode->name, size, n);
goto out; goto out;
} }
if (replace) { if (replace) {
drm_mm_replace_node(&tmp, &nodes[n]); drm_mm_replace_node(&tmp, &nodes[n]);
if (drm_mm_node_allocated(&tmp)) { if (drm_mm_node_allocated(&tmp)) {
pr_err("replaced old-node still allocated! step %d\n", KUNIT_FAIL(test,
n); "replaced old-node still allocated! step %d\n",
n);
goto out; goto out;
} }
if (!assert_node(&nodes[n], &mm, size, 0, n)) { if (!assert_node(test, &nodes[n], &mm, size, 0, n)) {
pr_err("replaced node did not inherit parameters, size %llu step %d\n", KUNIT_FAIL(test,
size, n); "replaced node did not inherit parameters, size %llu step %d\n",
size, n);
goto out; goto out;
} }
if (tmp.start != nodes[n].start) { if (tmp.start != nodes[n].start) {
pr_err("replaced node mismatch location expected [%llx + %llx], found [%llx + %llx]\n", KUNIT_FAIL(test,
tmp.start, size, "replaced node mismatch location expected [%llx + %llx], found [%llx + %llx]\n",
nodes[n].start, nodes[n].size); tmp.start, size, nodes[n].start, nodes[n].size);
goto out; goto out;
} }
} }
} }
/* After random insertion the nodes should be in order */ /* After random insertion the nodes should be in order */
if (!assert_continuous(&mm, size)) if (!assert_continuous(test, &mm, size))
goto out; goto out;
/* Repeated use should then fail */ /* Repeated use should then fail */
if (!expect_insert_fail(&mm, size)) if (!expect_insert_fail(test, &mm, size))
goto out; goto out;
/* Remove one and reinsert, as the only hole it should refill itself */ /* Remove one and reinsert, as the only hole it should refill itself */
...@@ -640,19 +599,20 @@ static int __igt_insert(unsigned int count, u64 size, bool replace) ...@@ -640,19 +599,20 @@ static int __igt_insert(unsigned int count, u64 size, bool replace)
u64 addr = nodes[n].start; u64 addr = nodes[n].start;
drm_mm_remove_node(&nodes[n]); drm_mm_remove_node(&nodes[n]);
if (!expect_insert(&mm, &nodes[n], size, 0, n, mode)) { if (!expect_insert(test, &mm, &nodes[n], size, 0, n, mode)) {
pr_err("%s reinsert failed, size %llu step %d\n", KUNIT_FAIL(test, "%s reinsert failed, size %llu step %d\n",
mode->name, size, n); mode->name, size, n);
goto out; goto out;
} }
if (nodes[n].start != addr) { if (nodes[n].start != addr) {
pr_err("%s reinsert node moved, step %d, expected %llx, found %llx\n", KUNIT_FAIL(test,
mode->name, n, addr, nodes[n].start); "%s reinsert node moved, step %d, expected %llx, found %llx\n",
mode->name, n, addr, nodes[n].start);
goto out; goto out;
} }
if (!assert_continuous(&mm, size)) if (!assert_continuous(test, &mm, size))
goto out; goto out;
} }
...@@ -665,19 +625,20 @@ static int __igt_insert(unsigned int count, u64 size, bool replace) ...@@ -665,19 +625,20 @@ static int __igt_insert(unsigned int count, u64 size, bool replace)
for (m = 0; m < n; m++) { for (m = 0; m < n; m++) {
node = &nodes[order[(o + m) % count]]; node = &nodes[order[(o + m) % count]];
if (!expect_insert(&mm, node, size, 0, n, mode)) { if (!expect_insert(test, &mm, node, size, 0, n, mode)) {
pr_err("%s multiple reinsert failed, size %llu step %d\n", KUNIT_FAIL(test,
mode->name, size, n); "%s multiple reinsert failed, size %llu step %d\n",
mode->name, size, n);
goto out; goto out;
} }
} }
o += n; o += n;
if (!assert_continuous(&mm, size)) if (!assert_continuous(test, &mm, size))
goto out; goto out;
if (!expect_insert_fail(&mm, size)) if (!expect_insert_fail(test, &mm, size))
goto out; goto out;
} }
...@@ -696,42 +657,29 @@ static int __igt_insert(unsigned int count, u64 size, bool replace) ...@@ -696,42 +657,29 @@ static int __igt_insert(unsigned int count, u64 size, bool replace)
kfree(order); kfree(order);
err_nodes: err_nodes:
vfree(nodes); vfree(nodes);
err:
return ret; return ret;
} }
static int igt_insert(void *ignored) static void igt_mm_insert(struct kunit *test)
{ {
const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); const unsigned int count = min_t(unsigned int, BIT(10), max_iterations);
unsigned int n; unsigned int n;
int ret;
for_each_prime_number_from(n, 1, 54) { for_each_prime_number_from(n, 1, 54) {
u64 size = BIT_ULL(n); u64 size = BIT_ULL(n);
ret = __igt_insert(count, size - 1, false); KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size - 1, false));
if (ret) KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size, false));
return ret; KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size + 1, false));
ret = __igt_insert(count, size, false);
if (ret)
return ret;
ret = __igt_insert(count, size + 1, false);
if (ret)
return ret;
cond_resched(); cond_resched();
} }
return 0;
} }
static int igt_replace(void *ignored) static void igt_mm_replace(struct kunit *test)
{ {
const unsigned int count = min_t(unsigned int, BIT(10), max_iterations); const unsigned int count = min_t(unsigned int, BIT(10), max_iterations);
unsigned int n; unsigned int n;
int ret;
/* Reuse igt_insert to exercise replacement by inserting a dummy node, /* Reuse igt_insert to exercise replacement by inserting a dummy node,
* then replacing it with the intended node. We want to check that * then replacing it with the intended node. We want to check that
...@@ -742,28 +690,17 @@ static int igt_replace(void *ignored) ...@@ -742,28 +690,17 @@ static int igt_replace(void *ignored)
for_each_prime_number_from(n, 1, 54) { for_each_prime_number_from(n, 1, 54) {
u64 size = BIT_ULL(n); u64 size = BIT_ULL(n);
ret = __igt_insert(count, size - 1, true); KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size - 1, true));
if (ret) KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size, true));
return ret; KUNIT_ASSERT_FALSE(test, __igt_insert(test, count, size + 1, true));
ret = __igt_insert(count, size, true);
if (ret)
return ret;
ret = __igt_insert(count, size + 1, true);
if (ret)
return ret;
cond_resched(); cond_resched();
} }
return 0;
} }
static bool expect_insert_in_range(struct drm_mm *mm, struct drm_mm_node *node, static bool expect_insert_in_range(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *node,
u64 size, u64 alignment, unsigned long color, u64 size, u64 alignment, unsigned long color,
u64 range_start, u64 range_end, u64 range_start, u64 range_end, const struct insert_mode *mode)
const struct insert_mode *mode)
{ {
int err; int err;
...@@ -772,13 +709,14 @@ static bool expect_insert_in_range(struct drm_mm *mm, struct drm_mm_node *node, ...@@ -772,13 +709,14 @@ static bool expect_insert_in_range(struct drm_mm *mm, struct drm_mm_node *node,
range_start, range_end, range_start, range_end,
mode->mode); mode->mode);
if (err) { if (err) {
pr_err("insert (size=%llu, alignment=%llu, color=%lu, mode=%s) nto range [%llx, %llx] failed with err=%d\n", KUNIT_FAIL(test,
size, alignment, color, mode->name, "insert (size=%llu, alignment=%llu, color=%lu, mode=%s) nto range [%llx, %llx] failed with err=%d\n",
range_start, range_end, err); size, alignment, color, mode->name,
range_start, range_end, err);
return false; return false;
} }
if (!assert_node(node, mm, size, alignment, color)) { if (!assert_node(test, node, mm, size, alignment, color)) {
drm_mm_remove_node(node); drm_mm_remove_node(node);
return false; return false;
} }
...@@ -786,67 +724,63 @@ static bool expect_insert_in_range(struct drm_mm *mm, struct drm_mm_node *node, ...@@ -786,67 +724,63 @@ static bool expect_insert_in_range(struct drm_mm *mm, struct drm_mm_node *node,
return true; return true;
} }
static bool expect_insert_in_range_fail(struct drm_mm *mm, static bool expect_insert_in_range_fail(struct kunit *test, struct drm_mm *mm,
u64 size, u64 size, u64 range_start, u64 range_end)
u64 range_start,
u64 range_end)
{ {
struct drm_mm_node tmp = {}; struct drm_mm_node tmp = {};
int err; int err;
err = drm_mm_insert_node_in_range(mm, &tmp, err = drm_mm_insert_node_in_range(mm, &tmp, size, 0, 0, range_start, range_end,
size, 0, 0,
range_start, range_end,
0); 0);
if (likely(err == -ENOSPC)) if (likely(err == -ENOSPC))
return true; return true;
if (!err) { if (!err) {
pr_err("impossible insert succeeded, node %llx + %llu, range [%llx, %llx]\n", KUNIT_FAIL(test,
tmp.start, tmp.size, range_start, range_end); "impossible insert succeeded, node %llx + %llu, range [%llx, %llx]\n",
tmp.start, tmp.size, range_start, range_end);
drm_mm_remove_node(&tmp); drm_mm_remove_node(&tmp);
} else { } else {
pr_err("impossible insert failed with wrong error %d [expected %d], size %llu, range [%llx, %llx]\n", KUNIT_FAIL(test,
err, -ENOSPC, size, range_start, range_end); "impossible insert failed with wrong error %d [expected %d], size %llu, range [%llx, %llx]\n",
err, -ENOSPC, size, range_start, range_end);
} }
return false; return false;
} }
static bool assert_contiguous_in_range(struct drm_mm *mm, static bool assert_contiguous_in_range(struct kunit *test, struct drm_mm *mm,
u64 size, u64 size, u64 start, u64 end)
u64 start,
u64 end)
{ {
struct drm_mm_node *node; struct drm_mm_node *node;
unsigned int n; unsigned int n;
if (!expect_insert_in_range_fail(mm, size, start, end)) if (!expect_insert_in_range_fail(test, mm, size, start, end))
return false; return false;
n = div64_u64(start + size - 1, size); n = div64_u64(start + size - 1, size);
drm_mm_for_each_node(node, mm) { drm_mm_for_each_node(node, mm) {
if (node->start < start || node->start + node->size > end) { if (node->start < start || node->start + node->size > end) {
pr_err("node %d out of range, address [%llx + %llu], range [%llx, %llx]\n", KUNIT_FAIL(test,
n, node->start, node->start + node->size, start, end); "node %d out of range, address [%llx + %llu], range [%llx, %llx]\n",
n, node->start, node->start + node->size, start, end);
return false; return false;
} }
if (node->start != n * size) { if (node->start != n * size) {
pr_err("node %d out of order, expected start %llx, found %llx\n", KUNIT_FAIL(test, "node %d out of order, expected start %llx, found %llx\n",
n, n * size, node->start); n, n * size, node->start);
return false; return false;
} }
if (node->size != size) { if (node->size != size) {
pr_err("node %d has wrong size, expected size %llx, found %llx\n", KUNIT_FAIL(test, "node %d has wrong size, expected size %llx, found %llx\n",
n, size, node->size); n, size, node->size);
return false; return false;
} }
if (drm_mm_hole_follows(node) && if (drm_mm_hole_follows(node) && drm_mm_hole_node_end(node) < end) {
drm_mm_hole_node_end(node) < end) { KUNIT_FAIL(test, "node %d is followed by a hole!\n", n);
pr_err("node %d is followed by a hole!\n", n);
return false; return false;
} }
...@@ -856,8 +790,8 @@ static bool assert_contiguous_in_range(struct drm_mm *mm, ...@@ -856,8 +790,8 @@ static bool assert_contiguous_in_range(struct drm_mm *mm,
if (start > 0) { if (start > 0) {
node = __drm_mm_interval_first(mm, 0, start - 1); node = __drm_mm_interval_first(mm, 0, start - 1);
if (drm_mm_node_allocated(node)) { if (drm_mm_node_allocated(node)) {
pr_err("node before start: node=%llx+%llu, start=%llx\n", KUNIT_FAIL(test, "node before start: node=%llx+%llu, start=%llx\n",
node->start, node->size, start); node->start, node->size, start);
return false; return false;
} }
} }
...@@ -865,8 +799,8 @@ static bool assert_contiguous_in_range(struct drm_mm *mm, ...@@ -865,8 +799,8 @@ static bool assert_contiguous_in_range(struct drm_mm *mm,
if (end < U64_MAX) { if (end < U64_MAX) {
node = __drm_mm_interval_first(mm, end, U64_MAX); node = __drm_mm_interval_first(mm, end, U64_MAX);
if (drm_mm_node_allocated(node)) { if (drm_mm_node_allocated(node)) {
pr_err("node after end: node=%llx+%llu, end=%llx\n", KUNIT_FAIL(test, "node after end: node=%llx+%llu, end=%llx\n",
node->start, node->size, end); node->start, node->size, end);
return false; return false;
} }
} }
...@@ -874,7 +808,7 @@ static bool assert_contiguous_in_range(struct drm_mm *mm, ...@@ -874,7 +808,7 @@ static bool assert_contiguous_in_range(struct drm_mm *mm,
return true; return true;
} }
static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end) static int __igt_insert_range(struct kunit *test, unsigned int count, u64 size, u64 start, u64 end)
{ {
const struct insert_mode *mode; const struct insert_mode *mode;
struct drm_mm mm; struct drm_mm mm;
...@@ -886,14 +820,13 @@ static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end) ...@@ -886,14 +820,13 @@ static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end)
DRM_MM_BUG_ON(!size); DRM_MM_BUG_ON(!size);
DRM_MM_BUG_ON(end <= start); DRM_MM_BUG_ON(end <= start);
/* Very similar to __igt_insert(), but now instead of populating the /* Very similar to __igt_insert(struct kunit *test, ), but now instead of populating the
* full range of the drm_mm, we try to fill a small portion of it. * full range of the drm_mm, we try to fill a small portion of it.
*/ */
ret = -ENOMEM; ret = -ENOMEM;
nodes = vzalloc(array_size(count, sizeof(*nodes))); nodes = vzalloc(array_size(count, sizeof(*nodes)));
if (!nodes) KUNIT_ASSERT_TRUE(test, nodes);
goto err;
ret = -EINVAL; ret = -EINVAL;
drm_mm_init(&mm, 0, count * size); drm_mm_init(&mm, 0, count * size);
...@@ -903,20 +836,19 @@ static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end) ...@@ -903,20 +836,19 @@ static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end)
for (mode = insert_modes; mode->name; mode++) { for (mode = insert_modes; mode->name; mode++) {
for (n = start_n; n <= end_n; n++) { for (n = start_n; n <= end_n; n++) {
if (!expect_insert_in_range(&mm, &nodes[n], if (!expect_insert_in_range(test, &mm, &nodes[n], size, size, n,
size, size, n,
start, end, mode)) { start, end, mode)) {
pr_err("%s insert failed, size %llu, step %d [%d, %d], range [%llx, %llx]\n", KUNIT_FAIL(test,
mode->name, size, n, "%s insert failed, size %llu, step %d [%d, %d], range [%llx, %llx]\n",
start_n, end_n, mode->name, size, n, start_n, end_n, start, end);
start, end);
goto out; goto out;
} }
} }
if (!assert_contiguous_in_range(&mm, size, start, end)) { if (!assert_contiguous_in_range(test, &mm, size, start, end)) {
pr_err("%s: range [%llx, %llx] not full after initialisation, size=%llu\n", KUNIT_FAIL(test,
mode->name, start, end, size); "%s: range [%llx, %llx] not full after initialisation, size=%llu\n",
mode->name, start, end, size);
goto out; goto out;
} }
...@@ -925,23 +857,24 @@ static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end) ...@@ -925,23 +857,24 @@ static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end)
u64 addr = nodes[n].start; u64 addr = nodes[n].start;
drm_mm_remove_node(&nodes[n]); drm_mm_remove_node(&nodes[n]);
if (!expect_insert_in_range(&mm, &nodes[n], if (!expect_insert_in_range(test, &mm, &nodes[n], size, size, n,
size, size, n,
start, end, mode)) { start, end, mode)) {
pr_err("%s reinsert failed, step %d\n", mode->name, n); KUNIT_FAIL(test, "%s reinsert failed, step %d\n", mode->name, n);
goto out; goto out;
} }
if (nodes[n].start != addr) { if (nodes[n].start != addr) {
pr_err("%s reinsert node moved, step %d, expected %llx, found %llx\n", KUNIT_FAIL(test,
mode->name, n, addr, nodes[n].start); "%s reinsert node moved, step %d, expected %llx, found %llx\n",
mode->name, n, addr, nodes[n].start);
goto out; goto out;
} }
} }
if (!assert_contiguous_in_range(&mm, size, start, end)) { if (!assert_contiguous_in_range(test, &mm, size, start, end)) {
pr_err("%s: range [%llx, %llx] not full after reinsertion, size=%llu\n", KUNIT_FAIL(test,
mode->name, start, end, size); "%s: range [%llx, %llx] not full after reinsertion, size=%llu\n",
mode->name, start, end, size);
goto out; goto out;
} }
...@@ -958,11 +891,10 @@ static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end) ...@@ -958,11 +891,10 @@ static int __igt_insert_range(unsigned int count, u64 size, u64 start, u64 end)
drm_mm_remove_node(node); drm_mm_remove_node(node);
drm_mm_takedown(&mm); drm_mm_takedown(&mm);
vfree(nodes); vfree(nodes);
err:
return ret; return ret;
} }
static int insert_outside_range(void) static int insert_outside_range(struct kunit *test)
{ {
struct drm_mm mm; struct drm_mm mm;
const unsigned int start = 1024; const unsigned int start = 1024;
...@@ -971,81 +903,58 @@ static int insert_outside_range(void) ...@@ -971,81 +903,58 @@ static int insert_outside_range(void)
drm_mm_init(&mm, start, size); drm_mm_init(&mm, start, size);
if (!expect_insert_in_range_fail(&mm, 1, 0, start)) if (!expect_insert_in_range_fail(test, &mm, 1, 0, start))
return -EINVAL; return -EINVAL;
if (!expect_insert_in_range_fail(&mm, size, if (!expect_insert_in_range_fail(test, &mm, size,
start - size/2, start + (size+1)/2)) start - size / 2, start + (size + 1) / 2))
return -EINVAL; return -EINVAL;
if (!expect_insert_in_range_fail(&mm, size, if (!expect_insert_in_range_fail(test, &mm, size,
end - (size+1)/2, end + size/2)) end - (size + 1) / 2, end + size / 2))
return -EINVAL; return -EINVAL;
if (!expect_insert_in_range_fail(&mm, 1, end, end + size)) if (!expect_insert_in_range_fail(test, &mm, 1, end, end + size))
return -EINVAL; return -EINVAL;
drm_mm_takedown(&mm); drm_mm_takedown(&mm);
return 0; return 0;
} }
static int igt_insert_range(void *ignored) static void igt_mm_insert_range(struct kunit *test)
{ {
const unsigned int count = min_t(unsigned int, BIT(13), max_iterations); const unsigned int count = min_t(unsigned int, BIT(13), max_iterations);
unsigned int n; unsigned int n;
int ret;
/* Check that requests outside the bounds of drm_mm are rejected. */ /* Check that requests outside the bounds of drm_mm are rejected. */
ret = insert_outside_range(); KUNIT_ASSERT_FALSE(test, insert_outside_range(test));
if (ret)
return ret;
for_each_prime_number_from(n, 1, 50) { for_each_prime_number_from(n, 1, 50) {
const u64 size = BIT_ULL(n); const u64 size = BIT_ULL(n);
const u64 max = count * size; const u64 max = count * size;
ret = __igt_insert_range(count, size, 0, max); KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, 0, max));
if (ret) KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, 1, max));
return ret; KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, 0, max - 1));
KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, 0, max / 2));
ret = __igt_insert_range(count, size, 1, max); KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size, max / 2, max / 2));
if (ret) KUNIT_ASSERT_FALSE(test, __igt_insert_range(test, count, size,
return ret; max / 4 + 1, 3 * max / 4 - 1));
ret = __igt_insert_range(count, size, 0, max - 1);
if (ret)
return ret;
ret = __igt_insert_range(count, size, 0, max/2);
if (ret)
return ret;
ret = __igt_insert_range(count, size, max/2, max);
if (ret)
return ret;
ret = __igt_insert_range(count, size, max/4+1, 3*max/4-1);
if (ret)
return ret;
cond_resched(); cond_resched();
} }
return 0;
} }
static int prepare_igt_frag(struct drm_mm *mm, static int prepare_igt_frag(struct kunit *test, struct drm_mm *mm,
struct drm_mm_node *nodes, struct drm_mm_node *nodes, unsigned int num_insert,
unsigned int num_insert,
const struct insert_mode *mode) const struct insert_mode *mode)
{ {
unsigned int size = 4096; unsigned int size = 4096;
unsigned int i; unsigned int i;
for (i = 0; i < num_insert; i++) { for (i = 0; i < num_insert; i++) {
if (!expect_insert(mm, &nodes[i], size, 0, i, if (!expect_insert(test, mm, &nodes[i], size, 0, i, mode) != 0) {
mode) != 0) { KUNIT_FAIL(test, "%s insert failed\n", mode->name);
pr_err("%s insert failed\n", mode->name);
return -EINVAL; return -EINVAL;
} }
} }
...@@ -1057,12 +966,10 @@ static int prepare_igt_frag(struct drm_mm *mm, ...@@ -1057,12 +966,10 @@ static int prepare_igt_frag(struct drm_mm *mm,
} }
return 0; return 0;
} }
static u64 get_insert_time(struct drm_mm *mm, static u64 get_insert_time(struct kunit *test, struct drm_mm *mm,
unsigned int num_insert, unsigned int num_insert, struct drm_mm_node *nodes,
struct drm_mm_node *nodes,
const struct insert_mode *mode) const struct insert_mode *mode)
{ {
unsigned int size = 8192; unsigned int size = 8192;
...@@ -1071,8 +978,8 @@ static u64 get_insert_time(struct drm_mm *mm, ...@@ -1071,8 +978,8 @@ static u64 get_insert_time(struct drm_mm *mm,
start = ktime_get(); start = ktime_get();
for (i = 0; i < num_insert; i++) { for (i = 0; i < num_insert; i++) {
if (!expect_insert(mm, &nodes[i], size, 0, i, mode) != 0) { if (!expect_insert(test, mm, &nodes[i], size, 0, i, mode) != 0) {
pr_err("%s insert failed\n", mode->name); KUNIT_FAIL(test, "%s insert failed\n", mode->name);
return 0; return 0;
} }
} }
...@@ -1080,28 +987,26 @@ static u64 get_insert_time(struct drm_mm *mm, ...@@ -1080,28 +987,26 @@ static u64 get_insert_time(struct drm_mm *mm,
return ktime_to_ns(ktime_sub(ktime_get(), start)); return ktime_to_ns(ktime_sub(ktime_get(), start));
} }
static int igt_frag(void *ignored) static void igt_mm_frag(struct kunit *test)
{ {
struct drm_mm mm; struct drm_mm mm;
const struct insert_mode *mode; const struct insert_mode *mode;
struct drm_mm_node *nodes, *node, *next; struct drm_mm_node *nodes, *node, *next;
unsigned int insert_size = 10000; unsigned int insert_size = 10000;
unsigned int scale_factor = 4; unsigned int scale_factor = 4;
int ret = -EINVAL;
/* We need 4 * insert_size nodes to hold intermediate allocated /* We need 4 * insert_size nodes to hold intermediate allocated
* drm_mm nodes. * drm_mm nodes.
* 1 times for prepare_igt_frag() * 1 times for prepare_igt_frag(struct kunit *test, )
* 1 times for get_insert_time() * 1 times for get_insert_time(struct kunit *test, )
* 2 times for get_insert_time() * 2 times for get_insert_time(struct kunit *test, )
*/ */
nodes = vzalloc(array_size(insert_size * 4, sizeof(*nodes))); nodes = vzalloc(array_size(insert_size * 4, sizeof(*nodes)));
if (!nodes) KUNIT_ASSERT_TRUE(test, nodes);
return -ENOMEM;
/* For BOTTOMUP and TOPDOWN, we first fragment the /* For BOTTOMUP and TOPDOWN, we first fragment the
* address space using prepare_igt_frag() and then try to verify * address space using prepare_igt_frag(struct kunit *test, ) and then try to verify
* that that insertions scale quadratically from 10k to 20k insertions * that insertions scale quadratically from 10k to 20k insertions
*/ */
drm_mm_init(&mm, 1, U64_MAX - 2); drm_mm_init(&mm, 1, U64_MAX - 2);
for (mode = insert_modes; mode->name; mode++) { for (mode = insert_modes; mode->name; mode++) {
...@@ -1111,28 +1016,25 @@ static int igt_frag(void *ignored) ...@@ -1111,28 +1016,25 @@ static int igt_frag(void *ignored)
mode->mode != DRM_MM_INSERT_HIGH) mode->mode != DRM_MM_INSERT_HIGH)
continue; continue;
ret = prepare_igt_frag(&mm, nodes, insert_size, mode); if (prepare_igt_frag(test, &mm, nodes, insert_size, mode))
if (ret)
goto err; goto err;
insert_time1 = get_insert_time(&mm, insert_size, insert_time1 = get_insert_time(test, &mm, insert_size,
nodes + insert_size, mode); nodes + insert_size, mode);
if (insert_time1 == 0) if (insert_time1 == 0)
goto err; goto err;
insert_time2 = get_insert_time(&mm, (insert_size * 2), insert_time2 = get_insert_time(test, &mm, (insert_size * 2),
nodes + insert_size * 2, mode); nodes + insert_size * 2, mode);
if (insert_time2 == 0) if (insert_time2 == 0)
goto err; goto err;
pr_info("%s fragmented insert of %u and %u insertions took %llu and %llu nsecs\n", kunit_info(test, "%s fragmented insert of %u and %u insertions took %llu and %llu nsecs\n",
mode->name, insert_size, insert_size * 2, mode->name, insert_size, insert_size * 2, insert_time1, insert_time2);
insert_time1, insert_time2);
if (insert_time2 > (scale_factor * insert_time1)) { if (insert_time2 > (scale_factor * insert_time1)) {
pr_err("%s fragmented insert took %llu nsecs more\n", KUNIT_FAIL(test, "%s fragmented insert took %llu nsecs more\n",
mode->name, mode->name, insert_time2 - (scale_factor * insert_time1));
insert_time2 - (scale_factor * insert_time1));
goto err; goto err;
} }
...@@ -1140,24 +1042,20 @@ static int igt_frag(void *ignored) ...@@ -1140,24 +1042,20 @@ static int igt_frag(void *ignored)
drm_mm_remove_node(node); drm_mm_remove_node(node);
} }
ret = 0;
err: err:
drm_mm_for_each_node_safe(node, next, &mm) drm_mm_for_each_node_safe(node, next, &mm)
drm_mm_remove_node(node); drm_mm_remove_node(node);
drm_mm_takedown(&mm); drm_mm_takedown(&mm);
vfree(nodes); vfree(nodes);
return ret;
} }
static int igt_align(void *ignored) static void igt_mm_align(struct kunit *test)
{ {
const struct insert_mode *mode; const struct insert_mode *mode;
const unsigned int max_count = min(8192u, max_prime); const unsigned int max_count = min(8192u, max_prime);
struct drm_mm mm; struct drm_mm mm;
struct drm_mm_node *nodes, *node, *next; struct drm_mm_node *nodes, *node, *next;
unsigned int prime; unsigned int prime;
int ret = -EINVAL;
/* For each of the possible insertion modes, we pick a few /* For each of the possible insertion modes, we pick a few
* arbitrary alignments and check that the inserted node * arbitrary alignments and check that the inserted node
...@@ -1165,8 +1063,7 @@ static int igt_align(void *ignored) ...@@ -1165,8 +1063,7 @@ static int igt_align(void *ignored)
*/ */
nodes = vzalloc(array_size(max_count, sizeof(*nodes))); nodes = vzalloc(array_size(max_count, sizeof(*nodes)));
if (!nodes) KUNIT_ASSERT_TRUE(test, nodes);
goto err;
drm_mm_init(&mm, 1, U64_MAX - 2); drm_mm_init(&mm, 1, U64_MAX - 2);
...@@ -1176,11 +1073,9 @@ static int igt_align(void *ignored) ...@@ -1176,11 +1073,9 @@ static int igt_align(void *ignored)
for_each_prime_number_from(prime, 1, max_count) { for_each_prime_number_from(prime, 1, max_count) {
u64 size = next_prime_number(prime); u64 size = next_prime_number(prime);
if (!expect_insert(&mm, &nodes[i], if (!expect_insert(test, &mm, &nodes[i], size, prime, i, mode)) {
size, prime, i, KUNIT_FAIL(test, "%s insert failed with alignment=%d",
mode)) { mode->name, prime);
pr_err("%s insert failed with alignment=%d",
mode->name, prime);
goto out; goto out;
} }
...@@ -1194,22 +1089,18 @@ static int igt_align(void *ignored) ...@@ -1194,22 +1089,18 @@ static int igt_align(void *ignored)
cond_resched(); cond_resched();
} }
ret = 0;
out: out:
drm_mm_for_each_node_safe(node, next, &mm) drm_mm_for_each_node_safe(node, next, &mm)
drm_mm_remove_node(node); drm_mm_remove_node(node);
drm_mm_takedown(&mm); drm_mm_takedown(&mm);
vfree(nodes); vfree(nodes);
err:
return ret;
} }
static int igt_align_pot(int max) static void igt_align_pot(struct kunit *test, int max)
{ {
struct drm_mm mm; struct drm_mm mm;
struct drm_mm_node *node, *next; struct drm_mm_node *node, *next;
int bit; int bit;
int ret = -EINVAL;
/* Check that we can align to the full u64 address space */ /* Check that we can align to the full u64 address space */
...@@ -1220,51 +1111,45 @@ static int igt_align_pot(int max) ...@@ -1220,51 +1111,45 @@ static int igt_align_pot(int max)
node = kzalloc(sizeof(*node), GFP_KERNEL); node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node) { if (!node) {
ret = -ENOMEM; KUNIT_FAIL(test, "failed to allocate node");
goto out; goto out;
} }
align = BIT_ULL(bit); align = BIT_ULL(bit);
size = BIT_ULL(bit-1) + 1; size = BIT_ULL(bit - 1) + 1;
if (!expect_insert(&mm, node, if (!expect_insert(test, &mm, node, size, align, bit, &insert_modes[0])) {
size, align, bit, KUNIT_FAIL(test, "insert failed with alignment=%llx [%d]", align, bit);
&insert_modes[0])) {
pr_err("insert failed with alignment=%llx [%d]",
align, bit);
goto out; goto out;
} }
cond_resched(); cond_resched();
} }
ret = 0;
out: out:
drm_mm_for_each_node_safe(node, next, &mm) { drm_mm_for_each_node_safe(node, next, &mm) {
drm_mm_remove_node(node); drm_mm_remove_node(node);
kfree(node); kfree(node);
} }
drm_mm_takedown(&mm); drm_mm_takedown(&mm);
return ret;
} }
static int igt_align32(void *ignored) static void igt_mm_align32(struct kunit *test)
{ {
return igt_align_pot(32); igt_align_pot(test, 32);
} }
static int igt_align64(void *ignored) static void igt_mm_align64(struct kunit *test)
{ {
return igt_align_pot(64); igt_align_pot(test, 64);
} }
static void show_scan(const struct drm_mm_scan *scan) static void show_scan(struct kunit *test, const struct drm_mm_scan *scan)
{ {
pr_info("scan: hit [%llx, %llx], size=%lld, align=%lld, color=%ld\n", kunit_info(test, "scan: hit [%llx, %llx], size=%lld, align=%lld, color=%ld\n",
scan->hit_start, scan->hit_end, scan->hit_start, scan->hit_end, scan->size, scan->alignment, scan->color);
scan->size, scan->alignment, scan->color);
} }
static void show_holes(const struct drm_mm *mm, int count) static void show_holes(struct kunit *test, const struct drm_mm *mm, int count)
{ {
u64 hole_start, hole_end; u64 hole_start, hole_end;
struct drm_mm_node *hole; struct drm_mm_node *hole;
...@@ -1274,19 +1159,15 @@ static void show_holes(const struct drm_mm *mm, int count) ...@@ -1274,19 +1159,15 @@ static void show_holes(const struct drm_mm *mm, int count)
const char *node1 = NULL, *node2 = NULL; const char *node1 = NULL, *node2 = NULL;
if (drm_mm_node_allocated(hole)) if (drm_mm_node_allocated(hole))
node1 = kasprintf(GFP_KERNEL, node1 = kasprintf(GFP_KERNEL, "[%llx + %lld, color=%ld], ",
"[%llx + %lld, color=%ld], ",
hole->start, hole->size, hole->color); hole->start, hole->size, hole->color);
if (drm_mm_node_allocated(next)) if (drm_mm_node_allocated(next))
node2 = kasprintf(GFP_KERNEL, node2 = kasprintf(GFP_KERNEL, ", [%llx + %lld, color=%ld]",
", [%llx + %lld, color=%ld]",
next->start, next->size, next->color); next->start, next->size, next->color);
pr_info("%sHole [%llx - %llx, size %lld]%s\n", kunit_info(test, "%sHole [%llx - %llx, size %lld]%s\n", node1,
node1, hole_start, hole_end, hole_end - hole_start, node2);
hole_start, hole_end, hole_end - hole_start,
node2);
kfree(node2); kfree(node2);
kfree(node1); kfree(node1);
...@@ -1301,12 +1182,9 @@ struct evict_node { ...@@ -1301,12 +1182,9 @@ struct evict_node {
struct list_head link; struct list_head link;
}; };
static bool evict_nodes(struct drm_mm_scan *scan, static bool evict_nodes(struct kunit *test, struct drm_mm_scan *scan,
struct evict_node *nodes, struct evict_node *nodes, unsigned int *order, unsigned int count,
unsigned int *order, bool use_color, struct list_head *evict_list)
unsigned int count,
bool use_color,
struct list_head *evict_list)
{ {
struct evict_node *e, *en; struct evict_node *e, *en;
unsigned int i; unsigned int i;
...@@ -1322,8 +1200,9 @@ static bool evict_nodes(struct drm_mm_scan *scan, ...@@ -1322,8 +1200,9 @@ static bool evict_nodes(struct drm_mm_scan *scan,
list_del(&e->link); list_del(&e->link);
} }
if (list_empty(evict_list)) { if (list_empty(evict_list)) {
pr_err("Failed to find eviction: size=%lld [avail=%d], align=%lld (color=%lu)\n", KUNIT_FAIL(test,
scan->size, count, scan->alignment, scan->color); "Failed to find eviction: size=%lld [avail=%d], align=%lld (color=%lu)\n",
scan->size, count, scan->alignment, scan->color);
return false; return false;
} }
...@@ -1340,7 +1219,8 @@ static bool evict_nodes(struct drm_mm_scan *scan, ...@@ -1340,7 +1219,8 @@ static bool evict_nodes(struct drm_mm_scan *scan,
} }
} else { } else {
if (drm_mm_scan_color_evict(scan)) { if (drm_mm_scan_color_evict(scan)) {
pr_err("drm_mm_scan_color_evict unexpectedly reported overlapping nodes!\n"); KUNIT_FAIL(test,
"drm_mm_scan_color_evict unexpectedly reported overlapping nodes!\n");
return false; return false;
} }
} }
...@@ -1348,9 +1228,8 @@ static bool evict_nodes(struct drm_mm_scan *scan, ...@@ -1348,9 +1228,8 @@ static bool evict_nodes(struct drm_mm_scan *scan,
return true; return true;
} }
static bool evict_nothing(struct drm_mm *mm, static bool evict_nothing(struct kunit *test, struct drm_mm *mm,
unsigned int total_size, unsigned int total_size, struct evict_node *nodes)
struct evict_node *nodes)
{ {
struct drm_mm_scan scan; struct drm_mm_scan scan;
LIST_HEAD(evict_list); LIST_HEAD(evict_list);
...@@ -1371,7 +1250,7 @@ static bool evict_nothing(struct drm_mm *mm, ...@@ -1371,7 +1250,7 @@ static bool evict_nothing(struct drm_mm *mm,
e = &nodes[n]; e = &nodes[n];
if (!drm_mm_node_allocated(&e->node)) { if (!drm_mm_node_allocated(&e->node)) {
pr_err("node[%d] no longer allocated!\n", n); KUNIT_FAIL(test, "node[%d] no longer allocated!\n", n);
return false; return false;
} }
...@@ -1387,17 +1266,16 @@ static bool evict_nothing(struct drm_mm *mm, ...@@ -1387,17 +1266,16 @@ static bool evict_nothing(struct drm_mm *mm,
e = &nodes[n]; e = &nodes[n];
if (!e->link.next) { if (!e->link.next) {
pr_err("node[%d] no longer connected!\n", n); KUNIT_FAIL(test, "node[%d] no longer connected!\n", n);
return false; return false;
} }
} }
return assert_continuous(mm, nodes[0].node.size); return assert_continuous(test, mm, nodes[0].node.size);
} }
static bool evict_everything(struct drm_mm *mm, static bool evict_everything(struct kunit *test, struct drm_mm *mm,
unsigned int total_size, unsigned int total_size, struct evict_node *nodes)
struct evict_node *nodes)
{ {
struct drm_mm_scan scan; struct drm_mm_scan scan;
LIST_HEAD(evict_list); LIST_HEAD(evict_list);
...@@ -1417,8 +1295,8 @@ static bool evict_everything(struct drm_mm *mm, ...@@ -1417,8 +1295,8 @@ static bool evict_everything(struct drm_mm *mm,
list_for_each_entry(e, &evict_list, link) { list_for_each_entry(e, &evict_list, link) {
if (!drm_mm_scan_remove_block(&scan, &e->node)) { if (!drm_mm_scan_remove_block(&scan, &e->node)) {
if (!err) { if (!err) {
pr_err("Node %lld not marked for eviction!\n", KUNIT_FAIL(test, "Node %lld not marked for eviction!\n",
e->node.start); e->node.start);
err = -EINVAL; err = -EINVAL;
} }
} }
...@@ -1429,29 +1307,25 @@ static bool evict_everything(struct drm_mm *mm, ...@@ -1429,29 +1307,25 @@ static bool evict_everything(struct drm_mm *mm,
list_for_each_entry(e, &evict_list, link) list_for_each_entry(e, &evict_list, link)
drm_mm_remove_node(&e->node); drm_mm_remove_node(&e->node);
if (!assert_one_hole(mm, 0, total_size)) if (!assert_one_hole(test, mm, 0, total_size))
return false; return false;
list_for_each_entry(e, &evict_list, link) { list_for_each_entry(e, &evict_list, link) {
err = drm_mm_reserve_node(mm, &e->node); err = drm_mm_reserve_node(mm, &e->node);
if (err) { if (err) {
pr_err("Failed to reinsert node after eviction: start=%llx\n", KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n",
e->node.start); e->node.start);
return false; return false;
} }
} }
return assert_continuous(mm, nodes[0].node.size); return assert_continuous(test, mm, nodes[0].node.size);
} }
static int evict_something(struct drm_mm *mm, static int evict_something(struct kunit *test, struct drm_mm *mm,
u64 range_start, u64 range_end, u64 range_start, u64 range_end, struct evict_node *nodes,
struct evict_node *nodes, unsigned int *order, unsigned int count, unsigned int size,
unsigned int *order, unsigned int alignment, const struct insert_mode *mode)
unsigned int count,
unsigned int size,
unsigned int alignment,
const struct insert_mode *mode)
{ {
struct drm_mm_scan scan; struct drm_mm_scan scan;
LIST_HEAD(evict_list); LIST_HEAD(evict_list);
...@@ -1459,38 +1333,35 @@ static int evict_something(struct drm_mm *mm, ...@@ -1459,38 +1333,35 @@ static int evict_something(struct drm_mm *mm,
struct drm_mm_node tmp; struct drm_mm_node tmp;
int err; int err;
drm_mm_scan_init_with_range(&scan, mm, drm_mm_scan_init_with_range(&scan, mm, size, alignment, 0, range_start,
size, alignment, 0, range_end, mode->mode);
range_start, range_end, if (!evict_nodes(test, &scan, nodes, order, count, false, &evict_list))
mode->mode);
if (!evict_nodes(&scan,
nodes, order, count, false,
&evict_list))
return -EINVAL; return -EINVAL;
memset(&tmp, 0, sizeof(tmp)); memset(&tmp, 0, sizeof(tmp));
err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, 0, err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, 0,
DRM_MM_INSERT_EVICT); DRM_MM_INSERT_EVICT);
if (err) { if (err) {
pr_err("Failed to insert into eviction hole: size=%d, align=%d\n", KUNIT_FAIL(test, "Failed to insert into eviction hole: size=%d, align=%d\n",
size, alignment); size, alignment);
show_scan(&scan); show_scan(test, &scan);
show_holes(mm, 3); show_holes(test, mm, 3);
return err; return err;
} }
if (tmp.start < range_start || tmp.start + tmp.size > range_end) { if (tmp.start < range_start || tmp.start + tmp.size > range_end) {
pr_err("Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n", KUNIT_FAIL(test,
tmp.start, tmp.size, range_start, range_end); "Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n",
tmp.start, tmp.size, range_start, range_end);
err = -EINVAL; err = -EINVAL;
} }
if (!assert_node(&tmp, mm, size, alignment, 0) || if (!assert_node(test, &tmp, mm, size, alignment, 0) ||
drm_mm_hole_follows(&tmp)) { drm_mm_hole_follows(&tmp)) {
pr_err("Inserted did not fill the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx, hole-follows?=%d\n", KUNIT_FAIL(test,
tmp.size, size, "Inserted did not fill the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx, hole-follows?=%d\n",
alignment, misalignment(&tmp, alignment), tmp.size, size, alignment, misalignment(&tmp, alignment),
tmp.start, drm_mm_hole_follows(&tmp)); tmp.start, drm_mm_hole_follows(&tmp));
err = -EINVAL; err = -EINVAL;
} }
...@@ -1501,21 +1372,21 @@ static int evict_something(struct drm_mm *mm, ...@@ -1501,21 +1372,21 @@ static int evict_something(struct drm_mm *mm,
list_for_each_entry(e, &evict_list, link) { list_for_each_entry(e, &evict_list, link) {
err = drm_mm_reserve_node(mm, &e->node); err = drm_mm_reserve_node(mm, &e->node);
if (err) { if (err) {
pr_err("Failed to reinsert node after eviction: start=%llx\n", KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n",
e->node.start); e->node.start);
return err; return err;
} }
} }
if (!assert_continuous(mm, nodes[0].node.size)) { if (!assert_continuous(test, mm, nodes[0].node.size)) {
pr_err("range is no longer continuous\n"); KUNIT_FAIL(test, "range is no longer continuous\n");
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
} }
static int igt_evict(void *ignored) static void igt_mm_evict(struct kunit *test)
{ {
DRM_RND_STATE(prng, random_seed); DRM_RND_STATE(prng, random_seed);
const unsigned int size = 8192; const unsigned int size = 8192;
...@@ -1524,7 +1395,6 @@ static int igt_evict(void *ignored) ...@@ -1524,7 +1395,6 @@ static int igt_evict(void *ignored)
struct evict_node *nodes; struct evict_node *nodes;
struct drm_mm_node *node, *next; struct drm_mm_node *node, *next;
unsigned int *order, n; unsigned int *order, n;
int ret, err;
/* Here we populate a full drm_mm and then try and insert a new node /* Here we populate a full drm_mm and then try and insert a new node
* by evicting other nodes in a random order. The drm_mm_scan should * by evicting other nodes in a random order. The drm_mm_scan should
...@@ -1533,61 +1403,49 @@ static int igt_evict(void *ignored) ...@@ -1533,61 +1403,49 @@ static int igt_evict(void *ignored)
* sizes to try and stress the hole finder. * sizes to try and stress the hole finder.
*/ */
ret = -ENOMEM;
nodes = vzalloc(array_size(size, sizeof(*nodes))); nodes = vzalloc(array_size(size, sizeof(*nodes)));
if (!nodes) KUNIT_ASSERT_TRUE(test, nodes);
goto err;
order = drm_random_order(size, &prng); order = drm_random_order(size, &prng);
if (!order) if (!order)
goto err_nodes; goto err_nodes;
ret = -EINVAL;
drm_mm_init(&mm, 0, size); drm_mm_init(&mm, 0, size);
for (n = 0; n < size; n++) { for (n = 0; n < size; n++) {
err = drm_mm_insert_node(&mm, &nodes[n].node, 1); if (drm_mm_insert_node(&mm, &nodes[n].node, 1)) {
if (err) { KUNIT_FAIL(test, "insert failed, step %d\n", n);
pr_err("insert failed, step %d\n", n);
ret = err;
goto out; goto out;
} }
} }
/* First check that using the scanner doesn't break the mm */ /* First check that using the scanner doesn't break the mm */
if (!evict_nothing(&mm, size, nodes)) { if (!evict_nothing(test, &mm, size, nodes)) {
pr_err("evict_nothing() failed\n"); KUNIT_FAIL(test, "evict_nothing() failed\n");
goto out; goto out;
} }
if (!evict_everything(&mm, size, nodes)) { if (!evict_everything(test, &mm, size, nodes)) {
pr_err("evict_everything() failed\n"); KUNIT_FAIL(test, "evict_everything() failed\n");
goto out; goto out;
} }
for (mode = evict_modes; mode->name; mode++) { for (mode = evict_modes; mode->name; mode++) {
for (n = 1; n <= size; n <<= 1) { for (n = 1; n <= size; n <<= 1) {
drm_random_reorder(order, size, &prng); drm_random_reorder(order, size, &prng);
err = evict_something(&mm, 0, U64_MAX, if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size, n, 1,
nodes, order, size, mode)) {
n, 1, KUNIT_FAIL(test, "%s evict_something(size=%u) failed\n",
mode); mode->name, n);
if (err) {
pr_err("%s evict_something(size=%u) failed\n",
mode->name, n);
ret = err;
goto out; goto out;
} }
} }
for (n = 1; n < size; n <<= 1) { for (n = 1; n < size; n <<= 1) {
drm_random_reorder(order, size, &prng); drm_random_reorder(order, size, &prng);
err = evict_something(&mm, 0, U64_MAX, if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size,
nodes, order, size, size / 2, n, mode)) {
size/2, n, KUNIT_FAIL(test,
mode); "%s evict_something(size=%u, alignment=%u) failed\n",
if (err) { mode->name, size / 2, n);
pr_err("%s evict_something(size=%u, alignment=%u) failed\n",
mode->name, size/2, n);
ret = err;
goto out; goto out;
} }
} }
...@@ -1598,14 +1456,11 @@ static int igt_evict(void *ignored) ...@@ -1598,14 +1456,11 @@ static int igt_evict(void *ignored)
DRM_MM_BUG_ON(!nsize); DRM_MM_BUG_ON(!nsize);
drm_random_reorder(order, size, &prng); drm_random_reorder(order, size, &prng);
err = evict_something(&mm, 0, U64_MAX, if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size,
nodes, order, size, nsize, n, mode)) {
nsize, n, KUNIT_FAIL(test,
mode); "%s evict_something(size=%u, alignment=%u) failed\n",
if (err) { mode->name, nsize, n);
pr_err("%s evict_something(size=%u, alignment=%u) failed\n",
mode->name, nsize, n);
ret = err;
goto out; goto out;
} }
} }
...@@ -1613,7 +1468,6 @@ static int igt_evict(void *ignored) ...@@ -1613,7 +1468,6 @@ static int igt_evict(void *ignored)
cond_resched(); cond_resched();
} }
ret = 0;
out: out:
drm_mm_for_each_node_safe(node, next, &mm) drm_mm_for_each_node_safe(node, next, &mm)
drm_mm_remove_node(node); drm_mm_remove_node(node);
...@@ -1621,11 +1475,9 @@ static int igt_evict(void *ignored) ...@@ -1621,11 +1475,9 @@ static int igt_evict(void *ignored)
kfree(order); kfree(order);
err_nodes: err_nodes:
vfree(nodes); vfree(nodes);
err:
return ret;
} }
static int igt_evict_range(void *ignored) static void igt_mm_evict_range(struct kunit *test)
{ {
DRM_RND_STATE(prng, random_seed); DRM_RND_STATE(prng, random_seed);
const unsigned int size = 8192; const unsigned int size = 8192;
...@@ -1637,28 +1489,22 @@ static int igt_evict_range(void *ignored) ...@@ -1637,28 +1489,22 @@ static int igt_evict_range(void *ignored)
struct evict_node *nodes; struct evict_node *nodes;
struct drm_mm_node *node, *next; struct drm_mm_node *node, *next;
unsigned int *order, n; unsigned int *order, n;
int ret, err;
/* Like igt_evict() but now we are limiting the search to a /* Like igt_evict() but now we are limiting the search to a
* small portion of the full drm_mm. * small portion of the full drm_mm.
*/ */
ret = -ENOMEM;
nodes = vzalloc(array_size(size, sizeof(*nodes))); nodes = vzalloc(array_size(size, sizeof(*nodes)));
if (!nodes) KUNIT_ASSERT_TRUE(test, nodes);
goto err;
order = drm_random_order(size, &prng); order = drm_random_order(size, &prng);
if (!order) if (!order)
goto err_nodes; goto err_nodes;
ret = -EINVAL;
drm_mm_init(&mm, 0, size); drm_mm_init(&mm, 0, size);
for (n = 0; n < size; n++) { for (n = 0; n < size; n++) {
err = drm_mm_insert_node(&mm, &nodes[n].node, 1); if (drm_mm_insert_node(&mm, &nodes[n].node, 1)) {
if (err) { KUNIT_FAIL(test, "insert failed, step %d\n", n);
pr_err("insert failed, step %d\n", n);
ret = err;
goto out; goto out;
} }
} }
...@@ -1666,26 +1512,22 @@ static int igt_evict_range(void *ignored) ...@@ -1666,26 +1512,22 @@ static int igt_evict_range(void *ignored)
for (mode = evict_modes; mode->name; mode++) { for (mode = evict_modes; mode->name; mode++) {
for (n = 1; n <= range_size; n <<= 1) { for (n = 1; n <= range_size; n <<= 1) {
drm_random_reorder(order, size, &prng); drm_random_reorder(order, size, &prng);
err = evict_something(&mm, range_start, range_end, if (evict_something(test, &mm, range_start, range_end, nodes,
nodes, order, size, order, size, n, 1, mode)) {
n, 1, KUNIT_FAIL(test,
mode); "%s evict_something(size=%u) failed with range [%u, %u]\n",
if (err) { mode->name, n, range_start, range_end);
pr_err("%s evict_something(size=%u) failed with range [%u, %u]\n",
mode->name, n, range_start, range_end);
goto out; goto out;
} }
} }
for (n = 1; n <= range_size; n <<= 1) { for (n = 1; n <= range_size; n <<= 1) {
drm_random_reorder(order, size, &prng); drm_random_reorder(order, size, &prng);
err = evict_something(&mm, range_start, range_end, if (evict_something(test, &mm, range_start, range_end, nodes,
nodes, order, size, order, size, range_size / 2, n, mode)) {
range_size/2, n, KUNIT_FAIL(test,
mode); "%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n",
if (err) { mode->name, range_size / 2, n, range_start, range_end);
pr_err("%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n",
mode->name, range_size/2, n, range_start, range_end);
goto out; goto out;
} }
} }
...@@ -1696,13 +1538,11 @@ static int igt_evict_range(void *ignored) ...@@ -1696,13 +1538,11 @@ static int igt_evict_range(void *ignored)
DRM_MM_BUG_ON(!nsize); DRM_MM_BUG_ON(!nsize);
drm_random_reorder(order, size, &prng); drm_random_reorder(order, size, &prng);
err = evict_something(&mm, range_start, range_end, if (evict_something(test, &mm, range_start, range_end, nodes,
nodes, order, size, order, size, nsize, n, mode)) {
nsize, n, KUNIT_FAIL(test,
mode); "%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n",
if (err) { mode->name, nsize, n, range_start, range_end);
pr_err("%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n",
mode->name, nsize, n, range_start, range_end);
goto out; goto out;
} }
} }
...@@ -1710,7 +1550,6 @@ static int igt_evict_range(void *ignored) ...@@ -1710,7 +1550,6 @@ static int igt_evict_range(void *ignored)
cond_resched(); cond_resched();
} }
ret = 0;
out: out:
drm_mm_for_each_node_safe(node, next, &mm) drm_mm_for_each_node_safe(node, next, &mm)
drm_mm_remove_node(node); drm_mm_remove_node(node);
...@@ -1718,8 +1557,6 @@ static int igt_evict_range(void *ignored) ...@@ -1718,8 +1557,6 @@ static int igt_evict_range(void *ignored)
kfree(order); kfree(order);
err_nodes: err_nodes:
vfree(nodes); vfree(nodes);
err:
return ret;
} }
static unsigned int node_index(const struct drm_mm_node *node) static unsigned int node_index(const struct drm_mm_node *node)
...@@ -1727,9 +1564,10 @@ static unsigned int node_index(const struct drm_mm_node *node) ...@@ -1727,9 +1564,10 @@ static unsigned int node_index(const struct drm_mm_node *node)
return div64_u64(node->start, node->size); return div64_u64(node->start, node->size);
} }
static int igt_topdown(void *ignored) static void igt_mm_topdown(struct kunit *test)
{ {
const struct insert_mode *topdown = &insert_modes[TOPDOWN]; const struct insert_mode *topdown = &insert_modes[TOPDOWN];
DRM_RND_STATE(prng, random_seed); DRM_RND_STATE(prng, random_seed);
const unsigned int count = 8192; const unsigned int count = 8192;
unsigned int size; unsigned int size;
...@@ -1737,17 +1575,14 @@ static int igt_topdown(void *ignored) ...@@ -1737,17 +1575,14 @@ static int igt_topdown(void *ignored)
struct drm_mm mm; struct drm_mm mm;
struct drm_mm_node *nodes, *node, *next; struct drm_mm_node *nodes, *node, *next;
unsigned int *order, n, m, o = 0; unsigned int *order, n, m, o = 0;
int ret;
/* When allocating top-down, we expect to be returned a node /* When allocating top-down, we expect to be returned a node
* from a suitable hole at the top of the drm_mm. We check that * from a suitable hole at the top of the drm_mm. We check that
* the returned node does match the highest available slot. * the returned node does match the highest available slot.
*/ */
ret = -ENOMEM;
nodes = vzalloc(array_size(count, sizeof(*nodes))); nodes = vzalloc(array_size(count, sizeof(*nodes)));
if (!nodes) KUNIT_ASSERT_TRUE(test, nodes);
goto err;
bitmap = bitmap_zalloc(count, GFP_KERNEL); bitmap = bitmap_zalloc(count, GFP_KERNEL);
if (!bitmap) if (!bitmap)
...@@ -1757,28 +1592,26 @@ static int igt_topdown(void *ignored) ...@@ -1757,28 +1592,26 @@ static int igt_topdown(void *ignored)
if (!order) if (!order)
goto err_bitmap; goto err_bitmap;
ret = -EINVAL;
for (size = 1; size <= 64; size <<= 1) { for (size = 1; size <= 64; size <<= 1) {
drm_mm_init(&mm, 0, size*count); drm_mm_init(&mm, 0, size * count);
for (n = 0; n < count; n++) { for (n = 0; n < count; n++) {
if (!expect_insert(&mm, &nodes[n], if (!expect_insert(test, &mm, &nodes[n], size, 0, n, topdown)) {
size, 0, n, KUNIT_FAIL(test, "insert failed, size %u step %d\n", size, n);
topdown)) {
pr_err("insert failed, size %u step %d\n", size, n);
goto out; goto out;
} }
if (drm_mm_hole_follows(&nodes[n])) { if (drm_mm_hole_follows(&nodes[n])) {
pr_err("hole after topdown insert %d, start=%llx\n, size=%u", KUNIT_FAIL(test,
n, nodes[n].start, size); "hole after topdown insert %d, start=%llx\n, size=%u",
n, nodes[n].start, size);
goto out; goto out;
} }
if (!assert_one_hole(&mm, 0, size*(count - n - 1))) if (!assert_one_hole(test, &mm, 0, size * (count - n - 1)))
goto out; goto out;
} }
if (!assert_continuous(&mm, size)) if (!assert_continuous(test, &mm, size))
goto out; goto out;
drm_random_reorder(order, count, &prng); drm_random_reorder(order, count, &prng);
...@@ -1793,23 +1626,23 @@ static int igt_topdown(void *ignored) ...@@ -1793,23 +1626,23 @@ static int igt_topdown(void *ignored)
unsigned int last; unsigned int last;
node = &nodes[order[(o + m) % count]]; node = &nodes[order[(o + m) % count]];
if (!expect_insert(&mm, node, if (!expect_insert(test, &mm, node, size, 0, 0, topdown)) {
size, 0, 0, KUNIT_FAIL(test, "insert failed, step %d/%d\n", m, n);
topdown)) {
pr_err("insert failed, step %d/%d\n", m, n);
goto out; goto out;
} }
if (drm_mm_hole_follows(node)) { if (drm_mm_hole_follows(node)) {
pr_err("hole after topdown insert %d/%d, start=%llx\n", KUNIT_FAIL(test,
m, n, node->start); "hole after topdown insert %d/%d, start=%llx\n",
m, n, node->start);
goto out; goto out;
} }
last = find_last_bit(bitmap, count); last = find_last_bit(bitmap, count);
if (node_index(node) != last) { if (node_index(node) != last) {
pr_err("node %d/%d, size %d, not inserted into upmost hole, expected %d, found %d\n", KUNIT_FAIL(test,
m, n, size, last, node_index(node)); "node %d/%d, size %d, not inserted into upmost hole, expected %d, found %d\n",
m, n, size, last, node_index(node));
goto out; goto out;
} }
...@@ -1827,7 +1660,6 @@ static int igt_topdown(void *ignored) ...@@ -1827,7 +1660,6 @@ static int igt_topdown(void *ignored)
cond_resched(); cond_resched();
} }
ret = 0;
out: out:
drm_mm_for_each_node_safe(node, next, &mm) drm_mm_for_each_node_safe(node, next, &mm)
drm_mm_remove_node(node); drm_mm_remove_node(node);
...@@ -1837,13 +1669,12 @@ static int igt_topdown(void *ignored) ...@@ -1837,13 +1669,12 @@ static int igt_topdown(void *ignored)
bitmap_free(bitmap); bitmap_free(bitmap);
err_nodes: err_nodes:
vfree(nodes); vfree(nodes);
err:
return ret;
} }
static int igt_bottomup(void *ignored) static void igt_mm_bottomup(struct kunit *test)
{ {
const struct insert_mode *bottomup = &insert_modes[BOTTOMUP]; const struct insert_mode *bottomup = &insert_modes[BOTTOMUP];
DRM_RND_STATE(prng, random_seed); DRM_RND_STATE(prng, random_seed);
const unsigned int count = 8192; const unsigned int count = 8192;
unsigned int size; unsigned int size;
...@@ -1851,16 +1682,13 @@ static int igt_bottomup(void *ignored) ...@@ -1851,16 +1682,13 @@ static int igt_bottomup(void *ignored)
struct drm_mm mm; struct drm_mm mm;
struct drm_mm_node *nodes, *node, *next; struct drm_mm_node *nodes, *node, *next;
unsigned int *order, n, m, o = 0; unsigned int *order, n, m, o = 0;
int ret;
/* Like igt_topdown, but instead of searching for the last hole, /* Like igt_topdown, but instead of searching for the last hole,
* we search for the first. * we search for the first.
*/ */
ret = -ENOMEM;
nodes = vzalloc(array_size(count, sizeof(*nodes))); nodes = vzalloc(array_size(count, sizeof(*nodes)));
if (!nodes) KUNIT_ASSERT_TRUE(test, nodes);
goto err;
bitmap = bitmap_zalloc(count, GFP_KERNEL); bitmap = bitmap_zalloc(count, GFP_KERNEL);
if (!bitmap) if (!bitmap)
...@@ -1870,22 +1698,20 @@ static int igt_bottomup(void *ignored) ...@@ -1870,22 +1698,20 @@ static int igt_bottomup(void *ignored)
if (!order) if (!order)
goto err_bitmap; goto err_bitmap;
ret = -EINVAL;
for (size = 1; size <= 64; size <<= 1) { for (size = 1; size <= 64; size <<= 1) {
drm_mm_init(&mm, 0, size*count); drm_mm_init(&mm, 0, size * count);
for (n = 0; n < count; n++) { for (n = 0; n < count; n++) {
if (!expect_insert(&mm, &nodes[n], if (!expect_insert(test, &mm, &nodes[n], size, 0, n, bottomup)) {
size, 0, n, KUNIT_FAIL(test,
bottomup)) { "bottomup insert failed, size %u step %d\n", size, n);
pr_err("bottomup insert failed, size %u step %d\n", size, n);
goto out; goto out;
} }
if (!assert_one_hole(&mm, size*(n + 1), size*count)) if (!assert_one_hole(test, &mm, size * (n + 1), size * count))
goto out; goto out;
} }
if (!assert_continuous(&mm, size)) if (!assert_continuous(test, &mm, size))
goto out; goto out;
drm_random_reorder(order, count, &prng); drm_random_reorder(order, count, &prng);
...@@ -1900,17 +1726,16 @@ static int igt_bottomup(void *ignored) ...@@ -1900,17 +1726,16 @@ static int igt_bottomup(void *ignored)
unsigned int first; unsigned int first;
node = &nodes[order[(o + m) % count]]; node = &nodes[order[(o + m) % count]];
if (!expect_insert(&mm, node, if (!expect_insert(test, &mm, node, size, 0, 0, bottomup)) {
size, 0, 0, KUNIT_FAIL(test, "insert failed, step %d/%d\n", m, n);
bottomup)) {
pr_err("insert failed, step %d/%d\n", m, n);
goto out; goto out;
} }
first = find_first_bit(bitmap, count); first = find_first_bit(bitmap, count);
if (node_index(node) != first) { if (node_index(node) != first) {
pr_err("node %d/%d not inserted into bottom hole, expected %d, found %d\n", KUNIT_FAIL(test,
m, n, first, node_index(node)); "node %d/%d not inserted into bottom hole, expected %d, found %d\n",
m, n, first, node_index(node));
goto out; goto out;
} }
__clear_bit(first, bitmap); __clear_bit(first, bitmap);
...@@ -1927,7 +1752,6 @@ static int igt_bottomup(void *ignored) ...@@ -1927,7 +1752,6 @@ static int igt_bottomup(void *ignored)
cond_resched(); cond_resched();
} }
ret = 0;
out: out:
drm_mm_for_each_node_safe(node, next, &mm) drm_mm_for_each_node_safe(node, next, &mm)
drm_mm_remove_node(node); drm_mm_remove_node(node);
...@@ -1937,47 +1761,39 @@ static int igt_bottomup(void *ignored) ...@@ -1937,47 +1761,39 @@ static int igt_bottomup(void *ignored)
bitmap_free(bitmap); bitmap_free(bitmap);
err_nodes: err_nodes:
vfree(nodes); vfree(nodes);
err:
return ret;
} }
static int __igt_once(unsigned int mode) static void __igt_once(struct kunit *test, unsigned int mode)
{ {
struct drm_mm mm; struct drm_mm mm;
struct drm_mm_node rsvd_lo, rsvd_hi, node; struct drm_mm_node rsvd_lo, rsvd_hi, node;
int err;
drm_mm_init(&mm, 0, 7); drm_mm_init(&mm, 0, 7);
memset(&rsvd_lo, 0, sizeof(rsvd_lo)); memset(&rsvd_lo, 0, sizeof(rsvd_lo));
rsvd_lo.start = 1; rsvd_lo.start = 1;
rsvd_lo.size = 1; rsvd_lo.size = 1;
err = drm_mm_reserve_node(&mm, &rsvd_lo); if (drm_mm_reserve_node(&mm, &rsvd_lo)) {
if (err) { KUNIT_FAIL(test, "Could not reserve low node\n");
pr_err("Could not reserve low node\n");
goto err; goto err;
} }
memset(&rsvd_hi, 0, sizeof(rsvd_hi)); memset(&rsvd_hi, 0, sizeof(rsvd_hi));
rsvd_hi.start = 5; rsvd_hi.start = 5;
rsvd_hi.size = 1; rsvd_hi.size = 1;
err = drm_mm_reserve_node(&mm, &rsvd_hi); if (drm_mm_reserve_node(&mm, &rsvd_hi)) {
if (err) { KUNIT_FAIL(test, "Could not reserve low node\n");
pr_err("Could not reserve low node\n");
goto err_lo; goto err_lo;
} }
if (!drm_mm_hole_follows(&rsvd_lo) || !drm_mm_hole_follows(&rsvd_hi)) { if (!drm_mm_hole_follows(&rsvd_lo) || !drm_mm_hole_follows(&rsvd_hi)) {
pr_err("Expected a hole after lo and high nodes!\n"); KUNIT_FAIL(test, "Expected a hole after lo and high nodes!\n");
err = -EINVAL;
goto err_hi; goto err_hi;
} }
memset(&node, 0, sizeof(node)); memset(&node, 0, sizeof(node));
err = drm_mm_insert_node_generic(&mm, &node, 2, 0, 0, mode); if (drm_mm_insert_node_generic(&mm, &node, 2, 0, 0, mode)) {
if (err) { KUNIT_FAIL(test, "Could not insert the node into the available hole!\n");
pr_err("Could not insert the node into the available hole!\n");
err = -EINVAL;
goto err_hi; goto err_hi;
} }
...@@ -1988,23 +1804,20 @@ static int __igt_once(unsigned int mode) ...@@ -1988,23 +1804,20 @@ static int __igt_once(unsigned int mode)
drm_mm_remove_node(&rsvd_lo); drm_mm_remove_node(&rsvd_lo);
err: err:
drm_mm_takedown(&mm); drm_mm_takedown(&mm);
return err;
} }
static int igt_lowest(void *ignored) static void igt_mm_lowest(struct kunit *test)
{ {
return __igt_once(DRM_MM_INSERT_LOW); __igt_once(test, DRM_MM_INSERT_LOW);
} }
static int igt_highest(void *ignored) static void igt_mm_highest(struct kunit *test)
{ {
return __igt_once(DRM_MM_INSERT_HIGH); __igt_once(test, DRM_MM_INSERT_HIGH);
} }
static void separate_adjacent_colors(const struct drm_mm_node *node, static void separate_adjacent_colors(const struct drm_mm_node *node,
unsigned long color, unsigned long color, u64 *start, u64 *end)
u64 *start,
u64 *end)
{ {
if (drm_mm_node_allocated(node) && node->color != color) if (drm_mm_node_allocated(node) && node->color != color)
++*start; ++*start;
...@@ -2014,12 +1827,12 @@ static void separate_adjacent_colors(const struct drm_mm_node *node, ...@@ -2014,12 +1827,12 @@ static void separate_adjacent_colors(const struct drm_mm_node *node,
--*end; --*end;
} }
static bool colors_abutt(const struct drm_mm_node *node) static bool colors_abutt(struct kunit *test, const struct drm_mm_node *node)
{ {
if (!drm_mm_hole_follows(node) && if (!drm_mm_hole_follows(node) &&
drm_mm_node_allocated(list_next_entry(node, node_list))) { drm_mm_node_allocated(list_next_entry(node, node_list))) {
pr_err("colors abutt; %ld [%llx + %llx] is next to %ld [%llx + %llx]!\n", KUNIT_FAIL(test, "colors abutt; %ld [%llx + %llx] is next to %ld [%llx + %llx]!\n",
node->color, node->start, node->size, node->color, node->start, node->size,
list_next_entry(node, node_list)->color, list_next_entry(node, node_list)->color,
list_next_entry(node, node_list)->start, list_next_entry(node, node_list)->start,
list_next_entry(node, node_list)->size); list_next_entry(node, node_list)->size);
...@@ -2029,14 +1842,13 @@ static bool colors_abutt(const struct drm_mm_node *node) ...@@ -2029,14 +1842,13 @@ static bool colors_abutt(const struct drm_mm_node *node)
return false; return false;
} }
static int igt_color(void *ignored) static void igt_mm_color(struct kunit *test)
{ {
const unsigned int count = min(4096u, max_iterations); const unsigned int count = min(4096u, max_iterations);
const struct insert_mode *mode; const struct insert_mode *mode;
struct drm_mm mm; struct drm_mm mm;
struct drm_mm_node *node, *nn; struct drm_mm_node *node, *nn;
unsigned int n; unsigned int n;
int ret = -EINVAL, err;
/* Color adjustment complicates everything. First we just check /* Color adjustment complicates everything. First we just check
* that when we insert a node we apply any color_adjustment callback. * that when we insert a node we apply any color_adjustment callback.
...@@ -2049,15 +1861,11 @@ static int igt_color(void *ignored) ...@@ -2049,15 +1861,11 @@ static int igt_color(void *ignored)
for (n = 1; n <= count; n++) { for (n = 1; n <= count; n++) {
node = kzalloc(sizeof(*node), GFP_KERNEL); node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node) { if (!node)
ret = -ENOMEM;
goto out; goto out;
}
if (!expect_insert(&mm, node, if (!expect_insert(test, &mm, node, n, 0, n, &insert_modes[0])) {
n, 0, n, KUNIT_FAIL(test, "insert failed, step %d\n", n);
&insert_modes[0])) {
pr_err("insert failed, step %d\n", n);
kfree(node); kfree(node);
goto out; goto out;
} }
...@@ -2065,8 +1873,8 @@ static int igt_color(void *ignored) ...@@ -2065,8 +1873,8 @@ static int igt_color(void *ignored)
drm_mm_for_each_node_safe(node, nn, &mm) { drm_mm_for_each_node_safe(node, nn, &mm) {
if (node->color != node->size) { if (node->color != node->size) {
pr_err("invalid color stored: expected %lld, found %ld\n", KUNIT_FAIL(test, "invalid color stored: expected %lld, found %ld\n",
node->size, node->color); node->size, node->color);
goto out; goto out;
} }
...@@ -2081,18 +1889,14 @@ static int igt_color(void *ignored) ...@@ -2081,18 +1889,14 @@ static int igt_color(void *ignored)
u64 last; u64 last;
node = kzalloc(sizeof(*node), GFP_KERNEL); node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node) { if (!node)
ret = -ENOMEM;
goto out; goto out;
}
node->size = 1 + 2*count; node->size = 1 + 2 * count;
node->color = node->size; node->color = node->size;
err = drm_mm_reserve_node(&mm, node); if (drm_mm_reserve_node(&mm, node)) {
if (err) { KUNIT_FAIL(test, "initial reserve failed!\n");
pr_err("initial reserve failed!\n");
ret = err;
goto out; goto out;
} }
...@@ -2102,19 +1906,15 @@ static int igt_color(void *ignored) ...@@ -2102,19 +1906,15 @@ static int igt_color(void *ignored)
int rem; int rem;
node = kzalloc(sizeof(*node), GFP_KERNEL); node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node) { if (!node)
ret = -ENOMEM;
goto out; goto out;
}
node->start = last; node->start = last;
node->size = n + count; node->size = n + count;
node->color = node->size; node->color = node->size;
err = drm_mm_reserve_node(&mm, node); if (drm_mm_reserve_node(&mm, node) != -ENOSPC) {
if (err != -ENOSPC) { KUNIT_FAIL(test, "reserve %d did not report color overlap!", n);
pr_err("reserve %d did not report color overlap! err=%d\n",
n, err);
goto out; goto out;
} }
...@@ -2122,10 +1922,8 @@ static int igt_color(void *ignored) ...@@ -2122,10 +1922,8 @@ static int igt_color(void *ignored)
rem = misalignment(node, n + count); rem = misalignment(node, n + count);
node->start += n + count - rem; node->start += n + count - rem;
err = drm_mm_reserve_node(&mm, node); if (drm_mm_reserve_node(&mm, node)) {
if (err) { KUNIT_FAIL(test, "reserve %d failed", n);
pr_err("reserve %d failed, err=%d\n", n, err);
ret = err;
goto out; goto out;
} }
...@@ -2134,16 +1932,11 @@ static int igt_color(void *ignored) ...@@ -2134,16 +1932,11 @@ static int igt_color(void *ignored)
for (n = 1; n <= count; n++) { for (n = 1; n <= count; n++) {
node = kzalloc(sizeof(*node), GFP_KERNEL); node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node) { if (!node)
ret = -ENOMEM;
goto out; goto out;
}
if (!expect_insert(&mm, node, if (!expect_insert(test, &mm, node, n, n, n, mode)) {
n, n, n, KUNIT_FAIL(test, "%s insert failed, step %d\n", mode->name, n);
mode)) {
pr_err("%s insert failed, step %d\n",
mode->name, n);
kfree(node); kfree(node);
goto out; goto out;
} }
...@@ -2153,19 +1946,21 @@ static int igt_color(void *ignored) ...@@ -2153,19 +1946,21 @@ static int igt_color(void *ignored)
u64 rem; u64 rem;
if (node->color != node->size) { if (node->color != node->size) {
pr_err("%s invalid color stored: expected %lld, found %ld\n", KUNIT_FAIL(test,
mode->name, node->size, node->color); "%s invalid color stored: expected %lld, found %ld\n",
mode->name, node->size, node->color);
goto out; goto out;
} }
if (colors_abutt(node)) if (colors_abutt(test, node))
goto out; goto out;
div64_u64_rem(node->start, node->size, &rem); div64_u64_rem(node->start, node->size, &rem);
if (rem) { if (rem) {
pr_err("%s colored node misaligned, start=%llx expected alignment=%lld [rem=%lld]\n", KUNIT_FAIL(test,
mode->name, node->start, node->size, rem); "%s colored node misaligned, start=%llx expected alignment=%lld [rem=%lld]\n",
mode->name, node->start, node->size, rem);
goto out; goto out;
} }
...@@ -2176,25 +1971,18 @@ static int igt_color(void *ignored) ...@@ -2176,25 +1971,18 @@ static int igt_color(void *ignored)
cond_resched(); cond_resched();
} }
ret = 0;
out: out:
drm_mm_for_each_node_safe(node, nn, &mm) { drm_mm_for_each_node_safe(node, nn, &mm) {
drm_mm_remove_node(node); drm_mm_remove_node(node);
kfree(node); kfree(node);
} }
drm_mm_takedown(&mm); drm_mm_takedown(&mm);
return ret;
} }
static int evict_color(struct drm_mm *mm, static int evict_color(struct kunit *test, struct drm_mm *mm, u64 range_start,
u64 range_start, u64 range_end, u64 range_end, struct evict_node *nodes, unsigned int *order,
struct evict_node *nodes, unsigned int count, unsigned int size, unsigned int alignment,
unsigned int *order, unsigned long color, const struct insert_mode *mode)
unsigned int count,
unsigned int size,
unsigned int alignment,
unsigned long color,
const struct insert_mode *mode)
{ {
struct drm_mm_scan scan; struct drm_mm_scan scan;
LIST_HEAD(evict_list); LIST_HEAD(evict_list);
...@@ -2202,39 +1990,37 @@ static int evict_color(struct drm_mm *mm, ...@@ -2202,39 +1990,37 @@ static int evict_color(struct drm_mm *mm,
struct drm_mm_node tmp; struct drm_mm_node tmp;
int err; int err;
drm_mm_scan_init_with_range(&scan, mm, drm_mm_scan_init_with_range(&scan, mm, size, alignment, color, range_start,
size, alignment, color, range_end, mode->mode);
range_start, range_end, if (!evict_nodes(test, &scan, nodes, order, count, true, &evict_list))
mode->mode);
if (!evict_nodes(&scan,
nodes, order, count, true,
&evict_list))
return -EINVAL; return -EINVAL;
memset(&tmp, 0, sizeof(tmp)); memset(&tmp, 0, sizeof(tmp));
err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, color, err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, color,
DRM_MM_INSERT_EVICT); DRM_MM_INSERT_EVICT);
if (err) { if (err) {
pr_err("Failed to insert into eviction hole: size=%d, align=%d, color=%lu, err=%d\n", KUNIT_FAIL(test,
size, alignment, color, err); "Failed to insert into eviction hole: size=%d, align=%d, color=%lu, err=%d\n",
show_scan(&scan); size, alignment, color, err);
show_holes(mm, 3); show_scan(test, &scan);
show_holes(test, mm, 3);
return err; return err;
} }
if (tmp.start < range_start || tmp.start + tmp.size > range_end) { if (tmp.start < range_start || tmp.start + tmp.size > range_end) {
pr_err("Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n", KUNIT_FAIL(test,
tmp.start, tmp.size, range_start, range_end); "Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n",
tmp.start, tmp.size, range_start, range_end);
err = -EINVAL; err = -EINVAL;
} }
if (colors_abutt(&tmp)) if (colors_abutt(test, &tmp))
err = -EINVAL; err = -EINVAL;
if (!assert_node(&tmp, mm, size, alignment, color)) { if (!assert_node(test, &tmp, mm, size, alignment, color)) {
pr_err("Inserted did not fit the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx\n", KUNIT_FAIL(test,
tmp.size, size, "Inserted did not fit the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx\n",
alignment, misalignment(&tmp, alignment), tmp.start); tmp.size, size, alignment, misalignment(&tmp, alignment), tmp.start);
err = -EINVAL; err = -EINVAL;
} }
...@@ -2245,8 +2031,8 @@ static int evict_color(struct drm_mm *mm, ...@@ -2245,8 +2031,8 @@ static int evict_color(struct drm_mm *mm,
list_for_each_entry(e, &evict_list, link) { list_for_each_entry(e, &evict_list, link) {
err = drm_mm_reserve_node(mm, &e->node); err = drm_mm_reserve_node(mm, &e->node);
if (err) { if (err) {
pr_err("Failed to reinsert node after eviction: start=%llx\n", KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n",
e->node.start); e->node.start);
return err; return err;
} }
} }
...@@ -2255,7 +2041,7 @@ static int evict_color(struct drm_mm *mm, ...@@ -2255,7 +2041,7 @@ static int evict_color(struct drm_mm *mm,
return 0; return 0;
} }
static int igt_color_evict(void *ignored) static void igt_mm_color_evict(struct kunit *test)
{ {
DRM_RND_STATE(prng, random_seed); DRM_RND_STATE(prng, random_seed);
const unsigned int total_size = min(8192u, max_iterations); const unsigned int total_size = min(8192u, max_iterations);
...@@ -2265,7 +2051,6 @@ static int igt_color_evict(void *ignored) ...@@ -2265,7 +2051,6 @@ static int igt_color_evict(void *ignored)
struct evict_node *nodes; struct evict_node *nodes;
struct drm_mm_node *node, *next; struct drm_mm_node *node, *next;
unsigned int *order, n; unsigned int *order, n;
int ret, err;
/* Check that the drm_mm_scan also honours color adjustment when /* Check that the drm_mm_scan also honours color adjustment when
* choosing its victims to create a hole. Our color_adjust does not * choosing its victims to create a hole. Our color_adjust does not
...@@ -2273,23 +2058,20 @@ static int igt_color_evict(void *ignored) ...@@ -2273,23 +2058,20 @@ static int igt_color_evict(void *ignored)
* enlarging the set of victims that must be evicted. * enlarging the set of victims that must be evicted.
*/ */
ret = -ENOMEM;
nodes = vzalloc(array_size(total_size, sizeof(*nodes))); nodes = vzalloc(array_size(total_size, sizeof(*nodes)));
if (!nodes) KUNIT_ASSERT_TRUE(test, nodes);
goto err;
order = drm_random_order(total_size, &prng); order = drm_random_order(total_size, &prng);
if (!order) if (!order)
goto err_nodes; goto err_nodes;
ret = -EINVAL; drm_mm_init(&mm, 0, 2 * total_size - 1);
drm_mm_init(&mm, 0, 2*total_size - 1);
mm.color_adjust = separate_adjacent_colors; mm.color_adjust = separate_adjacent_colors;
for (n = 0; n < total_size; n++) { for (n = 0; n < total_size; n++) {
if (!expect_insert(&mm, &nodes[n].node, if (!expect_insert(test, &mm, &nodes[n].node,
1, 0, color++, 1, 0, color++,
&insert_modes[0])) { &insert_modes[0])) {
pr_err("insert failed, step %d\n", n); KUNIT_FAIL(test, "insert failed, step %d\n", n);
goto out; goto out;
} }
} }
...@@ -2297,26 +2079,19 @@ static int igt_color_evict(void *ignored) ...@@ -2297,26 +2079,19 @@ static int igt_color_evict(void *ignored)
for (mode = evict_modes; mode->name; mode++) { for (mode = evict_modes; mode->name; mode++) {
for (n = 1; n <= total_size; n <<= 1) { for (n = 1; n <= total_size; n <<= 1) {
drm_random_reorder(order, total_size, &prng); drm_random_reorder(order, total_size, &prng);
err = evict_color(&mm, 0, U64_MAX, if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size,
nodes, order, total_size, n, 1, color++, mode)) {
n, 1, color++, KUNIT_FAIL(test, "%s evict_color(size=%u) failed\n", mode->name, n);
mode);
if (err) {
pr_err("%s evict_color(size=%u) failed\n",
mode->name, n);
goto out; goto out;
} }
} }
for (n = 1; n < total_size; n <<= 1) { for (n = 1; n < total_size; n <<= 1) {
drm_random_reorder(order, total_size, &prng); drm_random_reorder(order, total_size, &prng);
err = evict_color(&mm, 0, U64_MAX, if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size,
nodes, order, total_size, total_size / 2, n, color++, mode)) {
total_size/2, n, color++, KUNIT_FAIL(test, "%s evict_color(size=%u, alignment=%u) failed\n",
mode); mode->name, total_size / 2, n);
if (err) {
pr_err("%s evict_color(size=%u, alignment=%u) failed\n",
mode->name, total_size/2, n);
goto out; goto out;
} }
} }
...@@ -2327,13 +2102,10 @@ static int igt_color_evict(void *ignored) ...@@ -2327,13 +2102,10 @@ static int igt_color_evict(void *ignored)
DRM_MM_BUG_ON(!nsize); DRM_MM_BUG_ON(!nsize);
drm_random_reorder(order, total_size, &prng); drm_random_reorder(order, total_size, &prng);
err = evict_color(&mm, 0, U64_MAX, if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size,
nodes, order, total_size, nsize, n, color++, mode)) {
nsize, n, color++, KUNIT_FAIL(test, "%s evict_color(size=%u, alignment=%u) failed\n",
mode); mode->name, nsize, n);
if (err) {
pr_err("%s evict_color(size=%u, alignment=%u) failed\n",
mode->name, nsize, n);
goto out; goto out;
} }
} }
...@@ -2341,21 +2113,16 @@ static int igt_color_evict(void *ignored) ...@@ -2341,21 +2113,16 @@ static int igt_color_evict(void *ignored)
cond_resched(); cond_resched();
} }
ret = 0;
out: out:
if (ret)
show_mm(&mm);
drm_mm_for_each_node_safe(node, next, &mm) drm_mm_for_each_node_safe(node, next, &mm)
drm_mm_remove_node(node); drm_mm_remove_node(node);
drm_mm_takedown(&mm); drm_mm_takedown(&mm);
kfree(order); kfree(order);
err_nodes: err_nodes:
vfree(nodes); vfree(nodes);
err:
return ret;
} }
static int igt_color_evict_range(void *ignored) static void igt_mm_color_evict_range(struct kunit *test)
{ {
DRM_RND_STATE(prng, random_seed); DRM_RND_STATE(prng, random_seed);
const unsigned int total_size = 8192; const unsigned int total_size = 8192;
...@@ -2368,29 +2135,25 @@ static int igt_color_evict_range(void *ignored) ...@@ -2368,29 +2135,25 @@ static int igt_color_evict_range(void *ignored)
struct evict_node *nodes; struct evict_node *nodes;
struct drm_mm_node *node, *next; struct drm_mm_node *node, *next;
unsigned int *order, n; unsigned int *order, n;
int ret, err;
/* Like igt_color_evict(), but limited to small portion of the full /* Like igt_color_evict(), but limited to small portion of the full
* drm_mm range. * drm_mm range.
*/ */
ret = -ENOMEM;
nodes = vzalloc(array_size(total_size, sizeof(*nodes))); nodes = vzalloc(array_size(total_size, sizeof(*nodes)));
if (!nodes) KUNIT_ASSERT_TRUE(test, nodes);
goto err;
order = drm_random_order(total_size, &prng); order = drm_random_order(total_size, &prng);
if (!order) if (!order)
goto err_nodes; goto err_nodes;
ret = -EINVAL; drm_mm_init(&mm, 0, 2 * total_size - 1);
drm_mm_init(&mm, 0, 2*total_size - 1);
mm.color_adjust = separate_adjacent_colors; mm.color_adjust = separate_adjacent_colors;
for (n = 0; n < total_size; n++) { for (n = 0; n < total_size; n++) {
if (!expect_insert(&mm, &nodes[n].node, if (!expect_insert(test, &mm, &nodes[n].node,
1, 0, color++, 1, 0, color++,
&insert_modes[0])) { &insert_modes[0])) {
pr_err("insert failed, step %d\n", n); KUNIT_FAIL(test, "insert failed, step %d\n", n);
goto out; goto out;
} }
} }
...@@ -2398,26 +2161,22 @@ static int igt_color_evict_range(void *ignored) ...@@ -2398,26 +2161,22 @@ static int igt_color_evict_range(void *ignored)
for (mode = evict_modes; mode->name; mode++) { for (mode = evict_modes; mode->name; mode++) {
for (n = 1; n <= range_size; n <<= 1) { for (n = 1; n <= range_size; n <<= 1) {
drm_random_reorder(order, range_size, &prng); drm_random_reorder(order, range_size, &prng);
err = evict_color(&mm, range_start, range_end, if (evict_color(test, &mm, range_start, range_end, nodes, order,
nodes, order, total_size, total_size, n, 1, color++, mode)) {
n, 1, color++, KUNIT_FAIL(test,
mode); "%s evict_color(size=%u) failed for range [%x, %x]\n",
if (err) { mode->name, n, range_start, range_end);
pr_err("%s evict_color(size=%u) failed for range [%x, %x]\n",
mode->name, n, range_start, range_end);
goto out; goto out;
} }
} }
for (n = 1; n < range_size; n <<= 1) { for (n = 1; n < range_size; n <<= 1) {
drm_random_reorder(order, total_size, &prng); drm_random_reorder(order, total_size, &prng);
err = evict_color(&mm, range_start, range_end, if (evict_color(test, &mm, range_start, range_end, nodes, order,
nodes, order, total_size, total_size, range_size / 2, n, color++, mode)) {
range_size/2, n, color++, KUNIT_FAIL(test,
mode); "%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n",
if (err) { mode->name, total_size / 2, n, range_start, range_end);
pr_err("%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n",
mode->name, total_size/2, n, range_start, range_end);
goto out; goto out;
} }
} }
...@@ -2428,13 +2187,11 @@ static int igt_color_evict_range(void *ignored) ...@@ -2428,13 +2187,11 @@ static int igt_color_evict_range(void *ignored)
DRM_MM_BUG_ON(!nsize); DRM_MM_BUG_ON(!nsize);
drm_random_reorder(order, total_size, &prng); drm_random_reorder(order, total_size, &prng);
err = evict_color(&mm, range_start, range_end, if (evict_color(test, &mm, range_start, range_end, nodes, order,
nodes, order, total_size, total_size, nsize, n, color++, mode)) {
nsize, n, color++, KUNIT_FAIL(test,
mode); "%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n",
if (err) { mode->name, nsize, n, range_start, range_end);
pr_err("%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n",
mode->name, nsize, n, range_start, range_end);
goto out; goto out;
} }
} }
...@@ -2442,46 +2199,57 @@ static int igt_color_evict_range(void *ignored) ...@@ -2442,46 +2199,57 @@ static int igt_color_evict_range(void *ignored)
cond_resched(); cond_resched();
} }
ret = 0;
out: out:
if (ret)
show_mm(&mm);
drm_mm_for_each_node_safe(node, next, &mm) drm_mm_for_each_node_safe(node, next, &mm)
drm_mm_remove_node(node); drm_mm_remove_node(node);
drm_mm_takedown(&mm); drm_mm_takedown(&mm);
kfree(order); kfree(order);
err_nodes: err_nodes:
vfree(nodes); vfree(nodes);
err:
return ret;
} }
#include "drm_selftest.c" static int drm_mm_init_test(struct kunit *test)
static int __init test_drm_mm_init(void)
{ {
int err;
while (!random_seed) while (!random_seed)
random_seed = get_random_int(); random_seed = get_random_int();
pr_info("Testing DRM range manager (struct drm_mm), with random_seed=0x%x max_iterations=%u max_prime=%u\n", return 0;
random_seed, max_iterations, max_prime);
err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL);
return err > 0 ? 0 : err;
}
static void __exit test_drm_mm_exit(void)
{
} }
module_init(test_drm_mm_init);
module_exit(test_drm_mm_exit);
module_param(random_seed, uint, 0400); module_param(random_seed, uint, 0400);
module_param(max_iterations, uint, 0400); module_param(max_iterations, uint, 0400);
module_param(max_prime, uint, 0400); module_param(max_prime, uint, 0400);
static struct kunit_case drm_mm_tests[] = {
KUNIT_CASE(igt_mm_init),
KUNIT_CASE(igt_mm_debug),
KUNIT_CASE(igt_mm_reserve),
KUNIT_CASE(igt_mm_insert),
KUNIT_CASE(igt_mm_replace),
KUNIT_CASE(igt_mm_insert_range),
KUNIT_CASE(igt_mm_frag),
KUNIT_CASE(igt_mm_align),
KUNIT_CASE(igt_mm_align32),
KUNIT_CASE(igt_mm_align64),
KUNIT_CASE(igt_mm_evict),
KUNIT_CASE(igt_mm_evict_range),
KUNIT_CASE(igt_mm_topdown),
KUNIT_CASE(igt_mm_bottomup),
KUNIT_CASE(igt_mm_lowest),
KUNIT_CASE(igt_mm_highest),
KUNIT_CASE(igt_mm_color),
KUNIT_CASE(igt_mm_color_evict),
KUNIT_CASE(igt_mm_color_evict_range),
{}
};
static struct kunit_suite drm_mm_test_suite = {
.name = "drm_mm",
.init = drm_mm_init_test,
.test_cases = drm_mm_tests,
};
kunit_test_suite(drm_mm_test_suite);
MODULE_AUTHOR("Intel Corporation"); MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment