lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-Id: <20250811072723.762608-1-tianyaxiong@kylinos.cn>
Date: Mon, 11 Aug 2025 15:27:23 +0800
From: Yaxiong Tian <tianyaxiong@...inos.cn>
To: rafael@...nel.org,
	daniel.lezcano@...aro.org,
	lenb@...nel.org,
	robert.moore@...el.com
Cc: linux-pm@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	linux-acpi@...r.kernel.org,
	acpica-devel@...ts.linux.dev,
	Yaxiong Tian <tianyaxiong@...inos.cn>,
	Shaobo Huang <huangshaobo2075@...tium.com.cn>,
	Yinfeng Wang <wangyinfeng@...tium.com.cn>,
	Xu Wang <wangxu@...tium.com.cn>
Subject: [PATCH 2/2] ACPI: processor: idle: Replace single idle driver with per-CPU model for better hybrid CPU support

Current implementations of hybrid architectures (e.g., ARM64 big.LITTLE
and Intel Alder Lake) feature CPU cores with different exit latencies.
Using a single driver to describe_LPI states for all core types is
therefore suboptimal. This is further supported by ACPI specification
8.4.4.1 which states: "In a processor hierarchy, each node has its
own _LPI low-power states specific to that node."

To address these limitations, we replace the monolithic idle driver
with a per-CPU model. This approach enables accurate idle state representation
for each core type

Tested-by: Shaobo Huang <huangshaobo2075@...tium.com.cn>
Signed-off-by: Yaxiong Tian <tianyaxiong@...inos.cn>
Signed-off-by: Shaobo Huang <huangshaobo2075@...tium.com.cn>
Signed-off-by: Yinfeng Wang <wangyinfeng@...tium.com.cn>
Signed-off-by: Xu Wang<wangxu@...tium.com.cn>
---
 drivers/acpi/Kconfig            |  1 +
 drivers/acpi/processor_driver.c |  3 +-
 drivers/acpi/processor_idle.c   | 60 ++++++++++++++++-----------------
 include/acpi/processor.h        |  2 +-
 4 files changed, 34 insertions(+), 32 deletions(-)

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index ca00a5dbcf75..d92c0faca978 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -276,6 +276,7 @@ config ACPI_PROCESSOR_CSTATE
 config ACPI_PROCESSOR_IDLE
 	bool
 	select CPU_IDLE
+	select CPU_IDLE_MULTIPLE_DRIVERS
 
 config ACPI_MCFG
 	bool
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 65e779be64ff..22db9c904437 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -166,7 +166,8 @@ static int __acpi_processor_start(struct acpi_device *device)
 	if (result && !IS_ENABLED(CONFIG_ACPI_CPU_FREQ_PSS))
 		dev_dbg(&device->dev, "CPPC data invalid or not present\n");
 
-	if (!cpuidle_get_driver() || cpuidle_get_driver() == &acpi_idle_driver)
+	if (!cpuidle_get_cpu_driver_by_cpu(pr->id) || cpuidle_get_cpu_driver_by_cpu(pr->id)
+			== per_cpu_ptr(&acpi_idle_driver, pr->id))
 		acpi_processor_power_init(pr);
 
 	acpi_pss_perf_init(pr);
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 2c2dc559e0f8..4922110da0bf 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -51,15 +51,12 @@ module_param(latency_factor, uint, 0644);
 
 static DEFINE_PER_CPU(struct cpuidle_device *, acpi_cpuidle_device);
 
-struct cpuidle_driver acpi_idle_driver = {
-	.name =		"acpi_idle",
-	.owner =	THIS_MODULE,
-};
+DEFINE_PER_CPU(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)
+	if (cpuidle_get_driver() == this_cpu_ptr(&acpi_idle_driver))
 		arch_cpu_rescan_dead_smt_siblings();
 }
 
@@ -738,12 +735,13 @@ static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr,
 	int i, count = ACPI_IDLE_STATE_START;
 	struct acpi_processor_cx *cx;
 	struct cpuidle_state *state;
+	struct cpuidle_driver *drv = per_cpu_ptr(&acpi_idle_driver, pr->id);
 
 	if (max_cstate == 0)
 		max_cstate = 1;
 
 	for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
-		state = &acpi_idle_driver.states[count];
+		state = &drv->states[count];
 		cx = &pr->power.states[i];
 
 		if (!cx->valid)
@@ -776,7 +774,7 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr)
 	int i, count;
 	struct acpi_processor_cx *cx;
 	struct cpuidle_state *state;
-	struct cpuidle_driver *drv = &acpi_idle_driver;
+	struct cpuidle_driver *drv = per_cpu_ptr(&acpi_idle_driver, pr->id);
 
 	if (max_cstate == 0)
 		max_cstate = 1;
@@ -1198,7 +1196,7 @@ static int acpi_processor_setup_lpi_states(struct acpi_processor *pr)
 	int i;
 	struct acpi_lpi_state *lpi;
 	struct cpuidle_state *state;
