Power management fixes for 7.2-rc6

- Allow fast frequency switching in the ACPI CPPC library only when
    every supported control used by the driver callback has an address
    space already accepted for fast access (Christian Loehle)
 
  - Skip writes to unsupported performance controls in the ACPI CPPC
    library (Christian Loehle)
 
  - Update cppc_cpufreq_update_perf_limits() to read policy->min and
    policy->max once and, if the lockless snapshot is inconsistent,
    reduce the minimum to the observed maximum, along the lines of
    cpufreq_driver_resolve_freq() (Christian Loehle)
 
  - Fix a possible memory leak in the powernowk8_cpu_init() error
    paths (Abdun Nihaal)
 
  - Loosen the requirement on lowest nonlinear frequency != min freq in
    the amd-pstate driver that is too tight for new systems some of
    which actually have the lowest nonlinear frequency identical to the
    minimum frequency (Mario Limonciello)
 
  - Prevent amd-pstate from loading on unsupported hardware (Rong Zhang)
 
  - Address an initialization race in the schedutil governor when it
    runs on multi-CPU cpufreq policies, by making it initialize all
    per-CPU structures first and only then publish the per-CPU
    utilization update hooks (Zhongqiu Han)
 -----BEGIN PGP SIGNATURE-----
 
 iQFGBAABCAAwFiEEcM8Aw/RY0dgsiRUR7l+9nS/U47UFAmprmRgSHHJqd0Byand5
 c29ja2kubmV0AAoJEO5fvZ0v1OO1Ro0H/3llnBhbu9nq7QcFmlPqcRh2yqWozPoG
 oJTYEc/FEvPTEy65b6YxPFm64heiVTpv2gSZk0NVLnsXmDc3qNfoVFN7BvY8soPV
 X4hEZbVi/ZE84l2gmc3t9HjK+j9mxfyWiWj/QDqZvZzf3v0QN8Klo9SvzlwXlnXX
 1KdBpPPZYv7H0xIIOiL7K17JbvVTAXIwsV2SidOkm+29xArMYnHnzxqaIoh70kx7
 vvRLR0NwubHzPZt66L5TFQCwixa+O71YaQ2l1k6TOzZ5G2BEtriXjAaimAd88iAu
 Ogpie91bcERZdmoBIGtBCg9jSrHIqJE8ItugBsZw21BnIBsAKpgpJgw=
 =U/2b
 -----END PGP SIGNATURE-----

Merge tag 'pm-7.2-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm

Pull power management fixes from Rafael Wysocki:
 "These fix issues related to cpufreq, in the ACPI CPPC library and the
  generic CPPC cpufreq driver, in the powernow-k8 and amd-pstate
  drivers, and in the schedutil governor:

   - Allow fast frequency switching in the ACPI CPPC library only when
     every supported control used by the driver callback has an address
     space already accepted for fast access (Christian Loehle)

   - Skip writes to unsupported performance controls in the ACPI CPPC
     library (Christian Loehle)

   - Update cppc_cpufreq_update_perf_limits() to read policy->min and
     policy->max once and, if the lockless snapshot is inconsistent,
     reduce the minimum to the observed maximum, along the lines of
     cpufreq_driver_resolve_freq() (Christian Loehle)

   - Fix a possible memory leak in the powernowk8_cpu_init() error paths
     (Abdun Nihaal)

   - Loosen the requirement on lowest nonlinear frequency != min freq in
     the amd-pstate driver that is too tight for new systems some of
     which actually have the lowest nonlinear frequency identical to the
     minimum frequency (Mario Limonciello)

   - Prevent amd-pstate from loading on unsupported hardware (Rong
     Zhang)

   - Address an initialization race in the schedutil governor when it
     runs on multi-CPU cpufreq policies, by making it initialize all
     per-CPU structures first and only then publish the per-CPU
     utilization update hooks (Zhongqiu Han)"

* tag 'pm-7.2-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  cpufreq: powernow-k8: Fix possible memory leak in powernowk8_cpu_init()
  ACPI: CPPC: Skip writes to unsupported performance controls
  cpufreq/amd-pstate: Prevent the driver from loading on unsupported hardware
  cpufreq/amd-pstate: Loosen requirement on lowest nonlinear frequency != min freq
  cpufreq: schedutil: Publish util hooks only after all sg_cpu are initialized
  cpufreq: cppc: Sanitize lockless policy limit snapshots
  ACPI: CPPC: Check all controls for fast switching
