[<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