Управление вентилятором Radeon RX-6xx0: различия между версиями

Материал из ALT Linux Wiki
 
(не показано 189 промежуточных версий этого же участника)
Строка 1: Строка 1:
[[Файл:1.Zalman Z1.jpg|мини|Zalman Z1]]
=Введение=
=Введение=
Столкнулся с проблемой перегрева видюхи RX-6700XT на игровых нагрузках — и не только её самой, поскольку она ведь и вокруг себя знатно греет.
Столкнулся с проблемой перегрева видюхи RX-6700XT на игровых нагрузках — и не только её самой, поскольку она ведь и вокруг себя изрядно греет.
*Процессор прямо над нею кипятится — места в простенке между видюхой, крышкой и стенками отнюдь не завались.
*Процессор прямо над нею кипятится — мéста между видюхой, крышкой и стенками отнюдь не завались.
*М2-накопитель между видюхой и процом тоже знатно подгорает, хоть и с радиатором, и от проц-кулера на него дует... горячим. :)
*М2-накопитель посреди видюхи и проца тоже знатно подгорает хоть и с радиатором, и от проц-кулера на него дует… горячим.
*Дополнительные винчестеры в корзине, вставленной в пространство 5-дюймовых отсеков.
*Дополнительным винчестерам в корзине, вставленной в пространство 5-дюймовых отсеков, жарко и душно.
*Да и тем, что в нижних 3,5-дюймовых отсеках, ни разу не уютно.
*Да и тем, что в нижних 3,5-дюймовых, ни разу не уютно.
В некоторой степени спасает вариант поставить (скорей, положить) перед открытой крышкой системника большой комнатный вентилятор — для всего, кроме самой видюхи: она ж к нему торцом. А заводская авторегулировка видюхиного ШИМа как-то не особо способствует её охлаждению даже зимой, о жаре за бортом вовсе молчу.
До некоторой степени спасает вариант поставить перед открытой крышкой системника большой настольный вентилятор (см. фото) — для всего, кроме самой видюхи: она ж к нему краем, и толку почти ноль. А заводская авторегулировка видюхиного ШИМа как-то не особо способствует её охлаждению даже зимой, о жаре за бортом вовсе молчу.
=Реализация=
[[Файл:4.Final.jpg|мини|AeroCool xPredator FullTower]]
Пошарив по просторам, нарыл массу графических «мониторилок» — но ни одной «крутилки» именно для АМД, хотя для ненаВидии — завались (один лишь [https://www.flathub.org/apps/details/com.leinardi.gwe «позеленевший от зависти»] чего стóит). И это невзирая на то, что всё нужное доступно прямо из системы, без нужды ковыряться в проприетарщине… Странное дело.
 
При этом для командной строки нарубинено да напитонено всякого, но к чему плодить сущности, если всё можно сделать перманентно доступным шеллом? Проникся довольно замороченным решением [https://github.com/WieWaldi/amdgpu-fancontrol <code>amdgpu-fancontrol</code>] и по образу/подобию слепил на скорую руку собственный упрощённый вариант, для которого понадобится тройка файлов — скрипт да конфиг c юнитом к нему (выложены на [https://github.com/Smoque/amdgpufan ГитХаб]).
===1) Файл настроек===
{|class="mw-collapsible mw-collapsed wikitable"
!/etc/sysconfig/amdgpufan &nbsp;
|-
|<source lang="ini">
# restart service amdgpufan if config changed
card=0          # vega rather has number 1
rate=3          # check frequency
step=5          # min to max divider
within=2        # temperature borders and pwm diff tolerance
min_tmp=45      # no need cooling lower
sensor=junction # 'edge' (gpu), 'mem' or 'junction' (hottest one)
</source>
|}
===2) Запускающий юнит===
{|class="mw-collapsible mw-collapsed wikitable"
!/lib/systemd/system/amdgpufan.service &nbsp;
|-
|<source lang="ini">
[Unit]
Description = AMD GPU fan control
 
[Service]
ExecStart = /usr/local/sbin/amdgpufan
Restart = on-failure
RestartSec = 5


=Реализация=
[Install]
Вдохновлённый решением одного немца [https://github.com/WieWaldi/amdgpu-fancontrol <code>amdgpu-fancontrol</code>], сваял собственный сильно упрощённый вариант.
WantedBy = default.target
</source>
|}
Как включать юниты, все давно знают. :)
===3) Сам скрипт===
{|class="mw-collapsible mw-collapsed wikitable"
!/usr/local/sbin/amdgpufan &nbsp;
|-
|<source lang="bash">
#!/bin/sh


Понадобится создать два файла.
[ $UID -eq 0 ] || { # Check user permissions.
#Собственно скрипт запуска <code>/usr/local/sbin/amdgpufan</code>:
#:<source lang="bash">
#!/bin/bash
# Check root privileges first.
[ $UID -eq 0 ] || {
     echo "Writing to /sys requires root privileges."
     echo "Writing to /sys requires root privileges."
     exit 1
     exit 1
}
}
   
 
## User variables #############################################
## User defaults if config absend ################################
card=0
card=0         # vega rather has number 1
rate=5
rate=3          # check frequency
min_tmp=40
step=5         # min to max divider
gap_tmp=$[min_tmp/2]
within=2        # temperature borders and pwm diff tolerance
## System variables ###########################################
min_tmp=45      # no need cooling lower
sys_dir=`ls -d /sys/class/drm/card$card/device/hwmon/*`
sensor=junction # 'edge' (gpu), 'mem' or 'junction' (hottest one)
## temp# - 1:edge, 2:junction, 3:memory
 
sys_tmp=${sys_dir}/temp2_input
config=/etc/sysconfig/amdgpufan
[ -f $config -a -s $config ] && source $config ||
echo "User settings not found, using default values."
 
case $sensor in
    junction) # let's add some degrees to most hot sensor
        let min_tmp+=5
        sensor=2;;
    edge)sensor=1;;
    mem) sensor=3;;
