[PATCH 07/34] drm: Add a simple prime number generator

Chris Wilson chris at chris-wilson.co.uk
Mon Dec 12 11:53:23 UTC 2016


Prime numbers are interesting for testing components that use multiplies
and divides, such as testing struct drm_mm alignment computations.

Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>
---
 drivers/gpu/drm/Kconfig                 |   5 +
 drivers/gpu/drm/Makefile                |   1 +
 drivers/gpu/drm/lib/drm_prime_numbers.c | 174 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/lib/drm_prime_numbers.h |  10 ++
 4 files changed, 190 insertions(+)
 create mode 100644 drivers/gpu/drm/lib/drm_prime_numbers.c
 create mode 100644 drivers/gpu/drm/lib/drm_prime_numbers.h

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 04d1d0a32c5c..09f076896844 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -52,11 +52,16 @@ config DRM_LIB_RAND
 	bool
 	default n
 
+config DRM_LIB_PRIMES
+	bool
+	default n
+
 config DRM_DEBUG_MM_SELFTEST
 	tristate "kselftests for DRM range manager (struct drm_mm)"
 	depends on DRM
 	depends on DEBUG_KERNEL
 	select DRM_LIB_RAND
+	select DRM_LIB_PRIMES
 	default n
 	help
 	  This option provides a kernel module that can be used to test
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 363eb1a23151..24fe5d47960a 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -19,6 +19,7 @@ drm-y       :=	drm_auth.o drm_bufs.o drm_cache.o \
 		drm_dumb_buffers.o drm_mode_config.o
 
 drm-$(CONFIG_DRM_LIB_RAND) += lib/drm_rand.o
+obj-$(CONFIG_DRM_LIB_PRIMES) += lib/drm_prime_numbers.o
 obj-$(CONFIG_DRM_DEBUG_MM_SELFTEST) += selftests/test-drm_mm.o
 
 drm-$(CONFIG_COMPAT) += drm_ioc32.o
diff --git a/drivers/gpu/drm/lib/drm_prime_numbers.c b/drivers/gpu/drm/lib/drm_prime_numbers.c
new file mode 100644
index 000000000000..c6cc0c32ce22
--- /dev/null
+++ b/drivers/gpu/drm/lib/drm_prime_numbers.c
@@ -0,0 +1,174 @@
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include "drm_prime_numbers.h"
+
+static DEFINE_MUTEX(lock);
+
+static struct primes {
+	struct rcu_head rcu;
+	unsigned long last, sz;
+	unsigned long primes[];
+} __rcu *primes;
+
+static bool slow_is_prime_number(unsigned long x)
+{
+	unsigned long y = int_sqrt(x) + 1;
+
+	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 = (start / x + 1) * x;
+
+	while (m < end) {
+		__clear_bit(m, p);
+		m += x;
+	}
+
+	return x;
+}
+
+static struct primes *expand(unsigned long x)
+{
+	unsigned long sz, y, prev;
+	struct primes *p, *new;
+
+	sz = x * x;
+	if (sz < x)
+		return NULL;
+
+	mutex_lock(&lock);
+	p = rcu_dereference_protected(primes, lockdep_is_held(&lock));
+	if (p && x < p->last)
+		goto unlock;
+
+	sz = round_up(sz, BITS_PER_LONG);
+	new = kmalloc(sizeof(*new) + sz / sizeof(long), GFP_KERNEL);
+	if (!new) {
+		p = NULL;
+		goto unlock;
+	}
+
+	/* Where memory permits, track the primes using the
+	 * Sieve of Eratosthenes.
+	 */
+	if (p) {
+		prev = p->sz;
+		memcpy(new->primes, p->primes, prev / BITS_PER_LONG);
+	} else {
+		prev = 0;
+	}
+	memset(new->primes + prev / BITS_PER_LONG,
+	       0xff, (sz - prev) / sizeof(long));
+	for (y = 2UL; y < sz; y = find_next_bit(new->primes, sz, y + 1))
+		new->last = mark_multiples(y, new->primes, prev, sz);
+	new->sz = sz;
+
+	rcu_assign_pointer(primes, new);
+	if (p)
+		kfree_rcu(p, rcu);
+	p = new;
+
+unlock:
+	mutex_unlock(&lock);
+	return p;
+}
+
+unsigned long drm_next_prime_number(unsigned long x)
+{
+	struct primes *p;
+
+	if (x < 2)
+		return 2;
+
+	rcu_read_lock();
+	p = rcu_dereference(primes);
+	if (!p || x >= p->last) {
+		rcu_read_unlock();
+
+		p = expand(x);
+		if (!p)
+			return slow_next_prime_number(x);
+
+		rcu_read_lock();
+	}
+
+	x = find_next_bit(p->primes, p->last, x + 1);
+	rcu_read_unlock();
+
+	return x;
+}
+EXPORT_SYMBOL(drm_next_prime_number);
+
+bool drm_is_prime_number(unsigned long x)
+{
+	struct primes *p;
+	bool result;
+
+	switch (x) {
+	case 0:
+		return false;
+	case 1:
+	case 2:
+	case 3:
+		return true;
+	}
+
+	rcu_read_lock();
+	p = rcu_dereference(primes);
+	if (!p || x >= p->last) {
+		rcu_read_unlock();
+
+		p = expand(x);
+		if (!p)
+			return slow_is_prime_number(x);
+
+		rcu_read_lock();
+	}
+
+	result = test_bit(x, p->primes);
+	rcu_read_unlock();
+
+	return result;
+}
+EXPORT_SYMBOL(drm_is_prime_number);
+
+static int __init drm_primes_init(void)
+{
+	return 0;
+}
+
+static void __exit drm_primes_exit(void)
+{
+	kfree(primes);
+}
+
+module_init(drm_primes_init);
+module_exit(drm_primes_exit);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/lib/drm_prime_numbers.h b/drivers/gpu/drm/lib/drm_prime_numbers.h
new file mode 100644
index 000000000000..7bc58cf9a86c
--- /dev/null
+++ b/drivers/gpu/drm/lib/drm_prime_numbers.h
@@ -0,0 +1,10 @@
+#ifndef __DRM_PRIMES_H__
+#define __DRM_PRIMES_H__
+
+bool drm_is_prime_number(unsigned long x);
+unsigned long drm_next_prime_number(unsigned long x);
+
+#define drm_for_each_prime(prime, max) \
+	for (prime = 1;	prime < (max); prime = drm_next_prime_number(prime))
+
+#endif /* __DRM_PRIMES_H__ */
-- 
2.11.0



More information about the dri-devel mailing list