Skip to content

Commit dfa11d5

Browse files
spandruvadagregkh
authored andcommitted
ACPI / processor: Request native thermal interrupt handling via _OSC
commit a21211672c9a1d730a39aa65d4a5b3414700adfb upstream. There are several reports of freeze on enabling HWP (Hardware PStates) feature on Skylake-based systems by the Intel P-states driver. The root cause is identified as the HWP interrupts causing BIOS code to freeze. HWP interrupts use the thermal LVT which can be handled by Linux natively, but on the affected Skylake-based systems SMM will respond to it by default. This is a problem for several reasons: - On the affected systems the SMM thermal LVT handler is broken (it will crash when invoked) and a BIOS update is necessary to fix it. - With thermal interrupt handled in SMM we lose all of the reporting features of the arch/x86/kernel/cpu/mcheck/therm_throt driver. - Some thermal drivers like x86-package-temp depend on the thermal threshold interrupts signaled via the thermal LVT. - The HWP interrupts are useful for debugging and tuning performance (if the kernel can handle them). The native handling of thermal interrupts needs to be enabled because of that. This requires some way to tell SMM that the OS can handle thermal interrupts. That can be done by using _OSC/_PDC in processor scope very early during ACPI initialization. The meaning of _OSC/_PDC bit 12 in processor scope is whether or not the OS supports native handling of interrupts for Collaborative Processor Performance Control (CPPC) notifications. Since on HWP-capable systems CPPC is a firmware interface to HWP, setting this bit effectively tells the firmware that the OS will handle thermal interrupts natively going forward. For details on _OSC/_PDC refer to: http://www.intel.com/content/www/us/en/standards/processor-vendor-specific-acpi-specification.html To implement the _OSC/_PDC handshake as described, introduce a new function, acpi_early_processor_osc(), that walks the ACPI namespace looking for ACPI processor objects and invokes _OSC for them with bit 12 in the capabilities buffer set and terminates the namespace walk on the first success. Also modify intel_thermal_interrupt() to clear HWP status bits in the HWP_STATUS MSR to acknowledge HWP interrupts (which prevents them from firing continuously). Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com> [ rjw: Subject & changelog, function rename ] Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 8e1001c commit dfa11d5

4 files changed

Lines changed: 64 additions & 0 deletions

File tree

arch/x86/kernel/cpu/mcheck/therm_throt.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,9 @@ static void intel_thermal_interrupt(void)
385385
{
386386
__u64 msr_val;
387387

388+
if (static_cpu_has(X86_FEATURE_HWP))
389+
wrmsrl_safe(MSR_HWP_STATUS, 0);
390+
388391
rdmsrl(MSR_IA32_THERM_STATUS, msr_val);
389392

390393
/* Check for violation of core thermal thresholds*/

drivers/acpi/acpi_processor.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,58 @@ static void acpi_processor_remove(struct acpi_device *device)
491491
}
492492
#endif /* CONFIG_ACPI_HOTPLUG_CPU */
493493

494+
#ifdef CONFIG_X86
495+
static bool acpi_hwp_native_thermal_lvt_set;
496+
static acpi_status __init acpi_hwp_native_thermal_lvt_osc(acpi_handle handle,
497+
u32 lvl,
498+
void *context,
499+
void **rv)
500+
{
501+
u8 sb_uuid_str[] = "4077A616-290C-47BE-9EBD-D87058713953";
502+
u32 capbuf[2];
503+
struct acpi_osc_context osc_context = {
504+
.uuid_str = sb_uuid_str,
505+
.rev = 1,
506+
.cap.length = 8,
507+
.cap.pointer = capbuf,
508+
};
509+
510+
if (acpi_hwp_native_thermal_lvt_set)
511+
return AE_CTRL_TERMINATE;
512+
513+
capbuf[0] = 0x0000;
514+
capbuf[1] = 0x1000; /* set bit 12 */
515+
516+
if (ACPI_SUCCESS(acpi_run_osc(handle, &osc_context))) {
517+
if (osc_context.ret.pointer && osc_context.ret.length > 1) {
518+
u32 *capbuf_ret = osc_context.ret.pointer;
519+
520+
if (capbuf_ret[1] & 0x1000) {
521+
acpi_handle_info(handle,
522+
"_OSC native thermal LVT Acked\n");
523+
acpi_hwp_native_thermal_lvt_set = true;
524+
}
525+
}
526+
kfree(osc_context.ret.pointer);
527+
}
528+
529+
return AE_OK;
530+
}
531+
532+
void __init acpi_early_processor_osc(void)
533+
{
534+
if (boot_cpu_has(X86_FEATURE_HWP)) {
535+
acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
536+
ACPI_UINT32_MAX,
537+
acpi_hwp_native_thermal_lvt_osc,
538+
NULL, NULL, NULL);
539+
acpi_get_devices(ACPI_PROCESSOR_DEVICE_HID,
540+
acpi_hwp_native_thermal_lvt_osc,
541+
NULL, NULL);
542+
}
543+
}
544+
#endif
545+
494546
/*
495547
* The following ACPI IDs are known to be suitable for representing as
496548
* processor devices.

drivers/acpi/bus.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,9 @@ static int __init acpi_bus_init(void)
10041004
goto error1;
10051005
}
10061006

1007+
/* Set capability bits for _OSC under processor scope */
1008+
acpi_early_processor_osc();
1009+
10071010
/*
10081011
* _OSC method may exist in module level code,
10091012
* so it must be run after ACPI_FULL_INITIALIZATION

drivers/acpi/internal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,12 @@ void acpi_early_processor_set_pdc(void);
130130
static inline void acpi_early_processor_set_pdc(void) {}
131131
#endif
132132

133+
#ifdef CONFIG_X86
134+
void acpi_early_processor_osc(void);
135+
#else
136+
static inline void acpi_early_processor_osc(void) {}
137+
#endif
138+
133139
/* --------------------------------------------------------------------------
134140
Embedded Controller
135141
-------------------------------------------------------------------------- */

0 commit comments

Comments
 (0)