esac
 
## System variables ##############################################
gpuinfo=/sys/kernel/debug/dri/$card/amdgpu_pm_info
dev_dir=/sys/class/drm/card$card/device
sys_dir=`ls -d $dev_dir/hwmon/*`
sys_lvl=${dev_dir}/power_dpm_force_performance_level
sys_clk=${dev_dir}/pp_od_clk_voltage
sys_tmp=${sys_dir}/temp${sensor}_input
sys_pwm=${sys_dir}/pwm1
sys_pwm=${sys_dir}/pwm1
sys_fan=${sys_pwm}_enable
sys_fan=${sys_pwm}_enable


## Calculated variables ##########################################
part=$[step-2] # loops' divider: steps num except first and last
# Achievable frequencies for gpu & mem:
mhz=(`awk '/^(M|S)/{gsub("Mhz","");print $3}' $sys_clk | sort -V`)
max_tmp=$[`cat $sys_dir/temp*_crit | sort -V | head -1`/1000]
max_pwm=`cat ${sys_pwm}_max`
min_pwm=`cat ${sys_pwm}_min`
min_pwm=`cat ${sys_pwm}_min`
max_pwm=`cat ${sys_pwm}_max`
gap_pwm=$[(max_pwm-min_pwm)/step]
max_tmp=$[`cat ${sys_dir}/temp*_crit | sort -V | head -1`/1000]
gap_tmp=$[(max_tmp-min_tmp)/step]
###############################################################


reset(){
reset(){ # Sudden break.
     printf "Exiting and setting fan mode"
     printf "Exiting and s"
     fanmode auto
     setmode auto
     exit 0
     exit 0
}
}


fanmode(){
setmode(){ # Change as mode as gpu & mem frequency.
    echo "etting clock & fan mode to '$1'."
     case $1 in
     case $1 in
        manual) mode=1;;
         auto)  mode=2;;
         auto)  mode=2;;
         *)     mode=0 # max
         manual) mode=1
                echo m 1 ${mhz[0]}  >$sys_clk
                echo s 1 ${mhz[1]}  >$sys_clk
        ;;
     esac
     esac
     echo " to '$1'."
     echo $mode  >$sys_fan
     echo $mode >$sys_fan
     echo $1    >$sys_lvl
}
}


main(){
main(){ # Whole magic.
     cur_pwm=`cat $sys_pwm`
     cur_pwm=`cat $sys_pwm`
     cur_tmp=$[`cat $sys_tmp`/1000]
     cur_tmp=$[`cat $sys_tmp`/1000]
     [[ `cat $sys_fan` -eq 1 ]] || {
     [[ `cat $sys_fan` -eq 1 ]] || {
         printf "Wrong fan mode, setting"
         printf "S"
         fanmode manual
         setmode manual
     }
     }
     if [[ $cur_tmp -gt $max_tmp ]]; then
     if [[ $cur_tmp -gt $max_tmp ]]; then
         echo "$cur_tmp°C above critical, setting PWM to $max_pwm for max speed."
         echo "$cur_tmp°C exceeds critical, set 100% blowing."
         new_pwm=$max_pwm
         new_pwm=$max_pwm
    elif [[ $cur_tmp -le $min_tmp ]]; then
        new_pwm=$min_pwm
     else
     else
         [[ $cur_tmp -le $min_tmp ]] && new_pwm=$min_pwm || {
         for i in `seq 0 $part`; do
            for n in `seq 0 4`; do
            [[ $cur_tmp -ge $[min_tmp+gap_tmp*i]     ]] &&
                case $n in
            [[ $cur_tmp -le $[min_tmp+gap_tmp*(i+1)] ]] && {
                    0) add=70;;
                new_pwm=$[min_pwm+gap_pwm*(i+1)]
                    1) add=90;;
                break
                    2) add=140;;
            }
                    3) add=210;;
        done
                esac
                [[ $cur_tmp -gt $[min_tmp+gap_tmp*n - 1] ]] &&
                [[ $cur_tmp -le $[min_tmp+gap_tmp*(n+1)] ]] && {
                    new_pwm=$[min_pwm+add]
                    break
                }
            done
        }
     fi
     fi
     printf "%3d°C @%3d PWM" $cur_tmp $cur_pwm
     printf "%3d°C @%3d PWM" $cur_tmp $cur_pwm
     [[ $cur_pwm -ge $[new_pwm-2] ]] &&
     [[ $cur_pwm -ge $[new_pwm-within] ]] &&
     [[ $cur_pwm -le $new_pwm   ]] &&
     [[ $cur_pwm -le $[new_pwm+within] ]] &&
     echo "." || { # Change PWM if differ.
     echo "." || { # Change PWM if differ.
         printf ", changing to %3d.\n" $new_pwm
         for i in `seq 0 $part`; do
        echo $new_pwm >$sys_pwm
            [[ $cur_tmp -ge $[min_tmp+gap_tmp*i-within] ]] &&
            [[ $cur_tmp -le $[min_tmp+gap_tmp*i+within] ]] && {
                echo "."
                break
            }
        done || {
            printf ", switching to %3d.\n" $new_pwm
            echo $new_pwm >$sys_pwm
        }
     }
     }
}
}
   
 
trap "reset" SIGINT SIGTERM # Handle signals.
trap "reset" SIGINT SIGTERM # Handle signals.
   
 
while :; do # run daemon
while :; do # Go on!
     main
     main
     sleep ${rate}s
     sleep ${rate}s
