Re: [PATCH 2/2] lib/prime_numbers: convert self-test to KUnit

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, 8 Feb 2025 at 06:33, Tamir Duberstein <tamird@xxxxxxxxx> wrote:
>
> 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>
> ---

I'm not totally sold on moving everything into the
prime_numbers_private.h file, as we could end up with duplicate copies
of the small primes list and associated variables.

Would it not make more sense to keep as many of these private as
possible, and only export slow_is_prime_number() -- perhaps
conditionally if the test is enabled --, and use that from the test.
The lists of primes (both the small primes list and the rcu-controlled
larger cache) seem to me to still be implementation details, and the
test itself should share the existing ones.

(And, if we wanted the test to keep its own independent versions of
these, we'd still be in trouble, as the prime number library and the
test might access separate versions of the lists, if they're static to
separate files/modules.)

The only tricky bit would be handling dump_primes(): that could be
done either by exporting (perhaps conditionally / in a namespace) the
prime lists, and having dump_primes() be a part of the test, or
exporting dump_primes() -- which would be simpler, but make it harder
to use the kunit log functions.

Thoughts?

Cheers,
-- David

>  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();

I'd argue that we should keep this here: the primes should be freed
when the prime number library exits, not the test.

> -}
> -
> -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,

Should we be freeing the primes when the test exits? I suspect we
should leave them in case any other part of the kernel needs them.

(This, of course, potentially makes the test more brittle if run
multiple times (or on a system with already initialised primes), but
seems right to me.


> +       .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
>

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature


[Index of Archives]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Share Photos]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]

  Powered by Linux