From patchwork Sat Dec 17 12:41:02 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chris Wilson X-Patchwork-Id: 9479013 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id E3BC960760 for ; Sat, 17 Dec 2016 12:41:15 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DBFF428537 for ; Sat, 17 Dec 2016 12:41:15 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D099F2853C; Sat, 17 Dec 2016 12:41:15 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.1 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED, T_DKIM_INVALID autolearn=unavailable version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 6298E28537 for ; Sat, 17 Dec 2016 12:41:15 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 89B916E263; Sat, 17 Dec 2016 12:41:08 +0000 (UTC) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mail-wm0-x242.google.com (mail-wm0-x242.google.com [IPv6:2a00:1450:400c:c09::242]) by gabe.freedesktop.org (Postfix) with ESMTPS id 921F06E259; Sat, 17 Dec 2016 12:41:06 +0000 (UTC) Received: by mail-wm0-x242.google.com with SMTP id g23so10178818wme.1; Sat, 17 Dec 2016 04:41:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=Nmuf0DTbxSPSNN6gJnhtMbrSvzH3raW3iJf+fBDoS0Q=; b=Gc2MEBIKAyZ/GwBaRI9Q1yjO46mIjiYUXAjMselOTY4Dgw3VoGsAENaF6rhk2Mx7Cr YDK1CUwQEd5ACJoxxJhXQ0yvt7tjAKjB4Mr1Q/S1E67BhasSpDC9DAo5cXDWJLrgWmYF PD1AxtQptRCvG5E1ioTGmVkNHmxiPDXRTq+2eICY62v2A6s4Lm3mbkgf0/otb9A8gxcd uvpJFRVJC5PLX3gdWNdIVxLeGSLk/BtgGq7bK2eatB02GyfZU9m9eX5zgSV6jC48Ydqn UUMngr4Wk9ld2ux3h9DTA/I4Xx7QjUzKFAG7maRQkjZSuDiJ27IyHCZVkPJi4bx+Amq4 ++2g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=Nmuf0DTbxSPSNN6gJnhtMbrSvzH3raW3iJf+fBDoS0Q=; b=d8s2VnowNOEG6y1c0jgztYNcDhoOUkI8TnNhspBnWdNET1THXqAPs7CFhaD6S22RHJ wFcjWtJniod3LFhVWVUaqTEcUFxElrjQ2qlYqQN+7zXLyAw8cH6c9G1eB2d2xo2JpEUH YNik20M7wnoSpSVyVRwGymq78FUHUm52NLk68wx1emL/MFbOzLs6Ti1qSBRD3eBvMMYH luBVlqH5T9zzh2UqYTlduDLyOKE25xcBhf5xzVsuE0V67rbZKJqPG1cFsF2//77oV3+P xPLW274xKnY+H7X/jgOLdhfFAednYBRJCYRxe/x5OqJJaJrwkDEuWKMdjX8pb8rFCDCa zjpQ== X-Gm-Message-State: AIkVDXJv9Jr84aonXSidBnBIOm8yE2JIKhoPGNwwGmz2aAjIcYLTv76sntZmpRrAA6vchg== X-Received: by 10.28.98.66 with SMTP id w63mr7431406wmb.142.1481978464449; Sat, 17 Dec 2016 04:41:04 -0800 (PST) Received: from haswell.alporthouse.com ([78.156.65.138]) by smtp.gmail.com with ESMTPSA id kp5sm11370068wjb.8.2016.12.17.04.41.03 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sat, 17 Dec 2016 04:41:03 -0800 (PST) From: Chris Wilson To: dri-devel@lists.freedesktop.org Subject: [PATCH] lib: Add a simple prime number generator Date: Sat, 17 Dec 2016 12:41:02 +0000 Message-Id: <20161217124102.6520-1-chris@chris-wilson.co.uk> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20161216192550.8352-5-chris@chris-wilson.co.uk> References: <20161216192550.8352-5-chris@chris-wilson.co.uk> Cc: intel-gfx@lists.freedesktop.org X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.freedesktop.org Sender: "dri-devel" X-Virus-Scanned: ClamAV using ClamSMTP 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 Signed-off-by: Chris Wilson Cc: Lukas Wunner --- include/linux/prime_numbers.h | 13 ++ lib/Kconfig | 7 + lib/Makefile | 2 + lib/prime_numbers.c | 238 +++++++++++++++++++++++++++ tools/testing/selftests/lib/prime_numbers.sh | 15 ++ 5 files changed, 275 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..877f6acbd0b6 --- /dev/null +++ b/include/linux/prime_numbers.h @@ -0,0 +1,13 @@ +#ifndef __LINUX_PRIME_NUMBERS_H +#define __LINUX_PRIME_NUMBERS_H + +#include + +bool is_prime_number(unsigned long x); +unsigned long next_prime_number(unsigned long x); + +/* A useful white-lie here is that 1 is prime. */ +#define for_each_prime_number(prime, max) \ + for (prime = 1; 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..f5a704033a3c --- /dev/null +++ b/lib/prime_numbers.c @@ -0,0 +1,238 @@ +#define pr_fmt(fmt) "prime numbers: " fmt "\n" + +#include +#include +#include +#include + +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 = { 0x28208a20a08a28acUL } +}; +#elif BITS_PER_LONG == 32 +static const struct primes small_primes = { + .last = 31, + .sz = 32, + .primes = { 0xa08a28acUL } +}; +#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) +{ + for (;;) { + if (slow_is_prime_number(++x)) + return x; + } +} + +static unsigned long mark_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 const struct primes *expand_to_next(unsigned long x) +{ + const struct primes *p; + struct primes *new; + unsigned long sz, y; + + rcu_read_unlock(); + + /* Betrand's Theorem states: + * For all n > 1, there exists a prime p: n < p <= 2*n. + */ + sz = 2 * x + 1; + if (sz < x) + return NULL; + + sz = round_up(sz, BITS_PER_LONG); + new = kmalloc(sizeof(*new) + sz / sizeof(long), GFP_KERNEL); + if (!new) + return NULL; + + mutex_lock(&lock); + p = rcu_dereference_protected(primes, lockdep_is_held(&lock)); + if (x < p->last) { + kfree(new); + goto relock; + } + + /* Where memory permits, track the primes using the + * Sieve of Eratosthenes. + */ + memcpy(new->primes, p->primes, p->sz / BITS_PER_LONG * sizeof(long)); + memset(new->primes + p->sz / BITS_PER_LONG, + 0xff, (sz - p->sz) / BITS_PER_LONG * sizeof(long)); + for (y = 2UL; y < sz; y = find_next_bit(new->primes, sz, y + 1)) + new->last = mark_multiples(y, new->primes, p->sz, sz); + new->sz = sz; + + rcu_assign_pointer(primes, new); + if (p != &small_primes) + kfree_rcu((struct primes *)p, rcu); + p = new; + +relock: + rcu_read_lock(); + mutex_unlock(&lock); + return p; +} + +static const struct primes *get_primes(unsigned long x) +{ + const struct primes *p; + + rcu_read_lock(); + p = rcu_dereference(primes); + if (!p || x >= p->last) + p = expand_to_next(x); + + /* returns under RCU iff p != NULL */ + return p; +} + +unsigned long next_prime_number(unsigned long x) +{ + const struct primes *p; + + p = get_primes(x); + if (unlikely(!p)) + return slow_next_prime_number(x); + + x = find_next_bit(p->primes, p->last, x + 1); + rcu_read_unlock(); + + return x; +} +EXPORT_SYMBOL(next_prime_number); + +bool is_prime_number(unsigned long x) +{ + const struct primes *p; + bool result; + + p = get_primes(x); + if (unlikely(!p)) + return slow_is_prime_number(x); + + 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; + + rcu_read_lock(); + p = rcu_dereference(primes); + pr_info("primes.{last=%lu, .sz=%lu, .primes[]=...x%lx}", + p->last, p->sz, p->primes[p->sz / BITS_PER_LONG - 1]); + rcu_read_unlock(); +} + +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) +{ + 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); +} + +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