Skip to content

Commit e30ca6d

Browse files
committed
cpufreq/amd-pstate: Add dynamic energy performance preference
Dynamic energy performance preference changes the EPP profile based on whether the machine is running on AC or DC power. A notification chain from the power supply core is used to adjust EPP values on plug in or plug out events. When enabled, the driver exposes a sysfs toggle for dynamic EPP, blocks manual writes to energy_performance_preference while it "owns" the EPP updates. For non-server systems: * the default EPP for AC mode is `performance`. * the default EPP for DC mode is `balance_performance`. For server systems dynamic EPP is mostly a no-op. Reviewed-by: Gautham R. Shenoy <gautham.shenoy@amd.com> Signed-off-by: Mario Limonciello (AMD) <superm1@kernel.org>
1 parent a362ae6 commit e30ca6d

4 files changed

Lines changed: 160 additions & 8 deletions

File tree

Documentation/admin-guide/pm/amd-pstate.rst

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ and user can change current preference according to energy or performance needs
325325
Please get all support profiles list from
326326
``energy_performance_available_preferences`` attribute, all the profiles are
327327
integer values defined between 0 to 255 when EPP feature is enabled by platform
328-
firmware, if EPP feature is disabled, driver will ignore the written value
328+
firmware, but if the dynamic EPP feature is enabled, driver will block writes.
329329
This attribute is read-write.
330330

331331
``boost``
@@ -347,6 +347,22 @@ boost or `1` to enable it, for the respective CPU using the sysfs path
347347
Other performance and frequency values can be read back from
348348
``/sys/devices/system/cpu/cpuX/acpi_cppc/``, see :ref:`cppc_sysfs`.
349349

350+
Dynamic energy performance profile
351+
==================================
352+
The amd-pstate driver supports dynamically selecting the energy performance
353+
profile based on whether the machine is running on AC or DC power.
354+
355+
Whether this behavior is enabled by default depends on the kernel
356+
config option `CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP`. This behavior can also be overridden
357+
at runtime by the sysfs file ``/sys/devices/system/cpu/cpufreq/policyX/dynamic_epp``.
358+
359+
When set to enabled, the driver will select a different energy performance
360+
profile when the machine is running on battery or AC power.
361+
When set to disabled, the driver will not change the energy performance profile
362+
based on the power source and will not react to user desired power state.
363+
364+
Attempting to manually write to the ``energy_performance_preference`` sysfs
365+
file will fail when ``dynamic_epp`` is enabled.
350366

351367
``amd-pstate`` vs ``acpi-cpufreq``
352368
======================================

drivers/cpufreq/Kconfig.x86

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,18 @@ config X86_AMD_PSTATE_DEFAULT_MODE
6868
For details, take a look at:
6969
<file:Documentation/admin-guide/pm/amd-pstate.rst>.
7070

71+
config X86_AMD_PSTATE_DYNAMIC_EPP
72+
bool "AMD Processor P-State dynamic EPP support"
73+
depends on X86_AMD_PSTATE
74+
default n
75+
help
76+
Allow the kernel to dynamically change the energy performance
77+
value from events like ACPI platform profile and AC adapter plug
78+
events.
79+
80+
This feature can also be changed at runtime, this configuration
81+
option only sets the kernel default value behavior.
82+
7183
config X86_AMD_PSTATE_UT
7284
tristate "selftest for AMD Processor P-State driver"
7385
depends on X86 && ACPI_PROCESSOR

drivers/cpufreq/amd-pstate.c

Lines changed: 122 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include <linux/io.h>
3737
#include <linux/delay.h>
3838
#include <linux/uaccess.h>
39+
#include <linux/power_supply.h>
3940
#include <linux/static_call.h>
4041
#include <linux/topology.h>
4142

@@ -86,6 +87,11 @@ static struct cpufreq_driver amd_pstate_driver;
8687
static struct cpufreq_driver amd_pstate_epp_driver;
8788
static int cppc_state = AMD_PSTATE_UNDEFINED;
8889
static bool amd_pstate_prefcore = true;
90+
#ifdef CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP
91+
static bool dynamic_epp = CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP;
92+
#else
93+
static bool dynamic_epp;
94+
#endif
8995
static struct quirk_entry *quirks;
9096

