Skip to content

Commit 313b38a

Browse files
tamirdkees
authored andcommitted
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>
1 parent 9ab6188 commit 313b38a

7 files changed

Lines changed: 109 additions & 77 deletions

File tree

lib/Kconfig.debug

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3235,6 +3235,20 @@ config GCD_KUNIT_TEST
32353235

32363236
If unsure, say N
32373237

3238+
config PRIME_NUMBERS_KUNIT_TEST
3239+
tristate "Prime number generator test" if !KUNIT_ALL_TESTS
3240+
depends on KUNIT
3241+
select PRIME_NUMBERS
3242+
default KUNIT_ALL_TESTS
3243+
help
3244+
This option enables the KUnit test suite for the {is,next}_prime_number
3245+
functions.
3246+
3247+
Enabling this option will include tests that compare the prime number
3248+
generator functions against a brute force implementation.
3249+
3250+
If unsure, say N
3251+
32383252
endif # RUNTIME_TESTING_MENU
32393253

32403254
config ARCH_USE_MEMTEST

lib/math/prime_numbers.c

Lines changed: 19 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
// SPDX-License-Identifier: GPL-2.0-only
2-
#define pr_fmt(fmt) "prime numbers: " fmt
32

43
#include <linux/module.h>
54
#include <linux/mutex.h>
65
#include <linux/prime_numbers.h>
76
#include <linux/slab.h>
87

9-
struct primes {
10-
struct rcu_head rcu;
11-
unsigned long last, sz;
12-
unsigned long primes[];
13-
};
8+
#include "prime_numbers_private.h"
149

