[PATCH 2/2] lib/prime_numbers: convert self-test to KUnit
From: Tamir Duberstein
Date: Fri Feb 07 2025 - 17:33:50 EST
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@xxxxxxxxx>
---
lib/Kconfig.debug | 14 +++
lib/math/prime_numbers.c | 151 +--------------------------
lib/math/prime_numbers_private.h | 64 ++++++++++++
lib/math/tests/Makefile | 1 +
lib/math/tests/prime_numbers_kunit.c | 92 ++++++++++++++++
tools/testing/selftests/lib/config | 1 -
tools/testing/selftests/lib/prime_numbers.sh | 4 -
7 files changed, 173 insertions(+), 154 deletions(-)
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 1af972a92d06..616beaca4a2b 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -3197,6 +3197,20 @@ config INT_SQRT_KUNIT_TEST
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
config ARCH_USE_MEMTEST
diff --git a/lib/math/prime_numbers.c b/lib/math/prime_numbers.c
index 9a17ee9af93a..0842b0826672 100644
--- a/lib/math/prime_numbers.c
+++ b/lib/math/prime_numbers.c
@@ -1,70 +1,11 @@
// SPDX-License-Identifier: GPL-2.0-only
-#define pr_fmt(fmt) "prime numbers: " fmt
-#include <linux/module.h>
-#include <linux/mutex.h>
#include <linux/prime_numbers.h>
#include <linux/slab.h>
-struct primes {
- struct rcu_head rcu;
- unsigned long last, sz;
- unsigned long primes[];
-};
+#include "prime_numbers_private.h"
-#if BITS_PER_LONG == 64
-static const struct primes small_primes = {
- .last = 61,
- .sz = 64,
- .primes = {
- BIT(2) |
- BIT(3) |
- BIT(5) |
- BIT(7) |
- BIT(11) |
- BIT(13) |
- BIT(17) |
- BIT(19) |
- BIT(23) |
- BIT(29) |
- BIT(31) |
- BIT(37) |
- BIT(41) |
- BIT(43) |
- BIT(47) |
- BIT(53) |
- BIT(59) |
- BIT(61)
- }
-};
-#elif BITS_PER_LONG == 32
-static const struct primes small_primes = {
- .last = 31,
- .sz = 32,
- .primes = {
- BIT(2) |
- BIT(3) |
- BIT(5) |
- BIT(7) |
- BIT(11) |
- BIT(13) |
- BIT(17) |
- BIT(19) |
- BIT(23) |
- BIT(29) |
- BIT(31)
- }
-};
-#else
-#error "unhandled BITS_PER_LONG"
-#endif
-
-static DEFINE_MUTEX(lock);
-static const struct primes __rcu *primes = RCU_INITIALIZER(&small_primes);
-
-static unsigned long selftest_max;
-
-static bool slow_is_prime_number(unsigned long x)
+bool slow_is_prime_number(unsigned long x)
{
unsigned long y = int_sqrt(x);
@@ -156,19 +97,6 @@ static bool expand_to_next_prime(unsigned long x)
return true;
}
-static void free_primes(void)
-{
- const struct primes *p;
-
- mutex_lock(&lock);
- p = rcu_dereference_protected(primes, lockdep_is_held(&lock));
- if (p != &small_primes) {
- rcu_assign_pointer(primes, &small_primes);
- kfree_rcu((struct primes *)p, rcu);
- }
- mutex_unlock(&lock);
-}
-
/**
* next_prime_number - return the next prime number
* @x: the starting point for searching to test
@@ -238,78 +166,3 @@ bool is_prime_number(unsigned long x)
return result;
}
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)
-{
- free_primes();
-}
-
-module_init(primes_init);
-module_exit(primes_exit);
-
-module_param_named(selftest, selftest_max, ulong, 0400);
-
-MODULE_AUTHOR("Intel Corporation");
-MODULE_DESCRIPTION("Prime number library");
-MODULE_LICENSE("GPL");
diff --git a/lib/math/prime_numbers_private.h b/lib/math/prime_numbers_private.h
new file mode 100644
index 000000000000..d0da5584aee8
--- /dev/null
+++ b/lib/math/prime_numbers_private.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <linux/bits.h>
+#include <linux/mutex.h>
+#include <linux/rcupdate.h>
+#include <linux/types.h>
+
+struct primes {
+ struct rcu_head rcu;
+ unsigned long last, sz;
+ unsigned long primes[];
+};
+
+#if BITS_PER_LONG == 64
+static const struct primes small_primes = {
+ .last = 61,
+ .sz = 64,
+ .primes = {
+ BIT(2) |
+ BIT(3) |
+ BIT(5) |
+ BIT(7) |
+ BIT(11) |
+ BIT(13) |
+ BIT(17) |
+ BIT(19) |
+ BIT(23) |
+ BIT(29) |
+ BIT(31) |
+ BIT(37) |
+ BIT(41) |
+ BIT(43) |
+ BIT(47) |
+ BIT(53) |
+ BIT(59) |
+ BIT(61)
+ }
+};
+#elif BITS_PER_LONG == 32
+static const struct primes small_primes = {
+ .last = 31,
+ .sz = 32,
+ .primes = {
+ BIT(2) |
+ BIT(3) |
+ BIT(5) |
+ BIT(7) |
+ BIT(11) |
+ BIT(13) |
+ BIT(17) |
+ BIT(19) |
+ BIT(23) |
+ BIT(29) |
+ BIT(31)
+ }
+};
+#else
+#error "unhandled BITS_PER_LONG"
+#endif
+
+static const struct primes __rcu *primes = RCU_INITIALIZER(&small_primes);
+static DEFINE_MUTEX(lock);
+
+bool slow_is_prime_number(unsigned long x);
diff --git a/lib/math/tests/Makefile b/lib/math/tests/Makefile
index e1a79f093b2d..da21a592c75a 100644
--- a/lib/math/tests/Makefile
+++ b/lib/math/tests/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_INT_POW_TEST) += int_pow_kunit.o
obj-$(CONFIG_INT_SQRT_KUNIT_TEST) += int_sqrt_kunit.o
+obj-$(CONFIG_PRIME_NUMBERS_KUNIT_TEST) += prime_numbers_kunit.o
diff --git a/lib/math/tests/prime_numbers_kunit.c b/lib/math/tests/prime_numbers_kunit.c
new file mode 100644
index 000000000000..8b0884887f20
--- /dev/null
+++ b/lib/math/tests/prime_numbers_kunit.c
@@ -0,0 +1,92 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <kunit/test.h>
+#include <linux/module.h>
+#include <linux/prime_numbers.h>
+#include <linux/slab.h>
+
+#include "../prime_numbers_private.h"
+
+static void free_primes(struct kunit_suite *suite)
+{
+ const struct primes *p;
+
+ mutex_lock(&lock);
+ p = rcu_dereference_protected(primes, lockdep_is_held(&lock));
+ if (p != &small_primes) {
+ rcu_assign_pointer(primes, &small_primes);
+ kfree_rcu((struct primes *)p, rcu);
+ }
+ mutex_unlock(&lock);
+}
+
+static void dump_primes(struct kunit *test)
+{
+ 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);
+ kunit_info(test, "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 void prime_numbers_test(struct kunit *test)
+{
+ const unsigned long max = 65536;
+ unsigned long x, last;
+
+ for (last = 0, x = 2; x < max; x++) {
+ bool slow = slow_is_prime_number(x);
+ bool fast = is_prime_number(x);
+
+ if (slow != fast) {
+ KUNIT_FAIL(test,
+ "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) {
+ KUNIT_FAIL(test,
+ "incorrect result for next-prime(%lu): expected %lu, got %lu\n",
+ last, x, next_prime_number(last));
+ goto err;
+ }
+ last = x;
+ }
+
+ kunit_info(test, "%s(%lu) passed, last prime was %lu\n", __func__, x, last);
+
+err:
+ dump_primes(test);
+}
+
+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 = free_primes,
+ .test_cases = prime_numbers_cases,
+};
+
+kunit_test_suite(prime_numbers_suite);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("Prime number library");
+MODULE_LICENSE("GPL");
diff --git a/tools/testing/selftests/lib/config b/tools/testing/selftests/lib/config
index dc15aba8d0a3..306a3d4dca98 100644
--- a/tools/testing/selftests/lib/config
+++ b/tools/testing/selftests/lib/config
@@ -1,5 +1,4 @@
CONFIG_TEST_PRINTF=m
CONFIG_TEST_SCANF=m
CONFIG_TEST_BITMAP=m
-CONFIG_PRIME_NUMBERS=m
CONFIG_TEST_BITOPS=m
diff --git a/tools/testing/selftests/lib/prime_numbers.sh b/tools/testing/selftests/lib/prime_numbers.sh
deleted file mode 100755
index 370b79a9cb2e..000000000000
--- a/tools/testing/selftests/lib/prime_numbers.sh
+++ /dev/null
@@ -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
--
2.48.1