done
done
</source>
</source>
#Запускающий его юнит <code>/lib/systemd/system/amdgpufan.service</code>:
|}
#:<source lang="ini">
===Пояснения===
[Unit]
#Пользовательскими переменными из конфига задаю:
Description = AMD GPU fan control
#:*'''card''' — № видюхи (судя по статьям от спецов, при наличии встройки оная будет пронумерована единичкой);
#:*'''min_tmp''' — температуру, ниже которой мослать вентилятором бессмысленно (с завода вентилятор не включается до 52°C процессора, а при остывании останавливается на 46°C);
#:*'''sensor''' — какой из датчиков «пасти»: процессора, самый холодный (памяти) или самый горячий (температуры перехода), однако '''max_tmp''' выбираю минимальную из критических;
#:*'''rate''' — частоту опроса со сменой значения в ШИМ-контроллере при выходе температуры из предыдущего диапазона (не обязательно делать это ежесекундно, чтоб и не дать перегреться, и не надоело листать портянку журнала);
#:*'''step''' — число сегментов, на которые будет разбит как диапазон регулируемых температур, так и шкала делений ШИМ-контроллера (вроде процентной, но не на 100, а на 255 делений);
#:*'''whithin''' — допустимый разброс задаваемого значения ШИМ с не всегда одинаковым текущим, а также перехлёст температурных диапазонов, дабы вентилятор не дёргался на градус разницы, внося сумятицу в умы и сердца.
#Запускать скрипт по таймеру не стал, оставил а-ля «резидентным» (см. цикл '''while'''), дабы между его запусками не бросать видюху в ручном режиме без управления (см. функцию '''setmode''').
==Результаты==
===Утро/вечер: веб-сёрфинг, общение, редактирование и пр.===
Переключение ШИМа на 20% (51) после примерно 5-минутного прогрева до 48°C, далее — колебания в пределах 50..55°C.


[Service]
Отход на перекус — температура примерно за то же время падает до 42°C, ШИМ обнуляется, далее — колебания как ночью.
ExecStart = /usr/local/sbin/amdgpufan
Restart = on-failure
RestartSec = 5


[Install]
Будним днём с повышением забортной температуры системная тоже подрастает и гуляет в рамках 40°C+ без запуска вентиляторов.
WantedBy = default.target
===Мёртвая петля (DeathLoop) на максималках===
</source>
Колебания температуры между 59 и 76°C с прыжками ШИМ от 51 до 102, 153 и обратно:
Пример его работы:
<pre>
<pre>Вечер: веб-сёрфинг, редактирование и пр. Колебания Т°C при PWM 69
июн 03 21:17:40 comp.ill amdgpufan[1492297]: 66°C @153 PWM, switching to 102.
  54°C @ 69 PWM.
июн 03 21:17:43 comp.ill amdgpufan[1492297]: 72°C @102 PWM.
...Запуск игры.
июн 03 21:17:46 comp.ill amdgpufan[1492297]:  72°C @102 PWM.
  60°C @ 69 PWM, changing to  90.
июн 03 21:17:49 comp.ill amdgpufan[1492297]:  70°C @102 PWM.
  59°C @ 89 PWM, changing to  70.
июн 03 21:17:52 comp.ill amdgpufan[1492297]: 76°C @102 PWM, switching to 153.
  60°C @ 69 PWM, changing to 90.
июн 03 21:17:55 comp.ill amdgpufan[1492297]: 75°C @153 PWM.
  57°C @ 89 PWM, changing to 70.
...
  54°C @ 69 PWM.
июн 03 21:18:07 comp.ill amdgpufan[1492297]: 59°C @153 PWM.
  59°C @ 69 PWM.
