Hold fan speed unless the change is worth making

The setpoint was a continuous function of instantaneous temperature and
was pushed to the BMC every pass, so ~1% of fan per degree of CPU noise
meant the fans never settled. Measured on iz-pve0: stock iDRAC held one
speed for 5 minutes through 57-59c jitter while this script made 7
changes in 16 minutes off the same signal.

Adds a 5% deadband around the applied speed, with full-speed requests
never held back and a periodic re-assert in case the BMC forgets.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Zeb Hering
2026-08-28 20:07:33 -07:00
parent 64e6e03dba
commit 56ae7542a2
3 changed files with 69 additions and 3 deletions

View File

@@ -82,6 +82,17 @@ selftest() {
echo "0,50,0,38,20" >> "$TREND_FILE"
check "partial window stays silent" 0 "$(rise_over_window 4)"
echo "fan speed deadband (hold unless the change is worth making):"
local now; now=$(date +%s)
HIGH_FAN_SPEED=50
should_apply_fan_speed 21 20 "$now" && check "1% drift holds" hold apply || check "1% drift holds" hold hold
should_apply_fan_speed 23 20 "$now" && check "3% drift holds" hold apply || check "3% drift holds" hold hold
should_apply_fan_speed 25 20 "$now" && check "5% rise applies" apply apply || check "5% rise applies" apply hold
should_apply_fan_speed 15 20 "$now" && check "5% fall applies" apply apply || check "5% fall applies" apply hold
should_apply_fan_speed 50 20 "$now" && check "full speed always applies" apply apply || check "full speed always applies" apply hold
should_apply_fan_speed 21 "" "$now" && check "first pass always applies" apply apply || check "first pass always applies" apply hold
should_apply_fan_speed 21 20 $((now - 400)) && check "stale setpoint is reasserted" apply apply || check "stale setpoint is reasserted" apply hold
echo "alert rate limiting:"
local out
out=$( { raise_alert testkey "first" "body"; raise_alert testkey "second" "body"; } | grep -c '^MAIL\[' )
@@ -180,6 +191,8 @@ fi
cache_disk_limits
echo "Monitoring $DISK_COUNT_EXPECTED disk(s)."
LAST_SMART_CHECK=0
APPLIED_FAN_SPEED=""
APPLIED_FAN_SPEED_AT=0
#######################################
#######################################
@@ -232,11 +245,21 @@ one_pass() {
FAN_SPEED_DRIVER="disk"
fi
apply_user_fan_control "$DECIMAL_CURRENT_FAN_SPEED"
# 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 held=""
if should_apply_fan_speed "$DECIMAL_CURRENT_FAN_SPEED" "$APPLIED_FAN_SPEED" "$APPLIED_FAN_SPEED_AT"; then
apply_user_fan_control "$DECIMAL_CURRENT_FAN_SPEED"
APPLIED_FAN_SPEED=$DECIMAL_CURRENT_FAN_SPEED
APPLIED_FAN_SPEED_AT=$(date +%s)
else
held=" held, want $DECIMAL_CURRENT_FAN_SPEED"
fi
COMMENT="CPU1:$CPU1_TEMPERATURE | CPU2:$CPU2_TEMPERATURE | GPU:$GPU_TEMPERATURE | Inlet:$INLET_TEMPERATURE | Exhaust:$EXHAUST_TEMPERATURE | Disk:$HOTTEST_DISK_TEMPERATURE/$HOTTEST_DISK_LIMIT($(basename $HOTTEST_DISK_DEVICE)) | Fan Speed:$DECIMAL_CURRENT_FAN_SPEED($FAN_SPEED_DRIVER)"
COMMENT="CPU1:$CPU1_TEMPERATURE | CPU2:$CPU2_TEMPERATURE | GPU:$GPU_TEMPERATURE | Inlet:$INLET_TEMPERATURE | Exhaust:$EXHAUST_TEMPERATURE | Disk:$HOTTEST_DISK_TEMPERATURE/$HOTTEST_DISK_LIMIT($(basename $HOTTEST_DISK_DEVICE)) | Fan Speed:$APPLIED_FAN_SPEED($FAN_SPEED_DRIVER$held)"
record_sample "$HIGHEST_CPU_TEMPERATURE" "$GPU_TEMPERATURE" "$HOTTEST_DISK_TEMPERATURE" "$DECIMAL_CURRENT_FAN_SPEED"
# Record what the fans are actually doing, not what was merely requested.
record_sample "$HIGHEST_CPU_TEMPERATURE" "$GPU_TEMPERATURE" "$HOTTEST_DISK_TEMPERATURE" "$APPLIED_FAN_SPEED"
check_alarms
}