diff options
author | H. Peter Anvin <hpa@linux.intel.com> | 2010-09-17 18:39:11 -0400 |
---|---|---|
committer | H. Peter Anvin <hpa@linux.intel.com> | 2010-09-17 18:39:11 -0400 |
commit | ea53069231f9317062910d6e772cca4ce93de8c8 (patch) | |
tree | 0a78cf38c59c92114473925c74a3cf86ed1e543b | |
parent | bc83cccc761953f878088cdfa682de0970b5561f (diff) |
x86, hotplug: Use mwait to offline a processor, fix the legacy case
The code in native_play_dead() has a number of problems:
1. We should use MWAIT when available, to put ourselves into a deeper
sleep state.
2. We use the existence of CLFLUSH to determine if WBINVD is safe, but
that is totally bogus -- WBINVD is 486+, whereas CLFLUSH is a much
later addition.
3. We should do WBINVD inside the loop, just in case of something like
setting an A bit on page tables. Pointed out by Arjan van de Ven.
This code is based in part of a previous patch by Venki Pallipadi, but
unlike that patch this one keeps all the detection code local instead
of pre-caching a bunch of information. We're shutting down the CPU;
there is absolutely no hurry.
This patch moves all the code to C and deletes the global
wbinvd_halt() which is broken anyway.
Originally-by: Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
Reviewed-by: Arjan van de Ven <arjan@linux.intel.com>
Cc: Len Brown <lenb@kernel.org>
Cc: Venkatesh Pallipadi <venki@google.com>
Cc: Peter Zijlstra <a.p.zijlstra@chello.hl>
LKML-Reference: <20090522232230.162239000@intel.com>
-rw-r--r-- | arch/x86/include/asm/processor.h | 23 | ||||
-rw-r--r-- | arch/x86/kernel/smpboot.c | 63 |
2 files changed, 62 insertions, 24 deletions
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 325b7bdbebaa..f358241e4121 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h | |||
@@ -764,29 +764,6 @@ extern unsigned long idle_halt; | |||
764 | extern unsigned long idle_nomwait; | 764 | extern unsigned long idle_nomwait; |
765 | extern bool c1e_detected; | 765 | extern bool c1e_detected; |
766 | 766 | ||
767 | /* | ||
768 | * on systems with caches, caches must be flashed as the absolute | ||
769 | * last instruction before going into a suspended halt. Otherwise, | ||
770 | * dirty data can linger in the cache and become stale on resume, | ||
771 | * leading to strange errors. | ||
772 | * | ||
773 | * perform a variety of operations to guarantee that the compiler | ||
774 | * will not reorder instructions. wbinvd itself is serializing | ||
775 | * so the processor will not reorder. | ||
776 | * | ||
777 | * Systems without cache can just go into halt. | ||
778 | */ | ||
779 | static inline void wbinvd_halt(void) | ||
780 | { | ||
781 | mb(); | ||
782 | /* check for clflush to determine if wbinvd is legal */ | ||
783 | if (cpu_has_clflush) | ||
784 | asm volatile("cli; wbinvd; 1: hlt; jmp 1b" : : : "memory"); | ||
785 | else | ||
786 | while (1) | ||
787 | halt(); | ||
788 | } | ||
789 | |||
790 | extern void enable_sep_cpu(void); | 767 | extern void enable_sep_cpu(void); |
791 | extern int sysenter_setup(void); | 768 | extern int sysenter_setup(void); |
792 | 769 | ||
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 8b3bfc4dd708..07bf4233441d 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c | |||
@@ -62,6 +62,7 @@ | |||
62 | #include <asm/pgtable.h> | 62 | #include <asm/pgtable.h> |
63 | #include <asm/tlbflush.h> | 63 | #include <asm/tlbflush.h> |
64 | #include <asm/mtrr.h> | 64 | #include <asm/mtrr.h> |
65 | #include <asm/mwait.h> | ||
65 | #include <asm/vmi.h> | 66 | #include <asm/vmi.h> |
66 | #include <asm/apic.h> | 67 | #include <asm/apic.h> |
67 | #include <asm/setup.h> | 68 | #include <asm/setup.h> |
@@ -1383,11 +1384,71 @@ void play_dead_common(void) | |||
1383 | local_irq_disable(); | 1384 | local_irq_disable(); |
1384 | } | 1385 | } |
1385 | 1386 | ||
1387 | /* | ||
1388 | * We need to flush the caches before going to sleep, lest we have | ||
1389 | * dirty data in our caches when we come back up. | ||
1390 | */ | ||
1391 | static inline void mwait_play_dead(void) | ||
1392 | { | ||
1393 | unsigned int eax, ebx, ecx, edx; | ||
1394 | unsigned int highest_cstate = 0; | ||
1395 | unsigned int highest_subcstate = 0; | ||
1396 | int i; | ||
1397 | |||
1398 | if (!cpu_has(¤t_cpu_data, X86_FEATURE_MWAIT)) | ||
1399 | return; | ||
1400 | if (current_cpu_data.cpuid_level < CPUID_MWAIT_LEAF) | ||
1401 | return; | ||
1402 | |||
1403 | eax = CPUID_MWAIT_LEAF; | ||
1404 | ecx = 0; | ||
1405 | native_cpuid(&eax, &ebx, &ecx, &edx); | ||
1406 | |||
1407 | /* | ||
1408 | * eax will be 0 if EDX enumeration is not valid. | ||
1409 | * Initialized below to cstate, sub_cstate value when EDX is valid. | ||
1410 | */ | ||
1411 | if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED)) { | ||
1412 | eax = 0; | ||
1413 | } else { | ||
1414 | edx >>= MWAIT_SUBSTATE_SIZE; | ||
1415 | for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) { | ||
1416 | if (edx & MWAIT_SUBSTATE_MASK) { | ||
1417 | highest_cstate = i; | ||
1418 | highest_subcstate = edx & MWAIT_SUBSTATE_MASK; | ||
1419 | } | ||
1420 | } | ||
1421 | eax = (highest_cstate << MWAIT_SUBSTATE_SIZE) | | ||
1422 | (highest_subcstate - 1); | ||
1423 | } | ||
1424 | |||
1425 | while (1) { | ||
1426 | mb(); | ||
1427 | wbinvd(); | ||
1428 | __monitor(¤t_thread_info()->flags, 0, 0); | ||
1429 | mb(); | ||
1430 | __mwait(eax, 0); | ||
1431 | } | ||
1432 | } | ||
1433 | |||
1434 | static inline void hlt_play_dead(void) | ||
1435 | { | ||
1436 | while (1) { | ||
1437 | mb(); | ||
1438 | if (current_cpu_data.x86 >= 4) | ||
1439 | wbinvd(); | ||
1440 | mb(); | ||
1441 | native_halt(); | ||
1442 | } | ||
1443 | } | ||
1444 | |||
1386 | void native_play_dead(void) | 1445 | void native_play_dead(void) |
1387 | { | 1446 | { |
1388 | play_dead_common(); | 1447 | play_dead_common(); |
1389 | tboot_shutdown(TB_SHUTDOWN_WFS); | 1448 | tboot_shutdown(TB_SHUTDOWN_WFS); |
1390 | wbinvd_halt(); | 1449 | |
1450 | mwait_play_dead(); /* Only returns on failure */ | ||
1451 | hlt_play_dead(); | ||
1391 | } | 1452 | } |
1392 | 1453 | ||
1393 | #else /* ... !CONFIG_HOTPLUG_CPU */ | 1454 | #else /* ... !CONFIG_HOTPLUG_CPU */ |