From f5e1cc9a284bff2510981643a5bca4bc4c21b81a Mon Sep 17 00:00:00 2001 From: Di Shen Date: Mon, 27 Apr 2026 20:00:47 +0800 Subject: [PATCH 1/7] OPP: Fix race between OPP addition and lookup A race exists between dev_pm_opp_add_dynamic() and dev_pm_opp_find_freq_exact(): CPU0 (add) CPU1 (lookup) ------------------------------- ------------------------------ _opp_add() mutex_lock() list_add(&new_opp->node, head) mutex_unlock() _opp_table_find_key() mutex_lock() dev_pm_opp_get(opp) kref_get() mutex_unlock() kref_init(&new_opp->kref) dev_pm_opp_put() kref_put_mutex() The newly added OPP is inserted into the list before its kref is initialized. A concurrent lookup can find this OPP and increment its reference count while it is still uninitialized, leading to refcount corruption and a potential premature free. Fix this by initializing ->kref and ->opp_table before making the OPP visible via list_add(). This ensures any concurrent lookup observes a fully initialized object. Fixes: 7034764a1e4a (PM / OPP: Add 'struct kref' to struct dev_pm_opp) Co-developed-by: Ling Xu Signed-off-by: Ling Xu Signed-off-by: Di Shen [ Viresh: Updated commit log ] Signed-off-by: Viresh Kumar --- drivers/opp/core.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index da3f5eba4341..ab0b0a2f85a1 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -2088,11 +2088,10 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, return ret; list_add(&new_opp->node, head); + new_opp->opp_table = opp_table; + kref_init(&new_opp->kref); } - new_opp->opp_table = opp_table; - kref_init(&new_opp->kref); - opp_debug_create_one(new_opp, opp_table); if (!_opp_supported_by_regulators(new_opp, opp_table)) { From 69f888381d2ecbe18ed9f112c096f8fd3623db98 Mon Sep 17 00:00:00 2001 From: Abdun Nihaal Date: Mon, 11 May 2026 12:12:11 +0530 Subject: [PATCH 2/7] OPP: of: Fix potential memory leak in opp_parse_supplies() The memory allocated for microvolt, microamp and microwatt is not freed in one of the paths in opp_parse_supplies() which returns directly. Fix that by adding a goto to the error unwind ladder. Fixes: 2eedf62e66c2 ("OPP: decouple dt properties in opp_parse_supplies()") Cc: stable@vger.kernel.org Signed-off-by: Abdun Nihaal Signed-off-by: Viresh Kumar --- drivers/opp/of.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/opp/of.c b/drivers/opp/of.c index f96adfd5b219..c02e20632fa6 100644 --- a/drivers/opp/of.c +++ b/drivers/opp/of.c @@ -673,7 +673,7 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, */ if (unlikely(opp_table->regulator_count == -1)) { opp_table->regulator_count = 0; - return 0; + goto free_microwatt; } for (i = 0, j = 0; i < opp_table->regulator_count; i++) { @@ -696,6 +696,7 @@ static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, opp->supplies[i].u_watt = microwatt[i]; } +free_microwatt: kfree(microwatt); free_microamp: kfree(microamp); From 06dbe2628063d762bc01a5a4d746db9e11e084cf Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sat, 25 Apr 2026 10:25:29 +0300 Subject: [PATCH 3/7] intel_idle: Add constants for MSR_PKG_CST_CONFIG_CONTROL Add two constants for the package C-state limit fields in MSR_PKG_CST_CONFIG_CONTROL. The SKX_ prefix stands for "Skylake Xeon" and makes it explicit that the mask is CPU model-specific. The same values have applied to all Xeon platforms starting from SKX. Reviewed-by: Andy Shevchenko Signed-off-by: Artem Bityutskiy Link: https://patch.msgid.link/20260425072532.358365-2-dedekind1@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index f49354e37777..259013c246d9 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -81,6 +81,11 @@ static bool ibrs_off __read_mostly; /* Maximum allowed C-state target residency */ #define MAX_CMDLINE_RESIDENCY_US (100 * USEC_PER_MSEC) +/* The Package C-State Limit bits in MSR_PKG_CST_CONFIG_CONTROL */ +#define SKX_PKG_CST_LIMIT_MASK GENMASK(2, 0) +/* PC6 is enabled when Package C-State Limit >= this value */ +#define SKX_PKG_CST_LIMIT_PC6 2 + static char cmdline_table_str[MAX_CMDLINE_TABLE_LEN] __read_mostly; static struct cpuidle_device __percpu *intel_idle_cpuidle_devices; @@ -2090,7 +2095,7 @@ static void __init skx_idle_state_table_update(void) * 011b: C6 (retention) * 111b: No Package C state limits. */ - if ((msr & 0x7) < 2) { + if ((msr & SKX_PKG_CST_LIMIT_MASK) < SKX_PKG_CST_LIMIT_PC6) { /* * Uses the CC6 + PC0 latency and 3 times of * latency for target_residency if the PC6 @@ -2118,7 +2123,7 @@ static void __init spr_idle_state_table_update(void) rdmsrq(MSR_PKG_CST_CONFIG_CONTROL, msr); /* Limit value 2 and above allow for PC6. */ - if ((msr & 0x7) < 2) { + if ((msr & SKX_PKG_CST_LIMIT_MASK) < SKX_PKG_CST_LIMIT_PC6) { spr_cstates[2].exit_latency = 190; spr_cstates[2].target_residency = 600; } From 27428514d2085694ec0b03f8d3ac3c3f570ec8ec Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sat, 25 Apr 2026 10:25:31 +0300 Subject: [PATCH 4/7] intel_idle: Introduce a helper for checking PC6 Introduce the skx_is_pc6_disabled() for checking if PC6 is disabled and switch the following functions to use it: - skx_idle_state_table_update() - spr_idle_state_table_update() At the same time, clean them up improving the commentary and moving it to the function kernel-doc. Purely a clean up, no functional changes intended. Reviewed-by: Andy Shevchenko Signed-off-by: Artem Bityutskiy Link: https://patch.msgid.link/20260425072532.358365-4-dedekind1@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 48 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 259013c246d9..def04b1ab355 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -2079,12 +2079,13 @@ static void __init sklh_idle_state_table_update(void) } /** - * skx_idle_state_table_update - Adjust the Sky Lake/Cascade Lake - * idle states table. + * skx_is_pc6_disabled() - Check if PC6 is disabled in BIOS. + * + * Return: %true if PC6 is disabled, %false otherwise. */ -static void __init skx_idle_state_table_update(void) +static bool __init skx_is_pc6_disabled(void) { - unsigned long long msr; + u64 msr; rdmsrq(MSR_PKG_CST_CONFIG_CONTROL, msr); @@ -2095,35 +2096,34 @@ static void __init skx_idle_state_table_update(void) * 011b: C6 (retention) * 111b: No Package C state limits. */ - if ((msr & SKX_PKG_CST_LIMIT_MASK) < SKX_PKG_CST_LIMIT_PC6) { - /* - * Uses the CC6 + PC0 latency and 3 times of - * latency for target_residency if the PC6 - * is disabled in BIOS. This is consistent - * with how intel_idle driver uses _CST - * to set the target_residency. - */ + return (msr & SKX_PKG_CST_LIMIT_MASK) < SKX_PKG_CST_LIMIT_PC6; +} + +/** + * skx_idle_state_table_update - Adjust the SKX/CLX idle states table. + * + * Adjust Sky Lake or Cascade Lake Xeon idle states if PC6 is disabled in BIOS. + * Use the CC6 + PC0 latency and 3 times of that latency for target_residency. + * This is consistent with how the intel_idle driver uses _CST to set the + * target_residency. + */ +static void __init skx_idle_state_table_update(void) +{ + if (skx_is_pc6_disabled()) { skx_cstates[2].exit_latency = 92; skx_cstates[2].target_residency = 276; } } /** - * spr_idle_state_table_update - Adjust Sapphire Rapids idle states table. + * spr_idle_state_table_update - Adjust Sapphire Rapids Xeon idle states table. + * + * By default, the C6 state assumes the worst-case scenario of package C6. + * However, if PC6 is disabled in BIOS, update the numbers to match core C6. */ static void __init spr_idle_state_table_update(void) { - unsigned long long msr; - - /* - * By default, the C6 state assumes the worst-case scenario of package - * C6. However, if PC6 is disabled, we update the numbers to match - * core C6. - */ - rdmsrq(MSR_PKG_CST_CONFIG_CONTROL, msr); - - /* Limit value 2 and above allow for PC6. */ - if ((msr & SKX_PKG_CST_LIMIT_MASK) < SKX_PKG_CST_LIMIT_PC6) { + if (skx_is_pc6_disabled()) { spr_cstates[2].exit_latency = 190; spr_cstates[2].target_residency = 600; } From 86b488b19f969e4f32246dba042638bf1a1240ac Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Sat, 25 Apr 2026 10:25:32 +0300 Subject: [PATCH 5/7] intel_idle: Drop C-states redundant when PC6 is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On modern Xeon platforms, such as Granite Rapids, Sierra Forest, and Clearwater Forest, there are two flavors of requestable C6 states: C6 and C6P. C6 allows only core C6 (CC6), while C6P allows both CC6 and package C6 (PC6). PC6 saves more power but also has a higher exit latency, so many users disable it in BIOS. When PC6 is disabled, C6P becomes identical to C6 — the CPU treats C6P requests as C6 requests. Exposing both C6 and C6P to user space in this situation is confusing: two states with the same name look different but behave the same. It also adds unnecessary overhead to the cpuidle subsystem, which is a fast path: the governor evaluates every registered state on idle entry. Drop C-states that are redundant when PC6 is disabled by marking them with CPUIDLE_FLAG_UNUSABLE, which causes cpuidle to exclude them when registering idle states. Signed-off-by: Artem Bityutskiy Link: https://patch.msgid.link/20260425072532.358365-5-dedekind1@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/idle/intel_idle.c | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index def04b1ab355..b2705d79a4ee 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -2129,6 +2129,53 @@ static void __init spr_idle_state_table_update(void) } } +/** + * drop_pc6_redundant_cstates() - Drop C-states redundant when PC6 is disabled. + * @states: Idle states table to modify. + * + * When PC6 is disabled in BIOS, C-states that exist solely to enable PC6 + * entry (such as C6P or C6SP) become identical to shallower C-states like + * C6, and are therefore redundant. Should be called only on systems with + * multiple C6 flavors. + */ +static void __init drop_pc6_redundant_cstates(struct cpuidle_state *states) +{ + int count; + + if (!skx_is_pc6_disabled()) + /* PC6 is not disabled, nothing to do */ + return; + + for (count = 0; states[count].enter; count++) + continue; + + if (count < 2) { + pr_debug("Too few idle states to drop PC6-redundant states\n"); + return; + } + + /* + * Sanity check: At this point all platforms with multiple C6 flavors + * use the CPUIDLE_FLAG_PARTIAL_HINT_MATCH flag. And the last state in + * the table is the one that becomes redundant when PC6 is disabled. + */ + if (!(states[count - 1].flags & CPUIDLE_FLAG_PARTIAL_HINT_MATCH)) { + pr_debug("Can't drop PC6-redundant states: unexpected flags\n"); + return; + } + + /* + * On all current platforms with multiple C6 flavors, there is only one + * C-state that becomes redundant when PC6 is disabled. This state is + * the last one in the table. Drop it by marking it with + * CPUIDLE_FLAG_UNUSABLE so that cpuidle excludes it when registering + * idle states. + */ + pr_info("Dropping idle state %s because PC6 is disabled\n", + states[count - 1].name); + states[count - 1].flags |= CPUIDLE_FLAG_UNUSABLE; +} + /** * byt_cht_auto_demotion_disable - Disable Bay/Cherry Trail auto-demotion. */ @@ -2218,6 +2265,12 @@ static void __init intel_idle_init_cstates_icpu(struct cpuidle_driver *drv) case INTEL_ATOM_AIRMONT: byt_cht_auto_demotion_disable(); break; + case INTEL_GRANITERAPIDS_D: + case INTEL_GRANITERAPIDS_X: + case INTEL_ATOM_CRESTMONT_X: + case INTEL_ATOM_DARKMONT_X: + drop_pc6_redundant_cstates(cpuidle_state_table); + break; } for (cstate = 0; cstate < CPUIDLE_STATE_MAX; ++cstate) { From b59020834b2d8718fe37a4e77367f554cbf95982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Antinori?= Date: Sun, 24 May 2026 12:40:16 -0300 Subject: [PATCH 6/7] opp: rust: mark OPP methods as inline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building the kernel using llvm-19.1.7-rust-1.85.0-x86_64, the following symbols are generated: $ nm vmlinux | grep ' _R'.*OPP | rustfilt ffffffff81801560 T ::freq ffffffff81801540 T ::dec_ref ffffffff81801520 T ::inc_ref However, these Rust symbols are trivial wrappers around the `dev_pm_opp_get`, `dev_pm_opp_put` and `dev_pm_opp_get_freq_indexed` functions. It doesn't make sense to go through a trivial wrapper for these functions. After applying this patch, the above command will produce no output. Link: https://github.com/Rust-for-Linux/linux/issues/1145 Suggested-by: Alice Ryhl Signed-off-by: Nicolás Antinori Signed-off-by: Viresh Kumar --- rust/kernel/opp.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rust/kernel/opp.rs b/rust/kernel/opp.rs index a760fac28765..62e44676125d 100644 --- a/rust/kernel/opp.rs +++ b/rust/kernel/opp.rs @@ -1042,11 +1042,13 @@ unsafe impl Sync for OPP {} /// SAFETY: The type invariants guarantee that [`OPP`] is always refcounted. unsafe impl AlwaysRefCounted for OPP { + #[inline] fn inc_ref(&self) { // SAFETY: The existence of a shared reference means that the refcount is nonzero. unsafe { bindings::dev_pm_opp_get(self.0.get()) }; } + #[inline] unsafe fn dec_ref(obj: ptr::NonNull) { // SAFETY: The safety requirements guarantee that the refcount is nonzero. unsafe { bindings::dev_pm_opp_put(obj.cast().as_ptr()) } @@ -1095,6 +1097,7 @@ fn as_raw(&self) -> *mut bindings::dev_pm_opp { } /// Returns the frequency of an [`OPP`]. + #[inline] pub fn freq(&self, index: Option) -> Hertz { let index = index.unwrap_or(0); From 51a7c560cffdd3653ac2b930d01410569b23b23e Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Mon, 8 Jun 2026 13:07:48 -0400 Subject: [PATCH 7/7] PM: QoS: Fix misc device registration unwind cpu_latency_qos_init() registers cpu_dma_latency first and, when CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP is enabled, registers cpu_wakeup_latency afterwards. The second registration overwrites the first return value. As a result, a failure to register cpu_dma_latency can be masked if the second registration succeeds. Conversely, if cpu_dma_latency succeeds and cpu_wakeup_latency fails, the function returns an error while leaving the first misc device registered. Return immediately on the first registration failure and deregister cpu_dma_latency if the second registration fails. Fixes: a4e6512a79d8 ("PM: QoS: Introduce a CPU system wakeup QoS limit") Signed-off-by: Yuho Choi Link: https://patch.msgid.link/20260608170748.82273-1-dbgh9129@gmail.com Signed-off-by: Rafael J. Wysocki --- kernel/power/qos.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/kernel/power/qos.c b/kernel/power/qos.c index 398b994b73aa..1944dbeb0d4c 100644 --- a/kernel/power/qos.c +++ b/kernel/power/qos.c @@ -519,18 +519,23 @@ static int __init cpu_latency_qos_init(void) int ret; ret = misc_register(&cpu_latency_qos_miscdev); - if (ret < 0) + if (ret < 0) { pr_err("%s: %s setup failed\n", __func__, cpu_latency_qos_miscdev.name); + return ret; + } #ifdef CONFIG_PM_QOS_CPU_SYSTEM_WAKEUP ret = misc_register(&cpu_wakeup_latency_qos_miscdev); - if (ret < 0) + if (ret < 0) { pr_err("%s: %s setup failed\n", __func__, cpu_wakeup_latency_qos_miscdev.name); + misc_deregister(&cpu_latency_qos_miscdev); + return ret; + } #endif - return ret; + return 0; } late_initcall(cpu_latency_qos_init); #endif /* CONFIG_CPU_IDLE */