-
Notifications
You must be signed in to change notification settings - Fork 2
/
ipmi_fancontrol.pl
219 lines (188 loc) · 7.99 KB
/
ipmi_fancontrol.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
#!/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 GPU Temperatures.
# This script is tested on the SuperMicro X10 4027GR-TRT Server. It may work on other X9/X10/X11 motherboards.
#
# NOTE: The script puts your server into "Full Fan Speed Mode", and then modifies what "Full Speed" means,
# You have to manually use IPMI to set it to e.g. "Optimal" when you're not using the script.
#
# NOTE: You use this script at your own risk, and no warranty is provided. Do not use in produciton environments.
#
# Author: Layla Mah <[email protected]>
# System Configuration
my $number_of_cpus = 2; # Number of CPUs to search for
my $number_of_gpus = 4; # Number of GPUs to search for
my $number_of_fans = 8; # Number of FANs to search for
my $number_of_fanbanks = 4; # Number of BANKs of FANs to update
my $min_temp_change = 5; # *C minimum change to actually cause a fan speed update
my $seconds_to_sleep = 5; # Number of seconds to sleep between update loops
# IPMI Configuration
my $ipmi_username = "username_goes_here";
my $ipmi_password = "password_goes_here";
my $ipmi_ipaddress = "ip_address_goes_here";
my $ipmi_connectmode = "lan";
my $ipmi_cmd_listall = "sdr list full";
my $ipmi_preamble = 'ipmitool -I $ipmi_connectmode -U $ipmi_username -P $ipmi_password -H $ipmi_ipaddress';
# CPU Temp -> Fan Speed Mappings
my %cpu_temp_to_fan_speed;
$cpu_temp_to_fan_speed{80} = 0x64;
$cpu_temp_to_fan_speed{75} = 0x56;
$cpu_temp_to_fan_speed{70} = 0x48;
$cpu_temp_to_fan_speed{60} = 0x40;
$cpu_temp_to_fan_speed{55} = 0x32;
$cpu_temp_to_fan_speed{50} = 0x16;
$cpu_temp_to_fan_speed{10} = 0x8;
# GPU Temp -> Fan Speed Mappings
my %gpu_temp_to_fan_speed;
$gpu_temp_to_fan_speed{93} = 0x64;
$gpu_temp_to_fan_speed{90} = 0x56;
$gpu_temp_to_fan_speed{85} = 0x48;
$gpu_temp_to_fan_speed{80} = 0x40;
$gpu_temp_to_fan_speed{75} = 0x32;
$gpu_temp_to_fan_speed{70} = 0x24;
$gpu_temp_to_fan_speed{65} = 0x22;
$gpu_temp_to_fan_speed{50} = 0x20;
$gpu_temp_to_fan_speed{40} = 0x18;
$gpu_temp_to_fan_speed{30} = 0x16;
$gpu_temp_to_fan_speed{10} = 0x8;
# Below this line follows the actual implementation of the script
my $g_current_fan_duty_cycle = 0;
my $g_current_gpu_temp = 0;
my $g_current_cpu_temp = 0;
my $g_last_set_cpu_temp = 0;
my $g_last_set_gpu_temp = 0;
sub Internal_DoSetFanSpeed
{
my ( $fan_speed ) = @_;
foreach my $fanbank (0..$number_of_fanbanks)
{
my $hex_fanbank = sprintf( "0x%x", $fanbank );
`ipmitool -I $ipmi_connectmode -U $ipmi_username -P $ipmi_password -H $ipmi_ipaddress raw 0x30 0x70 0x66 0x01 $hex_fanbank $fan_speed`;
}
}
sub SetFanSpeed
{
my ( $fan_speed ) = @_;
my $cpu_temp_difference = $g_current_cpu_temp - $g_last_set_cpu_temp;
my $gpu_temp_difference = $g_current_gpu_temp - $g_last_set_gpu_temp;
if( ( (abs $cpu_temp_difference) > $min_temp_change ) or ( (abs $gpu_temp_difference) > $min_temp_change ) )
{
# Set all 4 fan banks to operate at $fan_speed duty cycle (0x0-0x64 valid range)
print "\n";
print "******************** Updating Fan Speeds ********************\n";
print "We last updated fan speed $cpu_temp_difference *C ago (CPU Temperature).\n";
print "We last updated fan speed $gpu_temp_difference *C ago (GPU Temperature).\n";
print "Current CPU Temperature is $g_current_cpu_temp *C.\n";
print "Current GPU Temperature is $g_current_gpu_temp *C.\n";
print "Setting Fan Speed on all fan banks to $fan_speed\n";
print "*************************************************************\n";
$g_last_set_cpu_temp = $g_current_cpu_temp;
$g_last_set_gpu_temp = $g_current_gpu_temp;
$g_current_fan_duty_cycle = $fan_speed;
Internal_DoSetFanSpeed( $fan_speed );
}
}
sub UpdateFanSpeed
{
# Gather statistics for fan speed and CPU Temp and stuch
my $ipmi_output = `ipmitool -I $ipmi_connectmode -U $ipmi_username -P $ipmi_password -H $ipmi_ipaddress $ipmi_cmd_listall`;
my @vals = split( "\n", $ipmi_output );
my $current_cpu_temp = 0;
my $current_gpu_temp = 0;
my $min_fan_speed = 30000;
my $max_fan_speed = 0;
foreach my $value (@vals)
{
foreach my $fan (1..$number_of_fans)
{
if( $value =~ /^(FAN$fan)\s.*\s(\d+)\s.*RPM.*/gi )
{
#print "Value : $value\n";
#print "Matched : $1\n";
#print "FanSpeed: $2 RPM\n";
print "$1 ....: $2 RPM\n";
my $fan_speed = $2;
$min_fan_speed = min( $fan_speed, $min_fan_speed );
$max_fan_speed = max( $fan_speed, $max_fan_speed );
}
} # foreach my $fan (1..$number_of_fans)
foreach my $cpu (1..$number_of_cpus)
{
if( $value =~ /^(CPU$cpu\sTemp).*\s(\d+)\s.*degrees\sC.*/gi )
{
#print "Value : $value\n";
#print "Matched: $1\n";
#print "Temp : $2 degrees C\n";
print "$1: $2 degrees C\n";
my $cpu_temp = $2;
$current_cpu_temp = max( $cpu_temp, $current_cpu_temp );
}
} # foreach my $cpu (1..$number_of_cpus)
foreach my $gpu (1..$number_of_gpus)
{
if( $value =~ /^(GPU$gpu\sTemp).*\s(\d+)\s.*degrees\sC.*/gi )
{
#print "Value : $value\n";
#print "Matched: $1\n";
#print "Temp : $2 degrees C\n";
print "$1: $2 degrees C\n";
my $gpu_temp = $2;
$current_gpu_temp = max( $gpu_temp, $current_gpu_temp );
}
} # foreach my $gpu (1..$number_of_gpus)
} # foreach my $value (@vals)
$g_current_cpu_temp = $current_cpu_temp;
$g_current_gpu_temp = $current_gpu_temp;
print "Maximum CPU Temperature Seen: $current_cpu_temp degrees C.\n";
print "Maximum GPU Temperature Seen: $current_gpu_temp degrees C.\n";
print "Current Minimum Fan Speed: $min_fan_speed RPM\n";
print "Current Maximum Fan Speed: $max_fan_speed RPM\n";
my $desired_fan_speed = 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 = max( $cpu_temp_to_fan_speed{ $cpu_temp }, $desired_fan_speed );
#print "The fan speed setting for CPU Temp $cpu_temp *C is $cpu_temp_to_fan_speed{$cpu_temp} % duty cycle\n";
}
}
my @gpu_temps = keys %gpu_temp_to_fan_speed;
for my $gpu_temp (@gpu_temps)
{
if( $current_gpu_temp > $gpu_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 = max( $gpu_temp_to_fan_speed{ $gpu_temp }, $desired_fan_speed );
#print "The fan speed setting for GPU Temp $gpu_temp *C is $gpu_temp_to_fan_speed{$gpu_temp} % duty cycle\n";
}
}
if( $desired_fan_speed == 0x0 )
{
print "\n***** ERROR: Failed to determine a desired fan speed. Forcing fans to 100% duty cycle as a safety fallback measure. *****\n";
$desired_fan_speed = 0x64;
}
print "Current Fan Duty Cycle: $g_current_fan_duty_cycle%\n";
print "Desired Fan Duty Cycle: $desired_fan_speed%\n";
SetFanSpeed( $desired_fan_speed );
}
print "Setting Fan mode to FULL SPEED.\n";
# Ensure Fan Mode is set to Full Speed
`ipmitool -I $ipmi_connectmode -U $ipmi_username -P $ipmi_password -H $ipmi_ipaddress raw 0x30 0x45 0x01 0x01`;
while( 1 )
{
print "\n";
print "=================================================================\n";
print "Calling UpdateFanSpeed()...\n";
print "=================================================================\n";
UpdateFanSpeed();
print "=================================================================\n";
print "Update Complete - going to sleep for $seconds_to_sleep seconds...\n";
print "=================================================================\n";
sleep $seconds_to_sleep;
}