test_hash.c: refactor into kunit

Use KUnit framework to make tests more easily integrable with CIs.  Even
though these tests are not yet properly written as unit tests this
change should help in debugging.

Also remove kernel messages (i.e.  through pr_info) as KUnit handles all
debugging output and let it handle module init and exit details.

Link: https://lkml.kernel.org/r/20211208183711.390454-6-isabbasso@riseup.net
Reviewed-by: David Gow <davidgow@google.com>
Reported-by: kernel test robot <lkp@intel.com>
Tested-by: David Gow <davidgow@google.com>
Co-developed-by: Augusto Durães Camargo <augusto.duraes33@gmail.com>
Signed-off-by: Augusto Durães Camargo <augusto.duraes33@gmail.com>
Co-developed-by: Enzo Ferreira <ferreiraenzoa@gmail.com>
Signed-off-by: Enzo Ferreira <ferreiraenzoa@gmail.com>
Signed-off-by: Isabella Basso <isabbasso@riseup.net>
Cc: Brendan Higgins <brendanhiggins@google.com>
Cc: Daniel Latypov <dlatypov@google.com>
Cc: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Rodrigo Siqueira <rodrigosiqueiramelo@gmail.com>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Isabella Basso 2022-01-19 18:09:15 -08:00 committed by Linus Torvalds
parent 88168bf35c
commit 0acc968f35
3 changed files with 81 additions and 143 deletions

View File

