Merge branch 'pm-cpuidle'

Merge cpuidle updates for 6.16-rc2:

 - Update data types of variables passed as arguments to
   mwait_idle_with_hints() to match the function definition
   after recent changes (Uros Bizjak).

 - Eliminate mwait_play_dead_cpuid_hint() again after reverting its
   elimination during the merge window due to a problem with handling
   "dead" SMT siblings, but this time prevent leaving them in C1 after
   initialization by taking them online and back offline when a proper
   cpuidle driver for the platform has been registered (Rafael Wysocki).

* pm-cpuidle:
  intel_idle: Update arguments of mwait_idle_with_hints()
  Reapply "x86/smp: Eliminate mwait_play_dead_cpuid_hint()"
  ACPI: processor: Rescan "dead" SMT siblings during initialization
  intel_idle: Rescan "dead" SMT siblings during initialization
  x86/smp: PM/hibernate: Split arch_resume_nosmt()
  intel_idle: Use subsys_initcall_sync() for initialization
This commit is contained in:
Rafael J. Wysocki 2025-06-13 21:28:07 +02:00
commit dd3581853c
8 changed files with 63 additions and 64 deletions

View File

@ -299,3 +299,27 @@ struct smp_ops smp_ops = {
.send_call_func_single_ipi = native_send_call_func_single_ipi,
};
EXPORT_SYMBOL_GPL(smp_ops);
int arch_cpu_rescan_dead_smt_siblings(void)
{
enum cpuhp_smt_control old = cpu_smt_control;
int ret;
/*
* If SMT has been disabled and SMT siblings are in HLT, bring them back
* online and offline them again so that they end up in MWAIT proper.
*
* Called with hotplug enabled.
*/
if (old != CPU_SMT_DISABLED && old != CPU_SMT_FORCE_DISABLED)
return 0;
ret = cpuhp_smt_enable();
if (ret)
return ret;
ret = cpuhp_smt_disable(old);
return ret;
}
EXPORT_SYMBOL_GPL(arch_cpu_rescan_dead_smt_siblings);

View File

@ -1244,6 +1244,10 @@ void play_dead_common(void)
local_irq_disable();
}
/*
* We need to flush the caches before going to sleep, lest we have
* dirty data in our caches when we come back up.
*/
void __noreturn mwait_play_dead(unsigned int eax_hint)
{
struct mwait_cpu_dead *md = this_cpu_ptr(&mwait_cpu_dead);
@ -1289,50 +1293,6 @@ void __noreturn mwait_play_dead(unsigned int eax_hint)
}
}
/*
* We need to flush the caches before going to sleep, lest we have
* dirty data in our caches when we come back up.
*/
static inline void mwait_play_dead_cpuid_hint(void)
{
unsigned int eax, ebx, ecx, edx;
unsigned int highest_cstate = 0;
unsigned int highest_subcstate = 0;
int i;
if (boot_cpu_data.x86_vendor == X86_VENDOR_AMD ||
boot_cpu_data.x86_vendor == X86_VENDOR_HYGON)
return;
if (!this_cpu_has(X86_FEATURE_MWAIT))
return;
if (!this_cpu_has(X86_FEATURE_CLFLUSH))
return;
eax = CPUID_LEAF_MWAIT;
ecx = 0;
native_cpuid(&eax, &ebx, &ecx, &edx);
/*
* eax will be 0 if EDX enumeration is not valid.
* Initialized below to cstate, sub_cstate value when EDX is valid.
*/
if (!(ecx & CPUID5_ECX_EXTENSIONS_SUPPORTED)) {
eax = 0;
} else {
edx >>= MWAIT_SUBSTATE_SIZE;
for (i = 0; i < 7 && edx; i++, edx >>= MWAIT_SUBSTATE_SIZE) {
if (edx & MWAIT_SUBSTATE_MASK) {
highest_cstate = i;
highest_subcstate = edx & MWAIT_SUBSTATE_MASK;
}
}
eax = (highest_cstate << MWAIT_SUBSTATE_SIZE) |
(highest_subcstate - 1);
}
mwait_play_dead(eax);
}
/*
* Kick all "offline" CPUs out of mwait on kexec(). See comment in
* mwait_play_dead().
@ -1383,9 +1343,9 @@ void native_play_dead(void)
play_dead_common();
tboot_shutdown(TB_SHUTDOWN_WFS);
mwait_play_dead_cpuid_hint();
if (cpuidle_play_dead())
hlt_play_dead();
/* Below returns only on error. */
cpuidle_play_dead();
hlt_play_dead();
}
#else /* ... !CONFIG_HOTPLUG_CPU */

View File