9197
/*
@@ -1155,6 +1161,73 @@ static void amd_pstate_cpu_exit(struct cpufreq_policy *policy)
11551161
kfree(cpudata);
11561162
}
11571163

1164+
static int amd_pstate_get_balanced_epp(struct cpufreq_policy *policy)
1165+
{
1166+
struct amd_cpudata *cpudata = policy->driver_data;
1167+
1168+
if (power_supply_is_system_supplied())
1169+
return cpudata->epp_default_ac;
1170+
else
1171+
return cpudata->epp_default_dc;
1172+
}
1173+
1174+
static int amd_pstate_power_supply_notifier(struct notifier_block *nb,
1175+
unsigned long event, void *data)
1176+
{
1177+
struct amd_cpudata *cpudata = container_of(nb, struct amd_cpudata, power_nb);
1178+
struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpudata->cpu);
1179+
u8 epp;
1180+
int ret;
1181+
1182+
if (event != PSY_EVENT_PROP_CHANGED)
1183+
return NOTIFY_OK;
1184+
1185+
epp = amd_pstate_get_balanced_epp(policy);
1186+
1187+
ret = amd_pstate_set_epp(policy, epp);
1188+
if (ret)
1189+
pr_warn("Failed to set CPU %d EPP %u: %d\n", cpudata->cpu, epp, ret);
1190+
1191+
return NOTIFY_OK;
1192+
}
1193+
static void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy)
1194+
{
1195+
struct amd_cpudata *cpudata = policy->driver_data;
1196+
1197+
if (cpudata->power_nb.notifier_call)
1198+
power_supply_unreg_notifier(&cpudata->power_nb);
1199+
cpudata->dynamic_epp = false;
1200+
}
1201+
1202+
static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
1203+
{
1204+
struct amd_cpudata *cpudata = policy->driver_data;
1205+
int ret;
1206+
u8 epp;
1207+
1208+
epp = amd_pstate_get_balanced_epp(policy);
1209+
ret = amd_pstate_set_epp(policy, epp);
1210+
if (ret)
1211+
return ret;
1212+
1213+
/* only enable notifier if things will actually change */
1214+
if (cpudata->epp_default_ac != cpudata->epp_default_dc) {
1215+
cpudata->power_nb.notifier_call = amd_pstate_power_supply_notifier;
1216+
ret = power_supply_reg_notifier(&cpudata->power_nb);
1217+
if (ret)
1218+
goto cleanup;
1219+
}
1220+
1221+
cpudata->dynamic_epp = true;
1222+
1223+
return 0;
1224+
1225+
cleanup:
1226+
amd_pstate_clear_dynamic_epp(policy);
1227+
1228+
return ret;
1229+
}
1230+
11581231
/* Sysfs attributes */
11591232

11601233
/*
@@ -1244,21 +1317,28 @@ static ssize_t store_energy_performance_preference(
12441317
ssize_t ret;
12451318
u8 epp;
12461319

1320+
if (cpudata->dynamic_epp) {
1321+
pr_debug("EPP cannot be set when dynamic EPP is enabled\n");
1322+
return -EBUSY;
1323+
}
1324+
12471325
ret = sysfs_match_string(energy_perf_strings, buf);
12481326
if (ret < 0)
12491327
return -EINVAL;
12501328

1251-
if (!ret)
1252-
epp = cpudata->epp_default;
1253-
else
1329+
if (ret)
12541330
epp = epp_values[ret];
1331+
else
1332+
epp = amd_pstate_get_balanced_epp(policy);
12551333

12561334
if (epp > 0 && policy->policy == CPUFREQ_POLICY_PERFORMANCE) {
12571335
pr_debug("EPP cannot be set under performance policy\n");
12581336
return -EBUSY;
12591337
}
12601338

12611339
ret = amd_pstate_set_epp(policy, epp);
1340+
if (ret)
1341+
return ret;
12621342

12631343
return ret ? ret : count;
12641344
}
@@ -1620,12 +1700,42 @@ static ssize_t prefcore_show(struct device *dev,
16201700
return sysfs_emit(buf, "%s\n", str_enabled_disabled(amd_pstate_prefcore));
16211701
}
16221702

1703+
static ssize_t dynamic_epp_show(struct device *dev,
1704+
struct device_attribute *attr, char *buf)
1705+
{
1706+
return sysfs_emit(buf, "%s\n", str_enabled_disabled(dynamic_epp));
1707+
}
1708+
1709+
static ssize_t dynamic_epp_store(struct device *a, struct device_attribute *b,
1710+
const char *buf, size_t count)
1711+
{
1712+
bool enabled;
1713+
int ret;
1714+
1715+
ret = kstrtobool(buf, &enabled);
1716+
if (ret)
1717+
return ret;
1718+
1719+
if (dynamic_epp == enabled)
1720+
return -EINVAL;
1721+
1722+
/* reinitialize with desired dynamic EPP value */
1723+
dynamic_epp = enabled;
1724+
ret = amd_pstate_change_driver_mode(cppc_state);
1725+
if (ret)
1726+
dynamic_epp = false;
1727+
1728+
return ret ? ret : count;
1729+
}
1730+
16231731
static DEVICE_ATTR_RW(status);
16241732
static DEVICE_ATTR_RO(prefcore);
1733+
static DEVICE_ATTR_RW(dynamic_epp);
16251734

