linux-pm.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [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).