diff options
author | Len Brown <len.brown@intel.com> | 2014-01-15 00:37:34 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2015-03-16 06:14:21 -0400 |
commit | b253149b843f89cd300cbdbea27ce1f847506f99 (patch) | |
tree | ad121a964474d5097e5d58df062d0a4215fc185d /arch/x86/kernel/process.c | |
parent | 44fb085bfa17628c6d2aaa6af6b292a8499e9cbd (diff) |
sched/idle/x86: Restore mwait_idle() to fix boot hangs, to improve power savings and to improve performance
In Linux-3.9 we removed the mwait_idle() loop:
69fb3676df33 ("x86 idle: remove mwait_idle() and "idle=mwait" cmdline param")
The reasoning was that modern machines should be sufficiently
happy during the boot process using the default_idle() HALT
loop, until cpuidle loads and either acpi_idle or intel_idle
invoke the newer MWAIT-with-hints idle loop.
But two machines reported problems:
1. Certain Core2-era machines support MWAIT-C1 and HALT only.
MWAIT-C1 is preferred for optimal power and performance.
But if they support just C1, cpuidle never loads and
so they use the boot-time default idle loop forever.
2. Some laptops will boot-hang if HALT is used,
but will boot successfully if MWAIT is used.
This appears to be a hidden assumption in BIOS SMI,
that is presumably valid on the proprietary OS
where the BIOS was validated.
https://bugzilla.kernel.org/show_bug.cgi?id=60770
So here we effectively revert the patch above, restoring
the mwait_idle() loop. However, we don't bother restoring
the idle=mwait cmdline parameter, since it appears to add
no value.
Maintainer notes:
For 3.9, simply revert 69fb3676df
for 3.10, patch -F3 applies, fuzz needed due to __cpuinit use in
context For 3.11, 3.12, 3.13, this patch applies cleanly
Tested-by: Mike Galbraith <bitbucket@online.de>
Signed-off-by: Len Brown <len.brown@intel.com>
Acked-by: Mike Galbraith <bitbucket@online.de>
Cc: <stable@vger.kernel.org> # 3.9+
Cc: Borislav Petkov <bp@alien8.de>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Ian Malone <ibmalone@gmail.com>
Cc: Josh Boyer <jwboyer@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/345254a551eb5a6a866e048d7ab570fd2193aca4.1389763084.git.len.brown@intel.com
[ Ported to recent kernels. ]
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/kernel/process.c')
-rw-r--r-- | arch/x86/kernel/process.c | 47 |
1 files changed, 47 insertions, 0 deletions
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index e127ddaa2d5a..da06f741d2a6 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <asm/syscalls.h> | 24 | #include <asm/syscalls.h> |
25 | #include <asm/idle.h> | 25 | #include <asm/idle.h> |
26 | #include <asm/uaccess.h> | 26 | #include <asm/uaccess.h> |
27 | #include <asm/mwait.h> | ||
27 | #include <asm/i387.h> | 28 | #include <asm/i387.h> |
28 | #include <asm/fpu-internal.h> | 29 | #include <asm/fpu-internal.h> |
29 | #include <asm/debugreg.h> | 30 | #include <asm/debugreg.h> |
@@ -398,6 +399,49 @@ static void amd_e400_idle(void) | |||
398 | default_idle(); | 399 | default_idle(); |
399 | } | 400 | } |
400 | 401 | ||
402 | /* | ||
403 | * Intel Core2 and older machines prefer MWAIT over HALT for C1. | ||
404 | * We can't rely on cpuidle installing MWAIT, because it will not load | ||
405 | * on systems that support only C1 -- so the boot default must be MWAIT. | ||
406 | * | ||
407 | * Some AMD machines are the opposite, they depend on using HALT. | ||
408 | * | ||
409 | * So for default C1, which is used during boot until cpuidle loads, | ||
410 | * use MWAIT-C1 on Intel HW that has it, else use HALT. | ||
411 | */ | ||
412 | static int prefer_mwait_c1_over_halt(const struct cpuinfo_x86 *c) | ||
413 | { | ||
414 | if (c->x86_vendor != X86_VENDOR_INTEL) | ||
415 | return 0; | ||
416 | |||
417 | if (!cpu_has(c, X86_FEATURE_MWAIT)) | ||
418 | return 0; | ||
419 | |||
420 | return 1; | ||
421 | } | ||
422 | |||
423 | /* | ||
424 | * MONITOR/MWAIT with no hints, used for default default C1 state. | ||
425 | * This invokes MWAIT with interrutps enabled and no flags, | ||
426 | * which is backwards compatible with the original MWAIT implementation. | ||
427 | */ | ||
428 | |||
429 | static void mwait_idle(void) | ||
430 | { | ||
431 | if (!need_resched()) { | ||
432 | if (this_cpu_has(X86_BUG_CLFLUSH_MONITOR)) | ||
433 | clflush((void *)¤t_thread_info()->flags); | ||
434 | |||
435 | __monitor((void *)¤t_thread_info()->flags, 0, 0); | ||
436 | smp_mb(); | ||
437 | if (!need_resched()) | ||
438 | __sti_mwait(0, 0); | ||
439 | else | ||
440 | local_irq_enable(); | ||
441 | } else | ||
442 | local_irq_enable(); | ||
443 | } | ||
444 | |||
401 | void select_idle_routine(const struct cpuinfo_x86 *c) | 445 | void select_idle_routine(const struct cpuinfo_x86 *c) |
402 | { | 446 | { |
403 | #ifdef CONFIG_SMP | 447 | #ifdef CONFIG_SMP |
@@ -411,6 +455,9 @@ void select_idle_routine(const struct cpuinfo_x86 *c) | |||
411 | /* E400: APIC timer interrupt does not wake up CPU from C1e */ | 455 | /* E400: APIC timer interrupt does not wake up CPU from C1e */ |
412 | pr_info("using AMD E400 aware idle routine\n"); | 456 | pr_info("using AMD E400 aware idle routine\n"); |
413 | x86_idle = amd_e400_idle; | 457 | x86_idle = amd_e400_idle; |
458 | } else if (prefer_mwait_c1_over_halt(c)) { | ||
459 | pr_info("using mwait in idle threads\n"); | ||
460 | x86_idle = mwait_idle; | ||
414 | } else | 461 | } else |
415 | x86_idle = default_idle; | 462 | x86_idle = default_idle; |
416 | } | 463 | } |