16261735
static struct attribute *pstate_global_attributes[] = {
16271736
&dev_attr_status.attr,
16281737
&dev_attr_prefcore.attr,
1738+
&dev_attr_dynamic_epp.attr,
16291739
NULL
16301740
};
16311741

@@ -1715,13 +1825,17 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
17151825
if (amd_pstate_acpi_pm_profile_server() ||
17161826
amd_pstate_acpi_pm_profile_undefined()) {
17171827
policy->policy = CPUFREQ_POLICY_PERFORMANCE;
1718-
cpudata->epp_default = amd_pstate_get_epp(cpudata);
1828+
cpudata->epp_default_ac = cpudata->epp_default_dc = amd_pstate_get_epp(cpudata);
17191829
} else {
17201830
policy->policy = CPUFREQ_POLICY_POWERSAVE;
1721-
cpudata->epp_default = AMD_CPPC_EPP_BALANCE_PERFORMANCE;
1831+
cpudata->epp_default_ac = AMD_CPPC_EPP_PERFORMANCE;
1832+
cpudata->epp_default_dc = AMD_CPPC_EPP_BALANCE_PERFORMANCE;
17221833
}
17231834

1724-
ret = amd_pstate_set_epp(policy, cpudata->epp_default);
1835+
if (dynamic_epp)
1836+
ret = amd_pstate_set_dynamic_epp(policy);
1837+
else
1838+
ret = amd_pstate_set_epp(policy, amd_pstate_get_balanced_epp(policy));
17251839
if (ret)
17261840
goto free_cpudata1;
17271841

@@ -1753,6 +1867,8 @@ static void amd_pstate_epp_cpu_exit(struct cpufreq_policy *policy)
17531867
amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false);
17541868
amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf);
17551869

1870+
if (cpudata->dynamic_epp)
1871+
amd_pstate_clear_dynamic_epp(policy);
17561872
kfree(cpudata);
17571873
policy->driver_data = NULL;
17581874
}

drivers/cpufreq/amd-pstate.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,11 @@ struct amd_aperf_mperf {
8585
* AMD P-State driver supports preferred core featue.
8686
* @epp_cached: Cached CPPC energy-performance preference value
8787
* @policy: Cpufreq policy value
88+
* @suspended: If CPU core if offlined
89+
* @epp_default_ac: Default EPP value for AC power source
90+
* @epp_default_dc: Default EPP value for DC power source
91+
* @dynamic_epp: Whether dynamic EPP is enabled
92+
* @power_nb: Notifier block for power events
8893
*
8994
* The amd_cpudata is key private data for each CPU thread in AMD P-State, and
9095
* represents all the attributes and goals that AMD P-State requests at runtime.
@@ -118,7 +123,10 @@ struct amd_cpudata {
118123
/* EPP feature related attributes*/
119124
u32 policy;
120125
bool suspended;
121-
u8 epp_default;
126+
u8 epp_default_ac;
127+
u8 epp_default_dc;
128+
bool dynamic_epp;
129+
struct notifier_block power_nb;
122130
};
123131

124132
/*

0 commit comments

Comments
 (0)