Make IPMI fan control temperature-aware without Docker

Manual fan mode pins the fans at a fixed duty and never ramps, so a sustained
load had no cooling response until the BMC's own critical-temperature
override stepped in. docker-idrac.sh solved that, but needs Docker, which is
not installed on the host.

ipmi-fancontrol-daemon polls the CPU sensors and switches between the fixed
duty and the BMC's automatic curve around a threshold, which is what that
container did. Measured on the host: 1440 RPM pinned versus roughly 4200 RPM
on the automatic curve at the same 60C.

- Hysteresis, default 5C, stops the fans oscillating at the boundary.
- An unreadable temperature fails safe to automatic rather than staying
  pinned.
- SIGTERM restores the automatic curve, with ExecStopPost as the backstop for
  an outright kill.

Default to the local "open" interface. It needs no credentials and no
network, which matters for the shutdown path, where lanplus to the iDRAC
could fail once networking is down. ipmi-fancontrol-auto and -manual honour
the same setting and stay available for ad-hoc use.

Enable the third-party PCIe cooling response suppression that docker-idrac.sh
was configured with, so the automatic curve is not held to a raised floor by
the aftermarket NICs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
ergosteur
2026-08-06 16:31:05 +00:00
co-authored by Claude Opus 5
parent af838987dd
commit 00384e7463
5 changed files with 207 additions and 27 deletions
+22 -11
View File
@@ -1,6 +1,8 @@
#!/bin/sh
# Put the BMC into manual fan control and pin the fans to a fixed speed.
# Paired with ipmi-fancontrol-auto via ipmi-fancontrol.service.
#
# This is the ad-hoc override; ipmi-fancontrol.service runs the temperature-aware
# ipmi-fancontrol-daemon instead. Note that manual mode does not ramp under load.
set -eu
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
@@ -8,22 +10,31 @@ CONFIG=${IPMI_FANCONTROL_CONFIG:-$SCRIPT_DIR/conf/ipmi-fancontrol.conf}
if [ ! -r "$CONFIG" ]; then
echo "Error: cannot read $CONFIG" >&2
echo "Copy conf/ipmi-fancontrol.conf.example there and fill in the BMC credentials." >&2
echo "Copy conf/ipmi-fancontrol.conf.example there and fill it in." >&2
exit 1
fi
. "$CONFIG"
: "${IPMI_HOST:?not set in $CONFIG}"
: "${IPMI_USER:?not set in $CONFIG}"
: "${IPMI_PASSWORD:?not set in $CONFIG}"
: "${IPMI_INTERFACE:=open}"
: "${FAN_SPEED:=0x15}"
# ipmitool -E reads the password from IPMI_PASSWORD, keeping it out of argv.
export IPMI_PASSWORD
ipmi() {
if [ "$IPMI_INTERFACE" = "lanplus" ]; then
ipmitool -I lanplus -H "$IPMI_HOST" -U "$IPMI_USER" -E "$@"
else
ipmitool -I open "$@"
fi
}
# Disable the BMC's automatic fan curve.
ipmitool -I lanplus -H "$IPMI_HOST" -U "$IPMI_USER" -E raw 0x30 0x30 0x01 0x00
if [ "$IPMI_INTERFACE" = "lanplus" ]; then
: "${IPMI_HOST:?not set in $CONFIG}"
: "${IPMI_USER:?not set in $CONFIG}"
: "${IPMI_PASSWORD:?not set in $CONFIG}"
# ipmitool -E reads the password from the environment, keeping it out of argv.
export IPMI_PASSWORD
fi
# Apply the fixed speed to all fans.
ipmitool -I lanplus -H "$IPMI_HOST" -U "$IPMI_USER" -E raw 0x30 0x30 0x02 0xff "$FAN_SPEED"
# Disable the BMC's automatic fan curve, then apply the fixed speed to all fans.
ipmi raw 0x30 0x30 0x01 0x00
ipmi raw 0x30 0x30 0x02 0xff "$FAN_SPEED"