Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Tamir Duberstein and committed by
Kees Cook
313b38a6 9ab61886

+109 -77
+14
lib/Kconfig.debug
··· 3235 3235 3236 3236 If unsure, say N 3237 3237 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 + 3238 3252 endif # RUNTIME_TESTING_MENU 3239 3253 3240 3254 config ARCH_USE_MEMTEST
+19 -72
lib/math/prime_numbers.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0-only 2 - #define pr_fmt(fmt) "prime numbers: " fmt 3 2 4 3 #include <linux/module.h> 5 4 #include <linux/mutex.h> 6 5 #include <linux/prime_numbers.h> 7 6 #include <linux/slab.h> 8 7 9 - struct primes { 10 - struct rcu_head rcu; 11 - unsigned long last, sz; 12 - unsigned long primes[]; 13 - }; 8 + #include "prime_numbers_private.h" 14 9 15 10 #if BITS_PER_LONG == 64 16 11 static const struct primes small_primes = { ··· 57 62 static DEFINE_MUTEX(lock); 58 63 static const struct primes __rcu *primes = RCU_INITIALIZER(&small_primes); 59 64 60 - static unsigned long selftest_max; 65 + #if IS_ENABLED(CONFIG_PRIME_NUMBERS_KUNIT_TEST) 66 + /* 67 + * Calls the callback under RCU lock. The callback must not retain 68 + * the primes pointer. 69 + */ 70 + void with_primes(void *ctx, primes_fn fn) 71 + { 72 + rcu_read_lock(); 73 + fn(ctx, rcu_dereference(primes)); 74 + rcu_read_unlock(); 75 + } 76 + EXPORT_SYMBOL(with_primes); 61 77 62 - static bool slow_is_prime_number(unsigned long x) 78 + EXPORT_SYMBOL(slow_is_prime_number); 79 + 80 + #else 81 + static 82 + #endif 83 + bool slow_is_prime_number(unsigned long x) 63 84 { 64 85 unsigned long y = int_sqrt(x); 65 86 ··· 250 239 } 251 240 EXPORT_SYMBOL(is_prime_number); 252 241 253 - static void dump_primes(void) 254 - { 255 - const struct primes *p; 256 - char *buf; 257 - 258 - buf = kmalloc(PAGE_SIZE, GFP_KERNEL); 259 - 260 - rcu_read_lock(); 261 - p = rcu_dereference(primes); 262 - 263 - if (buf) 264 - bitmap_print_to_pagebuf(true, buf, p->primes, p->sz); 265 - pr_info("primes.{last=%lu, .sz=%lu, .primes[]=...x%lx} = %s\n", 266 - p->last, p->sz, p->primes[BITS_TO_LONGS(p->sz) - 1], buf); 267 - 268 - rcu_read_unlock(); 269 - 270 - kfree(buf); 271 - } 272 - 273 - static int selftest(unsigned long max) 274 - { 275 - unsigned long x, last; 276 - 277 - if (!max) 278 - return 0; 279 - 280 - for (last = 0, x = 2; x < max; x++) { 281 - bool slow = slow_is_prime_number(x); 282 - bool fast = is_prime_number(x); 283 - 284 - if (slow != fast) { 285 - pr_err("inconsistent result for is-prime(%lu): slow=%s, fast=%s!\n", 286 - x, slow ? "yes" : "no", fast ? "yes" : "no"); 287 - goto err; 288 - } 289 - 290 - if (!slow) 291 - continue; 292 - 293 - if (next_prime_number(last) != x) { 294 - pr_err("incorrect result for next-prime(%lu): expected %lu, got %lu\n", 295 - last, x, next_prime_number(last)); 296 - goto err; 297 - } 298 - last = x; 299 - } 300 - 301 - pr_info("%s(%lu) passed, last prime was %lu\n", __func__, x, last); 302 - return 0; 303 - 304 - err: 305 - dump_primes(); 306 - return -EINVAL; 307 - } 308 - 309 - static int __init primes_init(void) 310 - { 311 - return selftest(selftest_max); 312 - } 313 - 314 242 static void __exit primes_exit(void) 315 243 { 316 244 free_primes(); 317 245 } 318 246 319 - module_init(primes_init); 320 247 module_exit(primes_exit); 321 - 322 - module_param_named(selftest, selftest_max, ulong, 0400); 323 248 324 249 MODULE_AUTHOR("Intel Corporation"); 325 250 MODULE_DESCRIPTION("Prime number library");
+16
lib/math/prime_numbers_private.h
··· 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
+1
lib/math/tests/Makefile
··· 4 4 obj-$(CONFIG_INT_LOG_KUNIT_TEST) += int_log_kunit.o 5 5 obj-$(CONFIG_INT_POW_KUNIT_TEST) += int_pow_kunit.o 6 6 obj-$(CONFIG_INT_SQRT_KUNIT_TEST) += int_sqrt_kunit.o 7 + obj-$(CONFIG_PRIME_NUMBERS_KUNIT_TEST) += prime_numbers_kunit.o 7 8 obj-$(CONFIG_RATIONAL_KUNIT_TEST) += rational_kunit.o
+59
lib/math/tests/prime_numbers_kunit.c
··· 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");
-1
tools/testing/selftests/lib/config
··· 1 1 CONFIG_TEST_PRINTF=m 2 2 CONFIG_TEST_SCANF=m 3 3 CONFIG_TEST_BITMAP=m 4 - CONFIG_PRIME_NUMBERS=m 5 4 CONFIG_TEST_BITOPS=m
-4
tools/testing/selftests/lib/prime_numbers.sh
··· 1 - #!/bin/sh 2 - # SPDX-License-Identifier: GPL-2.0 3 - # Checks fast/slow prime_number generation for inconsistencies 4 - $(dirname $0)/../kselftest/module.sh "prime numbers" prime_numbers selftest=65536