@ -2207,15 +2207,6 @@ config TEST_RHASHTABLE
If unsure, say N. If unsure, say N.
config TEST_HASH
tristate "Perform selftest on hash functions"
help
Enable this option to test the kernel's integer (<linux/hash.h>), and
string (<linux/stringhash.h>) hash functions on boot (or module load).
This is intended to help people writing architecture-specific
optimized versions. If unsure, say N.
config TEST_SIPHASH config TEST_SIPHASH
tristate "Perform selftest on siphash functions" tristate "Perform selftest on siphash functions"
help help
@ -2364,6 +2355,25 @@ config BITFIELD_KUNIT
If unsure, say N. If unsure, say N.
config HASH_KUNIT_TEST
tristate "KUnit Test for integer hash functions" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
help
Enable this option to test the kernel's string (<linux/stringhash.h>), and
integer (<linux/hash.h>) hash functions on boot.
KUnit tests run during boot and output the results to the debug log
in TAP format (https://testanything.org/). Only useful for kernel devs
running the KUnit test harness, and not intended for inclusion into a
production build.
For more information on KUnit and unit tests in general please refer
to the KUnit documentation in Documentation/dev-tools/kunit/.
This is intended to help people writing architecture-specific
optimized versions. If unsure, say N.
config RESOURCE_KUNIT_TEST config RESOURCE_KUNIT_TEST
tristate "KUnit test for resource API" tristate "KUnit test for resource API"
depends on KUNIT depends on KUNIT

View File

@ -62,7 +62,7 @@ obj-$(CONFIG_TEST_BITOPS) += test_bitops.o
CFLAGS_test_bitops.o += -Werror CFLAGS_test_bitops.o += -Werror
obj-$(CONFIG_TEST_SYSCTL) += test_sysctl.o obj-$(CONFIG_TEST_SYSCTL) += test_sysctl.o
obj-$(CONFIG_TEST_SIPHASH) += test_siphash.o obj-$(CONFIG_TEST_SIPHASH) += test_siphash.o
obj-$(CONFIG_TEST_HASH) += test_hash.o obj-$(CONFIG_HASH_KUNIT_TEST) += test_hash.o
obj-$(CONFIG_TEST_IDA) += test_ida.o obj-$(CONFIG_TEST_IDA) += test_ida.o
obj-$(CONFIG_KASAN_KUNIT_TEST) += test_kasan.o obj-$(CONFIG_KASAN_KUNIT_TEST) += test_kasan.o
CFLAGS_test_kasan.o += -fno-builtin CFLAGS_test_kasan.o += -fno-builtin

View File

@ -14,17 +14,15 @@
* and hash_64(). * and hash_64().
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt "\n"
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/hash.h> #include <linux/hash.h>
#include <linux/stringhash.h> #include <linux/stringhash.h>
#include <linux/printk.h> #include <kunit/test.h>
/* 32-bit XORSHIFT generator. Seed must not be zero. */ /* 32-bit XORSHIFT generator. Seed must not be zero. */
static u32 __init __attribute_const__ static u32 __attribute_const__
xorshift(u32 seed) xorshift(u32 seed)
{ {
seed ^= seed << 13; seed ^= seed << 13;
@ -34,7 +32,7 @@ xorshift(u32 seed)
} }
/* Given a non-zero x, returns a non-zero byte. */ /* Given a non-zero x, returns a non-zero byte. */
static u8 __init __attribute_const__ static u8 __attribute_const__
mod255(u32 x) mod255(u32 x)
{ {
x = (x & 0xffff) + (x >> 16); /* 1 <= x <= 0x1fffe */ x = (x & 0xffff) + (x >> 16); /* 1 <= x <= 0x1fffe */
@ -45,8 +43,7 @@ mod255(u32 x)
} }
/* Fill the buffer with non-zero bytes. */ /* Fill the buffer with non-zero bytes. */
static void __init static void fill_buf(char *buf, size_t len, u32 seed)
fill_buf(char *buf, size_t len, u32 seed)
{ {
size_t i; size_t i;
@ -71,40 +68,32 @@ struct test_hash_params {
}; };
#ifdef HAVE_ARCH__HASH_32 #ifdef HAVE_ARCH__HASH_32
static bool __init static void
test_int__hash_32(struct test_hash_params *params) test_int__hash_32(struct kunit *test, struct test_hash_params *params)
{ {
params->hash_or[1][0] |= params->h2 = __hash_32_generic(params->h0); params->hash_or[1][0] |= params->h2 = __hash_32_generic(params->h0);
#if HAVE_ARCH__HASH_32 == 1 #if HAVE_ARCH__HASH_32 == 1
if (params->h1 != params->h2) { KUNIT_EXPECT_EQ_MSG(test, params->h1, params->h2,
pr_err("__hash_32(%#x) = %#x != __hash_32_generic() = %#x", "__hash_32(%#x) = %#x != __hash_32_generic() = %#x",
params->h0, params->h1, params->h2); params->h0, params->h1, params->h2);
return false;
}
#endif #endif
return true;
} }
#endif #endif
#ifdef HAVE_ARCH_HASH_64 #ifdef HAVE_ARCH_HASH_64
static bool __init static void
test_int_hash_64(struct test_hash_params *params, u32 const *m, int *k) test_int_hash_64(struct kunit *test, struct test_hash_params *params, u32 const *m, int *k)
{ {
params->h2 = hash_64_generic(*params->h64, *k); params->h2 = hash_64_generic(*params->h64, *k);
#if HAVE_ARCH_HASH_64 == 1 #if HAVE_ARCH_HASH_64 == 1
if (params->h1 != params->h2) { KUNIT_EXPECT_EQ_MSG(test, params->h1, params->h2,
pr_err("hash_64(%#llx, %d) = %#x != hash_64_generic() = %#x", "hash_64(%#llx, %d) = %#x != hash_64_generic() = %#x",
*params->h64, *k, params->h1, params->h2); *params->h64, *k, params->h1, params->h2);
return false;
}
#else #else
if (params->h2 > *m) { KUNIT_EXPECT_LE_MSG(test, params->h1, params->h2,
pr_err("hash_64_generic(%#llx, %d) = %#x > %#x", "hash_64_generic(%#llx, %d) = %#x > %#x",
*params->h64, *k, params->h1, *m); *params->h64, *k, params->h1, *m);
return false;
}
#endif #endif
return true;
} }
#endif #endif
@ -117,8 +106,8 @@ test_int_hash_64(struct test_hash_params *params, u32 const *m, int *k)
* inline, the code being tested is actually in the module, and you can * inline, the code being tested is actually in the module, and you can
* recompile and re-test the module without rebooting. * recompile and re-test the module without rebooting.
*/ */
static bool __init static void
test_int_hash(unsigned long long h64, u32 hash_or[2][33]) test_int_hash(struct kunit *test, unsigned long long h64, u32 hash_or[2][33])
{ {
int k; int k;
struct test_hash_params params = { &h64, (u32)h64, 0, 0, hash_or }; struct test_hash_params params = { &h64, (u32)h64, 0, 0, hash_or };
@ -126,8 +115,7 @@ test_int_hash(unsigned long long h64, u32 hash_or[2][33])
/* Test __hash32 */ /* Test __hash32 */
hash_or[0][0] |= params.h1 = __hash_32(params.h0); hash_or[0][0] |= params.h1 = __hash_32(params.h0);
#ifdef HAVE_ARCH__HASH_32 #ifdef HAVE_ARCH__HASH_32
if (!test_int__hash_32(&params)) test_int__hash_32(test, &params);
return false;
#endif #endif
/* Test k = 1..32 bits */ /* Test k = 1..32 bits */
@ -136,29 +124,24 @@ test_int_hash(unsigned long long h64, u32 hash_or[2][33])
/* Test hash_32 */ /* Test hash_32 */
hash_or[0][k] |= params.h1 = hash_32(params.h0, k); hash_or[0][k] |= params.h1 = hash_32(params.h0, k);
if (params.h1 > m) { KUNIT_EXPECT_LE_MSG(test, params.h1, m,
pr_err("hash_32(%#x, %d) = %#x > %#x", params.h0, k, params.h1, m); "hash_32(%#x, %d) = %#x > %#x",
return false; params.h0, k, params.h1, m);
}
/* Test hash_64 */ /* Test hash_64 */
hash_or[1][k] |= params.h1 = hash_64(h64, k); hash_or[1][k] |= params.h1 = hash_64(h64, k);
if (params.h1 > m) { KUNIT_EXPECT_LE_MSG(test, params.h1, m,
pr_err("hash_64(%#llx, %d) = %#x > %#x", h64, k, params.h1, m); "hash_64(%#llx, %d) = %#x > %#x",
return false; h64, k, params.h1, m);
}
#ifdef HAVE_ARCH_HASH_64 #ifdef HAVE_ARCH_HASH_64
if (!test_int_hash_64(&params, &m, &k)) test_int_hash_64(test, &params, &m, &k);
return false;
#endif #endif
} }
return true;
} }
#define SIZE 256 /* Run time is cubic in SIZE */ #define SIZE 256 /* Run time is cubic in SIZE */
static int __init test_string_or(void) static void test_string_or(struct kunit *test)
{ {
char buf[SIZE+1]; char buf[SIZE+1];
u32 string_or = 0; u32 string_or = 0;
@ -178,20 +161,15 @@ static int __init test_string_or(void)
} /* j */ } /* j */
/* The OR of all the hash values should cover all the bits */ /* The OR of all the hash values should cover all the bits */
if (~string_or) { KUNIT_EXPECT_EQ_MSG(test, string_or, -1u,
pr_err("OR of all string hash results = %#x != %#x", "OR of all string hash results = %#x != %#x",
string_or, -1u); string_or, -1u);
return -EINVAL;
} }
return 0; static void test_hash_or(struct kunit *test)
}
static int __init test_hash_or(void)
{ {
char buf[SIZE+1]; char buf[SIZE+1];
u32 hash_or[2][33] = { { 0, } }; u32 hash_or[2][33] = { { 0, } };
unsigned tests = 0;
unsigned long long h64 = 0; unsigned long long h64 = 0;
int i, j; int i, j;
@ -206,39 +184,27 @@ static int __init test_hash_or(void)
u32 h0 = full_name_hash(buf+i, buf+i, j-i); u32 h0 = full_name_hash(buf+i, buf+i, j-i);
/* Check that hashlen_string gets the length right */ /* Check that hashlen_string gets the length right */
if (hashlen_len(hashlen) != j-i) { KUNIT_EXPECT_EQ_MSG(test, hashlen_len(hashlen), j-i,
pr_err("hashlen_string(%d..%d) returned length" "hashlen_string(%d..%d) returned length %u, expected %d",
" %u, expected %d",
i, j, hashlen_len(hashlen), j-i); i, j, hashlen_len(hashlen), j-i);
return -EINVAL;
}
/* Check that the hashes match */ /* Check that the hashes match */
if (hashlen_hash(hashlen) != h0) { KUNIT_EXPECT_EQ_MSG(test, hashlen_hash(hashlen), h0,
pr_err("hashlen_string(%d..%d) = %08x != " "hashlen_string(%d..%d) = %08x != full_name_hash() = %08x",
"full_name_hash() = %08x",
i, j, hashlen_hash(hashlen), h0); i, j, hashlen_hash(hashlen), h0);
return -EINVAL;
}
h64 = h64 << 32 | h0; /* For use with hash_64 */ h64 = h64 << 32 | h0; /* For use with hash_64 */
if (!test_int_hash(h64, hash_or)) test_int_hash(test, h64, hash_or);
return -EINVAL;
tests++;
} /* i */ } /* i */
} /* j */ } /* j */
if (~hash_or[0][0]) { KUNIT_EXPECT_EQ_MSG(test, hash_or[0][0], -1u,
pr_err("OR of all __hash_32 results = %#x != %#x", "OR of all __hash_32 results = %#x != %#x",
hash_or[0][0], -1u); hash_or[0][0], -1u);
return -EINVAL;
}
#ifdef HAVE_ARCH__HASH_32 #ifdef HAVE_ARCH__HASH_32
#if HAVE_ARCH__HASH_32 != 1 /* Test is pointless if results match */ #if HAVE_ARCH__HASH_32 != 1 /* Test is pointless if results match */
if (~hash_or[1][0]) { KUNIT_EXPECT_EQ_MSG(test, hash_or[1][0], -1u,
pr_err("OR of all __hash_32_generic results = %#x != %#x", "OR of all __hash_32_generic results = %#x != %#x",
hash_or[1][0], -1u); hash_or[1][0], -1u);
return -EINVAL;
}
#endif #endif
#endif #endif
@ -246,65 +212,27 @@ static int __init test_hash_or(void)
for (i = 1; i <= 32; i++) { for (i = 1; i <= 32; i++) {
u32 const m = ((u32)2 << (i-1)) - 1; /* Low i bits set */ u32 const m = ((u32)2 << (i-1)) - 1; /* Low i bits set */
if (hash_or[0][i] != m) { KUNIT_EXPECT_EQ_MSG(test, hash_or[0][i], m,
pr_err("OR of all hash_32(%d) results = %#x " "OR of all hash_32(%d) results = %#x (%#x expected)",
"(%#x expected)", i, hash_or[0][i], m); i, hash_or[0][i], m);
return -EINVAL; KUNIT_EXPECT_EQ_MSG(test, hash_or[1][i], m,
} "OR of all hash_64(%d) results = %#x (%#x expected)",
if (hash_or[1][i] != m) { i, hash_or[1][i], m);
pr_err("OR of all hash_64(%d) results = %#x "
"(%#x expected)", i, hash_or[1][i], m);
return -EINVAL;
} }
} }
pr_notice("%u tests passed.", tests); static struct kunit_case hash_test_cases[] __refdata = {
KUNIT_CASE(test_string_or),
KUNIT_CASE(test_hash_or),
{}
};
return 0; static struct kunit_suite hash_test_suite = {
} .name = "hash",
.test_cases = hash_test_cases,
};
static void __init notice_skipped_tests(void)
{
/* Issue notices about skipped tests. */
#ifdef HAVE_ARCH__HASH_32
#if HAVE_ARCH__HASH_32 != 1
pr_info("__hash_32() is arch-specific; not compared to generic.");
#endif
#else
pr_info("__hash_32() has no arch implementation to test.");
#endif
#ifdef HAVE_ARCH_HASH_64
#if HAVE_ARCH_HASH_64 != 1
pr_info("hash_64() is arch-specific; not compared to generic.");
#endif
#else
pr_info("hash_64() has no arch implementation to test.");
#endif
}
static int __init kunit_test_suite(hash_test_suite);
test_hash_init(void)
{
int ret;
ret = test_string_or();
if (ret < 0)
return ret;
ret = test_hash_or();
if (ret < 0)
return ret;
notice_skipped_tests();
return ret;
}
static void __exit test_hash_exit(void)
{
}
module_init(test_hash_init); /* Does everything */
module_exit(test_hash_exit); /* Does nothing */
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");