[patch 22/24] Immediate Values - Powerpc Optimization NMI MCE support

2007-12-20 Thread Mathieu Desnoyers
Use an atomic update for immediate values.

Signed-off-by: Mathieu Desnoyers <[EMAIL PROTECTED]>
CC: Rusty Russell <[EMAIL PROTECTED]>
CC: Christoph Hellwig <[EMAIL PROTECTED]>
CC: Paul Mackerras <[EMAIL PROTECTED]>
---
 arch/powerpc/kernel/Makefile|1 
 arch/powerpc/kernel/immediate.c |   73 
 include/asm-powerpc/immediate.h |   18 +
 3 files changed, 92 insertions(+)

Index: linux-2.6-lttng/arch/powerpc/kernel/immediate.c
===
--- /dev/null   1970-01-01 00:00:00.0 +
+++ linux-2.6-lttng/arch/powerpc/kernel/immediate.c 2007-12-20 
20:52:27.0 -0500
@@ -0,0 +1,73 @@
+/*
+ * Powerpc optimized immediate values enabling/disabling.
+ *
+ * Mathieu Desnoyers <[EMAIL PROTECTED]>
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define LI_OPCODE_LEN  2
+
+/**
+ * arch_imv_update - update one immediate value
+ * @imv: pointer of type const struct __imv to update
+ * @early: early boot (1), normal (0)
+ *
+ * Update one immediate value. Must be called with imv_mutex held.
+ */
+int arch_imv_update(const struct __imv *imv, int early)
+{
+#ifdef CONFIG_KPROBES
+   kprobe_opcode_t *insn;
+   /*
+* Fail if a kprobe has been set on this instruction.
+* (TODO: we could eventually do better and modify all the (possibly
+* nested) kprobes for this site if kprobes had an API for this.
+*/
+   switch (imv->size) {
+   case 1: /* The uint8_t points to the 3rd byte of the
+* instruction */
+   insn = (void *)(imv->imv - 1 - LI_OPCODE_LEN);
+   break;
+   case 2: insn = (void *)(imv->imv - LI_OPCODE_LEN);
+   break;
+   default:
+   return -EINVAL;
+   }
+
+   if (unlikely(!early && *insn == BREAKPOINT_INSTRUCTION)) {
+   printk(KERN_WARNING "Immediate value in conflict with kprobe. "
+   "Variable at %p, "
+   "instruction at %p, size %lu\n",
+   (void *)imv->imv,
+   (void *)imv->var, imv->size);
+   return -EBUSY;
+   }
+#endif
+
+   /*
+* If the variable and the instruction have the same value, there is
+* nothing to do.
+*/
+   switch (imv->size) {
+   case 1: if (*(uint8_t *)imv->imv
+   == *(uint8_t *)imv->var)
+   return 0;
+   break;
+   case 2: if (*(uint16_t *)imv->imv
+   == *(uint16_t *)imv->var)
+   return 0;
+   break;
+   default:return -EINVAL;
+   }
+   memcpy((void *)imv->imv, (void *)imv->var,
+   imv->size);
+   flush_icache_range(imv->imv,
+   imv->imv + imv->size);
+   return 0;
+}
Index: linux-2.6-lttng/include/asm-powerpc/immediate.h
===
--- linux-2.6-lttng.orig/include/asm-powerpc/immediate.h2007-12-20 
20:52:20.0 -0500
+++ linux-2.6-lttng/include/asm-powerpc/immediate.h 2007-12-20 
20:52:27.0 -0500
@@ -12,6 +12,16 @@
 
 #include 
 
+struct __imv {
+   unsigned long var;  /* Identifier variable of the immediate value */
+   unsigned long imv;  /*
+* Pointer to the memory location that holds
+* the immediate value within the load immediate
+* instruction.
+*/
+   unsigned char size; /* Type size. */
+} __attribute__ ((packed));
+
 /**
  * imv_read - read immediate variable
  * @name: immediate value name
@@ -19,6 +29,11 @@
  * Reads the value of @name.
  * Optimized version of the immediate.
  * Do not use in __init and __exit functions. Use _imv_read() instead.
+ * Makes sure the 2 bytes update will be atomic by aligning the immediate
+ * value. Use a normal memory read for the 4 bytes immediate because there is 
no
+ * way to atomically update it without using a seqlock read side, which would
+ * cost more in term of total i-cache and d-cache space than a simple memory
+ * read.
  */
 #define imv_read(name) \
