diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-18 08:47:09 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-18 08:47:09 -0700 |
| commit | a5778046a02570fd23bdf0b48dce330237d1996c (patch) | |
| tree | 4c9d59146cd9a292b63777f803cee958a05e3f59 | |
| parent | 0d508f1745b57272d4d4a3d50dc35aa2a3682238 (diff) | |
| parent | 7544a367adc1ce5e9cc62315b4c32dc72b509451 (diff) | |
Merge tag 'pm-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management updates from Rafael Wysocki:
"As has been the case for quite some time, this set of changes is
dominated by cpufreq updates including intel-pstate and amd-pstate
driver updates, minor fixes and cleanups of other assorted cpufreq
drivers, schedutil governor updates, fixes of the Rust bindings, new
hardware support (IPQ5210 in qcom-nvmem), and some updates of self
tests related to cpufreq.
The second largest group of changes are cpuidle updates consisting of
intel_idle driver updates and ACPI processor idle driver updates, both
mostly related to ACPI _LPI support.
There are also updates related to system sleep, mostly in the
hibernation core code, two operating performance points (OPP) updates,
one runtime PM framework update, one power capping update, and some
tools updates including the addition of ACPI CPPC support to cpupower.
Specifics:
- Minor fixes and cleanups in assorted cpufreq drivers (Dan
Carpenter, Guru Das Srinagesh, Haoxiang Li, Karl Mehltretter, Sasha
Finkelstein, and Pan Chuang)
- Fix cpufreq table creation and bios_limits() callback in the Rust
bindings (Priya Bala Govindasamy)
- Add IPQ5210 support to qcom-nvmem driver (Varadarajan Narayanan)
- Adjust the .adjust_perf() cpufreq driver callback to allow the
maximum performance value to be passed to drivers and update the
intel_pstate driver to use it (Rafael Wysocki)
- Set policy->cur to the actual requested frequency in the
intel_pstate driver when the performance policy is used (Rafael
Wysocki)
- Simplify HWP handling on Broadwell processors in intel_pstate
(Rafael Wysocki)
- Fix setting minimum P-state at init time in intel_pstate (Rafael
Wysocki)
- Consolidate frequency values computation in intel_pstate and clean
up code in that driver (Rafael Wysocki)
- Add missing kernel-doc descriptions for structure and union members
in the amd-pstate driver (David Vernet)
- Handle missing policy in dynamic EPP callbacks in the amd-pstate
driver (EDAMAMEX)
- Introduce EXPORT_SYMBOL_FOR_PSTATE_UT() to export amd-pstate driver
symbols to the amd-pstate-ut subdriver (K Prateek Nayak)
- Add dynamic EPP as an "energy_performance_preference" mode in
amd-pstate, remove the "amd_dynamic_epp" kernel command line option
and the "dynamic_epp" sysfs attribute, and update the dynamic_epp
documentation accordingly (K Prateek Nayak)
- Add unit tests for CPPC Performance Priority and the "dynamic" EPP
mode in the amd-pstate driver (K Prateek Nayak)
- Set min_limit_freq based on bios_min_perf in amd-pstate and remove
the defensive check for bios_min_perf from it (K Prateek Nayak)
- Fix EPP return type and handle errors in amd-pstate during
initialization, toggle auto_sel in active mode on shared memory
systems, and cache the firmware programmed EPP value (Marco
Scardovi)
- Skip tests in amd-pstate-ut if the amd-pstate driver is not in
active use (Qianheng Peng)
- Replace sprintf() with sysfs_emit() in sysfs show in the cpufreq
schedutil governor and fix a self-contradictory comment in
sugov_iowait_apply() (Zhongqiu Han)
- Fix the usage example for the sampling_rate tunable of the ondemand
cpufreq governor in admin-guide (wangxiaodong)
- Avoid using deep idle states during initialization in the
intel_idle driver to work around device handling issues (Rafael
Wysocki)
- Fix and refactor the ACPI processor driver code related to ACPI
_LPI support and add ACPI _LPI support to intel_idle based on that
ACPI processor driver update (Rafael Wysocki)
- Backup and restore governor for cpufreq sptests (Yiwei Lin)
- Remove unnecessary sudo from quick_shuffle() and remove unused
local variables from switch_show_governor() in cpufreq selftests
(Jinseok Kim)
- Rename the PM core module parameter prefix to "pm" and allow the PM
transition (DPM) watchdog to be disabled by default (Tzung-Bi Shih)
- Fix off-by-one in wakelocks number limit check in the system sleep
sysfs interface (Haowen Tu)
- Remove kernel-doc markings from helper descriptions in the core
hibernation code (Adi Nata)
- Use %pe to print error pointer values in the hibernation core
(Ronan Marchal)
- Fix memory leak in snapshot_write_next() error path (Malaya Kumar
Rout)
- Delay allocating and linking the next swap_map_page in the
hibernation image saving code until another image page actually
needs to be recorded (Haesung Kim)
- Fix cleanup ordering around scope-based pointers in OPP (Gregor
Herburger).
- Use clk_get_optional() for optional clocks in OPP (Praveen Talari).
- Stop setting runtime_error on runtime resume callback failures to
allow drivers to recover from resume issues (Praveen Talari)
- Handle PMU registration failure during probe in the intel_rapl_tpmi
driver (Sumeet Pawnikar)
- Avoid optional imports in intel_pstate_tracer unless they are
really needed (Yousef Alhouseen)
- Add generic CPPC performance display to the cpupower utility, build
and call CPPC information on non-AMD processors, make cpupower
print kernel and hardware frequency information, and add libm to
cpupower for generic CPPC view (Jeremy Linton)
- Remove conditional return with no effect from cpupower (Sang-Heon
Jeon)"
* tag 'pm-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm: (76 commits)
cpufreq: imx6q: fix out-of-bounds write when probed more than once
cpufreq: imx6q: fix devres accumulation across driver rebind
rust: cpufreq: Fix temporary write in Registration::bios_limit_callback
rust: cpufreq: Add CPUFREQ_TABLE_END as last table entry in TableBuilder::to_table
opp: Use clk_get_optional() to avoid leaving opp_table->clk as an error pointer
intel_idle: Avoid using deep idle states during initialization
cpupower: remove conditional return with no effect
cpufreq: intel_pstate: Adjust policy->cur in active mode to policy
cpufreq/amd-pstate: Document missing kernel-doc members
cpufreq/amd-pstate-ut: Add unit test for CPPC Performance Priority
cpufreq/amd-pstate-ut: Add unit test for "dynamic" EPP mode
cpufreq/amd-pstate: Reduce the scope of exported symbols
Documentation/amd-pstate: Update dynamic_epp documentation with new behavior
cpufreq/amd-pstate: Remove "amd_dynamic_epp" cmdline and "dynamic_epp" sysfs
cpufreq/amd-pstate: Add dynamic EPP as an "energy_performance_preference" mode
cpufreq/amd-pstate: Extract platform profile to EPP conversion into a helper
cpufreq/amd-pstate: Remove the defensive check for bios_min_perf
cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf
powercap: intel_rapl_tpmi: Handle PMU registration failure during probe
PM: sleep: Allow disabling DPM watchdog by default
...
39 files changed, 1274 insertions, 737 deletions
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index e8ff36982d97..c6ce3ccf5e04 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -47,6 +47,7 @@ PCI PCI bus support is enabled. PCIE PCI Express support is enabled. PCMCIA The PCMCIA subsystem is enabled. + PM Power Management support is enabled. PNP Plug & Play support is enabled. PPC PowerPC architecture is enabled. PPT Parallel port support is enabled. @@ -5354,6 +5355,12 @@ Kernel parameters pm_debug_messages [SUSPEND,KNL] Enable suspend/resume debug messages during boot up. + pm.dpm_watchdog_enabled= + [PM] Enable or disable the DPM watchdog. Requires + CONFIG_PM_SLEEP and CONFIG_DPM_WATCHDOG enabled. + Format: <bool> + Default value is set by CONFIG_DPM_WATCHDOG_ENABLED. + pnp.debug=1 [PNP] Enable PNP debug messages (depends on the CONFIG_PNP_DEBUG_MESSAGES option). Change at run-time diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst index a95e2ebce005..c7bf7cc2db87 100644 --- a/Documentation/admin-guide/pm/amd-pstate.rst +++ b/Documentation/admin-guide/pm/amd-pstate.rst @@ -317,7 +317,9 @@ These profiles represent different hints that are provided to the low-level firmware about the user's desired energy vs efficiency tradeoff. ``default`` represents the epp value is set by platform firmware. ``custom`` designates that integer values 0-255 may be written -as well. This attribute is read-only. +as well. ``dynamic`` designates that the EPP is modified dynamically +on kernel events. See ``Dynamic energy performance profile`` section below to +know more about the ``dynamic`` mode. This attribute is read-only. ``energy_performance_preference`` @@ -326,13 +328,11 @@ and user can change current preference according to energy or performance needs Coarse named profiles are available in the attribute ``energy_performance_available_preferences``. Users can also write individual integer values between 0 to 255. -When dynamic EPP is enabled, writes to energy_performance_preference are blocked -even when EPP feature is enabled by platform firmware. Lower epp values shift the bias -towards improved performance while a higher epp value shifts the bias towards -power-savings. The exact impact can change from one platform to the other. -If a valid integer was last written, then a number will be returned on future reads. -If a valid string was last written then a string will be returned on future reads. -This attribute is read-write. +Lower epp values shift the bias towards improved performance while a higher epp +value shifts the bias towards power-savings. The exact impact can change from +one platform to the other. If a valid integer was last written, then a number +will be returned on future reads. If a valid string was last written then a +string will be returned on future reads. This attribute is read-write. ``boost`` The `boost` sysfs attribute provides control over the CPU core @@ -356,21 +356,20 @@ Other performance and frequency values can be read back from Dynamic energy performance profile ================================== The amd-pstate driver supports dynamically selecting the energy performance -profile based on whether the machine is running on AC or DC power. - -Whether this behavior is enabled by default depends on the kernel command line option -``amd_dynamic_epp`` is set. This behavior can also be overridden -at runtime by the sysfs file ``/sys/devices/system/cpu/amd_pstate/dynamic_epp``. - -When set to enabled, the driver will select a different energy performance -profile when the machine is running on battery or AC power. The driver will -also register with the platform profile handler to receive notifications of -user desired power state and react to those. -When set to disabled, the driver will not change the energy performance profile -based on the power source and will not react to user desired power state. - -Attempting to manually write to the ``energy_performance_preference`` sysfs -file will fail when ``dynamic_epp`` is enabled. +profile based on the system profile and the current power source in active mode. + +The ``dynamic`` mode is listed in +``/sys/devices/system/cpu/cpuX/cpufreq/energy_performance_available_preferences`` +when available while running under the ``powersave`` governor. The ``dynamic`` +mode can be toggled on by writing the same to the sysfs file +``/sys/devices/system/cpu/cpuX/cpufreq/energy_performance_preference`` when +available. + +When ``energy_performance_preference`` is set to ``dynamic``, the driver will +select a different energy performance profile when the machine is running on +battery or AC power. The driver will also register with the platform profile +handler to receive notifications of user desired power state and react to +those. ``amd-pstate`` vs ``acpi-cpufreq`` ====================================== diff --git a/Documentation/admin-guide/pm/cpufreq.rst b/Documentation/admin-guide/pm/cpufreq.rst index 8831cface585..34baf20cc202 100644 --- a/Documentation/admin-guide/pm/cpufreq.rst +++ b/Documentation/admin-guide/pm/cpufreq.rst @@ -497,7 +497,7 @@ This governor exposes the following tunables: represented by it to be 1.5 times as high as the transition latency (the default):: - # echo `$(($(cat cpuinfo_transition_latency) * 3 / 2))` > ondemand/sampling_rate + # echo $(($(cat cpuinfo_transition_latency) * 3 / 2)) > ondemand/sampling_rate ``up_threshold`` If the estimated CPU load is above this value (in percent), the governor diff --git a/Documentation/admin-guide/pm/intel_idle.rst b/Documentation/admin-guide/pm/intel_idle.rst index 188d52cd26e8..996e5bd83c42 100644 --- a/Documentation/admin-guide/pm/intel_idle.rst +++ b/Documentation/admin-guide/pm/intel_idle.rst @@ -87,17 +87,22 @@ tables with any processor model recognized by it; see `below <intel-idle-parameters_>`_.] If the ACPI tables are going to be used for building the list of available idle -states, ``intel_idle`` first looks for a ``_CST`` object under one of the ACPI -objects corresponding to the CPUs in the system (refer to the ACPI specification -[2]_ for the description of ``_CST`` and its output package). Because the -``CPUIdle`` subsystem expects that the list of idle states supplied by the -driver will be suitable for all of the CPUs handled by it and ``intel_idle`` is -registered as the ``CPUIdle`` driver for all of the CPUs in the system, the -driver looks for the first ``_CST`` object returning at least one valid idle -state description and such that all of the idle states included in its return -package are of the FFH (Functional Fixed Hardware) type, which means that the -``MWAIT`` instruction is expected to be used to tell the processor that it can -enter one of them. The return package of that ``_CST`` is then assumed to be +states, ``intel_idle`` will be looking for ``_LPI`` or ``_CST`` objects in them +(refer to the ACPI specification [2]_ for the definitions of the ``_LPI`` and +``_CST`` objects). If ``_LPI`` is present under at least one of the ACPI +objects representing the CPUs in the system and ``_LPI`` processing produces a +non-empty list of valid idle states, it will be used. Otherwise, ``_CST`` will +be used so long as it is present under at least one of the ACPI objects +representing the CPUs in the system and it returns a non-empty list of valid +idle states. In either case, since the ``CPUIdle`` subsystem expects that the +list of idle states supplied by the driver will be suitable for all of the CPUs +handled by it and ``intel_idle`` is registered as the ``CPUIdle`` driver for all +of the CPUs in the system, ``intel_idle`` looks for the first CPU where the +ACPI-supplied list of idle states (coming from either ``_LPI`` or ``_CST``) +is not empty. Moreover, all of the states in that list need to be of the FFH +(Functional Fixed Hardware) type, which means that the ``MWAIT`` instruction is +expected to be used to tell the processor that the given idle state may be +entered. If that expectation is met, the list of idle states is assumed to be applicable to all of the other CPUs in the system and the idle state descriptions extracted from it are stored in a preliminary list of idle states coming from the ACPI tables. [This step is skipped if ``intel_idle`` is @@ -129,18 +134,21 @@ If the given processor model is not recognized by ``intel_idle``, but it supports ``MWAIT``, the preliminary list of idle states coming from the ACPI tables is used for building the final list that will be supplied to the ``CPUIdle`` core during driver registration. For each idle state in that list, -the description, ``MWAIT`` hint and exit latency are copied to the corresponding -entry in the final list of idle states. The name of the idle state represented -by it (to be returned by the ``name`` idle state attribute in ``sysfs``) is -"CX_ACPI", where X is the index of that idle state in the final list (note that -the minimum value of X is 1, because 0 is reserved for the "polling" state), and -its target residency is based on the exit latency value. Specifically, for -C1-type idle states the exit latency value is also used as the target residency -(for compatibility with the majority of the "internal" tables of idle states for -various processor models recognized by ``intel_idle``) and for the other idle -state types (C2 and C3) the target residency value is 3 times the exit latency -(again, that is because it reflects the target residency to exit latency ratio -in the majority of cases for the processor models recognized by ``intel_idle``). +the description, ``MWAIT`` hint and exit (wake) latency are copied to the +corresponding entry in the final list of idle states. If the preliminary list +of idle states has been obtained through ``_LPI`` processing, the minimum +residency parameter of the given idle state is taken as its target residency. +Otherwise, for C1-type idle states, the exit latency value is also used as the +target residency (for compatibility with the majority of the "internal" tables +of idle states for various processor models recognized by ``intel_idle``), and +for the other idle state types (C2 and C3) the target residency value is 3 times +the exit latency (again, that is because it reflects the target residency to +exit latency ratio in the majority of cases for the processor models recognized +by ``intel_idle``). The name of the idle state (to be returned by the ``name`` +idle state attribute in ``sysfs``) is either "Cx_LPI" (if it comes from ``_LPI`` +processing) or "Cx_ACPI", where x is the index of that idle state in the final +list (note that the minimum value of x is 1, because 0 is reserved for the +"polling" state), and its target residency is based on the exit latency value. All of the idle states in the final list are enabled by default in this case. diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c index 00775b91bd41..5fbab54171b7 100644 --- a/drivers/acpi/acpi_processor.c +++ b/drivers/acpi/acpi_processor.c @@ -994,3 +994,357 @@ end: } EXPORT_SYMBOL_NS_GPL(acpi_processor_evaluate_cst, "ACPI_PROCESSOR_IDLE"); #endif /* CONFIG_ACPI_PROCESSOR_CSTATE */ + +#ifdef CONFIG_ACPI_PROCESSOR_IDLE +struct acpi_lpi_states_array { + unsigned int size; + unsigned int composite_states_size; + struct acpi_lpi_state *entries; + struct acpi_lpi_state *composite_states[ACPI_PROCESSOR_MAX_POWER]; +}; + +static int obj_get_integer(union acpi_object *obj, u32 *value) +{ + if (obj->type != ACPI_TYPE_INTEGER) + return -EINVAL; + + *value = obj->integer.value; + return 0; +} + +#define lpi_state_debug(handle, message, state_idx) \ + acpi_handle_debug(handle, message " for _LPI state %u\n", state_idx) + +static void process_lpi_state_package(union acpi_object *lpi_pkg, + struct acpi_lpi_state *lpi_state, + acpi_handle handle, + unsigned int state_idx, bool strict) +{ + union acpi_object *lpi_pkg_elem, *obj; + + if (lpi_pkg->type != ACPI_TYPE_PACKAGE || lpi_pkg->package.count < 7) + return; + + lpi_pkg_elem = lpi_pkg->package.elements; + + /* Get the entry method first and skip the state if that fails. */ + obj = &lpi_pkg_elem[6]; + if (obj->type == ACPI_TYPE_BUFFER) { + struct acpi_power_register *reg; + + if (obj->buffer.length < sizeof(*reg)) { + lpi_state_debug(handle, "Invalid register data", state_idx); + return; + } + + reg = (struct acpi_power_register *)obj->buffer.pointer; + if (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) { + lpi_state_debug(handle, "Unsupported entry method", state_idx); + return; + } + + lpi_state->entry_method = ACPI_CSTATE_FFH; + lpi_state->address = reg->address; + } else if (obj->type == ACPI_TYPE_INTEGER) { + lpi_state->entry_method = ACPI_CSTATE_INTEGER; + lpi_state->address = obj->integer.value; + } else { + lpi_state_debug(handle, "Invalid entry method", state_idx); + return; + } + + if (obj_get_integer(&lpi_pkg_elem[0], &lpi_state->min_residency)) { + if (strict) { + lpi_state_debug(handle, "No min. residency", state_idx); + return; + } + + lpi_state_debug(handle, "Assuming 10 us min. residency", state_idx); + lpi_state->min_residency = 10; + } + + if (obj_get_integer(&lpi_pkg_elem[1], &lpi_state->wake_latency)) { + if (strict) { + lpi_state_debug(handle, "No wake latency", state_idx); + return; + } + + lpi_state_debug(handle, "Assuming 10 us wake latency", state_idx); + lpi_state->wake_latency = 10; + } + + if (obj_get_integer(&lpi_pkg_elem[2], &lpi_state->flags)) + lpi_state->flags = 0; + + if (obj_get_integer(&lpi_pkg_elem[3], &lpi_state->arch_flags)) + lpi_state->arch_flags = 0; + + if (obj_get_integer(&lpi_pkg_elem[4], &lpi_state->res_cnt_freq)) + lpi_state->res_cnt_freq = 1; + + if (obj_get_integer(&lpi_pkg_elem[5], &lpi_state->enable_parent_state)) + lpi_state->enable_parent_state = 0; + + /* Skip elements [7-8] i.e. Residency/Usage counters. */ + + /* + * Avoid out-of-bounds access if the size of the package is less than + * expected. + */ + if (lpi_pkg->package.count < 10) + return; + + obj = &lpi_pkg_elem[9]; + if (obj->type == ACPI_TYPE_STRING) + strscpy(lpi_state->desc, obj->string.pointer, ACPI_CX_DESC_LEN); +} + +static int acpi_processor_evaluate_lpi(acpi_handle handle, + struct acpi_lpi_states_array *info, + bool strict) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *lpi_data, *lpi_pkg; + unsigned int lpi_pkg_count, state_idx; + struct acpi_lpi_state *lpi_state; + acpi_status status; + int ret = 0; + + status = acpi_evaluate_object(handle, "_LPI", NULL, &buffer); + if (ACPI_FAILURE(status)) { + acpi_handle_debug(handle, "No _LPI, giving up\n"); + return -ENODEV; + } + + lpi_data = buffer.pointer; + + /* There must be at least 4 elements = 3 elements + 1 package */ + if (!lpi_data || lpi_data->type != ACPI_TYPE_PACKAGE || + lpi_data->package.count < 4) { + acpi_handle_debug(handle, "Not enough elements in _LPI\n"); + ret = -ENODATA; + goto end; + } + + lpi_pkg_count = lpi_data->package.elements[2].integer.value; + + /* Validate number of power states. */ + if (!lpi_pkg_count || lpi_pkg_count != lpi_data->package.count - 3) { + acpi_handle_debug(handle, "Invalid _LPI state count\n"); + ret = -ENODATA; + goto end; + } + + lpi_state = kzalloc_objs(*lpi_state, lpi_pkg_count); + if (!lpi_state) { + ret = -ENOMEM; + goto end; + } + + info->size = lpi_pkg_count; + info->entries = lpi_state; + + /* _LPI State packages start at index 3. */ + lpi_pkg = &lpi_data->package.elements[3]; + + for (state_idx = 1; state_idx <= lpi_pkg_count; state_idx++) { + lpi_state->index = state_idx; + process_lpi_state_package(lpi_pkg++, lpi_state++, handle, + state_idx, strict); + } + + acpi_handle_debug(handle, "Found %u power states\n", lpi_pkg_count); +end: + kfree(buffer.pointer); + return ret; +} + +/** + * combine_lpi_states - combine local and parent LPI states to form a composite LPI state + * + * @local: local LPI state + * @parent: parent LPI state + * @result: composite LPI state + */ +static bool combine_lpi_states(struct acpi_lpi_state *local, + struct acpi_lpi_state *parent, + struct acpi_lpi_state *result) +{ + if (parent->entry_method == ACPI_CSTATE_INTEGER) { + if (!parent->address) /* 0 means autopromotable */ + return false; + result->address = local->address + parent->address; + } else { + result->address = parent->address; + } + + result->min_residency = max(local->min_residency, parent->min_residency); + result->wake_latency = local->wake_latency + parent->wake_latency; + result->enable_parent_state = parent->enable_parent_state; + result->entry_method = local->entry_method; + + result->flags = parent->flags; + result->arch_flags = parent->arch_flags; + result->index = parent->index; + + scnprintf(result->desc, ACPI_CX_DESC_LEN, "%s+%s", local->desc, parent->desc); + return true; +} + +#define ACPI_LPI_STATE_FLAGS_ENABLED BIT(0) + +static void stash_composite_state(struct acpi_lpi_states_array *curr_level, + struct acpi_lpi_state *t) +{ + curr_level->composite_states[curr_level->composite_states_size++] = t; +} + +static bool too_many_states(acpi_handle handle, unsigned int state_count) +{ + if (state_count < ACPI_PROCESSOR_MAX_POWER) + return false; + + acpi_handle_info(handle, "No space for more _LPI states than %d\n", + ACPI_PROCESSOR_MAX_POWER); + return true; +} + +static unsigned int flatten_lpi_states(acpi_handle handle, + struct acpi_lpi_state *lpi_states, + unsigned int state_count, + struct acpi_lpi_states_array *curr, + struct acpi_lpi_states_array *prev) +{ + struct acpi_lpi_state *parent_lpi = curr->entries; + unsigned int j; + + /* + * Combine each of the "raw" _LPI states from the current (processor + * container) level with all of the composite _LPI states from the + * previous (processor or processor container) level. + */ + for (j = 0; j < curr->size; j++, parent_lpi++) { + struct acpi_lpi_state *flpi; + int i; + + if (!(parent_lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) + continue; + + if (too_many_states(handle, state_count)) + break; + + flpi = &lpi_states[state_count]; + + for (i = 0; i < prev->composite_states_size; i++) { + struct acpi_lpi_state *local_lpi = prev->composite_states[i]; + + if (parent_lpi->index > local_lpi->enable_parent_state) + continue; + + if (!combine_lpi_states(local_lpi, parent_lpi, flpi)) + continue; + + stash_composite_state(curr, flpi); + state_count++; + flpi++; + + if (state_count >= ACPI_PROCESSOR_MAX_POWER) + break; + } + } + + return state_count; +} + +int acpi_processor_extract_lpi_info(acpi_handle pr_handle, + struct acpi_processor_power *pr_power, + bool strict) +{ + struct acpi_lpi_states_array info[2], *prev, *curr; + acpi_handle handle = pr_handle; + unsigned int state_count = 0; + unsigned int i; + int ret; + + if (!osc_pc_lpi_support_confirmed) + return -EOPNOTSUPP; + + curr = &info[0]; + curr->composite_states_size = 0; + + ret = acpi_processor_evaluate_lpi(handle, curr, strict); + if (ret) + return ret; + + /* Copy all of the usable first-level states to power.lpi_states[]. */ + for (i = 0; i < curr->size; i++) { + struct acpi_lpi_state *lpi = &curr->entries[i]; + struct acpi_lpi_state *flpi; + + /* + * Skip states that are not enabled or have an inadequate entry + * method for this level. + */ + if (!(lpi->flags & ACPI_LPI_STATE_FLAGS_ENABLED) || + lpi->entry_method == ACPI_CSTATE_INTEGER) + continue; + + if (too_many_states(pr_handle, state_count)) + break; + + flpi = &pr_power->lpi_states[state_count++]; + memcpy(flpi, lpi, sizeof(*lpi)); + stash_composite_state(curr, flpi); + } + + kfree(curr->entries); + + /* + * If there are no _LPI states at the first level, there are no _LPI + * states at all. + */ + if (!state_count) + return -ENODATA; + + prev = curr; + curr = &info[1]; + + for (;;) { + struct acpi_lpi_states_array *tmp; + struct acpi_device *d; + + if (ACPI_FAILURE(acpi_get_parent(handle, &handle))) + break; + + d = acpi_fetch_acpi_dev(handle); + if (!d) + break; + + if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) + break; + + curr->composite_states_size = 0; + + ret = acpi_processor_evaluate_lpi(handle, curr, strict); + if (ret) + break; + + /* flatten all the LPI states in this level of hierarchy */ + state_count = flatten_lpi_states(pr_handle, pr_power->lpi_states, + state_count, curr, prev); + + kfree(curr->entries); + + tmp = prev, prev = curr, curr = tmp; + } + + /* reset the index after flattening */ + for (i = 0; i < state_count; i++) + pr_power->lpi_states[i].index = i; + + pr_power->count = state_count; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(acpi_processor_extract_lpi_info, "ACPI_PROCESSOR_IDLE"); +#endif /* CONFIG_ACPI_PROCESSOR_IDLE */ diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 4482cf28f56a..e113bcbbb882 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -853,223 +853,6 @@ static int acpi_processor_setup_cstates(struct acpi_processor *pr) #endif /* CONFIG_ACPI_PROCESSOR_CSTATE */ -struct acpi_lpi_states_array { - unsigned int size; - unsigned int composite_states_size; - struct acpi_lpi_state *entries; - struct acpi_lpi_state *composite_states[ACPI_PROCESSOR_MAX_POWER]; -}; - -static int obj_get_integer(union acpi_object *obj, u32 *value) -{ - if (obj->type != ACPI_TYPE_INTEGER) - return -EINVAL; - - *value = obj->integer.value; - return 0; -} - -static int acpi_processor_evaluate_lpi(acpi_handle handle, - struct acpi_lpi_states_array *info) -{ - acpi_status status; - int ret = 0; - int pkg_count, state_idx = 1, loop; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *lpi_data; - struct acpi_lpi_state *lpi_state; - - status = acpi_evaluate_object(handle, "_LPI", NULL, &buffer); - if (ACPI_FAILURE(status)) { - acpi_handle_debug(handle, "No _LPI, giving up\n"); - return -ENODEV; - } - - lpi_data = buffer.pointer; - - /* There must be at least 4 elements = 3 elements + 1 package */ - if (!lpi_data || lpi_data->type != ACPI_TYPE_PACKAGE || - lpi_data->package.count < 4) { - pr_debug("not enough elements in _LPI\n"); - ret = -ENODATA; - goto end; - } - - pkg_count = lpi_data->package.elements[2].integer.value; - - /* Validate number of power states. */ - if (pkg_count < 1 || pkg_count != lpi_data->package.count - 3) { - pr_debug("count given by _LPI is not valid\n"); - ret = -ENODATA; - goto end; - } - - lpi_state = kzalloc_objs(*lpi_state, pkg_count); - if (!lpi_state) { - ret = -ENOMEM; - goto end; - } - - info->size = pkg_count; - info->entries = lpi_state; - - /* LPI States start at index 3 */ - for (loop = 3; state_idx <= pkg_count; loop++, state_idx++, lpi_state++) { - union acpi_object *element, *pkg_elem, *obj; - - element = &lpi_data->package.elements[loop]; - if (element->type != ACPI_TYPE_PACKAGE || element->package.count < 7) - continue; - - pkg_elem = element->package.elements; - - obj = pkg_elem + 6; - if (obj->type == ACPI_TYPE_BUFFER) { - struct acpi_power_register *reg; - - reg = (struct acpi_power_register *)obj->buffer.pointer; - if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO && - reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE) - continue; - - lpi_state->address = reg->address; - lpi_state->entry_method = - reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE ? - ACPI_CSTATE_FFH : ACPI_CSTATE_SYSTEMIO; - } else if (obj->type == ACPI_TYPE_INTEGER) { - lpi_state->entry_method = ACPI_CSTATE_INTEGER; - lpi_state->address = obj->integer.value; - } else { - pr_debug("Entry method of state-%d is invalid, disable it.\n", - state_idx); - continue; - } - - /* elements[7,8] skipped for now i.e. Residency/Usage counter*/ - - obj = pkg_elem + 9; - if (obj->type == ACPI_TYPE_STRING) - strscpy(lpi_state->desc, obj->string.pointer, - ACPI_CX_DESC_LEN); - - lpi_state->index = state_idx; - if (obj_get_integer(pkg_elem + 0, &lpi_state->min_residency)) { - pr_debug("No min. residency found, assuming 10 us\n"); - lpi_state->min_residency = 10; - } - - if (obj_get_integer(pkg_elem + 1, &lpi_state->wake_latency)) { - pr_debug("No wakeup residency found, assuming 10 us\n"); - lpi_state->wake_latency = 10; - } - - if (obj_get_integer(pkg_elem + 2, &lpi_state->flags)) - lpi_state->flags = 0; - - if (obj_get_integer(pkg_elem + 3, &lpi_state->arch_flags)) - lpi_state->arch_flags = 0; - - if (obj_get_integer(pkg_elem + 4, &lpi_state->res_cnt_freq)) - lpi_state->res_cnt_freq = 1; - - if (obj_get_integer(pkg_elem + 5, &lpi_state->enable_parent_state)) - lpi_state->enable_parent_state = 0; - } - - acpi_handle_debug(handle, "Found %d power states\n", state_idx); -end: - kfree(buffer.pointer); - return ret; -} - -/** - * combine_lpi_states - combine local and parent LPI states to form a composite LPI state - * - * @local: local LPI state - * @parent: parent LPI state - * @result: composite LPI state - */ -static bool combine_lpi_states(struct acpi_lpi_state *local, - struct acpi_lpi_state *parent, - struct acpi_lpi_state *result) -{ - if (parent->entry_method == ACPI_CSTATE_INTEGER) { - if (!parent->address) /* 0 means autopromotable */ - return false; - result->address = local->address + parent->address; - } else { - result->address = parent->address; - } - - result->min_residency = max(local->min_residency, parent->min_residency); - result->wake_latency = local->wake_latency + parent->wake_latency; - result->enable_parent_state = parent->enable_parent_state; - result->entry_method = local->entry_method; - - result->flags = parent->flags; - result->arch_flags = parent->arch_flags; - result->index = parent->index; - - scnprintf(result->desc, ACPI_CX_DESC_LEN, "%s+%s", local->desc, parent->desc); - return true; -} - -#define ACPI_LPI_STATE_FLAGS_ENABLED BIT(0) - -static void stash_composite_state(struct acpi_lpi_states_array *curr_level, - struct acpi_lpi_state *t) -{ - curr_level->composite_states[curr_level->composite_states_size++] = t; -} - -static unsigned int flatten_lpi_states(struct acpi_processor *pr, - unsigned int flat_state_cnt, - struct acpi_lpi_states_array *curr_level, - struct acpi_lpi_states_array *prev_level) -{ - int i, j, state_count = curr_level->size; - struct acpi_lpi_state *p, *t = curr_level->entries; - - curr_level->composite_states_size = 0; - for (j = 0; j < state_count; j++, t++) { - struct acpi_lpi_state *flpi; - - if (!(t->flags & ACPI_LPI_STATE_FLAGS_ENABLED)) - continue; - - if (flat_state_cnt >= ACPI_PROCESSOR_MAX_POWER) { - pr_warn("Limiting number of LPI states to max (%d)\n", - ACPI_PROCESSOR_MAX_POWER); - pr_warn("Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n"); - break; - } - - flpi = &pr->power.lpi_states[flat_state_cnt]; - - if (!prev_level) { /* leaf/processor node */ - memcpy(flpi, t, sizeof(*t)); - stash_composite_state(curr_level, flpi); - flat_state_cnt++; - continue; - } - - for (i = 0; i < prev_level->composite_states_size; i++) { - p = prev_level->composite_states[i]; - if (t->index <= p->enable_parent_state && - combine_lpi_states(p, t, flpi)) { - stash_composite_state(curr_level, flpi); - flat_state_cnt++; - flpi++; - if (flat_state_cnt >= ACPI_PROCESSOR_MAX_POWER) - break; - } - } - } - - kfree(curr_level->entries); - return flat_state_cnt; -} - int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu) { return -EOPNOTSUPP; @@ -1077,64 +860,16 @@ int __weak acpi_processor_ffh_lpi_probe(unsigned int cpu) static int acpi_processor_get_lpi_info(struct acpi_processor *pr) { - int ret, i; - acpi_status status; - acpi_handle handle = pr->handle, pr_ahandle; - struct acpi_device *d = NULL; - struct acpi_lpi_states_array info[2], *tmp, *prev, *curr; - unsigned int state_count; + int ret; /* make sure our architecture has support */ ret = acpi_processor_ffh_lpi_probe(pr->id); if (ret == -EOPNOTSUPP) return ret; - if (!osc_pc_lpi_support_confirmed) - return -EOPNOTSUPP; - - if (!acpi_has_method(handle, "_LPI")) - return -EINVAL; - - prev = &info[0]; - curr = &info[1]; - handle = pr->handle; - ret = acpi_processor_evaluate_lpi(handle, prev); + ret = acpi_processor_extract_lpi_info(pr->handle, &pr->power, false); if (ret) return ret; - state_count = flatten_lpi_states(pr, 0, prev, NULL); - - status = acpi_get_parent(handle, &pr_ahandle); - while (ACPI_SUCCESS(status)) { - d = acpi_fetch_acpi_dev(pr_ahandle); - if (!d) - break; - - handle = pr_ahandle; - - if (strcmp(acpi_device_hid(d), ACPI_PROCESSOR_CONTAINER_HID)) - break; - - /* can be optional ? */ - if (!acpi_has_method(handle, "_LPI")) - break; - - ret = acpi_processor_evaluate_lpi(handle, curr); - if (ret) - break; - - /* flatten all the LPI states in this level of hierarchy */ - state_count = flatten_lpi_states(pr, state_count, curr, prev); - - tmp = prev, prev = curr, curr = tmp; - - status = acpi_get_parent(handle, &pr_ahandle); - } - - /* reset the index after flattening */ - for (i = 0; i < state_count; i++) - pr->power.lpi_states[i].index = i; - - pr->power.count = state_count; /* Tell driver that _LPI is supported. */ pr->flags.has_lpi = 1; diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index f71467f6ada4..184dc4b3b938 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -41,6 +41,9 @@ #include "../base.h" #include "power.h" +#undef MODULE_PARAM_PREFIX +#define MODULE_PARAM_PREFIX "pm." + typedef int (*pm_callback_t)(struct device *); /* @@ -532,6 +535,11 @@ module_param(dpm_watchdog_all_cpu_backtrace, bool, 0644); MODULE_PARM_DESC(dpm_watchdog_all_cpu_backtrace, "Backtrace all CPUs on DPM watchdog timeout"); +static bool __read_mostly dpm_watchdog_enabled = + IS_ENABLED(CONFIG_DPM_WATCHDOG_ENABLED); +module_param(dpm_watchdog_enabled, bool, 0644); +MODULE_PARM_DESC(dpm_watchdog_enabled, "Enable DPM watchdog"); + static unsigned int __read_mostly dpm_watchdog_timeout = CONFIG_DPM_WATCHDOG_TIMEOUT; static unsigned int __read_mostly dpm_watchdog_warning_timeout = CONFIG_DPM_WATCHDOG_WARNING_TIMEOUT; @@ -627,6 +635,9 @@ static void dpm_watchdog_set(struct dpm_watchdog *wd, struct device *dev) { struct timer_list *timer = &wd->timer; + if (!dpm_watchdog_enabled) + return; + wd->dev = dev; wd->tsk = current; wd->fatal = dpm_watchdog_timeout == dpm_watchdog_warning_timeout; @@ -645,6 +656,9 @@ static void dpm_watchdog_clear(struct dpm_watchdog *wd) { struct timer_list *timer = &wd->timer; + if (!dpm_watchdog_enabled) + return; + timer_delete_sync(timer); timer_destroy_on_stack(timer); } diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 335288e8b5b3..fab38bc98113 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -469,9 +469,6 @@ static int rpm_callback(int (*cb)(struct device *), struct device *dev) if (retval == -EACCES) retval = -EAGAIN; - if (retval != -EAGAIN && retval != -EBUSY) - dev->power.runtime_error = retval; - return retval; } @@ -751,6 +748,9 @@ static int rpm_suspend(struct device *dev, int rpmflags) dev->power.deferred_resume = false; wake_up_all(&dev->power.wait_queue); + if (retval != -EAGAIN && retval != -EBUSY) + dev->power.runtime_error = retval; + /* * On transient errors, if the callback routine failed an autosuspend, * and if the last_busy time has been updated so that there is a new diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c index 735b29f76438..e23773680e05 100644 --- a/drivers/cpufreq/amd-pstate-ut.c +++ b/drivers/cpufreq/amd-pstate-ut.c @@ -59,6 +59,7 @@ static int amd_pstate_ut_check_freq(u32 index); static int amd_pstate_ut_epp(u32 index); static int amd_pstate_ut_check_driver(u32 index); static int amd_pstate_ut_check_freq_attrs(u32 index); +static int amd_pstate_ut_check_floor_freq(u32 index); static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = { {"amd_pstate_ut_acpi_cpc_valid", amd_pstate_ut_acpi_cpc_valid }, @@ -68,6 +69,7 @@ static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = { {"amd_pstate_ut_epp", amd_pstate_ut_epp }, {"amd_pstate_ut_check_driver", amd_pstate_ut_check_driver }, {"amd_pstate_ut_check_freq_attrs", amd_pstate_ut_check_freq_attrs }, + {"amd_pstate_ut_check_floor_freq", amd_pstate_ut_check_floor_freq }, }; static bool test_in_list(const char *list, const char *name) @@ -275,6 +277,7 @@ static int amd_pstate_set_mode(enum amd_pstate_mode mode) static int amd_pstate_ut_epp(u32 index) { static const char * const epp_strings[] = { + "dynamic", "power", "balance_power", "balance_performance", @@ -282,10 +285,10 @@ static int amd_pstate_ut_epp(u32 index) }; char *buf __free(cleanup_page) = NULL; struct cpufreq_policy *policy = NULL; + unsigned long orig_dynamic_epp = 0; enum amd_pstate_mode orig_mode; struct amd_cpudata *cpudata; unsigned long orig_policy; - bool orig_dynamic_epp; int ret, cpu = 0; u16 epp; int i; @@ -294,9 +297,11 @@ static int amd_pstate_ut_epp(u32 index) if (!policy) return -ENODEV; - cpudata = policy->driver_data; orig_mode = amd_pstate_get_status(); - orig_dynamic_epp = cpudata->dynamic_epp; + if (policy->driver_data) { + cpudata = policy->driver_data; + orig_dynamic_epp = cpudata->dynamic_epp; + } /* Drop reference before potential driver change. */ cpufreq_cpu_put(policy); @@ -321,16 +326,6 @@ static int amd_pstate_ut_epp(u32 index) orig_policy = cpudata->policy; cpudata->policy = CPUFREQ_POLICY_POWERSAVE; - /* - * Disable dynamic EPP before running test. If "orig_dynamic_epp" is - * true, the driver will do a redundant switch at the end and there - * is no need for enabling it again at the end of the test. - */ - if (cpudata->dynamic_epp) { - pr_debug("Dynamic EPP is enabled, disabling it\n"); - amd_pstate_clear_dynamic_epp(policy); - } - for (epp = 0; epp <= U8_MAX; epp++) { u8 val; @@ -367,6 +362,11 @@ static int amd_pstate_ut_epp(u32 index) if (ret < 0) goto out; strreplace(buf, '\n', '\0'); + /* + * "dynamic" mode reports the EPP as "dynamic(profile:X)" + * Trim at "(" and just compare tie the epp string. + */ + strreplace(buf, '(', '\0'); if (strcmp(buf, epp_strings[i])) { pr_err("String EPP value mismatch: %s != %s\n", buf, epp_strings[i]); @@ -380,18 +380,23 @@ static int amd_pstate_ut_epp(u32 index) out: if (policy) { cpudata->policy = orig_policy; + /* + * If the driver had enabled dynamic_epp to begin with, + * restore it here before dropping policy reference. + */ + if (orig_dynamic_epp) { + int ret2; + + ret2 = store_energy_performance_preference(policy, + epp_strings[0], + strlen(epp_strings[0])); + if (!ret && (ret2 < 0)) + ret = ret2; + } up_write(&policy->rwsem); cpufreq_cpu_put(policy); } - if (orig_dynamic_epp) { - int ret2; - - ret2 = amd_pstate_set_mode(AMD_PSTATE_DISABLE); - if (!ret && ret2) - ret = ret2; - } - if (orig_mode != amd_pstate_get_status()) { int ret2; @@ -557,9 +562,88 @@ out: return ret; } +static int amd_pstate_ut_check_floor_freq(u32 index) +{ + struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL; + char *buf __free(cleanup_page) = NULL; + unsigned int orig_floor_freq; + unsigned int floor_freq; + int ret, cpu = 0; + + if (!cpu_feature_enabled(X86_FEATURE_CPPC_PERF_PRIO)) + return -EOPNOTSUPP; + + policy = cpufreq_cpu_get(cpu); + if (!policy) + return -ENODEV; + + buf = (char *)__get_free_page(GFP_KERNEL); + if (!buf) + return -ENOMEM; + + guard(rwsem_write)(&policy->rwsem); + + if (!policy->driver_data) + return -ENODEV; + + /* Retrieve original floor frequency */ + memset(buf, 0, PAGE_SIZE); + ret = show_amd_pstate_floor_freq(policy, buf); + if (ret < 0) + return ret; + + ret = kstrtou32(buf, 0, &orig_floor_freq); + if (ret) + return ret; + + memset(buf, 0, PAGE_SIZE); + snprintf(buf, PAGE_SIZE, "%u", policy->cpuinfo.min_freq); + + /* Set floor frequency to cpuinfo.min_freq */ + ret = store_amd_pstate_floor_freq(policy, buf, strlen(buf)); + if (ret < 0) { + pr_err("Failed to set floor frequency to %s\n", buf); + return ret; + } + + memset(buf, 0, PAGE_SIZE); + ret = show_amd_pstate_floor_freq(policy, buf); + if (ret < 0) + return ret; + + strreplace(buf, '\n', '\0'); + ret = kstrtou32(buf, 0, &floor_freq); + if (ret) + return ret; + + /* Confirm sysfs reflects the change correctly. */ + if (floor_freq != policy->cpuinfo.min_freq) { + pr_err("Floor frequency value mismatch: %u != %u\n", + floor_freq, policy->cpuinfo.min_freq); + return -EINVAL; + } + + memset(buf, 0, PAGE_SIZE); + snprintf(buf, PAGE_SIZE, "%u", orig_floor_freq); + + /* Restore the original value. */ + ret = store_amd_pstate_floor_freq(policy, buf, strlen(buf)); + if (ret < 0) { + pr_err("Failed to restore floor frequency to %s\n", buf); + return ret; + } + + return 0; +} + static int __init amd_pstate_ut_init(void) { u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases); + enum amd_pstate_mode mode = amd_pstate_get_status(); + + /* don't test if no running amd-pstate driver */ + if (mode == AMD_PSTATE_UNDEFINED || mode == AMD_PSTATE_DISABLE) + return -EOPNOTSUPP; for (i = 0; i < arr_size; i++) { int ret; @@ -570,10 +654,16 @@ static int __init amd_pstate_ut_init(void) ret = amd_pstate_ut_cases[i].func(i); - if (ret) + if (ret) { + /* Platform does not support the feature being tested. */ + if (ret == -EOPNOTSUPP) { + pr_err("%-4d %-20s\t skipped!\n", i+1, amd_pstate_ut_cases[i].name); + continue; + } pr_err("%-4d %-20s\t fail: %d!\n", i+1, amd_pstate_ut_cases[i].name, ret); - else + } else { pr_info("%-4d %-20s\t success!\n", i+1, amd_pstate_ut_cases[i].name); + } } return 0; diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 3a6b4b224a66..d4ff8b228f86 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -75,7 +75,7 @@ const char *amd_pstate_get_mode_string(enum amd_pstate_mode mode) mode = AMD_PSTATE_UNDEFINED; return amd_pstate_mode_string[mode]; } -EXPORT_SYMBOL_GPL(amd_pstate_get_mode_string); +EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_mode_string); struct quirk_entry { u32 nominal_freq; @@ -87,7 +87,6 @@ static struct cpufreq_driver amd_pstate_driver; static struct cpufreq_driver amd_pstate_epp_driver; static int cppc_state = AMD_PSTATE_UNDEFINED; static bool amd_pstate_prefcore = true; -static bool dynamic_epp; static struct quirk_entry *quirks; /* @@ -106,6 +105,7 @@ static struct quirk_entry *quirks; * 3 balance_power * 4 power * 5 custom (for raw EPP values) + * 6 dynamic (platform profile driven selection) */ enum energy_perf_value_index { EPP_INDEX_DEFAULT = 0, @@ -114,6 +114,7 @@ enum energy_perf_value_index { EPP_INDEX_BALANCE_POWERSAVE, EPP_INDEX_POWERSAVE, EPP_INDEX_CUSTOM, + EPP_INDEX_DYNAMIC, EPP_INDEX_MAX, }; @@ -124,6 +125,7 @@ static const char * const energy_perf_strings[] = { [EPP_INDEX_BALANCE_POWERSAVE] = "balance_power", [EPP_INDEX_POWERSAVE] = "power", [EPP_INDEX_CUSTOM] = "custom", + [EPP_INDEX_DYNAMIC] = "dynamic", }; static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX); @@ -134,7 +136,7 @@ static unsigned int epp_values[] = { [EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_BALANCE_POWERSAVE, [EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_POWERSAVE, }; -static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 1); +static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 2); typedef int (*cppc_mode_transition_fn)(int); @@ -199,7 +201,7 @@ static inline int get_mode_idx_from_str(const char *str, size_t size) static DEFINE_MUTEX(amd_pstate_driver_lock); -static u8 msr_get_epp(struct amd_cpudata *cpudata) +static int msr_get_epp(struct amd_cpudata *cpudata) { u64 value; int ret; @@ -215,12 +217,12 @@ static u8 msr_get_epp(struct amd_cpudata *cpudata) DEFINE_STATIC_CALL(amd_pstate_get_epp, msr_get_epp); -static inline s16 amd_pstate_get_epp(struct amd_cpudata *cpudata) +static inline int amd_pstate_get_epp(struct amd_cpudata *cpudata) { return static_call(amd_pstate_get_epp)(cpudata); } -static u8 shmem_get_epp(struct amd_cpudata *cpudata) +static int shmem_get_epp(struct amd_cpudata *cpudata) { u64 epp; int ret; @@ -462,7 +464,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata) { union perf_cached perf = READ_ONCE(cpudata->perf); u64 cap1, numerator, cppc_req; - u8 min_perf; int ret = rdmsrq_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_CAP1, &cap1); @@ -478,16 +479,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata) return ret; WRITE_ONCE(cpudata->cppc_req_cached, cppc_req); - min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req); - - /* - * Clear out the min_perf part to check if the rest of the MSR is 0, if yes, this is an - * indication that the min_perf value is the one specified through the BIOS option - */ - cppc_req &= ~(AMD_CPPC_MIN_PERF_MASK); - - if (!cppc_req) - perf.bios_min_perf = min_perf; perf.highest_perf = numerator; perf.max_limit_perf = numerator; @@ -495,6 +486,7 @@ static int msr_init_perf(struct amd_cpudata *cpudata) perf.nominal_perf = FIELD_GET(AMD_CPPC_NOMINAL_PERF_MASK, cap1); perf.lowest_nonlinear_perf = FIELD_GET(AMD_CPPC_LOWNONLIN_PERF_MASK, cap1); perf.lowest_perf = FIELD_GET(AMD_CPPC_LOWEST_PERF_MASK, cap1); + perf.bios_min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req); WRITE_ONCE(cpudata->perf, perf); WRITE_ONCE(cpudata->prefcore_ranking, FIELD_GET(AMD_CPPC_HIGHEST_PERF_MASK, cap1)); WRITE_ONCE(cpudata->floor_perf_cnt, FIELD_GET(AMD_CPPC_FLOOR_PERF_CNT_MASK, cap1)); @@ -526,9 +518,6 @@ static int shmem_init_perf(struct amd_cpudata *cpudata) WRITE_ONCE(cpudata->perf, perf); WRITE_ONCE(cpudata->prefcore_ranking, cppc_perf.highest_perf); - if (cppc_state == AMD_PSTATE_ACTIVE) - return 0; - ret = cppc_get_auto_sel(cpudata->cpu, &auto_sel); if (ret) { pr_warn("failed to get auto_sel, ret: %d\n", ret); @@ -702,9 +691,12 @@ static void amd_pstate_update_min_max_limit(struct cpufreq_policy *policy) WRITE_ONCE(cpudata->max_limit_freq, policy->max); if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) { + u8 min_limit_perf = perf.bios_min_perf ?: perf.nominal_perf; + u32 min_limit_freq; + /* - * For performance policy, set MinPerf to nominal_perf rather than - * highest_perf or lowest_nonlinear_perf. + * For performance policy, set MinPerf to nominal_perf / bios_min_perf + * rather than highest_perf or lowest_nonlinear_perf. * * Per commit 0c411b39e4f4c, using highest_perf was observed * to cause frequency throttling on power-limited platforms, leading to @@ -712,11 +704,18 @@ static void amd_pstate_update_min_max_limit(struct cpufreq_policy *policy) * performance too much for HPC workloads requiring high frequency * operation and minimal wakeup latency from idle states. * - * nominal_perf therefore provides a balance by avoiding throttling - * while still maintaining enough performance for HPC workloads. + * nominal_perf therefore provides a balanced default by avoiding + * throttling while still maintaining enough performance for HPC + * workloads when bios_min_perf is not available. + * + * When bios_min_perf is available, users have profiled their workloads + * to understand the best idling frequency. Use that instead. */ - perf.min_limit_perf = min(perf.nominal_perf, perf.max_limit_perf); - WRITE_ONCE(cpudata->min_limit_freq, min(cpudata->nominal_freq, cpudata->max_limit_freq)); + min_limit_perf = min(min_limit_perf, perf.max_limit_perf); + min_limit_freq = perf_to_freq(perf, cpudata->nominal_freq, min_limit_perf); + perf.min_limit_perf = min_limit_perf; + + WRITE_ONCE(cpudata->min_limit_freq, min(min_limit_freq, cpudata->max_limit_freq)); } else { perf.min_limit_perf = freq_to_perf(perf, cpudata->nominal_freq, policy->min); WRITE_ONCE(cpudata->min_limit_freq, policy->min); @@ -782,6 +781,7 @@ static unsigned int amd_pstate_fast_switch(struct cpufreq_policy *policy, static void amd_pstate_adjust_perf(struct cpufreq_policy *policy, unsigned long _min_perf, unsigned long target_perf, + unsigned long _max_perf, unsigned long capacity) { u8 max_perf, min_perf, des_perf, cap_perf; @@ -1037,6 +1037,13 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata) return -EINVAL; } + if (perf.bios_min_perf) { + u32 bios_min_freq = perf_to_freq(perf, cpudata->nominal_freq, perf.bios_min_perf); + + pr_debug("Found Requested CPU Min Frequency of %uKHz on CPU%d\n", + bios_min_freq, cpudata->cpu); + } + return 0; } @@ -1173,6 +1180,9 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb, if (cpudata->current_profile != PLATFORM_PROFILE_BALANCED) return 0; + if (!policy) + return NOTIFY_OK; + epp = amd_pstate_get_balanced_epp(policy); ret = amd_pstate_set_epp(policy, epp); @@ -1182,6 +1192,24 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb, return NOTIFY_OK; } +static int amd_pstate_get_epp_from_platform_profile(struct cpufreq_policy *policy, + enum platform_profile_option profile) +{ + switch (profile) { + case PLATFORM_PROFILE_PERFORMANCE: + return AMD_CPPC_EPP_PERFORMANCE; + case PLATFORM_PROFILE_BALANCED: + return amd_pstate_get_balanced_epp(policy); + case PLATFORM_PROFILE_LOW_POWER: + return AMD_CPPC_EPP_POWERSAVE; + default: + break; + } + + pr_err("Unknown Platform Profile %d\n", profile); + return -EOPNOTSUPP; +} + static int amd_pstate_profile_probe(void *drvdata, unsigned long *choices) { set_bit(PLATFORM_PROFILE_LOW_POWER, choices); @@ -1207,28 +1235,19 @@ static int amd_pstate_profile_set(struct device *dev, struct amd_cpudata *cpudata = dev_get_drvdata(dev); struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpudata->cpu); int ret; + u8 epp; - switch (profile) { - case PLATFORM_PROFILE_LOW_POWER: - ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_POWERSAVE); - if (ret) - return ret; - break; - case PLATFORM_PROFILE_BALANCED: - ret = amd_pstate_set_epp(policy, - amd_pstate_get_balanced_epp(policy)); - if (ret) - return ret; - break; - case PLATFORM_PROFILE_PERFORMANCE: - ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_PERFORMANCE); - if (ret) - return ret; - break; - default: - pr_err("Unknown Platform Profile %d\n", profile); - return -EOPNOTSUPP; - } + if (!policy) + return -ENODEV; + + ret = amd_pstate_get_epp_from_platform_profile(policy, profile); + if (ret < 0) + return ret; + + epp = (u8)ret; + ret = amd_pstate_set_epp(policy, epp); + if (ret) + return ret; cpudata->current_profile = profile; @@ -1254,28 +1273,20 @@ void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy) kfree(cpudata->profile_name); cpudata->dynamic_epp = false; } -EXPORT_SYMBOL_GPL(amd_pstate_clear_dynamic_epp); +EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_clear_dynamic_epp); static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy) { struct amd_cpudata *cpudata = policy->driver_data; + u64 prev = READ_ONCE(cpudata->cppc_req_cached); int ret; u8 epp; - switch (cpudata->current_profile) { - case PLATFORM_PROFILE_PERFORMANCE: - epp = AMD_CPPC_EPP_PERFORMANCE; - break; - case PLATFORM_PROFILE_LOW_POWER: - epp = AMD_CPPC_EPP_POWERSAVE; - break; - case PLATFORM_PROFILE_BALANCED: - epp = amd_pstate_get_balanced_epp(policy); - break; - default: - pr_err("Unknown Platform Profile %d\n", cpudata->current_profile); - return -EOPNOTSUPP; - } + ret = amd_pstate_get_epp_from_platform_profile(policy, cpudata->current_profile); + if (ret < 0) + return ret; + + epp = (u8)ret; ret = amd_pstate_set_epp(policy, epp); if (ret) return ret; @@ -1308,6 +1319,9 @@ static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy) cleanup: amd_pstate_clear_dynamic_epp(policy); + epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, prev); + /* Restore previous EPP if toggling Dynamic EPP failed. */ + amd_pstate_set_epp(policy, epp); return ret; } @@ -1378,7 +1392,7 @@ static ssize_t show_amd_pstate_hw_prefcore(struct cpufreq_policy *policy, static ssize_t show_energy_performance_available_preferences( struct cpufreq_policy *policy, char *buf) { - int offset = 0, i; + int i, offset = 0; struct amd_cpudata *cpudata = policy->driver_data; if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) @@ -1401,11 +1415,6 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, bool raw_epp = false; u8 epp; - if (cpudata->dynamic_epp) { - pr_debug("EPP cannot be set when dynamic EPP is enabled\n"); - return -EBUSY; - } - /* * if the value matches a number, use that, otherwise see if * matches an index in the energy_perf_strings array @@ -1416,6 +1425,25 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, ret = sysfs_match_string(energy_perf_strings, buf); if (ret < 0 || ret == EPP_INDEX_CUSTOM) return -EINVAL; + + if (ret == EPP_INDEX_DYNAMIC) { + if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) + return -EBUSY; + /* + * Dynamic EPP was already enabled for this CPU. + * Nothing to do. + */ + if (cpudata->dynamic_epp) + return count; + + cpudata->current_profile = PLATFORM_PROFILE_BALANCED; + ret = amd_pstate_set_dynamic_epp(policy); + if (ret) + return ret; + + return count; + } + if (ret) epp = epp_values[ret]; else @@ -1427,6 +1455,13 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, return -EBUSY; } + /* + * Dynamic EPP was enabled previously! + * Switch back to the static EPP mode. + */ + if (cpudata->dynamic_epp) + amd_pstate_clear_dynamic_epp(policy); + ret = amd_pstate_set_epp(policy, epp); if (ret) return ret; @@ -1435,7 +1470,7 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, return count; } -EXPORT_SYMBOL_GPL(store_energy_performance_preference); +EXPORT_SYMBOL_FOR_PSTATE_UT(store_energy_performance_preference); ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf) { @@ -1444,7 +1479,7 @@ ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char * epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached); - if (cpudata->raw_epp) + if (!cpudata->dynamic_epp && cpudata->raw_epp) return sysfs_emit(buf, "%u\n", epp); switch (epp) { @@ -1464,12 +1499,14 @@ ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char * return -EINVAL; } + if (cpudata->dynamic_epp) + return sysfs_emit(buf, "dynamic(profile:%s)\n", energy_perf_strings[preference]); + return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]); } -EXPORT_SYMBOL_GPL(show_energy_performance_preference); +EXPORT_SYMBOL_FOR_PSTATE_UT(show_energy_performance_preference); -static ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, - const char *buf, size_t count) +ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, const char *buf, size_t count) { struct amd_cpudata *cpudata = policy->driver_data; union perf_cached perf = READ_ONCE(cpudata->perf); @@ -1492,13 +1529,15 @@ static ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, return ret ?: count; } +EXPORT_SYMBOL_FOR_PSTATE_UT(store_amd_pstate_floor_freq); -static ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf) +ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf) { struct amd_cpudata *cpudata = policy->driver_data; return sysfs_emit(buf, "%u\n", cpudata->floor_freq); } +EXPORT_SYMBOL_FOR_PSTATE_UT(show_amd_pstate_floor_freq); static ssize_t show_amd_pstate_floor_count(struct cpufreq_policy *policy, char *buf) { @@ -1566,7 +1605,7 @@ struct freq_attr **amd_pstate_get_current_attrs(void) return NULL; return current_pstate_driver->attr; } -EXPORT_SYMBOL_GPL(amd_pstate_get_current_attrs); +EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_current_attrs); static struct freq_attr **get_freq_attrs(void) { @@ -1751,7 +1790,7 @@ int amd_pstate_get_status(void) { return cppc_state; } -EXPORT_SYMBOL_GPL(amd_pstate_get_status); +EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_status); int amd_pstate_update_status(const char *buf, size_t size) { @@ -1771,7 +1810,7 @@ int amd_pstate_update_status(const char *buf, size_t size) return 0; } -EXPORT_SYMBOL_GPL(amd_pstate_update_status); +EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_update_status); static ssize_t status_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1799,50 +1838,12 @@ static ssize_t prefcore_show(struct device *dev, return sysfs_emit(buf, "%s\n", str_enabled_disabled(amd_pstate_prefcore)); } -static ssize_t dynamic_epp_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - return sysfs_emit(buf, "%s\n", str_enabled_disabled(dynamic_epp)); -} - -static ssize_t dynamic_epp_store(struct device *a, struct device_attribute *b, - const char *buf, size_t count) -{ - bool enabled; - int ret; - - ret = kstrtobool(buf, &enabled); - if (ret) - return ret; - - guard(mutex)(&amd_pstate_driver_lock); - - if (cppc_state != AMD_PSTATE_ACTIVE) { - pr_debug("dynamic_epp can only be toggled in active mode\n"); - return -EINVAL; - } - - /* Nothing to do */ - if (dynamic_epp == enabled) - return count; - - /* reinitialize with desired dynamic EPP value */ - dynamic_epp = enabled; - ret = amd_pstate_change_driver_mode(cppc_state); - if (ret) - dynamic_epp = false; - - return ret ? ret : count; -} - static DEVICE_ATTR_RW(status); static DEVICE_ATTR_RO(prefcore); -static DEVICE_ATTR_RW(dynamic_epp); static struct attribute *pstate_global_attributes[] = { &dev_attr_status.attr, &dev_attr_prefcore.attr, - &dev_attr_dynamic_epp.attr, NULL }; @@ -1876,6 +1877,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) struct amd_cpudata *cpudata; union perf_cached perf; struct device *dev; + int default_epp; int ret; /* @@ -1924,6 +1926,14 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) policy->boost_supported = READ_ONCE(cpudata->boost_supported); + /* Cache the firmware programmed EPP */ + default_epp = amd_pstate_get_epp(cpudata); + if (default_epp < 0) { + ret = default_epp; + goto free_cpudata1; + } + FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &cpudata->cppc_req_cached, default_epp); + /* * Set the policy to provide a valid fallback value in case * the default cpufreq governor is neither powersave nor performance. @@ -1931,7 +1941,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) if (amd_pstate_acpi_pm_profile_server() || amd_pstate_acpi_pm_profile_undefined()) { policy->policy = CPUFREQ_POLICY_PERFORMANCE; - cpudata->epp_default_ac = cpudata->epp_default_dc = amd_pstate_get_epp(cpudata); + cpudata->epp_default_ac = cpudata->epp_default_dc = default_epp; cpudata->current_profile = PLATFORM_PROFILE_PERFORMANCE; } else { policy->policy = CPUFREQ_POLICY_POWERSAVE; @@ -1940,10 +1950,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) cpudata->current_profile = PLATFORM_PROFILE_BALANCED; } - if (dynamic_epp) - ret = amd_pstate_set_dynamic_epp(policy); - else - ret = amd_pstate_set_epp(policy, cpudata->epp_default_dc); + ret = amd_pstate_set_epp(policy, cpudata->epp_default_dc); if (ret) goto free_cpudata1; @@ -2015,6 +2022,18 @@ static int amd_pstate_epp_set_policy(struct cpufreq_policy *policy) if (!policy->cpuinfo.max_freq) return -ENODEV; + /* Must be a switch between PERFORMANCE and POWERSAVE */ + if (cpudata->policy != policy->policy) { + /* + * Disable dynamic_epp when switching + * out of CPUFREQ_POLICY_POWERSAVE. + */ + if (cpudata->dynamic_epp) { + WARN_ON_ONCE(cpudata->policy != CPUFREQ_POLICY_POWERSAVE); + amd_pstate_clear_dynamic_epp(policy); + } + } + cpudata->policy = policy->policy; ret = amd_pstate_epp_update_limit(policy, true); @@ -2342,19 +2361,8 @@ static int __init amd_prefcore_param(char *str) return 0; } -static int __init amd_dynamic_epp_param(char *str) -{ - if (!strcmp(str, "disable")) - dynamic_epp = false; - if (!strcmp(str, "enable")) - dynamic_epp = true; - - return 0; -} - early_param("amd_pstate", amd_pstate_param); early_param("amd_prefcore", amd_prefcore_param); -early_param("amd_dynamic_epp", amd_dynamic_epp_param); MODULE_AUTHOR("Huang Rui <ray.huang@amd.com>"); MODULE_DESCRIPTION("AMD Processor P-state Frequency Driver"); diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h index 23e8baa05849..9f5a81976eae 100644 --- a/drivers/cpufreq/amd-pstate.h +++ b/drivers/cpufreq/amd-pstate.h @@ -11,6 +11,13 @@ #include <linux/pm_qos.h> #include <linux/platform_profile.h> +#if IS_MODULE(CONFIG_X86_AMD_PSTATE_UT) +#define EXPORT_SYMBOL_FOR_PSTATE_UT(symbol) \ + EXPORT_SYMBOL_FOR_MODULES(symbol, "amd-pstate-ut") +#else +#define EXPORT_SYMBOL_FOR_PSTATE_UT(symbol) +#endif + /********************************************************************* * AMD P-state INTERFACE * *********************************************************************/ @@ -32,6 +39,7 @@ * @min_limit_perf: Cached value of the performance corresponding to policy->min * @max_limit_perf: Cached value of the performance corresponding to policy->max * @bios_min_perf: Cached perf value corresponding to the "Requested CPU Min Frequency" BIOS option + * @val: Raw 64-bit value for atomic access via READ_ONCE()/WRITE_ONCE() */ union perf_cached { struct { @@ -89,7 +97,12 @@ struct amd_aperf_mperf { * @epp_default_ac: Default EPP value for AC power source * @epp_default_dc: Default EPP value for DC power source * @dynamic_epp: Whether dynamic EPP is enabled + * @raw_epp: Whether the last EPP write was a raw numeric value rather than a + * named preference * @power_nb: Notifier block for power events + * @current_profile: Currently selected platform profile option + * @ppdev: Device registered with the platform profile handler + * @profile_name: Name under which @ppdev is registered * * The amd_cpudata is key private data for each CPU thread in AMD P-State, and * represents all the attributes and goals that AMD P-State requests at runtime. @@ -153,6 +166,8 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy, const char *buf, size_t count); ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf); void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy); +ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, const char *buf, size_t count); +ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf); struct freq_attr; diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index 638e5bf72185..7c50ce74f911 100644 --- a/drivers/cpufreq/apple-soc-cpufreq.c +++ b/drivers/cpufreq/apple-soc-cpufreq.c @@ -249,21 +249,19 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy) return -ENODEV; } - ret = dev_pm_opp_of_add_table(cpu_dev); - if (ret < 0) { - dev_err(cpu_dev, "%s: failed to add OPP table: %d\n", __func__, ret); - return ret; - } + priv = kzalloc_obj(*priv); + if (!priv) + return -ENOMEM; ret = apple_soc_cpufreq_find_cluster(policy, ®_base, &info); if (ret) { dev_err(cpu_dev, "%s: failed to get cluster info: %d\n", __func__, ret); - return ret; + goto out_free_priv; } - ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus); - if (ret) { - dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", __func__, ret); + ret = dev_pm_opp_of_cpumask_add_table(policy->cpus); + if (ret < 0) { + dev_err(cpu_dev, "%s: failed to add OPP table: %d\n", __func__, ret); goto out_iounmap; } @@ -271,24 +269,18 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy) if (ret <= 0) { dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n"); ret = -EPROBE_DEFER; - goto out_free_opp; - } - - priv = kzalloc_obj(*priv); - if (!priv) { - ret = -ENOMEM; - goto out_free_opp; + goto out_free_table; } ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table); if (ret) { dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret); - goto out_free_priv; + goto out_free_table; } /* Get OPP levels (p-state indexes) and stash them in driver_data */ for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) { - unsigned long rate = freq_table[i].frequency * 1000 + 999; + unsigned long rate = freq_table[i].frequency * 1000UL + 999; struct dev_pm_opp *opp = dev_pm_opp_find_freq_floor(cpu_dev, &rate); if (IS_ERR(opp)) { @@ -318,12 +310,12 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy) out_free_cpufreq_table: dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); -out_free_priv: - kfree(priv); -out_free_opp: - dev_pm_opp_remove_all_dynamic(cpu_dev); +out_free_table: + dev_pm_opp_of_cpumask_remove_table(policy->cpus); out_iounmap: iounmap(reg_base); +out_free_priv: + kfree(priv); return ret; } @@ -332,7 +324,7 @@ static void apple_soc_cpufreq_exit(struct cpufreq_policy *policy) struct apple_cpu_priv *priv = policy->driver_data; dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); - dev_pm_opp_remove_all_dynamic(priv->cpu_dev); + dev_pm_opp_of_cpumask_remove_table(policy->cpus); iounmap(priv->reg_base); kfree(priv); } diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c index 71450cca8e9f..7d902856ac01 100644 --- a/drivers/cpufreq/brcmstb-avs-cpufreq.c +++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c @@ -584,11 +584,8 @@ static int brcm_avs_prepare_init(struct platform_device *pdev) ret = devm_request_irq(dev, priv->host_irq, irq_handler, IRQF_TRIGGER_RISING, BRCM_AVS_HOST_INTR, priv); - if (ret && priv->host_irq >= 0) { - dev_err(dev, "IRQ request failed: %s (%d) -- %d\n", - BRCM_AVS_HOST_INTR, priv->host_irq, ret); + if (ret && priv->host_irq >= 0) goto unmap_intr_base; - } if (brcm_avs_is_firmware_loaded(priv)) return 0; diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index ff1204c666b1..284eece9e230 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -200,6 +200,7 @@ static const struct of_device_id blocklist[] __initconst = { { .compatible = "ti,am62l3", }, { .compatible = "ti,am62p5", }, + { .compatible = "qcom,ipq5210", }, { .compatible = "qcom,ipq5332", }, { .compatible = "qcom,ipq5424", }, { .compatible = "qcom,ipq6018", }, diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index b898b6544069..0d0df986fa3d 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2225,14 +2225,17 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch); * @policy: cpufreq policy object of the target CPU. * @min_perf: Minimum (required) performance level (units of @capacity). * @target_perf: Target (desired) performance level (units of @capacity). + * @max_perf: Maximum (allowed) performance level (units of @capacity). * @capacity: Capacity of the target CPU. * - * Carry out a fast performance level switch of @cpu without sleeping. + * Carry out a fast performance level adjustment for the CPU represented by + * @policy without sleeping. * * The driver's ->adjust_perf() callback invoked by this function must be - * suitable for being called from within RCU-sched read-side critical sections - * and it is expected to select a suitable performance level equal to or above - * @min_perf and preferably equal to or below @target_perf. + * suitable for calling from within RCU-sched read-side critical sections and + * it is expected to program the processor to select suitable performance + * levels between @min_perf and @max_perf inclusive and preferably close to + * @target_perf going forward for the CPU represented by @policy. * * This function must not be called if policy->fast_switch_enabled is unset. * @@ -2244,9 +2247,10 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch); void cpufreq_driver_adjust_perf(struct cpufreq_policy *policy, unsigned long min_perf, unsigned long target_perf, + unsigned long max_perf, unsigned long capacity) { - cpufreq_driver->adjust_perf(policy, min_perf, target_perf, capacity); + cpufreq_driver->adjust_perf(policy, min_perf, target_perf, max_perf, capacity); } /** diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index e93697d3edfd..731f5721ff1e 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -55,7 +55,6 @@ static unsigned int max_freq; static unsigned int transition_latency; static u32 *imx6_soc_volt; -static u32 soc_opp_count; static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index) { @@ -330,6 +329,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) const struct property *prop; const __be32 *val; u32 nr, i, j; + u32 soc_opp_count = 0; cpu_dev = get_cpu_device(0); if (!cpu_dev) { @@ -400,7 +400,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev) } /* Make imx6_soc_volt array's size same as arm opp number */ - imx6_soc_volt = devm_kcalloc(cpu_dev, num, sizeof(*imx6_soc_volt), + imx6_soc_volt = devm_kcalloc(&pdev->dev, num, sizeof(*imx6_soc_volt), GFP_KERNEL); if (imx6_soc_volt == NULL) { ret = -ENOMEM; @@ -485,6 +485,7 @@ soc_opp_out: return 0; free_freq_table: + imx6_soc_volt = NULL; dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); out_free_opp: dev_pm_opp_of_remove_table(cpu_dev); @@ -506,6 +507,7 @@ put_node: static void imx6q_cpufreq_remove(struct platform_device *pdev) { cpufreq_unregister_driver(&imx6q_cpufreq_driver); + imx6_soc_volt = NULL; dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table); dev_pm_opp_of_remove_table(cpu_dev); regulator_put(arm_reg); diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 6e984c114d96..7ce5981cbae5 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -299,7 +299,6 @@ struct pstate_funcs { static struct pstate_funcs pstate_funcs __read_mostly; static bool hwp_active __ro_after_init; -static int hwp_mode_bdw __ro_after_init; static bool per_cpu_limits __ro_after_init; static bool hwp_forced __ro_after_init; static bool hwp_boost __read_mostly; @@ -587,21 +586,14 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu) hwp_is_hybrid = true; - cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_pstate * scaling, - perf_ctl_scaling); - cpu->pstate.max_freq = rounddown(cpu->pstate.max_pstate * scaling, - perf_ctl_scaling); - freq = perf_ctl_max_phys * perf_ctl_scaling; cpu->pstate.max_pstate_physical = intel_pstate_freq_to_hwp(cpu, freq); - freq = cpu->pstate.min_pstate * perf_ctl_scaling; - cpu->pstate.min_freq = 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, freq); + cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, cpu->pstate.min_freq); } static bool turbo_is_disabled(void) @@ -979,12 +971,10 @@ static int hybrid_get_cost(struct device *dev, unsigned long freq, * capacity. Similarly, P-cores start to be populated when E-cores are * utilized above 60% of the capacity. */ - if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_ATOM) { - if (hybrid_has_l3(dev->id)) /* E-core */ - *cost += 1; - } else { /* P-core */ + if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_CORE) /* P-core */ *cost += 2; - } + else if (hybrid_has_l3(dev->id)) /* E-core */ + *cost += 1; return 0; } @@ -1185,6 +1175,22 @@ static bool hybrid_clear_max_perf_cpu(void) return ret; } +static void intel_pstate_update_freq_limits(struct cpudata *cpu) +{ + int scaling = cpu->pstate.scaling; + unsigned int turbo_freq = cpu->pstate.turbo_pstate * scaling; + unsigned int max_freq = cpu->pstate.max_pstate * scaling; + int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; + + if (scaling != perf_ctl_scaling) { + turbo_freq = rounddown(turbo_freq, perf_ctl_scaling); + max_freq = rounddown(max_freq, perf_ctl_scaling); + } + + cpu->pstate.turbo_freq = turbo_freq; + cpu->pstate.max_freq = max_freq; +} + static void __intel_pstate_get_hwp_cap(struct cpudata *cpu) { u64 cap; @@ -1197,20 +1203,8 @@ static void __intel_pstate_get_hwp_cap(struct cpudata *cpu) static void intel_pstate_get_hwp_cap(struct cpudata *cpu) { - int scaling = cpu->pstate.scaling; - __intel_pstate_get_hwp_cap(cpu); - - cpu->pstate.max_freq = cpu->pstate.max_pstate * scaling; - cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * scaling; - if (scaling != cpu->pstate.perf_ctl_scaling) { - int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling; - - cpu->pstate.max_freq = rounddown(cpu->pstate.max_freq, - perf_ctl_scaling); - cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_freq, - perf_ctl_scaling); - } + intel_pstate_update_freq_limits(cpu); } static void hybrid_update_capacity(struct cpudata *cpu) @@ -2299,33 +2293,16 @@ static int hwp_get_cpu_scaling(int cpu) return intel_pstate_cppc_get_scaling(cpu); } -static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) -{ - trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu); - cpu->pstate.current_pstate = pstate; - /* - * Generally, there is no guarantee that this code will always run on - * the CPU being updated, so force the register update to run on the - * right CPU. - */ - wrmsrq_on_cpu(cpu->cpu, MSR_IA32_PERF_CTL, - pstate_funcs.get_val(cpu, pstate)); -} - -static void intel_pstate_set_min_pstate(struct cpudata *cpu) -{ - intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); -} - 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.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; - if (hwp_active && !hwp_mode_bdw) { + if (hwp_active) { __intel_pstate_get_hwp_cap(cpu); if (pstate_funcs.get_cpu_scaling) { @@ -2345,19 +2322,13 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu) cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(cpu->cpu); } - if (cpu->pstate.scaling == perf_ctl_scaling) { - cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling; - cpu->pstate.max_freq = cpu->pstate.max_pstate * perf_ctl_scaling; - cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * perf_ctl_scaling; - } + intel_pstate_update_freq_limits(cpu); if (pstate_funcs.get_aperf_mperf_shift) cpu->aperf_mperf_shift = pstate_funcs.get_aperf_mperf_shift(); if (pstate_funcs.get_vid) pstate_funcs.get_vid(cpu); - - intel_pstate_set_min_pstate(cpu); } /* @@ -2884,8 +2855,22 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu, cpu->min_perf_ratio); } +static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate) +{ + trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu); + cpu->pstate.current_pstate = pstate; + /* + * Generally, there is no guarantee that this code will always run on + * the CPU being updated, so force the register update to run on the + * right CPU. + */ + wrmsrq_on_cpu(cpu->cpu, MSR_IA32_PERF_CTL, + pstate_funcs.get_val(cpu, pstate)); +} + static int intel_pstate_set_policy(struct cpufreq_policy *policy) { + unsigned int freq = policy->min; struct cpudata *cpu; if (!policy->cpuinfo.max_freq) @@ -2901,7 +2886,23 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) intel_pstate_update_perf_limits(cpu, policy->min, policy->max); - if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { + if (hwp_active) { + /* + * The active mode only requires an update util hook if HWP + * boost is used and the policy is not "performance". + */ + if (hwp_boost && cpu->policy != CPUFREQ_POLICY_PERFORMANCE) { + intel_pstate_set_update_util_hook(policy->cpu); + } else { + intel_pstate_clear_update_util_hook(policy->cpu); + if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { + freq = cpu->max_perf_ratio * cpu->pstate.scaling; + if (cpu->pstate.scaling != cpu->pstate.perf_ctl_scaling) + freq = rounddown(freq, cpu->pstate.perf_ctl_scaling); + } + } + intel_pstate_hwp_set(policy->cpu); + } else if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) { int pstate = max(cpu->pstate.min_pstate, cpu->max_perf_ratio); /* @@ -2910,25 +2911,17 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy) */ intel_pstate_clear_update_util_hook(policy->cpu); intel_pstate_set_pstate(cpu, pstate); + freq = pstate * cpu->pstate.scaling; } else { intel_pstate_set_update_util_hook(policy->cpu); } - - if (hwp_active) { - /* - * When hwp_boost was active before and dynamically it - * was turned off, in that case we need to clear the - * update util hook. - */ - if (!hwp_boost) - intel_pstate_clear_update_util_hook(policy->cpu); - intel_pstate_hwp_set(policy->cpu); - } /* - * policy->cur is never updated with the intel_pstate driver, but it - * is used as a stale frequency value. So, keep it within limits. + * policy->cur is never updated in the intel_pstate driver, but it is + * used as a stale frequency value, so set it to reflect the actual + * requested P-state in the "performance" policy case and to the min + * otherwise. */ - policy->cur = policy->min; + policy->cur = freq; mutex_unlock(&intel_pstate_limits_lock); @@ -2971,6 +2964,11 @@ static int intel_pstate_verify_policy(struct cpufreq_policy_data *policy) return 0; } +static void intel_pstate_set_min_pstate(struct cpudata *cpu) +{ + intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate); +} + static int intel_cpufreq_cpu_offline(struct cpufreq_policy *policy) { struct cpudata *cpu = all_cpu_data[policy->cpu]; @@ -3063,6 +3061,7 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy) static int intel_pstate_cpu_init(struct cpufreq_policy *policy) { int ret = __intel_pstate_cpu_init(policy); + struct cpudata *cpu; if (ret) return ret; @@ -3073,11 +3072,11 @@ static int intel_pstate_cpu_init(struct cpufreq_policy *policy) */ policy->policy = CPUFREQ_POLICY_POWERSAVE; - if (hwp_active) { - struct cpudata *cpu = all_cpu_data[policy->cpu]; - + cpu = all_cpu_data[policy->cpu]; + if (hwp_active) cpu->epp_cached = intel_pstate_get_epp(cpu, 0); - } + else + intel_pstate_set_min_pstate(cpu); return 0; } @@ -3243,6 +3242,7 @@ static unsigned int intel_cpufreq_fast_switch(struct cpufreq_policy *policy, static void intel_cpufreq_adjust_perf(struct cpufreq_policy *policy, unsigned long min_perf, unsigned long target_perf, + unsigned long max_perf, unsigned long capacity) { struct cpudata *cpu = all_cpu_data[policy->cpu]; @@ -3273,7 +3273,13 @@ static void intel_cpufreq_adjust_perf(struct cpufreq_policy *policy, if (min_pstate > cpu->max_perf_ratio) min_pstate = cpu->max_perf_ratio; - max_pstate = min(cap_pstate, cpu->max_perf_ratio); + max_pstate = cap_pstate; + if (max_perf < capacity) + max_pstate = DIV_ROUND_UP(cap_pstate * max_perf, capacity); + + if (max_pstate > cpu->max_perf_ratio) + max_pstate = cpu->max_perf_ratio; + if (max_pstate < min_pstate) max_pstate = min_pstate; @@ -3301,8 +3307,6 @@ static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy) return ret; policy->cpuinfo.transition_latency = INTEL_CPUFREQ_TRANSITION_LATENCY; - /* This reflects the intel_pstate_get_cpu_pstates() setting. */ - policy->cur = policy->cpuinfo.min_freq; req = kzalloc_objs(*req, 2); if (!req) { @@ -3323,9 +3327,15 @@ static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy) WRITE_ONCE(cpu->hwp_req_cached, value); cpu->epp_cached = intel_pstate_get_epp(cpu, value); + + intel_cpufreq_hwp_update(cpu, cpu->pstate.min_pstate, + cpu->pstate.max_pstate, + cpu->pstate.min_pstate, false); } else { policy->transition_delay_us = INTEL_CPUFREQ_TRANSITION_DELAY; + intel_pstate_set_min_pstate(cpu); } + policy->cur = policy->cpuinfo.min_freq; freq = DIV_ROUND_UP(cpu->pstate.turbo_freq * global.min_perf_pct, 100); @@ -3676,14 +3686,14 @@ static inline bool intel_pstate_has_acpi_ppc(void) { return false; } static inline void intel_pstate_request_control_from_smm(void) {} #endif /* CONFIG_ACPI */ -#define INTEL_PSTATE_HWP_BROADWELL 0x01 +#define INTEL_PSTATE_HWP_NOT_HYBRID 0x01 #define X86_MATCH_HWP(vfm, hwp_mode) \ X86_MATCH_VFM_FEATURE(vfm, X86_FEATURE_HWP, hwp_mode) static const struct x86_cpu_id hwp_support_ids[] __initconst = { - X86_MATCH_HWP(INTEL_BROADWELL_X, INTEL_PSTATE_HWP_BROADWELL), - X86_MATCH_HWP(INTEL_BROADWELL_D, INTEL_PSTATE_HWP_BROADWELL), + X86_MATCH_HWP(INTEL_BROADWELL_X, INTEL_PSTATE_HWP_NOT_HYBRID), + X86_MATCH_HWP(INTEL_BROADWELL_D, INTEL_PSTATE_HWP_NOT_HYBRID), X86_MATCH_HWP(INTEL_ANY, 0), {} }; @@ -3808,7 +3818,6 @@ static int __init intel_pstate_init(void) if (!no_hwp) { hwp_active = true; - hwp_mode_bdw = id->driver_data; intel_pstate.attr = hwp_cpufreq_attrs; intel_cpufreq.attr = hwp_cpufreq_attrs; intel_cpufreq.flags |= CPUFREQ_NEED_UPDATE_LIMITS; @@ -3816,7 +3825,8 @@ static int __init intel_pstate_init(void) if (!default_driver) default_driver = &intel_pstate; - pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling; + if (!id->driver_data) + pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling; goto hwp_cpu_matched; } diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c index e6d28d162442..efa766e98d86 100644 --- a/drivers/cpufreq/qcom-cpufreq-nvmem.c +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c @@ -200,6 +200,13 @@ static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev, case QCOM_ID_IPQ9574: drv->versions = 1 << (unsigned int)(*speedbin); break; + case QCOM_ID_IPQ5200: + case QCOM_ID_IPQ5210: + case QCOM_ID_QCF2200: + case QCOM_ID_QCF3200: + case QCOM_ID_QCF3210: + drv->versions = (*speedbin == 0xcd) ? BIT(1) : BIT(0); + break; case QCOM_ID_IPQ5424: case QCOM_ID_IPQ5404: drv->versions = (*speedbin == 0x3b) ? BIT(1) : BIT(0); @@ -618,6 +625,7 @@ static const struct of_device_id qcom_cpufreq_match_list[] __initconst __maybe_u { .compatible = "qcom,msm8909", .data = &match_data_msm8909 }, { .compatible = "qcom,msm8996", .data = &match_data_kryo }, { .compatible = "qcom,qcs404", .data = &match_data_qcs404 }, + { .compatible = "qcom,ipq5210", .data = &match_data_kryo }, { .compatible = "qcom,ipq5332", .data = &match_data_kryo }, { .compatible = "qcom,ipq5424", .data = &match_data_kryo }, { .compatible = "qcom,ipq6018", .data = &match_data_ipq6018 }, diff --git a/drivers/cpufreq/rcpufreq_dt.rs b/drivers/cpufreq/rcpufreq_dt.rs index 10106fa13095..5d27857261da 100644 --- a/drivers/cpufreq/rcpufreq_dt.rs +++ b/drivers/cpufreq/rcpufreq_dt.rs @@ -4,12 +4,19 @@ use kernel::{ clk::Clk, - cpu, cpufreq, + cpu, + cpufreq, // cpumask::CpumaskVar, - device::{Core, Device}, + device::{ + Core, + Device, // + }, error::code::*, macros::vtable, - module_platform_driver, of, opp, platform, + module_platform_driver, + of, + opp, + platform, // prelude::*, str::CString, sync::Arc, diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c index 81a0780b2ebf..ffe5eda82f0b 100644 --- a/drivers/cpufreq/spear-cpufreq.c +++ b/drivers/cpufreq/spear-cpufreq.c @@ -79,9 +79,9 @@ static int spear1340_set_cpu_rate(struct clk *sys_pclk, unsigned long newfreq) int ret = 0; sys_clk = clk_get_parent(spear_cpufreq.clk); - if (IS_ERR(sys_clk)) { + if (!sys_clk) { pr_err("failed to get cpu's parent (sys) clock\n"); - return PTR_ERR(sys_clk); + return -EINVAL; } /* Set the rate of the source clock before changing the parent */ diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index d74b478db280..651408df9c24 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -53,6 +53,7 @@ #include <linux/notifier.h> #include <linux/cpu.h> #include <linux/moduleparam.h> +#include <linux/pm_qos.h> #include <linux/sysfs.h> #include <asm/cpuid/api.h> #include <asm/cpu_device_id.h> @@ -1779,6 +1780,7 @@ module_param_named(no_native, no_native, bool, 0444); MODULE_PARM_DESC(no_native, "Ignore cpu specific (native) idle states in lieu of ACPI idle states"); static struct acpi_processor_power acpi_state_table __initdata; +static bool acpi_lpi_available __initdata; /** * intel_idle_cst_usable - Check if the _CST information can be used. @@ -1803,18 +1805,37 @@ static bool __init intel_idle_cst_usable(void) return true; } -static bool __init intel_idle_acpi_cst_extract(void) +static bool __init intel_idle_acpi_extract_lpi_cstates(void) { unsigned int cpu; - if (no_acpi) { - pr_debug("Not allowed to use ACPI _CST\n"); - return false; + for_each_possible_cpu(cpu) { + struct acpi_processor *pr; + + pr = per_cpu(processors, cpu); + if (!pr) + continue; + + if (acpi_processor_extract_lpi_info(pr->handle, + &acpi_state_table, true)) + continue; + + acpi_lpi_available = true; + return true; } + pr_debug("No ACPI _LPI idle states\n"); + return false; +} + +static bool __init intel_idle_acpi_extract_cst_cstates(void) +{ + unsigned int cpu; + for_each_possible_cpu(cpu) { - struct acpi_processor *pr = per_cpu(processors, cpu); + struct acpi_processor *pr; + pr = per_cpu(processors, cpu); if (!pr) continue; @@ -1826,18 +1847,96 @@ static bool __init intel_idle_acpi_cst_extract(void) if (!intel_idle_cst_usable()) continue; - if (!acpi_processor_claim_cst_control()) - break; + return true; + } + + pr_debug("ACPI _CST not found or not usable\n"); + return false; +} +static bool __init intel_idle_acpi_extract_cstates(void) +{ + if (intel_idle_acpi_extract_lpi_cstates()) return true; + + if (intel_idle_acpi_extract_cst_cstates()) + return true; + + return false; +} + +static bool __init intel_idle_acpi_probe(void) +{ + if (no_acpi) { + pr_debug("Not allowed to use ACPI for C-states extraction\n"); + return false; } + if (intel_idle_acpi_extract_cstates() && + acpi_processor_claim_cst_control()) + return true; + acpi_state_table.count = 0; - pr_debug("ACPI _CST not found or not usable\n"); return false; } -static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) +static void __init intel_idle_complete_state_init(struct cpuidle_state *state) +{ + if (intel_idle_state_needs_timer_stop(state)) + state->flags |= CPUIDLE_FLAG_TIMER_STOP; + + state->enter = intel_idle; + state->enter_dead = intel_idle_enter_dead; + state->enter_s2idle = intel_idle_s2idle; +} + +static void __init intel_idle_init_cstates_acpi_lpi(struct cpuidle_driver *drv) +{ + int index; + + for (index = 0; index < acpi_state_table.count; index++) { + struct acpi_lpi_state *lpi_state; + struct cpuidle_state *state; + + if (intel_idle_max_cstate_reached(index)) + break; + + lpi_state = &acpi_state_table.lpi_states[index]; + + state = &drv->states[drv->state_count++]; + + scnprintf(state->name, CPUIDLE_NAME_LEN, "C%d_LPI", index + 1); + strscpy(state->desc, lpi_state->desc, CPUIDLE_DESC_LEN); + state->exit_latency = lpi_state->wake_latency; + state->target_residency = lpi_state->min_residency; + state->flags = MWAIT2flg(lpi_state->address); + /* + * Assume that entering any of the idle states extracted from + * _LPI except for the first two will cause the TLB to be + * flushed and let the core call leave_mm() for them upfront + * to avoid unnecessary wakeups due to TLB shootdowns. + */ + if (index > 1) + state->flags |= CPUIDLE_FLAG_TLB_FLUSHED; + + if (disabled_states_mask & BIT(index + 1)) + state->flags |= CPUIDLE_FLAG_OFF; + + intel_idle_complete_state_init(state); + + pr_info("%s: MWAIT hint 0x%x\n", state->name, flg2MWAIT(state->flags)); + } + + /* + * Assume the first idle state in the table to be C1 and if any deeper + * idle states are exposed while X86_FEATURE_NONSTOP_TSC is unset, mark + * the TSC as unstable. + */ + if (index > 1 && !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) + mark_tsc_unstable("TSC halts in idle"); +} + +static void __init intel_idle_init_cstates_acpi_cst(struct cpuidle_driver *drv) { int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); @@ -1879,49 +1978,76 @@ static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) if (disabled_states_mask & BIT(cstate)) state->flags |= CPUIDLE_FLAG_OFF; - if (intel_idle_state_needs_timer_stop(state)) - state->flags |= CPUIDLE_FLAG_TIMER_STOP; - if (cx->type > ACPI_STATE_C1 && !boot_cpu_has(X86_FEATURE_NONSTOP_TSC)) mark_tsc_unstable("TSC halts in idle"); - state->enter = intel_idle; - state->enter_dead = intel_idle_enter_dead; - state->enter_s2idle = intel_idle_s2idle; + intel_idle_complete_state_init(state); } } -static bool __init intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) +static void __init intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) { - int cstate, limit; + if (acpi_lpi_available) + intel_idle_init_cstates_acpi_lpi(drv); + else + intel_idle_init_cstates_acpi_cst(drv); +} - /* - * If there are no _CST C-states, do not disable any C-states by - * default. - */ - if (!acpi_state_table.count) - return false; +static bool __init intel_idle_acpi_hint_match(unsigned int flags, u32 acpi_hint, + u32 table_hint) +{ + if (flags & CPUIDLE_FLAG_PARTIAL_HINT_MATCH) { + acpi_hint &= ~MWAIT_SUBSTATE_MASK; + table_hint &= ~MWAIT_SUBSTATE_MASK; + } + return acpi_hint == table_hint; +} + +static bool __init intel_idle_off_by_default_lpi(unsigned int flags, u32 mwait_hint) +{ + int index; + + for (index = 0; index < acpi_state_table.count; index++) { + u32 acpi_hint = acpi_state_table.lpi_states[index].address; + + if (intel_idle_acpi_hint_match(flags, acpi_hint, mwait_hint)) + return false; + } + return true; +} + +static bool __init intel_idle_off_by_default_cst(unsigned int flags, u32 mwait_hint) +{ + int cstate, limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); - limit = min_t(int, CPUIDLE_STATE_MAX, acpi_state_table.count); /* * If limit > 0, intel_idle_cst_usable() has returned 'true', so all of * the interesting states are ACPI_CSTATE_FFH. */ for (cstate = 1; cstate < limit; cstate++) { u32 acpi_hint = acpi_state_table.states[cstate].address; - u32 table_hint = mwait_hint; - - if (flags & CPUIDLE_FLAG_PARTIAL_HINT_MATCH) { - acpi_hint &= ~MWAIT_SUBSTATE_MASK; - table_hint &= ~MWAIT_SUBSTATE_MASK; - } - if (acpi_hint == table_hint) + if (intel_idle_acpi_hint_match(flags, acpi_hint, mwait_hint)) return false; } return true; } +static bool __init intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) +{ + /* + * If there is no C-states information in the ACPI tables, do not + * disable any C-states by default. + */ + if (!acpi_state_table.count) + return false; + + if (acpi_lpi_available) + return intel_idle_off_by_default_lpi(flags, mwait_hint); + + return intel_idle_off_by_default_cst(flags, mwait_hint); +} + static inline bool ignore_native(void) { return no_native && !no_acpi; @@ -1929,7 +2055,7 @@ static inline bool ignore_native(void) #else /* !CONFIG_ACPI_PROCESSOR_CSTATE */ #define force_use_acpi (false) -static inline bool intel_idle_acpi_cst_extract(void) { return false; } +static inline bool intel_idle_acpi_probe(void) { return false; } static inline void intel_idle_init_cstates_acpi(struct cpuidle_driver *drv) { } static inline bool intel_idle_off_by_default(unsigned int flags, u32 mwait_hint) { @@ -2697,6 +2823,9 @@ error: pr_info("Failed to adjust C-states with data from 'intel_idle.table'\n"); } +#define INTEL_IDLE_INIT_QOS 20 +static struct pm_qos_request qos_req __initdata; + static int __init intel_idle_init(void) { const struct x86_cpu_id *id; @@ -2741,7 +2870,7 @@ static int __init intel_idle_init(void) if (icpu) { if (icpu->state_table) cpuidle_state_table = icpu->state_table; - else if (!intel_idle_acpi_cst_extract()) + else if (!intel_idle_acpi_probe()) return -ENODEV; auto_demotion_disable_flags = icpu->auto_demotion_disable_flags; @@ -2750,8 +2879,8 @@ static int __init intel_idle_init(void) if (icpu->c1_demotion_supported) c1_demotion_supported = true; if (icpu->use_acpi || force_use_acpi) - intel_idle_acpi_cst_extract(); - } else if (!intel_idle_acpi_cst_extract()) { + intel_idle_acpi_probe(); + } else if (!intel_idle_acpi_probe()) { return -ENODEV; } @@ -2766,6 +2895,13 @@ static int __init intel_idle_init(void) if (retval) pr_warn("failed to initialized sysfs"); + /* + * Some platforms, in particular the Intel S1200BTL motherboard, have a + * problem with using package idle states too early, so prevent that + * from taking place until the device_initcall() phase is over. + */ + cpu_latency_qos_add_request(&qos_req, INTEL_IDLE_INIT_QOS); + retval = cpuidle_register_driver(&intel_idle_driver); if (retval) { struct cpuidle_driver *drv = cpuidle_get_driver(); @@ -2790,6 +2926,9 @@ hp_setup_fail: intel_idle_cpuidle_devices_uninit(); cpuidle_unregister_driver(&intel_idle_driver); init_driver_fail: + if (cpu_latency_qos_request_active((&qos_req))) + cpu_latency_qos_remove_request(&qos_req); + intel_idle_sysfs_uninit(); free_percpu(intel_idle_cpuidle_devices); return retval; @@ -2797,6 +2936,15 @@ init_driver_fail: } subsys_initcall_sync(intel_idle_init); +static int __init intel_idle_init_complete(void) +{ + if (cpu_latency_qos_request_active((&qos_req))) + cpu_latency_qos_remove_request(&qos_req); + + return 0; +} +device_initcall_sync(intel_idle_init_complete); + /* * We are not really modular, but we used to support that. Meaning we also * support "intel_idle.max_cstate=..." at boot and also a read-only export of diff --git a/drivers/opp/core.c b/drivers/opp/core.c index ab0b0a2f85a1..cd0e82dae776 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -1412,13 +1412,12 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table, */ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq) { + struct opp_table *opp_table __free(put_opp_table) = + _find_opp_table(dev); struct dev_pm_opp *opp __free(put_opp) = NULL; unsigned long freq = 0, temp_freq; bool forced = false; - struct opp_table *opp_table __free(put_opp_table) = - _find_opp_table(dev); - if (IS_ERR(opp_table)) { dev_err(dev, "%s: device's opp table doesn't exist\n", __func__); return PTR_ERR(opp_table); @@ -1582,8 +1581,6 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, struct opp_table *opp_table, bool getclk) { - int ret; - /* * Return early if we don't need to get clk or we have already done it * earlier. @@ -1592,39 +1589,35 @@ static struct opp_table *_update_opp_table_clk(struct device *dev, opp_table->clks) return opp_table; - /* Find clk for the device */ - opp_table->clk = clk_get(dev, NULL); + /* + * There are few platforms which don't want the OPP core to manage + * device's clock settings. In such cases neither the platform + * provides the clks explicitly to us, nor the DT contains a valid + * clk entry. The OPP nodes in DT may still contain "opp-hz" property + * though, which we need to parse and allow the platform to find an + * OPP based on freq later on. + * + * This is a simple solution to take care of such corner cases, i.e. + * make the clk_count 1, which lets us allocate space for frequency + * in opp->rates and also parse the entries in DT. Use + * clk_get_optional() instead of clk_get() so opp_table->clk stays + * NULL for such devices, instead of holding an ERR_PTR(-ENOENT) that + * consumers must remember to special-case. + */ + opp_table->clk = clk_get_optional(dev, NULL); - ret = PTR_ERR_OR_ZERO(opp_table->clk); - if (!ret) { - opp_table->config_clks = _opp_config_clk_single; - opp_table->clk_count = 1; - return opp_table; + if (IS_ERR(opp_table->clk)) { + dev_pm_opp_put_opp_table(opp_table); + dev_err_probe(dev, PTR_ERR(opp_table->clk), "Couldn't find clock\n"); + return ERR_CAST(opp_table->clk); } - if (ret == -ENOENT) { - /* - * There are few platforms which don't want the OPP core to - * manage device's clock settings. In such cases neither the - * platform provides the clks explicitly to us, nor the DT - * contains a valid clk entry. The OPP nodes in DT may still - * contain "opp-hz" property though, which we need to parse and - * allow the platform to find an OPP based on freq later on. - * - * This is a simple solution to take care of such corner cases, - * i.e. make the clk_count 1, which lets us allocate space for - * frequency in opp->rates and also parse the entries in DT. - */ - opp_table->clk_count = 1; - - dev_dbg(dev, "%s: Couldn't find clock: %d\n", __func__, ret); - return opp_table; - } + if (opp_table->clk) + opp_table->config_clks = _opp_config_clk_single; - dev_pm_opp_put_opp_table(opp_table); - dev_err_probe(dev, ret, "Couldn't find clock\n"); + opp_table->clk_count = 1; - return ERR_PTR(ret); + return opp_table; } /* @@ -2870,11 +2863,10 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_add_dynamic); static int _opp_set_availability(struct device *dev, unsigned long freq, bool availability_req) { - struct dev_pm_opp *opp __free(put_opp) = ERR_PTR(-ENODEV), *tmp_opp; - /* Find the opp_table */ struct opp_table *opp_table __free(put_opp_table) = _find_opp_table(dev); + struct dev_pm_opp *opp __free(put_opp) = ERR_PTR(-ENODEV), *tmp_opp; if (IS_ERR(opp_table)) { dev_warn(dev, "%s: Device OPP not found (%ld)\n", __func__, @@ -2932,12 +2924,11 @@ int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, unsigned long u_volt_max) { - struct dev_pm_opp *opp __free(put_opp) = ERR_PTR(-ENODEV), *tmp_opp; - int r; - /* Find the opp_table */ struct opp_table *opp_table __free(put_opp_table) = _find_opp_table(dev); + struct dev_pm_opp *opp __free(put_opp) = ERR_PTR(-ENODEV), *tmp_opp; + int r; if (IS_ERR(opp_table)) { r = PTR_ERR(opp_table); diff --git a/drivers/powercap/intel_rapl_tpmi.c b/drivers/powercap/intel_rapl_tpmi.c index 7f41491d9cd1..73f36d9c09b1 100644 --- a/drivers/powercap/intel_rapl_tpmi.c +++ b/drivers/powercap/intel_rapl_tpmi.c @@ -414,7 +414,10 @@ static int intel_rapl_tpmi_probe(struct auxiliary_device *auxdev, goto err; } - rapl_package_add_pmu(trp->rp); + ret = rapl_package_add_pmu(trp->rp); + if (ret) + dev_info(&auxdev->dev, "Failed to add RAPL PMU for Package%d, %d\n", + info->package_id, ret); auxiliary_set_drvdata(auxdev, trp); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 99afad0eb49e..ddacac812094 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -321,6 +321,19 @@ static inline int acpi_processor_evaluate_cst(acpi_handle handle, u32 cpu, } #endif +#ifdef CONFIG_ACPI_PROCESSOR_IDLE +int acpi_processor_extract_lpi_info(acpi_handle pr_handle, + struct acpi_processor_power *pr_power, + bool strict); +#else +static inline int acpi_processor_extract_lpi_info(acpi_handle pr_handle, + struct acpi_processor_power *pr_power, + bool strict) +{ + return -ENODEV; +} +#endif + #ifdef CONFIG_ACPI_HOTPLUG_CPU /* Arch dependent functions for cpu hotplug support */ int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index ae9d1ce4f49c..35ce665edfd8 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -379,6 +379,7 @@ struct cpufreq_driver { void (*adjust_perf)(struct cpufreq_policy *policy, unsigned long min_perf, unsigned long target_perf, + unsigned long max_perf, unsigned long capacity); /* @@ -624,6 +625,7 @@ unsigned int cpufreq_driver_fast_switch(struct cpufreq_policy *policy, void cpufreq_driver_adjust_perf(struct cpufreq_policy *policy, unsigned long min_perf, unsigned long target_perf, + unsigned long max_perf, unsigned long capacity); bool cpufreq_driver_has_adjust_perf(void); int cpufreq_driver_target(struct cpufreq_policy *policy, diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 530c897311d4..71165e7f04f4 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -268,6 +268,16 @@ config DPM_WATCHDOG captured in pstore device for inspection in subsequent boot session. +config DPM_WATCHDOG_ENABLED + bool "Enable DPM watchdog by default" + depends on DPM_WATCHDOG + default y + help + If you say Y here, the DPM watchdog will be enabled by default. + If you say N, it will be compiled in but disabled. It can be + enabled at boot time via the "pm.dpm_watchdog_enabled" kernel + parameter or at runtime via sysfs. + config DPM_WATCHDOG_TIMEOUT int "Watchdog timeout to panic in seconds" range 1 120 diff --git a/kernel/power/snapshot.c b/kernel/power/snapshot.c index d933b5b2c05d..b209712cb2c3 100644 --- a/kernel/power/snapshot.c +++ b/kernel/power/snapshot.c @@ -458,7 +458,7 @@ static struct rtree_node *alloc_rtree_node(gfp_t gfp_mask, int safe_needed, return node; } -/** +/* * add_rtree_block - Add a new leave node to the radix tree. * * The leave nodes need to be allocated in order to keep the leaves @@ -528,7 +528,7 @@ static int add_rtree_block(struct mem_zone_bm_rtree *zone, gfp_t gfp_mask, static void free_zone_bm_rtree(struct mem_zone_bm_rtree *zone, int clear_nosave_free); -/** +/* * create_zone_bm_rtree - Create a radix tree for one zone. * * Allocated the mem_zone_bm_rtree structure and initializes it. @@ -566,7 +566,7 @@ static struct mem_zone_bm_rtree *create_zone_bm_rtree(gfp_t gfp_mask, return zone; } -/** +/* * free_zone_bm_rtree - Free the memory of the radix tree. * * Free all node pages of the radix tree. The mem_zone_bm_rtree @@ -678,7 +678,7 @@ static int create_mem_extents(struct list_head *list, gfp_t gfp_mask) return 0; } -/** +/* * memory_bm_create - Allocate memory for a memory bitmap. */ static int memory_bm_create(struct memory_bitmap *bm, gfp_t gfp_mask, @@ -720,7 +720,7 @@ static int memory_bm_create(struct memory_bitmap *bm, gfp_t gfp_mask, goto Exit; } -/** +/* * memory_bm_free - Free memory occupied by the memory bitmap. * @bm: Memory bitmap. */ @@ -736,7 +736,7 @@ static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free) INIT_LIST_HEAD(&bm->zones); } -/** +/* * memory_bm_find_bit - Find the bit for a given PFN in a memory bitmap. * * Find the bit in memory bitmap @bm that corresponds to the given PFN. @@ -988,7 +988,7 @@ static void memory_bm_recycle(struct memory_bitmap *bm) } } -/** +/* * register_nosave_region - Register a region of unsaveable memory. * * Register a range of page frames the contents of which should not be saved @@ -1305,7 +1305,7 @@ static unsigned int count_free_highmem_pages(void) return cnt; } -/** +/* * saveable_highmem_page - Check if a highmem page is saveable. * * Determine whether a highmem page should be included in a hibernation image. @@ -1362,7 +1362,7 @@ static unsigned int count_highmem_pages(void) } #endif /* CONFIG_HIGHMEM */ -/** +/* * saveable_page - Check if the given page is saveable. * * Determine whether a non-highmem page should be included in a hibernation @@ -1440,7 +1440,7 @@ static inline bool do_copy_page(long *dst, long *src) return !z; } -/** +/* * safe_copy_page - Copy a page in a safe way. * * Check if the page we are going to copy is marked as present in the kernel @@ -1687,7 +1687,7 @@ static unsigned long preallocate_image_highmem(unsigned long nr_pages) return preallocate_image_pages(nr_pages, GFP_IMAGE | __GFP_HIGHMEM); } -/** +/* * __fraction - Compute (an approximation of) x * (multiplier / base). */ static unsigned long __fraction(u64 x, u64 multiplier, u64 base) @@ -1982,7 +1982,7 @@ int hibernate_preallocate_memory(void) } #ifdef CONFIG_HIGHMEM -/** +/* * count_pages_for_highmem - Count non-highmem pages needed for copying highmem. * * Compute the number of non-highmem pages that will be necessary for creating @@ -2003,7 +2003,7 @@ static unsigned int count_pages_for_highmem(unsigned int nr_highmem) static unsigned int count_pages_for_highmem(unsigned int nr_highmem) { return 0; } #endif /* CONFIG_HIGHMEM */ -/** +/* * enough_free_mem - Check if there is enough free memory for the image. */ static int enough_free_mem(unsigned int nr_pages, unsigned int nr_highmem) @@ -2023,7 +2023,7 @@ static int enough_free_mem(unsigned int nr_pages, unsigned int nr_highmem) } #ifdef CONFIG_HIGHMEM -/** +/* * get_highmem_buffer - Allocate a buffer for highmem pages. * * If there are some highmem pages in the hibernation image, we may need a @@ -2035,7 +2035,7 @@ static inline int get_highmem_buffer(int safe_needed) return buffer ? 0 : -ENOMEM; } -/** +/* * alloc_highmem_pages - Allocate some highmem pages for the image. * * Try to allocate as many pages as needed, but if the number of free highmem @@ -2065,7 +2065,7 @@ static inline unsigned int alloc_highmem_pages(struct memory_bitmap *bm, unsigned int n) { return 0; } #endif /* CONFIG_HIGHMEM */ -/** +/* * swsusp_alloc - Allocate memory for hibernation image. * * We first try to allocate as many highmem pages as there are @@ -2292,7 +2292,7 @@ static void duplicate_memory_bitmap(struct memory_bitmap *dst, } } -/** +/* * mark_unsafe_pages - Mark pages that were used before hibernation. * * Mark the pages that cannot be used for storing the image during restoration, @@ -2330,7 +2330,7 @@ static int check_header(struct swsusp_info *info) return 0; } -/** +/* * load_header - Check the image header and copy the data from it. */ static int load_header(struct swsusp_info *info) @@ -2483,7 +2483,7 @@ static int prepare_highmem_image(struct memory_bitmap *bm, static struct page *last_highmem_page; -/** +/* * get_highmem_page_buffer - Prepare a buffer to store a highmem image page. * * For a given highmem image page get a buffer that suspend_write_next() should @@ -2706,7 +2706,7 @@ static int prepare_image(struct memory_bitmap *new_bm, struct memory_bitmap *bm, return error; } -/** +/* * get_buffer - Get the address to store the next image data page. * * Get the address that snapshot_write_next() should return to its caller to @@ -2797,9 +2797,10 @@ next: return error; error = memory_bm_create(&zero_bm, GFP_ATOMIC, PG_ANY); - if (error) + if (error) { + memory_bm_free(©_bm, PG_UNSAFE_CLEAR); return error; - + } nr_zero_pages = 0; hibernate_restore_protection_begin(); @@ -2843,7 +2844,7 @@ next: return PAGE_SIZE; } -/** +/* * snapshot_write_finalize - Complete the loading of a hibernation image. * * Must be called after the last call to snapshot_write_next() in case the last diff --git a/kernel/power/swap.c b/kernel/power/swap.c index c626e9dc3c1c..c78f1593600b 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -430,19 +430,22 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, if (!handle->cur) return -EINVAL; - offset = alloc_swapdev_block(root_swap); - error = write_page(buf, offset, hb); - if (error) - return error; - handle->cur->entries[handle->k++] = offset; + + /* + * If the current map page is full, allocate and link next one first. + * Delaying this until here avoids writing an empty swap map page when + * the image size is an exact MAP_PAGE_ENTRIES multiple. + */ if (handle->k >= MAP_PAGE_ENTRIES) { offset = alloc_swapdev_block(root_swap); if (!offset) return -ENOSPC; + handle->cur->next_swap = offset; error = write_page(handle->cur, handle->cur_swap, hb); if (error) - goto out; + return error; + clear_page(handle->cur); handle->cur_swap = offset; handle->k = 0; @@ -450,7 +453,7 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, if (hb && low_free_pages() <= handle->reqd_free_pages) { error = hib_wait_io(hb); if (error) - goto out; + return error; /* * Recalculate the number of required free pages, to * make sure we never take more than half. @@ -458,14 +461,21 @@ static int swap_write_page(struct swap_map_handle *handle, void *buf, handle->reqd_free_pages = reqd_free_pages(); } } - out: - return error; + + offset = alloc_swapdev_block(root_swap); + error = write_page(buf, offset, hb); + if (error) + return error; + handle->cur->entries[handle->k++] = offset; + return 0; } static int flush_swap_writer(struct swap_map_handle *handle) { - if (handle->cur && handle->cur_swap) + if (handle->cur && handle->cur_swap && handle->k) return write_page(handle->cur, handle->cur_swap, NULL); + else if (handle->cur && handle->cur_swap) + return 0; else return -EINVAL; } @@ -739,7 +749,7 @@ static int save_compressed_image(struct swap_map_handle *handle, data[thr].cc = crypto_alloc_acomp(hib_comp_algo, 0, CRYPTO_ALG_ASYNC); if (IS_ERR_OR_NULL(data[thr].cc)) { - pr_err("Could not allocate comp stream %ld\n", PTR_ERR(data[thr].cc)); + pr_err("Could not allocate comp stream %pe\n", data[thr].cc); ret = -EFAULT; goto out_clean; } @@ -1243,7 +1253,7 @@ static int load_compressed_image(struct swap_map_handle *handle, data[thr].cc = crypto_alloc_acomp(hib_comp_algo, 0, CRYPTO_ALG_ASYNC); if (IS_ERR_OR_NULL(data[thr].cc)) { - pr_err("Could not allocate comp stream %ld\n", PTR_ERR(data[thr].cc)); + pr_err("Could not allocate comp stream %pe\n", data[thr].cc); ret = -EFAULT; goto out_clean; } diff --git a/kernel/power/wakelock.c b/kernel/power/wakelock.c index fd763da06a87..a8b6bd5ec46b 100644 --- a/kernel/power/wakelock.c +++ b/kernel/power/wakelock.c @@ -63,7 +63,7 @@ static unsigned int number_of_wakelocks; static inline bool wakelocks_limit_exceeded(void) { - return number_of_wakelocks > CONFIG_PM_WAKELOCKS_LIMIT; + return number_of_wakelocks >= CONFIG_PM_WAKELOCKS_LIMIT; } static inline void increment_wakelocks_number(void) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index dff4ee04694c..a1782755efcc 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -50,6 +50,7 @@ struct sugov_cpu { unsigned long util; unsigned long bw_min; + unsigned long bw_max; /* The field below is for single-CPU policies only: */ #ifdef CONFIG_NO_HZ_COMMON @@ -232,6 +233,7 @@ static void sugov_get_util(struct sugov_cpu *sg_cpu, unsigned long boost) util = effective_cpu_util(sg_cpu->cpu, util, &min, &max); util = max(util, boost); sg_cpu->bw_min = min; + sg_cpu->bw_max = max; sg_cpu->util = sugov_effective_cpu_perf(sg_cpu->cpu, util, min, max); } @@ -314,7 +316,7 @@ static void sugov_iowait_boost(struct sugov_cpu *sg_cpu, u64 time, * A CPU running a task which woken up after an IO operation can have its * utilization boosted to speed up the completion of those IO operations. * The IO boost value is increased each time a task wakes up from IO, in - * sugov_iowait_apply(), and it's instead decreased by this function, + * sugov_iowait_boost(), and it's instead decreased by this function, * each time an increase has not been requested (!iowait_boost_pending). * * A CPU which also appears to have been idle for at least one tick has also @@ -484,7 +486,7 @@ static void sugov_update_single_perf(struct update_util_data *hook, u64 time, sg_cpu->util = prev_util; cpufreq_driver_adjust_perf(sg_policy->policy, sg_cpu->bw_min, - sg_cpu->util, max_cap); + sg_cpu->util, sg_cpu->bw_max, max_cap); sg_policy->need_freq_update = false; sg_policy->last_freq_update_time = time; @@ -590,7 +592,7 @@ static ssize_t rate_limit_us_show(struct gov_attr_set *attr_set, char *buf) { struct sugov_tunables *tunables = to_sugov_tunables(attr_set); - return sprintf(buf, "%u\n", tunables->rate_limit_us); + return sysfs_emit(buf, "%u\n", tunables->rate_limit_us); } static ssize_t diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index 58ac04c650a1..affa2b9490ef 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -361,23 +361,28 @@ impl TableBuilder { } } - /// Adds a new entry to the table. - pub fn add(&mut self, freq: Hertz, flags: u32, driver_data: u32) -> Result { + /// Adds a raw frequency-table entry. + fn push(&mut self, frequency: u32, flags: u32, driver_data: u32) -> Result { // Adds the new entry at the end of the vector. Ok(self.entries.push( bindings::cpufreq_frequency_table { flags, driver_data, - frequency: freq.as_khz() as u32, + frequency, }, GFP_KERNEL, )?) } + /// Adds a new entry to the table. + pub fn add(&mut self, freq: Hertz, flags: u32, driver_data: u32) -> Result { + self.push(freq.as_khz() as u32, flags, driver_data) + } + /// Consumes the [`TableBuilder`] and returns [`TableBox`]. pub fn to_table(mut self) -> Result<TableBox> { // Add last entry to the table. - self.add(Hertz(c_ulong::MAX), 0, 0)?; + self.push(bindings::CPUFREQ_TABLE_END as u32, 0, 0)?; TableBox::new(self.entries) } @@ -792,7 +797,13 @@ pub trait Driver { } /// Driver's `adjust_perf` callback. - fn adjust_perf(_policy: &mut Policy, _min_perf: usize, _target_perf: usize, _capacity: usize) { + fn adjust_perf( + _policy: &mut Policy, + _min_perf: usize, + _target_perf: usize, + _max_perf: usize, + _capacity: usize, + ) { build_error!(VTABLE_DEFAULT_ERROR) } @@ -817,7 +828,9 @@ pub trait Driver { } /// Driver's `bios_limit` callback. - fn bios_limit(_policy: &mut Policy, _limit: &mut u32) -> Result { + /// + /// Returns HW/BIOS max frequency limitations for the CPU. + fn bios_limit(_policy: &mut Policy) -> Result<u32> { build_error!(VTABLE_DEFAULT_ERROR) } @@ -1263,12 +1276,13 @@ impl<T: Driver> Registration<T> { ptr: *mut bindings::cpufreq_policy, min_perf: c_ulong, target_perf: c_ulong, + max_perf: c_ulong, capacity: c_ulong, ) { // SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the // lifetime of `policy`. let policy = unsafe { Policy::from_raw_mut(ptr) }; - T::adjust_perf(policy, min_perf, target_perf, capacity); + T::adjust_perf(policy, min_perf, target_perf, max_perf, capacity); } /// Driver's `get_intermediate` callback. @@ -1352,9 +1366,12 @@ impl<T: Driver> Registration<T> { from_result(|| { let mut policy = PolicyCpu::from_cpu(cpu_id)?; - + let val = T::bios_limit(&mut policy)?; // SAFETY: `limit` is guaranteed by the C code to be valid. - T::bios_limit(&mut policy, &mut (unsafe { *limit })).map(|()| 0) + unsafe { + *limit = val; + } + Ok(0) }) } diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index 969716dfe8de..ab428e336d87 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -131,7 +131,7 @@ override CFLAGS += -DVERSION=\"$(VERSION)\" -DPACKAGE=\"$(PACKAGE)\" \ UTIL_OBJS = utils/helpers/amd.o utils/helpers/msr.o \ utils/helpers/sysfs.o utils/helpers/misc.o utils/helpers/cpuid.o \ - utils/helpers/pci.o utils/helpers/bitmask.o \ + utils/helpers/pci.o utils/helpers/bitmask.o utils/helpers/cppc.o \ utils/idle_monitor/nhm_idle.o utils/idle_monitor/snb_idle.o \ utils/idle_monitor/hsw_ext_idle.o \ utils/idle_monitor/amd_fam14h_idle.o utils/idle_monitor/cpuidle_sysfs.o \ @@ -236,9 +236,9 @@ $(OUTPUT)%.o: %.c $(OUTPUT)cpupower: $(UTIL_OBJS) $(OUTPUT)$(LIBCPUPOWER) $(ECHO) " CC " $@ ifeq ($(strip $(STATIC)),true) - $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lrt -lpci -L$(OUTPUT) -o $@ + $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lm -lrt -lpci -L$(OUTPUT) -o $@ else - $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lcpupower -lrt -lpci -L$(OUTPUT) -o $@ + $(QUIET) $(CC) $(CFLAGS) $(LDFLAGS) $(UTIL_OBJS) -lm -lcpupower -lrt -lpci -L$(OUTPUT) -o $@ endif $(QUIET) $(STRIPCMD) $@ diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index 5a242b491a9d..11629ae49f98 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -270,10 +270,10 @@ static int get_freq_hardware(unsigned int cpu, unsigned int human) { unsigned long freq; - if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF)) + freq = cpufreq_get_freq_hardware(cpu); + if (!(cpupower_cpu_info.caps & CPUPOWER_CAP_APERF) && !freq) return -EINVAL; - freq = cpufreq_get_freq_hardware(cpu); printf(_(" current CPU frequency: ")); if (!freq) { printf("Unable to call hardware\n"); @@ -477,12 +477,13 @@ static int get_latency(unsigned int cpu, unsigned int human) } /* --performance / -c */ - static int get_perf_cap(unsigned int cpu) { if (cpupower_cpu_info.vendor == X86_VENDOR_AMD && cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATE) amd_pstate_show_perf_and_freq(cpu, no_rounding); + else + cppc_show_perf_and_freq(cpu, no_rounding); return 0; } @@ -513,8 +514,8 @@ static void debug_output_one(unsigned int cpu) get_available_governors(cpu); get_policy(cpu); - if (get_freq_hardware(cpu, 1) < 0) - get_freq_kernel(cpu, 1); + get_freq_hardware(cpu, 1); + get_freq_kernel(cpu, 1); get_boost_mode(cpu); get_perf_cap(cpu); } diff --git a/tools/power/cpupower/utils/helpers/cppc.c b/tools/power/cpupower/utils/helpers/cppc.c new file mode 100644 index 000000000000..3493ce8551ea --- /dev/null +++ b/tools/power/cpupower/utils/helpers/cppc.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "helpers/helpers.h" +#include "cpufreq.h" +#include "acpi_cppc.h" + +#define cppc_to_frequency(perf) (roundf(slope * (perf) + intercept)) + +void cppc_show_perf_and_freq(unsigned int cpu, int no_rounding) +{ + int64_t nominal = acpi_cppc_get_data(cpu, NOMINAL_PERF); + int64_t nominal_freq = acpi_cppc_get_data(cpu, NOMINAL_FREQ) * 1000; + int64_t lowest = acpi_cppc_get_data(cpu, LOWEST_PERF); + int64_t lowest_freq = acpi_cppc_get_data(cpu, LOWEST_FREQ) * 1000; + unsigned long non_linear = acpi_cppc_get_data(cpu, LOWEST_NONLINEAR_PERF); + unsigned long highest = acpi_cppc_get_data(cpu, HIGHEST_PERF); + float slope, intercept; + + /* do the optional freq fields look invalid? */ + if (!nominal_freq || !lowest_freq || nominal == lowest) + return; + + slope = (float)(nominal_freq - lowest_freq) / (nominal - lowest); + intercept = lowest_freq - slope * lowest; + + printf(_(" CPPC limits:\n")); + printf(_(" Highest Performance: %lu. Maximum Frequency: "), + highest); + /* + * If boost isn't active, the cpuinfo_max doesn't indicate real max + * frequency. + */ + print_speed(cppc_to_frequency(highest), no_rounding); + printf(".\n"); + + printf(_(" Nominal Performance: %lu. Nominal Frequency: "), + acpi_cppc_get_data(cpu, NOMINAL_PERF)); + print_speed(nominal_freq, no_rounding); + printf(".\n"); + + printf(_(" Lowest Non-linear Performance: %lu. Lowest Non-linear Frequency: "), + non_linear); + print_speed(cppc_to_frequency(non_linear), no_rounding); + printf(".\n"); + + printf(_(" Lowest Performance: %lu. Lowest Frequency: "), + acpi_cppc_get_data(cpu, LOWEST_PERF)); + print_speed(lowest_freq, no_rounding); + printf(".\n"); +} diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index a3ad80b9c2c2..9c5126b63966 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -221,4 +221,6 @@ void print_online_cpus(void); void print_offline_cpus(void); void print_speed(unsigned long speed, int no_rounding); +void cppc_show_perf_and_freq(unsigned int cpu, int no_rounding); + #endif /* __CPUPOWERUTILS_HELPERS__ */ diff --git a/tools/power/cpupower/utils/powercap-info.c b/tools/power/cpupower/utils/powercap-info.c index e53033488218..88a5edb76315 100644 --- a/tools/power/cpupower/utils/powercap-info.c +++ b/tools/power/cpupower/utils/powercap-info.c @@ -47,8 +47,6 @@ static int powercap_print_one_zone(struct powercap_zone *zone) printf("\n"); - if (ret != 0) - return ret; return ret; } diff --git a/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py index 38cfbdcdedb7..64001bc80f68 100755 --- a/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py +++ b/tools/power/x86/intel_pstate_tracer/intel_pstate_tracer.py @@ -32,8 +32,6 @@ import re import signal import sys import getopt -import Gnuplot -from numpy import * from decimal import * __author__ = "Srinivas Pandruvada" @@ -88,8 +86,8 @@ def print_help(driver_name): print(' kbytes: Kilo bytes of memory per CPU to allocate to the trace buffer. Default: 10240') print(' Output:') print(' If not already present, creates a "results/test_name" folder in the current working directory with:') - print(' cpu.csv - comma seperated values file with trace contents and some additional calculations.') - print(' cpu???.csv - comma seperated values file for CPU number ???.') + print(' cpu.csv - comma separated values file with trace contents and some additional calculations.') + print(' cpu???.csv - comma separated values file for CPU number ???.') print(' *.png - a variety of PNG format plot files created from the trace contents and the additional calculations.') print(' Notes:') print(' Avoid the use of _ (underscore) in test names, because in gnuplot it is a subscript directive.') @@ -295,6 +293,8 @@ def common_all_gnuplot_settings(output_png): def common_gnuplot_settings(): """ common gnuplot settings. """ + import Gnuplot + g_plot = Gnuplot.Gnuplot(persist=1) # The following line is for rigor only. It seems to be assumed for .csv files g_plot('set datafile separator \",\"') @@ -343,7 +343,7 @@ def store_csv(cpu_int, time_pre_dec, time_post_dec, core_busy, scaled, _from, _t graph_data_present = True; def split_csv(current_max_cpu, cpu_mask): - """ seperate the all csv file into per CPU csv files. """ + """ separate the main csv file into per CPU csv files. """ if os.path.exists('cpu.csv'): for index in range(0, current_max_cpu + 1): @@ -482,7 +482,7 @@ def read_trace_data(filename, cpu_mask): if cpu_int > current_max_cpu: current_max_cpu = cpu_int # End of for each trace line loop -# Now seperate the main overall csv file into per CPU csv files. +# Now separate the main overall csv file into per CPU csv files. split_csv(current_max_cpu, cpu_mask) def signal_handler(signal, frame): @@ -508,8 +508,6 @@ if __name__ == "__main__": valid1 = False valid2 = False - cpu_mask = zeros((MAX_CPUS,), dtype=int) - try: opts, args = getopt.getopt(sys.argv[1:],"ht:i:c:n:m:",["help","trace_file=","interval=","cpu=","name=","memory="]) except getopt.GetoptError: @@ -538,6 +536,10 @@ if __name__ == "__main__": print_help('intel_pstate') sys.exit() + from numpy import zeros + + cpu_mask = zeros((MAX_CPUS,), dtype=int) + if cpu_list: for p in re.split("[,]", cpu_list): if int(p) < MAX_CPUS : diff --git a/tools/testing/selftests/cpufreq/governor.sh b/tools/testing/selftests/cpufreq/governor.sh index fe37df79c087..cf59e63f8e14 100755 --- a/tools/testing/selftests/cpufreq/governor.sh +++ b/tools/testing/selftests/cpufreq/governor.sh @@ -16,6 +16,12 @@ source cpufreq.sh CUR_GOV= CUR_FREQ= +# Per-policy backup, keyed by policy so multiple policies can be saved at once +# (backup_governor/restore_governor also keep CUR_GOV/CUR_FREQ for callers that +# read them directly). +declare -A SAVED_GOVERNORS +declare -A SAVED_FREQS + # Find governor's directory path # $1: policy, $2: governor find_gov_directory() @@ -39,11 +45,13 @@ find_current_governor() backup_governor() { CUR_GOV=$(find_current_governor $1) + SAVED_GOVERNORS[$1]=$CUR_GOV printf "Governor backup done for $1: $CUR_GOV\n" if [ $CUR_GOV == "userspace" ]; then CUR_FREQ=$(find_current_freq $1) + SAVED_FREQS[$1]=$CUR_FREQ printf "Governor frequency backup done for $1: $CUR_FREQ\n" fi @@ -53,11 +61,13 @@ backup_governor() # $1: policy restore_governor() { + CUR_GOV=${SAVED_GOVERNORS[$1]} __switch_governor $1 $CUR_GOV printf "Governor restored for $1 to $CUR_GOV\n" if [ $CUR_GOV == "userspace" ]; then + CUR_FREQ=${SAVED_FREQS[$1]} set_cpu_frequency $1 $CUR_FREQ printf "Governor frequency restored for $1: $CUR_FREQ\n" fi @@ -65,6 +75,17 @@ restore_governor() printf "\n" } +# Save/restore governors for every policy at once +save_all_governors() +{ + for_each_policy backup_governor +} + +restore_all_governors() +{ + for_each_policy restore_governor +} + # param: # $1: policy, $2: governor __switch_governor() @@ -100,11 +121,6 @@ switch_governor() # $1: policy, $2: governor switch_show_governor() { - cur_gov=find_current_governor - if [ $cur_gov == "userspace" ]; then - cur_freq=find_current_freq - fi - # switch governor __switch_governor $1 $2 diff --git a/tools/testing/selftests/cpufreq/special-tests.sh b/tools/testing/selftests/cpufreq/special-tests.sh index 8d40505dc468..e87ed7c8e5e5 100755 --- a/tools/testing/selftests/cpufreq/special-tests.sh +++ b/tools/testing/selftests/cpufreq/special-tests.sh @@ -40,7 +40,9 @@ simple_lockdep() { printf "** Test: Running ${FUNCNAME[0]} **\n" + save_all_governors for_each_policy __simple_lockdep + restore_all_governors } # Test 2 @@ -56,7 +58,10 @@ concurrent_lockdep() { printf "** Test: Running ${FUNCNAME[0]} **\n" + save_all_governors for_each_policy_concurrent __concurrent_lockdep + wait + restore_all_governors } # Test 3 @@ -65,20 +70,26 @@ quick_shuffle() # this is called concurrently from governor_race for I in `seq 1000` do - echo ondemand | sudo tee $CPUFREQROOT/policy*/scaling_governor & - echo userspace | sudo tee $CPUFREQROOT/policy*/scaling_governor & + echo ondemand | tee $CPUFREQROOT/policy*/scaling_governor & + echo userspace | tee $CPUFREQROOT/policy*/scaling_governor & done + wait } governor_race() { printf "** Test: Running ${FUNCNAME[0]} **\n" + save_all_governors + # run 8 concurrent instances for I in `seq 8` do quick_shuffle & done + wait + + restore_all_governors } # Test 4 @@ -112,5 +123,8 @@ hotplug_with_updates_cpu() hotplug_with_updates() { + save_all_governors for_each_non_boot_cpu hotplug_with_updates_cpu + wait + restore_all_governors } |
