tuxedocomputers/tuxedo-control-center

TCC does not set scaling governor and energy performance preference correctly (Pulse 14 Gen3)

Opened this issue · 2 comments

Hey there,

I observed some unexpected behavior of how TCC sets CPU scaling governor and energy performance profiles.

System Info

  • Device: Pulse 14 Gen3
  • OS: EndeavourOS
  • Kernel: 6.7.8-arch1-1

CPU Governor and EPP:

Both obtained via /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor and /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference

Profile Scaling Governor Energy Performance Preference
TUXEDO Defaults powersave balance_performance
Default powersave balance_performance
Cool and breezy ondemand No such file or directory
Powersave extreme ondemand No such file or directory

Logs:

Obtained with journalctl -u tccd -f:

TUXEDO Defaults:

Mar 07 08:55:40 tuxedo tccd[361863]: StateSwitcherWorker: Temp profile 'TUXEDO Defaults' (__default_custom_profile__) selected
Mar 07 08:55:41 tuxedo tccd[361863]: CpuWorker: Failed to set default cpu config => Error: Could not write value 'default' to path: /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference => Error: EINVAL: invalid argument, write

Default:

Mar 07 08:56:13 tuxedo tccd[361863]: StateSwitcherWorker: Temp profile 'Default' (__legacy_default__) selected
Mar 07 08:56:14 tuxedo tccd[361863]: CpuWorker: Failed to set default cpu config => Error: Could not write value 'default' to path: /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference => Error: EINVAL: invalid argument, write ```

Cool and breezy:

Mar 07 08:56:27 tuxedo tccd[361863]: StateSwitcherWorker: Temp profile 'Cool and breezy' (__legacy_cool_and_breezy__) selected
Mar 07 08:56:28 tuxedo tccd[361863]: CpuWorker: Failed to set default cpu config => Error: Could not write value 'default' to path: /sys/devices/system/cpu/cpu0/cpufreq/energy_performance_preference => Error: EINVAL: invalid argument, write

Powersave extreme:

Mar 07 08:57:45 tuxedo tccd[361863]: StateSwitcherWorker: Temp profile 'Powersave extreme' (__legacy_powersave_extreme__) selected
Mar 07 08:57:46 tuxedo tccd[361863]: Set display brightness to 60% (153) on amdgpu_bl1

Investigating the TUXEDO Defaults profile

Let's look at the "TUXEDO Defaults" profile. The default governor is specified here:

governor: 'powersave', // unused: see CpuWorker.ts->applyCpuProfile(...)

but apparently this variable is unused and we need to check the logic in CpuWorker.ts->applyCpuProfile(...) over at

private applyCpuProfile(profile: ITccProfile) {

Further down this method, we can find

if (!profile.cpu.useMaxPerfGov) {
// Note: Hard set governor to default (not included in profiles atm)
profile.cpu.governor = this.findDefaultGovernor();
this.cpuCtrl.setGovernor(profile.cpu.governor);
this.cpuCtrl.setEnergyPerformancePreference(profile.cpu.energyPerformancePreference);

which first finds the "default" governor.

This is done by checking the scaling driver at /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver

public findDefaultGovernor(): string {
let chosenName: string;
try {
let scalingDriver: string;
if (this.cpuCtrl.cores[0].scalingDriver.isAvailable()) {
scalingDriver = this.cpuCtrl.cores[0].scalingDriver.readValueNT();
}

Mine is:

$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver
amd-pstate-epp

It then checks the special case intel_pstate and if its not intel_pstate it continues to find the scalingAvailableGovernors (/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors) and selects the first governor in CpuWorker.preferredAcpiFreqGovernors (one of [ 'ondemand', 'schedutil', 'conservative' ])

if (scalingDriver === 'intel_pstate') {
// Fixed 'powersave' governor for intel_pstate
return 'powersave';
} else {
// Preferred governors list for other drivers, mainly 'acpi-cpufreq'.
// Also includes 'intel_cpufreq' which according to kernel.org doc on intel_pstate
// behaves as the acpi-cpufreq governors.
const availableGovernors = this.cpuCtrl.cores[0].scalingAvailableGovernors.readValue();
for (const governorName of this.preferredAcpiFreqGovernors) {
if (availableGovernors.includes(governorName)) {
chosenName = governorName;
break;
}
}
return chosenName;

Preferred acpi freq governors in CpuWorker:

private readonly preferredAcpiFreqGovernors = [ 'ondemand', 'schedutil', 'conservative' ];

On my system, the available scaling governors are

$ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
performance powersave

So this matches none of the CpuWorker.preferredAcpiFreqGovernors which makes CpuWorker.findDefaultGovernor() return chosenName = undefined at

const availableGovernors = this.cpuCtrl.cores[0].scalingAvailableGovernors.readValue();
for (const governorName of this.preferredAcpiFreqGovernors) {
if (availableGovernors.includes(governorName)) {
chosenName = governorName;
break;
}
}
return chosenName;

Then the profile will have an undefined governor

profile.cpu.governor = this.findDefaultGovernor();
this.cpuCtrl.setGovernor(profile.cpu.governor);

which calls CpuController.setGovernor(governor) and does nothing since it is undefined:

public setGovernor(governor?: string) {
if (governor === undefined) {
return;
}

Since applyCpuProfile calls setCpuDefaultConfig before applying any settings, this should then end up with the defaults:

But setCpuDefaultConfig itself calls this.findDefaultGovernor which again returns undefined:

this.cpuCtrl.setGovernor(this.findDefaultGovernor());

Suggestions

Logic needs to be implemented for the case that findDefaultGovernor does not match any of the defined governors here https://github.com/tuxedocomputers/tuxedo-control-center/blob/3006a3a4df7baeeb5a9abe817a94358ae2f903eb/src/service-app/classes/CpuWorker.ts#L30C22-L30C48

I'm also surprised, that none of them is performance?

I'm not sure what happened with energy_performance_preference in the case of the Cool and breezy profile: apparently the file is deleted? The profile is defined in a file called LegacyProfiles.ts and I didn't want to start investigating what happens with something that you already deem as Legacy.

Anyway, I'm switching away from TCC to auto-cpufreq or tlp until this is fixed. I hope I could help. Let me know if you need any further information from my system or how TCC behaves on my system.

Hello,

thanks for the detailed investigation.

I had a look and there does seem to be missing the case where behaviour for the scaling driver amd-pstate-epp should be handled like intel_pstate. I will fix this but I don't think it will fix nor explain the behaviour that is causing your confusion.

Here's an attempt to explain what is happening.

The amd-pstate-epp scaling driver is relatively new and behaves similarly to the intel_pstate one in that

  • it has two scaling_governors: powersave and performance
  • it has an energy_performance_preference (epp) knob

However, in this mode the scaling_min_freq and scaling_max_freq (even though writable) do not have any effect. Turns out you can change a status knob to "passive" to switch out of this mode. Therefore current logic in TCC is if min/max freq is used to restrict frequencies the status will be set to "passive", otherwise remain in active mode.

The consequence of the "passive" mode which allows the change is that the scaling_driver is set to amd-pstate instead. With this the governors are replaced by the traditional acpi-cpufreq governors. In this state the TCC logic chooses one known available governor where cpu frequency limitation is known possible (first choice ondemand).

Further, the epp knob for the amd-pstate-epp scaling driver does list a possible "default" value option (which TCC writes to enforce a default value). Writing to it, however, yields an error. This, as far as I can see, is a bug in the driver.

Coming back to the topic

TCC does not set scaling governor and energy performance preference correctly

what would be correct?

Thanks for the response!

I had a look and there does seem to be missing the case where behaviour for the scaling driver amd-pstate-epp should be handled like intel_pstate.

Exactly, I just only read up on amd_pstate_epp yesterday evening and thought that this might be missing in TCC.

TCC does not set scaling governor and energy performance preference correctly

what would be correct?

I understand, that it is not simple, but from a user perspective, I would enjoy having the following two ways of setting up a TCC profile (the "System Performance" part at least):

  • A toggle between "Simple" and "Advanced" configuration
  • Simple:
    • Have three modes: "Low-power", "Balanced", "Performance" (basically what you have right now)
    • This sets the appropriate scaling driver, governor, and (if available) epp settings based off this phoronix benchmark
    • No other configurations
  • Advanced:
    • Expose all the options and let the user configure everything on their own, that is
      • scaling driver
      • scaling governor (depending on scaling driver)
      • epp setting (depending on scaling driver)
      • min/max freq. (also depneding on scaling driver as its e.g., as you mentioned, ignored if it's amd_pstate_epp), number of logical cores

The issue I see with how it is implemented right now is that there are conflicting options. E.g. one can choose "System Profile: Low-power" but set "Maximum performance" to True (I guess this chooses performance as governor?). It is not really clear what "System Profile" (even after reading the short description on mouse hover) sets. Also, looking at the predefined TCC profiles, all of them set the "System Profile" to "Performance" -- this probably adds to the confusion.

But maybe this also goes against your ideas of how the TCC should work and which low-level options it should expose to the user or how abstract the concepts should be. Anyway, I'm glad that I could help identifying the missing amd_pstate_epp case :-)