Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

+160 -8
+17 -1
Documentation/admin-guide/pm/amd-pstate.rst
··· 325 325 Please get all support profiles list from 326 326 ``energy_performance_available_preferences`` attribute, all the profiles are 327 327 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. 329 329 This attribute is read-write. 330 330 331 331 ``boost`` ··· 347 347 Other performance and frequency values can be read back from 348 348 ``/sys/devices/system/cpu/cpuX/acpi_cppc/``, see :ref:`cppc_sysfs`. 349 349 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. 350 366 351 367 ``amd-pstate`` vs ``acpi-cpufreq`` 352 368 ======================================
+12
drivers/cpufreq/Kconfig.x86
··· 68 68 For details, take a look at: 69 69 <file:Documentation/admin-guide/pm/amd-pstate.rst>. 70 70 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 + 71 83 config X86_AMD_PSTATE_UT 72 84 tristate "selftest for AMD Processor P-State driver" 73 85 depends on X86 && ACPI_PROCESSOR
+122 -6
drivers/cpufreq/amd-pstate.c
··· 36 36 #include <linux/io.h> 37 37 #include <linux/delay.h> 38 38 #include <linux/uaccess.h> 39 + #include <linux/power_supply.h> 39 40 #include <linux/static_call.h> 40 41 #include <linux/topology.h> 41 42 ··· 87 86 static struct cpufreq_driver amd_pstate_epp_driver; 88 87 static int cppc_state = AMD_PSTATE_UNDEFINED; 89 88 static bool amd_pstate_prefcore = true; 89 + #ifdef CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP 90 + static bool dynamic_epp = CONFIG_X86_AMD_PSTATE_DYNAMIC_EPP; 91 + #else 92 + static bool dynamic_epp; 93 + #endif 90 94 static struct quirk_entry *quirks; 91 95 92 96 /* ··· 1161 1155 kfree(cpudata); 1162 1156 } 1163 1157 1158 + static int amd_pstate_get_balanced_epp(struct cpufreq_policy *policy) 1159 + { 1160 + struct amd_cpudata *cpudata = policy->driver_data; 1161 + 1162 + if (power_supply_is_system_supplied()) 1163 + return cpudata->epp_default_ac; 1164 + else 1165 + return cpudata->epp_default_dc; 1166 + } 1167 + 1168 + static int amd_pstate_power_supply_notifier(struct notifier_block *nb, 1169 + unsigned long event, void *data) 1170 + { 1171 + struct amd_cpudata *cpudata = container_of(nb, struct amd_cpudata, power_nb); 1172 + struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpudata->cpu); 1173 + u8 epp; 1174 + int ret; 1175 + 1176 + if (event != PSY_EVENT_PROP_CHANGED) 1177 + return NOTIFY_OK; 1178 + 1179 + epp = amd_pstate_get_balanced_epp(policy); 1180 + 1181 + ret = amd_pstate_set_epp(policy, epp); 1182 + if (ret) 1183 + pr_warn("Failed to set CPU %d EPP %u: %d\n", cpudata->cpu, epp, ret); 1184 + 1185 + return NOTIFY_OK; 1186 + } 1187 + static void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy) 1188 + { 1189 + struct amd_cpudata *cpudata = policy->driver_data; 1190 + 1191 + if (cpudata->power_nb.notifier_call) 1192 + power_supply_unreg_notifier(&cpudata->power_nb); 1193 + cpudata->dynamic_epp = false; 1194 + } 1195 + 1196 + static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy) 1197 + { 1198 + struct amd_cpudata *cpudata = policy->driver_data; 1199 + int ret; 1200 + u8 epp; 1201 + 1202 + epp = amd_pstate_get_balanced_epp(policy); 1203 + ret = amd_pstate_set_epp(policy, epp); 1204 + if (ret) 1205 + return ret; 1206 + 1207 + /* only enable notifier if things will actually change */ 1208 + if (cpudata->epp_default_ac != cpudata->epp_default_dc) { 1209 + cpudata->power_nb.notifier_call = amd_pstate_power_supply_notifier; 1210 + ret = power_supply_reg_notifier(&cpudata->power_nb); 1211 + if (ret) 1212 + goto cleanup; 1213 + } 1214 + 1215 + cpudata->dynamic_epp = true; 1216 + 1217 + return 0; 1218 + 1219 + cleanup: 1220 + amd_pstate_clear_dynamic_epp(policy); 1221 + 1222 + return ret; 1223 + } 1224 + 1164 1225 /* Sysfs attributes */ 1165 1226 1166 1227 /* ··· 1317 1244 ssize_t ret; 1318 1245 u8 epp; 1319 1246 1247 + if (cpudata->dynamic_epp) { 1248 + pr_debug("EPP cannot be set when dynamic EPP is enabled\n"); 1249 + return -EBUSY; 1250 + } 1251 + 1320 1252 ret = sysfs_match_string(energy_perf_strings, buf); 1321 1253 if (ret < 0) 1322 1254 return -EINVAL; 1323 1255 1324 - if (!ret) 1325 - epp = cpudata->epp_default; 1326 - else 1256 + if (ret) 1327 1257 epp = epp_values[ret]; 1258 + else 1259 + epp = amd_pstate_get_balanced_epp(policy); 1328 1260 1329 1261 if (epp > 0 && policy->policy == CPUFREQ_POLICY_PERFORMANCE) { 1330 1262 pr_debug("EPP cannot be set under performance policy\n"); ··· 1337 1259 } 1338 1260 1339 1261 ret = amd_pstate_set_epp(policy, epp); 1262 + if (ret) 1263 + return ret; 1340 1264 1341 1265 return ret ? ret : count; 1342 1266 } ··· 1700 1620 return sysfs_emit(buf, "%s\n", str_enabled_disabled(amd_pstate_prefcore)); 1701 1621 } 1702 1622 1623 + static ssize_t dynamic_epp_show(struct device *dev, 1624 + struct device_attribute *attr, char *buf) 1625 + { 1626 + return sysfs_emit(buf, "%s\n", str_enabled_disabled(dynamic_epp)); 1627 + } 1628 + 1629 + static ssize_t dynamic_epp_store(struct device *a, struct device_attribute *b, 1630 + const char *buf, size_t count) 1631 + { 1632 + bool enabled; 1633 + int ret; 1634 + 1635 + ret = kstrtobool(buf, &enabled); 1636 + if (ret) 1637 + return ret; 1638 + 1639 + if (dynamic_epp == enabled) 1640 + return -EINVAL; 1641 + 1642 + /* reinitialize with desired dynamic EPP value */ 1643 + dynamic_epp = enabled; 1644 + ret = amd_pstate_change_driver_mode(cppc_state); 1645 + if (ret) 1646 + dynamic_epp = false; 1647 + 1648 + return ret ? ret : count; 1649 + } 1650 + 1703 1651 static DEVICE_ATTR_RW(status); 1704 1652 static DEVICE_ATTR_RO(prefcore); 1653 + static DEVICE_ATTR_RW(dynamic_epp); 1705 1654 1706 1655 static struct attribute *pstate_global_attributes[] = { 1707 1656 &dev_attr_status.attr, 1708 1657 &dev_attr_prefcore.attr, 1658 + &dev_attr_dynamic_epp.attr, 1709 1659 NULL 1710 1660 }; 1711 1661 ··· 1825 1715 if (amd_pstate_acpi_pm_profile_server() || 1826 1716 amd_pstate_acpi_pm_profile_undefined()) { 1827 1717 policy->policy = CPUFREQ_POLICY_PERFORMANCE; 1828 - cpudata->epp_default = amd_pstate_get_epp(cpudata); 1718 + cpudata->epp_default_ac = cpudata->epp_default_dc = amd_pstate_get_epp(cpudata); 1829 1719 } else { 1830 1720 policy->policy = CPUFREQ_POLICY_POWERSAVE; 1831 - cpudata->epp_default = AMD_CPPC_EPP_BALANCE_PERFORMANCE; 1721 + cpudata->epp_default_ac = AMD_CPPC_EPP_PERFORMANCE; 1722 + cpudata->epp_default_dc = AMD_CPPC_EPP_BALANCE_PERFORMANCE; 1832 1723 } 1833 1724 1834 - ret = amd_pstate_set_epp(policy, cpudata->epp_default); 1725 + if (dynamic_epp) 1726 + ret = amd_pstate_set_dynamic_epp(policy); 1727 + else 1728 + ret = amd_pstate_set_epp(policy, amd_pstate_get_balanced_epp(policy)); 1835 1729 if (ret) 1836 1730 goto free_cpudata1; 1837 1731 ··· 1867 1753 amd_pstate_update_perf(policy, perf.bios_min_perf, 0U, 0U, 0U, false); 1868 1754 amd_pstate_set_floor_perf(policy, cpudata->bios_floor_perf); 1869 1755 1756 + if (cpudata->dynamic_epp) 1757 + amd_pstate_clear_dynamic_epp(policy); 1870 1758 kfree(cpudata); 1871 1759 policy->driver_data = NULL; 1872 1760 }
+9 -1
drivers/cpufreq/amd-pstate.h
··· 85 85 * AMD P-State driver supports preferred core featue. 86 86 * @epp_cached: Cached CPPC energy-performance preference value 87 87 * @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 88 93 * 89 94 * The amd_cpudata is key private data for each CPU thread in AMD P-State, and 90 95 * represents all the attributes and goals that AMD P-State requests at runtime. ··· 123 118 /* EPP feature related attributes*/ 124 119 u32 policy; 125 120 bool suspended; 126 - u8 epp_default; 121 + u8 epp_default_ac; 122 + u8 epp_default_dc; 123 + bool dynamic_epp; 124 + struct notifier_block power_nb; 127 125 }; 128 126 129 127 /*