| From d70e28f57e14a481977436695b0c9ba165472431 Mon Sep 17 00:00:00 2001 |
| From: Len Brown <len.brown@intel.com> |
| Date: Sun, 13 Mar 2016 00:33:48 -0500 |
| Subject: intel_idle: prevent SKL-H boot failure when C8+C9+C10 enabled |
| |
| From: Len Brown <len.brown@intel.com> |
| |
| commit d70e28f57e14a481977436695b0c9ba165472431 upstream. |
| |
| Some SKL-H configurations require "intel_idle.max_cstate=7" to boot. |
| While that is an effective workaround, it disables C10. |
| |
| This patch detects the problematic configuration, |
| and disables C8 and C9, keeping C10 enabled. |
| |
| Note that enabling SGX in BIOS SETUP can also prevent this issue, |
| if the system BIOS provides that option. |
| |
| https://bugzilla.kernel.org/show_bug.cgi?id=109081 |
| "Freezes with Intel i7 6700HQ (Skylake), unless intel_idle.max_cstate=7" |
| |
| Signed-off-by: Len Brown <len.brown@intel.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/idle/intel_idle.c | 106 ++++++++++++++++++++++++++++++++++++---------- |
| 1 file changed, 85 insertions(+), 21 deletions(-) |
| |
| --- a/drivers/idle/intel_idle.c |
| +++ b/drivers/idle/intel_idle.c |
| @@ -65,7 +65,7 @@ |
| #include <asm/mwait.h> |
| #include <asm/msr.h> |
| |
| -#define INTEL_IDLE_VERSION "0.4" |
| +#define INTEL_IDLE_VERSION "0.4.1" |
| #define PREFIX "intel_idle: " |
| |
| static struct cpuidle_driver intel_idle_driver = { |
| @@ -994,36 +994,92 @@ static void intel_idle_cpuidle_devices_u |
| } |
| |
| /* |
| - * intel_idle_state_table_update() |
| - * |
| - * Update the default state_table for this CPU-id |
| + * ivt_idle_state_table_update(void) |
| * |
| - * Currently used to access tuned IVT multi-socket targets |
| + * Tune IVT multi-socket targets |
| * Assumption: num_sockets == (max_package_num + 1) |
| */ |
| -void intel_idle_state_table_update(void) |
| +static void ivt_idle_state_table_update(void) |
| { |
| /* IVT uses a different table for 1-2, 3-4, and > 4 sockets */ |
| - if (boot_cpu_data.x86_model == 0x3e) { /* IVT */ |
| - int cpu, package_num, num_sockets = 1; |
| + int cpu, package_num, num_sockets = 1; |
| |
| - for_each_online_cpu(cpu) { |
| - package_num = topology_physical_package_id(cpu); |
| - if (package_num + 1 > num_sockets) { |
| - num_sockets = package_num + 1; |
| - |
| - if (num_sockets > 4) { |
| - cpuidle_state_table = ivt_cstates_8s; |
| - return; |
| - } |
| + for_each_online_cpu(cpu) { |
| + package_num = topology_physical_package_id(cpu); |
| + if (package_num + 1 > num_sockets) { |
| + num_sockets = package_num + 1; |
| + |
| + if (num_sockets > 4) { |
| + cpuidle_state_table = ivt_cstates_8s; |
| + return; |
| } |
| } |
| + } |
| + |
| + if (num_sockets > 2) |
| + cpuidle_state_table = ivt_cstates_4s; |
| + |
| + /* else, 1 and 2 socket systems use default ivt_cstates */ |
| +} |
| +/* |
| + * sklh_idle_state_table_update(void) |
| + * |
| + * On SKL-H (model 0x5e) disable C8 and C9 if: |
| + * C10 is enabled and SGX disabled |
| + */ |
| +static void sklh_idle_state_table_update(void) |
| +{ |
| + unsigned long long msr; |
| + unsigned int eax, ebx, ecx, edx; |
| + |
| + |
| + /* if PC10 disabled via cmdline intel_idle.max_cstate=7 or shallower */ |
| + if (max_cstate <= 7) |
| + return; |
| + |
| + /* if PC10 not present in CPUID.MWAIT.EDX */ |
| + if ((mwait_substates & (0xF << 28)) == 0) |
| + return; |
| + |
| + rdmsrl(MSR_NHM_SNB_PKG_CST_CFG_CTL, msr); |
| + |
| + /* PC10 is not enabled in PKG C-state limit */ |
| + if ((msr & 0xF) != 8) |
| + return; |
| + |
| + ecx = 0; |
| + cpuid(7, &eax, &ebx, &ecx, &edx); |
| + |
| + /* if SGX is present */ |
| + if (ebx & (1 << 2)) { |
| + |
| + rdmsrl(MSR_IA32_FEATURE_CONTROL, msr); |
| |
| - if (num_sockets > 2) |
| - cpuidle_state_table = ivt_cstates_4s; |
| - /* else, 1 and 2 socket systems use default ivt_cstates */ |
| + /* if SGX is enabled */ |
| + if (msr & (1 << 18)) |
| + return; |
| + } |
| + |
| + skl_cstates[5].disabled = 1; /* C8-SKL */ |
| + skl_cstates[6].disabled = 1; /* C9-SKL */ |
| +} |
| +/* |
| + * intel_idle_state_table_update() |
| + * |
| + * Update the default state_table for this CPU-id |
| + */ |
| + |
| +static void intel_idle_state_table_update(void) |
| +{ |
| + switch (boot_cpu_data.x86_model) { |
| + |
| + case 0x3e: /* IVT */ |
| + ivt_idle_state_table_update(); |
| + break; |
| + case 0x5e: /* SKL-H */ |
| + sklh_idle_state_table_update(); |
| + break; |
| } |
| - return; |
| } |
| |
| /* |
| @@ -1063,6 +1119,14 @@ static int __init intel_idle_cpuidle_dri |
| if (num_substates == 0) |
| continue; |
| |
| + /* if state marked as disabled, skip it */ |
| + if (cpuidle_state_table[cstate].disabled != 0) { |
| + pr_debug(PREFIX "state %s is disabled", |
| + cpuidle_state_table[cstate].name); |
| + continue; |
| + } |
| + |
| + |
| if (((mwait_cstate + 1) > 2) && |
| !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) |
| mark_tsc_unstable("TSC halts in idle" |