This commit is contained in:
Linus Torvalds 2026-07-30 12:03:50 -07:00
commit 3708dd9488
6 changed files with 67 additions and 22 deletions

View File

@ -475,17 +475,29 @@ bool acpi_cpc_valid(void)
}
EXPORT_SYMBOL_GPL(acpi_cpc_valid);
bool cppc_allow_fast_switch(void)
bool cppc_allow_fast_switch(const struct cpumask *cpus)
{
struct cpc_register_resource *desired_reg;
struct cpc_register_resource *desired_reg, *min_reg, *max_reg;
struct cpc_desc *cpc_ptr;
int cpu;
for_each_online_cpu(cpu) {
for_each_cpu(cpu, cpus) {
cpc_ptr = per_cpu(cpc_desc_ptr, cpu);
if (!cpc_ptr)
return false;
desired_reg = &cpc_ptr->cpc_regs[DESIRED_PERF];
if (!CPC_IN_SYSTEM_MEMORY(desired_reg) &&
!CPC_IN_SYSTEM_IO(desired_reg))
min_reg = &cpc_ptr->cpc_regs[MIN_PERF];
max_reg = &cpc_ptr->cpc_regs[MAX_PERF];
if (!CPC_SUPPORTED(desired_reg) ||
(!CPC_IN_SYSTEM_MEMORY(desired_reg) &&
!CPC_IN_SYSTEM_IO(desired_reg)) ||
(CPC_SUPPORTED(min_reg) &&
!CPC_IN_SYSTEM_MEMORY(min_reg) &&
!CPC_IN_SYSTEM_IO(min_reg)) ||
(CPC_SUPPORTED(max_reg) &&
!CPC_IN_SYSTEM_MEMORY(max_reg) &&
!CPC_IN_SYSTEM_IO(max_reg)))
return false;
}
@ -1951,16 +1963,17 @@ int cppc_set_perf(int cpu, struct cppc_perf_ctrls *perf_ctrls)
cpc_desc->write_cmd_status = 0;
}
cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
if (CPC_SUPPORTED(desired_reg))
cpc_write(cpu, desired_reg, perf_ctrls->desired_perf);
/*
* Only write if min_perf and max_perf not zero. Some drivers pass zero
* value to min and max perf, but they don't mean to set the zero value,
* they just don't want to write to those registers.
*/
if (perf_ctrls->min_perf)
if (perf_ctrls->min_perf && CPC_SUPPORTED(min_perf_reg))
cpc_write(cpu, min_perf_reg, perf_ctrls->min_perf);
if (perf_ctrls->max_perf)
if (perf_ctrls->max_perf && CPC_SUPPORTED(max_perf_reg))
cpc_write(cpu, max_perf_reg, perf_ctrls->max_perf);
if (CPC_IN_PCC(desired_reg) || CPC_IN_PCC(min_perf_reg) || CPC_IN_PCC(max_perf_reg))

View File

@ -1031,7 +1031,7 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
return -EINVAL;
}
if (lowest_nonlinear_freq <= min_freq || lowest_nonlinear_freq > nominal_freq) {
if (lowest_nonlinear_freq < min_freq || lowest_nonlinear_freq > nominal_freq) {
pr_err("lowest_nonlinear_freq(%d) value is out of range [min_freq(%d), nominal_freq(%d)]\n",
lowest_nonlinear_freq, min_freq, nominal_freq);
return -EINVAL;
@ -2167,6 +2167,7 @@ static struct cpufreq_driver amd_pstate_epp_driver = {
};
/*
* Processors without frequency scaling support can't do CPPC.
* CPPC function is not supported for family ID 17H with model_ID ranging from 0x10 to 0x2F.
* show the debug message that helps to check if the CPU has CPPC support for loading issue.
*/
@ -2175,6 +2176,11 @@ static bool amd_cppc_supported(void)
struct cpuinfo_x86 *c = &cpu_data(0);
bool warn = false;
if (!cpu_feature_enabled(X86_FEATURE_HW_PSTATE)) {
pr_debug_once("frequency scaling is not supported by the processor\n");
return false;
}
if ((boot_cpu_data.x86 == 0x17) && (boot_cpu_data.x86_model < 0x30)) {
pr_debug_once("CPPC feature is not supported by the processor\n");
return false;

View File

@ -290,19 +290,32 @@ static inline void cppc_freq_invariance_exit(void)
}
#endif /* CONFIG_ACPI_CPPC_CPUFREQ_FIE */
static void cppc_cpufreq_get_perf_limits(struct cppc_cpudata *cpu_data,
struct cpufreq_policy *policy,
u32 *min_perf, u32 *max_perf)
{
struct cppc_perf_caps *caps = &cpu_data->perf_caps;
unsigned int min_freq, max_freq;
u32 min, max;
min_freq = READ_ONCE(policy->min);
max_freq = READ_ONCE(policy->max);
if (unlikely(min_freq > max_freq))
min_freq = max_freq;
min = cppc_khz_to_perf(caps, min_freq);
max = cppc_khz_to_perf(caps, max_freq);
*min_perf = clamp_t(u32, min, caps->lowest_perf, caps->highest_perf);
*max_perf = clamp_t(u32, max, caps->lowest_perf, caps->highest_perf);
}
static void cppc_cpufreq_update_perf_limits(struct cppc_cpudata *cpu_data,
struct cpufreq_policy *policy)
{
struct cppc_perf_caps *caps = &cpu_data->perf_caps;
u32 min_perf, max_perf;
min_perf = cppc_khz_to_perf(caps, policy->min);
max_perf = cppc_khz_to_perf(caps, policy->max);
cpu_data->perf_ctrls.min_perf =
clamp_t(u32, min_perf, caps->lowest_perf, caps->highest_perf);
cpu_data->perf_ctrls.max_perf =
clamp_t(u32, max_perf, caps->lowest_perf, caps->highest_perf);
cppc_cpufreq_get_perf_limits(cpu_data, policy,
&cpu_data->perf_ctrls.min_perf,
&cpu_data->perf_ctrls.max_perf);
}
static int cppc_cpufreq_set_target(struct cpufreq_policy *policy,
@ -693,7 +706,7 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy)
goto out;
}
policy->fast_switch_possible = cppc_allow_fast_switch();
policy->fast_switch_possible = cppc_allow_fast_switch(policy->cpus);
policy->dvfs_possible_from_any_cpu = true;
/*

View File

@ -1084,6 +1084,7 @@ static int powernowk8_cpu_init(struct cpufreq_policy *pol)
err_out_exit_acpi:
powernow_k8_cpu_exit_acpi(data);
kfree(data->powernow_table);
err_out:
kfree(data);

View File

@ -170,7 +170,7 @@ extern u64 cppc_get_dmi_max_khz(void);
extern unsigned int cppc_perf_to_khz(struct cppc_perf_caps *caps, unsigned int perf);
extern unsigned int cppc_khz_to_perf(struct cppc_perf_caps *caps, unsigned int freq);
extern bool acpi_cpc_valid(void);
extern bool cppc_allow_fast_switch(void);
bool cppc_allow_fast_switch(const struct cpumask *cpus);
extern int acpi_get_psd_map(unsigned int cpu, struct cppc_cpudata *cpu_data);
extern int cppc_get_transition_latency(int cpu);
extern bool cpc_ffh_supported(void);
@ -234,7 +234,8 @@ static inline bool acpi_cpc_valid(void)
{
return false;
}
static inline bool cppc_allow_fast_switch(void)
static inline bool cppc_allow_fast_switch(const struct cpumask *cpus)
{
return false;
}

View File

@ -870,8 +870,19 @@ static int sugov_start(struct cpufreq_policy *policy)
memset(sg_cpu, 0, sizeof(*sg_cpu));
sg_cpu->cpu = cpu;
sg_cpu->sg_policy = sg_policy;
}
/*
* Publish the hooks only after all per-CPU data is initialized, so a
* shared policy's sugov_update_shared() never reads an uninitialized
* sibling sugov_cpu.
*/
for_each_cpu(cpu, policy->cpus) {
struct sugov_cpu *sg_cpu = &per_cpu(sugov_cpu, cpu);
cpufreq_add_update_util_hook(cpu, &sg_cpu->update_util, uu);
}
return 0;
}