* [Bug 221795] New: amd-pstate: max_perf stuck at nominal_perf after boot on Ryzen AI 9 HX 370
@ 2026-07-26 11:34 bugzilla-daemon
2026-07-26 11:37 ` [Bug 221795] " bugzilla-daemon
0 siblings, 1 reply; 2+ messages in thread
From: bugzilla-daemon @ 2026-07-26 11:34 UTC (permalink / raw)
To: linux-pm
https://bugzilla.kernel.org/show_bug.cgi?id=221795
Bug ID: 221795
Summary: amd-pstate: max_perf stuck at nominal_perf after boot
on Ryzen AI 9 HX 370
Product: Power Management
Version: 2.5
Hardware: All
OS: Linux
Status: NEW
Severity: normal
Priority: P3
Component: cpufreq
Assignee: linux-pm@vger.kernel.org
Reporter: mogdad@yahoo.Com
Regression: No
Created attachment 310511
--> https://bugzilla.kernel.org/attachment.cgi?id=310511&action=edit
a full state dump taken in the performance profile: uname, cmdline, firmware
version, cpupower frequency-info, lscpu -ae, every relevant cpufreq and
acpi_cppc sysfs value, MSR HWCR, and the system
==============================================================================
FORM FIELDS
==============================================================================
Product: Power Management
Component: cpufreq
Kernel Version: 7.1.4-1-cachyos
Hardware: x86-64
OS: Linux
Severity: normal
Summary: amd-pstate: max_perf stuck at nominal_perf after boot,
single-threaded frequency capped to base clock (Ryzen AI 9 HX 370)
(If "Power Management / cpufreq" is not available in the dropdown, use
Product "Drivers" and the closest power-management component.)
Suggested CC: the amd-pstate maintainers listed in MAINTAINERS
(Mario Limonciello, Perry Yuan, Gautham R. Shenoy)
==============================================================================
DESCRIPTION
==============================================================================
Note: I am not a kernel developer. My laptop felt slower than it should and I
worked through the diagnosis with an AI assistant, which suggested the tests
and helped interpret the output. All measurements below were taken on my own
machine and are reproducible; the interpretations may be wrong. Corrections
welcome, and I am happy to re-run anything or test patches.
Also note: this is a CachyOS kernel, which carries out-of-tree patches. I have
not yet confirmed the behaviour on a vanilla mainline kernel. Please treat the
report accordingly; I can test a vanilla build if that helps.
SUMMARY
-------
amd-pstate initialises correctly at boot: shortly after startup,
scaling_max_freq reads 5157895, the true hardware maximum. But by the time the
desktop session is up, scaling_max_freq has dropped to the nominal frequency
(2000000) on all 24 policies while cpuinfo_max_freq still reports 5157895.
Single-threaded workloads then run at exactly 2.0 GHz instead of ~4.9 GHz.
The trigger is a platform_profile change, which the desktop session performs
shortly after login. Every subsequent platform_profile change re-applies the
clamp. This happens on every boot and after every suspend/resume cycle, so in
practice the machine is always clamped by the time a user would notice.
Re-registering the driver (writing "guided" then "active" back to
/sys/devices/system/cpu/amd_pstate/status) restores the correct limits and
makes the system immune to the trigger for the rest of the session.
Impact: Geekbench 7 single-core went from 1100 to ~2450. Idle power management
also improved (package now reaches 2.1 W in power-saver, which it never did
before).
SYSTEM
------
Kernel: 7.1.4-1-cachyos
CPU: AMD Ryzen AI 9 HX 370 (Strix Point, 4x Zen 5 + 8x Zen 5c,
12C/24T)
Machine: ASUS Zenbook S 16 UM5606WA
BIOS: UM5606WA.321, 10/14/2025
amd_pstate/status: active (amd-pstate-epp)
Governor: performance
EPP: performance
platform_profile: performance
cpufreq/boost: 1 (global and per-policy)
Kernel cmdline: quiet nowatchdog splash rw root=UUID=...
(no amd_pstate parameter)
Running: power-profiles-daemon, asusd
Not running: tlp; sched_ext disabled
SYMPTOM 1: single-threaded workloads capped at exactly nominal
--------------------------------------------------------------
Pinned to CPU 0 (a Zen 5 core, highest_perf = 208), continuous load, no
migration possible:
$ taskset -c 0 perf stat -e cycles,task-clock -- stress-ng --cpu 1 --timeout
10s
20 058 634 429 cycles:u
10 015,23 msec task-clock:u
20058634429 / 10.015 s = 2.003 GHz. The HX 370 base clock is 2.0 GHz.
SYMPTOM 2: the driver knows the real maximum but does not use it
----------------------------------------------------------------
$ grep .
/sys/devices/system/cpu/cpu0/cpufreq/{scaling_max_freq,cpuinfo_max_freq}
scaling_max_freq:2000000
cpuinfo_max_freq:5157895
All 24 policies report scaling_max_freq = 2000000.
Writing cpuinfo_max_freq back into scaling_max_freq on every policy is accepted
without error but silently reverted: a subsequent read still returns 2000000,
and the measured frequency stays at 2.002 GHz. The driver actively re-imposes
the clamp.
SYMPTOM 3: boost is enabled everywhere, including in firmware
--------------------------------------------------------------
$ cat /sys/devices/system/cpu/cpufreq/boost
1
$ grep . /sys/devices/system/cpu/cpufreq/policy*/boost | sort -u
... all report 1
$ sudo rdmsr -a 0xc0010015 | sort -u
1c9006011
MSR HWCR bit 25 (CpbDis) is 0 on every core, so Core Performance Boost is
enabled at firmware level. This is not a BIOS setting.
SYMPTOM 4: the clamp is not honoured under multi-core load
-----------------------------------------------------------
With scaling_max_freq = 2000000 on all 24 policies:
$ perf stat -e cycles,task-clock -- stress-ng --cpu 24 --timeout 10s
642 698 375 128 cycles:u
238 108,23 msec task-clock:u
642698375128 / 238.108 s = 2.70 GHz average per thread, i.e. above the
advertised 2.0 GHz ceiling, with observed peaks around 3.1 GHz.
Loading only two Zen 5 cores is more striking still (taskset -c 0,3,12,15,
i.e. cores 0x0 and 0x3 with their SMT siblings), as seen by turbostat:
Core CPU Bzy_MHz PkgWatt
- - 4334 18.80
0x0 0 4440 18.79
0x0 12 4440
0x3 3 4440
0x3 15 4440
4440 MHz with the ceiling still at 2000000. The switchover therefore happens
somewhere between one and two active cores: the SMU appears to honour the CPPC
max_perf hint only in the low-activity regime.
Multi-core performance is degraded too (2.7-3.1 GHz instead of ~4.9 GHz), just
not clamped to the exact nominal value the way single-threaded work is.
SYMPTOM 5: a multi-core load transiently lifts the clamp
---------------------------------------------------------
After running the two-core test above, a subsequent single-threaded pinned run
returned 4.808 GHz, while scaling_max_freq still read 2000000. So the sysfs
value does not faithfully reflect the max_perf actually written to
MSR_AMD_CPPC_REQ, and sufficient activity un-sticks the clamp on its own.
This makes the bug easy to miss and easy to lose while investigating: one
multi-core benchmark is enough to mask it until the next trigger.
SYMPTOM 6: before the driver reset, any platform_profile change re-triggers it
------------------------------------------------------------------------------
$ powerprofilesctl set balanced && sleep 2 && powerprofilesctl set
performance && sleep 2
$ taskset -c 0 perf stat -e cycles,task-clock -- stress-ng --cpu 1 --timeout
10s
20 030 590 526 cycles:u
10 013,24 msec task-clock:u -> 2.000 GHz
It also reproduces by writing the sysfs file directly, which rules out
power-profiles-daemon as the sole cause and points at the platform_profile
path itself (asus-wmi):
$ echo balanced | sudo tee /sys/firmware/acpi/platform_profile
$ sleep 2
$ echo performance | sudo tee /sys/firmware/acpi/platform_profile
$ sleep 2
$ taskset -c 0 perf stat -e cycles,task-clock -- stress-ng --cpu 1 --timeout
10s
20 050 594 801 cycles:u
10 016,86 msec task-clock:u -> 2.002 GHz
SYMPTOM 7: after re-registering the driver, the trigger no longer works
------------------------------------------------------------------------
$ echo guided | sudo tee /sys/devices/system/cpu/amd_pstate/status
$ sleep 1
$ echo active | sudo tee /sys/devices/system/cpu/amd_pstate/status
$ cat /sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq
5157895
Repeating the platform_profile toggle afterwards no longer degrades anything:
4.800 GHz on two threads, 4.867 GHz on one.
This suggests an initialisation ordering problem at boot: asusd and/or
power-profiles-daemon setting the platform profile while amd-pstate is still
initialising leaves the driver with an inconsistent limit table. Once the
driver is re-registered after everything has settled, it behaves correctly.
SYMPTOM 8: it does not survive suspend/resume, and resume is worse
-------------------------------------------------------------------
After a suspend/resume cycle the clamp comes back.
One further complication: cpuinfo_max_freq is not a reliable reference either.
It has been observed both ways in the clamped state - still correctly reporting
5157895 during the early investigation, and collapsed to 2000000 on later
occasions, both after resume and after boot once the session had applied its
power profile. When both values read 2000000, no sysfs file exposes the real
hardware maximum any more, and any workaround that compares the two to decide
whether action is needed becomes a silent no-op:
juil. 26 10:41:08 systemd[1]: Starting Reinitialise amd_pstate apres reprise
de veille...
juil. 26 10:41:10 amd-pstate-fix[10527]: Rien a faire : scaling_max_freq =
2000000 Hz
juil. 26 10:41:10 systemd[1]: amd-pstate-fix-resume.service: Deactivated
successfully.
(that message is only printed when scaling_max_freq >= cpuinfo_max_freq)
The re-registration has to be applied unconditionally on resume.
Update: once a re-registration has actually succeeded (see symptom 9), the
immunity appears to extend across suspend/resume. A later resume found the
limits already correct before the workaround even ran:
juil. 26 12:07:55 amd-pstate-fix[4723]: reference = 5157895 Hz, actuel =
5157895 Hz
On that reading, the clamp returning after resume was a consequence of the
driver never having been correctly re-registered in the first place, rather
than a separate resume-specific failure. This is a single observation so far.
SYMPTOM 9: the re-registration itself fails if applied too early after boot
---------------------------------------------------------------------------
Writing "guided" then "active" back to amd_pstate/status does not always work.
When the clamp is already present a few seconds into the boot sequence, the
write completes without error but the limits stay at nominal:
juil. 26 11:54:33 systemd[1]: Starting Reinitialise amd_pstate au
demarrage...
juil. 26 11:54:33 amd-pstate-fix[1120]: avant = 2000000 Hz (cpuinfo_max_freq
= 2000000 Hz)
juil. 26 11:54:35 amd-pstate-fix[1120]: apres = 2000000 Hz
The exact same command run manually a few minutes later, same boot, restored
5157895 immediately. The driver appears to rebuild its tables from whatever
CPPC state is current, and that state is still wrong immediately after boot.
Practical consequence: a workaround cannot rely on the write succeeding, and
has to retry until the limits actually come back.
JOURNAL EVIDENCE
----------------
The workaround script logs the value it finds, so the systemd journal records
the state automatically at each boot and each resume.
This is the clearest single piece of evidence, because both readings come from
the same boot. The service runs a few seconds after startup, before the desktop
session is up, and finds the limits correct:
juil. 26 11:38:41 systemd[1]: Starting Reinitialise amd_pstate apres asusd et
power-profiles-daemon...
juil. 26 11:38:41 amd-pstate-fix[1122]: Rien a faire : scaling_max_freq =
5157895 Hz
juil. 26 11:38:41 systemd[1]: Finished Reinitialise amd_pstate apres asusd et
power-profiles-daemon.
Reading the same file manually once the desktop has loaded, same boot, no
intervening action:
$ cat /sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq
2000000
So amd-pstate starts out with correct limits and loses them later in the boot
sequence, when the session applies a power profile. The service above found
nothing to do and therefore never re-registered the driver, which is why the
clamp was free to appear afterwards.
After a resume, the same service logs the clamped value directly. The message
shown is only printed when scaling_max_freq >= cpuinfo_max_freq, so on resume
both values had collapsed to 2000000:
juil. 26 10:41:08 systemd[1]: Starting Reinitialise amd_pstate apres reprise
de veille...
juil. 26 10:41:10 amd-pstate-fix[10527]: Rien a faire : scaling_max_freq =
2000000 Hz
juil. 26 10:41:10 systemd[1]: amd-pstate-fix-resume.service: Deactivated
successfully.
REPRODUCTION
------------
1. Boot normally with power-profiles-daemon and asusd running.
2. cat /sys/devices/system/cpu/cpufreq/policy0/scaling_max_freq
-> 2000000, while cpuinfo_max_freq -> 5157895.
3. taskset -c 0 perf stat -e cycles,task-clock -- stress-ng --cpu 1 --timeout
10s
-> ~2.0 GHz.
4. echo guided | sudo tee /sys/devices/system/cpu/amd_pstate/status
sleep 1
echo active | sudo tee /sys/devices/system/cpu/amd_pstate/status
5. Repeat step 3 -> ~4.9 GHz.
Important: step 3 must be run before any multi-core load, otherwise the clamp
lifts on its own (symptom 5) and the reproduction is lost.
Always reproduce in the "performance" profile. In "power-saver",
power-profiles-daemon sets per-policy boost to 0 and the cores cap at the
nominal frequency, which looks exactly like this bug (measured: 2.000 GHz with
boost = 0). That is expected behaviour and not what is reported here.
One observation muddied this and is worth recording. Immediately after a
successful re-registration, running "powerprofilesctl set power-saver" left
per-policy boost at 1 and a pinned single-threaded run measured 4.946 GHz. The
explanation, since verified, is that the machine was already in power-saver, so
the command was a no-op: the daemon does not re-apply an unchanged profile, and
the boost flag was simply still set from the re-registration. Repeating the
sequence deliberately - re-register while in power-saver, then set power-saver
again - reproduces boost = 1 and 4.895 GHz, whereas any real profile transition
into power-saver gives boost = 0 and 2.000 GHz.
Note also that per-policy boost has been observed at 1 while clamped in the
performance profile, so the correlation between the boost flag and the clamp is
not straightforward.
REFERENCE OUTPUT
----------------
Healthy state, performance profile (cpupower frequency-info, CPU 8, a Zen 5c
core):
driver: amd-pstate-epp
energy performance preference: performance
hardware limits: 605 MHz - 3.29 GHz
current policy: frequency should be within 605 MHz and 3.29 GHz.
boost state support:
Supported: yes
Active: yes
amd-pstate limits:
Highest Performance: 125. Maximum Frequency: 3.29 GHz.
Nominal Performance: 76. Nominal Frequency: 2.00 GHz.
Lowest Non-linear Performance: 23. Lowest Non-linear Frequency: 605 MHz.
Lowest Performance: 23. Lowest Frequency: 599 MHz.
Preferred Core Support: 0. Preferred Core Ranking: 0.
lscpu -ae in the same healthy state shows correctly differentiated maxima:
5157.8950 MHz on cores 0-3 (Zen 5), 3289.4741 MHz on cores 4-11 (Zen 5c).
In the degraded state, lscpu -ae reports MAXMHZ 2000.0000 on all 24 CPUs,
including the Zen 5c cores whose real maximum is 3.29 GHz. So this is a global
clamp to the nominal frequency, not "stuck at the Zen 5 nominal".
WORKAROUND
----------
Three requirements, all learned the hard way:
- the re-registration must be retried until it works (symptom 9)
- cpuinfo_max_freq cannot be used to decide whether action is needed, since
it collapses too (symptom 8), so the known-good maximum is remembered in a
small state file instead
- the systemd units need a long TimeoutStartSec, because the retry loop can
run for several minutes
/usr/local/bin/amd-pstate-fix:
#!/bin/sh
STATUS=/sys/devices/system/cpu/amd_pstate/status
POLICY=/sys/devices/system/cpu/cpufreq/policy0
REF=/var/lib/amd-pstate-fix.ref
ATTEMPTS=10
DELAY=20
force=0
[ "$1" = "--force" ] && force=1
cur=$(cat "$POLICY/scaling_max_freq")
cpuinfo=$(cat "$POLICY/cpuinfo_max_freq")
ref=0
[ -r "$REF" ] && ref=$(cat "$REF" 2>/dev/null)
[ -z "$ref" ] && ref=0
for v in "$cur" "$cpuinfo" "$ref"; do
[ "$v" -gt "$ref" ] && ref=$v
done
echo "$ref" > "$REF" 2>/dev/null
[ "$force" -eq 0 ] && [ "$cur" -ge "$ref" ] && exit 0
reset_driver() {
mode=$(cat "$STATUS")
case "$mode" in
guided) intermediate=passive ;;
*) intermediate=guided ;;
esac
echo "$intermediate" > "$STATUS"
sleep 1
echo "$mode" > "$STATUS"
sleep 1
}
i=1
while [ "$i" -le "$ATTEMPTS" ]; do
reset_driver
cur=$(cat "$POLICY/scaling_max_freq")
[ "$cur" -ge "$ref" ] && exit 0
i=$((i + 1))
[ "$i" -le "$ATTEMPTS" ] && sleep "$DELAY"
done
exit 1
Driven by two oneshot units:
# /etc/systemd/system/amd-pstate-fix.service
[Unit]
Description=Reinitialise amd_pstate at boot
After=multi-user.target power-profiles-daemon.service asusd.service
Wants=power-profiles-daemon.service
[Service]
Type=oneshot
ExecStartPre=/bin/sleep 10
ExecStart=/usr/local/bin/amd-pstate-fix --force
TimeoutStartSec=900
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/amd-pstate-fix-resume.service
[Unit]
Description=Reinitialise amd_pstate after resume
After=suspend.target hibernate.target hybrid-sleep.target
suspend-then-hibernate.target
[Service]
Type=oneshot
ExecStartPre=/bin/sleep 5
ExecStart=/usr/local/bin/amd-pstate-fix --force
TimeoutStartSec=900
[Install]
WantedBy=suspend.target hibernate.target hybrid-sleep.target
suspend-then-hibernate.target
Confirmed across a reboot and a suspend/resume cycle, with no manual
intervention: scaling_max_freq stays at 5157895 and a pinned single-threaded
run measures 49789468684 cycles / 10.005 s = 4.977 GHz.
POSSIBLY RELATED: prefcore is disabled
---------------------------------------
$ cat /sys/devices/system/cpu/amd_pstate/prefcore
disabled
$ cat /proc/sys/kernel/sched_itmt_enabled
cat: /proc/sys/kernel/sched_itmt_enabled: No such file or directory
This is with a clean kernel cmdline (no amd_prefcore=disable) and clearly
heterogeneous rankings:
$ grep . /sys/devices/system/cpu/cpu*/acpi_cppc/highest_perf | sort -t: -k2
-n
cpu4-11, cpu16-23 : 125 # Zen 5c
cpu1, cpu13 : 196 # Zen 5
cpu2, cpu14 : 202 # Zen 5
cpu0, cpu12 : 208 # Zen 5
cpu3, cpu15 : 208 # Zen 5
cpupower reports "Preferred Core Support: 0. Preferred Core Ranking: 0." on
every CPU, so the driver does not consider preferred-core support to be
advertised, despite the clearly per-core rankings above.
Two related sysfs values that may be relevant:
/sys/devices/system/cpu/cpu0/cpufreq/amd_pstate_highest_perf:196
/sys/devices/system/cpu/cpu0/acpi_cppc/highest_perf:208
/sys/devices/system/cpu/cpu0/acpi_cppc/guaranteed_perf:0
The driver caps highest_perf at 196 while ACPI reports 208 for the same core,
and guaranteed_perf reads 0 on every CPU.
Worth noting separately: amd_pstate_max_freq keeps the correct value (5157895)
even when cpuinfo_max_freq has collapsed to 2000000. It may be a more reliable
reference than cpuinfo_max_freq for anyone writing a workaround.
Practical impact is modest (pinning Geekbench 7 single-core to a preferred core
only gains ~4.6%, 2357 -> 2465), but it may share a root cause with the clamp,
since both involve amd-pstate misreading or mishandling the CPPC tables at
init.
POSSIBLY RELATED BUGS
---------------------
This may be a variant of the class of issues around per-policy boost_enabled
and max_perf handling in amd-pstate, e.g. the "prevent the per-policy
boost_enabled flag from getting out of sync with the actual state after boot
failures" fix that went into 6.14-rc2, and bug 218759 (highest_perf/prefcore
handling limiting performance on similar hardware).
ATTACHMENTS
-----------
Attached:
- a full state dump taken in the performance profile: uname, cmdline, firmware
version, cpupower frequency-info, lscpu -ae, every relevant cpufreq and
acpi_cppc sysfs value, MSR HWCR, and the systemd journal for the workaround
units across two boots and a resume
- acpidump
--
You may reply to this email to add a comment.
You are receiving this mail because:
You are the assignee for the bug.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-07-26 11:37 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-26 11:34 [Bug 221795] New: amd-pstate: max_perf stuck at nominal_perf after boot on Ryzen AI 9 HX 370 bugzilla-daemon
2026-07-26 11:37 ` [Bug 221795] " bugzilla-daemon
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox