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:
@@ -32,6 +32,13 @@ settle — measurably worse than stock. Measured on iz-pve0: stock iDRAC held on
|
||||
minutes off the same signal. A request for full speed is never held back, and the held
|
||||
value is re-pushed every `FAN_REASSERT_INTERVAL` in case the BMC forgets it.
|
||||
|
||||
A deadband alone makes a machine permanently louder: it catches the fan on the way up and
|
||||
then nothing brings it down until the request falls a full deadband. Measured on `iz-pve0`
|
||||
under load, 17 of 20 samples ran at 38% while the curve wanted 34-36%. So a re-assert -
|
||||
and only a re-assert - is allowed to settle the speed **downward** to what is actually
|
||||
wanted. Upward moves still need a full deadband crossing, which is what stops this
|
||||
reintroducing the drift it was built to prevent.
|
||||
|
||||
Note that iDRAC's own profile regulates to a target *RPM* (closed loop, rock steady),
|
||||
while manual control sets a *PWM* percentage (open loop, so measured RPM still wanders a
|
||||
percent or two at a fixed setpoint). That residual is inherent to manual control, not
|
||||
|
||||
18
fan_speed.sh
18
fan_speed.sh
@@ -117,6 +117,14 @@ selftest() {
|
||||
# leak is what let the setpoint drift 32 -> 36 -> 31 on iz-pve0.
|
||||
check "re-assert re-pushes held value" 32 "$(next_fan_speed 34 32)"
|
||||
|
||||
echo "settling downward (damping must not make the machine permanently louder):"
|
||||
check "holds above the want between re-asserts" 38 "$(settle_fan_speed 34 38 0)"
|
||||
check "settles down to the want on re-assert" 34 "$(settle_fan_speed 34 38 1)"
|
||||
check "small rise still held on re-assert" 38 "$(settle_fan_speed 42 38 1)"
|
||||
check "real rise still adopted" 46 "$(settle_fan_speed 46 38 1)"
|
||||
# The old upward leak: a re-assert must never drift the setpoint up.
|
||||
check "no upward drift on re-assert" 32 "$(settle_fan_speed 36 32 1)"
|
||||
|
||||
echo "trend verdict (load is not a fault - only cooling that cannot keep up is):"
|
||||
TREND_SAMPLES=5
|
||||
HIGH_FAN_SPEED=50
|
||||
@@ -293,12 +301,12 @@ one_pass() {
|
||||
|
||||
# Hold the current speed unless the change is worth making - see the deadband
|
||||
# note in monitor.sh. Chasing every degree is what made this louder than stock.
|
||||
local target now held=""
|
||||
target=$(next_fan_speed "$DECIMAL_CURRENT_FAN_SPEED" "$APPLIED_FAN_SPEED")
|
||||
[ "$target" = "$DECIMAL_CURRENT_FAN_SPEED" ] || held=" held, want $DECIMAL_CURRENT_FAN_SPEED"
|
||||
local target now due=0 held=""
|
||||
now=$(date +%s)
|
||||
if [ "$target" != "$APPLIED_FAN_SPEED" ] ||
|
||||
[ $((now - APPLIED_FAN_SPEED_AT)) -ge "$FAN_REASSERT_INTERVAL" ]; then
|
||||
[ $((now - APPLIED_FAN_SPEED_AT)) -ge "$FAN_REASSERT_INTERVAL" ] && due=1
|
||||
target=$(settle_fan_speed "$DECIMAL_CURRENT_FAN_SPEED" "$APPLIED_FAN_SPEED" "$due")
|
||||
[ "$target" = "$DECIMAL_CURRENT_FAN_SPEED" ] || held=" held, want $DECIMAL_CURRENT_FAN_SPEED"
|
||||
if [ "$target" != "$APPLIED_FAN_SPEED" ] || [ "$due" = 1 ]; then
|
||||
apply_user_fan_control "$target"
|
||||
APPLIED_FAN_SPEED=$target
|
||||
APPLIED_FAN_SPEED_AT=$now
|
||||
|
||||
19
monitor.sh
19
monitor.sh
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user