-	struct cpuidle_driver *drv = &acpi_idle_driver;
+	struct cpuidle_driver *drv = per_cpu_ptr(&acpi_idle_driver, pr->id);
 
 	if (!pr->flags.has_lpi)
 		return -EOPNOTSUPP;
@@ -1232,7 +1230,7 @@ static int acpi_processor_setup_lpi_states(struct acpi_processor *pr)
 static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
 {
 	int i;
-	struct cpuidle_driver *drv = &acpi_idle_driver;
+	struct cpuidle_driver *drv = per_cpu_ptr(&acpi_idle_driver, pr->id);
 
 	if (!pr->flags.power_setup_done || !pr->flags.power)
 		return -EINVAL;
@@ -1316,13 +1314,7 @@ int acpi_processor_power_state_has_changed(struct acpi_processor *pr)
 	if (!pr->flags.power_setup_done)
 		return -ENODEV;
 
-	/*
-	 * FIXME:  Design the ACPI notification to make it once per
-	 * system instead of once per-cpu.  This condition is a hack
-	 * to make the code that updates C-States be called once.
-	 */
-
-	if (pr->id == 0 && cpuidle_get_driver() == &acpi_idle_driver) {
+	if (cpuidle_get_cpu_driver_by_cpu(pr->id) == per_cpu_ptr(&acpi_idle_driver, pr->id)) {
 
 		/* Protect against cpu-hotplug */
 		cpus_read_lock();
@@ -1360,12 +1352,14 @@ int acpi_processor_power_state_has_changed(struct acpi_processor *pr)
 	return 0;
 }
 
-static int acpi_processor_registered;
 
 int acpi_processor_power_init(struct acpi_processor *pr)
 {
 	int retval;
+	struct cpumask *cpumask;
 	struct cpuidle_device *dev;
+	struct cpuidle_driver *drv = per_cpu_ptr(&acpi_idle_driver, pr->id);
+
 
 	if (disabled_by_idle_boot_param())
 		return 0;
@@ -1382,14 +1376,21 @@ int acpi_processor_power_init(struct acpi_processor *pr)
 	 */
 	if (pr->flags.power) {
 		/* Register acpi_idle_driver if not already registered */
-		if (!acpi_processor_registered) {
-			acpi_processor_setup_cpuidle_states(pr);
-			retval = cpuidle_register_driver(&acpi_idle_driver);
-			if (retval)
-				return retval;
-			pr_debug("%s registered with cpuidle\n",
-				 acpi_idle_driver.name);
+		acpi_processor_setup_cpuidle_states(pr);
+
+		drv->name = "acpi_idle";
+		drv->owner = THIS_MODULE;
+		cpumask = kzalloc(cpumask_size(), GFP_KERNEL);
+		cpumask_set_cpu(pr->id, cpumask);
+		drv->cpumask = cpumask;
+
+		retval = cpuidle_register_driver(drv);
+		if (retval) {
+			kfree(cpumask);
+			return retval;
 		}
+		pr_debug("cpu %d:%s registered with cpuidle\n", pr->id,
+			 drv->name);
 
 		dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 		if (!dev)
@@ -1403,11 +1404,10 @@ int acpi_processor_power_init(struct acpi_processor *pr)
 		 */
 		retval = cpuidle_register_device(dev);
 		if (retval) {
-			if (acpi_processor_registered == 0)
-				cpuidle_unregister_driver(&acpi_idle_driver);
+			cpuidle_unregister_driver(drv);
+			kfree(cpumask);
 			return retval;
 		}
-		acpi_processor_registered++;
 	}
 	return 0;
 }
@@ -1415,17 +1415,17 @@ int acpi_processor_power_init(struct acpi_processor *pr)
 int acpi_processor_power_exit(struct acpi_processor *pr)
 {
 	struct cpuidle_device *dev = per_cpu(acpi_cpuidle_device, pr->id);
+	struct cpuidle_driver *drv = per_cpu_ptr(&acpi_idle_driver, pr->id);
 
 	if (disabled_by_idle_boot_param())
 		return 0;
 
 	if (pr->flags.power) {
 		cpuidle_unregister_device(dev);
-		acpi_processor_registered--;
-		if (acpi_processor_registered == 0)
-			cpuidle_unregister_driver(&acpi_idle_driver);
+		cpuidle_unregister_driver(drv);
 
 		kfree(dev);
+		kfree(drv->cpumask);
 	}
 
 	pr->flags.power_setup_done = 0;
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index d0eccbd920e5..36940c6b96cc 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -417,7 +417,7 @@ static inline void acpi_processor_throttling_init(void) {}
 #endif	/* CONFIG_ACPI_CPU_FREQ_PSS */
 
 /* in processor_idle.c */
-extern struct cpuidle_driver acpi_idle_driver;
+DECLARE_PER_CPU(struct cpuidle_driver, acpi_idle_driver);
 #ifdef CONFIG_ACPI_PROCESSOR_IDLE
 int acpi_processor_power_init(struct acpi_processor *pr);
 int acpi_processor_power_exit(struct acpi_processor *pr);
-- 
2.25.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