cpumask: debug options for cpumasks

It's useful to check that noone is accessing > nr_cpu_ids for
cpumasks.  This also allows you to turn on CONFIG_CPUMASKS_OFFSTACK
even for smaller CONFIG_NR_CPUS.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
 include/linux/cpumask.h |   23 ++++++++++++++++++-----
 lib/Kconfig.debug       |    6 ++++++
 lib/cpumask.c           |    5 +++--
 3 files changed, 27 insertions(+), 7 deletions(-)

diff -r ee7dbdcde651 include/linux/cpumask.h
--- a/include/linux/cpumask.h	Sun Oct 05 16:50:24 2008 +1100
+++ b/include/linux/cpumask.h	Sun Oct 05 16:51:05 2008 +1100
@@ -200,22 +200,34 @@ extern int nr_cpu_ids;
 
 #define cpumask_size() (BITS_TO_LONGS(nr_cpu_ids) * sizeof(long))
 
+static inline unsigned int cpumask_check(unsigned int cpu)
+{
+#ifdef CONFIG_DEBUG_PER_CPU_MAPS
+	/* This breaks build if known at build time. */
+	(void)(sizeof(char[1 - 2 * !!(cpu >= NR_CPUS)]));
+	/* This breaks at runtime. */
+	BUG_ON(cpu >= nr_cpu_ids);
+#endif /* CONFIG_DEBUG_PER_CPU_MAPS */
+	return cpu;
+}
+
 static inline void cpumask_set_cpu(int cpu, volatile struct cpumask *dstp)
 {
-	set_bit(cpu, cpumask_bits(dstp));
+	set_bit(cpumask_check(cpu), cpumask_bits(dstp));
 }
 
 static inline void cpumask_clear_cpu(int cpu, volatile struct cpumask *dstp)
 {
-	clear_bit(cpu, cpumask_bits(dstp));
+	clear_bit(cpumask_check(cpu), cpumask_bits(dstp));
 }
 
 /* No static inline type checking - see Subtlety (1) above. */
-#define cpumask_test_cpu(cpu, cpumask) test_bit((cpu), cpumask_bits(cpumask))
+#define cpumask_test_cpu(cpu, cpumask) \
+	test_bit(cpumask_check(cpu), cpumask_bits(cpumask))
 
 static inline int cpumask_test_and_set_cpu(int cpu, struct cpumask *addr)
 {
-	return test_and_set_bit(cpu, cpumask_bits(addr));
+	return test_and_set_bit(cpumask_check(cpu), cpumask_bits(addr));
 }
 
 static inline void cpumask_setall(struct cpumask *dstp)
@@ -342,7 +354,8 @@ static inline int cpumask_cpuremap(int o
 				   const struct cpumask *oldp,
 				   const struct cpumask *newp)
 {
-	return bitmap_bitremap(oldbit, cpumask_bits(oldp), cpumask_bits(newp),
+	return bitmap_bitremap(cpumask_check(oldbit),
+			       cpumask_bits(oldp), cpumask_bits(newp),
 			       nr_cpu_ids);
 }
 
diff -r ee7dbdcde651 lib/Kconfig.debug
--- a/lib/Kconfig.debug	Sun Oct 05 16:50:24 2008 +1100
+++ b/lib/Kconfig.debug	Sun Oct 05 16:51:05 2008 +1100
@@ -701,6 +701,12 @@ config SYSCTL_SYSCALL_CHECK
 	  to properly maintain and use. This enables checks that help
 	  you to keep things correct.
 
+config DEBUG_PER_CPU_MAPS
+	bool "Cpumask debug checks"
+	---help---
+	  Extra debugging for cpumasks.
+	  eg. to make sure accesses to cpumasks are < nr_cpu_ids.
+
 source kernel/trace/Kconfig
 
 config PROVIDE_OHCI1394_DMA_INIT
diff -r ee7dbdcde651 lib/cpumask.c
--- a/lib/cpumask.c	Sun Oct 05 16:50:24 2008 +1100
+++ b/lib/cpumask.c	Sun Oct 05 16:51:05 2008 +1100
@@ -11,7 +11,8 @@ EXPORT_SYMBOL(cpumask_first);
 
 int cpumask_next(int n, const struct cpumask *srcp)
 {
-	return find_next_bit(cpumask_bits(srcp), nr_cpu_ids, n+1);
+	return find_next_bit(cpumask_bits(srcp), nr_cpu_ids,
+			     cpumask_check(n)+1);
 }
 EXPORT_SYMBOL(cpumask_next);
 
@@ -29,7 +30,7 @@ int cpumask_next_both(int n,
 int cpumask_next_both(int n,
 		      const struct cpumask *src1, const struct cpumask *src2)
 {
-	while ((n = cpumask_next(n+1, src1)) < nr_cpu_ids)
+	while ((n = cpumask_next(cpumask_check(n)+1, src1)) < nr_cpu_ids)
 		if (cpumask_test_cpu(n, src2))
 			break;
 	return n;
