A Linux daemon to manage fan speeds on Supermicro X10 (and possibly other) systems.
Copyright 2021 Ian Pilcher <arequipeno@gmail.com>
In theory, this program should not be needed. The BMC should manage the fan speeds to balance noise and temperatures. In reality, I have found that the BMC in my SYS-5028D-TN4T does not adequately cool the hard drives in the system.
The BMC provides 4 fan management modes — standard, full, optimal, and heavy I/O. Standard and optimal modes adjust the CPU fan speed in respnse to changes in the CPU (and possibly PCH) temperature, but they do not appear to monitor the hard disk temperatures, and the "system" fan runs at a very low speed. Full speed runs both the CPU and system fans at 100%; this does keep the disk drives cool, but it runs the CPU fan at more than 6,000 RPM, which creates an annoying whine. Heavy I/O mode sounds promising, but it also runs the CPU fan at an unnecessarily high speed, even when not required.
Fortunately, it is possible to exercise a degree of programatic control of the fan speeds (see here and here). When the BMC fan mode is set to full, fan zone duty cycles can be set via IPMI.
The X10SDV-TLN4F system
board in my server has 2 fan management zones. Headers FAN1
, FAN2
, and FAN3
appear to be
controlled by zone 0, and FAN4
(which is inconveniently located under my GPU card) is controlled
by zone 1. As shipped, the CPU fan is connected to FAN2
. My server's
721TQ-250B chassis only
accomodates a single fan (in addition to the CPU fan), so I have connected it to FAN4
. This
configuration allows the two fans to be controlled independently; zone 0 controls the CPU fan, and
zone 1 controls the "system" fan.
The installation steps below are written for Fedora 33, but they should work on (or be easily adapted to) any modern Linux distribution.
- FreeIPMI for communication with the (local) BMC
- libatasmart for reading disk drive temperatures
- LibYAML for parsing the configuration file
$ sudo dnf install freeipmi-devel libatasmart-devel libyaml-devel
$ git clone https://github.com/ipilcher/smfd.git
⋮
$ cd smfd
This requires that the selinux-policy-devel
package be installed.
$ make -f /usr/share/selinux/devel/Makefile
⋮
$ sudo semodule -i smfd.pp
$ gcc -O3 -Wall -Wextra -o smfd smfd.c -lfreeipmi -latasmart -lyaml
$ sudo cp smfd /usr/local/bin/
$ sudo chcon -t smfd_exec_t /usr/local/bin/smfd
$ sudo ipmi-sensors
Caching SDR repository information: /root/.freeipmi/sdr-cache/sdr-cache-⋯.localhost
⋮
$ sudo mkdir /var/lib/smfd
$ sudo cp /root/.freeipmi/sdr-cache/sdr-cache-⋯.localhost /var/lib/smfd/sdr-cache
$ sudo chcon -R -t smfd_var_lib_t /var/lib/smfd
NOTE: The name of the SDR cache file created by
ipmi-sensors
varies, based on the system hostname.
The output of ipmi-sensors
includes the IPMI SDR record IDs of the fan sensors, which are needed
in the next step. For example:
540 | FAN1 | Fan | N/A | RPM | N/A
607 | FAN2 | Fan | 4600.00 | RPM | 'OK'
674 | FAN3 | Fan | N/A | RPM | N/A
741 | FAN4 | Fan | 2000.00 | RPM | 'OK'
$ sudo mkdir /etc/smfd
$ sudo cp config.yaml /etc/smfd/
$ sudo chcon -R -t smfd_etc_t /etc/smfd
$ sudo vi /etc/smfd/config.yaml
⋮
$ sudo useradd -c 'Supermicro fan daemon' -d /var/lib/smfd -r -s /usr/sbin/nologin -G disk smfd
$ echo 'SUBSYSTEM=="ipmi", KERNEL=="ipmi0", GROUP="smfd"' | sudo tee /etc/udev/rules.d/99-ipmi-smfd.rules
$ ls -l /dev/ipmi0
crw-rw----. 1 root smfd 239, 0 Mar 30 13:13 /dev/ipmi0
$ cat << EOF | sudo tee /etc/systemd/system/smfd.service
[Unit]
Description=Supermicro X10 fan daemon
[Service]
Type=simple
User=smfd
AmbientCapabilities=CAP_SYS_RAWIO
ExecStart=/usr/local/bin/smfd
[Install]
WantedBy=multi-user.target
EOF
⋮
$ sudo systemctl daemon-reload
$ sudo systemctl enable smfd.service --now
Double check that the daemon is managing your fan speeds appropriately by checking its logs.
$ sudo journalctl -f -u smfd.service
smfd
reacts to two signals while it is running.
-
SIGUSR1
will toggle debugging messages on and off. -
SIGUSR2
will cause the daemon to immediately log information about the system state and the temperatures that it monitors. This is the same information is normally logged periodically. (Seelog_interval
inconfig.yaml
.) Sending this signal resets the logging data and interval start.