Message ID | 20240626042733.3747-3-mario.limonciello@amd.com (mailing list archive) |
---|---|
State | Under Review |
Delegated to: | Mario Limonciello |
Headers | show |
Series | AMD Pstate Driver Core Performance Boost | expand |
Mario Limonciello <mario.limonciello@amd.com> writes: > From: Perry Yuan <Perry.Yuan@amd.com> > > The "Core Performance Boost (CPB) feature, when enabled in the BIOS, > allows the OS to control the highest performance for each individual > core. The active, passive and the guided modes of the amd-pstate driver > do support controlling the core frequency boost when this BIOS feature > is enabled. Additionally, the amd-pstate driver provides a sysfs > interface allowing the user to activate/deactivate this core performance > boost feature at runtime. > > Add support for the set_boost callback in the active mode driver to > enable boost control via the cpufreq core. This ensures a consistent > boost control interface across all pstate modes, including passive > mode, guided mode, and active mode. > > With this addition, all three pstate modes can support the same boost > control interface with unique interface and global CPB control. Each > CPU also supports individual boost control, allowing global CPB to > change all cores' boost states simultaneously. Specific CPUs can > update their boost states separately, ensuring all cores' boost > states are synchronized. > > Cc: Oleksandr Natalenko <oleksandr@natalenko.name> > Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217931 > Signed-off-by: Perry Yuan <Perry.Yuan@amd.com> > Co-developed-by: Mario Limonciello <mario.limonciello@amd.com> > Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> > --- > v14->v15: > * When turning off boost only reset frequency if it's set above > nominal frequency Looks good to me. Reviewed-by: Gautham R. Shenoy <gautham.shenoy@amd.com> > --- > drivers/cpufreq/amd-pstate.c | 117 ++++++++++++++++++++++++++++------- > drivers/cpufreq/amd-pstate.h | 1 + > 2 files changed, 96 insertions(+), 22 deletions(-) > > diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c > index afcf398574f6..db4fbd8d1e06 100644 > --- a/drivers/cpufreq/amd-pstate.c > +++ b/drivers/cpufreq/amd-pstate.c > @@ -679,43 +679,105 @@ static void amd_pstate_adjust_perf(unsigned int cpu, > cpufreq_cpu_put(policy); > } > > -static int amd_pstate_set_boost(struct cpufreq_policy *policy, int state) > +static int amd_pstate_cpu_boost_update(struct cpufreq_policy *policy, bool on) > { > struct amd_cpudata *cpudata = policy->driver_data; > + struct cppc_perf_ctrls perf_ctrls; > + u32 highest_perf, nominal_perf, nominal_freq, max_freq; > int ret; > > - if (!cpudata->boost_supported) { > - pr_err("Boost mode is not supported by this processor or SBIOS\n"); > - return -EINVAL; > + highest_perf = READ_ONCE(cpudata->highest_perf); > + nominal_perf = READ_ONCE(cpudata->nominal_perf); > + nominal_freq = READ_ONCE(cpudata->nominal_freq); > + max_freq = READ_ONCE(cpudata->max_freq); > + > + if (boot_cpu_has(X86_FEATURE_CPPC)) { > + u64 value = READ_ONCE(cpudata->cppc_req_cached); > + > + value &= ~GENMASK_ULL(7, 0); > + value |= on ? highest_perf : nominal_perf; > + WRITE_ONCE(cpudata->cppc_req_cached, value); > + > + wrmsrl_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ, value); > + } else { > + perf_ctrls.max_perf = on ? highest_perf : nominal_perf; > + ret = cppc_set_perf(cpudata->cpu, &perf_ctrls); > + if (ret) { > + cpufreq_cpu_release(policy); > + pr_debug("Failed to set max perf on CPU:%d. ret:%d\n", > + cpudata->cpu, ret); > + return ret; > + } > } > > - if (state) > - policy->cpuinfo.max_freq = cpudata->max_freq; > - else > - policy->cpuinfo.max_freq = cpudata->nominal_freq * 1000; > + if (on) > + policy->cpuinfo.max_freq = max_freq; > + else if (policy->cpuinfo.max_freq > nominal_freq * 1000) > + policy->cpuinfo.max_freq = nominal_freq * 1000; > > policy->max = policy->cpuinfo.max_freq; > > - ret = freq_qos_update_request(&cpudata->req[1], > - policy->cpuinfo.max_freq); > - if (ret < 0) > - return ret; > + if (cppc_state == AMD_PSTATE_PASSIVE) { > + ret = freq_qos_update_request(&cpudata->req[1], policy->cpuinfo.max_freq); > + if (ret < 0) > + pr_debug("Failed to update freq constraint: CPU%d\n", cpudata->cpu); > + } > > - return 0; > + return ret < 0 ? ret : 0; > } > > -static void amd_pstate_boost_init(struct amd_cpudata *cpudata) > +static int amd_pstate_set_boost(struct cpufreq_policy *policy, int state) > { > - u32 highest_perf, nominal_perf; > + struct amd_cpudata *cpudata = policy->driver_data; > + int ret; > > - highest_perf = READ_ONCE(cpudata->highest_perf); > - nominal_perf = READ_ONCE(cpudata->nominal_perf); > + if (!cpudata->boost_supported) { > + pr_err("Boost mode is not supported by this processor or SBIOS\n"); > + return -EOPNOTSUPP; > + } > + mutex_lock(&amd_pstate_driver_lock); > + ret = amd_pstate_cpu_boost_update(policy, state); > + WRITE_ONCE(cpudata->boost_state, !ret ? state : false); > + policy->boost_enabled = !ret ? state : false; > + refresh_frequency_limits(policy); > + mutex_unlock(&amd_pstate_driver_lock); > > - if (highest_perf <= nominal_perf) > - return; > + return ret; > +} > > - cpudata->boost_supported = true; > +static int amd_pstate_init_boost_support(struct amd_cpudata *cpudata) > +{ > + u64 boost_val; > + int ret = -1; > + > + /* > + * If platform has no CPB support or disable it, initialize current driver > + * boost_enabled state to be false, it is not an error for cpufreq core to handle. > + */ > + if (!cpu_feature_enabled(X86_FEATURE_CPB)) { > + pr_debug_once("Boost CPB capabilities not present in the processor\n"); > + ret = 0; > + goto exit_err; > + } > + > + /* at least one CPU supports CPB, even if others fail later on to set up */ > current_pstate_driver->boost_enabled = true; > + > + ret = rdmsrl_on_cpu(cpudata->cpu, MSR_K7_HWCR, &boost_val); > + if (ret) { > + pr_err_once("failed to read initial CPU boost state!\n"); > + ret = -EIO; > + goto exit_err; > + } > + > + if (!(boost_val & MSR_K7_HWCR_CPB_DIS)) > + cpudata->boost_supported = true; > + > + return 0; > + > +exit_err: > + cpudata->boost_supported = false; > + return ret; > } > > static void amd_perf_ctl_reset(unsigned int cpu) > @@ -968,6 +1030,10 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy) > if (ret) > goto free_cpudata1; > > + ret = amd_pstate_init_boost_support(cpudata); > + if (ret) > + goto free_cpudata1; > + > min_freq = READ_ONCE(cpudata->min_freq); > max_freq = READ_ONCE(cpudata->max_freq); > > @@ -980,6 +1046,8 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy) > policy->cpuinfo.min_freq = min_freq; > policy->cpuinfo.max_freq = max_freq; > > + policy->boost_enabled = READ_ONCE(cpudata->boost_supported); > + > /* It will be updated by governor */ > policy->cur = policy->cpuinfo.min_freq; > > @@ -1005,7 +1073,6 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy) > > policy->driver_data = cpudata; > > - amd_pstate_boost_init(cpudata); > if (!current_pstate_driver->adjust_perf) > current_pstate_driver->adjust_perf = amd_pstate_adjust_perf; > > @@ -1420,6 +1487,10 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) > if (ret) > goto free_cpudata1; > > + ret = amd_pstate_init_boost_support(cpudata); > + if (ret) > + goto free_cpudata1; > + > min_freq = READ_ONCE(cpudata->min_freq); > max_freq = READ_ONCE(cpudata->max_freq); > > @@ -1435,6 +1506,8 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) > policy->min = policy->cpuinfo.min_freq; > policy->max = policy->cpuinfo.max_freq; > > + policy->boost_enabled = READ_ONCE(cpudata->boost_supported); > + > /* > * Set the policy to provide a valid fallback value in case > * the default cpufreq governor is neither powersave nor performance. > @@ -1456,7 +1529,6 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) > return ret; > WRITE_ONCE(cpudata->cppc_cap1_cached, value); > } > - amd_pstate_boost_init(cpudata); > > return 0; > > @@ -1718,6 +1790,7 @@ static struct cpufreq_driver amd_pstate_epp_driver = { > .suspend = amd_pstate_epp_suspend, > .resume = amd_pstate_epp_resume, > .update_limits = amd_pstate_update_limits, > + .set_boost = amd_pstate_set_boost, > .name = "amd-pstate-epp", > .attr = amd_pstate_epp_attr, > }; > diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h > index f80b33fa5d43..cc8bb2bc325a 100644 > --- a/drivers/cpufreq/amd-pstate.h > +++ b/drivers/cpufreq/amd-pstate.h > @@ -100,6 +100,7 @@ struct amd_cpudata { > u64 cppc_cap1_cached; > bool suspended; > s16 epp_default; > + bool boost_state; > }; > > #endif /* _LINUX_AMD_PSTATE_H */ > -- > 2.43.0
diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index afcf398574f6..db4fbd8d1e06 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -679,43 +679,105 @@ static void amd_pstate_adjust_perf(unsigned int cpu, cpufreq_cpu_put(policy); } -static int amd_pstate_set_boost(struct cpufreq_policy *policy, int state) +static int amd_pstate_cpu_boost_update(struct cpufreq_policy *policy, bool on) { struct amd_cpudata *cpudata = policy->driver_data; + struct cppc_perf_ctrls perf_ctrls; + u32 highest_perf, nominal_perf, nominal_freq, max_freq; int ret; - if (!cpudata->boost_supported) { - pr_err("Boost mode is not supported by this processor or SBIOS\n"); - return -EINVAL; + highest_perf = READ_ONCE(cpudata->highest_perf); + nominal_perf = READ_ONCE(cpudata->nominal_perf); + nominal_freq = READ_ONCE(cpudata->nominal_freq); + max_freq = READ_ONCE(cpudata->max_freq); + + if (boot_cpu_has(X86_FEATURE_CPPC)) { + u64 value = READ_ONCE(cpudata->cppc_req_cached); + + value &= ~GENMASK_ULL(7, 0); + value |= on ? highest_perf : nominal_perf; + WRITE_ONCE(cpudata->cppc_req_cached, value); + + wrmsrl_on_cpu(cpudata->cpu, MSR_AMD_CPPC_REQ, value); + } else { + perf_ctrls.max_perf = on ? highest_perf : nominal_perf; + ret = cppc_set_perf(cpudata->cpu, &perf_ctrls); + if (ret) { + cpufreq_cpu_release(policy); + pr_debug("Failed to set max perf on CPU:%d. ret:%d\n", + cpudata->cpu, ret); + return ret; + } } - if (state) - policy->cpuinfo.max_freq = cpudata->max_freq; - else - policy->cpuinfo.max_freq = cpudata->nominal_freq * 1000; + if (on) + policy->cpuinfo.max_freq = max_freq; + else if (policy->cpuinfo.max_freq > nominal_freq * 1000) + policy->cpuinfo.max_freq = nominal_freq * 1000; policy->max = policy->cpuinfo.max_freq; - ret = freq_qos_update_request(&cpudata->req[1], - policy->cpuinfo.max_freq); - if (ret < 0) - return ret; + if (cppc_state == AMD_PSTATE_PASSIVE) { + ret = freq_qos_update_request(&cpudata->req[1], policy->cpuinfo.max_freq); + if (ret < 0) + pr_debug("Failed to update freq constraint: CPU%d\n", cpudata->cpu); + } - return 0; + return ret < 0 ? ret : 0; } -static void amd_pstate_boost_init(struct amd_cpudata *cpudata) +static int amd_pstate_set_boost(struct cpufreq_policy *policy, int state) { - u32 highest_perf, nominal_perf; + struct amd_cpudata *cpudata = policy->driver_data; + int ret; - highest_perf = READ_ONCE(cpudata->highest_perf); - nominal_perf = READ_ONCE(cpudata->nominal_perf); + if (!cpudata->boost_supported) { + pr_err("Boost mode is not supported by this processor or SBIOS\n"); + return -EOPNOTSUPP; + } + mutex_lock(&amd_pstate_driver_lock); + ret = amd_pstate_cpu_boost_update(policy, state); + WRITE_ONCE(cpudata->boost_state, !ret ? state : false); + policy->boost_enabled = !ret ? state : false; + refresh_frequency_limits(policy); + mutex_unlock(&amd_pstate_driver_lock); - if (highest_perf <= nominal_perf) - return; + return ret; +} - cpudata->boost_supported = true; +static int amd_pstate_init_boost_support(struct amd_cpudata *cpudata) +{ + u64 boost_val; + int ret = -1; + + /* + * If platform has no CPB support or disable it, initialize current driver + * boost_enabled state to be false, it is not an error for cpufreq core to handle. + */ + if (!cpu_feature_enabled(X86_FEATURE_CPB)) { + pr_debug_once("Boost CPB capabilities not present in the processor\n"); + ret = 0; + goto exit_err; + } + + /* at least one CPU supports CPB, even if others fail later on to set up */ current_pstate_driver->boost_enabled = true; + + ret = rdmsrl_on_cpu(cpudata->cpu, MSR_K7_HWCR, &boost_val); + if (ret) { + pr_err_once("failed to read initial CPU boost state!\n"); + ret = -EIO; + goto exit_err; + } + + if (!(boost_val & MSR_K7_HWCR_CPB_DIS)) + cpudata->boost_supported = true; + + return 0; + +exit_err: + cpudata->boost_supported = false; + return ret; } static void amd_perf_ctl_reset(unsigned int cpu) @@ -968,6 +1030,10 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy) if (ret) goto free_cpudata1; + ret = amd_pstate_init_boost_support(cpudata); + if (ret) + goto free_cpudata1; + min_freq = READ_ONCE(cpudata->min_freq); max_freq = READ_ONCE(cpudata->max_freq); @@ -980,6 +1046,8 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.min_freq = min_freq; policy->cpuinfo.max_freq = max_freq; + policy->boost_enabled = READ_ONCE(cpudata->boost_supported); + /* It will be updated by governor */ policy->cur = policy->cpuinfo.min_freq; @@ -1005,7 +1073,6 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy) policy->driver_data = cpudata; - amd_pstate_boost_init(cpudata); if (!current_pstate_driver->adjust_perf) current_pstate_driver->adjust_perf = amd_pstate_adjust_perf; @@ -1420,6 +1487,10 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) if (ret) goto free_cpudata1; + ret = amd_pstate_init_boost_support(cpudata); + if (ret) + goto free_cpudata1; + min_freq = READ_ONCE(cpudata->min_freq); max_freq = READ_ONCE(cpudata->max_freq); @@ -1435,6 +1506,8 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) policy->min = policy->cpuinfo.min_freq; policy->max = policy->cpuinfo.max_freq; + policy->boost_enabled = READ_ONCE(cpudata->boost_supported); + /* * Set the policy to provide a valid fallback value in case * the default cpufreq governor is neither powersave nor performance. @@ -1456,7 +1529,6 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) return ret; WRITE_ONCE(cpudata->cppc_cap1_cached, value); } - amd_pstate_boost_init(cpudata); return 0; @@ -1718,6 +1790,7 @@ static struct cpufreq_driver amd_pstate_epp_driver = { .suspend = amd_pstate_epp_suspend, .resume = amd_pstate_epp_resume, .update_limits = amd_pstate_update_limits, + .set_boost = amd_pstate_set_boost, .name = "amd-pstate-epp", .attr = amd_pstate_epp_attr, }; diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h index f80b33fa5d43..cc8bb2bc325a 100644 --- a/drivers/cpufreq/amd-pstate.h +++ b/drivers/cpufreq/amd-pstate.h @@ -100,6 +100,7 @@ struct amd_cpudata { u64 cppc_cap1_cached; bool suspended; s16 epp_default; + bool boost_state; }; #endif /* _LINUX_AMD_PSTATE_H */