Let the fan settle downward on a re-assert

A symmetric deadband catches the fan on the way up and holds it there:
on iz-pve0 under load, 17 of 20 samples ran at 38% while the curve
wanted 34-36%, and nothing would bring it down until the request fell a
full 8 points. Damping was turning into a permanently louder machine.

A re-assert may now settle the speed downward to the current request.
Upward moves still require a full deadband crossing, so the drift this
was built to prevent cannot come back - covered by a test.

Verified under a live migration: settled 38 -> 36 on the first
re-assert, then one change in ten minutes while the request wandered
36-42, CPU stable at 66-69c and disks a degree cooler.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Zeb Hering
2026-08-28 22:33:38 -07:00
parent be440e95a3
commit 4dffbb070c
3 changed files with 39 additions and 5 deletions

View File

@@ -97,6 +97,25 @@ next_fan_speed() {
fi
}
# settle_fan_speed <wanted> <applied> <re-assert due, 0|1>
#
# A symmetric deadband catches the fan on the way up and then holds it there:
# measured on iz-pve0 under load, 17 of 20 samples ran at 38% while the curve
# wanted 34-36%, and nothing would bring it down until the request fell a full
# deadband. That is how damping turns into a permanently louder machine.
#
# So on a re-assert - and only then - let the speed settle DOWNWARD to what is
# actually wanted. Upward moves still require a full deadband crossing, so this
# cannot bring back the leak that let the setpoint drift 32 -> 36 -> 31.
settle_fan_speed() {
local wanted=$1 applied=$2 due=$3 target
target=$(next_fan_speed "$wanted" "$applied")
if [ "$due" = 1 ] && [ -n "$applied" ] && [ "$wanted" -lt "$target" ]; then
target=$wanted
fi
echo "$target"
}
ALERT_EMAIL=${ALERT_EMAIL:-Servers@ntfy1.izebra.xyz}
ALERT_COOLDOWN=${ALERT_COOLDOWN:-3600}
STATE_DIR=${STATE_DIR:-/root/fan_speed/state}