Commit 313b38a6 authored by Tamir Duberstein's avatar Tamir Duberstein Committed by Kees Cook
Browse files

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: default avatarTamir Duberstein <tamird@gmail.com>
Link: https://lore.kernel.org/r/20250208-prime_numbers-kunit-convert-v5-2-b0cb82ae7c7d@gmail.com


Signed-off-by: default avatarKees Cook <kees@kernel.org>
parent 9ab61886
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -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
+19 −72
Original line number Diff line number Diff line
// 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);

EXPORT_SYMBOL(slow_is_prime_number);

static bool slow_is_prime_number(unsigned long x)
#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 −0
Original line number Diff line number Diff line
/* 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
+1 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
// 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");
Loading