diff options
author | Jeremy Erickson <jerickso@cs.unc.edu> | 2014-04-18 17:06:00 -0400 |
---|---|---|
committer | Jeremy Erickson <jerickso@cs.unc.edu> | 2014-04-18 17:06:00 -0400 |
commit | a215aa7b9ab3759c047201199fba64d3042d7f13 (patch) | |
tree | bca37493d9b2233450e6d3ffced1261d0e4f71fe /arch/x86/kernel/smpboot.c | |
parent | d31199a77ef606f1d06894385f1852181ba6136b (diff) |
Update 2.6.36 to 2.6.36.4wip-dissipation2-jerickso
Diffstat (limited to 'arch/x86/kernel/smpboot.c')
-rw-r--r-- | arch/x86/kernel/smpboot.c | 85 |
1 files changed, 84 insertions, 1 deletions
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index 8b3bfc4dd708..016179e5ba09 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c | |||
@@ -1383,11 +1383,94 @@ void play_dead_common(void) | |||
1383 | local_irq_disable(); | 1383 | local_irq_disable(); |
1384 | } | 1384 | } |
1385 | 1385 | ||
1386 | #define MWAIT_SUBSTATE_MASK 0xf | ||
1387 | #define MWAIT_SUBSTATE_SIZE 4 | ||
1388 | |||
1389 | #define CPUID_MWAIT_LEAF 5 | ||
1390 | #define CPUID5_ECX_EXTENSIONS_SUPPORTED 0x1 | ||
1391 | |||
1392 | /* | ||
1393 | * We need to flush the caches before going to sleep, lest we have | ||
1394 | * dirty data in our caches when we come back up. | ||
1395 | */ | ||
1396 | static inline void mwait_play_dead(void) | ||
1397 | { | ||
1398 | unsigned int eax, ebx, ecx, edx; | ||
1399 | unsigned int highest_cstate = 0; | ||
1400 | unsigned int highest_subcstate = 0; | ||
1401 | int i; | ||
1402 | void *mwait_ptr; | ||
1403 | |||
1404 | if (!cpu_has(¤t_cpu_data, X86_FEATURE_MWAIT)) | ||
1405 | return; | ||
1406 | if (!cpu_has(¤t_cpu_data, X86_FEATURE_CLFLSH)) | ||
1407 | return; | ||
1408 | if (current_cpu_data.cpuid_level < CPUID_MWAIT_LEAF) | ||
1409 | return; | ||
1410 | |||
1411 | eax = CPUID_MWAIT_LEAF; | ||
1412 | ecx = 0; | ||
1413 | native_cpuid(&eax, &ebx, &ecx, &edx); | ||
1414 | |||
1415 | /* | ||
1416 | * eax will be 0 if EDX enumeration is not valid. | ||
1417 | * Initialized below to cstate, sub_cstate value when EDX is valid. | ||
1418 | */ | ||
1419 | if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED)) { | ||
1420 | eax = 0; | ||
1421 | } else { | ||
1422 | edx >>= MWAIT_SUBSTATE_SIZE; | ||
1423 | for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) { | ||
1424 | if (edx & MWAIT_SUBSTATE_MASK) { | ||
1425 | highest_cstate = i; | ||
1426 | highest_subcstate = edx & MWAIT_SUBSTATE_MASK; | ||
1427 | } | ||
1428 | } | ||
1429 | eax = (highest_cstate << MWAIT_SUBSTATE_SIZE) | | ||
1430 | (highest_subcstate - 1); | ||
1431 | } | ||
1432 | |||
1433 | /* | ||
1434 | * This should be a memory location in a cache line which is | ||
1435 | * unlikely to be touched by other processors. The actual | ||
1436 | * content is immaterial as it is not actually modified in any way. | ||
1437 | */ | ||
1438 | mwait_ptr = ¤t_thread_info()->flags; | ||
1439 | |||
1440 | wbinvd(); | ||
1441 | |||
1442 | while (1) { | ||
1443 | /* | ||
1444 | * The CLFLUSH is a workaround for erratum AAI65 for | ||
1445 | * the Xeon 7400 series. It's not clear it is actually | ||
1446 | * needed, but it should be harmless in either case. | ||
1447 | * The WBINVD is insufficient due to the spurious-wakeup | ||
1448 | * case where we return around the loop. | ||
1449 | */ | ||
1450 | clflush(mwait_ptr); | ||
1451 | __monitor(mwait_ptr, 0, 0); | ||
1452 | mb(); | ||
1453 | __mwait(eax, 0); | ||
1454 | } | ||
1455 | } | ||
1456 | |||
1457 | static inline void hlt_play_dead(void) | ||
1458 | { | ||
1459 | if (current_cpu_data.x86 >= 4) | ||
1460 | wbinvd(); | ||
1461 | |||
1462 | while (1) { | ||
1463 | native_halt(); | ||
1464 | } | ||
1465 | } | ||
1466 | |||
1386 | void native_play_dead(void) | 1467 | void native_play_dead(void) |
1387 | { | 1468 | { |
1388 | play_dead_common(); | 1469 | play_dead_common(); |
1389 | tboot_shutdown(TB_SHUTDOWN_WFS); | 1470 | tboot_shutdown(TB_SHUTDOWN_WFS); |
1390 | wbinvd_halt(); | 1471 | |
1472 | mwait_play_dead(); /* Only returns on failure */ | ||
1473 | hlt_play_dead(); | ||
1391 | } | 1474 | } |
1392 | 1475 | ||
1393 | #else /* ... !CONFIG_HOTPLUG_CPU */ | 1476 | #else /* ... !CONFIG_HOTPLUG_CPU */ |