Merge branches 'pm-cpuidle', 'pm-opp' and 'pm-qos'

Merge cpuidle updates, OPP (operating performance points) updates and a
PM QoS update for 7.2-rc1:

 - Allow the intel_idle driver to avoid exposing C-states that are
   redundant when PC6 is disabled (Artem Bityutskiy)

 - Fix memory leak and a potential race in the OPP core (Abdun Nihaal,
   Di Shen)

 - Mark Rust OPP methods as inline (Nicolás Antinori)

 - Fix misc device registration failure path in the PM QoS core (Yuho
   Choi)

* pm-cpuidle:
  intel_idle: Drop C-states redundant when PC6 is disabled
  intel_idle: Introduce a helper for checking PC6
  intel_idle: Add constants for MSR_PKG_CST_CONFIG_CONTROL

* pm-opp:
  opp: rust: mark OPP methods as inline
  OPP: of: Fix potential memory leak in opp_parse_supplies()
  OPP: Fix race between OPP addition and lookup

* pm-qos:
  PM: QoS: Fix misc device registration unwind
This commit is contained in:
Rafael J. Wysocki 2026-06-11 21:20:57 +02:00
commit 0d05de7004
5 changed files with 97 additions and 31 deletions

View File

@ -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;
@ -2074,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);
@ -2090,40 +2096,86 @@ static void __init skx_idle_state_table_update(void)
* 011b: C6 (retention)
* 111b: No Package C state limits.
*/
if ((msr & 0x7) < 2) {
/*
* 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 & 0x7) < 2) {
if (skx_is_pc6_disabled()) {
spr_cstates[2].exit_latency = 190;
spr_cstates[2].target_residency = 600;
}
}
/**
* 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.
*/
@ -2213,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) {

View File

@ -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)) {

View File

@ -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);

View File

@ -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 */

View File

@ -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<Self>) {
// 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<u32>) -> Hertz {
let index = index.unwrap_or(0);