From 3bff8f8e95fdc6ad19c8a1a8f87029094747e4bf Mon Sep 17 00:00:00 2001 From: Hui Su Date: Thu, 6 Aug 2026 22:23:04 +0800 Subject: [PATCH 1/6] 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/6] 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/6] 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 a8b842366f8ff469fe0fc700ba53770ded847ea9 Mon Sep 17 00:00:00 2001 From: "Li, Yifan" Date: Fri, 14 Aug 2026 11:10:08 +0800 Subject: [PATCH 4/6] powercap: intel_rapl: Sign-extend the PMU delta on counter wraparound The RAPL PMU misreports energy when the hardware energy counter overflows and wraps back to zero. perf event counts are defined to increase monotonically, but a single wraparound makes the PMU event count jump backwards by nearly the full counter range, and consumers that take the difference of two reads in unsigned arithmetic then underflow and report an absurd value. On a Panther Lake system (energy unit 61.035 uJ, counter range 262144 J) the package counter wraps every ~2.9 hours at 25 W, and turbostat prints one bogus sample per wraparound, per domain: PkgTmp PkgWatt CorWatt GFXWatt RAMWatt SysWatt 44 24.97 16.30 3.90 1.87 2145386370.35 43 2145240612.10 16.13 4.02 1.91 40.46 The RAPL energy counters are 32-bit wide on every register interface: MSR, MMIO and TPMI all describe ENERGY_COUNTER with a GENMASK(31, 0) mask. rapl_read_data_raw() applies that mask, so event_read_counter() returns the counter zero-extended in a u64. rapl_event_update() then computes delta = new_raw_count - prev_raw_count; without reducing the result modulo 2^32. While the counter does not wrap this is correct, but once the hardware counter wraps, new_raw_count < prev_raw_count and delta becomes (true_delta - 2^32), a large negative value. Declaring delta as s64 only makes that value representable; it does not correct it. That bogus delta is scaled and added to event->count, which is where the backwards jump comes from. Fix it the way arch/x86/events/rapl.c has done since the RAPL PMU was first introduced: shift both values up so that the 64-bit subtraction reduces modulo 2^32, then shift the difference back down with an arithmetic shift to sign-extend it. This is correct as long as at most one wraparound happens between two updates, which the existing overflow hrtimer already guarantees: its period is half of the counter range at the 200 W reference used in rapl_package_add_pmu_locked(). The problem has been present since the powercap RAPL PMU was added, but only affected TPMI RAPL until commit 748d6ba43afd ("powercap: intel_rapl: Enable MSR-based RAPL PMU support") routed MSR RAPL through the same PMU, which exposed it on client platforms such as Panther Lake. Fixes: 575024a8aa7c ("powercap: intel_rapl: Introduce APIs for PMU support") Reported-by: Jyoti, Anand B Signed-off-by: Li, Yifan Signed-off-by: Gao Jianfeng Tested-by: Jyoti, Anand B Acked-by: Srinivas Pandruvada Reviewed-by: Kuppuswamy Sathyanarayanan Link: https://patch.msgid.link/20260814031008.750911-1-yifan2.li@intel.com Signed-off-by: Rafael J. Wysocki --- drivers/powercap/intel_rapl_common.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c index 1006d183d508..6b7d11a0abc5 100644 --- a/drivers/powercap/intel_rapl_common.c +++ b/drivers/powercap/intel_rapl_common.c @@ -32,6 +32,9 @@ #define ENERGY_STATUS_MASK GENMASK(31, 0) +/* Width of the RAPL energy counters, see the *_ENERGY_STATUS_MASK defines */ +#define RAPL_CNTR_WIDTH 32 + #define POWER_UNIT_OFFSET 0x00 #define POWER_UNIT_MASK GENMASK(3, 0) @@ -1227,6 +1230,7 @@ static u64 rapl_event_update(struct perf_event *event) struct rapl_package_pmu_data *data = event_to_pmu_data(event); u64 prev_raw_count, new_raw_count; s64 delta, sdelta; + int shift = 64 - RAPL_CNTR_WIDTH; /* * Follow the generic code to drain hwc->prev_count. @@ -1243,8 +1247,13 @@ static u64 rapl_event_update(struct perf_event *event) * Now we have the new raw value and have updated the prev * timestamp already. We can now calculate the elapsed delta * (event-)time and add that to the generic event. + * + * Careful, the counter is narrower than u64 and is not + * sign-extended above its physical width. Shift both values up + * so that the subtraction wraps, then shift the result back down. */ - delta = new_raw_count - prev_raw_count; + delta = (new_raw_count << shift) - (prev_raw_count << shift); + delta >>= shift; /* * Scale delta to smallest unit (2^-32) 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 5/6] 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; } From 916b61abba586d950a4d8264e2841cc5ccfa4df1 Mon Sep 17 00:00:00 2001 From: Sumeet Pawnikar Date: Sat, 22 Aug 2026 15:16:57 +0530 Subject: [PATCH 6/6] powercap: intel_rapl: Fix kernel panic during PMU unbind rapl_package_add_pmu() fails internally at perf_pmu_register(), and rapl_pmu_update() leaves the global rapl_pmu.pmu structure zero-initialized via memset and returns an error. But any previously probed packages retain has_pmu = true. When the driver is subsequently unbound or removed, rapl_package_remove_pmu_locked() sees has_pmu == true and unconditionally calls perf_pmu_unregister(&rapl_pmu.pmu) on the zeroed-out structure. This attempts a list_del_rcu() on a NULL list head, immediately causing a kernel panic. Fix this by checking if the PMU is actually registered before attempting to unregister it. Signed-off-by: Sumeet Pawnikar Reviewed-by: Abel Vesa [ rjw: Added empty line after the new conditional ] Link: https://patch.msgid.link/20260822094657.12489-1-sumeet4linux@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/powercap/intel_rapl_common.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c index 6b7d11a0abc5..0c743a86df76 100644 --- a/drivers/powercap/intel_rapl_common.c +++ b/drivers/powercap/intel_rapl_common.c @@ -1661,7 +1661,9 @@ void rapl_package_remove_pmu_locked(struct rapl_package *rp) return; } - perf_pmu_unregister(&rapl_pmu.pmu); + if (rapl_pmu.registered) + perf_pmu_unregister(&rapl_pmu.pmu); + memset(&rapl_pmu, 0, sizeof(struct rapl_pmu)); } EXPORT_SYMBOL_NS_GPL(rapl_package_remove_pmu_locked, "INTEL_RAPL");