@ -192,7 +192,8 @@ int relocate_restore_code(void)
int arch_resume_nosmt(void)
{
int ret = 0;
int ret;
/*
* We reached this while coming out of hibernation. This means
* that SMT siblings are sleeping in hlt, as mwait is not safe
@ -206,18 +207,10 @@ int arch_resume_nosmt(void)
* Called with hotplug disabled.
*/
cpu_hotplug_enable();
if (cpu_smt_control == CPU_SMT_DISABLED ||
cpu_smt_control == CPU_SMT_FORCE_DISABLED) {
enum cpuhp_smt_control old = cpu_smt_control;
ret = cpuhp_smt_enable();
if (ret)
goto out;
ret = cpuhp_smt_disable(old);
if (ret)
goto out;
}
out:
ret = arch_cpu_rescan_dead_smt_siblings();
cpu_hotplug_disable();
return ret;
}

View File

@ -175,6 +175,12 @@ bool processor_physically_present(acpi_handle handle);
static inline void acpi_early_processor_control_setup(void) {}
#endif
#ifdef CONFIG_ACPI_PROCESSOR_CSTATE
void acpi_idle_rescan_dead_smt_siblings(void);
#else
static inline void acpi_idle_rescan_dead_smt_siblings(void) {}
#endif
/* --------------------------------------------------------------------------
Embedded Controller
-------------------------------------------------------------------------- */

View File

@ -279,6 +279,9 @@ static int __init acpi_processor_driver_init(void)
* after acpi_cppc_processor_probe() has been called for all online CPUs
*/
acpi_processor_init_invariance_cppc();
acpi_idle_rescan_dead_smt_siblings();
return 0;
err:
driver_unregister(&acpi_processor_driver);

View File

@ -24,6 +24,8 @@
#include <acpi/processor.h>
#include <linux/context_tracking.h>
#include "internal.h"
/*
* Include the apic definitions for x86 to have the APIC timer related defines
* available also for UP (on SMP it gets magically included via linux/smp.h).
@ -55,6 +57,12 @@ struct cpuidle_driver acpi_idle_driver = {
};
#ifdef CONFIG_ACPI_PROCESSOR_CSTATE
void acpi_idle_rescan_dead_smt_siblings(void)
{
if (cpuidle_get_driver() == &acpi_idle_driver)
arch_cpu_rescan_dead_smt_siblings();
}
static
DEFINE_PER_CPU(struct acpi_processor_cx * [CPUIDLE_STATE_MAX], acpi_cstate);

View File

@ -152,8 +152,8 @@ static __always_inline int __intel_idle(struct cpuidle_device *dev,
int index, bool irqoff)
{
struct cpuidle_state *state = &drv->states[index];
unsigned long eax = flg2MWAIT(state->flags);
unsigned long ecx = 1*irqoff; /* break on interrupt flag */
unsigned int eax = flg2MWAIT(state->flags);
unsigned int ecx = 1*irqoff; /* break on interrupt flag */
mwait_idle_with_hints(eax, ecx);
@ -226,9 +226,9 @@ static __cpuidle int intel_idle_xstate(struct cpuidle_device *dev,
static __cpuidle int intel_idle_s2idle(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index)
{
unsigned long ecx = 1; /* break on interrupt flag */
struct cpuidle_state *state = &drv->states[index];
unsigned long eax = flg2MWAIT(state->flags);
unsigned int eax = flg2MWAIT(state->flags);
unsigned int ecx = 1; /* break on interrupt flag */
if (state->flags & CPUIDLE_FLAG_INIT_XSTATE)
fpu_idle_fpregs();
@ -2507,6 +2507,8 @@ static int __init intel_idle_init(void)
pr_debug("Local APIC timer is reliable in %s\n",
boot_cpu_has(X86_FEATURE_ARAT) ? "all C-states" : "C1");
arch_cpu_rescan_dead_smt_siblings();
return 0;
hp_setup_fail:
@ -2518,7 +2520,7 @@ static int __init intel_idle_init(void)
return retval;
}
device_initcall(intel_idle_init);
subsys_initcall_sync(intel_idle_init);
/*
* We are not really modular, but we used to support that. Meaning we also

View File

@ -120,6 +120,7 @@ extern void cpu_maps_update_begin(void);
extern void cpu_maps_update_done(void);
int bringup_hibernate_cpu(unsigned int sleep_cpu);
void bringup_nonboot_cpus(unsigned int max_cpus);
int arch_cpu_rescan_dead_smt_siblings(void);
#else /* CONFIG_SMP */
#define cpuhp_tasks_frozen 0
@ -134,6 +135,8 @@ static inline void cpu_maps_update_done(void)
static inline int add_cpu(unsigned int cpu) { return 0;}
static inline int arch_cpu_rescan_dead_smt_siblings(void) { return 0; }
#endif /* CONFIG_SMP */
extern const struct bus_type cpu_subsys;