From 3bff8f8e95fdc6ad19c8a1a8f87029094747e4bf Mon Sep 17 00:00:00 2001 From: Hui Su Date: Thu, 6 Aug 2026 22:23:04 +0800 Subject: [PATCH 1/4] cpufreq: schedutil: Fix rate limit overflow rate_limit_us is an unsigned int, while NSEC_PER_USEC is defined as 1000L. On 32-bit systems, the multiplication is therefore performed using 32-bit unsigned arithmetic before the result is assigned to freq_update_delay_ns. For example, writing 4294968 to rate_limit_us wraps the delay from 4294968000 ns to 704 ns. This makes schedutil update far more often than configured. Add sugov_update_rate_limit_us() to widen rate_limit_us to s64 before converting it to nanoseconds. Use the helper when updating the tunable through sysfs and when starting the governor, so both paths perform the conversion without overflow. Fixes: 9bdcb44e391d ("cpufreq: schedutil: New governor based on scheduler utilization data") Signed-off-by: Hui Su Reviewed-by: Zhongqiu Han Cc: All applicable Link: https://patch.msgid.link/20260806142304.1761454-1-sh_def@163.com Signed-off-by: Rafael J. Wysocki --- kernel/sched/cpufreq_schedutil.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index a1782755efcc..49ccd6f1c185 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -62,6 +62,17 @@ static DEFINE_PER_CPU(struct sugov_cpu, sugov_cpu); /************************ Governor internals ***********************/ +static void sugov_update_rate_limit_us(struct sugov_policy *sg_policy) +{ + /* + * Cast rate_limit_us before multiplication to force 64-bit arithmetic. + * Otherwise, on 32-bit platforms, both operands are converted to + * 32-bit unsigned long and the multiplication may overflow. + */ + sg_policy->freq_update_delay_ns = + (s64)sg_policy->tunables->rate_limit_us * NSEC_PER_USEC; +} + static bool sugov_should_update_freq(struct sugov_policy *sg_policy, u64 time) { s64 delta_ns; @@ -608,7 +619,7 @@ rate_limit_us_store(struct gov_attr_set *attr_set, const char *buf, size_t count tunables->rate_limit_us = rate_limit_us; list_for_each_entry(sg_policy, &attr_set->policy_list, tunables_hook) - sg_policy->freq_update_delay_ns = rate_limit_us * NSEC_PER_USEC; + sugov_update_rate_limit_us(sg_policy); return count; } @@ -850,7 +861,7 @@ static int sugov_start(struct cpufreq_policy *policy) void (*uu)(struct update_util_data *data, u64 time, unsigned int flags); unsigned int cpu; - sg_policy->freq_update_delay_ns = sg_policy->tunables->rate_limit_us * NSEC_PER_USEC; + sugov_update_rate_limit_us(sg_policy); sg_policy->last_freq_update_time = 0; sg_policy->next_freq = 0; sg_policy->work_in_progress = false; From c659fa329ea25baa6f8ab01e73d9bc925911c4b2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 5 Aug 2026 14:15:31 +0200 Subject: [PATCH 2/4] cpufreq: intel_pstate: Consolidate HWP P-states initialization After previous changes, intel_pstate_hybrid_hwp_adjust() does not do much and its name and kerneldoc comment (which is not really necessary because the function is static) have become a bit confusing. Moreover, the initialization of P-states on systems with HWP enabled is divided between it and a direct conditional statement branch in intel_pstate_get_cpu_pstates() which is not super-easy to follow. Address this by introducing intel_pstate_get_hwp_pstates() for the entire HWP-specific initialization of P-states and moving the code from intel_pstate_hybrid_hwp_adjust() into it along with some HWP-related code from intel_pstate_get_cpu_pstates(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/6021518.DvuYhMxLoT@rafael.j.wysocki --- drivers/cpufreq/intel_pstate.c | 113 ++++++++++++++++----------------- 1 file changed, 54 insertions(+), 59 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 7ce5981cbae5..8a068fe8320a 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -555,47 +555,6 @@ static int intel_pstate_freq_to_hwp(struct cpudata *cpu, int freq) return intel_pstate_freq_to_hwp_rel(cpu, freq, CPUFREQ_RELATION_L); } -/** - * intel_pstate_hybrid_hwp_adjust - Calibrate HWP performance levels. - * @cpu: Target CPU. - * - * On hybrid processors, HWP may expose more performance levels than there are - * P-states accessible through the PERF_CTL interface. If that happens, the - * scaling factor between HWP performance levels and CPU frequency will be less - * than the scaling factor between P-state values and CPU frequency. - * - * In that case, adjust the CPU parameters used in computations accordingly. - */ -static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu) -{ - int perf_ctl_max_phys = cpu->pstate.max_pstate_physical; - int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; - int perf_ctl_turbo = pstate_funcs.get_turbo(cpu->cpu); - int scaling = cpu->pstate.scaling; - int freq; - - pr_debug("CPU%d: PERF_CTL max_phys = %d\n", cpu->cpu, perf_ctl_max_phys); - pr_debug("CPU%d: PERF_CTL turbo = %d\n", cpu->cpu, perf_ctl_turbo); - pr_debug("CPU%d: PERF_CTL scaling = %d\n", cpu->cpu, perf_ctl_scaling); - pr_debug("CPU%d: HWP_CAP guaranteed = %d\n", cpu->cpu, cpu->pstate.max_pstate); - pr_debug("CPU%d: HWP_CAP highest = %d\n", cpu->cpu, cpu->pstate.turbo_pstate); - pr_debug("CPU%d: HWP-to-frequency scaling factor: %d\n", cpu->cpu, scaling); - - if (scaling == perf_ctl_scaling) - return; - - hwp_is_hybrid = true; - - freq = perf_ctl_max_phys * perf_ctl_scaling; - cpu->pstate.max_pstate_physical = intel_pstate_freq_to_hwp(cpu, freq); - - /* - * Cast the min P-state value retrieved via pstate_funcs.get_min() to - * the effective range of HWP performance levels. - */ - cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, cpu->pstate.min_freq); -} - static bool turbo_is_disabled(void) { u64 misc_en; @@ -2293,34 +2252,70 @@ static int hwp_get_cpu_scaling(int cpu) return intel_pstate_cppc_get_scaling(cpu); } +static void intel_pstate_get_hwp_pstates(struct cpudata *cpu) +{ + int perf_ctl_max_phys = cpu->pstate.max_pstate_physical; + int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; + int perf_ctl_turbo = cpu->pstate.turbo_pstate; + int cpuid = cpu->cpu; + + __intel_pstate_get_hwp_cap(cpu); + + if (!pstate_funcs.get_cpu_scaling) + return; + + pr_debug("CPU%d: PERF_CTL max_phys = %d\n", cpuid, perf_ctl_max_phys); + pr_debug("CPU%d: PERF_CTL turbo = %d\n", cpuid, perf_ctl_turbo); + pr_debug("CPU%d: PERF_CTL scaling = %d\n", cpuid, perf_ctl_scaling); + pr_debug("CPU%d: PERF_CTL min = %d\n", cpuid, cpu->pstate.min_pstate); + pr_debug("CPU%d: HWP_CAP guaranteed = %d\n", cpuid, cpu->pstate.max_pstate); + pr_debug("CPU%d: HWP_CAP highest = %d\n", cpuid, cpu->pstate.turbo_pstate); + + cpu->pstate.scaling = pstate_funcs.get_cpu_scaling(cpuid); + + pr_debug("CPU%d: HWP-to-frequency scaling = %d\n", cpuid, cpu->pstate.scaling); + + /* + * On hybrid processors, HWP may expose more performance levels than + * there are P-states accessible through the PERF_CTL interface. If + * that happens, the scaling between HWP performance levels and CPU + * frequency will be less than the scaling between P-state values and + * CPU frequency. In that case, update the maximum physical non-turbo + * performance level accordingly. + */ + if (cpu->pstate.scaling != perf_ctl_scaling) { + int freq; + + freq = perf_ctl_max_phys * perf_ctl_scaling; + cpu->pstate.max_pstate_physical = intel_pstate_freq_to_hwp(cpu, freq); + + freq = cpu->pstate.min_freq; + cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, freq); + + hwp_is_hybrid = true; + } + /* + * If the CPU is going online for the first time and it was offline + * initially, asym capacity scaling may need to be updated. + */ + hybrid_update_capacity(cpu); +} + static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) { int perf_ctl_scaling = pstate_funcs.get_scaling(); cpu->pstate.max_pstate_physical = pstate_funcs.get_max_physical(cpu->cpu); + cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(cpu->cpu); cpu->pstate.min_pstate = pstate_funcs.get_min(cpu->cpu); cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling; cpu->pstate.perf_ctl_scaling = perf_ctl_scaling; + cpu->pstate.scaling = perf_ctl_scaling; - if (hwp_active) { - __intel_pstate_get_hwp_cap(cpu); - - if (pstate_funcs.get_cpu_scaling) { - cpu->pstate.scaling = pstate_funcs.get_cpu_scaling(cpu->cpu); - intel_pstate_hybrid_hwp_adjust(cpu); - } else { - cpu->pstate.scaling = perf_ctl_scaling; - } - /* - * If the CPU is going online for the first time and it was - * offline initially, asym capacity scaling needs to be updated. - */ - hybrid_update_capacity(cpu); - } else { - cpu->pstate.scaling = perf_ctl_scaling; + if (hwp_active) + intel_pstate_get_hwp_pstates(cpu); + else cpu->pstate.max_pstate = pstate_funcs.get_max(cpu->cpu); - cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(cpu->cpu); - } intel_pstate_update_freq_limits(cpu); From d657aa1b8ed345eeb24e33967de2655547993fce Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 5 Aug 2026 14:18:11 +0200 Subject: [PATCH 3/4] cpufreq: intel_pstate: Avoid using DESIRED_PERF when DEC is enabled In principle, the desired performance level can be set in MSR_HWP_REQUEST to indicate to the processor what performance level the OS would like the given CPU to run at, but if the Dynamic Efficiency Control (DEC) feature is enabled in the processor, doing so may result in confusing the processor firmware. It is then better to let the processor firmware figure out the most suitable performance level by itself. Accordingly, make intel_pstate always set the desired performance level to zero (which means "no preference") when running on a platform with DEC enabled. Signed-off-by: Rafael J. Wysocki Acked-by: Srinivas Pandruvada Link: https://patch.msgid.link/4758098.LvFx2qVVIh@rafael.j.wysocki --- drivers/cpufreq/intel_pstate.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 8a068fe8320a..ceb340f7a110 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -303,6 +303,7 @@ static bool per_cpu_limits __ro_after_init; static bool hwp_forced __ro_after_init; static bool hwp_boost __read_mostly; static bool hwp_is_hybrid; +static u32 hwp_desired_mask __read_mostly = ~0U; static struct cpufreq_driver *intel_pstate_driver __read_mostly; @@ -3150,7 +3151,7 @@ static void intel_cpufreq_hwp_update(struct cpudata *cpu, u32 min, u32 max, value |= HWP_MAX_PERF(max); value &= ~HWP_DESIRED_PERF(~0L); - value |= HWP_DESIRED_PERF(desired); + value |= HWP_DESIRED_PERF(desired & hwp_desired_mask); if (value == prev) return; @@ -3760,9 +3761,9 @@ static bool hwp_check_epp(void) static bool hwp_check_dec(void) { - u64 power_ctl; + u64 power_ctl = 0; - rdmsrq(MSR_IA32_POWER_CTL, power_ctl); + rdmsrq_safe(MSR_IA32_POWER_CTL, &power_ctl); return !!(power_ctl & BIT(POWER_CTL_DEC_ENABLE)); } @@ -3787,6 +3788,7 @@ static int __init intel_pstate_init(void) id = x86_match_cpu(hwp_support_ids); if (id) { bool epp_present = hwp_check_epp(); + bool dec_present = hwp_check_dec(); /* * If HWP is enabled already, there is no choice but to deal @@ -3798,7 +3800,7 @@ static int __init intel_pstate_init(void) no_hwp = 0; } else if (no_load) { return -ENODEV; - } else if (!epp_present && !hwp_check_dec()) { + } else if (!epp_present && !dec_present) { /* * Avoid enabling HWP for processors without EPP support * unless the Dynamic Efficiency Control (DEC) enable @@ -3820,6 +3822,9 @@ static int __init intel_pstate_init(void) if (!default_driver) default_driver = &intel_pstate; + if (dec_present) + hwp_desired_mask = 0; + if (!id->driver_data) pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling; From cb258d651d747a7f7063d40f145418bee562ceaf Mon Sep 17 00:00:00 2001 From: Shibo Zhu <3499129952@qq.com> Date: Wed, 19 Aug 2026 00:18:39 +0800 Subject: [PATCH 4/4] PM: sleep: Unblock runtime PM when device prepare fails device_prepare() blocks runtime PM for a device with runtime PM disabled before invoking its system-sleep ->prepare() callback. For a device that has never enabled runtime PM, this changes dev->power.last_status from RPM_INVALID to RPM_BLOCKED. If the callback returns an error, dpm_prepare() does not move the device to dpm_prepared_list. Consequently, the recovery path through dpm_complete() never calls device_complete() for the failing device. The error path drops the runtime PM usage reference, but does not clear RPM_BLOCKED. A later legitimate pm_runtime_enable() then reports: Attempt to enable runtime PM when it is blocked before clearing the stale state. Call pm_runtime_unblock() on the prepare error path before dropping the runtime PM reference, matching the cleanup performed by device_complete(). The issue was reproduced with a platform test device whose ->prepare() callback returns -EIO while runtime PM has never been enabled. Before the fix, last_status remained RPM_BLOCKED after the failed suspend and the first pm_runtime_enable() produced the warning above. With the fix, last_status is restored to RPM_INVALID and the warning is absent. Fixes: 3e5eee147b7b ("PM: Block enabling of runtime PM during system suspend") Cc: All applicable Signed-off-by: Shibo Zhu <3499129952@qq.com> Link: https://patch.msgid.link/tencent_C5AC0A02FC01F700E764F8C2E3ECE4F41009@qq.com Signed-off-by: Rafael J. Wysocki --- drivers/base/power/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 184dc4b3b938..e130da428141 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -2248,6 +2248,7 @@ static int device_prepare(struct device *dev, pm_message_t state) if (ret < 0) { suspend_report_result(dev, callback, ret); + pm_runtime_unblock(dev); pm_runtime_put(dev); return ret; }