Linux Kernel Markers - Use Immediate Values Optimization
Subsystems (3)
cover Linux Kernel Markers - Use Immediate Values Optimization
Hi,
The Markers are meant to have a _very_ low impact on performance (even on d-cache hits) when disabled. This is provided by the Immediate Values.
However, to be able to instrument code called from traps (NMI, MCE handler, all code called by there functions and all traps that can be triggered by these), the rock-solid implementation of the immediate values seems required. It, however, comes at the cost of some added complexity.
This patch applies on 2.6.24-rc4-git3, after the
Text Edit Lock
Immediate Values (redux)
Profiling Use Immediate Values
patchsets.
It could be interesting to queue this for 2.6.25 so we can also add basic kernel instrumentation (patches follow).
Mathieu
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 1/6 Immediate Values - Move Kprobes x86 restore_interrupt to kdebug.h
Since the breakpoint handler is useful both to kprobes and immediate values, it makes sense to make the required restore_interrupt() available through asm-i386/kdebug.h.
--- include/asm-x86/kdebug.h | 12 ++++++++++++ include/asm-x86/kprobes_32.h | 9 --------- include/asm-x86/kprobes_64.h | 9 --------- 3 files changed, 12 insertions(+), 18 deletions(-)
include/asm-x86/kdebug.h +12 -0
@@ -3,6 +3,9 @@#include <linux/notifier.h> +#include <linux/ptrace.h> +#include <asm/system.h> +struct pt_regs ;/* Grossly misnamed. */
@@ -30,4 +33,13 @@ extern void dump_pagetable(unsigned longextern unsigned long oops_begin (void );extern void oops_end (unsigned long ); +/* trap3/1 are intr gates for kprobes. So, restore the status of IF, +* if necessary, before executing the original int3/1 (trap) handler. +*/ +static inline void restore_interrupts (struct pt_regs *regs) +{ +if (regs->eflags &IF_MASK ) +local_irq_enable (); +} +#endif
include/asm-x86/kprobes_32.h +0 -9
@@ -79,15 +79,6 @@ struct kprobe_ctlblk {struct prev_kprobe prev_kprobe; }; -/* trap3/1 are intr gates for kprobes. So, restore the status of IF, -* if necessary, before executing the original int3/1 (trap) handler. -*/ -static inline void restore_interrupts (struct pt_regs *regs) -{ -if (regs->eflags &IF_MASK ) -local_irq_enable (); -} -extern int kprobe_exceptions_notify (struct notifier_block *self,unsigned long val,void *data);extern int kprobe_fault_handler (struct pt_regs *regs,int trapnr);
include/asm-x86/kprobes_64.h +0 -9
@@ -72,15 +72,6 @@ struct kprobe_ctlblk {struct prev_kprobe prev_kprobe; }; -/* trap3/1 are intr gates for kprobes. So, restore the status of IF, -* if necessary, before executing the original int3/1 (trap) handler. -*/ -static inline void restore_interrupts (struct pt_regs *regs) -{ -if (regs->eflags &IF_MASK ) -local_irq_enable (); -} -extern int post_kprobe_handler (struct pt_regs *regs);extern int kprobe_fault_handler (struct pt_regs *regs,int trapnr);extern int kprobe_handler (struct pt_regs *regs);
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 2/6 Add __discard section to x86
In reply to Mathieu DesnoyersAdd a __discard sectionto the linker script. Code produced in this section will not be put in the vmlinux file. This is useful when we have to calculate the size of an instruction before actually declaring it (for alignment purposes for instance). This is used by the immediate values.
--- arch/x86/kernel/vmlinux_32.lds.S | 1 + arch/x86/kernel/vmlinux_64.lds.S | 1 + 2 files changed, 2 insertions(+)
arch/x86/kernel/vmlinux_32.lds.S +1 -0
@@ -205,6 +205,7 @@ SECTIONS /* Sections to be discarded */ /DISCARD/ : { *(.exitcall.exit) + *(__discard) } STABS_DEBUG
arch/x86/kernel/vmlinux_64.lds.S +1 -0
@@ -227,6 +227,7 @@ SECTIONS /DISCARD/ : { *(.exitcall.exit) *(.eh_frame) + *(__discard) } STABS_DEBUG
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 3/6 Immediate Values - x86 Optimization NMI and MCE support
In reply to Mathieu Desnoyersx86 optimization of the immediate values which uses a movl with code patching to set/unset the value used to populate the register used as variable source. It uses a breakpoint to bypass the instruction being changed, which lessens the interrupt latency of the operation and protects against NMIs and MCE.
Changelog:
- Use text_poke_early with cr0 WP save/restore to patch the bypass. We are doing non atomic writes to a code region only touched by us (nobody can execute it since we are protected by the imv_mutex).
- Add x86_64 support, ready for i386+x86_64 -> x86 merge.
- Use asm-x86/asm.h.
- Change the immediate.c update code to support variable length opcodes.
- Use imv_* instead of immediate_*.
--- arch/x86/kernel/Makefile_32 | 1 arch/x86/kernel/Makefile_64 | 1 arch/x86/kernel/immediate.c | 278 ++++++++++++++++++++++++++++++++++++++++++++ arch/x86/kernel/traps_32.c | 10 - include/asm-x86/immediate.h | 42 +++++- 5 files changed, 323 insertions(+), 9 deletions(-)
include/asm-x86/immediate.h +37 -5
@@ -12,6 +12,18 @@#include <asm/asm.h> +struct __imv { +unsigned long var;/* Pointer to the identifier variable of the +* immediate value +*/ +unsigned long imv;/* +* Pointer to the memory location of the +* immediate value within the instruction. +*/ +unsigned char size;/* Type size. */ +unsigned char insn_size;/* Type size. */ +} __attribute__ ((packed)); +/** * imv_read - read immediate variable * @name: immediate value name
@@ -26,6 +38,11 @@* what will generate an instruction with 8 bytes immediate value (not the REX.W * prefixed one that loads a sign extended 32 bits immediate value in a r64 * register). +* +* Create the instruction in a discarded section to calculate its size. This is +* how we can align the beginning of the instruction on an address that will +* permit atomic modification of the immediate value without knowing the size of +* the opcode used by the compiler. The operand size is known in advance. */ #define imv_read (name) \ ({ \
@@ -35,8 +52,9 @@case 1 : \ asm(".section __imv,\"a\",@progbits\n\t" \ _ASM_PTR"%c1, (3f)-%c2\n\t" \ -".byte %c2\n\t" \ +".byte %c2, (3f-2f)\n\t" \".previous\n\t" \ +"2:\n\t" \"mov $0,%0\n\t" \"3:\n\t" \ : "=q" (value) \
@@ -45,10 +63,16 @@break ; \case 2 : \case 4 : \ - asm(".section __imv,\"a\",@progbits\n\t" \ + asm(".section __discard,\"\",@progbits\n\t" \ +"1:\n\t" \ +"mov $0,%0\n\t" \ +"2:\n\t" \ +".previous\n\t" \ +".section __imv,\"a\",@progbits\n\t" \ _ASM_PTR"%c1, (3f)-%c2\n\t" \ -".byte %c2\n\t" \ +".byte %c2, (2b-1b)\n\t" \".previous\n\t" \ +".org . + ((-.-(2b-1b)) & (%c2-1)), 0x90\n\t" \"mov $0,%0\n\t" \"3:\n\t" \ : "=r" (value) \
@@ -60,10 +84,16 @@ value = name##__imv; \break ; \ } \ - asm(".section __imv,\"a\",@progbits\n\t" \ + asm(".section __discard,\"\",@progbits\n\t" \ +"1:\n\t" \ +"mov $0xFEFEFEFE01010101,%0\n\t" \ +"2:\n\t" \ +".previous\n\t" \ +".section __imv,\"a\",@progbits\n\t" \ _ASM_PTR"%c1, (3f)-%c2\n\t" \ -".byte %c2\n\t" \ +".byte %c2, (2b-1b)\n\t" \".previous\n\t" \ +".org . + ((-.-(2b-1b)) & (%c2-1)), 0x90\n\t" \"mov $0xFEFEFEFE01010101,%0\n\t" \"3:\n\t" \ : "=r" (value) \
@@ -74,4 +104,6 @@ value; \ }) +extern int arch_imv_update (const struct __imv *imv,int early); +#endif /* _ASM_X86_IMMEDIATE_H */
arch/x86/kernel/traps_32.c +6 -4
@@ -549,7 +549,7 @@ fastcall void do_##name(struct pt_regs * } DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->eip) -#ifndef CONFIG_KPROBES +#if !defined(CONFIG_KPROBES ) && !defined(CONFIG_IMMEDIATE ) DO_VM86_ERROR( 3, SIGTRAP, "int3", int3)#endif DO_VM86_ERROR (4 ,SIGSEGV ,"overflow" , overflow)
@@ -791,7 +791,7 @@ void restart_nmi(void)acpi_nmi_enable (); } -#ifdef CONFIG_KPROBES +#if defined(CONFIG_KPROBES ) || defined(CONFIG_IMMEDIATE ) fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code) {trace_hardirqs_fixup ();
@@ -799,8 +799,10 @@ fastcall void __kprobes do_int3(struct pif (notify_die (DIE_INT3 ,"int3" , regs, error_code,3 ,SIGTRAP ) ==NOTIFY_STOP )return ; -/* This is an interrupt gate, because kprobes wants interrupts -disabled. Normal trap handlers don't. */ +/* +* This is an interrupt gate, because kprobes and immediate values wants +* interrupts disabled. Normal trap handlers don't. +*/ restore_interrupts (regs);do_trap (3 ,SIGTRAP ,"int3" ,1 , regs, error_code,NULL ); }
arch/x86/kernel/Makefile_64 +1 -0
@@ -35,6 +35,7 @@ obj-$(CONFIG_X86_PM_TIMER) += pmtimer_64 obj-$(CONFIG_X86_VSMP) += vsmp_64.o obj-$(CONFIG_K8_NB) += k8.o obj-$(CONFIG_AUDIT) += audit_64.o +obj-$(CONFIG_IMMEDIATE) += immediate.o obj-$(CONFIG_MODULES) += module_64.o obj-$(CONFIG_PCI) += early-quirks.o
arch/x86/kernel/Makefile_32 +1 -0
@@ -35,6 +35,7 @@ obj-$(CONFIG_KPROBES) += kprobes_32.o obj-$(CONFIG_MODULES) += module_32.o obj-y += sysenter_32.o vsyscall_32.o obj-$(CONFIG_ACPI_SRAT) += srat_32.o +obj-$(CONFIG_IMMEDIATE) += immediate.o obj-$(CONFIG_EFI) += efi_32.o efi_stub_32.o obj-$(CONFIG_DOUBLEFAULT) += doublefault_32.o obj-$(CONFIG_VM86) += vm86_32.o
arch/x86/kernel/immediate.c new file +278 -0
278 lines not on this page · raw message
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
Re: [patch 3/6] Immediate Values - x86 Optimization NMI and MCE support (update)
x86 optimization of the immediate values which uses a movl with code patching to set/unset the value used to populate the register used as variable source. It uses a breakpoint to bypass the instruction being changed, which lessens the interrupt latency of the operation and protects against NMIs and MCE.
Changelog:
- Use text_poke_early with cr0 WP save/restore to patch the bypass. We are doing non atomic writes to a code region only touched by us (nobody can execute it since we are protected by the imv_mutex).
- Add x86_64 support, ready for i386+x86_64 -> x86 merge.
- Use asm-x86/asm.h.
- Change the immediate.c update code to support variable length opcodes.
- Use imv_* instead of immediate_*.
- Use kernel_wp_disable/enable instead of save/restore.
--- arch/x86/kernel/Makefile_32 | 1 arch/x86/kernel/Makefile_64 | 1 arch/x86/kernel/immediate.c | 277 ++++++++++++++++++++++++++++++++++++++++++++ arch/x86/kernel/traps_32.c | 10 - include/asm-x86/immediate.h | 42 +++++- 5 files changed, 322 insertions(+), 9 deletions(-)
include/asm-x86/immediate.h +37 -5
@@ -12,6 +12,18 @@#include <asm/asm.h> +struct __imv { +unsigned long var;/* Pointer to the identifier variable of the +* immediate value +*/ +unsigned long imv;/* +* Pointer to the memory location of the +* immediate value within the instruction. +*/ +unsigned char size;/* Type size. */ +unsigned char insn_size;/* Type size. */ +} __attribute__ ((packed)); +/** * imv_read - read immediate variable * @name: immediate value name
@@ -26,6 +38,11 @@* what will generate an instruction with 8 bytes immediate value (not the REX.W * prefixed one that loads a sign extended 32 bits immediate value in a r64 * register). +* +* Create the instruction in a discarded section to calculate its size. This is +* how we can align the beginning of the instruction on an address that will +* permit atomic modification of the immediate value without knowing the size of +* the opcode used by the compiler. The operand size is known in advance. */ #define imv_read (name) \ ({ \
@@ -35,8 +52,9 @@case 1 : \ asm(".section __imv,\"a\",@progbits\n\t" \ _ASM_PTR"%c1, (3f)-%c2\n\t" \ -".byte %c2\n\t" \ +".byte %c2, (3f-2f)\n\t" \".previous\n\t" \ +"2:\n\t" \"mov $0,%0\n\t" \"3:\n\t" \ : "=q" (value) \
@@ -45,10 +63,16 @@break ; \case 2 : \case 4 : \ - asm(".section __imv,\"a\",@progbits\n\t" \ + asm(".section __discard,\"\",@progbits\n\t" \ +"1:\n\t" \ +"mov $0,%0\n\t" \ +"2:\n\t" \ +".previous\n\t" \ +".section __imv,\"a\",@progbits\n\t" \ _ASM_PTR"%c1, (3f)-%c2\n\t" \ -".byte %c2\n\t" \ +".byte %c2, (2b-1b)\n\t" \".previous\n\t" \ +".org . + ((-.-(2b-1b)) & (%c2-1)), 0x90\n\t" \"mov $0,%0\n\t" \"3:\n\t" \ : "=r" (value) \
@@ -60,10 +84,16 @@ value = name##__imv; \break ; \ } \ - asm(".section __imv,\"a\",@progbits\n\t" \ + asm(".section __discard,\"\",@progbits\n\t" \ +"1:\n\t" \ +"mov $0xFEFEFEFE01010101,%0\n\t" \ +"2:\n\t" \ +".previous\n\t" \ +".section __imv,\"a\",@progbits\n\t" \ _ASM_PTR"%c1, (3f)-%c2\n\t" \ -".byte %c2\n\t" \ +".byte %c2, (2b-1b)\n\t" \".previous\n\t" \ +".org . + ((-.-(2b-1b)) & (%c2-1)), 0x90\n\t" \"mov $0xFEFEFEFE01010101,%0\n\t" \"3:\n\t" \ : "=r" (value) \
@@ -74,4 +104,6 @@ value; \ }) +extern int arch_imv_update (const struct __imv *imv,int early); +#endif /* _ASM_X86_IMMEDIATE_H */
arch/x86/kernel/traps_32.c +6 -4
@@ -549,7 +549,7 @@ fastcall void do_##name(struct pt_regs * } DO_VM86_ERROR_INFO( 0, SIGFPE, "divide error", divide_error, FPE_INTDIV, regs->eip) -#ifndef CONFIG_KPROBES +#if !defined(CONFIG_KPROBES ) && !defined(CONFIG_IMMEDIATE ) DO_VM86_ERROR( 3, SIGTRAP, "int3", int3)#endif DO_VM86_ERROR (4 ,SIGSEGV ,"overflow" , overflow)
@@ -791,7 +791,7 @@ void restart_nmi(void)acpi_nmi_enable (); } -#ifdef CONFIG_KPROBES +#if defined(CONFIG_KPROBES ) || defined(CONFIG_IMMEDIATE ) fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code) {trace_hardirqs_fixup ();
@@ -799,8 +799,10 @@ fastcall void __kprobes do_int3(struct pif (notify_die (DIE_INT3 ,"int3" , regs, error_code,3 ,SIGTRAP ) ==NOTIFY_STOP )return ; -/* This is an interrupt gate, because kprobes wants interrupts -disabled. Normal trap handlers don't. */ +/* +* This is an interrupt gate, because kprobes and immediate values wants +* interrupts disabled. Normal trap handlers don't. +*/ restore_interrupts (regs);do_trap (3 ,SIGTRAP ,"int3" ,1 , regs, error_code,NULL ); }
arch/x86/kernel/Makefile_64 +1 -0
@@ -35,6 +35,7 @@ obj-$(CONFIG_X86_PM_TIMER) += pmtimer_64 obj-$(CONFIG_X86_VSMP) += vsmp_64.o obj-$(CONFIG_K8_NB) += k8.o obj-$(CONFIG_AUDIT) += audit_64.o +obj-$(CONFIG_IMMEDIATE) += immediate.o obj-$(CONFIG_MODULES) += module_64.o obj-$(CONFIG_PCI) += early-quirks.o
arch/x86/kernel/Makefile_32 +1 -0
@@ -35,6 +35,7 @@ obj-$(CONFIG_KPROBES) += kprobes_32.o obj-$(CONFIG_MODULES) += module_32.o obj-y += sysenter_32.o vsyscall_32.o obj-$(CONFIG_ACPI_SRAT) += srat_32.o +obj-$(CONFIG_IMMEDIATE) += immediate.o obj-$(CONFIG_EFI) += efi_32.o efi_stub_32.o obj-$(CONFIG_DOUBLEFAULT) += doublefault_32.o obj-$(CONFIG_VM86) += vm86_32.o
arch/x86/kernel/immediate.c new file +277 -0
277 lines not on this page · raw message
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 4/6 Immediate Values - Powerpc Optimization NMI MCE support
In reply to Mathieu DesnoyersUse an atomic update for immediate values.
--- arch/powerpc/kernel/Makefile | 1 arch/powerpc/kernel/immediate.c | 73 ++++++++++++++++++++++++++++++++++++++++ include/asm-powerpc/immediate.h | 18 +++++++++ 3 files changed, 92 insertions(+)
arch/powerpc/kernel/immediate.c new file +73 -0
@@ -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 ; +}
include/asm-powerpc/immediate.h +18 -0
@@ -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" \ : "=r" (value) \
@@ -52,4 +68,6 @@ value; \ }) +extern int arch_imv_update (const struct __imv *imv,int early); +#endif /* _ASM_POWERPC_IMMEDIATE_H */
arch/powerpc/kernel/Makefile +1 -0
@@ -91,3 +91,4 @@ obj-$(CONFIG_PPC64) += $(obj64-y) extra-$(CONFIG_PPC_FPU) += fpu.o extra-$(CONFIG_PPC64) += entry_64.o +obj-$(CONFIG_IMMEDIATE) += immediate.o
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 5/6 Immediate Values Use Arch NMI and MCE Support
In reply to Mathieu DesnoyersRemove the architecture agnostic code now replaced by architecture specific, atomic instruction updates.
--- include/linux/immediate.h | 11 ---- kernel/immediate.c | 113 +--------------------------------------------- 2 files changed, 4 insertions(+), 120 deletions(-)
kernel/immediate.c +4 -109
@@ -19,9 +19,6 @@#include <linux/mutex.h> #include <linux/immediate.h> #include <linux/memory.h> -#include <linux/cpu.h> - -#include <asm/cacheflush.h> /* * Kernel ready to execute the SMP update that may depend on trap and ipi.
@@ -37,111 +34,6 @@ extern const struct __imv __stop___imv[]*/ static DEFINE_MUTEX (imv_mutex); -static atomic_t wait_sync; - -struct ipi_loop_data { -long value; -const struct __imv *imv; -} loop_data; - -static void ipi_busy_loop (void *arg) -{ -unsigned long flags; - -local_irq_save (flags); -atomic_dec (&wait_sync); -do { -/* Make sure the wait_sync gets re-read */ -smp_mb (); - }while (atomic_read (&wait_sync) > loop_data.value); -atomic_dec (&wait_sync); -do { -/* Make sure the wait_sync gets re-read */ -smp_mb (); - }while (atomic_read (&wait_sync) >0 ); -/* -* Issuing a synchronizing instruction must be done on each CPU before -* reenabling interrupts after modifying an instruction. Required by -* Intel's errata. -*/ -sync_core (); -flush_icache_range (loop_data.imv->imv, - loop_data.imv->imv + loop_data.imv->size); -local_irq_restore (flags); -} - -/** -* apply_imv_update - update one immediate value -* @imv: pointer of type const struct __imv to update -* -* Update one immediate value. Must be called with imv_mutex held. -* It makes sure all CPUs are not executing the modified code by having them -* busy looping with interrupts disabled. -* It does _not_ protect against NMI and MCE (could be a problem with Intel's -* errata if we use immediate values in their code path). -*/ -static int apply_imv_update (const struct __imv *imv) -{ -unsigned long flags; -long online_cpus; - -/* -* 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 ; -case 4 :if (*(uint32_t *)imv->imv - == *(uint32_t *)imv->var) -return 0 ; -break ; -case 8 :if (*(uint64_t *)imv->imv - == *(uint64_t *)imv->var) -return 0 ; -break ; -default :return -EINVAL ; - } - -if (imv_early_boot_complete) { -kernel_text_lock (); -lock_cpu_hotplug (); - online_cpus =num_online_cpus (); -atomic_set (&wait_sync,2 * online_cpus); - loop_data.value = online_cpus; - loop_data.imv = imv; -smp_call_function (ipi_busy_loop,NULL ,1 ,0 ); -local_irq_save (flags); -atomic_dec (&wait_sync); -do { -/* Make sure the wait_sync gets re-read */ -smp_mb (); - }while (atomic_read (&wait_sync) > online_cpus); -text_poke ((void *)imv->imv, (void *)imv->var, - imv->size); -/* -* Make sure the modified instruction is seen by all CPUs before -* we continue (visible to other CPUs and local interrupts). -*/ -wmb (); -atomic_dec (&wait_sync); -flush_icache_range (imv->imv, - imv->imv + imv->size); -local_irq_restore (flags); -unlock_cpu_hotplug (); -kernel_text_unlock (); - }else -text_poke_early ((void *)imv->imv, (void *)imv->var, - imv->size); -return 0 ; -} -/** * imv_update_range - Update immediate values in a range * @begin: pointer to the beginning of the range
@@ -154,9 +46,12 @@ void imv_update_range(const struct __imv {const struct __imv *iter;int ret; +for (iter = begin; iter < end; iter++) {mutex_lock (&imv_mutex); - ret =apply_imv_update (iter); +kernel_text_lock (); + ret =arch_imv_update (iter, !imv_early_boot_complete); +kernel_text_unlock ();if (imv_early_boot_complete && ret)printk (KERN_WARNING "Invalid immediate value. "
include/linux/immediate.h +0 -11
@@ -12,17 +12,6 @@#ifdef CONFIG_IMMEDIATE -struct __imv { -unsigned long var;/* Pointer to the identifier variable of the -* immediate value -*/ -unsigned long imv;/* -* Pointer to the memory location of the -* immediate value within the instruction. -*/ -unsigned char size;/* Type size. */ -} __attribute__ ((packed)); -#include <asm/immediate.h> /**
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68
PATCH 6/6 Linux Kernel Markers - Use Immediate Values
In reply to Mathieu DesnoyersMake markers use immediate values.
Changelog :
- Use imv_* instead of immediate_*.
--- Documentation/markers.txt | 17 +++++++++++++---- include/linux/marker.h | 42 ++++++++++++++++++++++++++++++++---------- kernel/marker.c | 8 ++++++-- kernel/module.c | 1 + 4 files changed, 52 insertions(+), 16 deletions(-)
include/linux/marker.h +32 -10
@@ -12,6 +12,7 @@* See the file COPYING for more details. */ +#include <linux/immediate.h> #include <linux/types.h> struct module ;
@@ -42,7 +43,7 @@ struct marker {const char *format;/* Marker format string, describing the * variable argument list. */ -char state;/* Marker state. */ +DEFINE_IMV (char, state);/* Immediate value state. */ char ptype;/* probe type : 0 : single, 1 : multi */ void (*call)(const struct marker *mdata,/* Probe wrapper */ void *call_private,const char *fmt, ...);
@@ -53,13 +54,14 @@ struct marker {#ifdef CONFIG_MARKERS /* +* Generic marker flavor always available. * Note : the empty asm volatile with read constraint is used here instead of a * "used" attribute to fix a gcc 4.1.x bug. * Make sure the alignment of the structure in the __markers section will * not add unwanted padding between the beginning of the section and the * structure. Force alignment to the same alignment as the section start. */ -#define __trace_mark(name, call_private, format, args...) \ +#define __trace_mark(generic, name, call_private, format, args...) \ do { \ static const char __mstrtab_##name[] \ __attribute__((section("__markers_strings"))) \
@@ -70,17 +72,23 @@ struct marker {0 ,0 , marker_probe_cb, \ { __mark_empty_function, NULL}, NULL }; \ __mark_check_format(format, ## args); \ - if (unlikely(__mark_##name.state)) { \ - (*__mark_##name.call) \ - (&__mark_##name, call_private, \ - format, ## args); \ + if (!generic) { \ + if (unlikely(imv_read(__mark_##name.state))) \ + (*__mark_##name.call) \ + (&__mark_##name, call_private, \ + format, ## args); \ + } else { \ + if (unlikely(_imv_read(__mark_##name.state))) \ + (*__mark_##name.call) \ + (&__mark_##name, call_private, \ + format, ## args); \ } \ } while (0)extern void marker_update_probe_range (struct marker *begin,struct marker *end);#else /* !CONFIG_MARKERS */ -#define __trace_mark(name, call_private, format, args...) \ +#define __trace_mark(generic, name, call_private, format, args...) \ __mark_check_format(format, ## args)static inline void marker_update_probe_range (struct marker *begin,struct marker *end)
@@ -88,15 +96,29 @@ static inline void marker_update_probe_r#endif /* CONFIG_MARKERS */ /** -* trace_mark - Marker +* trace_mark - Marker using code patching * @name: marker name, not quoted. * @format: format string * @args...: variable argument list * -* Places a marker. +* Places a marker using optimized code patching technique (imv_read()) +* to be enabled. */ #define trace_mark(name, format, args...) \ - __trace_mark(name, NULL, format, ## args) + __trace_mark(0, name, NULL, format, ## args) + +/** +* _trace_mark - Marker using variable read +* @name: marker name, not quoted. +* @format: format string +* @args...: variable argument list +* +* Places a marker using a standard memory read (_imv_read()) to be +* enabled. Should be used for markers in __init and __exit functions and in +* lockdep code. +*/ +#define _trace_mark(name, format, args...) \ + __trace_mark(1, name, NULL, format, ## args)/** * MARK_NOARGS - Format string for a marker with no argument.
kernel/marker.c +6 -2
@@ -23,6 +23,7 @@#include <linux/rcupdate.h> #include <linux/marker.h> #include <linux/err.h> +#include <linux/immediate.h> extern struct marker __start___markers[];extern struct marker __stop___markers[];
@@ -544,7 +545,7 @@ static int set_marker(struct marker_entr*/ smp_wmb (); elem->ptype = (*entry)->ptype; - elem->state = active; + elem->state__imv = active;return 0 ; }
@@ -558,7 +559,7 @@ static int set_marker(struct marker_entrstatic void disable_marker (struct marker *elem) {/* leave "call" as is. It is known statically. */ - elem->state =0 ; + elem->state__imv =0 ; elem->single.func = __mark_empty_function;/* Update the function before setting the ptype */ smp_wmb ();
@@ -625,6 +626,9 @@ static void marker_update_probes(void)marker_update_probe_range (__start___markers, __stop___markers);/* Markers in modules. */ module_update_markers (); +/* Update immediate values */ +core_imv_update (); +module_imv_update (); }/**
Documentation/markers.txt +13 -4
@@ -15,10 +15,12 @@ provide at runtime. A marker can be "on" (no probe is attached). When a marker is "off" it has no effect, except for adding a tiny time penalty (checking a condition for a branch) and space penalty (adding a few bytes for the function call at the end of the -instrumented function and adds a data structure in a separate section). When a -marker is "on", the function you provide is called each time the marker is -executed, in the execution context of the caller. When the function provided -ends its execution, it returns to the caller (continuing from the marker site). +instrumented function and adds a data structure in a separate section). The +immediate values are used to minimize the impact on data cache, encoding the +condition in the instruction stream. When a marker is "on", the function you +provide is called each time the marker is executed, in the execution context of +the caller. When the function provided ends its execution, it returns to the +caller (continuing from the marker site). You can put markers at important locations in the code. Markers are lightweight hooks that can pass an arbitrary number of parameters,
@@ -69,6 +71,13 @@ a printk warning which identifies the in "Format mismatch for probe probe_name (format), marker (format)" +* Optimization for a given architecture + +To force use of a non-optimized version of the markers, _trace_mark() should be +used. It takes the same parameters as the normal markers, but it does not use +the immediate values based on code patching. + + * Probe / marker example See the example provided in samples/markers/src
kernel/module.c +1 -0
@@ -2005,6 +2005,7 @@ static struct module *load_module(void _ mod->markers + mod->num_markers);#endif #ifdef CONFIG_IMMEDIATE +/* Immediate values must be updated after markers */ imv_update_range (mod->immediate, mod->immediate + mod->num_immediate);#endif
Signature · 3 lines
Mathieu Desnoyers Computer Engineering Ph.D. Student, Ecole Polytechnique de Montreal OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F BA06 3F25 A8FE 3BAE 9A68