* [PATCH 0/2] ACPI: processor: idle: Per-CPU idle driver for hybrid CPUs
@ 2025-08-11 7:23 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 ` [PATCH 2/2] ACPI: processor: idle: Replace single idle driver with per-CPU model for better hybrid CPU support Yaxiong Tian
0 siblings, 2 replies; 4+ messages in thread
From: Yaxiong Tian @ 2025-08-11 7:23 UTC (permalink / raw)
To: rafael, daniel.lezcano, lenb, robert.moore
Cc: linux-pm, linux-kernel, linux-acpi, acpica-devel, Yaxiong Tian
This series addresses limitations in the current ACPI idle driver model
for hybrid CPU architectures (e.g., ARM big.LITTLE, Intel Alder Lake),
where different core types have distinct _LPI-state characteristics.
This series introduces:
1. A per-CPU idle driver model to accurately represent idle-state per core type.
2. A new interface to fetch cpuidle_driver by CPU ID, required for early
registration scenarios.
This issue was initially discussed at:
https://lore.kernel.org/linux-pm/97e8bc72-e44b-487a-91ba-206732094955@arm.com/T/#t
Yaxiong Tian (2):
cpuidle: Add interface to get cpuidle_driver by CPU ID
ACPI: processor: idle: Replace single idle driver with per-CPU model
for better hybrid CPU support
drivers/acpi/Kconfig | 1 +
drivers/acpi/processor_driver.c | 3 +-
drivers/acpi/processor_idle.c | 60 ++++++++++++++++-----------------
drivers/cpuidle/driver.c | 16 +++++++++
include/acpi/processor.h | 2 +-
include/linux/cpuidle.h | 4 +++
6 files changed, 54 insertions(+), 32 deletions(-)
--
2.25.1
^ permalink raw reply [flat|nested] 4+ messages in thread
* [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
* Re: [PATCH 2/2] ACPI: processor: idle: Replace single idle driver with per-CPU model for better hybrid CPU support
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
@ 2025-08-13 14:25 ` kernel test robot
0 siblings, 0 replies; 4+ messages in thread
From: kernel test robot @ 2025-08-13 14:25 UTC (permalink / raw)
To: Yaxiong Tian
Cc: oe-lkp, lkp, Shaobo Huang, Yinfeng Wang, linux-acpi, acpica-devel,
rafael, daniel.lezcano, lenb, robert.moore, linux-pm,
linux-kernel, Yaxiong Tian, Xu Wang, oliver.sang
Hello,
kernel test robot noticed "BUG:using_smp_processor_id()in_preemptible" on:
commit: 91c8bbe586854e1485070da49806a93599f7636f ("[PATCH 2/2] ACPI: processor: idle: Replace single idle driver with per-CPU model for better hybrid CPU support")
url: https://github.com/intel-lab-lkp/linux/commits/Yaxiong-Tian/cpuidle-Add-interface-to-get-cpuidle_driver-by-CPU-ID/20250811-153002
base: https://git.kernel.org/cgit/linux/kernel/git/rafael/linux-pm.git linux-next
patch link: https://lore.kernel.org/all/20250811072723.762608-1-tianyaxiong@kylinos.cn/
patch subject: [PATCH 2/2] ACPI: processor: idle: Replace single idle driver with per-CPU model for better hybrid CPU support
in testcase: boot
config: i386-randconfig-004-20250812
compiler: gcc-12
test machine: qemu-system-i386 -enable-kvm -cpu SandyBridge -smp 2 -m 4G
(please refer to attached dmesg/kmsg for entire log/backtrace)
+--------------------------------------------+------------+------------+
| | 664e805e14 | 91c8bbe586 |
+--------------------------------------------+------------+------------+
| BUG:using_smp_processor_id()in_preemptible | 0 | 12 |
+--------------------------------------------+------------+------------+
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <oliver.sang@intel.com>
| Closes: https://lore.kernel.org/oe-lkp/202508131628.5bcb2ed6-lkp@intel.com
[ 9.129755][ T1] BUG: using smp_processor_id() in preemptible [00000000] code: swapper/0/1
[ 9.131211][ T1] caller is debug_smp_processor_id (lib/smp_processor_id.c:61)
[ 9.132199][ T1] CPU: 1 UID: 0 PID: 1 Comm: swapper/0 Not tainted 6.16.0-rc7-00167-g91c8bbe58685 #1 PREEMPTLAZY 2350dfb9c96e1334949e3e2979039571c1c27191
[ 9.132205][ T1] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
[ 9.132208][ T1] Call Trace:
[ 9.132212][ T1] dump_stack_lvl (lib/dump_stack.c:123)
[ 9.132218][ T1] dump_stack (lib/dump_stack.c:130)
[ 9.132224][ T1] check_preemption_disabled (arch/x86/include/asm/preempt.h:85 lib/smp_processor_id.c:53)
[ 9.132233][ T1] debug_smp_processor_id (lib/smp_processor_id.c:61)
[ 9.132237][ T1] acpi_idle_rescan_dead_smt_siblings (drivers/acpi/processor_idle.c:59)
[ 9.132246][ T1] acpi_processor_driver_init (drivers/acpi/processor_driver.c:286)
[ 9.132252][ T1] ? acpi_processor_stop (drivers/acpi/processor_driver.c:132)
[ 9.132259][ T1] do_one_initcall (init/main.c:1274)
[ 9.132263][ T1] ? acpi_fan_driver_init (drivers/acpi/processor_driver.c:251)
[ 9.132287][ T1] do_initcalls (init/main.c:1335 init/main.c:1352)
[ 9.132297][ T1] kernel_init_freeable (init/main.c:1588)
[ 9.132299][ T1] ? rest_init (init/main.c:1466)
[ 9.132302][ T1] kernel_init (init/main.c:1476)
[ 9.132305][ T1] ret_from_fork (arch/x86/kernel/process.c:154)
[ 9.132309][ T1] ? rest_init (init/main.c:1466)
[ 9.132315][ T1] ret_from_fork_asm (arch/x86/entry/entry_32.S:737)
[ 9.132319][ T1] entry_INT80_32 (arch/x86/entry/entry_32.S:945)
The kernel config and materials to reproduce are available at:
https://download.01.org/0day-ci/archive/20250813/202508131628.5bcb2ed6-lkp@intel.com
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2025-08-13 14:25 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH 2/2] ACPI: processor: idle: Replace single idle driver with per-CPU model for better hybrid CPU support Yaxiong Tian
2025-08-13 14:25 ` kernel test robot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).