ACPI: processor: idle: Reset cpuidle on C-state list changes

When a power notification event occurs, existing ACPI idle states may
become obsolete. The current implementation only performs a partial
update, leaving critical cpuidle parameters, like target_residency_ns
and exit_latency_ns, stale. Furthermore, per-CPU cpuidle_device data,
including last_residency_ns, states_usage, and the disable flag, are not
properly synchronized. Using these stale values leads to incorrect power
management decisions.

To ensure all parameters are correctly synchronized, modify the
notification handling logic:

 1. Unregister all cpuidle_device instances to ensure a clean slate.
 2. Unregister and re-register the ACPI idle driver. This forces the
    framework to re-evaluate global state parameters and ensures the
    driver state matches the new hardware power profile.
 3. Re-initialize power information and re-register cpuidle_device for
    all possible CPUs to restore functional idle management.

This complete reset ensures that the cpuidle framework and the underlying
ACPI states are perfectly synchronized after a power state change.

Signed-off-by: Huisong Li <lihuisong@huawei.com>
[ rjw: Subject rewrite ]
Link: https://patch.msgid.link/20260407081141.2493581-3-lihuisong@huawei.com
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
Huisong Li
2026-04-07 16:11:41 +08:00
committed by Rafael J. Wysocki
parent a4c6c18e93
commit 07cba0de55

View File

@@ -1307,37 +1307,42 @@ int acpi_processor_power_state_has_changed(struct acpi_processor *pr)
*/
if (pr->id == 0 && cpuidle_get_driver() == &acpi_idle_driver) {
/* Protect against cpu-hotplug */
cpus_read_lock();
/* Unregister cpuidle device of all CPUs */
cpuidle_pause_and_lock();
/* Disable all cpuidle devices */
for_each_online_cpu(cpu) {
_pr = per_cpu(processors, cpu);
if (!_pr || !_pr->flags.power_setup_done)
continue;
for_each_possible_cpu(cpu) {
dev = per_cpu(acpi_cpuidle_device, cpu);
cpuidle_disable_device(dev);
}
/* Populate Updated C-state information */
acpi_processor_get_power_info(pr);
acpi_processor_setup_cpuidle_states(pr);
/* Enable all cpuidle devices */
for_each_online_cpu(cpu) {
_pr = per_cpu(processors, cpu);
if (!_pr || !_pr->flags.power_setup_done)
if (!_pr || !_pr->flags.power || !dev)
continue;
acpi_processor_get_power_info(_pr);
if (_pr->flags.power) {
dev = per_cpu(acpi_cpuidle_device, cpu);
acpi_processor_setup_cpuidle_dev(_pr, dev);
cpuidle_enable_device(dev);
}
cpuidle_unregister_device_no_lock(dev);
kfree(dev);
_pr->flags.power = 0;
}
cpuidle_resume_and_unlock();
/*
* Unregister ACPI idle driver, reinitialize ACPI idle states
* and register ACPI idle driver again.
*/
acpi_processor_unregister_idle_driver();
acpi_processor_register_idle_driver();
/*
* Reinitialize power information of all CPUs and re-register
* all cpuidle devices. Now idle states is ok to use, can enable
* cpuidle of each CPU safely one by one.
*/
for_each_possible_cpu(cpu) {
_pr = per_cpu(processors, cpu);
if (!_pr)
continue;
acpi_processor_power_init(_pr);
}
cpus_read_unlock();
}