All of lore.kernel.org
 help / color / mirror / Atom feed
From: Daniel Vetter <daniel@ffwll.ch>
To: Chris Wilson <chris@chris-wilson.co.uk>
Cc: intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org
Subject: Re: [PATCH v10] lib: Add a simple prime number generator
Date: Tue, 27 Dec 2016 12:31:42 +0100	[thread overview]
Message-ID: <20161227113142.GG19172@dvetter-linux.ger.corp.intel.com> (raw)
In-Reply-To: <20161222144514.3911-1-chris@chris-wilson.co.uk>

On Thu, Dec 22, 2016 at 02:45:14PM +0000, Chris Wilson wrote:
> Prime numbers are interesting for testing components that use multiplies
> and divides, such as testing DRM's struct drm_mm alignment computations.
> 
> v2: Move to lib/, add selftest
> v3: Fix initial constants (exclude 0/1 from being primes)
> v4: More RCU markup to keep 0day/sparse happy
> v5: Fix RCU unwind on module exit, add to kselftests
> v6: Tidy computation of bitmap size
> v7: for_each_prime_number_from()
> v8: Compose small-primes using BIT() for easier verification
> v9: Move rcu dance entirely into callers.
> v10: Improve quote for Betrand's Postulate (aka Chebyshev's theorem)
> 
> Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Lukas Wunner <lukas@wunner.de>
> Reviewed-by: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
> ---
>  include/linux/prime_numbers.h                |  37 ++++
>  lib/Kconfig                                  |   7 +
>  lib/Makefile                                 |   2 +
>  lib/prime_numbers.c                          | 314 +++++++++++++++++++++++++++
>  tools/testing/selftests/lib/prime_numbers.sh |  15 ++

You typed all that nice kernel-doc, but no stanza in any .rst to pull it
in. Can you pls fix that up in a follow-up, cc: linux-doc@vger?
-Daniel

