Skip to content

cpu: fix cpu_power=0W on Zen 5 when cpu_temp is also enabled#2042

Open
Motaphe wants to merge 1 commit into
flightlessmango:masterfrom
Motaphe:fix/zenpower5-cpu-power-zen5
Open

cpu: fix cpu_power=0W on Zen 5 when cpu_temp is also enabled#2042
Motaphe wants to merge 1 commit into
flightlessmango:masterfrom
Motaphe:fix/zenpower5-cpu-power-zen5

Conversation

@Motaphe

@Motaphe Motaphe commented May 4, 2026

Copy link
Copy Markdown

Fixes #1794.

Problem

On Zen 5 hardware (Ryzen 7 9800X3D, kernel 7.0.3, zenpower5-dkms-git), cpu_power reports 0W whenever cpu_temp is also present in the config. Removing cpu_temp makes power display correctly. The data is readable in sysfs — the bug is entirely in MangoHud's sensor scan logic.

Root Cause

Three bugs interact:

1. zenpower5 power label not recognised

init_cpu_power_data_zenpower() searches only for SVI2 labels (SVI2_P_Core, SVI2_P_SoC) — the interface exposed by zenpower on Zen 2/3/4. zenpower5-dkms-git (Zen 5) exposes package power as a single RAPL reading instead:

power1_label = "RAPL_P_Package"
power1_input = 41000000   # µW, ~41W at idle

Neither SVI2 label is present, so find_input fails and the function returns nullptr.

2. Unconditional break blocks the zenergy fallback

} else if (name == "zenpower") {
    cpuPowerData = (CPUPowerData*)init_cpu_power_data_zenpower(path);
    break;  // exits even if init returned nullptr

When zenpower is enumerated before zenergy, the loop exits with cpuPowerData == nullptr. zenergy exports Esocket0 cumulative energy and would succeed as a fallback, but is never reached.

3. Why it correlates with cpu_temp

GetCpuFile(), called during cpu_temp init, opens files on the zenpower hwmon node. This warms the sysfs dcache entries for that device, altering readdir() bucket order on the subsequent InitCpuPowerData() call — zenpower now appears before zenergy. Without cpu_temp, zenergy appears first and succeeds immediately. With cpu_temp, zenpower comes first, returns nullptr, the unconditional break fires, and power stays 0W for the process lifetime.

Fix

init_cpu_power_data_zenpower — try SVI2 labels first (Zen 4 and older, code path unchanged). If not found, try RAPL_P_Package. Return nullptr only if neither is present.

get_cpu_power_zenpower — drop the early-return that required both corePowerFile and socPowerFile. When socPowerFile is null (Zen 5 single-RAPL case), socPower stays 0. The destructor already null-checks socPowerFile, so teardown is unaffected.

InitCpuPowerData loopbreakif (cpuPowerData) break; so a failed zenpower init lets the loop continue to zenergy.

Backward compatibility: Zen 4 and older zenpower (SVI2 labels present) takes the original code path unchanged — the RAPL_P_Package branch is only entered when SVI2 labels are absent.

Test

CPU:    Ryzen 7 9800X3D (Zen 5)
Kernel: 7.0.3-1-cachyos
Driver: zenpower5-dkms-git

Before: MANGOHUD_CONFIG="cpu_temp,cpu_power" mangohud glxgears  →  cpu_power = 0W
After:  MANGOHUD_CONFIG="cpu_temp,cpu_power" mangohud glxgears  →  cpu_power ≈ 41W ✓

MANGOHUD_CONFIG="cpu_power" (no cpu_temp) works correctly before and after.

zenpower5-dkms-git (Zen 5/9800X3D) exposes package power as
RAPL_P_Package on power1_input, not the SVI2 labels (SVI2_P_Core /
SVI2_P_SoC) that init_cpu_power_data_zenpower() was searching for.
This caused the function to return nullptr on Zen 5 hardware.

A second bug: InitCpuPowerData() unconditionally breaks out of the
hwmon enumeration loop after visiting a "zenpower" device, even when
init returned nullptr. This prevented the zenergy fallback from ever
being reached.

The correlation with cpu_temp: GetCpuFile() (called during cpu_temp
init) opens files on the zenpower hwmon node, altering the sysfs
readdir() bucket ordering so zenpower is enumerated first in the
subsequent power init scan, triggering the unconditional break before
zenergy is reached.

Fix:
- init_cpu_power_data_zenpower: try SVI2 labels first (Zen 4-);
  fall through to RAPL_P_Package if not found (Zen 5 / zenpower5)
- get_cpu_power_zenpower: make socPowerFile optional; when null
  (single RAPL reading), socPower stays 0
- InitCpuPowerData loop: change unconditional break to
  "if (cpuPowerData) break;" so zenergy is tried on zenpower failure

Fixes: flightlessmango#1794
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cpu_power 0 if cpu_temp enabled (on zen 5)

1 participant