июн 03 21:18:10 comp.ill amdgpufan[1492297]:  57°C @153 PWM, switching to  51.
  62°C @ 69 PWM, changing to  90.
</pre>
  77°C @ 89 PWM.
Насыщенная сцена:
...Активная фаза игры.
<pre>
  82°C @ 89 PWM, changing to 140.
июн 03 21:18:13 comp.ill amdgpufan[1492297]: 82°C @ 51 PWM.
  62°C @138 PWM, changing to 90.
июн 03 21:18:16 comp.ill amdgpufan[1492297]: 84°C @ 51 PWM.
  59°C @ 89 PWM, changing to  70.
июн 03 21:18:19 comp.ill amdgpufan[1492297]: 87°C @ 51 PWM, switching to 204.
  62°C @ 69 PWM, changing to  90.
июн 03 21:18:22 comp.ill amdgpufan[1492297]: 87°C @204 PWM.
  60°C @ 89 PWM.
...
июн 03 21:18:43 comp.ill amdgpufan[1492297]: 87°C @204 PWM.
июн 03 21:18:46 comp.ill amdgpufan[1492297]: 78°C @204 PWM, switching to 153.
июн 03 21:18:49 comp.ill amdgpufan[1492297]: 59°C @153 PWM.
июн 03 21:18:52 comp.ill amdgpufan[1492297]: 83°C @153 PWM.
июн 03 21:18:55 comp.ill amdgpufan[1492297]:  85°C @153 PWM.
июн 03 21:18:58 comp.ill amdgpufan[1492297]:  85°C @153 PWM.
июн 03 21:19:01 comp.ill amdgpufan[1492297]: 86°C @153 PWM, switching to 204.
июн 03 21:19:04 comp.ill amdgpufan[1492297]: 86°C @204 PWM.
...
июн 03 21:19:49 comp.ill amdgpufan[1492297]: 85°C @204 PWM.
июн 03 21:19:52 comp.ill amdgpufan[1492297]: 60°C @204 PWM.
июн 03 21:19:55 comp.ill amdgpufan[1492297]: 58°C @204 PWM, switching to  51.
июн 03 21:19:58 comp.ill amdgpufan[1492297]: 57°C @ 51 PWM.
...
...
61°C @ 89 PWM.
июн 03 21:20:22 comp.ill amdgpufan[1492297]: 58°C @ 51 PWM.
62°C @ 89 PWM.
июн 03 21:20:25 comp.ill amdgpufan[1492297]: 75°C @ 51 PWM, switching to 153.
  58°C @ 89 PWM, changing to  70.
июн 03 21:20:28 comp.ill amdgpufan[1492297]75°C @153 PWM.
60°C @ 69 PWM, changing to  90.
  75°C @ 89 PWM.
...Игра завершена, плавное падение температуры.
53°C @ 89 PWM, changing to 70.
52°C @ 69 PWM.
40°C @ 69 PWM.
39°C @ 69 PWM, changing to  0.
40°C @  0 PWM, changing to  70.
39°C @ 69 PWM, changing to  0.
39°C @  0 PWM.
...Ночь: колебания 35-38°C с отключёнными вентиляторами видюхи.
...Утро: вход в систему, веб-сёрфинг, редактирование и пр.
  40°C @ 0 PWM, changing to  70.
41°C @ 69 PWM.
...Колебания 42-55°C
</pre>
</pre>
=Послесловие=
Далее как в начале игры.
 
Судя по графику, выше 90°C не поднималось, обороты на максимум — тоже. ЦПУ греется сопоставимо, но почти всю игру 75..90°C.
===Ночь===
41±2°C при нулевом ШИМе (шуме).
 
=Выводы=
[[Файл:Raven-Low.jpg|мини|SilverStone Raven v03]]
До +30°C на улице большой внешний вентилятор сбоку более не нужен: игрушки на ультрах в высоком разрешении прогревают самый горячий компонент видюхи не выше 96°C, тогда как прежде и с вентилятором за сотку вылезало — теперь такое только при стресс-тестах, на которых до того система попросту аварийно отключалась. Как будет в самую жару, опробовать пока не довелось, но по результатам отчитаюсь.
 
Когда не ломанная / не чиненная железяка работает не так, как ожидается, порой на помощь спешат чип и… программные средства. Пусть без рюшечек и фантиков, зато задача решена на системном уровне. Ну, и есть надежда, что дружелюбного софта тоже не придётся долго ждать.
 
