Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 212048
b: refs/heads/master
c: ea53069
h: refs/heads/master
v: v3
  • Loading branch information
H. Peter Anvin committed Sep 17, 2010
1 parent 06df4fa commit 8db38af
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 25 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: bc83cccc761953f878088cdfa682de0970b5561f
refs/heads/master: ea53069231f9317062910d6e772cca4ce93de8c8
23 changes: 0 additions & 23 deletions trunk/arch/x86/include/asm/processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -764,29 +764,6 @@ extern unsigned long idle_halt;
extern unsigned long idle_nomwait;
extern bool c1e_detected;

/*
* 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 int sysenter_setup(void);

Expand Down
63 changes: 62 additions & 1 deletion trunk/arch/x86/kernel/smpboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
#include <asm/pgtable.h>
#include <asm/tlbflush.h>
#include <asm/mtrr.h>
#include <asm/mwait.h>
#include <asm/vmi.h>
#include <asm/apic.h>
#include <asm/setup.h>
Expand Down Expand Up @@ -1383,11 +1384,71 @@ void play_dead_common(void)
local_irq_disable();
}

/*
* 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;

if (!cpu_has(&current_cpu_data, X86_FEATURE_MWAIT))
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);
}

while (1) {
mb();
wbinvd();
__monitor(&current_thread_info()->flags, 0, 0);
mb();
__mwait(eax, 0);
}
}

static inline void hlt_play_dead(void)
{
while (1) {
mb();
if (current_cpu_data.x86 >= 4)
wbinvd();
mb();
native_halt();
}
}

void native_play_dead(void)
{
play_dead_common();
tboot_shutdown(TB_SHUTDOWN_WFS);
wbinvd_halt();

mwait_play_dead(); /* Only returns on failure */
hlt_play_dead();
}

#else /* ... !CONFIG_HOTPLUG_CPU */
Expand Down

0 comments on commit 8db38af

Please sign in to comment.