cpufreqd: only log transitions that actually happened; skip dwell on read-only

In read-only mode (detected VM/QEMU/KVM) apply_pstate is a no-op
so c.current_idx never advances. The previous log line was
emitted whenever the *requested* target (n) differed from
current_idx, regardless of whether the write actually fired —
on QEMU that produced thousands of P0->P1 lines per boot even
though no transition ever took place.

Gate the info!() on whether current_idx actually changed. Also
skip the dwell accumulation entirely on read-only hosts: writes
cannot take effect, so the hysteresis counter is meaningless.
The governor still tracks the target so the load value
reflects real demand, but no work fires per poll.

Closes Phase H of the LG Gram 16 (2025) S/P-state work.
This commit is contained in:
2026-07-01 00:03:32 +03:00
parent b8b348b11e
commit 4ded365124
@@ -463,7 +463,10 @@ fn main() {
// P0->P1->P0 oscillation on idle systems (QEMU and
// real hardware with stable 0% load) where the governor
// would otherwise toggle the state every poll cycle.
if n != c.current_idx {
// On read-only hosts (detected VM/QEMU) writes cannot
// take effect, so there is no point accumulating dwell
// — skip the check entirely.
if !c.read_only && n != c.current_idx {
if n == c.dwell_target {
c.dwell = c.dwell.saturating_add(1);
} else {
@@ -474,22 +477,28 @@ fn main() {
continue; // not enough polls at this target yet
}
} else {
// Same state as last poll: reset dwell counter (no transition
// was requested so dwell stays at 0).
// Same state as last poll (or read-only host): reset
// dwell counter (no transition was requested so dwell
// stays at 0).
c.dwell = 0;
}
if n != c.current_idx && n < c.pstates.len() {
let prev_idx = c.current_idx;
let prev_freq = c.pstates[c.current_idx].freq_khz;
let next_freq = c.pstates[n].freq_khz;
let l_pct = l * 100.0;
match c.mode {
PstateMode::Hwp => {
apply_pstate(c, n);
info!("CPU{} HWP→{}% ({}→{} kHz, load={:.0}%)", c.id, c.hwp_max.saturating_sub(n as u8 * (c.hwp_max - c.hwp_min) / c.pstates.len().saturating_sub(1).max(1) as u8), prev_freq, next_freq, l_pct);
if c.current_idx != prev_idx {
info!("CPU{} HWP→{}% ({}→{} kHz, load={:.0}%)", c.id, c.hwp_max.saturating_sub(n as u8 * (c.hwp_max - c.hwp_min) / c.pstates.len().saturating_sub(1).max(1) as u8), prev_freq, next_freq, l_pct);
}
}
PstateMode::LegacyPerfCtl => {
apply_pstate(c, n);
info!("CPU{}: P{}→P{} ({}→{} kHz, load={:.0}%)", c.id, c.current_idx, n, prev_freq, next_freq, l_pct);
if c.current_idx != prev_idx {
info!("CPU{}: P{}→P{} ({}→{} kHz, load={:.0}%)", c.id, prev_idx, n, prev_freq, next_freq, l_pct);
}
}
}
}