Merge branch 'pm-cpuidle'

Fix an issue in the PSCI cpuidle driver introduced recently and a nasty
x86 power regression introduced in 6.15:

 - Prevent freeing an uninitialized pointer in error path of
   dt_idle_state_present() in the PSCI cpuidle driver (Dan Carpenter).

 - Revert an x86 commit that went into 6.15 and caused idle power,
   including power in suspend-to-idle, to rise rather dramatically on
   systems booting with "nosmt" in the kernel command line (Rafael Wysocki).

* pm-cpuidle:
  Revert "x86/smp: Eliminate mwait_play_dead_cpuid_hint()"
  cpuidle: psci: Fix uninitialized variable in dt_idle_state_present()
This commit is contained in:
Rafael J. Wysocki 2025-05-30 20:21:36 +02:00
commit 3d031d0d8d
2 changed files with 51 additions and 12 deletions

View File

@ -1244,10 +1244,6 @@ 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);
@ -1293,6 +1289,50 @@ 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().
@ -1343,9 +1383,9 @@ void native_play_dead(void)
play_dead_common();
tboot_shutdown(TB_SHUTDOWN_WFS);
/* Below returns only on error. */
cpuidle_play_dead();
hlt_play_dead();
mwait_play_dead_cpuid_hint();
if (cpuidle_play_dead())
hlt_play_dead();
}
#else /* ... !CONFIG_HOTPLUG_CPU */

View File

@ -456,14 +456,13 @@ static struct faux_device_ops psci_cpuidle_ops = {
static bool __init dt_idle_state_present(void)
{
struct device_node *cpu_node __free(device_node);
struct device_node *state_node __free(device_node);
cpu_node = of_cpu_device_node_get(cpumask_first(cpu_possible_mask));
struct device_node *cpu_node __free(device_node) =
of_cpu_device_node_get(cpumask_first(cpu_possible_mask));
if (!cpu_node)
return false;
state_node = of_get_cpu_state_node(cpu_node, 0);
struct device_node *state_node __free(device_node) =
of_get_cpu_state_node(cpu_node, 0);
if (!state_node)
return false;