Commit 07cba0de authored by Huisong Li's avatar Huisong Li Committed by Rafael J. Wysocki
Browse files

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: default avatarHuisong Li <lihuisong@huawei.com>
[ rjw: Subject rewrite ]
Link: https://patch.msgid.link/20260407081141.2493581-3-lihuisong@huawei.com


Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent a4c6c18e
Loading
Loading
Loading
Loading
+25 −20
Original line number Diff line number Diff line
@@ -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();
	}