* [PATCH 1/2] cpuidle: Add interface to get cpuidle_driver by CPU ID
2025-08-11 7:23 [PATCH 0/2] ACPI: processor: idle: Per-CPU idle driver for hybrid CPUs Yaxiong Tian
@ 2025-08-11 7:27 ` Yaxiong Tian
2025-08-11 7:27 ` [PATCH 2/2] ACPI: processor: idle: Replace single idle driver with per-CPU model for better hybrid CPU support Yaxiong Tian
1 sibling, 0 replies; 4+ messages in thread
From: Yaxiong Tian @ 2025-08-11 7:27 UTC (permalink / raw)
To: rafael, daniel.lezcano, lenb, robert.moore
Cc: linux-pm, linux-kernel, linux-acpi, acpica-devel, Yaxiong Tian,
Shaobo Huang, Yinfeng Wang, Xu Wang
Some drivers need to obtain the cpuidle_driver via CPU ID before the
cpuidle_device is registered. Therefore, this interface is added.
Tested-by: Shaobo Huang <huangshaobo2075@phytium.com.cn>
Signed-off-by: Yaxiong Tian <tianyaxiong@kylinos.cn>
Signed-off-by: Shaobo Huang <huangshaobo2075@phytium.com.cn>
Signed-off-by: Yinfeng Wang <wangyinfeng@phytium.com.cn>
Signed-off-by: Xu Wang<wangxu@phytium.com.cn>
---
drivers/cpuidle/driver.c | 16 ++++++++++++++++
include/linux/cpuidle.h | 4 ++++
2 files changed, 20 insertions(+)
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c
index 9bbfa594c442..8647f8165863 100644
--- a/drivers/cpuidle/driver.c
+++ b/drivers/cpuidle/driver.c
@@ -353,6 +353,22 @@ struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev)
}
EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver);
+/**
+ * cpuidle_get_cpu_driver_by_cpu - return the driver registered for a CPU.
+ * @cpu_num: a valid cpu num
+ *
+ * Returns a struct cpuidle_driver pointer, or NULL if no driver is registered
+ * for the CPU associated with @cpu.
+ */
+struct cpuidle_driver *cpuidle_get_cpu_driver_by_cpu(int cpu_num)
+{
+ if (cpu_num < 0 || cpu_num >= nr_cpu_ids)
+ return NULL;
+
+ return __cpuidle_get_cpu_driver(cpu_num);
+}
+EXPORT_SYMBOL_GPL(cpuidle_get_cpu_driver_by_cpu);
+
/**
* cpuidle_driver_state_disabled - Disable or enable an idle state
* @drv: cpuidle driver owning the state
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index a9ee4fe55dcf..aebbaae6a501 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -200,6 +200,7 @@ extern void cpuidle_disable_device(struct cpuidle_device *dev);
extern int cpuidle_play_dead(void);
extern struct cpuidle_driver *cpuidle_get_cpu_driver(struct cpuidle_device *dev);
+extern struct cpuidle_driver *cpuidle_get_cpu_driver_by_cpu(int cpu_num);
static inline struct cpuidle_device *cpuidle_get_device(void)
{return __this_cpu_read(cpuidle_devices); }
#else
@@ -240,6 +241,9 @@ static inline void cpuidle_disable_device(struct cpuidle_device *dev) { }
static inline int cpuidle_play_dead(void) {return -ENODEV; }
static inline struct cpuidle_driver *cpuidle_get_cpu_driver(
struct cpuidle_device *dev) {return NULL; }
+
+static inline struct cpuidle_driver *cpuidle_get_cpu_driver_by_cpu(
+ int cpu_num) {return NULL; };
static inline struct cpuidle_device *cpuidle_get_device(void) {return NULL; }
#endif
--
2.25.1
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH 2/2] ACPI: processor: idle: Replace single idle driver with per-CPU model for better hybrid CPU support
2025-08-11 7:23 [PATCH 0/2] ACPI: processor: idle: Per-CPU idle driver for hybrid CPUs Yaxiong Tian
2025-08-11 7:27 ` [PATCH 1/2] cpuidle: Add interface to get cpuidle_driver by CPU ID Yaxiong Tian
@ 2025-08-11 7:27 ` Yaxiong Tian
2025-08-13 14:25 ` kernel test robot
1 sibling, 1 reply; 4+ messages in thread
From: Yaxiong Tian @ 2025-08-11 7:27 UTC (permalink / raw)
To: rafael, daniel.lezcano, lenb, robert.moore
Cc: linux-pm, linux-kernel, linux-acpi, acpica-devel, Yaxiong Tian,
Shaobo Huang, Yinfeng Wang, Xu Wang
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@phytium.com.cn>
Signed-off-by: Yaxiong Tian <tianyaxiong@kylinos.cn>
Signed-off-by: Shaobo Huang <huangshaobo2075@phytium.com.cn>
Signed-off-by: Yinfeng Wang <wangyinfeng@phytium.com.cn>
Signed-off-by: Xu Wang<wangxu@phytium.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
^ permalink raw reply related [flat|nested] 4+ messages in thread