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
|
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.
|
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),
|
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
|
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
|
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.
|
# 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)"
|
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):"
|
echo "trend verdict (load is not a fault - only cooling that cannot keep up is):"
|
||||||
TREND_SAMPLES=5
|
TREND_SAMPLES=5
|
||||||
HIGH_FAN_SPEED=50
|
HIGH_FAN_SPEED=50
|
||||||
@@ -293,12 +301,12 @@ one_pass() {
|
|||||||
|
|
||||||
# Hold the current speed unless the change is worth making - see the deadband
|
# 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.
|
# note in monitor.sh. Chasing every degree is what made this louder than stock.
|
||||||
local target now held=""
|
local target now due=0 held=""
|
||||||
target=$(next_fan_speed "$DECIMAL_CURRENT_FAN_SPEED" "$APPLIED_FAN_SPEED")
|
|
||||||
[ "$target" = "$DECIMAL_CURRENT_FAN_SPEED" ] || held=" held, want $DECIMAL_CURRENT_FAN_SPEED"
|
|
||||||
now=$(date +%s)
|
now=$(date +%s)
|
||||||
if [ "$target" != "$APPLIED_FAN_SPEED" ] ||
|
[ $((now - APPLIED_FAN_SPEED_AT)) -ge "$FAN_REASSERT_INTERVAL" ] && due=1
|
||||||
[ $((now - APPLIED_FAN_SPEED_AT)) -ge "$FAN_REASSERT_INTERVAL" ]; then
|
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"
|
apply_user_fan_control "$target"
|
||||||
APPLIED_FAN_SPEED=$target
|
APPLIED_FAN_SPEED=$target
|
||||||
APPLIED_FAN_SPEED_AT=$now
|
APPLIED_FAN_SPEED_AT=$now
|
||||||
|
|||||||
19
monitor.sh
19
monitor.sh
@@ -97,6 +97,25 @@ next_fan_speed() {
|
|||||||
fi
|
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_EMAIL=${ALERT_EMAIL:-Servers@ntfy1.izebra.xyz}
|
||||||
ALERT_COOLDOWN=${ALERT_COOLDOWN:-3600}
|
ALERT_COOLDOWN=${ALERT_COOLDOWN:-3600}
|
||||||
STATE_DIR=${STATE_DIR:-/root/fan_speed/state}
|
STATE_DIR=${STATE_DIR:-/root/fan_speed/state}
|
||||||
|
|||||||
Reference in New Issue
Block a user