|
@@ -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>
|
|
@@ -1395,11 +1396,88 @@ 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;
|
|
|
+ void *mwait_ptr;
|
|
|
+
|
|
|
+ if (!cpu_has(¤t_cpu_data, X86_FEATURE_MWAIT))
|
|
|
+ return;
|
|
|
+ if (!cpu_has(¤t_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 = ¤t_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)
|
|
|
{
|
|
|
play_dead_common();
|
|
|
tboot_shutdown(TB_SHUTDOWN_WFS);
|
|
|
- wbinvd_halt();
|
|
|
+
|
|
|
+ mwait_play_dead(); /* Only returns on failure */
|
|
|
+ hlt_play_dead();
|
|
|
}
|
|
|
|
|
|
#else /* ... !CONFIG_HOTPLUG_CPU */
|