=Итоги=
…А после всех потуг открыл для себя {{cmd|fancontrol}}, который:
*запускается как служба;
*распоряжается всеми ШИМ-вентиляторами, подключёнными к мат.плате;
*настраивается утилиткой <code>pwmconfig</code>;
*в дальнейшем правится вручную через конфиг.
Главное — правильно сопоставить конкретные вентиляторы остужаемым компонентам, не обязательно встроенным в материнку. При этом зависимость от датчиков вращения столь же необязательна. Вот пример моего конфига (уже под корпус Raven v03, ''см. ниже''):
{|class="mw-collapsible mw-collapsed wikitable"
!/etc/fancontrol &nbsp;
|-
|<source lang=bash>
# вентилятор    об/мин      -/+ШИМ  дБ/шум
##########################################
# AiGo          700..1700  48/80  2
# ArcticCool    450..1650  54/60  2
# Titan        300..2150  80/84  1
# Lucifer      650..1500  85/87  4
# AeroCool      550..1300  85/90  3
# Reeven        350..1700  80/95  6
# ID-Cooling    500..1800  65/68  5
# RX-6700XT    450..3200  55/68  7
 
# hwmon    разъём  вентилятор  где
##########################################
# 2/pwm1    Pump    AiGo        верх+морда
# 2/pwm2    CPU    ID-Cooling  проц
# 2/pwm3    Sys1    Reeven      бок, hddfancontrol
# 2/pwm4    Sys2    AiGo        тыл
# 2/pwm5    Sys3    Lucifer    крышка
# 2/temp7  ЦПУ
# 0/pwm1    видюха: 1) ГПУ, 2) переход, 3) память
 
INTERVAL=10
DEVPATH=    hwmon0=devices/pci0000:00/0000:00:03.1/0000:2d:00.0/0000:2e:00.0/0000:2f:00.0  hwmon1=devices/pci0000:00/0000:00:18.3  hwmon2=devices/platform/nct6775.2592
DEVNAME=    hwmon0=amdgpu                  hwmon1=k10temp                  hwmon2=nct6797
FCTEMPS=    hwmon0/pwm1=hwmon0/temp2_input  hwmon2/pwm1=hwmon2/temp7_input  hwmon2/pwm2=hwmon2/temp7_input  hwmon2/pwm4=hwmon0/temp2_input  hwmon2/pwm5=hwmon0/temp2_input
FCFANS=    hwmon0/pwm1=hwmon0/fan1_input  hwmon2/pwm1=hwmon2/fan1_input  hwmon2/pwm2=hwmon2/fan2_input  hwmon2/pwm4=hwmon2/fan4_input  hwmon2/pwm5=hwmon2/fan5_input
MINTEMP=    hwmon0/pwm1=55                  hwmon2/pwm1=45                  hwmon2/pwm2=45                  hwmon2/pwm4=48                  hwmon2/pwm5=48
MAXTEMP=    hwmon0/pwm1=85                  hwmon2/pwm1=80                  hwmon2/pwm2=80                  hwmon2/pwm4=85                  hwmon2/pwm5=85
MINSTOP=    hwmon0/pwm1=55                  hwmon2/pwm1=65                  hwmon2/pwm2=65                  hwmon2/pwm4=65                  hwmon2/pwm5=85
MINSTART=  hwmon0/pwm1=63                  hwmon2/pwm1=68                  hwmon2/pwm2=68                  hwmon2/pwm4=68                  hwmon2/pwm5=87
</source>
|}
'''Обратите внимание:''' знак равенства не должен отделяться пропусками от имени опции, а вот значения, которых может быть более одного — вполне себе.
 
<u>P.S.</u> И ещё поменял корпус на огромный AeroCool xPredator FullTower с кучей больших вентиляторов (6х14 и 2х22 см) — теперь совсем красота!
 
<u>UPD:</u> Поменял и этот на не хуже продуваемый, но более компактный и удобный SilverStone Raven v03 (мат.плата в нём развёрнута на 90° по часовой &mdash; портами вверх).
 
=Обратная связь=
*[https://t.me/gbIMoBou @gbIMoBou]
*[[Участник:Дым#Заметки|Другие статьи]]
{{Category navigation|title=Системному администратору|category=Admin|sortkey={{SUBPAGENAME}}}}
[[Категория:Admin]]
[[Категория:Hardware]]
[[Категория:HCL]]

Текущая версия от 11:46, 27 июля 2023

Zalman Z1

Введение

Столкнулся с проблемой перегрева видюхи RX-6700XT на игровых нагрузках — и не только её самой, поскольку она ведь и вокруг себя изрядно греет.

  • Процессор прямо над нею кипятится — мéста между видюхой, крышкой и стенками отнюдь не завались.
  • М2-накопитель посреди видюхи и проца тоже знатно подгорает — хоть и с радиатором, и от проц-кулера на него дует… горячим.
  • Дополнительным винчестерам в корзине, вставленной в пространство 5-дюймовых отсеков, жарко и душно.
  • Да и тем, что в нижних 3,5-дюймовых, ни разу не уютно.

До некоторой степени спасает вариант поставить перед открытой крышкой системника большой настольный вентилятор (см. фото) — для всего, кроме самой видюхи: она ж к нему краем, и толку почти ноль. А заводская авторегулировка видюхиного ШИМа как-то не особо способствует её охлаждению даже зимой, о жаре за бортом вовсе молчу.

Реализация

AeroCool xPredator FullTower

Пошарив по просторам, нарыл массу графических «мониторилок» — но ни одной «крутилки» именно для АМД, хотя для ненаВидии — завались (один лишь «позеленевший от зависти» чего стóит). И это невзирая на то, что всё нужное доступно прямо из системы, без нужды ковыряться в проприетарщине… Странное дело.

При этом для командной строки нарубинено да напитонено всякого, но к чему плодить сущности, если всё можно сделать перманентно доступным шеллом? Проникся довольно замороченным решением amdgpu-fancontrol и по образу/подобию слепил на скорую руку собственный упрощённый вариант, для которого понадобится тройка файлов — скрипт да конфиг c юнитом к нему (выложены на ГитХаб).

1) Файл настроек

/etc/sysconfig/amdgpufan  
# restart service amdgpufan if config changed
card=0          # vega rather has number 1
rate=3          # check frequency
step=5          # min to max divider
within=2        # temperature borders and pwm diff tolerance
min_tmp=45      # no need cooling lower
sensor=junction # 'edge' (gpu), 'mem' or 'junction' (hottest one)

2) Запускающий юнит

/lib/systemd/system/amdgpufan.service  
[Unit]
Description = AMD GPU fan control

[Service]
ExecStart = /usr/local/sbin/amdgpufan
Restart = on-failure
RestartSec = 5

[Install]
WantedBy = default.target

Как включать юниты, все давно знают. :)