1510
#if BITS_PER_LONG == 64
1611
static const struct primes small_primes = {
@@ -62,9 +57,25 @@ static const struct primes small_primes = {
6257
static DEFINE_MUTEX(lock);
6358
static const struct primes __rcu *primes = RCU_INITIALIZER(&small_primes);
6459

65-
static unsigned long selftest_max;
60+
#if IS_ENABLED(CONFIG_PRIME_NUMBERS_KUNIT_TEST)
61+
/*
62+
* Calls the callback under RCU lock. The callback must not retain
63+
* the primes pointer.
64+
*/
65+
void with_primes(void *ctx, primes_fn fn)
66+
{
67+
rcu_read_lock();
68+
fn(ctx, rcu_dereference(primes));
69+
rcu_read_unlock();
70+
}
71+
EXPORT_SYMBOL(with_primes);
72+
73+
EXPORT_SYMBOL(slow_is_prime_number);
6674

67-
static bool slow_is_prime_number(unsigned long x)
75+
#else
76+
static
77+
#endif
78+
bool slow_is_prime_number(unsigned long x)
6879
{
6980
unsigned long y = int_sqrt(x);
7081

@@ -239,77 +250,13 @@ bool is_prime_number(unsigned long x)
239250
}
240251
EXPORT_SYMBOL(is_prime_number);
241252

242-
static void dump_primes(void)
243-
{
244-
const struct primes *p;
245-
char *buf;
246-
247-
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
248-
249-
rcu_read_lock();
250-
p = rcu_dereference(primes);
251-
252-
if (buf)
253-
bitmap_print_to_pagebuf(true, buf, p->primes, p->sz);
254-
pr_info("primes.{last=%lu, .sz=%lu, .primes[]=...x%lx} = %s\n",
255-
p->last, p->sz, p->primes[BITS_TO_LONGS(p->sz) - 1], buf);
256-
257-
rcu_read_unlock();
258-
259-
kfree(buf);
260-
}
261-
262-
static int selftest(unsigned long max)
263-
{
264-
unsigned long x, last;
265-
266-
if (!max)
267-
return 0;
268-
269-
for (last = 0, x = 2; x < max; x++) {
270-
bool slow = slow_is_prime_number(x);
271-
bool fast = is_prime_number(x);
272-
273-
if (slow != fast) {
274-
pr_err("inconsistent result for is-prime(%lu): slow=%s, fast=%s!\n",
275-
x, slow ? "yes" : "no", fast ? "yes" : "no");
276-
goto err;
277-
}
278-
279-
if (!slow)
280-
continue;
281-
282-
if (next_prime_number(last) != x) {
283-
pr_err("incorrect result for next-prime(%lu): expected %lu, got %lu\n",
284-
last, x, next_prime_number(last));
285-
goto err;
286-
}
287-
last = x;
288-
}
289-
290-
pr_info("%s(%lu) passed, last prime was %lu\n", __func__, x, last);
291-
return 0;
292-
293-
err:
294-
dump_primes();
295-
return -EINVAL;
296-
}
297-
298-
static int __init primes_init(void)
299-
{
300-
return selftest(selftest_max);
301-
}
302-
303253
static void __exit primes_exit(void)
304254
{
305255
free_primes();
306256
}
307257

308-
module_init(primes_init);
309258
module_exit(primes_exit);
310259

311-
module_param_named(selftest, selftest_max, ulong, 0400);
312-
313260
MODULE_AUTHOR("Intel Corporation");
314261
MODULE_DESCRIPTION("Prime number library");
315262
MODULE_LICENSE("GPL");

lib/math/prime_numbers_private.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
3+
#include <linux/types.h>
4+
5+
struct primes {
6+
struct rcu_head rcu;
7+
unsigned long last, sz;
8+
unsigned long primes[];
9+
};
10+
11+
#if IS_ENABLED(CONFIG_PRIME_NUMBERS_KUNIT_TEST)
12+
typedef void (*primes_fn)(void *, const struct primes *);
13+
14+
void with_primes(void *ctx, primes_fn fn);
15+
bool slow_is_prime_number(unsigned long x);
16+
#endif

lib/math/tests/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ obj-$(CONFIG_GCD_KUNIT_TEST) += gcd_kunit.o
44
obj-$(CONFIG_INT_LOG_KUNIT_TEST) += int_log_kunit.o
55
obj-$(CONFIG_INT_POW_KUNIT_TEST) += int_pow_kunit.o
66
obj-$(CONFIG_INT_SQRT_KUNIT_TEST) += int_sqrt_kunit.o
7+
obj-$(CONFIG_PRIME_NUMBERS_KUNIT_TEST) += prime_numbers_kunit.o
78
obj-$(CONFIG_RATIONAL_KUNIT_TEST) += rational_kunit.o
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
3+
#include <kunit/test.h>
4+
#include <linux/module.h>
5+
#include <linux/prime_numbers.h>
6+
7+
#include "../prime_numbers_private.h"
8+
9+
static void dump_primes(void *ctx, const struct primes *p)
10+
{
11+
static char buf[PAGE_SIZE];
12+
struct kunit_suite *suite = ctx;
13+
14+
bitmap_print_to_pagebuf(true, buf, p->primes, p->sz);
15+
kunit_info(suite, "primes.{last=%lu, .sz=%lu, .primes[]=...x%lx} = %s",
16+
p->last, p->sz, p->primes[BITS_TO_LONGS(p->sz) - 1], buf);
17+
}
18+
19+
static void prime_numbers_test(struct kunit *test)
20+
{
21+
const unsigned long max = 65536;
22+
unsigned long x, last, next;
23+
24+
for (last = 0, x = 2; x < max; x++) {
25+
const bool slow = slow_is_prime_number(x);
26+
const bool fast = is_prime_number(x);
27+
28+
KUNIT_ASSERT_EQ_MSG(test, slow, fast, "is-prime(%lu)", x);
29+
30+
if (!slow)
31+
continue;
32+
33+
next = next_prime_number(last);
34+
KUNIT_ASSERT_EQ_MSG(test, next, x, "next-prime(%lu)", last);
35+
last = next;
36+
}
37+
}
38+
39+
static void kunit_suite_exit(struct kunit_suite *suite)
40+
{
41+
with_primes(suite, dump_primes);
42+
}
43+
44+
static struct kunit_case prime_numbers_cases[] = {
45+
KUNIT_CASE(prime_numbers_test),
46+
{},
47+
};
48+
49+
static struct kunit_suite prime_numbers_suite = {
50+
.name = "math-prime_numbers",
51+
.suite_exit = kunit_suite_exit,
52+
.test_cases = prime_numbers_cases,
53+
};
54+
55+
kunit_test_suite(prime_numbers_suite);
56+
57+
MODULE_AUTHOR("Intel Corporation");
58+
MODULE_DESCRIPTION("Prime number library");
59+
MODULE_LICENSE("GPL");

tools/testing/selftests/lib/config

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
CONFIG_TEST_PRINTF=m
22
CONFIG_TEST_SCANF=m
33
CONFIG_TEST_BITMAP=m
4-
CONFIG_PRIME_NUMBERS=m
54
CONFIG_TEST_BITOPS=m

tools/testing/selftests/lib/prime_numbers.sh

Lines changed: 0 additions & 4 deletions
This file was deleted.

0 commit comments

Comments
 (0)