mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
lib/prime_numbers: convert self-test to KUnit
Extract a private header and convert the prime_numbers self-test to a KUnit test. I considered parameterizing the test using `KUNIT_CASE_PARAM` but didn't see how it was possible since the test logic is entangled with the test parameter generation logic. Signed-off-by: Tamir Duberstein <tamird@gmail.com> Link: https://lore.kernel.org/r/20250208-prime_numbers-kunit-convert-v5-2-b0cb82ae7c7d@gmail.com Signed-off-by: Kees Cook <kees@kernel.org>
This commit is contained in:
parent
9ab61886ac
commit
313b38a6ec
7 changed files with 109 additions and 77 deletions
|
@ -3235,6 +3235,20 @@ config GCD_KUNIT_TEST
|
||||||
|
|
||||||
If unsure, say N
|
If unsure, say N
|
||||||
|
|
||||||
|
config PRIME_NUMBERS_KUNIT_TEST
|
||||||
|
tristate "Prime number generator test" if !KUNIT_ALL_TESTS
|
||||||
|
depends on KUNIT
|
||||||
|
select PRIME_NUMBERS
|
||||||
|
default KUNIT_ALL_TESTS
|
||||||
|
help
|
||||||
|
This option enables the KUnit test suite for the {is,next}_prime_number
|
||||||
|
functions.
|
||||||
|
|
||||||
|
Enabling this option will include tests that compare the prime number
|
||||||
|
generator functions against a brute force implementation.
|
||||||
|
|
||||||
|
If unsure, say N
|
||||||
|
|
||||||
endif # RUNTIME_TESTING_MENU
|
endif # RUNTIME_TESTING_MENU
|
||||||
|
|
||||||
config ARCH_USE_MEMTEST
|
config ARCH_USE_MEMTEST
|
||||||
|
|
|
@ -1,16 +1,11 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
#define pr_fmt(fmt) "prime numbers: " fmt
|
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/mutex.h>
|
#include <linux/mutex.h>
|
||||||
#include <linux/prime_numbers.h>
|
#include <linux/prime_numbers.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
struct primes {
|
#include "prime_numbers_private.h"
|
||||||
struct rcu_head rcu;
|
|
||||||
unsigned long last, sz;
|
|
||||||
unsigned long primes[];
|
|
||||||
};
|
|
||||||
|
|
||||||
#if BITS_PER_LONG == 64
|
#if BITS_PER_LONG == 64
|
||||||
static const struct primes small_primes = {
|
static const struct primes small_primes = {
|
||||||
|
@ -62,9 +57,25 @@ static const struct primes small_primes = {
|
||||||
static DEFINE_MUTEX(lock);
|
static DEFINE_MUTEX(lock);
|
||||||
static const struct primes __rcu *primes = RCU_INITIALIZER(&small_primes);
|
static const struct primes __rcu *primes = RCU_INITIALIZER(&small_primes);
|
||||||
|
|
||||||
static unsigned long selftest_max;
|
#if IS_ENABLED(CONFIG_PRIME_NUMBERS_KUNIT_TEST)
|
||||||
|
/*
|
||||||
|
* Calls the callback under RCU lock. The callback must not retain
|
||||||
|
* the primes pointer.
|
||||||
|
*/
|
||||||
|
void with_primes(void *ctx, primes_fn fn)
|
||||||
|
{
|
||||||
|
rcu_read_lock();
|
||||||
|
fn(ctx, rcu_dereference(primes));
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(with_primes);
|
||||||
|
|
||||||
static bool slow_is_prime_number(unsigned long x)
|
EXPORT_SYMBOL(slow_is_prime_number);
|
||||||
|
|
||||||
|
#else
|
||||||
|
static
|
||||||
|
#endif
|
||||||
|
bool slow_is_prime_number(unsigned long x)
|
||||||
{
|
{
|
||||||
unsigned long y = int_sqrt(x);
|
unsigned long y = int_sqrt(x);
|
||||||
|
|
||||||
|
@ -239,77 +250,13 @@ bool is_prime_number(unsigned long x)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(is_prime_number);
|
EXPORT_SYMBOL(is_prime_number);
|
||||||
|
|
||||||
static void dump_primes(void)
|
|
||||||
{
|
|
||||||
const struct primes *p;
|
|
||||||
char *buf;
|
|
||||||
|
|
||||||
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
|
|
||||||
|
|
||||||
rcu_read_lock();
|
|
||||||
p = rcu_dereference(primes);
|
|
||||||
|
|
||||||
if (buf)
|
|
||||||
bitmap_print_to_pagebuf(true, buf, p->primes, p->sz);
|
|
||||||
pr_info("primes.{last=%lu, .sz=%lu, .primes[]=...x%lx} = %s\n",
|
|
||||||
p->last, p->sz, p->primes[BITS_TO_LONGS(p->sz) - 1], buf);
|
|
||||||
|
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
kfree(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int selftest(unsigned long max)
|
|
||||||
{
|
|
||||||
unsigned long x, last;
|
|
||||||
|
|
||||||
if (!max)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for (last = 0, x = 2; x < max; x++) {
|
|
||||||
bool slow = slow_is_prime_number(x);
|
|
||||||
bool fast = is_prime_number(x);
|
|
||||||
|
|
||||||
if (slow != fast) {
|
|
||||||
pr_err("inconsistent result for is-prime(%lu): slow=%s, fast=%s!\n",
|
|
||||||
x, slow ? "yes" : "no", fast ? "yes" : "no");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!slow)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (next_prime_number(last) != x) {
|
|
||||||
pr_err("incorrect result for next-prime(%lu): expected %lu, got %lu\n",
|
|
||||||
last, x, next_prime_number(last));
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
last = x;
|
|
||||||
}
|
|
||||||
|
|
||||||
pr_info("%s(%lu) passed, last prime was %lu\n", __func__, x, last);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err:
|
|
||||||
dump_primes();
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int __init primes_init(void)
|
|
||||||
{
|
|
||||||
return selftest(selftest_max);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit primes_exit(void)
|
static void __exit primes_exit(void)
|
||||||
{
|
{
|
||||||
free_primes();
|
free_primes();
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(primes_init);
|
|
||||||
module_exit(primes_exit);
|
module_exit(primes_exit);
|
||||||
|
|
||||||
module_param_named(selftest, selftest_max, ulong, 0400);
|
|
||||||
|
|
||||||
MODULE_AUTHOR("Intel Corporation");
|
MODULE_AUTHOR("Intel Corporation");
|
||||||
MODULE_DESCRIPTION("Prime number library");
|
MODULE_DESCRIPTION("Prime number library");
|
||||||
MODULE_LICENSE("GPL");
|
MODULE_LICENSE("GPL");
|
||||||
|
|
16
lib/math/prime_numbers_private.h
Normal file
16
lib/math/prime_numbers_private.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
struct primes {
|
||||||
|
struct rcu_head rcu;
|
||||||
|
unsigned long last, sz;
|
||||||
|
unsigned long primes[];
|
||||||
|
};
|
||||||
|
|
||||||
|
#if IS_ENABLED(CONFIG_PRIME_NUMBERS_KUNIT_TEST)
|
||||||
|
typedef void (*primes_fn)(void *, const struct primes *);
|
||||||
|
|
||||||
|
void with_primes(void *ctx, primes_fn fn);
|
||||||
|
bool slow_is_prime_number(unsigned long x);
|
||||||
|
#endif
|
|
@ -4,4 +4,5 @@ obj-$(CONFIG_GCD_KUNIT_TEST) += gcd_kunit.o
|
||||||
obj-$(CONFIG_INT_LOG_KUNIT_TEST) += int_log_kunit.o
|
obj-$(CONFIG_INT_LOG_KUNIT_TEST) += int_log_kunit.o
|
||||||
obj-$(CONFIG_INT_POW_KUNIT_TEST) += int_pow_kunit.o
|
obj-$(CONFIG_INT_POW_KUNIT_TEST) += int_pow_kunit.o
|
||||||
obj-$(CONFIG_INT_SQRT_KUNIT_TEST) += int_sqrt_kunit.o
|
obj-$(CONFIG_INT_SQRT_KUNIT_TEST) += int_sqrt_kunit.o
|
||||||
|
obj-$(CONFIG_PRIME_NUMBERS_KUNIT_TEST) += prime_numbers_kunit.o
|
||||||
obj-$(CONFIG_RATIONAL_KUNIT_TEST) += rational_kunit.o
|
obj-$(CONFIG_RATIONAL_KUNIT_TEST) += rational_kunit.o
|
||||||
|
|
59
lib/math/tests/prime_numbers_kunit.c
Normal file
59
lib/math/tests/prime_numbers_kunit.c
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
|
||||||
|
#include <kunit/test.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/prime_numbers.h>
|
||||||
|
|
||||||
|
#include "../prime_numbers_private.h"
|
||||||
|
|
||||||
|
static void dump_primes(void *ctx, const struct primes *p)
|
||||||
|
{
|
||||||
|
static char buf[PAGE_SIZE];
|
||||||
|
struct kunit_suite *suite = ctx;
|
||||||
|
|
||||||
|
bitmap_print_to_pagebuf(true, buf, p->primes, p->sz);
|
||||||
|
kunit_info(suite, "primes.{last=%lu, .sz=%lu, .primes[]=...x%lx} = %s",
|
||||||
|
p->last, p->sz, p->primes[BITS_TO_LONGS(p->sz) - 1], buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prime_numbers_test(struct kunit *test)
|
||||||
|
{
|
||||||
|
const unsigned long max = 65536;
|
||||||
|
unsigned long x, last, next;
|
||||||
|
|
||||||
|
for (last = 0, x = 2; x < max; x++) {
|
||||||
|
const bool slow = slow_is_prime_number(x);
|
||||||
|
const bool fast = is_prime_number(x);
|
||||||
|
|
||||||
|
KUNIT_ASSERT_EQ_MSG(test, slow, fast, "is-prime(%lu)", x);
|
||||||
|
|
||||||
|
if (!slow)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
next = next_prime_number(last);
|
||||||
|
KUNIT_ASSERT_EQ_MSG(test, next, x, "next-prime(%lu)", last);
|
||||||
|
last = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void kunit_suite_exit(struct kunit_suite *suite)
|
||||||
|
{
|
||||||
|
with_primes(suite, dump_primes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kunit_case prime_numbers_cases[] = {
|
||||||
|
KUNIT_CASE(prime_numbers_test),
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct kunit_suite prime_numbers_suite = {
|
||||||
|
.name = "math-prime_numbers",
|
||||||
|
.suite_exit = kunit_suite_exit,
|
||||||
|
.test_cases = prime_numbers_cases,
|
||||||
|
};
|
||||||
|
|
||||||
|
kunit_test_suite(prime_numbers_suite);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Intel Corporation");
|
||||||
|
MODULE_DESCRIPTION("Prime number library");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -1,5 +1,4 @@
|
||||||
CONFIG_TEST_PRINTF=m
|
CONFIG_TEST_PRINTF=m
|
||||||
CONFIG_TEST_SCANF=m
|
CONFIG_TEST_SCANF=m
|
||||||
CONFIG_TEST_BITMAP=m
|
CONFIG_TEST_BITMAP=m
|
||||||
CONFIG_PRIME_NUMBERS=m
|
|
||||||
CONFIG_TEST_BITOPS=m
|
CONFIG_TEST_BITOPS=m
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
# SPDX-License-Identifier: GPL-2.0
|
|
||||||
# Checks fast/slow prime_number generation for inconsistencies
|
|
||||||
$(dirname $0)/../kselftest/module.sh "prime numbers" prime_numbers selftest=65536
|
|
Loading…
Add table
Reference in a new issue