({  \
@@ -40,6 +55,7 @@
PPC_LONG "%c1, ((1f)-2)\n\t"\
".byte 2\n\t"   \
".previous\n\t" \
+   ".align 2\n\t"  \
"li %0,0\n\t"   \
"1:\n\t"\
: 

[patch 22/24] Immediate Values - Powerpc Optimization NMI MCE support

2007-12-20 Thread Mathieu Desnoyers
Use an atomic update for immediate values.

Signed-off-by: Mathieu Desnoyers [EMAIL PROTECTED]
CC: Rusty Russell [EMAIL PROTECTED]
CC: Christoph Hellwig [EMAIL PROTECTED]
CC: Paul Mackerras [EMAIL PROTECTED]
---
 arch/powerpc/kernel/Makefile|1 
 arch/powerpc/kernel/immediate.c |   73 
 include/asm-powerpc/immediate.h |   18 +
 3 files changed, 92 insertions(+)

Index: linux-2.6-lttng/arch/powerpc/kernel/immediate.c
===
--- /dev/null   1970-01-01 00:00:00.0 +
+++ linux-2.6-lttng/arch/powerpc/kernel/immediate.c 2007-12-20 
20:52:27.0 -0500
@@ -0,0 +1,73 @@
+/*
+ * Powerpc optimized immediate values enabling/disabling.
+ *
+ * Mathieu Desnoyers [EMAIL PROTECTED]
+ */
+
+#include linux/module.h
+#include linux/immediate.h
+#include linux/string.h
+#include linux/kprobes.h
+#include asm/cacheflush.h
+#include asm/page.h
+
+#define LI_OPCODE_LEN  2
+
+/**
+ * arch_imv_update - update one immediate value
+ * @imv: pointer of type const struct __imv to update
+ * @early: early boot (1), normal (0)
+ *
+ * Update one immediate value. Must be called with imv_mutex held.
+ */
+int arch_imv_update(const struct __imv *imv, int early)
+{
+#ifdef CONFIG_KPROBES
+   kprobe_opcode_t *insn;
+   /*
+* Fail if a kprobe has been set on this instruction.
+* (TODO: we could eventually do better and modify all the (possibly
+* nested) kprobes for this site if kprobes had an API for this.
+*/
+   switch (imv-size) {
+   case 1: /* The uint8_t points to the 3rd byte of the
+* instruction */
+   insn = (void *)(imv-imv - 1 - LI_OPCODE_LEN);
+   break;
+   case 2: insn = (void *)(imv-imv - LI_OPCODE_LEN);
+   break;
+   default:
+   return -EINVAL;
+   }
+
+   if (unlikely(!early  *insn == BREAKPOINT_INSTRUCTION)) {
+   printk(KERN_WARNING Immediate value in conflict with kprobe. 
+   Variable at %p, 
+   instruction at %p, size %lu\n,
+   (void *)imv-imv,
+   (void *)imv-var, imv-size);
+   return -EBUSY;
+   }
+#endif
+
+   /*
+* If the variable and the instruction have the same value, there is
+* nothing to do.
+*/
+   switch (imv-size) {
+   case 1: if (*(uint8_t *)imv-imv
+   == *(uint8_t *)imv-var)
+   return 0;
+   break;
+   case 2: if (*(uint16_t *)imv-imv
+   == *(uint16_t *)imv-var)
+   return 0;
+   break;
+   default:return -EINVAL;
+   }
+   memcpy((void *)imv-imv, (void *)imv-var,
+   imv-size);
+   flush_icache_range(imv-imv,
+   imv-imv + imv-size);
+   return 0;
+}
Index: linux-2.6-lttng/include/asm-powerpc/immediate.h
===
--- linux-2.6-lttng.orig/include/asm-powerpc/immediate.h2007-12-20 
20:52:20.0 -0500
+++ linux-2.6-lttng/include/asm-powerpc/immediate.h 2007-12-20 
20:52:27.0 -0500
@@ -12,6 +12,16 @@
 
 #include asm/asm-compat.h
 
+struct __imv {
+   unsigned long var;  /* Identifier variable of the immediate value */
+   unsigned long imv;  /*
+* Pointer to the memory location that holds
+* the immediate value within the load immediate
+* instruction.
+*/
+   unsigned char size; /* Type size. */
+} __attribute__ ((packed));
+
 /**
  * imv_read - read immediate variable
  * @name: immediate value name
@@ -19,6 +29,11 @@
  * Reads the value of @name.
  * Optimized version of the immediate.
  * Do not use in __init and __exit functions. Use _imv_read() instead.
+ * Makes sure the 2 bytes update will be atomic by aligning the immediate
+ * value. Use a normal memory read for the 4 bytes immediate because there is 
no
+ * way to atomically update it without using a seqlock read side, which would
+ * cost more in term of total i-cache and d-cache space than a simple memory
+ * read.
  */
 #define imv_read(name) \
({  \
@@ -40,6 +55,7 @@
PPC_LONG %c1, ((1f)-2)\n\t\
.byte 2\n\t   \
.previous\n\t \
+   .align 2\n\t  \
li %0,0\n\t   \
1:\n\t