158 lines
8.8 KiB
Perl
Executable file
158 lines
8.8 KiB
Perl
Executable file
#!/usr/bin/perl
|
|
|
|
$static_speed_low=0x02;
|
|
$static_speed_high=0x12; # This is the speed value at 100% demand
|
|
# ie what we consider the point we don't
|
|
# really want to get hotter but still
|
|
# tolerate. It's quite a bit of a misnomer
|
|
# - we keep increasing the speed rapidly
|
|
# beyond that up to 0xFF if necessary, but
|
|
# it's perhaps a scaling factor.
|
|
$ipmi_inlet_sensorname ="Inlet Temp";
|
|
$ipmi_exhaust_sensorname="Exhaust Temp";
|
|
|
|
$default_exhaust_threshold=50; # The exhaust temperature we use, set
|
|
# from above, which we fail back to
|
|
# letting the drac control the fans
|
|
$base_temp = 26; # No fans when below this temp
|
|
$desired_temp1 = 36; # Aim to keep the temperature below this
|
|
$desired_temp2 = 44; # Really ramp up fans above this
|
|
$desired_temp3 = 50; # Really ramp up fans above this
|
|
$demand1 = 5; # Prescaled (not taking into effect static_speed_low/high) demand at temp1
|
|
$demand2 = 40; # Prescaled (not taking into effect static_speed_low/high) demand at temp2
|
|
$demand3 = 200; # Prescaled (not taking into effect static_speed_low/high) demand at temp3
|
|
|
|
$hysteresis = 2; # Don't ramp up velocity unless demand
|
|
# difference is greater than this. Ramp
|
|
# down ASAP however, to bias quietness, and
|
|
# thus end up removing noise changes for
|
|
# just small changes in computing
|
|
|
|
# We can optionally just tell all fans to stick to the one speed:
|
|
# @daemons=(0xff);
|
|
@daemons=qw(0 1 2 3 4 5);
|
|
|
|
sub custom_temperature_calculation {
|
|
# FIXME: it would be good to share memory between the 6 daemons and
|
|
# not call ipmitool 6 times in parallel just to discard the value of
|
|
# 4 of them.
|
|
#
|
|
# These devices must be present, or the daemon will fail when
|
|
# starting. Remove them from the calculation if you don't have one
|
|
# (eg, GPU). I manually worked out roughly which fans correspond to
|
|
# dropping the temperature of each device, by ramping that fan up to
|
|
# a high velocity and observing how quickly the temperature drops.
|
|
#
|
|
# You should manually run `sensors -j` and ensure that every device
|
|
# that has a temperature is somewhow included in the calculation of
|
|
# at least one fan speed. Note that drivetemp devices may already
|
|
# be included via $*_drives averages below.
|
|
my $gpu = $sensors_ref->{"amdgpu-pci-0400"}{"edge"}{"temp1_input"}; # 0.2*fan3 + 0.5*fan4 + 0.3*fan5
|
|
my $raid_card = max(raid_controller_temp(),raid_controller_battery_temp()); # fan4+fan5;
|
|
my $left_cpu = $sensors_ref->{"coretemp-isa-0000"}{"Package id 0"}{"temp1_input"}; # fan2;
|
|
my $right_cpu = $sensors_ref->{"coretemp-isa-0001"}{"Package id 1"}{"temp1_input"}; # fan4;
|
|
|
|
# You might want to factor in NVME drives too, that might appear
|
|
# through the `sensors -j` structure as eg:
|
|
# $sensors_ref->{"nvme-pci-0100"}{"temp composite Samsung-EVO-970-1TB"}{"temp1_input"}
|
|
#
|
|
# You might find that while "temp composite Samsung-EVO-970-1TB"
|
|
# (heatsink) is the bit you're cooling, "temp sensor 2
|
|
# Samsung-EVO-970-1TB" (maybe the silicon itself) is uncomfortably
|
|
# higher than the composite reading. So monitor that hotter sensor
|
|
# instead to add more fan demand.
|
|
|
|
# For the drives, there's no need to map each /dev/sd? onto their
|
|
# dynamic positioning inside the chassis. Just code each path as it
|
|
# maps from /dev/disk/by-path to how it appears on your backplane
|
|
# physically. I used: `dd if=/dev/disk/by-path/.... of=/dev/null`
|
|
# to work out the pattern that's appropriate to my LFF R730XD (4x3
|
|
# 3.5" drives at front, 2 2.5" drives at rear; hence why I don't
|
|
# know which of 12 and 13 are left or right rear - I can't pull my
|
|
# machine out far enough to watch the blinkenlights). The averages
|
|
# disregard any drives that are not present or spundown and
|
|
# inaccessible to SMART temperature sensing (the daemon will
|
|
# fallback to idrac control if you define a mapping where one fan
|
|
# doesn't cool *any devices*. If you were sure there was nothing
|
|
# that could be cooled from that fan (no PCIe cards, no CPU or RAM
|
|
# in right hand socket, no drives on right hand side, no drac, no
|
|
# RAID controller, etc), you could just set weighted_temp to 1 (not
|
|
# 0 - that's still an error).
|
|
|
|
# So let's calculate the average temperatures of drives in each
|
|
# vertical arrangement (the rear drives draw their airflow from the
|
|
# two leftmost fans, so just average them). If you have 2.5" SFF
|
|
# drive backplane, or a 1RU chassis, or..., your calculation will be
|
|
# different.
|
|
my $left_outside_drives =
|
|
average(hddtemp("/dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:0:0"), # front top SSD
|
|
hddtemp("/dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:1:0"), # front middle SSD
|
|
hddtemp("/dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:2:0")); # front bottom HDD
|
|
my $left_middle_drives =
|
|
average(hddtemp("/dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:3:0"), # front top SSD
|
|
hddtemp("/dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:4:0"), # front middle HDD
|
|
hddtemp("/dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:5:0")); # front bottom HDD
|
|
my $right_middle_drives =
|
|
average(hddtemp("/dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:6:0"), # front top HDD
|
|
hddtemp("/dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:7:0"), # front middle HDD
|
|
hddtemp("/dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:8:0")); # front bottom HDD
|
|
my $right_outside_drives =
|
|
average(hddtemp("/dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:9:0"), # front top HDD
|
|
hddtemp("/dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:10:0"), # front middle HDD
|
|
hddtemp("/dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:11:0")); # front bottom HDD
|
|
my $rear_drives =
|
|
average(hddtemp("/dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:12:0"), # rear SSD (left or right? Dunno, just average over both
|
|
hddtemp("/dev/disk/by-path/pci-0000:02:00.0-scsi-0:0:13:0")); # of them, and contribute both to both fan 1 and fan 2)
|
|
|
|
if (print_stats_once()) {
|
|
print "gpu=$gpu, raid_card=$raid_card, cpus=$left_cpu, $right_cpu\n";
|
|
print "drives = $left_outside_drives, $left_middle_drives, $right_middle_drives, $right_outside_drives ; $rear_drives\n";
|
|
}
|
|
|
|
# My original mapping to spin up all fans in sync was:
|
|
# Two thirds weighted CPU temps vs hdd temps, but if the HDD temps
|
|
# creep above this value, use them exclusively (more important to
|
|
# keep them cool than the CPUs):
|
|
# weighted_temp = max(
|
|
# average(average(@cputemps), average(@coretemps), average(@hddtemps)),
|
|
# average(@hddtemps));
|
|
|
|
# FIXME: probably want to take into account ambient temperature - if
|
|
# the difference between weighted_temp and ambient_temp is small
|
|
# because ambient_temp is large, then less need to run the fans
|
|
# because there's still low power demands, to a point, but
|
|
# eventually we have to keep all components under a threshold, so
|
|
# maybe it makes sense to just ignore ambient_temp.
|
|
|
|
# FIXME: better hysteresis function than what's in set_fans_servo()
|
|
|
|
# It is more important to keep both SSDs and HDDs cool than CPUs.
|
|
# You can replace cooked CPUs, and they'll throttle before that
|
|
# point anyway. We could put different offsets or multipliers on
|
|
# them. As it is here, we weight drives 3 times more important than
|
|
# CPU in the final weighting.
|
|
|
|
# Contribution needed from each to cool the drives alone:
|
|
my $fan0_drive_weighted_temp = average($left_outside_drives, $rear_drives);
|
|
my $fan1_drive_weighted_temp = average($left_outside_drives, $left_middle_drives, $rear_drives);
|
|
my $fan2_drive_weighted_temp = $left_middle_drives;
|
|
my $fan3_drive_weighted_temp = $right_middle_drives;
|
|
my $fan4_drive_weighted_temp = average($right_middle_drives, $right_outside_drives);
|
|
my $fan5_drive_weighted_temp = $right_outside_drives;
|
|
|
|
my @fan_weighted_temp;
|
|
|
|
# Contribution from drives, CPUs, raid controller, GPU:
|
|
$fan_weighted_temp[0] = $fan0_drive_weighted_temp;
|
|
$fan_weighted_temp[1] = $fan1_drive_weighted_temp;
|
|
$fan_weighted_temp[2] = weighted_average(3,$fan2_drive_weighted_temp, 2,$left_cpu);
|
|
$fan_weighted_temp[3] = weighted_average(3,$fan3_drive_weighted_temp, 1,$gpu);
|
|
$fan_weighted_temp[4] = weighted_average(3,$fan4_drive_weighted_temp, 1,$gpu, 1,$raid_card, 2,$right_cpu);
|
|
$fan_weighted_temp[5] = weighted_average(3,$fan5_drive_weighted_temp, 1,$gpu, 1,$raid_card);
|
|
|
|
# Which fans are being controlled by this daemon? 0xff = all fans,
|
|
# 0x00 to 0x05 for individual fans from left to right viewing from the
|
|
# front
|
|
|
|
return $fan_weighted_temp[$fans];
|
|
}
|