Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=8f4e956b313dcccbc7be6f10808952345e3b638c
Commit:     8f4e956b313dcccbc7be6f10808952345e3b638c
Parent:     19d36ccdc34f5ed444f8a6af0cbfdb6790eb1177
Author:     Andi Kleen <[EMAIL PROTECTED]>
AuthorDate: Sun Jul 22 11:12:32 2007 +0200
Committer:  Linus Torvalds <[EMAIL PROTECTED]>
CommitDate: Sun Jul 22 11:03:37 2007 -0700

    x86: Stop MCEs and NMIs during code patching
    
    When a machine check or NMI occurs while multiple byte code is patched
    the CPU could theoretically see an inconsistent instruction and crash.
    Prevent this by temporarily disabling MCEs and returning early in the
    NMI handler.
    
    Based on discussion with Mathieu Desnoyers.
    
    Cc: Mathieu Desnoyers <[EMAIL PROTECTED]>
    Cc: Jeremy Fitzhardinge <[EMAIL PROTECTED]>
    Signed-off-by: Andi Kleen <[EMAIL PROTECTED]>
    Signed-off-by: Linus Torvalds <[EMAIL PROTECTED]>
---
 arch/i386/kernel/alternative.c    |   15 +++++++++++++++
 arch/i386/kernel/cpu/mcheck/mce.c |   14 ++++++++++++++
 arch/i386/kernel/traps.c          |   17 ++++++++++++++++-
 arch/x86_64/kernel/mce.c          |   14 ++++++++++++++
 arch/x86_64/kernel/nmi.c          |   17 ++++++++++++++++-
 include/asm-i386/mce.h            |    4 ++++
 include/asm-i386/nmi.h            |    2 ++
 include/asm-x86_64/mce.h          |    3 +++
 include/asm-x86_64/nmi.h          |    2 ++
 9 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/arch/i386/kernel/alternative.c b/arch/i386/kernel/alternative.c
index 206ea2c..c3750c2 100644
--- a/arch/i386/kernel/alternative.c
+++ b/arch/i386/kernel/alternative.c
@@ -8,6 +8,8 @@
 #include <asm/alternative.h>
 #include <asm/sections.h>
 #include <asm/pgtable.h>
+#include <asm/mce.h>
+#include <asm/nmi.h>
 
 #ifdef CONFIG_HOTPLUG_CPU
 static int smp_alt_once;
@@ -373,6 +375,14 @@ void __init alternative_instructions(void)
 {
        unsigned long flags;
 
+       /* The patching is not fully atomic, so try to avoid local interruptions
+          that might execute the to be patched code.
+          Other CPUs are not running. */
+       stop_nmi();
+#ifdef CONFIG_MCE
+       stop_mce();
+#endif
+
        local_irq_save(flags);
        apply_alternatives(__alt_instructions, __alt_instructions_end);
 
@@ -405,6 +415,11 @@ void __init alternative_instructions(void)
 #endif
        apply_paravirt(__parainstructions, __parainstructions_end);
        local_irq_restore(flags);
+
+       restart_nmi();
+#ifdef CONFIG_MCE
+       restart_mce();
+#endif
 }
 
 /*
diff --git a/arch/i386/kernel/cpu/mcheck/mce.c 
b/arch/i386/kernel/cpu/mcheck/mce.c
index 56cd485..34c781e 100644
--- a/arch/i386/kernel/cpu/mcheck/mce.c
+++ b/arch/i386/kernel/cpu/mcheck/mce.c
@@ -60,6 +60,20 @@ void mcheck_init(struct cpuinfo_x86 *c)
        }
 }
 
+static unsigned long old_cr4 __initdata;
+
+void __init stop_mce(void)
+{
+       old_cr4 = read_cr4();
+       clear_in_cr4(X86_CR4_MCE);
+}
+
+void __init restart_mce(void)
+{
+       if (old_cr4 & X86_CR4_MCE)
+               set_in_cr4(X86_CR4_MCE);
+}
+
 static int __init mcheck_disable(char *str)
 {
        mce_disabled = 1;
diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
index 438949d..cfffe3d 100644
--- a/arch/i386/kernel/traps.c
+++ b/arch/i386/kernel/traps.c
@@ -775,6 +775,8 @@ static __kprobes void default_do_nmi(struct pt_regs * regs)
        reassert_nmi();
 }
 
+static int ignore_nmis;
+
 fastcall __kprobes void do_nmi(struct pt_regs * regs, long error_code)
 {
        int cpu;
@@ -785,11 +787,24 @@ fastcall __kprobes void do_nmi(struct pt_regs * regs, 
long error_code)
 
        ++nmi_count(cpu);
 
-       default_do_nmi(regs);
+       if (!ignore_nmis)
+               default_do_nmi(regs);
 
        nmi_exit();
 }
 
+void stop_nmi(void)
+{
+       acpi_nmi_disable();
+       ignore_nmis++;
+}
+
+void restart_nmi(void)
+{
+       ignore_nmis--;
+       acpi_nmi_enable();
+}
+
 #ifdef CONFIG_KPROBES
 fastcall void __kprobes do_int3(struct pt_regs *regs, long error_code)
 {
diff --git a/arch/x86_64/kernel/mce.c b/arch/x86_64/kernel/mce.c
index 4d8450e..a66d607 100644
--- a/arch/x86_64/kernel/mce.c
+++ b/arch/x86_64/kernel/mce.c
@@ -667,6 +667,20 @@ static struct miscdevice mce_log_device = {
        &mce_chrdev_ops,
 };
 
+static unsigned long old_cr4 __initdata;
+
+void __init stop_mce(void)
+{
+       old_cr4 = read_cr4();
+       clear_in_cr4(X86_CR4_MCE);
+}
+
+void __init restart_mce(void)
+{
+       if (old_cr4 & X86_CR4_MCE)
+               set_in_cr4(X86_CR4_MCE);
+}
+
 /* 
  * Old style boot options parsing. Only for compatibility. 
  */
