alloc_percpu: read_percpu_var / read_percpu_ptr

Impact: New API

get_cpu_var/get_cpu_ptr return lvalues, but for ia64 and x86/32 (and one
day x86/64) there are more efficient ways if you just want an rvalue.

The x86-specific versions have proven popular in that arch, so I expect
they'll be popular generally.

So introduce read_percpu_var and read_percpu_ptr.  They are separate,
because when we enlarge percpu areas IA64 is not going to be able to
do its trick on abitrary per-cpu pointers, only vars (which are in the
first 64k).

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
---
 arch/alpha/include/asm/percpu.h |    4 ++++
 arch/ia64/include/asm/percpu.h  |    1 +
 arch/x86/include/asm/percpu.h   |    4 ++++
 include/asm-generic/percpu.h    |   24 ++++++++++++++++++++++++
 4 files changed, 33 insertions(+)

diff --git a/arch/alpha/include/asm/percpu.h b/arch/alpha/include/asm/percpu.h
--- a/arch/alpha/include/asm/percpu.h
+++ b/arch/alpha/include/asm/percpu.h
@@ -62,6 +62,7 @@ extern unsigned long __per_cpu_offset[NR
 	(*SHIFT_PERCPU_PTR(var, my_cpu_offset))
 #define __raw_get_cpu_var(var) \
 	(*SHIFT_PERCPU_PTR(var, __my_cpu_offset))
+#define read_percpu_var(var) (0, __raw_get_cpu_var(var))
 #define __get_cpu_ptr(ptr) \
 	RELOC_HIDE(ptr, my_cpu_offset)
 #define __raw_get_cpu_ptr(ptr) \
@@ -72,15 +73,18 @@ extern unsigned long __per_cpu_offset[NR
 	RELOC_HIDE(ptr, my_cpu_offset)
 #define __raw_get_cpu_ptr(ptr) \
 	RELOC_HIDE(ptr, __my_cpu_offset)
+#define read_percpu_ptr(ptr) (0, *__raw_get_cpu_ptr(ptr))
 
 #else /* ! SMP */
 
 #define per_cpu(var, cpu)		(*((void)(cpu), &per_cpu_var(var)))
 #define __get_cpu_var(var)		per_cpu_var(var)
 #define __raw_get_cpu_var(var)		per_cpu_var(var)
+#define read_percpu_var(var)		(0, per_cpu_var(var))
 #define per_cpu_ptr(ptr, cpu)		(ptr)
 #define __get_cpu_ptr(ptr)		(ptr)
 #define __raw_get_cpu_ptr(ptr)		(ptr)
+#define read_percpu_ptr(ptr)		(0, *(ptr))
 
 #define PER_CPU_ATTRIBUTES
 
diff --git a/arch/ia64/include/asm/percpu.h b/arch/ia64/include/asm/percpu.h
--- a/arch/ia64/include/asm/percpu.h
+++ b/arch/ia64/include/asm/percpu.h
@@ -40,6 +40,7 @@ extern void *per_cpu_init(void);
  * more efficient.
  */
 #define __ia64_per_cpu_var(var)	per_cpu__##var
+#define read_percpu_var(var)	(0, __ia64_per_cpu_var(var))
 
 #include <asm-generic/percpu.h>
 
diff --git a/arch/x86/include/asm/percpu.h b/arch/x86/include/asm/percpu.h
--- a/arch/x86/include/asm/percpu.h
+++ b/arch/x86/include/asm/percpu.h
@@ -101,6 +101,10 @@ DECLARE_PER_CPU(struct x8664_pda, pda);
 #define __percpu_seg ""
 
 #endif	/* SMP */
+
+/* Define these now so asm-generic/percpu.h doesn't. */
+#define read_percpu_var(var) x86_read_percpu(var)
+#define read_percpu_ptr(ptr) x86_read_percpu(*(ptr))
 
 #include <asm-generic/percpu.h>
 
diff --git a/include/asm-generic/percpu.h b/include/asm-generic/percpu.h
--- a/include/asm-generic/percpu.h
+++ b/include/asm-generic/percpu.h
@@ -60,6 +60,17 @@ extern unsigned long __per_cpu_offset[NR
 #define __raw_get_cpu_var(var) \
 	(*SHIFT_PERCPU_PTR(&per_cpu_var(var), __my_cpu_offset))
 
+#ifndef read_percpu_var
+/**
+ * read_percpu_var - get a copy of this cpu's percpu simple var.
+ * @var: the name of the per-cpu variable.
+ *
+ * Like __raw_get_cpu_var(), but doesn't provide an lvalue.  Some platforms
+ * can do this more efficiently (x86/32).  Only works on fundamental types.
+ */
+#define read_percpu_var(var) (0, __raw_get_cpu_var(var))
+#endif /* read_percpu_var */
+
 /* Use RELOC_HIDE: some arch's SHIFT_PERCPU_PTR really want an identifier. */
 /**
  * per_cpu_ptr - get a pointer to a particular cpu's allocated memory
@@ -81,6 +92,17 @@ extern unsigned long __per_cpu_offset[NR
 #define __get_cpu_ptr(ptr) RELOC_HIDE(ptr, my_cpu_offset)
 #define __raw_get_cpu_ptr(ptr) RELOC_HIDE(ptr, __my_cpu_offset)
 
+#ifndef read_percpu_ptr
+/**
+ * read_percpu_ptr - deref this cpu's simple percpu pointer.
+ * @ptr: the address of the per-cpu variable.
+ *
+ * Like read_percpu_var(), but can be used on pointers returned from
+ * alloc_percpu.
+ */
+#define read_percpu_ptr(ptr) (0, *__raw_get_cpu_ptr(ptr))
+#endif /* read_percpu_ptr */
+
 #ifdef CONFIG_HAVE_SETUP_PER_CPU_AREA
 extern void setup_per_cpu_areas(void);
 #endif
@@ -90,9 +112,11 @@ extern void setup_per_cpu_areas(void);
 #define per_cpu(var, cpu)			(*((void)(cpu), &per_cpu_var(var)))
 #define __get_cpu_var(var)			per_cpu_var(var)
 #define __raw_get_cpu_var(var)			per_cpu_var(var)
+#define read_percpu_var(var)			(0, per_cpu_var(var))
 #define per_cpu_ptr(ptr, cpu)			(ptr)
 #define __get_cpu_ptr(ptr)			(ptr)
 #define __raw_get_cpu_ptr(ptr)			(ptr)
+#define read_percpu_ptr(ptr)			(0, *(ptr))
 
 #endif	/* SMP */
 
