]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ACPI: processor: idle: Reset cpuidle on C-state list changes
authorHuisong Li <lihuisong@huawei.com>
Tue, 7 Apr 2026 08:11:41 +0000 (16:11 +0800)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 7 Apr 2026 13:32:20 +0000 (15:32 +0200)
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>
drivers/acpi/processor_idle.c

index c1cddb4a5887b8fb1a4c7fa64effca378f5be132..ee5facccbe10c503c4d3275a79f584a6dc472c0f 100644 (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();
-               cpuidle_pause_and_lock();
 
-               /* Disable all cpuidle devices */
-               for_each_online_cpu(cpu) {
+               /* Unregister cpuidle device of all CPUs */
+               cpuidle_pause_and_lock();
+               for_each_possible_cpu(cpu) {
+                       dev = per_cpu(acpi_cpuidle_device, cpu);
                        _pr = per_cpu(processors, cpu);
-                       if (!_pr || !_pr->flags.power_setup_done)
+                       if (!_pr || !_pr->flags.power || !dev)
                                continue;
-                       dev = per_cpu(acpi_cpuidle_device, cpu);
-                       cpuidle_disable_device(dev);
+
+                       cpuidle_unregister_device_no_lock(dev);
+                       kfree(dev);
+                       _pr->flags.power = 0;
                }
+               cpuidle_resume_and_unlock();
 
-               /* Populate Updated C-state information */
-               acpi_processor_get_power_info(pr);
-               acpi_processor_setup_cpuidle_states(pr);
+               /*
+                * Unregister ACPI idle driver, reinitialize ACPI idle states
+                * and register ACPI idle driver again.
+                */
+               acpi_processor_unregister_idle_driver();
+               acpi_processor_register_idle_driver();
 
-               /* Enable all cpuidle devices */
-               for_each_online_cpu(cpu) {
+               /*
+                * 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 || !_pr->flags.power_setup_done)
+                       if (!_pr)
                                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);
-                       }
+                       acpi_processor_power_init(_pr);
                }
-               cpuidle_resume_and_unlock();
+
                cpus_read_unlock();
        }