2019-05-19 12:08:20 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2016-05-27 02:11:51 +00:00
|
|
|
/*
|
|
|
|
* Test cases for <linux/hash.h> and <linux/stringhash.h>
|
|
|
|
* This just verifies that various ways of computing a hash
|
|
|
|
* produce the same thing and, for cases where a k-bit hash
|
|
|
|
* value is requested, is of the requested size.
|
|
|
|
*
|
|
|
|
* We fill a buffer with a 255-byte null-terminated string,
|
|
|
|
* and use both full_name_hash() and hashlen_string() to hash the
|
|
|
|
* substrings from i to j, where 0 <= i < j < 256.
|
|
|
|
*
|
|
|
|
* The returned values are used to check that __hash_32() and
|
|
|
|
* __hash_32_generic() compute the same thing. Likewise hash_32()
|
|
|
|
* and hash_64().
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt "\n"
|
|
|
|
|
|
|
|
#include <linux/compiler.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/hash.h>
|
|
|
|
#include <linux/stringhash.h>
|
|
|
|
#include <linux/printk.h>
|
|
|
|
|
|
|
|
/* 32-bit XORSHIFT generator. Seed must not be zero. */
|
|
|
|
static u32 __init __attribute_const__
|
|
|
|
xorshift(u32 seed)
|
|
|
|
{
|
|
|
|
seed ^= seed << 13;
|
|
|
|
seed ^= seed >> 17;
|
|
|
|
seed ^= seed << 5;
|
|
|
|
return seed;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Given a non-zero x, returns a non-zero byte. */
|
|
|
|
static u8 __init __attribute_const__
|
|
|
|
mod255(u32 x)
|
|
|
|
{
|
|
|
|
x = (x & 0xffff) + (x >> 16); /* 1 <= x <= 0x1fffe */
|
|
|
|
x = (x & 0xff) + (x >> 8); /* 1 <= x <= 0x2fd */
|
|
|
|
x = (x & 0xff) + (x >> 8); /* 1 <= x <= 0x100 */
|
|
|
|
x = (x & 0xff) + (x >> 8); /* 1 <= x <= 0xff */
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fill the buffer with non-zero bytes. */
|
|
|
|
static void __init
|
|
|
|
fill_buf(char *buf, size_t len, u32 seed)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < len; i++) {
|
|
|
|
seed = xorshift(seed);
|
|
|
|
buf[i] = mod255(seed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-20 02:09:05 +00:00
|
|
|
/* Holds most testing variables for the int test. */
|
|
|
|
struct test_hash_params {
|
|
|
|
/* Pointer to integer to be hashed. */
|
|
|
|
unsigned long long *h64;
|
|
|
|
/* Low 32-bits of integer to be hashed. */
|
|
|
|
u32 h0;
|
|
|
|
/* Arch-specific hash result. */
|
|
|
|
u32 h1;
|
|
|
|
/* Generic hash result. */
|
|
|
|
u32 h2;
|
|
|
|
/* ORed hashes of given size (in bits). */
|
|
|
|
u32 (*hash_or)[33];
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef HAVE_ARCH__HASH_32
|
|
|
|
static bool __init
|
|
|
|
test_int__hash_32(struct test_hash_params *params)
|
|
|
|
{
|
|
|
|
params->hash_or[1][0] |= params->h2 = __hash_32_generic(params->h0);
|
|
|
|
#if HAVE_ARCH__HASH_32 == 1
|
|
|
|
if (params->h1 != params->h2) {
|
|
|
|
pr_err("__hash_32(%#x) = %#x != __hash_32_generic() = %#x",
|
|
|
|
params->h0, params->h1, params->h2);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_ARCH_HASH_64
|
|
|
|
static bool __init
|
|
|
|
test_int_hash_64(struct test_hash_params *params, u32 const *m, int *k)
|
|
|
|
{
|
|
|
|
params->h2 = hash_64_generic(*params->h64, *k);
|
|
|
|
#if HAVE_ARCH_HASH_64 == 1
|
|
|
|
if (params->h1 != params->h2) {
|
|
|
|
pr_err("hash_64(%#llx, %d) = %#x != hash_64_generic() = %#x",
|
|
|
|
*params->h64, *k, params->h1, params->h2);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (params->h2 > *m) {
|
|
|
|
pr_err("hash_64_generic(%#llx, %d) = %#x > %#x",
|
|
|
|
*params->h64, *k, params->h1, *m);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-05-27 02:11:51 +00:00
|
|
|
/*
|
|
|
|
* Test the various integer hash functions. h64 (or its low-order bits)
|
|
|
|
* is the integer to hash. hash_or accumulates the OR of the hash values,
|
|
|
|
* which are later checked to see that they cover all the requested bits.
|
|
|
|
*
|
|
|
|
* Because these functions (as opposed to the string hashes) are all
|
|
|
|
* inline, the code being tested is actually in the module, and you can
|
|
|
|
* recompile and re-test the module without rebooting.
|
|
|
|
*/
|
|
|
|
static bool __init
|
|
|
|
test_int_hash(unsigned long long h64, u32 hash_or[2][33])
|
|
|
|
{
|
|
|
|
int k;
|
2022-01-20 02:09:05 +00:00
|
|
|
struct test_hash_params params = { &h64, (u32)h64, 0, 0, hash_or };
|
2016-05-27 02:11:51 +00:00
|
|
|
|
|
|
|
/* Test __hash32 */
|
2022-01-20 02:09:05 +00:00
|
|
|
hash_or[0][0] |= params.h1 = __hash_32(params.h0);
|
2016-05-27 02:11:51 +00:00
|
|
|
#ifdef HAVE_ARCH__HASH_32
|
2022-01-20 02:09:05 +00:00
|
|
|
if (!test_int__hash_32(¶ms))
|
2016-05-27 02:11:51 +00:00
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Test k = 1..32 bits */
|
|
|
|
for (k = 1; k <= 32; k++) {
|
|
|
|
u32 const m = ((u32)2 << (k-1)) - 1; /* Low k bits set */
|
|
|
|
|
|
|
|
/* Test hash_32 */
|
2022-01-20 02:09:05 +00:00
|
|
|
hash_or[0][k] |= params.h1 = hash_32(params.h0, k);
|
|
|
|
if (params.h1 > m) {
|
|
|
|
pr_err("hash_32(%#x, %d) = %#x > %#x", params.h0, k, params.h1, m);
|
2016-05-27 02:11:51 +00:00
|
|
|
return false;
|
|
|
|
}
|
hash.h: remove unused define directive
Patch series "test_hash.c: refactor into KUnit", v3.
We refactored the lib/test_hash.c file into KUnit as part of the student
group LKCAMP [1] introductory hackathon for kernel development.
This test was pointed to our group by Daniel Latypov [2], so its full
conversion into a pure KUnit test was our goal in this patch series, but
we ran into many problems relating to it not being split as unit tests,
which complicated matters a bit, as the reasoning behind the original
tests is quite cryptic for those unfamiliar with hash implementations.
Some interesting developments we'd like to highlight are:
- In patch 1/5 we noticed that there was an unused define directive
that could be removed.
- In patch 4/5 we noticed how stringhash and hash tests are all under
the lib/test_hash.c file, which might cause some confusion, and we
also broke those kernel config entries up.
Overall KUnit developments have been made in the other patches in this
series:
In patches 2/5, 3/5 and 5/5 we refactored the lib/test_hash.c file so as
to make it more compatible with the KUnit style, whilst preserving the
original idea of the maintainer who designed it (i.e. George Spelvin),
which might be undesirable for unit tests, but we assume it is enough
for a first patch.
This patch (of 5):
Currently, there exist hash_32() and __hash_32() functions, which were
introduced in a patch [1] targeting architecture specific optimizations.
These functions can be overridden on a per-architecture basis to achieve
such optimizations. They must set their corresponding define directive
(HAVE_ARCH_HASH_32 and HAVE_ARCH__HASH_32, respectively) so that header
files can deal with these overrides properly.
As the supported 32-bit architectures that have their own hash function
implementation (i.e. m68k, Microblaze, H8/300, pa-risc) have only been
making use of the (more general) __hash_32() function (which only lacks
a right shift operation when compared to the hash_32() function), remove
the define directive corresponding to the arch-specific hash_32()
implementation.
[1] https://lore.kernel.org/lkml/20160525073311.5600.qmail@ns.sciencehorizons.net/
[akpm@linux-foundation.org: hash_32_generic() becomes hash_32()]
Link: https://lkml.kernel.org/r/20211208183711.390454-1-isabbasso@riseup.net
Link: https://lkml.kernel.org/r/20211208183711.390454-2-isabbasso@riseup.net
Reviewed-by: David Gow <davidgow@google.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: Geert Uytterhoeven <geert@linux-m68k.org>
Cc: Brendan Higgins <brendanhiggins@google.com>
Cc: Daniel Latypov <dlatypov@google.com>
Cc: Shuah Khan <skhan@linuxfoundation.org>
Cc: Rodrigo Siqueira <rodrigosiqueiramelo@gmail.com>
Cc: kernel test robot <lkp@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2022-01-20 02:09:02 +00:00
|
|
|
|
2016-05-27 02:11:51 +00:00
|
|
|
/* Test hash_64 */
|
2022-01-20 02:09:05 +00:00
|
|
|
hash_or[1][k] |= params.h1 = hash_64(h64, k);
|
|
|
|
if (params.h1 > m) {
|
|
|
|
pr_err("hash_64(%#llx, %d) = %#x > %#x", h64, k, params.h1, m);
|
2016-05-27 02:11:51 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#ifdef HAVE_ARCH_HASH_64
|
2022-01-20 02:09:05 +00:00
|
|
|
if (!test_int_hash_64(¶ms, &m, &k))
|
2016-05-27 02:11:51 +00:00
|
|
|
return false;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SIZE 256 /* Run time is cubic in SIZE */
|
|
|
|
|
2022-01-20 02:09:09 +00:00
|
|
|
static int __init test_string_or(void)
|
2016-05-27 02:11:51 +00:00
|
|
|
{
|
|
|
|
char buf[SIZE+1];
|
2022-01-20 02:09:09 +00:00
|
|
|
u32 string_or = 0;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
fill_buf(buf, SIZE, 1);
|
|
|
|
|
|
|
|
/* Test every possible non-empty substring in the buffer. */
|
|
|
|
for (j = SIZE; j > 0; --j) {
|
|
|
|
buf[j] = '\0';
|
|
|
|
|
|
|
|
for (i = 0; i <= j; i++) {
|
|
|
|
u32 h0 = full_name_hash(buf+i, buf+i, j-i);
|
|
|
|
|
|
|
|
string_or |= h0;
|
|
|
|
} /* i */
|
|
|
|
} /* j */
|
|
|
|
|
|
|
|
/* The OR of all the hash values should cover all the bits */
|
|
|
|
if (~string_or) {
|
|
|
|
pr_err("OR of all string hash results = %#x != %#x",
|
|
|
|
string_or, -1u);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __init test_hash_or(void)
|
|
|
|
{
|
|
|
|
char buf[SIZE+1];
|
|
|
|
u32 hash_or[2][33] = { { 0, } };
|
2016-05-27 02:11:51 +00:00
|
|
|
unsigned tests = 0;
|
|
|
|
unsigned long long h64 = 0;
|
|
|
|
int i, j;
|
|
|
|
|
|
|
|
fill_buf(buf, SIZE, 1);
|
|
|
|
|
|
|
|
/* Test every possible non-empty substring in the buffer. */
|
|
|
|
for (j = SIZE; j > 0; --j) {
|
|
|
|
buf[j] = '\0';
|
|
|
|
|
|
|
|
for (i = 0; i <= j; i++) {
|
2016-06-10 14:51:30 +00:00
|
|
|
u64 hashlen = hashlen_string(buf+i, buf+i);
|
|
|
|
u32 h0 = full_name_hash(buf+i, buf+i, j-i);
|
2016-05-27 02:11:51 +00:00
|
|
|
|
|
|
|
/* Check that hashlen_string gets the length right */
|
|
|
|
if (hashlen_len(hashlen) != j-i) {
|
|
|
|
pr_err("hashlen_string(%d..%d) returned length"
|
|
|
|
" %u, expected %d",
|
|
|
|
i, j, hashlen_len(hashlen), j-i);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
/* Check that the hashes match */
|
|
|
|
if (hashlen_hash(hashlen) != h0) {
|
|
|
|
pr_err("hashlen_string(%d..%d) = %08x != "
|
|
|
|
"full_name_hash() = %08x",
|
|
|
|
i, j, hashlen_hash(hashlen), h0);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
h64 = h64 << 32 | h0; /* For use with hash_64 */
|
|
|
|
if (!test_int_hash(h64, hash_or))
|
|
|
|
return -EINVAL;
|
|
|
|
tests++;
|
|
|
|
} /* i */
|
|
|
|
} /* j */
|
|
|
|
|
|
|
|
if (~hash_or[0][0]) {
|
|
|
|
pr_err("OR of all __hash_32 results = %#x != %#x",
|
|
|
|
hash_or[0][0], -1u);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
#ifdef HAVE_ARCH__HASH_32
|
|
|
|
#if HAVE_ARCH__HASH_32 != 1 /* Test is pointless if results match */
|
|
|
|
if (~hash_or[1][0]) {
|
|
|
|
pr_err("OR of all __hash_32_generic results = %#x != %#x",
|
|
|
|
hash_or[1][0], -1u);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Likewise for all the i-bit hash values */
|
|
|
|
for (i = 1; i <= 32; i++) {
|
|
|
|
u32 const m = ((u32)2 << (i-1)) - 1; /* Low i bits set */
|
|
|
|
|
|
|
|
if (hash_or[0][i] != m) {
|
|
|
|
pr_err("OR of all hash_32(%d) results = %#x "
|
|
|
|
"(%#x expected)", i, hash_or[0][i], m);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-20 02:09:09 +00:00
|
|
|
pr_notice("%u tests passed.", tests);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __init notice_skipped_tests(void)
|
|
|
|
{
|
2016-05-27 02:11:51 +00:00
|
|
|
/* Issue notices about skipped tests. */
|
2016-09-01 23:14:53 +00:00
|
|
|
#ifdef HAVE_ARCH__HASH_32
|
|
|
|
#if HAVE_ARCH__HASH_32 != 1
|
2016-05-27 02:11:51 +00:00
|
|
|
pr_info("__hash_32() is arch-specific; not compared to generic.");
|
|
|
|
#endif
|
2016-09-01 23:14:53 +00:00
|
|
|
#else
|
|
|
|
pr_info("__hash_32() has no arch implementation to test.");
|
|
|
|
#endif
|
|
|
|
#ifdef HAVE_ARCH_HASH_64
|
|
|
|
#if HAVE_ARCH_HASH_64 != 1
|
2016-05-27 02:11:51 +00:00
|
|
|
pr_info("hash_64() is arch-specific; not compared to generic.");
|
|
|
|
#endif
|
2016-09-01 23:14:53 +00:00
|
|
|
#else
|
|
|
|
pr_info("hash_64() has no arch implementation to test.");
|
|
|
|
#endif
|
2022-01-20 02:09:09 +00:00
|
|
|
}
|
2016-05-27 02:11:51 +00:00
|
|
|
|
2022-01-20 02:09:09 +00:00
|
|
|
static int __init
|
|
|
|
test_hash_init(void)
|
|
|
|
{
|
|
|
|
int ret;
|
2016-05-27 02:11:51 +00:00
|
|
|
|
2022-01-20 02:09:09 +00:00
|
|
|
ret = test_string_or();
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
ret = test_hash_or();
|
|
|
|
if (ret < 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
notice_skipped_tests();
|
|
|
|
|
|
|
|
return ret;
|
2016-05-27 02:11:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit test_hash_exit(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
module_init(test_hash_init); /* Does everything */
|
|
|
|
module_exit(test_hash_exit); /* Does nothing */
|
|
|
|
|
|
|
|
MODULE_LICENSE("GPL");
|