3) Сам скрипт

/usr/local/sbin/amdgpufan  
#!/bin/sh

[ $UID -eq 0 ] || { # Check user permissions.
    echo "Writing to /sys requires root privileges."
    exit 1
}

## User defaults if config absend ################################
card=0          # vega rather has number 1
rate=3          # check frequency
step=5          # min to max divider
within=2        # temperature borders and pwm diff tolerance
min_tmp=45      # no need cooling lower
sensor=junction # 'edge' (gpu), 'mem' or 'junction' (hottest one)

config=/etc/sysconfig/amdgpufan
[ -f $config -a -s $config ] && source $config ||
echo "User settings not found, using default values."

case $sensor in
    junction) # let's add some degrees to most hot sensor
         let min_tmp+=5
         sensor=2;;
    edge)sensor=1;;
    mem) sensor=3;;
esac

## System variables ##############################################
gpuinfo=/sys/kernel/debug/dri/$card/amdgpu_pm_info
dev_dir=/sys/class/drm/card$card/device
sys_dir=`ls -d $dev_dir/hwmon/*`
sys_lvl=${dev_dir}/power_dpm_force_performance_level
sys_clk=${dev_dir}/pp_od_clk_voltage
sys_tmp=${sys_dir}/temp${sensor}_input
sys_pwm=${sys_dir}/pwm1
sys_fan=${sys_pwm}_enable

## Calculated variables ##########################################
part=$[step-2] # loops' divider: steps num except first and last
# Achievable frequencies for gpu & mem:
mhz=(`awk '/^(M|S)/{gsub("Mhz","");print $3}' $sys_clk | sort -V`)
max_tmp=$[`cat $sys_dir/temp*_crit | sort -V | head -1`/1000]
max_pwm=`cat ${sys_pwm}_max`
min_pwm=`cat ${sys_pwm}_min`
gap_pwm=$[(max_pwm-min_pwm)/step]
gap_tmp=$[(max_tmp-min_tmp)/step]

reset(){ # Sudden break.
    printf "Exiting and s"
    setmode auto
    exit 0
}

setmode(){ # Change as mode as gpu & mem frequency.
    echo "etting clock & fan mode to '$1'."
    case $1 in
        auto)   mode=2;;
        manual) mode=1
                echo m 1 ${mhz[0]}  >$sys_clk
                echo s 1 ${mhz[1]}  >$sys_clk
        ;;
    esac
    echo $mode  >$sys_fan
    echo $1     >$sys_lvl
}

main(){ # Whole magic.
    cur_pwm=`cat $sys_pwm`
    cur_tmp=$[`cat $sys_tmp`/1000]
    [[ `cat $sys_fan` -eq 1 ]] || {
        printf "S"
        setmode manual
    }
    if [[ $cur_tmp -gt $max_tmp ]]; then
        echo "$cur_tmp°C exceeds critical, set 100% blowing."
        new_pwm=$max_pwm
    elif [[ $cur_tmp -le $min_tmp ]]; then
        new_pwm=$min_pwm
    else
        for i in `seq 0 $part`; do
            [[ $cur_tmp -ge $[min_tmp+gap_tmp*i]     ]] &&
            [[ $cur_tmp -le $[min_tmp+gap_tmp*(i+1)] ]] && {
                new_pwm=$[min_pwm+gap_pwm*(i+1)]
                break
            }
        done
    fi
    printf "%3d°C @%3d PWM" $cur_tmp $cur_pwm
    [[ $cur_pwm -ge $[new_pwm-within] ]] &&
    [[ $cur_pwm -le $[new_pwm+within] ]] &&
    echo "." || { # Change PWM if differ.
        for i in `seq 0 $part`; do
            [[ $cur_tmp -ge $[min_tmp+gap_tmp*i-within] ]] &&
            [[ $cur_tmp -le $[min_tmp+gap_tmp*i+within] ]] && {
                echo "."
                break
            }
        done || {
            printf ", switching to %3d.\n" $new_pwm
            echo $new_pwm >$sys_pwm
        }
    }
}

