Commit 226917b0 authored by H. Peter Anvin's avatar H. Peter Anvin Committed by Greg Kroah-Hartman

x86, hotplug: Use mwait to offline a processor, fix the legacy case

upstream ea530692
x86, hotplug: Use mwait to offline a processor, fix the legacy case

Here included also some small follow-on patches to the same code:

upstream a68e5c94
x86, hotplug: Move WBINVD back outside the play_dead loop

upstream ce5f6824
x86, hotplug: In the MWAIT case of play_dead, CLFLUSH the cache line

https://bugzilla.kernel.org/show_bug.cgi?id=5471Signed-off-by: default avatarH. Peter Anvin <hpa@linux.intel.com>
Signed-off-by: default avatarLen Brown <len.brown@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 6db0ed15
...@@ -765,29 +765,6 @@ extern unsigned long boot_option_idle_override; ...@@ -765,29 +765,6 @@ extern unsigned long boot_option_idle_override;
extern unsigned long idle_halt; extern unsigned long idle_halt;
extern unsigned long idle_nomwait; extern unsigned long idle_nomwait;
/*
* on systems with caches, caches must be flashed as the absolute
* last instruction before going into a suspended halt. Otherwise,
* dirty data can linger in the cache and become stale on resume,
* leading to strange errors.
*
* perform a variety of operations to guarantee that the compiler
* will not reorder instructions. wbinvd itself is serializing
* so the processor will not reorder.
*
* Systems without cache can just go into halt.
*/
static inline void wbinvd_halt(void)
{
mb();
/* check for clflush to determine if wbinvd is legal */
if (cpu_has_clflush)
asm volatile("cli; wbinvd; 1: hlt; jmp 1b" : : : "memory");
else
while (1)
halt();
}
extern void enable_sep_cpu(void); extern void enable_sep_cpu(void);
extern int sysenter_setup(void); extern int sysenter_setup(void);
......
...@@ -1338,11 +1338,94 @@ void play_dead_common(void) ...@@ -1338,11 +1338,94 @@ void play_dead_common(void)
local_irq_disable(); local_irq_disable();
} }
#define MWAIT_SUBSTATE_MASK 0xf
#define MWAIT_SUBSTATE_SIZE 4
#define CPUID_MWAIT_LEAF 5
#define CPUID5_ECX_EXTENSIONS_SUPPORTED 0x1
/*
* We need to flush the caches before going to sleep, lest we have
* dirty data in our caches when we come back up.
*/
static inline void mwait_play_dead(void)
{
unsigned int eax, ebx, ecx, edx;
unsigned int highest_cstate = 0;
unsigned int highest_subcstate = 0;
int i;
void *mwait_ptr;
if (!cpu_has(&current_cpu_data, X86_FEATURE_MWAIT))
return;
if (!cpu_has(&current_cpu_data, X86_FEATURE_CLFLSH))
return;
if (current_cpu_data.cpuid_level < CPUID_MWAIT_LEAF)
return;
eax = CPUID_MWAIT_LEAF;
ecx = 0;
native_cpuid(&eax, &ebx, &ecx, &edx);
/*
* eax will be 0 if EDX enumeration is not valid.
* Initialized below to cstate, sub_cstate value when EDX is valid.
*/
if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED)) {
eax = 0;
} else {
edx >>= MWAIT_SUBSTATE_SIZE;
for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) {
if (edx & MWAIT_SUBSTATE_MASK) {
highest_cstate = i;
highest_subcstate = edx & MWAIT_SUBSTATE_MASK;
}
}
eax = (highest_cstate << MWAIT_SUBSTATE_SIZE) |
(highest_subcstate - 1);
}
/*
* This should be a memory location in a cache line which is
* unlikely to be touched by other processors. The actual
* content is immaterial as it is not actually modified in any way.
*/
mwait_ptr = &current_thread_info()->flags;
wbinvd();
while (1) {
/*
* The CLFLUSH is a workaround for erratum AAI65 for
* the Xeon 7400 series. It's not clear it is actually
* needed, but it should be harmless in either case.
* The WBINVD is insufficient due to the spurious-wakeup
* case where we return around the loop.
*/
clflush(mwait_ptr);
__monitor(mwait_ptr, 0, 0);
mb();
__mwait(eax, 0);
}
}
static inline void hlt_play_dead(void)
{
if (current_cpu_data.x86 >= 4)
wbinvd();
while (1) {
native_halt();
}
}
void native_play_dead(void) void native_play_dead(void)
{ {
play_dead_common(); play_dead_common();
tboot_shutdown(TB_SHUTDOWN_WFS); tboot_shutdown(TB_SHUTDOWN_WFS);
wbinvd_halt();
mwait_play_dead(); /* Only returns on failure */
hlt_play_dead();
} }
#else /* ... !CONFIG_HOTPLUG_CPU */ #else /* ... !CONFIG_HOTPLUG_CPU */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment