cpumask: introduce cpumask_var_t for local cpumask vars

We want to move cpumasks off the stack: no local decls, no passing by
copy.  Linus suggested we use the array-or-pointer trick for on-stack
vars; we introduce a new cpumask_var_t for this.

Rather than pick an arbitrary limit, I chose a new config option so
arch maintainers can decide where their threshold is.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
 arch/x86/Kconfig        |    8 ++++++++
 include/linux/cpumask.h |   44 ++++++++++++++++++++++++++++++++++++++++++++
 lib/cpumask.c           |   30 ++++++++++++++++++++++++++++++
 3 files changed, 82 insertions(+)

diff -r db8ee35ed8eb arch/x86/Kconfig
--- a/arch/x86/Kconfig	Fri Oct 03 16:50:05 2008 +1000
+++ b/arch/x86/Kconfig	Fri Oct 03 17:12:16 2008 +1000
@@ -581,6 +581,7 @@ config MAXSMP
 config MAXSMP
 	bool "Configure Maximum number of SMP Processors and NUMA Nodes"
 	depends on X86_64 && SMP && BROKEN
+	select CPUMASK_OFFSTACK
 	default n
 	help
 	  Configure maximum number of CPUS and NUMA Nodes for this architecture.
@@ -600,6 +601,13 @@ config NR_CPUS
 
 	  This is purely to save memory - each supported CPU adds
 	  approximately eight kilobytes to the kernel image.
+
+config CPUMASK_OFFSTACK
+	bool "Force CPU masks off stack" if DEBUG_PER_CPU_MAPS
+	help
+	  Use dynamic allocation for cpumask_var_t, instead of putting
+	  them on the stack.  This is a bit more expensive, but avoids
+	  stack overflow.
 
 config SCHED_SMT
 	bool "SMT (Hyperthreading) scheduler support"
diff -r db8ee35ed8eb include/linux/cpumask.h
--- a/include/linux/cpumask.h	Fri Oct 03 16:50:05 2008 +1000
+++ b/include/linux/cpumask.h	Fri Oct 03 17:12:16 2008 +1000
@@ -463,6 +463,50 @@ int __next_cpu_nr(int n, const cpumask_t
 #endif /* NR_CPUS > 64 */
 
 /*
+ * cpumask_var_t: struct cpumask for stack usage.
+ *
+ * Oh, the wicked games we play!  In order to make kernel coding a
+ * little more difficult, we typedef cpumask_t to an array or a
+ * pointer: doing &mask on an array is a noop, so it still works.
+ *
+ * ie.
+ *	cpumask_var_t tmpmask;
+ *	if (!alloc_cpumask_var(&tmpmask, GFP_KERNEL))
+ *		return -ENOMEM;
+ *
+ *	  ... use 'tmpmask' like a normal struct cpumask * ...
+ *
+ *	free_cpumask_var(tmpmask);
+ */
+#ifdef CONFIG_CPUMASK_OFFSTACK
+typedef struct cpumask *cpumask_var_t;
+
+bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags);
+bool alloc_cpumask_var_copy(cpumask_var_t *mask, const cpumask_t *src, gfp_t);
+void free_cpumask_var(cpumask_var_t mask);
+#else
+typedef struct cpumask cpumask_var_t[1];
+
+static inline bool alloc_cpumask_var(cpumask_var_t mask, gfp_t flags)
+{
+	cpumask_clear(mask);
+	return true;
+}
+
+static inline bool alloc_cpumask_var_copy(cpumask_var_t mask,
+					  const struct cpumask *src,
+					  gfp_t flags)
+{
+	cpumask_copy(mask, src);
+	return true;
+}
+
+static inline void free_cpumask_var(cpumask_var_t mask)
+{
+}
+#endif /* CONFIG_CPUMASK_OFFSTACK */
+
+/*
  * The following particular system cpumasks and operations manage
  * possible, present, active and online cpus.  Each of them is a fixed size
  * bitmap of size NR_CPUS.
diff -r db8ee35ed8eb lib/cpumask.c
--- a/lib/cpumask.c	Fri Oct 03 16:50:05 2008 +1000
+++ b/lib/cpumask.c	Fri Oct 03 17:12:16 2008 +1000
@@ -34,3 +34,33 @@ int __any_online_cpu(const cpumask_t *ma
 	return cpu;
 }
 EXPORT_SYMBOL(__any_online_cpu);
+
+/* These are not inline because of header tangles. */
+#ifdef CONFIG_CPUMASK_OFFSTACK
+bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
+{
+	if (likely(slab_is_available()))
+		*mask = kmalloc(BITS_TO_LONGS(nr_cpu_ids)*sizeof(long), flags);
+	else
+		*mask = NULL;
+	return *mask != NULL;
+}
+EXPORT_SYMBOL(alloc_cpumask_var);
+
+bool alloc_cpumask_var_copy(cpumask_var_t *mask,
+			    const cpumask_t *src, gfp_t flags)
+{
+	if (unlikely(!alloc_cpumask_var(mask, flags)))
+		return false;
+
+	cpumask_copy(*mask, src);
+	return true;
+}
+EXPORT_SYMBOL(alloc_cpumask_var_copy);
+
+void free_cpumask_var(cpumask_var_t mask)
+{
+	kfree(mask);
+}
+EXPORT_SYMBOL(free_cpumask_var);
+#endif