trap "reset" SIGINT SIGTERM # Handle signals.

while :; do # Go on!
    main
    sleep ${rate}s
done

Пояснения

  1. Пользовательскими переменными из конфига задаю:
    • card — № видюхи (судя по статьям от спецов, при наличии встройки оная будет пронумерована единичкой);
    • min_tmp — температуру, ниже которой мослать вентилятором бессмысленно (с завода вентилятор не включается до 52°C процессора, а при остывании останавливается на 46°C);
    • sensor — какой из датчиков «пасти»: процессора, самый холодный (памяти) или самый горячий (температуры перехода), однако max_tmp выбираю минимальную из критических;
    • rate — частоту опроса со сменой значения в ШИМ-контроллере при выходе температуры из предыдущего диапазона (не обязательно делать это ежесекундно, чтоб и не дать перегреться, и не надоело листать портянку журнала);
    • step — число сегментов, на которые будет разбит как диапазон регулируемых температур, так и шкала делений ШИМ-контроллера (вроде процентной, но не на 100, а на 255 делений);
    • whithin — допустимый разброс задаваемого значения ШИМ с не всегда одинаковым текущим, а также перехлёст температурных диапазонов, дабы вентилятор не дёргался на градус разницы, внося сумятицу в умы и сердца.
  2. Запускать скрипт по таймеру не стал, оставил а-ля «резидентным» (см. цикл while), дабы между его запусками не бросать видюху в ручном режиме без управления (см. функцию setmode).

Результаты

Утро/вечер: веб-сёрфинг, общение, редактирование и пр.

Переключение ШИМа на 20% (51) после примерно 5-минутного прогрева до 48°C, далее — колебания в пределах 50..55°C.

Отход на перекус — температура примерно за то же время падает до 42°C, ШИМ обнуляется, далее — колебания как ночью.

Будним днём с повышением забортной температуры системная тоже подрастает и гуляет в рамках 40°C+ без запуска вентиляторов.

Мёртвая петля (DeathLoop) на максималках

Колебания температуры между 59 и 76°C с прыжками ШИМ от 51 до 102, 153 и обратно:

июн 03 21:17:40 comp.ill amdgpufan[1492297]:  66°C @153 PWM, switching to 102.
июн 03 21:17:43 comp.ill amdgpufan[1492297]:  72°C @102 PWM.
июн 03 21:17:46 comp.ill amdgpufan[1492297]:  72°C @102 PWM.
июн 03 21:17:49 comp.ill amdgpufan[1492297]:  70°C @102 PWM.
июн 03 21:17:52 comp.ill amdgpufan[1492297]:  76°C @102 PWM, switching to 153.
июн 03 21:17:55 comp.ill amdgpufan[1492297]:  75°C @153 PWM.
...
июн 03 21:18:07 comp.ill amdgpufan[1492297]:  59°C @153 PWM.
июн 03 21:18:10 comp.ill amdgpufan[1492297]:  57°C @153 PWM, switching to  51.

Насыщенная сцена:

июн 03 21:18:13 comp.ill amdgpufan[1492297]:  82°C @ 51 PWM.
июн 03 21:18:16 comp.ill amdgpufan[1492297]:  84°C @ 51 PWM.
июн 03 21:18:19 comp.ill amdgpufan[1492297]:  87°C @ 51 PWM, switching to 204.
июн 03 21:18:22 comp.ill amdgpufan[1492297]:  87°C @204 PWM.
...
июн 03 21:18:43 comp.ill amdgpufan[1492297]:  87°C @204 PWM.
июн 03 21:18:46 comp.ill amdgpufan[1492297]:  78°C @204 PWM, switching to 153.
июн 03 21:18:49 comp.ill amdgpufan[1492297]:  59°C @153 PWM.
июн 03 21:18:52 comp.ill amdgpufan[1492297]:  83°C @153 PWM.
июн 03 21:18:55 comp.ill amdgpufan[1492297]:  85°C @153 PWM.
июн 03 21:18:58 comp.ill amdgpufan[1492297]:  85°C @153 PWM.
июн 03 21:19:01 comp.ill amdgpufan[1492297]:  86°C @153 PWM, switching to 204.
июн 03 21:19:04 comp.ill amdgpufan[1492297]:  86°C @204 PWM.
...
июн 03 21:19:49 comp.ill amdgpufan[1492297]:  85°C @204 PWM.
июн 03 21:19:52 comp.ill amdgpufan[1492297]:  60°C @204 PWM.
июн 03 21:19:55 comp.ill amdgpufan[1492297]:  58°C @204 PWM, switching to  51.
июн 03 21:19:58 comp.ill amdgpufan[1492297]:  57°C @ 51 PWM.
...
июн 03 21:20:22 comp.ill amdgpufan[1492297]:  58°C @ 51 PWM.
июн 03 21:20:25 comp.ill amdgpufan[1492297]:  75°C @ 51 PWM, switching to 153.
июн 03 21:20:28 comp.ill amdgpufan[1492297]:  75°C @153 PWM.

