mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02: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
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,16 +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 = {
 | 
			
		||||
| 
						 | 
				
			
			@ -62,9 +57,25 @@ static const struct primes small_primes = {
 | 
			
		|||
static DEFINE_MUTEX(lock);
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -239,77 +250,13 @@ bool is_prime_number(unsigned long x)
 | 
			
		|||
}
 | 
			
		||||
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");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										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_POW_KUNIT_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
 | 
			
		||||
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_SCANF=m
 | 
			
		||||
CONFIG_TEST_BITMAP=m
 | 
			
		||||
CONFIG_PRIME_NUMBERS=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…
	
		Reference in a new issue