A bug is reported(https://bugzilla.redhat.com/show_bug.cgi?id=1227208) that, after resuming from S3, CPU is working at a low speed. After investigation, it is found that, BIOS has modified the value of THERM_CONTROL register during S3, changes it from 0 to 0x10, while the latter means CPU can only get 25% of the Duty Cycle, and this caused the problem.
Simple scenario to reproduce: 1.Boot up system 2.Get MSR with address 0x19a, it should output 0 3.Put system into sleep, then wake up 4.Get MSR with address 0x19a, it should output 0(actual it outputs 0x10) Although this is a BIOS issue, it would be more robust for linux to deal with this situation. This patch fixes this issue by introducing a quirk for problematic platform, thus save/restore THERM_CONTROL(now called CLOCK_MODULATION) register on suspend/resume. Since both 64/32-bit kernels are affected, this patch covers 64/32-bit common code path. And because THERM_CONTROL might not be available or readable in any situation, we use rdmsrl_safe to safely save these MSR registers. Tested-by: Marcin Kaszewski <marcin.kaszew...@intel.com> Signed-off-by: Chen Yu <yu.c.c...@intel.com> --- v3: - Simplify the patch to only focus on THERM_CONTROL register. This will make things 'just work'. v2: - Cover both 64/32-bit common code path. Use rdmsrl_safe to safely read MSR. Introduce a quirk framework for save/restore specified MSR on different platforms. --- arch/x86/include/asm/suspend_32.h | 2 ++ arch/x86/include/asm/suspend_64.h | 2 ++ arch/x86/power/cpu.c | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/arch/x86/include/asm/suspend_32.h b/arch/x86/include/asm/suspend_32.h index d1793f0..ae2785f 100644 --- a/arch/x86/include/asm/suspend_32.h +++ b/arch/x86/include/asm/suspend_32.h @@ -15,6 +15,8 @@ struct saved_context { unsigned long cr0, cr2, cr3, cr4; u64 misc_enable; bool misc_enable_saved; + u64 therm_control; + bool therm_control_saved; struct desc_ptr gdt_desc; struct desc_ptr idt; u16 ldt; diff --git a/arch/x86/include/asm/suspend_64.h b/arch/x86/include/asm/suspend_64.h index 7ebf0eb..b1e6fe6 100644 --- a/arch/x86/include/asm/suspend_64.h +++ b/arch/x86/include/asm/suspend_64.h @@ -24,6 +24,8 @@ struct saved_context { unsigned long cr0, cr2, cr3, cr4, cr8; u64 misc_enable; bool misc_enable_saved; + u64 therm_control; + bool therm_control_saved; unsigned long efer; u16 gdt_pad; /* Unused */ struct desc_ptr gdt_desc; diff --git a/arch/x86/power/cpu.c b/arch/x86/power/cpu.c index 9ab5279..7c14ced 100644 --- a/arch/x86/power/cpu.c +++ b/arch/x86/power/cpu.c @@ -23,6 +23,7 @@ #include <asm/debugreg.h> #include <asm/cpu.h> #include <asm/mmu_context.h> +#include <linux/dmi.h> #ifdef CONFIG_X86_32 __visible unsigned long saved_context_ebx; @@ -111,6 +112,12 @@ static void __save_processor_state(struct saved_context *ctxt) #endif ctxt->misc_enable_saved = !rdmsrl_safe(MSR_IA32_MISC_ENABLE, &ctxt->misc_enable); + + if (ctxt->therm_control_saved) { + ctxt->therm_control_saved = + !rdmsrl_safe(MSR_IA32_THERM_CONTROL, + &ctxt->therm_control); + } } /* Needed by apm.c */ @@ -229,6 +236,9 @@ static void notrace __restore_processor_state(struct saved_context *ctxt) x86_platform.restore_sched_clock_state(); mtrr_bp_restore(); perf_restore_debug_store(); + + if (ctxt->therm_control_saved) + wrmsrl(MSR_IA32_THERM_CONTROL, ctxt->therm_control); } /* Needed by apm.c */ @@ -320,3 +330,30 @@ static int __init bsp_pm_check_init(void) } core_initcall(bsp_pm_check_init); + +static int therm_control_need_save(const struct dmi_system_id *d) +{ + saved_context.therm_control_saved = true; + return 0; +} + +static struct dmi_system_id msr_save_dmi_table[] = { + { + .callback = therm_control_need_save, + .ident = "BROADWELL BDX_EP", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "GRANTLEY"), + DMI_MATCH(DMI_PRODUCT_VERSION, "E63448-400"), + }, + }, + {} +}; +static int pm_check_save_msr(void) +{ + saved_context.therm_control_saved = false; + dmi_check_system(msr_save_dmi_table); + return 0; +} + +late_initcall(pm_check_save_msr); -- 1.8.4.2 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/