commit 514ead4e030719b3cd220ce75bc83a12baa0bfbd Author: Ville Granroth Date: Thu Jul 18 21:29:45 2024 +0300 first commit diff --git a/fancontrol.pl b/fancontrol.pl new file mode 100755 index 0000000..0f75ccf --- /dev/null +++ b/fancontrol.pl @@ -0,0 +1,159 @@ +#!/usr/bin/perl +use strict; +use warnings; +use List::Util qw[min max]; + +# This script controls SuperMicro server fan speeds via IPMI in response to CPU and SYS temperatures. +# A fork of https://github.com/missmah/ipmi_tools by Layla Mah + +# Sensor treshold reset to allow slower fans + +`ipmitool sensor thresh FANA lower 0 100 200`; +`ipmitool sensor thresh FANB lower 0 100 200`; +`ipmitool sensor thresh FAN1 lower 0 100 200`; +`ipmitool sensor thresh FAN2 lower 0 100 200`; +`ipmitool sensor thresh FAN3 lower 0 100 200`; +`ipmitool sensor thresh FAN4 lower 0 100 200`; +`ipmitool sensor thresh FAN5 lower 0 100 200`; + +my $min_temp_change = 4; # *C minimum change to actually cause a fan speed update +my $seconds_to_sleep = 3; # Number of seconds to sleep between update loops + +# CPU Temp -> Fan Speed Mappings +my %cpu_temp_to_fan_speed; + $cpu_temp_to_fan_speed{75} = 0x64; + $cpu_temp_to_fan_speed{70} = 0x60; + $cpu_temp_to_fan_speed{65} = 0x54; + $cpu_temp_to_fan_speed{60} = 0x52; + $cpu_temp_to_fan_speed{50} = 0x44; + $cpu_temp_to_fan_speed{40} = 0x40; + +# SYS Temp -> Fan Speed Mappings +my %sys_temp_to_fan_speed; + $sys_temp_to_fan_speed{55} = 0x64; + $sys_temp_to_fan_speed{50} = 0x50; + $sys_temp_to_fan_speed{45} = 0x44; + $sys_temp_to_fan_speed{40} = 0x40; + +# Below this line follows the actual implementation of the script + +my $g_current_fan_duty_cycle = 0; +my $g_current_sys_temp = 0; +my $g_current_cpu_temp = 0; +my $g_last_set_cpu_temp = 0; +my $g_last_set_sys_temp = 0; + +sub Internal_DoSetFanSpeedSys { + my ( $fan_speed ) = @_; + `ipmitool raw 0x30 0x70 0x66 0x01 0x01 $fan_speed`; +} + +sub Internal_DoSetFanSpeedCpu { + my ( $fan_speed ) = @_; + `ipmitool raw 0x30 0x70 0x66 0x01 0x00 $fan_speed`; +} + +sub SetFanSpeed { + my ( $fan_speed ) = @_; + + my $cpu_temp_difference = $g_current_cpu_temp - $g_last_set_cpu_temp; + my $sys_temp_difference = $g_current_sys_temp - $g_last_set_sys_temp; + + if( (abs $cpu_temp_difference) > $min_temp_change ) { + print "We last updated fan speed $cpu_temp_difference *C ago (CPU Temperature).\n"; + print "Current CPU Temperature is $g_current_cpu_temp *C, setting CPU Fan Speed to $fan_speed\n"; + + $g_last_set_cpu_temp = $g_current_cpu_temp; + $g_current_fan_duty_cycle = $fan_speed; + + Internal_DoSetFanSpeedCpu( $fan_speed ); + } + + if( (abs $sys_temp_difference) > $min_temp_change ) { + print "We last updated fan speed $sys_temp_difference *C ago (SYS Temperature).\n"; + print "Current SYS Temperature is $g_current_sys_temp *C, setting SYS Fan Speed to $fan_speed\n"; + + $g_last_set_sys_temp = $g_current_sys_temp; + $g_current_fan_duty_cycle = $fan_speed; + + Internal_DoSetFanSpeedSys( $fan_speed ); + } +} + +sub UpdateFanSpeed { + # Gather statistics for fan speed and CPU Temp and stuch + my $ipmi_output = `ipmitool sdr list full`; + my @vals = split( "\n", $ipmi_output ); + + my $current_cpu_temp = 0; + my $current_sys_temp = 0; + + foreach my $value (@vals) { + + if( $value =~ /^(CPU\sTemp).*\s(\d+)\s.*degrees\sC.*/gi ) { + my $cpu_temp = $2; + $current_cpu_temp = max( $cpu_temp, $current_cpu_temp ); + } + + if( $value =~ /^(System\sTemp).*\s(\d+)\s.*degrees\sC.*/gi ) { + my $sys_temp = $2; + $current_sys_temp = max( $sys_temp, $current_sys_temp ); + } + + } # foreach my $value (@vals) + + $g_current_cpu_temp = $current_cpu_temp; + $g_current_sys_temp = $current_sys_temp; + + my $desired_fan_speed_cpu = 0x0; + my $desired_fan_speed_sys = 0x0; + + my @cpu_temps = keys %cpu_temp_to_fan_speed; + for my $cpu_temp (@cpu_temps) { + if( $current_cpu_temp > $cpu_temp ) { + # If the current CPU temperature is higher than the temperature enumerated by this hash lookup, + # Then set the desired fan speed (if our value is larger than the existing value) + $desired_fan_speed_cpu = max( $cpu_temp_to_fan_speed{ $cpu_temp }, $desired_fan_speed_cpu ); + # print "The fan speed setting for CPU Temp $cpu_temp *C is $cpu_temp_to_fan_speed{$cpu_temp} % duty cycle\n"; + } + } + + my @sys_temps = keys %sys_temp_to_fan_speed; + for my $sys_temp (@sys_temps) { + if( $current_sys_temp > $sys_temp ) { + # If the current gPU temperature is higher than the temperature enumerated by this hash lookup, + # Then set the desired fan speed (if our value is larger than the existing value) + $desired_fan_speed_sys = max( $sys_temp_to_fan_speed{ $sys_temp }, $desired_fan_speed_sys ); + # print "The fan speed setting for GPU Temp $sys_temp *C is $sys_temp_to_fan_speed{$sys_temp} % duty cycle\n"; + } + } + + if( $desired_fan_speed_cpu == 0x0 ) { + print "\n***** ERROR: Failed to determine a desired fan speed. Forcing CPU fans to 100% duty cycle as a safety fallback measure. *****\n"; + $desired_fan_speed_cpu = 0x64; + } + + if( $desired_fan_speed_sys == 0x0 ) { + print "\n***** ERROR: Failed to determine a desired fan speed. Forcing SYS fans to 100% duty cycle as a safety fallback measure. *****\n"; + $desired_fan_speed_sys = 0x64; + } + + #print "Current Fan Duty Cycle: $g_current_fan_duty_cycle%\n"; + #print "Desired CPU Fan Duty Cycle: $desired_fan_speed_cpu%\n"; + #print "Desired SYS Fan Duty Cycle: $desired_fan_speed_sys%\n"; + + SetFanSpeed( $desired_fan_speed_cpu ); + SetFanSpeed( $desired_fan_speed_sys ); + +} + +print "Setting Fan mode to FULL SPEED.\n"; +# Ensure Fan Mode is set to Full Speed +`ipmitool raw 0x30 0x45 0x01 0x01`; + +while( 1 ) { + #print "Calling UpdateFanSpeed()...\n"; + UpdateFanSpeed(); + #print "Update Complete - going to sleep for $seconds_to_sleep seconds...\n"; + sleep $seconds_to_sleep; +} diff --git a/ipmi-fancontrol.service b/ipmi-fancontrol.service new file mode 100755 index 0000000..88d9a47 --- /dev/null +++ b/ipmi-fancontrol.service @@ -0,0 +1,9 @@ +[Unit] +Description=IPMI Fan control + +[Service] +Type=simple +ExecStart=/bin/perl /opt/fancontrol.pl + +[Install] +WantedBy=network-online.target \ No newline at end of file