diff options
-rw-r--r-- | arch/x86/kernel/process_32.c | 5 | ||||
-rw-r--r-- | arch/x86/kernel/process_64.c | 5 | ||||
-rw-r--r-- | include/asm-x86/processor.h | 23 |
3 files changed, 27 insertions, 6 deletions
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index 53bc653ed5ca..3b7a1ddcc0bc 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c | |||
@@ -95,7 +95,6 @@ static inline void play_dead(void) | |||
95 | { | 95 | { |
96 | /* This must be done before dead CPU ack */ | 96 | /* This must be done before dead CPU ack */ |
97 | cpu_exit_clear(); | 97 | cpu_exit_clear(); |
98 | wbinvd(); | ||
99 | mb(); | 98 | mb(); |
100 | /* Ack it */ | 99 | /* Ack it */ |
101 | __get_cpu_var(cpu_state) = CPU_DEAD; | 100 | __get_cpu_var(cpu_state) = CPU_DEAD; |
@@ -104,8 +103,8 @@ static inline void play_dead(void) | |||
104 | * With physical CPU hotplug, we should halt the cpu | 103 | * With physical CPU hotplug, we should halt the cpu |
105 | */ | 104 | */ |
106 | local_irq_disable(); | 105 | local_irq_disable(); |
107 | while (1) | 106 | /* mask all interrupts, flush any and all caches, and halt */ |
108 | halt(); | 107 | wbinvd_halt(); |
109 | } | 108 | } |
110 | #else | 109 | #else |
111 | static inline void play_dead(void) | 110 | static inline void play_dead(void) |
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 3fb62a7d9a16..71553b664e2a 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c | |||
@@ -93,14 +93,13 @@ DECLARE_PER_CPU(int, cpu_state); | |||
93 | static inline void play_dead(void) | 93 | static inline void play_dead(void) |
94 | { | 94 | { |
95 | idle_task_exit(); | 95 | idle_task_exit(); |
96 | wbinvd(); | ||
97 | mb(); | 96 | mb(); |
98 | /* Ack it */ | 97 | /* Ack it */ |
99 | __get_cpu_var(cpu_state) = CPU_DEAD; | 98 | __get_cpu_var(cpu_state) = CPU_DEAD; |
100 | 99 | ||
101 | local_irq_disable(); | 100 | local_irq_disable(); |
102 | while (1) | 101 | /* mask all interrupts, flush any and all caches, and halt */ |
103 | halt(); | 102 | wbinvd_halt(); |
104 | } | 103 | } |
105 | #else | 104 | #else |
106 | static inline void play_dead(void) | 105 | static inline void play_dead(void) |
diff --git a/include/asm-x86/processor.h b/include/asm-x86/processor.h index 5f58da401b43..4df3e2f6fb56 100644 --- a/include/asm-x86/processor.h +++ b/include/asm-x86/processor.h | |||
@@ -728,6 +728,29 @@ extern unsigned long boot_option_idle_override; | |||
728 | extern unsigned long idle_halt; | 728 | extern unsigned long idle_halt; |
729 | extern unsigned long idle_nomwait; | 729 | extern unsigned long idle_nomwait; |
730 | 730 | ||
731 | /* | ||
732 | * on systems with caches, caches must be flashed as the absolute | ||
733 | * last instruction before going into a suspended halt. Otherwise, | ||
734 | * dirty data can linger in the cache and become stale on resume, | ||
735 | * leading to strange errors. | ||
736 | * | ||
737 | * perform a variety of operations to guarantee that the compiler | ||
738 | * will not reorder instructions. wbinvd itself is serializing | ||
739 | * so the processor will not reorder. | ||
740 | * | ||
741 | * Systems without cache can just go into halt. | ||
742 | */ | ||
743 | static inline void wbinvd_halt(void) | ||
744 | { | ||
745 | mb(); | ||
746 | /* check for clflush to determine if wbinvd is legal */ | ||
747 | if (cpu_has_clflush) | ||
748 | asm volatile("cli; wbinvd; 1: hlt; jmp 1b" : : : "memory"); | ||
749 | else | ||
750 | while (1) | ||
751 | halt(); | ||
752 | } | ||
753 | |||
731 | extern void enable_sep_cpu(void); | 754 | extern void enable_sep_cpu(void); |
732 | extern int sysenter_setup(void); | 755 | extern int sysenter_setup(void); |
733 | 756 | ||