Далее как в начале игры.

Судя по графику, выше 90°C не поднималось, обороты на максимум — тоже. ЦПУ греется сопоставимо, но почти всю игру 75..90°C.

Ночь

41±2°C при нулевом ШИМе (шуме).

Выводы

SilverStone Raven v03

До +30°C на улице большой внешний вентилятор сбоку более не нужен: игрушки на ультрах в высоком разрешении прогревают самый горячий компонент видюхи не выше 96°C, тогда как прежде и с вентилятором за сотку вылезало — теперь такое только при стресс-тестах, на которых до того система попросту аварийно отключалась. Как будет в самую жару, опробовать пока не довелось, но по результатам отчитаюсь.

Когда не ломанная / не чиненная железяка работает не так, как ожидается, порой на помощь спешат чип и… программные средства. Пусть без рюшечек и фантиков, зато задача решена на системном уровне. Ну, и есть надежда, что дружелюбного софта тоже не придётся долго ждать.

Итоги

…А после всех потуг открыл для себя fancontrol, который:

  • запускается как служба;
  • распоряжается всеми ШИМ-вентиляторами, подключёнными к мат.плате;
  • настраивается утилиткой pwmconfig;
  • в дальнейшем правится вручную через конфиг.

Главное — правильно сопоставить конкретные вентиляторы остужаемым компонентам, не обязательно встроенным в материнку. При этом зависимость от датчиков вращения столь же необязательна. Вот пример моего конфига (уже под корпус Raven v03, см. ниже):

/etc/fancontrol  
# вентилятор    об/мин      -/+ШИМ  дБ/шум
##########################################
# AiGo          700..1700   48/80   2
# ArcticCool    450..1650   54/60   2
# Titan         300..2150   80/84   1
# Lucifer       650..1500   85/87   4
# AeroCool      550..1300   85/90   3
# Reeven        350..1700   80/95   6
# ID-Cooling    500..1800   65/68   5
# RX-6700XT     450..3200   55/68   7

# hwmon     разъём  вентилятор  где
##########################################
# 2/pwm1    Pump    AiGo        верх+морда
# 2/pwm2    CPU     ID-Cooling  проц
# 2/pwm3    Sys1    Reeven      бок, hddfancontrol
# 2/pwm4    Sys2    AiGo        тыл
# 2/pwm5    Sys3    Lucifer     крышка
# 2/temp7   ЦПУ
# 0/pwm1    видюха: 1) ГПУ, 2) переход, 3) память

INTERVAL=10
DEVPATH=    hwmon0=devices/pci0000:00/0000:00:03.1/0000:2d:00.0/0000:2e:00.0/0000:2f:00.0   hwmon1=devices/pci0000:00/0000:00:18.3  hwmon2=devices/platform/nct6775.2592
DEVNAME=    hwmon0=amdgpu                   hwmon1=k10temp                  hwmon2=nct6797
FCTEMPS=    hwmon0/pwm1=hwmon0/temp2_input  hwmon2/pwm1=hwmon2/temp7_input  hwmon2/pwm2=hwmon2/temp7_input  hwmon2/pwm4=hwmon0/temp2_input  hwmon2/pwm5=hwmon0/temp2_input
FCFANS=     hwmon0/pwm1=hwmon0/fan1_input   hwmon2/pwm1=hwmon2/fan1_input   hwmon2/pwm2=hwmon2/fan2_input   hwmon2/pwm4=hwmon2/fan4_input   hwmon2/pwm5=hwmon2/fan5_input
MINTEMP=    hwmon0/pwm1=55                  hwmon2/pwm1=45                  hwmon2/pwm2=45                  hwmon2/pwm4=48                  hwmon2/pwm5=48
MAXTEMP=    hwmon0/pwm1=85                  hwmon2/pwm1=80                  hwmon2/pwm2=80                  hwmon2/pwm4=85                  hwmon2/pwm5=85
MINSTOP=    hwmon0/pwm1=55                  hwmon2/pwm1=65                  hwmon2/pwm2=65                  hwmon2/pwm4=65                  hwmon2/pwm5=85
MINSTART=   hwmon0/pwm1=63                  hwmon2/pwm1=68                  hwmon2/pwm2=68                  hwmon2/pwm4=68                  hwmon2/pwm5=87

Обратите внимание: знак равенства не должен отделяться пропусками от имени опции, а вот значения, которых может быть более одного — вполне себе.

P.S. И ещё поменял корпус на огромный AeroCool xPredator FullTower с кучей больших вентиляторов (6х14 и 2х22 см) — теперь совсем красота!

UPD: Поменял и этот на не хуже продуваемый, но более компактный и удобный SilverStone Raven v03 (мат.плата в нём развёрнута на 90° по часовой — портами вверх).

Обратная связь