>  5 files changed, 375 insertions(+)
>  create mode 100644 include/linux/prime_numbers.h
>  create mode 100644 lib/prime_numbers.c
>  create mode 100755 tools/testing/selftests/lib/prime_numbers.sh
> 
> diff --git a/include/linux/prime_numbers.h b/include/linux/prime_numbers.h
> new file mode 100644
> index 000000000000..14ec4f567342
> --- /dev/null
> +++ b/include/linux/prime_numbers.h
> @@ -0,0 +1,37 @@
> +#ifndef __LINUX_PRIME_NUMBERS_H
> +#define __LINUX_PRIME_NUMBERS_H
> +
> +#include <linux/types.h>
> +
> +bool is_prime_number(unsigned long x);
> +unsigned long next_prime_number(unsigned long x);
> +
> +/**
> + * for_each_prime_number - iterate over each prime upto a value
> + * @prime: the current prime number in this iteration
> + * @max: the upper limit
> + *
> + * Starting from the first prime number 2 iterate over each prime number up to
> + * the @max value. On each iteration, @prime is set to the current prime number.
> + * @max should be less than ULONG_MAX to ensure termination. To begin with
> + * @prime set to 1 on the first iteration use for_each_prime_number_from()
> + * instead.
> + */
> +#define for_each_prime_number(prime, max) \
> +	for_each_prime_number_from((prime), 2, (max))
> +
> +/**
> + * for_each_prime_number_from - iterate over each prime upto a value
> + * @prime: the current prime number in this iteration
> + * @from: the initial value
> + * @max: the upper limit
> + *
> + * Starting from @from iterate over each successive prime number up to the
> + * @max value. On each iteration, @prime is set to the current prime number.
> + * @max should be less than ULONG_MAX, and @from less than @max, to ensure
> + * termination.
> + */
> +#define for_each_prime_number_from(prime, from, max) \
> +	for (prime = (from); prime <= (max); prime = next_prime_number(prime))
> +
> +#endif /* !__LINUX_PRIME_NUMBERS_H */
> diff --git a/lib/Kconfig b/lib/Kconfig
> index 260a80e313b9..1788a1f50d28 100644
> --- a/lib/Kconfig
> +++ b/lib/Kconfig
> @@ -550,4 +550,11 @@ config STACKDEPOT
>  config SBITMAP
>  	bool
>  
> +config PRIME_NUMBERS
> +	tristate "Prime number generator"
> +	default n
> +	help
> +	  Provides a helper module to generate prime numbers. Useful for writing
> +	  test code, especially when checking multiplication and divison.
> +
>  endmenu
> diff --git a/lib/Makefile b/lib/Makefile
> index 50144a3aeebd..c664143fd917 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -197,6 +197,8 @@ obj-$(CONFIG_ASN1) += asn1_decoder.o
>  
>  obj-$(CONFIG_FONT_SUPPORT) += fonts/
>  
> +obj-$(CONFIG_PRIME_NUMBERS) += prime_numbers.o
> +
>  hostprogs-y	:= gen_crc32table
>  clean-files	:= crc32table.h
>  
> diff --git a/lib/prime_numbers.c b/lib/prime_numbers.c
> new file mode 100644
> index 000000000000..c9b3c29614aa
> --- /dev/null
> +++ b/lib/prime_numbers.c
> @@ -0,0 +1,314 @@
> +#define pr_fmt(fmt) "prime numbers: " fmt "\n"
> +
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/prime_numbers.h>
> +#include <linux/slab.h>
> +
> +#define bitmap_size(nbits) (BITS_TO_LONGS(nbits) * sizeof(unsigned long))
> +
> +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 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)
> +{
> +	unsigned long y = int_sqrt(x);
> +
> +	while (y > 1) {
> +		if ((x % y) == 0)
> +			break;
> +		y--;
> +	}
> +
> +	return y == 1;
> +}
> +
> +static unsigned long slow_next_prime_number(unsigned long x)
> +{
> +	while (x < ULONG_MAX && !slow_is_prime_number(++x))
> +		;
> +
> +	return x;
> +}
> +
> +static unsigned long clear_multiples(unsigned long x,
> +				     unsigned long *p,
> +				     unsigned long start,
> +				     unsigned long end)
> +{
> +	unsigned long m;
> +
> +	m = 2 * x;
> +	if (m < start)
> +		m = roundup(start, x);
> +
> +	while (m < end) {
> +		__clear_bit(m, p);
> +		m += x;
> +	}
> +
> +	return x;
> +}
> +
> +static bool expand_to_next_prime(unsigned long x)
> +{
> +	const struct primes *p;
> +	struct primes *new;
> +	unsigned long sz, y;
> +
> +	/* Betrand's Postulate (or Chebyshev's theorem) states that if n > 3,
> +	 * there is always at least one prime p between n and 2n - 2.
> +	 * Equivalently, if n > 1, then there is always at least one prime p
> +	 * such that n < p < 2n.
> +	 *
> +	 * http://mathworld.wolfram.com/BertrandsPostulate.html
> +	 * https://en.wikipedia.org/wiki/Bertrand's_postulate
> +	 */
> +	sz = 2 * x;
> +	if (sz < x)
> +		return false;
> +
> +	sz = round_up(sz, BITS_PER_LONG);
> +	new = kmalloc(sizeof(*new) + bitmap_size(sz), GFP_KERNEL);
> +	if (!new)
> +		return false;
> +
> +	mutex_lock(&lock);
> +	p = rcu_dereference_protected(primes, lockdep_is_held(&lock));
> +	if (x < p->last) {
> +		kfree(new);
> +		goto unlock;
> +	}
> +
> +	/* Where memory permits, track the primes using the
> +	 * Sieve of Eratosthenes. The sieve is to remove all multiples of known
> +	 * primes from the set, what remains in the set is therefore prime.
> +	 */
> +	bitmap_fill(new->primes, sz);
> +	bitmap_copy(new->primes, p->primes, p->sz);
> +	for (y = 2UL; y < sz; y = find_next_bit(new->primes, sz, y + 1))
> +		new->last = clear_multiples(y, new->primes, p->sz, sz);
> +	new->sz = sz;
> +
> +	BUG_ON(new->last <= x);
> +
> +	rcu_assign_pointer(primes, new);
> +	if (p != &small_primes)
> +		kfree_rcu((struct primes *)p, rcu);
> +
> +unlock:
> +	mutex_unlock(&lock);
> +	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
> + *
> + * A prime number is an integer greater than 1 that is only divisible by
> + * itself and 1.  The set of prime numbers is computed using the Sieve of
> + * Eratoshenes (on finding a prime, all multiples of that prime are removed
> + * from the set) enabling a fast lookup of the next prime number larger than
> + * @x. If the sieve fails (memory limitation), the search falls back to using
> + * slow trial-divison, up to the value of ULONG_MAX (which is reported as the
> + * final prime as a sentinel).
> + *
> + * Returns: the next prime number larger than @x
> + */
> +unsigned long next_prime_number(unsigned long x)
> +{
> +	const struct primes *p;
> +
> +	rcu_read_lock();
> +	p = rcu_dereference(primes);
> +	while (x >= p->last) {
> +		rcu_read_unlock();
> +
> +		if (!expand_to_next_prime(x))
> +			return slow_next_prime_number(x);
> +
> +		rcu_read_lock();
> +		p = rcu_dereference(primes);
> +	}
> +	x = find_next_bit(p->primes, p->last, x + 1);
> +	rcu_read_unlock();
> +
> +	return x;
> +}
> +EXPORT_SYMBOL(next_prime_number);
> +
> +/**
> + * is_prime_number - test whether the given number is prime
> + * @x: the number to test
> + *
> + * A prime number is an integer greater than 1 that is only divisible by
> + * itself and 1. Internally a cache of prime numbers is kept (to speed up
> + * searching for sequential primes, see next_prime_number()), but if the number
> + * falls outside of that cache, its primality is tested using trial-divison.
> + *
> + * Returns: true if @x is prime, false for composite numbers.
> + */
> +bool is_prime_number(unsigned long x)
> +{
> +	const struct primes *p;
> +	bool result;
> +
> +	rcu_read_lock();
> +	p = rcu_dereference(primes);
> +	while (x >= p->sz) {
> +		rcu_read_unlock();
> +
> +		if (!expand_to_next_prime(x))
> +			return slow_is_prime_number(x);
> +
> +		rcu_read_lock();
> +		p = rcu_dereference(primes);
> +	}
> +	result = test_bit(x, p->primes);
> +	rcu_read_unlock();
> +
> +	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",
> +		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!",
> +			       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",
> +			       last, x, next_prime_number(last));
> +			goto err;
> +		}
> +		last = x;
> +	}
> +
> +	pr_info("selftest(%lu) passed, last prime was %lu", 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_LICENSE("GPL");
> diff --git a/tools/testing/selftests/lib/prime_numbers.sh b/tools/testing/selftests/lib/prime_numbers.sh
> new file mode 100755
> index 000000000000..da4cbcd766f5
> --- /dev/null
> +++ b/tools/testing/selftests/lib/prime_numbers.sh
> @@ -0,0 +1,15 @@
> +#!/bin/sh
> +# Checks fast/slow prime_number generation for inconsistencies
> +
> +if ! /sbin/modprobe -q -r prime_numbers; then
> +	echo "prime_numbers: [SKIP]"
> +	exit 77
> +fi
> +
> +if /sbin/modprobe -q prime_numbers selftest=65536; then
> +	/sbin/modprobe -q -r prime_numbers
> +	echo "prime_numbers: ok"
> +else
> +	echo "prime_numbers: [FAIL]"
> +	exit 1
> +fi
> -- 
> 2.11.0
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/intel-gfx

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx

  reply	other threads:[~2016-12-27 11:31 UTC|newest]

Thread overview: 61+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-12-22  8:36 drm_mm fixes, take 4? Chris Wilson
2016-12-22  8:36 ` [PATCH v4 01/38] drm/i915: Use the MRU stack search after evicting Chris Wilson
2016-12-27 11:30   ` Daniel Vetter
2016-12-22  8:36 ` [PATCH v4 02/38] drm: Use drm_mm_nodes() as shorthand for the list of nodes under struct drm_mm Chris Wilson
2016-12-22  8:36 ` [PATCH v4 03/38] drm: Compile time enabling for asserts in drm_mm Chris Wilson
2016-12-22  8:36 ` [PATCH v4 04/38] lib: Add a simple prime number generator Chris Wilson
2016-12-22  9:52   ` Joonas Lahtinen
2016-12-22 10:00     ` [Intel-gfx] " Chris Wilson
2016-12-22 14:45   ` [PATCH v10] " Chris Wilson
2016-12-27 11:31     ` Daniel Vetter [this message]
2016-12-22  8:36 ` [PATCH v4 05/38] drm: Add a simple generator of random permutations Chris Wilson
2016-12-27 11:33   ` Daniel Vetter
2016-12-22  8:36 ` [PATCH v4 06/38] drm: Add some kselftests for the DRM range manager (struct drm_mm) Chris Wilson
2016-12-27 11:36   ` Daniel Vetter
2016-12-22  8:36 ` [PATCH v4 07/38] drm: kselftest for drm_mm_init() Chris Wilson
2016-12-22  8:36 ` [PATCH v4 08/38] drm: kselftest for drm_mm_debug() Chris Wilson
2016-12-22  8:36 ` [PATCH v4 09/38] drm: kselftest for drm_mm_reserve_node() Chris Wilson
2016-12-22  8:36 ` [PATCH v4 10/38] drm: kselftest for drm_mm_insert_node() Chris Wilson
2016-12-22  8:36 ` [PATCH v4 11/38] drm: kselftest for drm_mm_replace_node() Chris Wilson
2016-12-22  8:36 ` [PATCH v4 12/38] drm: kselftest for drm_mm_insert_node_in_range() Chris Wilson
2016-12-22  8:36 ` [PATCH v4 13/38] drm: kselftest for drm_mm and alignment Chris Wilson
2016-12-22  8:36 ` [PATCH v4 14/38] drm: kselftest for drm_mm and eviction Chris Wilson
2016-12-22  8:36 ` [PATCH v4 15/38] drm: kselftest for drm_mm and range restricted eviction Chris Wilson
2016-12-22  8:36 ` [PATCH v4 16/38] drm: kselftest for drm_mm and top-down allocation Chris Wilson
2016-12-22  8:36 ` [PATCH v4 17/38] drm: kselftest for drm_mm and color adjustment Chris Wilson
2016-12-22  8:36 ` [PATCH v4 18/38] drm: kselftest for drm_mm and color eviction Chris Wilson
2016-12-22  8:36 ` [PATCH v4 19/38] drm: kselftest for drm_mm and restricted " Chris Wilson
2016-12-22  8:36 ` [PATCH v4 20/38] drm/i915: Build DRM range manager selftests for CI Chris Wilson
2016-12-27 13:03   ` Daniel Vetter
2016-12-22  8:36 ` [PATCH v4 21/38] drm: Promote drm_mm alignment to u64 Chris Wilson
2016-12-22  8:36 ` [PATCH v4 22/38] drm: Fix kerneldoc for drm_mm_scan_remove_block() Chris Wilson
2016-12-22  8:36 ` [PATCH v4 23/38] drm: Detect overflow in drm_mm_reserve_node() Chris Wilson
2016-12-22  8:36 ` [PATCH v4 24/38] drm: Simplify drm_mm_clean() Chris Wilson
2016-12-22  8:36 ` [PATCH v4 25/38] drm: Add asserts to catch overflow in drm_mm_init() and drm_mm_init_scan() Chris Wilson
2016-12-27 13:12   ` Daniel Vetter
2016-12-22  8:36 ` [PATCH v4 26/38] drm: Extract struct drm_mm_scan from struct drm_mm Chris Wilson
2016-12-27 15:48   ` Daniel Vetter
2016-12-22  8:36 ` [PATCH v4 27/38] drm: Rename prev_node to hole in drm_mm_scan_add_block() Chris Wilson
2016-12-22  8:36 ` [PATCH v4 28/38] drm: Unconditionally do the range check " Chris Wilson
2016-12-22  8:36 ` [PATCH v4 29/38] drm: Fix application of color vs range restriction when scanning drm_mm Chris Wilson
2016-12-22  8:36 ` [PATCH v4 30/38] drm: Compute tight evictions for drm_mm_scan Chris Wilson
2016-12-28 13:01   ` [Intel-gfx] " Daniel Vetter
2016-12-28 14:36     ` Chris Wilson
2016-12-22  8:36 ` [PATCH v4 31/38] drm: Optimise power-of-two alignments in drm_mm_scan_add_block() Chris Wilson
2016-12-22  8:36 ` [PATCH v4 32/38] drm: Simplify drm_mm scan-list manipulation Chris Wilson
2016-12-22  8:36 ` [PATCH v4 33/38] drm: Apply tight eviction scanning to color_adjust Chris Wilson
2016-12-22  8:36 ` [PATCH v4 34/38] drm: Wrap drm_mm_node.hole_follows Chris Wilson
2016-12-28 13:02   ` Daniel Vetter
2016-12-28 13:31     ` Chris Wilson
2016-12-28 14:31       ` Daniel Vetter
2016-12-28 18:47         ` Chris Wilson
2016-12-22  8:36 ` [PATCH v4 35/38] drm: Apply range restriction after color adjustment when allocation Chris Wilson
2016-12-22  8:36 ` [PATCH v4 36/38] drm: Use drm_mm_insert_node_in_range_generic() for everyone Chris Wilson
2016-12-22  8:36 ` [PATCH v4 37/38] drm: Improve drm_mm search (and fix topdown allocation) with rbtrees Chris Wilson
2016-12-28 11:08   ` Chris Wilson
2016-12-28 13:48   ` Daniel Vetter
2016-12-28 14:34     ` Daniel Vetter
2016-12-22  8:36 ` [PATCH v4 38/38] drm: kselftest for drm_mm and bottom-up allocation Chris Wilson
2016-12-22  9:15 ` ✗ Fi.CI.BAT: warning for series starting with [v4,01/38] drm/i915: Use the MRU stack search after evicting Patchwork
2016-12-22  9:47   ` Imre Deak
2016-12-22 20:53 ` ✓ Fi.CI.BAT: success for series starting with [v4,01/38] drm/i915: Use the MRU stack search after evicting (rev2) Patchwork

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20161227113142.GG19172@dvetter-linux.ger.corp.intel.com \
    --to=daniel@ffwll.ch \
    --cc=chris@chris-wilson.co.uk \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=intel-gfx@lists.freedesktop.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.