diff --git a/arch/x86_64/kernel/nmi.c b/arch/x86_64/kernel/nmi.c
index edbbc59..cb8ee9d 100644
--- a/arch/x86_64/kernel/nmi.c
+++ b/arch/x86_64/kernel/nmi.c
@@ -384,11 +384,14 @@ int __kprobes nmi_watchdog_tick(struct pt_regs * regs, 
unsigned reason)
        return rc;
 }
 
+static unsigned ignore_nmis;
+
 asmlinkage __kprobes void do_nmi(struct pt_regs * regs, long error_code)
 {
        nmi_enter();
        add_pda(__nmi_count,1);
-       default_do_nmi(regs);
+       if (!ignore_nmis)
+               default_do_nmi(regs);
        nmi_exit();
 }
 
@@ -401,6 +404,18 @@ int do_nmi_callback(struct pt_regs * regs, int cpu)
        return 0;
 }
 
+void stop_nmi(void)
+{
+       acpi_nmi_disable();
+       ignore_nmis++;
+}
+
+void restart_nmi(void)
+{
+       ignore_nmis--;
+       acpi_nmi_enable();
+}
+
 #ifdef CONFIG_SYSCTL
 
 static int unknown_nmi_panic_callback(struct pt_regs *regs, int cpu)
diff --git a/include/asm-i386/mce.h b/include/asm-i386/mce.h
index b0a02ee..d56d897 100644
--- a/include/asm-i386/mce.h
+++ b/include/asm-i386/mce.h
@@ -5,3 +5,7 @@ extern void mcheck_init(struct cpuinfo_x86 *c);
 #endif
 
 extern int mce_disabled;
+
+extern void stop_mce(void);
+extern void restart_mce(void);
+
diff --git a/include/asm-i386/nmi.h b/include/asm-i386/nmi.h
index fb1e133..ff30c98 100644
--- a/include/asm-i386/nmi.h
+++ b/include/asm-i386/nmi.h
@@ -57,5 +57,7 @@ unsigned lapic_adjust_nmi_hz(unsigned hz);
 int lapic_watchdog_ok(void);
 void disable_lapic_nmi_watchdog(void);
 void enable_lapic_nmi_watchdog(void);
+void stop_nmi(void);
+void restart_nmi(void);
 
 #endif /* ASM_NMI_H */
diff --git a/include/asm-x86_64/mce.h b/include/asm-x86_64/mce.h
index 556be55..7bc030a 100644
--- a/include/asm-x86_64/mce.h
+++ b/include/asm-x86_64/mce.h
@@ -107,6 +107,9 @@ extern void do_machine_check(struct pt_regs *, long);
 
 extern int mce_notify_user(void);
 
+extern void stop_mce(void);
+extern void restart_mce(void);
+
 #endif
 
 #endif
diff --git a/include/asm-x86_64/nmi.h b/include/asm-x86_64/nmi.h
index d0a7f53..5fb3c0d 100644
--- a/include/asm-x86_64/nmi.h
+++ b/include/asm-x86_64/nmi.h
@@ -88,5 +88,7 @@ unsigned lapic_adjust_nmi_hz(unsigned hz);
 int lapic_watchdog_ok(void);
 void disable_lapic_nmi_watchdog(void);
 void enable_lapic_nmi_watchdog(void);
+void stop_nmi(void);
+void restart_nmi(void);
 
 #endif /* ASM_NMI_H */
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to