summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-11-12 09:37:30 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2026-11-12 09:37:30 -0800
commitaed8bd53bcf522fd81bc883dd5ade2dfc42097dc (patch)
tree3f19dad9c6b8e9684b055f5f80e8ee4425811f0a /drivers
parentdf43528e1bb685584394373bc60f71fbc542efd8 (diff)
parentdf42809bd599109660bd3f95e2e22330c80993df (diff)
downloadlinux-aed8bd53bcf522fd81bc883dd5ade2dfc42097dc.tar.gz
linux-aed8bd53bcf522fd81bc883dd5ade2dfc42097dc.zip
Merge tag 'pm-7.4-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management fixes from Rafael Wysocki: "These address a few issues in the cpufreq core and drivers, including issues actually seen in the field, a few issues related to runtime PM, including a false-positive warning introduced relatively recently and a long-standing defect in the runtime PM core code related to autosuspend, and revert a problematic recent update of the intel_rapl power capping driver. Specifics: - Revert a recent update of the intel_rapl power capping driver that caused perf to return bogus values (Dennis Chen) - Fix the synchronization of boost state updates in cpufreq (Christian Loehle) - Fix issues related to system suspend-resume synchronization in the cpufreq core and intel_pstate driver (Rafael Wysocki) - Fix P-state index mismatch in extract_io() in the ACPI cpufreq driver on systems with defective _PSS (Li RongQing) - Fix issues related to target frequency caching in cpufreq (Rafael Wysocki) - Prevent a false-positive warning in pm_runtime_remove() introduced by a previous fix during the 7.0 development cycle (Zhou Minqiang) - Extend a recent improvement related to the handling of suppliers during device runtime-suspend to some previously missed cases in which it is relevant for consistency (Brian Norris) - Update runtime PM documentation to reflect new behavior after recent changes (Stanley Jhu) - Add last_busy timestamp updates for devices requested to resume from runtime suspend that are missing if those devices are not actually suspended (Rafael Wysocki)" * tag 'pm-7.4-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: Revert "powercap: intel_rapl: Expose all package CPUs in PMU cpumask" PM: runtime: Update last_busy in rpm_resume() for active devices cpufreq: Fix updates of cached resolved frequency and index cpufreq: Cache post-resolution target frequency in __resolve_freq() cpufreq: acpi-cpufreq: fix P-state index mismatch in extract_io() cpufreq: Serialize global boost updates cpufreq: Preserve global boost state on failed updates PM: runtime: Update runtime_pm.rst for non-fatal resume errors PM: runtime: Only queue an idle check for RPM-linked suppliers (part 2) PM: runtime: Fix check_flush_dependency warning from pm_runtime_remove cpufreq: intel_pstate: Use rdmsrq_safe() to read MSR_MISC_PWR_MGMT cpufreq: intel_pstate: Clear last written P-state value on suspend cpufreq: Force passing target frequency to driver during resume cpufreq: Improve suspend-resume synchronization cpufreq: Hold the policy write lock when changing boost state
Diffstat (limited to 'drivers')
-rw-r--r--drivers/base/power/runtime.c11
-rw-r--r--drivers/cpufreq/acpi-cpufreq.c9
-rw-r--r--drivers/cpufreq/cpufreq.c162
-rw-r--r--drivers/cpufreq/intel_pstate.c18
-rw-r--r--drivers/powercap/intel_rapl_common.c15
5 files changed, 155 insertions, 60 deletions
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 304a2b123420..4e9c23059a40 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -343,6 +343,9 @@ static void __rpm_put_suppliers(struct device *dev, bool try_to_suspend)
list_for_each_entry_rcu(link, &dev->links.suppliers, c_node,
device_links_read_lock_held()) {
+ if (!device_link_test(link, DL_FLAG_PM_RUNTIME))
+ continue;
+
pm_runtime_release_supplier(link);
if (try_to_suspend)
pm_request_idle(link->supplier);
@@ -819,6 +822,12 @@ static int rpm_resume(struct device *dev, int rpmflags)
pm_runtime_deactivate_timer(dev);
if (dev->power.runtime_status == RPM_ACTIVE) {
+ /*
+ * The attempt to resume the device means that it is needed for
+ * something, so update its last_busy timestamp as in the case
+ * in which it actually resumes below.
+ */
+ pm_runtime_mark_last_busy(dev);
retval = 1;
goto out;
}
@@ -1910,7 +1919,7 @@ void pm_runtime_reinit(struct device *dev)
void pm_runtime_remove(struct device *dev)
{
__pm_runtime_disable(dev, false);
- flush_work(&dev->power.work);
+ cancel_work_sync(&dev->power.work);
pm_runtime_reinit(dev);
}
diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c
index 2e74a000cdc7..f64fa379c6b1 100644
--- a/drivers/cpufreq/acpi-cpufreq.c
+++ b/drivers/cpufreq/acpi-cpufreq.c
@@ -197,14 +197,13 @@ static unsigned extract_io(struct cpufreq_policy *policy, u32 value)
{
struct acpi_cpufreq_data *data = policy->driver_data;
struct acpi_processor_performance *perf;
- int i;
+ struct cpufreq_frequency_table *pos;
perf = to_perf_data(data);
- for (i = 0; i < perf->state_count; i++) {
- if (value == perf->states[i].status)
- return policy->freq_table[i].frequency;
- }
+ cpufreq_for_each_entry(pos, policy->freq_table)
+ if (value == perf->states[pos->driver_data].status)
+ return pos->frequency;
return 0;
}
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 54dde8419bdc..24319b214d7b 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -35,6 +35,9 @@
static LIST_HEAD(cpufreq_policy_list);
/* Macros to iterate over CPU policies */
+#define for_each_policy(__policy) \
+ list_for_each_entry(__policy, &cpufreq_policy_list, policy_list)
+
#define for_each_suitable_policy(__policy, __active) \
list_for_each_entry(__policy, &cpufreq_policy_list, policy_list) \
if ((__active) == !policy_is_inactive(__policy))
@@ -474,7 +477,7 @@ EXPORT_SYMBOL_GPL(cpufreq_disable_fast_switch);
static unsigned int __resolve_freq(struct cpufreq_policy *policy,
unsigned int target_freq,
unsigned int min, unsigned int max,
- unsigned int relation)
+ unsigned int relation, unsigned int *idx_p)
{
unsigned int idx;
@@ -484,8 +487,9 @@ static unsigned int __resolve_freq(struct cpufreq_policy *policy,
return target_freq;
idx = cpufreq_frequency_table_target(policy, target_freq, min, max, relation);
- policy->cached_resolved_idx = idx;
- policy->cached_target_freq = target_freq;
+ if (idx_p)
+ *idx_p = idx;
+
return policy->freq_table[idx].frequency;
}
@@ -517,7 +521,10 @@ unsigned int cpufreq_driver_resolve_freq(struct cpufreq_policy *policy,
if (unlikely(min > max))
min = max;
- return __resolve_freq(policy, target_freq, min, max, CPUFREQ_RELATION_LE);
+ policy->cached_target_freq = __resolve_freq(policy, target_freq, min, max,
+ CPUFREQ_RELATION_LE,
+ &policy->cached_resolved_idx);
+ return policy->cached_target_freq;
}
EXPORT_SYMBOL_GPL(cpufreq_driver_resolve_freq);
@@ -576,6 +583,8 @@ static int policy_set_boost(struct cpufreq_policy *policy, bool enable)
{
int ret;
+ lockdep_assert_held_write(&policy->rwsem);
+
if (policy->boost_enabled == enable)
return 0;
@@ -1629,6 +1638,8 @@ static int cpufreq_online(unsigned int cpu)
*/
if (cpufreq_driver->set_boost && policy->boost_supported &&
(new_policy || !cpufreq_boost_enabled())) {
+ guard(cpufreq_policy_write)(policy);
+
ret = policy_set_boost(policy, cpufreq_boost_enabled());
if (ret) {
/* If the set_boost fails, the online operation is not affected */
@@ -1956,6 +1967,14 @@ int cpufreq_generic_suspend(struct cpufreq_policy *policy)
}
EXPORT_SYMBOL(cpufreq_generic_suspend);
+static void cpufreq_suspend_policy(struct cpufreq_policy *policy)
+{
+ guard(cpufreq_policy_write)(policy);
+
+ cpufreq_stop_governor(policy);
+ policy->suspended = true;
+}
+
/**
* cpufreq_suspend() - Suspend CPUFreq governors.
*
@@ -1978,11 +1997,8 @@ void cpufreq_suspend(void)
pr_debug("%s: Suspending Governors\n", __func__);
for_each_active_policy(policy) {
- if (has_target()) {
- scoped_guard(cpufreq_policy_write, policy) {
- cpufreq_stop_governor(policy);
- }
- }
+ if (has_target())
+ cpufreq_suspend_policy(policy);
if (cpufreq_driver->suspend && cpufreq_driver->suspend(policy))
pr_err("%s: Failed to suspend driver: %s\n", __func__,
@@ -1994,6 +2010,41 @@ suspend:
cpus_read_unlock();
}
+static void cpufreq_resume_policy(struct cpufreq_policy *policy, bool start)
+{
+ guard(cpufreq_policy_write)(policy);
+
+ if (!policy->suspended)
+ return;
+
+ policy->suspended = false;
+
+ if (policy_is_inactive(policy)) {
+ /*
+ * The policy was active when it was suspended, so its governor
+ * is still there. Let the governor go.
+ */
+ cpufreq_exit_governor(policy);
+ return;
+ }
+
+ if (!start)
+ return;
+
+ /*
+ * Force executing the driver .target() callback, if present, in the
+ * next invocation of __cpufreq_driver_target() in case policy->cur has
+ * become stale while suspended, but do it only for drivers without a
+ * .get() callback for which cpufreq_verify_current_freq() is a NOP.
+ */
+ if (cpufreq_driver->target && !cpufreq_driver->get)
+ policy->force_target = true;
+
+ if (cpufreq_start_governor(policy))
+ pr_err("%s: Failed to start governor for CPU%u's policy\n",
+ __func__, policy->cpu);
+}
+
/**
* cpufreq_resume() - Resume CPUFreq governors.
*
@@ -2003,11 +2054,12 @@ suspend:
void cpufreq_resume(void)
{
struct cpufreq_policy *policy;
- int ret;
if (!cpufreq_driver)
return;
+ guard(cpus_read_lock)();
+
if (unlikely(!cpufreq_suspended))
return;
@@ -2018,19 +2070,18 @@ void cpufreq_resume(void)
pr_debug("%s: Resuming Governors\n", __func__);
- for_each_active_policy(policy) {
- if (cpufreq_driver->resume && cpufreq_driver->resume(policy)) {
- pr_err("%s: Failed to resume driver: %s\n", __func__,
- cpufreq_driver->name);
- } else if (has_target()) {
- scoped_guard(cpufreq_policy_write, policy) {
- ret = cpufreq_start_governor(policy);
- }
+ for_each_policy(policy) {
+ int ret = 0;
+ if (cpufreq_driver->resume && !policy_is_inactive(policy)) {
+ ret = cpufreq_driver->resume(policy);
if (ret)
- pr_err("%s: Failed to start governor for CPU%u's policy\n",
- __func__, policy->cpu);
+ pr_err("%s: Failed to resume driver: %s\n", __func__,
+ cpufreq_driver->name);
}
+
+ if (has_target())
+ cpufreq_resume_policy(policy, ret == 0);
}
}
@@ -2355,28 +2406,26 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
unsigned int relation)
{
unsigned int old_target_freq = target_freq;
+ unsigned int idx = 0;
if (cpufreq_disabled())
return -ENODEV;
target_freq = __resolve_freq(policy, target_freq, policy->min,
- policy->max, relation);
+ policy->max, relation, &idx);
pr_debug("CPU %u: cur %u kHz -> target %u kHz (req %u kHz, rel %u)\n",
policy->cpu, policy->cur, target_freq, old_target_freq, relation);
/*
- * This might look like a redundant call as we are checking it again
- * after finding index. But it is left intentionally for cases where
- * exactly same freq is called again and so we can save on few function
- * calls.
+ * Unless invoking the target callback is forced, skip the update if the
+ * frequency is not really changing.
*/
- if (target_freq == policy->cur) {
- if (!(cpufreq_driver->flags & CPUFREQ_NEED_UPDATE_LIMITS) ||
- !policy->update_limits)
- return 0;
-
- policy->update_limits = false;
+ if (policy->force_target) {
+ /* Avoid forcing the driver callback unnecessarily next time. */
+ policy->force_target = false;
+ } else if (target_freq == policy->cur) {
+ return 0;
}
if (cpufreq_driver->target) {
@@ -2393,7 +2442,7 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
if (!cpufreq_driver->target_index)
return -EINVAL;
- return __target_index(policy, policy->cached_resolved_idx);
+ return __target_index(policy, idx);
}
EXPORT_SYMBOL_GPL(__cpufreq_driver_target);
@@ -2412,12 +2461,17 @@ __weak struct cpufreq_governor *cpufreq_fallback_governor(void)
return NULL;
}
+static bool cpufreq_is_suspended(struct cpufreq_policy *policy)
+{
+ return cpufreq_suspended || policy->suspended;
+}
+
static int cpufreq_init_governor(struct cpufreq_policy *policy)
{
int ret;
- /* Don't start any governor operations if we are entering suspend */
- if (cpufreq_suspended)
+ /* Don't start any governor operations for suspended policies. */
+ if (cpufreq_is_suspended(policy))
return 0;
/*
* Governor might not be initiated here if ACPI _PPC changed
@@ -2460,7 +2514,7 @@ static int cpufreq_init_governor(struct cpufreq_policy *policy)
static void cpufreq_exit_governor(struct cpufreq_policy *policy)
{
- if (cpufreq_suspended || !policy->governor)
+ if (cpufreq_is_suspended(policy) || !policy->governor)
return;
pr_debug("%s: for CPU %u\n", __func__, policy->cpu);
@@ -2475,7 +2529,7 @@ int cpufreq_start_governor(struct cpufreq_policy *policy)
{
int ret;
- if (cpufreq_suspended)
+ if (cpufreq_is_suspended(policy))
return 0;
if (!policy->governor)
@@ -2499,7 +2553,7 @@ int cpufreq_start_governor(struct cpufreq_policy *policy)
void cpufreq_stop_governor(struct cpufreq_policy *policy)
{
- if (cpufreq_suspended || !policy->governor)
+ if (cpufreq_is_suspended(policy) || !policy->governor)
return;
pr_debug("%s: for CPU %u\n", __func__, policy->cpu);
@@ -2510,7 +2564,7 @@ void cpufreq_stop_governor(struct cpufreq_policy *policy)
static void cpufreq_governor_limits(struct cpufreq_policy *policy)
{
- if (cpufreq_suspended || !policy->governor)
+ if (cpufreq_is_suspended(policy) || !policy->governor)
return;
pr_debug("%s: for CPU %u\n", __func__, policy->cpu);
@@ -2630,6 +2684,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy,
struct cpufreq_governor *new_gov,
unsigned int new_pol)
{
+ bool need_update_limits = cpufreq_driver->flags & CPUFREQ_NEED_UPDATE_LIMITS;
struct cpufreq_policy_data new_data;
struct cpufreq_governor *old_gov;
unsigned int freq;
@@ -2666,26 +2721,26 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy,
* concurrently by cpufreq_driver_resolve_freq() during the update.
*/
freq = __resolve_freq(policy, new_data.max, new_data.min, new_data.max,
- CPUFREQ_RELATION_H);
+ CPUFREQ_RELATION_H, NULL);
if (freq != policy->max) {
WRITE_ONCE(policy->max, freq);
- policy->update_limits = true;
+ if (need_update_limits)
+ policy->force_target = true;
}
freq = __resolve_freq(policy, new_data.min, new_data.min, new_data.max,
- CPUFREQ_RELATION_L);
+ CPUFREQ_RELATION_L, NULL);
freq = min(freq, policy->max);
if (freq != policy->min) {
WRITE_ONCE(policy->min, freq);
- policy->update_limits = true;
+ if (need_update_limits)
+ policy->force_target = true;
}
trace_cpu_frequency_limits(policy);
cpufreq_update_pressure(policy);
- policy->cached_target_freq = UINT_MAX;
-
pr_debug("new min and max freqs are %u - %u kHz\n",
policy->min, policy->max);
@@ -2711,6 +2766,12 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy,
cpufreq_exit_governor(policy);
}
+ /*
+ * Initialize cached_target_freq while no governor is running to avoid
+ * racing with cpufreq_driver_resolve_freq(), which updates it.
+ */
+ policy->cached_target_freq = UINT_MAX;
+
/* start new governor */
policy->governor = new_gov;
ret = cpufreq_init_governor(policy);
@@ -2747,7 +2808,8 @@ static void cpufreq_policy_refresh(struct cpufreq_policy *policy)
* -> ask driver for current freq and notify governors about a change
*/
if (cpufreq_driver->get && has_target() &&
- (cpufreq_suspended || WARN_ON(!cpufreq_verify_current_freq(policy, false))))
+ (cpufreq_is_suspended(policy) ||
+ WARN_ON(!cpufreq_verify_current_freq(policy, false))))
return;
refresh_frequency_limits(policy);
@@ -2810,18 +2872,24 @@ int cpufreq_boost_set_sw(struct cpufreq_policy *policy, int state)
}
EXPORT_SYMBOL_GPL(cpufreq_boost_set_sw);
+static DEFINE_MUTEX(cpufreq_boost_mutex);
+
static int cpufreq_boost_trigger_state(int state)
{
struct cpufreq_policy *policy;
unsigned long flags;
+ bool boost_enabled;
int ret = -EOPNOTSUPP;
+ guard(mutex)(&cpufreq_boost_mutex);
+
/*
* Don't compare 'cpufreq_driver->boost_enabled' with 'state' here to
* make sure all policies are in sync with global boost flag.
*/
write_lock_irqsave(&cpufreq_driver_lock, flags);
+ boost_enabled = cpufreq_driver->boost_enabled;
cpufreq_driver->boost_enabled = state;
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
@@ -2830,6 +2898,8 @@ static int cpufreq_boost_trigger_state(int state)
if (!policy->boost_supported)
continue;
+ guard(cpufreq_policy_write)(policy);
+
ret = policy_set_boost(policy, state);
if (unlikely(ret))
break;
@@ -2841,7 +2911,7 @@ static int cpufreq_boost_trigger_state(int state)
return 0;
write_lock_irqsave(&cpufreq_driver_lock, flags);
- cpufreq_driver->boost_enabled = !state;
+ cpufreq_driver->boost_enabled = boost_enabled;
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
pr_err("%s: Cannot %s BOOST\n",
diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c
index 0e6ae4cdda5d..c78dcd681511 100644
--- a/drivers/cpufreq/intel_pstate.c
+++ b/drivers/cpufreq/intel_pstate.c
@@ -3389,10 +3389,11 @@ static void intel_cpufreq_cpu_exit(struct cpufreq_policy *policy)
static int intel_cpufreq_suspend(struct cpufreq_policy *policy)
{
+ struct cpudata *cpu = all_cpu_data[policy->cpu];
+
intel_pstate_suspend(policy);
if (hwp_active) {
- struct cpudata *cpu = all_cpu_data[policy->cpu];
u64 value = READ_ONCE(cpu->hwp_req_cached);
/*
@@ -3403,6 +3404,17 @@ static int intel_cpufreq_suspend(struct cpufreq_policy *policy)
value &= ~HWP_DESIRED_PERF(~0L);
wrmsrq_on_cpu(cpu->cpu, MSR_HWP_REQUEST, value);
WRITE_ONCE(cpu->hwp_req_cached, value);
+ } else {
+ /*
+ * Invalidate the cached P-state value that may be stale when
+ * the governor is started again during resume and may prevent
+ * MSR_IA32_PERF_CTL from being updated.
+ *
+ * This does not require synchronization because the core
+ * prevents frequency updates for suspended policies from
+ * occurring.
+ */
+ cpu->pstate.current_pstate = 0;
}
return 0;
@@ -3649,12 +3661,12 @@ static struct acpi_platform_list plat_info[] __initdata = {
static bool __init intel_pstate_platform_pwr_mgmt_exists(void)
{
const struct x86_cpu_id *id;
- u64 misc_pwr;
+ u64 misc_pwr = 0;
int idx;
id = x86_match_cpu(intel_pstate_cpu_oob_ids);
if (id) {
- rdmsrq(MSR_MISC_PWR_MGMT, misc_pwr);
+ rdmsrq_safe(MSR_MISC_PWR_MGMT, &misc_pwr);
if (misc_pwr & BITMASK_OOB) {
pr_debug("Bit 8 or 18 in the MISC_PWR_MGMT MSR set\n");
pr_debug("P states are controlled in Out of Band mode by the firmware/hardware\n");
diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c
index 70cab5f08bc6..f58649ff4f53 100644
--- a/drivers/powercap/intel_rapl_common.c
+++ b/drivers/powercap/intel_rapl_common.c
@@ -1139,21 +1139,23 @@ static struct rapl_pmu rapl_pmu;
/* PMU helpers */
-static void set_pmu_cpumask(struct rapl_package *rp, cpumask_var_t mask)
+static int get_pmu_cpu(struct rapl_package *rp)
{
int cpu;
if (!rp->has_pmu)
- return;
+ return nr_cpu_ids;
/* Only TPMI & MSR RAPL are supported for now */
if (rp->priv->type != RAPL_IF_TPMI && rp->priv->type != RAPL_IF_MSR)
- return;
+ return nr_cpu_ids;
/* TPMI/MSR RAPL uses any CPU in the package for PMU */
for_each_online_cpu(cpu)
if (topology_physical_package_id(cpu) == rp->id)
- cpumask_set_cpu(cpu, mask);
+ return cpu;
+
+ return nr_cpu_ids;
}
static bool is_rp_pmu_cpu(struct rapl_package *rp, int cpu)
@@ -1435,6 +1437,7 @@ static ssize_t cpumask_show(struct device *dev,
{
struct rapl_package *rp;
cpumask_var_t cpu_mask;
+ int cpu;
int ret;
if (!alloc_cpumask_var(&cpu_mask, GFP_KERNEL))
@@ -1446,7 +1449,9 @@ static ssize_t cpumask_show(struct device *dev,
/* Choose a cpu for each RAPL Package */
list_for_each_entry(rp, &rapl_packages, plist) {
- set_pmu_cpumask(rp, cpu_mask);
+ cpu = get_pmu_cpu(rp);
+ if (cpu < nr_cpu_ids)
+ cpumask_set_cpu(cpu, cpu_mask);
}
cpus_read_unlock();