[patch 22/24] Immediate Values - Powerpc Optimization NMI MCE support
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
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