Скрипт контроля частот и вентиляторов видеокарт AMD Radeon: различия между версиями
Материал из Linux Wiki
Перейти к навигацииПерейти к поиску
Rain (обсуждение | вклад) |
Rain (обсуждение | вклад) |
||
Строка 8: | Строка 8: | ||
<source lang=awk> | <source lang=awk> | ||
#!/bin/bash | #!/bin/bash | ||
# By Rain ;) | |||
################################### | |||
# IDLE ZONE - [hyst]-[tt1]-[hyst] - ZONE 1 - [hyst]-[tt2]-[hyst] - ZONE 2 - [hyst]-[toh] - OVERHEAT ZONE [Cut-off] - CUT-OFF ZONE | |||
# For example, for 65 / 70 / 80 / 85 and hysteresis level = 1; and fan min=15; target=60 and max=100: | |||
# .............. | 64-66 | ............... | 69-71 | .............. | 79-80 | .......................... | 85 ................. | |||
# idle | | zone1 | | zone2 | | overheat | cutoff | |||
# fan down to 15 | do noth. | fan up to 60 | do noth. | fan up to 100 | do noth. | fan to 100; clock step down | halt all; fan to 100 | |||
# | |||
# For idle zone fan decremental step is 1% | |||
# For zone 1 fan incremental step is 2% | |||
# For zone 2 fan incremental step is 5% | |||
# | |||
# If temperature goes lower than "$toh-(double hysteresis)" - <=77 C - core clock will be restored to config values | |||
# | |||
# TODO: looks like, no need to use hysteresis in between zone2 and overheat zones anymore | |||
################################### | |||
tt1='65' # First target temperature. Fan will be between fmin and tfan | |||
tt2='70' # Second target temperatune. Fan will be up to fmax | |||
toh='80' # Overheat temperature. Reducing frequencies, fan to fmax | |||
tco='85' # Cut-off temperature. Shutting down all mining processes | |||
hyst='1' # Hysteresis | |||
conf='/home/rain/config/cards.conf' | fmin='15' # Minimal fan speed | ||
tfan='60' # Maximum fan speed for first zone | |||
fmax='100' # Maximum fan speed | |||
ohfs='5' # Overheat frequency reducing step | |||
fups='50' # Clock restore step | |||
trst='75' # Clock restore temperature | |||
sleep='5' # internal timer | |||
corelow='300' | |||
memlow='1000' # not used | |||
conf='/home/rain/config/cards.conf' # cgminer/sgminer config file for getting core/mem frequencies | |||
rrdb='/home/rain/.gpustat.rrd' | |||
site='/tmp/site' | |||
################################### | |||
if [ "${1}" != 'single' ] | |||
then | |||
mkdir -p -m 755 "${site}" | |||
if [ -z "${RRD}" ] | |||
then | |||
if [ -x '/usr/bin/rrdtool' ] | |||
then | |||
rrd='nice -n 19 /usr/bin/rrdtool' | |||
fi | |||
else | |||
rrd=': ' | |||
fi | |||
[ ! -e "${rrdb}" ] && ${rrd} create "${rrdb}" \ | |||
--step 60 \ | |||
DS:gpu0temp:GAUGE:120:U:U \ | |||
DS:gpu1temp:GAUGE:120:U:U \ | |||
DS:gpu2temp:GAUGE:120:U:U \ | |||
DS:gpu3temp:GAUGE:120:U:U \ | |||
DS:gpu4temp:GAUGE:120:U:U \ | |||
DS:gpu5temp:GAUGE:120:U:U \ | |||
DS:gpu6temp:GAUGE:120:U:U \ | |||
DS:gpu7temp:GAUGE:120:U:U \ | |||
DS:gpu8temp:GAUGE:120:U:U \ | |||
DS:gpu9temp:GAUGE:120:U:U \ | |||
RRA:MIN:0.5:1:80 \ | |||
RRA:MIN:0.5:8:210 \ | |||
RRA:MIN:0.5:80:600 \ | |||
RRA:MIN:0.5:600:1100 \ | |||
RRA:AVERAGE:0.5:1:80 \ | |||
RRA:AVERAGE:0.5:8:210 \ | |||
RRA:AVERAGE:0.5:80:600 \ | |||
RRA:AVERAGE:0.5:600:1100 \ | |||
RRA:MAX:0.5:1:80 \ | |||
RRA:MAX:0.5:8:210 \ | |||
RRA:MAX:0.5:80:600 \ | |||
RRA:MAX:0.5:600:1100 | |||
mkgraph() { | |||
umask 022 | |||
if [ "$3" == 'normal' ];then | |||
size='--width 400 --height 200' | |||
elif [ "$3" == 'large' ]; then | |||
size='--width 900 --height 450' | |||
fi | |||
$rrd graph "${2}" --imgformat PNG \ | |||
DEF:gpu0tempmi="${1}":gpu0temp:MIN \ | |||
DEF:gpu1tempmi="${1}":gpu1temp:MIN \ | |||
DEF:gpu2tempmi="${1}":gpu2temp:MIN \ | |||
DEF:gpu3tempmi="${1}":gpu3temp:MIN \ | |||
DEF:gpu4tempmi="${1}":gpu4temp:MIN \ | |||
DEF:gpu5tempmi="${1}":gpu5temp:MIN \ | |||
DEF:gpu6tempmi="${1}":gpu6temp:MIN \ | |||
DEF:gpu7tempmi="${1}":gpu7temp:MIN \ | |||
DEF:gpu8tempmi="${1}":gpu8temp:MIN \ | |||
DEF:gpu9tempmi="${1}":gpu9temp:MIN \ | |||
\ | |||
DEF:gpu0tempav="${1}":gpu0temp:AVERAGE \ | |||
DEF:gpu1tempav="${1}":gpu1temp:AVERAGE \ | |||
DEF:gpu2tempav="${1}":gpu2temp:AVERAGE \ | |||
DEF:gpu3tempav="${1}":gpu3temp:AVERAGE \ | |||
DEF:gpu4tempav="${1}":gpu4temp:AVERAGE \ | |||
DEF:gpu5tempav="${1}":gpu5temp:AVERAGE \ | |||
DEF:gpu6tempav="${1}":gpu6temp:AVERAGE \ | |||
DEF:gpu7tempav="${1}":gpu7temp:AVERAGE \ | |||
DEF:gpu8tempav="${1}":gpu8temp:AVERAGE \ | |||
DEF:gpu9tempav="${1}":gpu9temp:AVERAGE \ | |||
\ | |||
DEF:gpu0tempma="${1}":gpu0temp:MAX \ | |||
DEF:gpu1tempma="${1}":gpu1temp:MAX \ | |||
DEF:gpu2tempma="${1}":gpu2temp:MAX \ | |||
DEF:gpu3tempma="${1}":gpu3temp:MAX \ | |||
DEF:gpu4tempma="${1}":gpu4temp:MAX \ | |||
DEF:gpu5tempma="${1}":gpu5temp:MAX \ | |||
DEF:gpu6tempma="${1}":gpu6temp:MAX \ | |||
DEF:gpu7tempma="${1}":gpu7temp:MAX \ | |||
DEF:gpu8tempma="${1}":gpu8temp:MAX \ | |||
DEF:gpu9tempma="${1}":gpu9temp:MAX \ | |||
\ | |||
CDEF:cdefmida=gpu0tempav,gpu0tempmi,- \ | |||
CDEF:cdefmidb=gpu1tempav,gpu1tempmi,- \ | |||
CDEF:cdefmidc=gpu2tempav,gpu2tempmi,- \ | |||
CDEF:cdefmidd=gpu3tempav,gpu3tempmi,- \ | |||
CDEF:cdefmide=gpu4tempav,gpu4tempmi,- \ | |||
CDEF:cdefmidf=gpu5tempav,gpu5tempmi,- \ | |||
CDEF:cdefmidg=gpu6tempav,gpu6tempmi,- \ | |||
CDEF:cdefmidh=gpu7tempav,gpu7tempmi,- \ | |||
CDEF:cdefmidi=gpu8tempav,gpu8tempmi,- \ | |||
CDEF:cdefmidj=gpu9tempav,gpu9tempmi,- \ | |||
\ | |||
CDEF:cdefmada=gpu0tempma,gpu0tempav,- \ | |||
CDEF:cdefmadb=gpu1tempma,gpu1tempav,- \ | |||
CDEF:cdefmadc=gpu2tempma,gpu2tempav,- \ | |||
CDEF:cdefmadd=gpu3tempma,gpu3tempav,- \ | |||
CDEF:cdefmade=gpu4tempma,gpu4tempav,- \ | |||
CDEF:cdefmadf=gpu5tempma,gpu5tempav,- \ | |||
CDEF:cdefmadg=gpu6tempma,gpu6tempav,- \ | |||
CDEF:cdefmadh=gpu7tempma,gpu7tempav,- \ | |||
CDEF:cdefmadi=gpu8tempma,gpu8tempav,- \ | |||
CDEF:cdefmadj=gpu9tempma,gpu9tempav,- \ | |||
\ | |||
LINE2:gpu0tempav#ff0000:'GPU 0' \ | |||
LINE0:gpu0tempmi#ff0000: \ | |||
AREA:cdefmida#ff000044::STACK \ | |||
AREA:cdefmada#ff000044::STACK \ | |||
GPRINT:gpu0tempmi:MIN:"(Min\: %3.1lf°C \t/" \ | |||
GPRINT:gpu0tempav:LAST:"Curr\: %3.1lf°C \t/" \ | |||
GPRINT:gpu0tempma:MAX:"Max\: %3.1lf°C)\n" \ | |||
\ | |||
LINE2:gpu1tempav#ffa800:"GPU 1" \ | |||
LINE0:gpu1tempmi#ffa800: \ | |||
AREA:cdefmidb#ffa80044::STACK \ | |||
AREA:cdefmadb#ffa80044::STACK \ | |||
GPRINT:gpu1tempmi:MIN:"(Min\: %3.1lf°C \t/" \ | |||
GPRINT:gpu1tempav:LAST:"Curr\: %3.1lf°C \t/" \ | |||
GPRINT:gpu1tempma:MAX:"Max\: %3.1lf°C)\n" \ | |||
\ | |||
LINE2:gpu2tempav#fff600:"GPU 2" \ | |||
LINE0:gpu2tempmi#fff600: \ | |||
AREA:cdefmidc#fff60044::STACK \ | |||
AREA:cdefmadc#fff60044::STACK \ | |||
GPRINT:gpu2tempmi:MIN:"(Min\: %3.1lf°C \t/" \ | |||
GPRINT:gpu2tempav:LAST:"Curr\: %3.1lf°C \t/" \ | |||
GPRINT:gpu2tempma:MAX:"Max\: %3.1lf°C)\n" \ | |||
\ | |||
LINE2:gpu3tempav#6cff00:"GPU 3" \ | |||
LINE0:gpu3tempmi#6cff00: \ | |||
AREA:cdefmidd#6cff0044::STACK \ | |||
AREA:cdefmadd#6cff0044::STACK \ | |||
GPRINT:gpu3tempmi:MIN:"(Max\: %3.1lf°C \t/" \ | |||
GPRINT:gpu3tempav:LAST:"Curr\: %3.1lf°C \t/" \ | |||
GPRINT:gpu3tempma:MAX:"Max\: %3.1lf°C)\n" \ | |||
\ | |||
LINE2:gpu4tempav#00ffc0:'GPU 4' \ | |||
LINE0:gpu4tempmi#00ffc0: \ | |||
AREA:cdefmide#00ffc044::STACK \ | |||
AREA:cdefmade#00ffc044::STACK \ | |||
GPRINT:gpu4tempmi:MIN:"(Min\: %3.1lf°C \t/" \ | |||
GPRINT:gpu4tempav:LAST:"Curr\: %3.1lf°C \t/" \ | |||
GPRINT:gpu4tempma:MAX:"Max\: %3.1lf°C)\n" \ | |||
\ | |||
LINE2:gpu5tempav#009cff:'GPU 5' \ | |||
LINE0:gpu5tempmi#009cff: \ | |||
AREA:cdefmidf#009cff44::STACK \ | |||
AREA:cdefmadf#009cff44::STACK \ | |||
GPRINT:gpu5tempmi:MIN:"(Min\: %3.1lf°C \t/" \ | |||
GPRINT:gpu5tempav:LAST:"Curr\: %3.1lf°C \t/" \ | |||
GPRINT:gpu5tempma:MAX:"Max\: %3.1lf°C)\n" \ | |||
\ | |||
LINE2:gpu6tempav#0030ff:'GPU 6' \ | |||
LINE0:gpu6tempmi#0030ff: \ | |||
AREA:cdefmidg#0030ff44::STACK \ | |||
AREA:cdefmadg#0030ff44::STACK \ | |||
GPRINT:gpu6tempmi:MIN:"(Min\: %3.1lf°C \t/" \ | |||
GPRINT:gpu6tempav:LAST:"Curr\: %3.1lf°C \t/" \ | |||
GPRINT:gpu6tempma:MAX:"Max\: %3.1lf°C)\n" \ | |||
\ | |||
LINE2:gpu7tempav#c000ff:'GPU 7' \ | |||
LINE0:gpu7tempmi#c000ff: \ | |||
AREA:cdefmidh#c000ff44::STACK \ | |||
AREA:cdefmadh#c000ff44::STACK \ | |||
GPRINT:gpu7tempmi:MIN:"(Min\: %3.1lf°C \t/" \ | |||
GPRINT:gpu7tempav:LAST:"Curr\: %3.1lf°C \t/" \ | |||
GPRINT:gpu7tempma:MAX:"Max\: %3.1lf°C)\n" \ | |||
\ | |||
LINE2:gpu8tempav#d4d2d5:'GPU 8' \ | |||
LINE0:gpu8tempmi#d4d2d5: \ | |||
AREA:cdefmidi#d4d2d544::STACK \ | |||
AREA:cdefmadi#d4d2d544::STACK \ | |||
GPRINT:gpu8tempmi:MIN:"(Min\: %3.1lf°C \t/" \ | |||
GPRINT:gpu8tempav:LAST:"Curr\: %3.1lf°C \t/" \ | |||
GPRINT:gpu8tempma:MAX:"Max\: %3.1lf°C)\n" \ | |||
\ | |||
LINE2:gpu9tempav#000000:'GPU 9' \ | |||
LINE0:gpu9tempmi#000000: \ | |||
AREA:cdefmidj#00000044::STACK \ | |||
AREA:cdefmadj#00000044::STACK \ | |||
GPRINT:gpu9tempmi:MIN:"(Min\: %3.1lf°C \t/" \ | |||
GPRINT:gpu9tempav:LAST:"Curr\: %3.1lf°C \t/" \ | |||
GPRINT:gpu9tempma:MAX:"Max\: %3.1lf°C)\n" \ | |||
${size} \ | |||
--title "${4}" \ | |||
-s ${5} \ | |||
--watermark="${HOSTNAME}" \ | |||
--slope-mode \ | |||
--lazy 1>/dev/null & | |||
} | |||
# http://localhost/index.html generator | |||
(echo -n "<html><head><style> body {font-family: Courier, monospace;}</style></head><body><b>" | |||
hostname | |||
echo "</b><br><br>" | |||
/home/rain/bin/sysinfo.sh | sed 's/$/<br>/g' | sed 's/ /\ \;/g' | |||
echo "<br><li><a href=/gputemp.html>GPU Temp</a>" | |||
echo "<br><iframe src=/dyn.html frameborder=0 height=50% width=100%></iframe></body></html>") > /tmp/index.html | |||
touch '/tmp/dyn.html' | |||
chmod 644 '/tmp/index.html' '/tmp/dyn.html' | |||
fi | |||
# Never use sgminer API if NOAPI is set | |||
[ ! -z "${NOAPI}" ] && cmd="false :" | [ ! -z "${NOAPI}" ] && cmd="false :" | ||
Строка 31: | Строка 264: | ||
echo -ne "\e[0;36m[\e[0;35m"$(date +%Y-%m-%d" "%H:%M:%S)"\e[0;36m]\e[0;0m " | echo -ne "\e[0;36m[\e[0;35m"$(date +%Y-%m-%d" "%H:%M:%S)"\e[0;36m]\e[0;0m " | ||
} | } | ||
# Allow log for debugging purposes | |||
debugme() { | |||
[ "${DEBUG}" == '1' ] && { | |||
showdate | |||
echo "${1}" | |||
} >> /tmp/ffc.log | |||
} | |||
# Halt all GPU activity. Change it if needed | |||
do_halt() { | do_halt() { | ||
/home/rain/bin/gpuoff 1>/dev/null 2>/dev/null | |||
/home/rain/bin/ethminerctl suspend | |||
pkill -9 Hashcat | |||
} | } | ||
# XMPP notifications | |||
#if [ "${1}" != 'single' ] | |||
# then | |||
# rm -f /tmp/sendxmpp 2>/dev/null | |||
# touch /tmp/sendxmpp | |||
# ( tail -qF /tmp/sendxmpp 2>/dev/null | sendxmpp -c -r $(hostname) -i -t rigs@conference.linuxoid.in ) & | |||
#fi | |||
sayto() { | sayto() { | ||
(cat - | tr -d '\n' ; echo -n " on $(hostname)") | sendxmpp -t -r $(hostname) -c rigs@conference.linuxoid.in | |||
# cat - >> /tmp/sendxmpp | |||
} | } | ||
# cgminer/sgminer core/mem clocks config parser | |||
getcoreconf(){ | getcoreconf(){ | ||
awk '/gpu-engine/{ | |||
gsub(/[0-9]+\-|\"/, "", $3) | |||
split($3, core, ",") | |||
c='$1'+1 | |||
print core[c] | |||
}' "${conf}" | |||
} | } | ||
getmemconf(){ | getmemconf(){ | ||
awk '/gpu-memclock/{ | |||
gsub(/[0-9]+\-|\"/, "", $3) | |||
split($3, mem, ",") | |||
c='$1'+1 | |||
print mem[c] | |||
}' "${conf}" | |||
} | } | ||
# Function for setting GPU clocks. 1-st parameter is a card number. If parameter is not set - all cards are used | |||
setfreqs() { | setfreqs() { | ||
[ -z "${1}" ] && card="$(seq 0 $(($(aticonfig --lsa | grep -c '[0-9]\.')-1)))" || card="${1}" | |||
for i in ${card} | |||
do | |||
# Allowing overclocking | |||
aticonfig --od-enable 1>/dev/null | |||
# Getting core and memory clocks. If getcoreconf/getmemconf returns nothing - using GPU 0 clocks | |||
# (sgminer behavior - if one parameter is set - it used for all cards) | |||
core="$(getcoreconf "${i}")" | |||
[ -z "${core}" ] && core="$(getcoreconf 0)" | |||
mem="$(getmemconf "${i}")" | |||
[ -z "${mem}" ] && core="$(getmemconf 0)" | |||
# Setting GPU clocks | |||
[[ ! -z "${core}" && ! -z "${mem}" ]] && { | |||
aticonfig --od-setclocks=$core,$mem --adapter=$i 1>/dev/null | |||
debugme "Setting freqs for GPU $i" | |||
# Clocks state flag. 1 = clocks set from config file, 2 = clocks is reduced. | |||
freqs[$i]='1' | |||
} | |||
done | |||
# Setting Powertune to 120% via atitweak utility | |||
[ -x /home/rain/bin/atitweak ] && /home/rain/bin/atitweak -p 20 > /dev/null | |||
debugme "Setting powertune" | |||
} | } | ||
################################### | |||
case "${1}" in | case "${1}" in | ||
freqs) | |||
setfreqs | |||
;; | |||
*) | |||
# [ "${1}" != 'single' ] && echo "Hello, i'm online" | sayto | |||
# Main loop | |||
while : | |||
do | |||
# If NOAPI is not set - checking API availibility | |||
$cmd nc -w 1 -z localhost 4028 && { | |||
api='1' # API detected flag | |||
ffci='0' # $fan array initialisation flag | |||
debugme "API detected" | |||
# Fetching API data | |||
devdata="$(/home/rain/bin/api-example.py devs | sed -r 's/, |\{|\}/\n/g;s/(\n)u|'\''/\1/g')" | |||
} || { | |||
api='0' # API not detected | |||
# sgminer process detection ' | |||
# ffci must be set to 0 and $fan array must be filled with real data every time when sgminer started or stopped | |||
if pidof sgminer 1>/dev/null | |||
then | |||
if [ -z "${sgdetect}" ] | |||
then | |||
debugme "sgminer detected" | |||
ffci='0' | |||
sgdetect='1' | |||
fi | |||
else | |||
if [ ! -z "${sgdetect}" ] | |||
then | |||
debugme "sgminer disabled" | |||
unset sgdetect | |||
ffci='0' | |||
fi | |||
fi | |||
debugme "sgminer=$sgdetect" | |||
[ "${ffci}" != '1' ] && { | |||
# init fan array | |||
for j in $(seq 0 $(($(aticonfig --lsa | grep -c '[0-9]\.')-1))) | |||
do | |||
fan[$j]="$(DISPLAY=:0.$j aticonfig --pplib-cmd "get fanspeed 0" | awk '/Result/{gsub(/%/, "", $4); print $4}')" | |||
debugme "Writing array data for GPU $j" | |||
done | |||
# Setting clocks for all cards | |||
if [ "${1}" != 'single' ] | |||
then | |||
setfreqs | |||
fi | |||
ffci='1' | |||
} | |||
# Fetching data from aticonfig utility | |||
atidata="$(aticonfig --odgt --odgc --adapter=all 2>/dev/null)" | |||
debugme "Getting atidata" | |||
#### fans and core frequency control section #### | |||
# Parsing aticonfig output and extracting card number, temperature, core and memory clocks | |||
# TODO: next time use this for all aticonfig data processing. For now we will see core clock updates only in next cycle | |||
for cdat in $(echo "${atidata}" | tr '\n' ' ' | sed 's/Adapter/\n&/g'| sort | sed -nr '/Adapter/N;s/\n/ /gp' | sed -r 's/^Adapter ([0-9]+) - .*Current Peak : *([0-9]+) +([0-9]+).*Temperature - ([0-9]{1,3}).[0-9]+ C.*/\1:\4:\2:\3/g') | |||
do | |||
# Separating values between different variables | |||
cnt="${cdat%:*:*}" | |||
cn="${cnt%:*}" # Card number | |||
#ct="${cnt#*:}" # Card temperature # TODO: use array in the future and remove this variable | |||
ct[$cn]="${cnt#*:}" # Card temperature array | |||
ccmf="${cdat#*:*:}" | |||
ccf="${ccmf%:*}" # Card core clock | |||
cmf="${ccmf#*:}" # Card memory clock | |||
[ "${freqs[$cn]}" == '1' ] && { | |||
cmax[$cn]=${ccf} # max core freq array | |||
mmax[$cn]=${cmf} # max mem freq array | |||
} | |||
debugme "Processing GPU $cn -> ${ct[$cn]} C" | |||
if [ "${1}" != 'single' ] | |||
then | |||
# If card temperature is over $tco - halting mining, fans to $fmax (usually 100%) | |||
[ "${ct[$cn]}" -ge "${tco}" ] && { | |||
# halt mining | |||
debugme "Cutoff temp on $cn" | |||
fan[$cn]=${fmax} | |||
DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fmax}" 1>/dev/null | |||
# showdate >> /tmp/ffc.log | |||
echo "ALERT! Critical temperature (${ct[$cn]}C) on GPU ${cn}" | sayto | |||
do_halt | |||
} | |||
# If card temperature great than $toh, but still less than $tco (80-85 degr. zone) - set fans to max speed and reduce core clocks | |||
[[ "${ct[$cn]}" -ge "${toh}" && "${ct[$cn]}" -lt "${tco}" ]] && { | |||
ftalk='25' | |||
# reduce freqs | |||
debugme "Overheat on ${cn}: ${freqs[$cn]}" | |||
# [ "${freqs[$cn]}" == '1' ] && { | |||
# showdate >> /tmp/ffc.log | |||
# echo "GPU $cn overheat, reducing clocks" >> /tmp/ffc.log | |||
debugme "Setting max fan speed on ${cn}" | |||
fan[$cn]=${fmax} | |||
DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fmax}" 1>/dev/null | |||
[ "${ccf}" -gt "$((${corelow}+${ohfs}))" ] && { | |||
ccf=$((${ccf}-${ohfs})) | |||
[ "${freqs[$cn]}" == '1' ] && { | |||
echo "GPU $cn overheat (${ct[$cn]} C), starting core clock reducing" | sayto | |||
} | |||
freqs[$cn]='2' | |||
debugme "Reducing freqs on $cn to ${ccf}/${cmf}" | |||
aticonfig --od-setclocks=${ccf},${cmf} --adapter=$cn 1>/dev/null | |||
} || { | |||
echo "No more space in frequency range on card ${cn} for clock reducing" | sayto | |||
debugme "No more space in frequency range on card ${cn} for clock reducing" | |||
} | |||
# } | |||
} | |||
# If card temperature goes below overheat level - report about new frequencies | |||
[[ "${ct[$cn]}" -lt "$((${toh}-${hyst}))" ]] && { | |||
[ ${freqs[$cn]} == '2' ] && { | |||
[ ${ftalk} -gt '0' ] && let ftalk-=1 | |||
[ ${ftalk} == '1' ] && { | |||
echo "GPU $cn clocks reduced to ${ccf}/${cmf}" | sayto | |||
ftalk='0' | |||
} | |||
} | |||
} | |||
# If card temperature decreased to safe level - $trst - restore core clocks | |||
[[ "${ct[$cn]}" -le "${trst}" ]] && { | |||
[ "${freqs[$cn]}" == '2' ] && { | |||
[ "$((${cmax[$cn]}-${ccf}))" -gt "${fups}" ] && { | |||
aticonfig --od-setclocks=$((${ccf}+${fups})),$cmf --adapter=$cn 1>/dev/null | |||
} || { | |||
setfreqs "${cn}" | |||
debugme "Clocks restored on GPU $cn" | |||
echo "Clocks restored on GPU $cn" | sayto | |||
} | |||
} | |||
} | |||
# If card temperature in second zone (typically 70-80 degr) - allowing fan speed to be up to $fmax | |||
[[ "${ct[$cn]}" -gt "$((${tt2}+${hyst}))" && "${ct[$cn]}" -lt "$((${toh}-${hyst}))" ]] && { | |||
debugme "2-nd fan zone on $cn" | |||
# allow fans to 100% | |||
[ "${fan[$cn]}" -lt "${fmax}" ] && { | |||
# old="${fan[$cn]}" | |||
let fan[$cn]+=5 | |||
[ "${fan[$cn]}" -lt "${tfan}" ] && { | |||
fan[$cn]=${tfan} | |||
debugme "Fast heating on $cn" | |||
} | |||
[ "${fan[$cn]}" -gt "${fmax}" ] && fan[$cn]="${fmax}" | |||
debugme "Setting fan to ${fan[$cn]} on GPU $cn" | |||
# showdate >> /tmp/ffc.log | |||
# echo "GPU ${cn} ${ct[$cn]}: setting fan $old -> ${fan[$cn]}" >> /tmp/ffc.log | |||
DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fan[$cn]}" 1>/dev/null | |||
} | |||
} | |||
# If card temperature in 1-st zone (65-70 degr) - fan speed can be up to $tfan | |||
[[ "${ct[$cn]}" -gt "$((${tt1}+${hyst}))" && "${ct[$cn]}" -lt "$((${tt2}-${hyst}))" ]] && { | |||
# allow fans to $tfan | |||
debugme "1-nd fan zone on $cn" | |||
[ "${freqs[$cn]}" == '2' ] && { | |||
setfreqs "${cn}" | |||
} | |||
[ "${fan[$cn]}" -lt "${tfan}" ] && { | |||
# old="${fan[$cn]}" | |||
let fan[$cn]+=2 | |||
[ "${fan[$cn]}" -gt "${tfan}" ] && fan[$cn]="${tfan}" | |||
# showdate >> /tmp/ffc.log | |||
# echo "GPU ${cn} ${ct[$cn]}: setting fan $old -> ${fan[$cn]}" >> /tmp/ffc.log | |||
debugme "Setting fan to ${fan[$cn]} on GPU $cn" | |||
DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fan[$cn]}" 1>/dev/null | |||
} | |||
} | |||
# If card temerature less than first target - fan goes idle | |||
[ "${ct[$cn]}" -lt "$((${tt1}-${hyst}))" ] && { | |||
# decrease fan speed | |||
debugme "Low temp on $cn" | |||
# old="${fan[$cn]}" | |||
let fan[$cn]-=1 | |||
[ "${fan[$cn]}" -lt "${fmin}" ] && fan[$cn]="${fmin}" | |||
# showdate >> /tmp/ffc.log | |||
# echo "GPU ${cn} ${ct[$cn]}: setting fan $old -> ${fan[$cn]}" >> /tmp/ffc.log | |||
DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fan[$cn]}" 1>>/dev/null | |||
} | |||
fi | |||
done | |||
# TODO: сделать это все как-то покрасивее; возможно - вынести 60 тут и выше (создание базы) в переменную | |||
[ -z "${st}" ] && st='60' | |||
let st-=$((60/$sleep)) | |||
[ "${st}" -le '0' ] && { | |||
st='60' | |||
$rrd update "${rrdb}" N$( | |||
s=10 | |||
for t in ${!ct[@]} | |||
do | |||
echo -n ":${ct[$t]}" | |||
let s-=1 | |||
done | |||
for i in $(seq 1 $s) | |||
do | |||
echo -n :U | |||
done | |||
) | |||
mkgraph "${rrdb}" "${site}"/gpustat.h.png 'normal' "GPU Temp, час" -60min | |||
mkgraph "${rrdb}" "${site}"/gpustat.h.large.png 'large' "GPU Temp, час" -60min | |||
mkgraph "${rrdb}" "${site}"/gpustat.d.png 'normal' "GPU Temp, день" -24h | |||
mkgraph "${rrdb}" "${site}"/gpustat.d.large.png 'large' "GPU Temp, день" -24h | |||
mkgraph "${rrdb}" "${site}"/gpustat.m.png 'normal' "GPU Temp, месяц" -30d | |||
mkgraph "${rrdb}" "${site}"/gpustat.m.large.png 'large' "GPU Temp, месяц" -30d | |||
mkgraph "${rrdb}" "${site}"/gpustat.y.png 'normal' "GPU Temp, год" -12mon | |||
mkgraph "${rrdb}" "${site}"/gpustat.y.large.png 'large' "GPU Temp, год" -12mon | |||
} | |||
devdata="$(( | |||
# Exporting fan speed for 2-nd part of this script | |||
for f in ${!fan[@]} | |||
do | |||
echo "Fan Speed $f ${fan[$f]}" | |||
done | |||
# ...and exporting frequency state | |||
# | for c in ${!freqs[@]} | ||
do | |||
echo "Freq State $c ${freqs[$c]}" | |||
done | |||
# Transforming aticonfig data for sgminer API compatible format... With some additions | |||
echo "${atidata}" | |||
) | awk ' | |||
/^Adapter/{ | |||
a=$2 | |||
name=$0 | |||
gsub(/^Adapter.* - /, "", name) | |||
} | |||
/Temp/{ | |||
OFMT="%.f" | |||
temp[a]=$5+0 | |||
} | |||
/Speed/{ | |||
speed[$3]=$4 | |||
} | |||
/Freq State/{ | |||
freqs[$3]=$4 | |||
} | |||
/Clocks/{ | |||
core[a]=$4 | |||
mem[a]=$5 | |||
} | |||
/load/{ | |||
load[a]=$4 | |||
gsub(/%/, "", load[a]) | |||
print "GPU: "a | |||
print "Devname: "name | |||
print "Temperature: " temp[a] | |||
print "Fan Percent: " speed[a] | |||
print "Freq State: " freqs[a] | |||
print "GPU Clock: " core[a] | |||
print "Memory Clock: " mem[a] | |||
print "GPU Activity: " load[a] | |||
print "Status: uAlive" | |||
print "Enabled: U" | |||
print "Utility" | |||
} | |||
')" | |||
} | |||
# Indication part | |||
data="$(echo -n "${devdata}" | gawk ' | |||
function pbar(prog, min, max) | |||
{ | |||
p="" | |||
if (min=="") min=10 | |||
if (max=="") max=100 | |||
inc=((max-min)/10)+0.1 | |||
for (i=min; i<=max; i+=inc) { | |||
if (i>prog) p=p" " | |||
else p=p"|" | |||
} | |||
return p | |||
} | |||
/Temperature/{ | |||
OFMT="%.f" | |||
temp=$2+0 | |||
if (temp>79) ctemp="\\e[5;91m" | |||
else | |||
if (temp>69) ctemp="\\e[0;33m" | |||
else | |||
ctemp="\\e[0;32m" | |||
} | |||
/Fan Percent/{ | |||
OFMT="%.f" | |||
fan=$3+0 | |||
if (fan>75) cfan="\\e[31m" | |||
else | |||
if (fan>45) cfan="\\e[33m" | |||
else | |||
cfan="\\e[32m" | |||
} | |||
/Freq State/{ | |||
if ($3=="1") fstate="\\e[32m" | |||
if ($3=="2") fstate="\\e[91m" | |||
} | |||
/GPU Activity/{ | |||
act=$3 | |||
if (act<80) cact="\\e[0;31m" | |||
else | |||
if (act<90) cact="\\e[0;33m" | |||
else | |||
cact="\\e[0;32m" | |||
} | |||
/GPU Clock/{ | |||
core=$3 | |||
} | |||
/Devname/{ | |||
name=$0 | |||
gsub(/Devname: /, " | ", name) | |||
} | |||
/Status/{ | |||
stat=$2 | |||
if (stat=="uAlive") stat="\\e[36m" | |||
else stat="\\e[5;31m" | |||
} | |||
/Memory Clock/{ | |||
mem=$3 | |||
} | |||
/Enabled/{ | |||
en=$2 | |||
if (en=="uN") en="X" | |||
else | |||
if (en=="U") en="U" | |||
else en="Y" | |||
} | |||
/^GPU:/{ | |||
gnum=$2 | |||
} | |||
/Utility/{ | |||
printf "%-67s %-48s %-22s %-61s %s\n", | |||
stat en " GPU "gnum": \\e[0;0m["ctemp pbar(temp, 30, 85) "\\e[0m] " ctemp temp"C\\e[0m", | |||
"| FAN: ["cfan pbar(fan)"\\e[0m] " cfan fan"% \\e[0m", | |||
"| "fstate core"/"mem"\\e[0m", | |||
"| Load: \\e[0;0m["cact pbar(act, 50, 99) "\\e[0m] " cact act"%\\e[0;0m", | |||
name | |||
}' | |||
)" | |||
[ "${api}" == '1' ] &&\ | |||
data="\t\t\t=========== sgminer stat ===========\n${data}" ||\ | |||
data="\t\t\t=========== aticonfig stat ===========\n${data}" | |||
[ "${1}" == 'single' ] && { | |||
echo -e "${data}" | |||
break | |||
} || { | |||
clear | |||
echo -e "${data}" | |||
# http://localhost/dyn.html generator | |||
(echo -ne "\n* " | |||
echo $(/home/rain/bin/ethspeed single) @ $(date +%H:%M:%S) | |||
echo -ne "\n" | |||
echo -e "${data}") | LANG=C awk ' | |||
BEGIN { | |||
print "<html>" | |||
print "<title>"strftime()"</title>" | |||
print "<meta http-equiv=\"refresh\" content=\"10;\">" | |||
print "<head><style> body '{'font-family: Courier, monospace;'}'</style></head><body>" | |||
} | |||
{ | |||
gsub(/ /, "\\ ", $0) | |||
gsub(/\t/, "\\ ", $0) | |||
gsub(/$/, "<br>", $0) | |||
gsub(/\[([0-9];)?31m/, "</font><font color=red>", $0) | |||
gsub(/\[([0-9];)?91m/, "</font><font color=red><span class=redcode>", $0) | |||
gsub(/\[([0-9];)?33m/, "</font><font color=darkorange><span class=yellowcode>", $0) | |||
gsub(/\[([0-9];)?32m/, "</font><font color=green><span class=greencode>", $0) | |||
gsub(/\[36m/, "<font color=blue>", $0) | |||
gsub(/\[(0;)?0m/, "</font></span>", $0) | |||
print $0 | |||
} | |||
END { | |||
print "</body></html>" | |||
}' > /tmp/dyn.html | |||
} | |||
debugme "Sleeping..." | |||
sleep "${sleep}" | |||
done | |||
;; | |||
esac | esac | ||
</source> | </source> | ||
[[Category:Майнинг]][[Category:Radeon]][[Category:Видеокарта]] | [[Category:Майнинг]][[Category:Radeon]][[Category:Видеокарта]] |
Версия 11:47, 11 июля 2016
Данный скрипт предназначен для управления частотами и вентиляторами в ригах на базе видеокарт AMD Radeon. Помимо управления, скрипт выводит текущее состояние карт. Если доступно API sgminer (или подобного майнера с такой же структурой вывода данных), то используются его данные и управление отдается ему, а скрипт просто выводит статистику.
ToDo: Дописать
ToDo: В setfreqs $i лишняя. Убрать и/или переделать на возможность указания определенной карты
#!/bin/bash
# By Rain ;)
###################################
# IDLE ZONE - [hyst]-[tt1]-[hyst] - ZONE 1 - [hyst]-[tt2]-[hyst] - ZONE 2 - [hyst]-[toh] - OVERHEAT ZONE [Cut-off] - CUT-OFF ZONE
# For example, for 65 / 70 / 80 / 85 and hysteresis level = 1; and fan min=15; target=60 and max=100:
# .............. | 64-66 | ............... | 69-71 | .............. | 79-80 | .......................... | 85 .................
# idle | | zone1 | | zone2 | | overheat | cutoff
# fan down to 15 | do noth. | fan up to 60 | do noth. | fan up to 100 | do noth. | fan to 100; clock step down | halt all; fan to 100
#
# For idle zone fan decremental step is 1%
# For zone 1 fan incremental step is 2%
# For zone 2 fan incremental step is 5%
#
# If temperature goes lower than "$toh-(double hysteresis)" - <=77 C - core clock will be restored to config values
#
# TODO: looks like, no need to use hysteresis in between zone2 and overheat zones anymore
###################################
tt1='65' # First target temperature. Fan will be between fmin and tfan
tt2='70' # Second target temperatune. Fan will be up to fmax
toh='80' # Overheat temperature. Reducing frequencies, fan to fmax
tco='85' # Cut-off temperature. Shutting down all mining processes
hyst='1' # Hysteresis
fmin='15' # Minimal fan speed
tfan='60' # Maximum fan speed for first zone
fmax='100' # Maximum fan speed
ohfs='5' # Overheat frequency reducing step
fups='50' # Clock restore step
trst='75' # Clock restore temperature
sleep='5' # internal timer
corelow='300'
memlow='1000' # not used
conf='/home/rain/config/cards.conf' # cgminer/sgminer config file for getting core/mem frequencies
rrdb='/home/rain/.gpustat.rrd'
site='/tmp/site'
###################################
if [ "${1}" != 'single' ]
then
mkdir -p -m 755 "${site}"
if [ -z "${RRD}" ]
then
if [ -x '/usr/bin/rrdtool' ]
then
rrd='nice -n 19 /usr/bin/rrdtool'
fi
else
rrd=': '
fi
[ ! -e "${rrdb}" ] && ${rrd} create "${rrdb}" \
--step 60 \
DS:gpu0temp:GAUGE:120:U:U \
DS:gpu1temp:GAUGE:120:U:U \
DS:gpu2temp:GAUGE:120:U:U \
DS:gpu3temp:GAUGE:120:U:U \
DS:gpu4temp:GAUGE:120:U:U \
DS:gpu5temp:GAUGE:120:U:U \
DS:gpu6temp:GAUGE:120:U:U \
DS:gpu7temp:GAUGE:120:U:U \
DS:gpu8temp:GAUGE:120:U:U \
DS:gpu9temp:GAUGE:120:U:U \
RRA:MIN:0.5:1:80 \
RRA:MIN:0.5:8:210 \
RRA:MIN:0.5:80:600 \
RRA:MIN:0.5:600:1100 \
RRA:AVERAGE:0.5:1:80 \
RRA:AVERAGE:0.5:8:210 \
RRA:AVERAGE:0.5:80:600 \
RRA:AVERAGE:0.5:600:1100 \
RRA:MAX:0.5:1:80 \
RRA:MAX:0.5:8:210 \
RRA:MAX:0.5:80:600 \
RRA:MAX:0.5:600:1100
mkgraph() {
umask 022
if [ "$3" == 'normal' ];then
size='--width 400 --height 200'
elif [ "$3" == 'large' ]; then
size='--width 900 --height 450'
fi
$rrd graph "${2}" --imgformat PNG \
DEF:gpu0tempmi="${1}":gpu0temp:MIN \
DEF:gpu1tempmi="${1}":gpu1temp:MIN \
DEF:gpu2tempmi="${1}":gpu2temp:MIN \
DEF:gpu3tempmi="${1}":gpu3temp:MIN \
DEF:gpu4tempmi="${1}":gpu4temp:MIN \
DEF:gpu5tempmi="${1}":gpu5temp:MIN \
DEF:gpu6tempmi="${1}":gpu6temp:MIN \
DEF:gpu7tempmi="${1}":gpu7temp:MIN \
DEF:gpu8tempmi="${1}":gpu8temp:MIN \
DEF:gpu9tempmi="${1}":gpu9temp:MIN \
\
DEF:gpu0tempav="${1}":gpu0temp:AVERAGE \
DEF:gpu1tempav="${1}":gpu1temp:AVERAGE \
DEF:gpu2tempav="${1}":gpu2temp:AVERAGE \
DEF:gpu3tempav="${1}":gpu3temp:AVERAGE \
DEF:gpu4tempav="${1}":gpu4temp:AVERAGE \
DEF:gpu5tempav="${1}":gpu5temp:AVERAGE \
DEF:gpu6tempav="${1}":gpu6temp:AVERAGE \
DEF:gpu7tempav="${1}":gpu7temp:AVERAGE \
DEF:gpu8tempav="${1}":gpu8temp:AVERAGE \
DEF:gpu9tempav="${1}":gpu9temp:AVERAGE \
\
DEF:gpu0tempma="${1}":gpu0temp:MAX \
DEF:gpu1tempma="${1}":gpu1temp:MAX \
DEF:gpu2tempma="${1}":gpu2temp:MAX \
DEF:gpu3tempma="${1}":gpu3temp:MAX \
DEF:gpu4tempma="${1}":gpu4temp:MAX \
DEF:gpu5tempma="${1}":gpu5temp:MAX \
DEF:gpu6tempma="${1}":gpu6temp:MAX \
DEF:gpu7tempma="${1}":gpu7temp:MAX \
DEF:gpu8tempma="${1}":gpu8temp:MAX \
DEF:gpu9tempma="${1}":gpu9temp:MAX \
\
CDEF:cdefmida=gpu0tempav,gpu0tempmi,- \
CDEF:cdefmidb=gpu1tempav,gpu1tempmi,- \
CDEF:cdefmidc=gpu2tempav,gpu2tempmi,- \
CDEF:cdefmidd=gpu3tempav,gpu3tempmi,- \
CDEF:cdefmide=gpu4tempav,gpu4tempmi,- \
CDEF:cdefmidf=gpu5tempav,gpu5tempmi,- \
CDEF:cdefmidg=gpu6tempav,gpu6tempmi,- \
CDEF:cdefmidh=gpu7tempav,gpu7tempmi,- \
CDEF:cdefmidi=gpu8tempav,gpu8tempmi,- \
CDEF:cdefmidj=gpu9tempav,gpu9tempmi,- \
\
CDEF:cdefmada=gpu0tempma,gpu0tempav,- \
CDEF:cdefmadb=gpu1tempma,gpu1tempav,- \
CDEF:cdefmadc=gpu2tempma,gpu2tempav,- \
CDEF:cdefmadd=gpu3tempma,gpu3tempav,- \
CDEF:cdefmade=gpu4tempma,gpu4tempav,- \
CDEF:cdefmadf=gpu5tempma,gpu5tempav,- \
CDEF:cdefmadg=gpu6tempma,gpu6tempav,- \
CDEF:cdefmadh=gpu7tempma,gpu7tempav,- \
CDEF:cdefmadi=gpu8tempma,gpu8tempav,- \
CDEF:cdefmadj=gpu9tempma,gpu9tempav,- \
\
LINE2:gpu0tempav#ff0000:'GPU 0' \
LINE0:gpu0tempmi#ff0000: \
AREA:cdefmida#ff000044::STACK \
AREA:cdefmada#ff000044::STACK \
GPRINT:gpu0tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu0tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu0tempma:MAX:"Max\: %3.1lf°C)\n" \
\
LINE2:gpu1tempav#ffa800:"GPU 1" \
LINE0:gpu1tempmi#ffa800: \
AREA:cdefmidb#ffa80044::STACK \
AREA:cdefmadb#ffa80044::STACK \
GPRINT:gpu1tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu1tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu1tempma:MAX:"Max\: %3.1lf°C)\n" \
\
LINE2:gpu2tempav#fff600:"GPU 2" \
LINE0:gpu2tempmi#fff600: \
AREA:cdefmidc#fff60044::STACK \
AREA:cdefmadc#fff60044::STACK \
GPRINT:gpu2tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu2tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu2tempma:MAX:"Max\: %3.1lf°C)\n" \
\
LINE2:gpu3tempav#6cff00:"GPU 3" \
LINE0:gpu3tempmi#6cff00: \
AREA:cdefmidd#6cff0044::STACK \
AREA:cdefmadd#6cff0044::STACK \
GPRINT:gpu3tempmi:MIN:"(Max\: %3.1lf°C \t/" \
GPRINT:gpu3tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu3tempma:MAX:"Max\: %3.1lf°C)\n" \
\
LINE2:gpu4tempav#00ffc0:'GPU 4' \
LINE0:gpu4tempmi#00ffc0: \
AREA:cdefmide#00ffc044::STACK \
AREA:cdefmade#00ffc044::STACK \
GPRINT:gpu4tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu4tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu4tempma:MAX:"Max\: %3.1lf°C)\n" \
\
LINE2:gpu5tempav#009cff:'GPU 5' \
LINE0:gpu5tempmi#009cff: \
AREA:cdefmidf#009cff44::STACK \
AREA:cdefmadf#009cff44::STACK \
GPRINT:gpu5tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu5tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu5tempma:MAX:"Max\: %3.1lf°C)\n" \
\
LINE2:gpu6tempav#0030ff:'GPU 6' \
LINE0:gpu6tempmi#0030ff: \
AREA:cdefmidg#0030ff44::STACK \
AREA:cdefmadg#0030ff44::STACK \
GPRINT:gpu6tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu6tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu6tempma:MAX:"Max\: %3.1lf°C)\n" \
\
LINE2:gpu7tempav#c000ff:'GPU 7' \
LINE0:gpu7tempmi#c000ff: \
AREA:cdefmidh#c000ff44::STACK \
AREA:cdefmadh#c000ff44::STACK \
GPRINT:gpu7tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu7tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu7tempma:MAX:"Max\: %3.1lf°C)\n" \
\
LINE2:gpu8tempav#d4d2d5:'GPU 8' \
LINE0:gpu8tempmi#d4d2d5: \
AREA:cdefmidi#d4d2d544::STACK \
AREA:cdefmadi#d4d2d544::STACK \
GPRINT:gpu8tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu8tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu8tempma:MAX:"Max\: %3.1lf°C)\n" \
\
LINE2:gpu9tempav#000000:'GPU 9' \
LINE0:gpu9tempmi#000000: \
AREA:cdefmidj#00000044::STACK \
AREA:cdefmadj#00000044::STACK \
GPRINT:gpu9tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu9tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu9tempma:MAX:"Max\: %3.1lf°C)\n" \
${size} \
--title "${4}" \
-s ${5} \
--watermark="${HOSTNAME}" \
--slope-mode \
--lazy 1>/dev/null &
}
# http://localhost/index.html generator
(echo -n "<html><head><style> body {font-family: Courier, monospace;}</style></head><body><b>"
hostname
echo "</b><br><br>"
/home/rain/bin/sysinfo.sh | sed 's/$/<br>/g' | sed 's/ /\ \;/g'
echo "<br><li><a href=/gputemp.html>GPU Temp</a>"
echo "<br><iframe src=/dyn.html frameborder=0 height=50% width=100%></iframe></body></html>") > /tmp/index.html
touch '/tmp/dyn.html'
chmod 644 '/tmp/index.html' '/tmp/dyn.html'
fi
# Never use sgminer API if NOAPI is set
[ ! -z "${NOAPI}" ] && cmd="false :"
showdate() {
echo -ne "\e[0;36m[\e[0;35m"$(date +%Y-%m-%d" "%H:%M:%S)"\e[0;36m]\e[0;0m "
}
# Allow log for debugging purposes
debugme() {
[ "${DEBUG}" == '1' ] && {
showdate
echo "${1}"
} >> /tmp/ffc.log
}
# Halt all GPU activity. Change it if needed
do_halt() {
/home/rain/bin/gpuoff 1>/dev/null 2>/dev/null
/home/rain/bin/ethminerctl suspend
pkill -9 Hashcat
}
# XMPP notifications
#if [ "${1}" != 'single' ]
# then
# rm -f /tmp/sendxmpp 2>/dev/null
# touch /tmp/sendxmpp
# ( tail -qF /tmp/sendxmpp 2>/dev/null | sendxmpp -c -r $(hostname) -i -t rigs@conference.linuxoid.in ) &
#fi
sayto() {
(cat - | tr -d '\n' ; echo -n " on $(hostname)") | sendxmpp -t -r $(hostname) -c rigs@conference.linuxoid.in
# cat - >> /tmp/sendxmpp
}
# cgminer/sgminer core/mem clocks config parser
getcoreconf(){
awk '/gpu-engine/{
gsub(/[0-9]+\-|\"/, "", $3)
split($3, core, ",")
c='$1'+1
print core[c]
}' "${conf}"
}
getmemconf(){
awk '/gpu-memclock/{
gsub(/[0-9]+\-|\"/, "", $3)
split($3, mem, ",")
c='$1'+1
print mem[c]
}' "${conf}"
}
# Function for setting GPU clocks. 1-st parameter is a card number. If parameter is not set - all cards are used
setfreqs() {
[ -z "${1}" ] && card="$(seq 0 $(($(aticonfig --lsa | grep -c '[0-9]\.')-1)))" || card="${1}"
for i in ${card}
do
# Allowing overclocking
aticonfig --od-enable 1>/dev/null
# Getting core and memory clocks. If getcoreconf/getmemconf returns nothing - using GPU 0 clocks
# (sgminer behavior - if one parameter is set - it used for all cards)
core="$(getcoreconf "${i}")"
[ -z "${core}" ] && core="$(getcoreconf 0)"
mem="$(getmemconf "${i}")"
[ -z "${mem}" ] && core="$(getmemconf 0)"
# Setting GPU clocks
[[ ! -z "${core}" && ! -z "${mem}" ]] && {
aticonfig --od-setclocks=$core,$mem --adapter=$i 1>/dev/null
debugme "Setting freqs for GPU $i"
# Clocks state flag. 1 = clocks set from config file, 2 = clocks is reduced.
freqs[$i]='1'
}
done
# Setting Powertune to 120% via atitweak utility
[ -x /home/rain/bin/atitweak ] && /home/rain/bin/atitweak -p 20 > /dev/null
debugme "Setting powertune"
}
###################################
case "${1}" in
freqs)
setfreqs
;;
*)
# [ "${1}" != 'single' ] && echo "Hello, i'm online" | sayto
# Main loop
while :
do
# If NOAPI is not set - checking API availibility
$cmd nc -w 1 -z localhost 4028 && {
api='1' # API detected flag
ffci='0' # $fan array initialisation flag
debugme "API detected"
# Fetching API data
devdata="$(/home/rain/bin/api-example.py devs | sed -r 's/, |\{|\}/\n/g;s/(\n)u|'\''/\1/g')"
} || {
api='0' # API not detected
# sgminer process detection '
# ffci must be set to 0 and $fan array must be filled with real data every time when sgminer started or stopped
if pidof sgminer 1>/dev/null
then
if [ -z "${sgdetect}" ]
then
debugme "sgminer detected"
ffci='0'
sgdetect='1'
fi
else
if [ ! -z "${sgdetect}" ]
then
debugme "sgminer disabled"
unset sgdetect
ffci='0'
fi
fi
debugme "sgminer=$sgdetect"
[ "${ffci}" != '1' ] && {
# init fan array
for j in $(seq 0 $(($(aticonfig --lsa | grep -c '[0-9]\.')-1)))
do
fan[$j]="$(DISPLAY=:0.$j aticonfig --pplib-cmd "get fanspeed 0" | awk '/Result/{gsub(/%/, "", $4); print $4}')"
debugme "Writing array data for GPU $j"
done
# Setting clocks for all cards
if [ "${1}" != 'single' ]
then
setfreqs
fi
ffci='1'
}
# Fetching data from aticonfig utility
atidata="$(aticonfig --odgt --odgc --adapter=all 2>/dev/null)"
debugme "Getting atidata"
#### fans and core frequency control section ####
# Parsing aticonfig output and extracting card number, temperature, core and memory clocks
# TODO: next time use this for all aticonfig data processing. For now we will see core clock updates only in next cycle
for cdat in $(echo "${atidata}" | tr '\n' ' ' | sed 's/Adapter/\n&/g'| sort | sed -nr '/Adapter/N;s/\n/ /gp' | sed -r 's/^Adapter ([0-9]+) - .*Current Peak : *([0-9]+) +([0-9]+).*Temperature - ([0-9]{1,3}).[0-9]+ C.*/\1:\4:\2:\3/g')
do
# Separating values between different variables
cnt="${cdat%:*:*}"
cn="${cnt%:*}" # Card number
#ct="${cnt#*:}" # Card temperature # TODO: use array in the future and remove this variable
ct[$cn]="${cnt#*:}" # Card temperature array
ccmf="${cdat#*:*:}"
ccf="${ccmf%:*}" # Card core clock
cmf="${ccmf#*:}" # Card memory clock
[ "${freqs[$cn]}" == '1' ] && {
cmax[$cn]=${ccf} # max core freq array
mmax[$cn]=${cmf} # max mem freq array
}
debugme "Processing GPU $cn -> ${ct[$cn]} C"
if [ "${1}" != 'single' ]
then
# If card temperature is over $tco - halting mining, fans to $fmax (usually 100%)
[ "${ct[$cn]}" -ge "${tco}" ] && {
# halt mining
debugme "Cutoff temp on $cn"
fan[$cn]=${fmax}
DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fmax}" 1>/dev/null
# showdate >> /tmp/ffc.log
echo "ALERT! Critical temperature (${ct[$cn]}C) on GPU ${cn}" | sayto
do_halt
}
# If card temperature great than $toh, but still less than $tco (80-85 degr. zone) - set fans to max speed and reduce core clocks
[[ "${ct[$cn]}" -ge "${toh}" && "${ct[$cn]}" -lt "${tco}" ]] && {
ftalk='25'
# reduce freqs
debugme "Overheat on ${cn}: ${freqs[$cn]}"
# [ "${freqs[$cn]}" == '1' ] && {
# showdate >> /tmp/ffc.log
# echo "GPU $cn overheat, reducing clocks" >> /tmp/ffc.log
debugme "Setting max fan speed on ${cn}"
fan[$cn]=${fmax}
DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fmax}" 1>/dev/null
[ "${ccf}" -gt "$((${corelow}+${ohfs}))" ] && {
ccf=$((${ccf}-${ohfs}))
[ "${freqs[$cn]}" == '1' ] && {
echo "GPU $cn overheat (${ct[$cn]} C), starting core clock reducing" | sayto
}
freqs[$cn]='2'
debugme "Reducing freqs on $cn to ${ccf}/${cmf}"
aticonfig --od-setclocks=${ccf},${cmf} --adapter=$cn 1>/dev/null
} || {
echo "No more space in frequency range on card ${cn} for clock reducing" | sayto
debugme "No more space in frequency range on card ${cn} for clock reducing"
}
# }
}
# If card temperature goes below overheat level - report about new frequencies
[[ "${ct[$cn]}" -lt "$((${toh}-${hyst}))" ]] && {
[ ${freqs[$cn]} == '2' ] && {
[ ${ftalk} -gt '0' ] && let ftalk-=1
[ ${ftalk} == '1' ] && {
echo "GPU $cn clocks reduced to ${ccf}/${cmf}" | sayto
ftalk='0'
}
}
}
# If card temperature decreased to safe level - $trst - restore core clocks
[[ "${ct[$cn]}" -le "${trst}" ]] && {
[ "${freqs[$cn]}" == '2' ] && {
[ "$((${cmax[$cn]}-${ccf}))" -gt "${fups}" ] && {
aticonfig --od-setclocks=$((${ccf}+${fups})),$cmf --adapter=$cn 1>/dev/null
} || {
setfreqs "${cn}"
debugme "Clocks restored on GPU $cn"
echo "Clocks restored on GPU $cn" | sayto
}
}
}
# If card temperature in second zone (typically 70-80 degr) - allowing fan speed to be up to $fmax
[[ "${ct[$cn]}" -gt "$((${tt2}+${hyst}))" && "${ct[$cn]}" -lt "$((${toh}-${hyst}))" ]] && {
debugme "2-nd fan zone on $cn"
# allow fans to 100%
[ "${fan[$cn]}" -lt "${fmax}" ] && {
# old="${fan[$cn]}"
let fan[$cn]+=5
[ "${fan[$cn]}" -lt "${tfan}" ] && {
fan[$cn]=${tfan}
debugme "Fast heating on $cn"
}
[ "${fan[$cn]}" -gt "${fmax}" ] && fan[$cn]="${fmax}"
debugme "Setting fan to ${fan[$cn]} on GPU $cn"
# showdate >> /tmp/ffc.log
# echo "GPU ${cn} ${ct[$cn]}: setting fan $old -> ${fan[$cn]}" >> /tmp/ffc.log
DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fan[$cn]}" 1>/dev/null
}
}
# If card temperature in 1-st zone (65-70 degr) - fan speed can be up to $tfan
[[ "${ct[$cn]}" -gt "$((${tt1}+${hyst}))" && "${ct[$cn]}" -lt "$((${tt2}-${hyst}))" ]] && {
# allow fans to $tfan
debugme "1-nd fan zone on $cn"
[ "${freqs[$cn]}" == '2' ] && {
setfreqs "${cn}"
}
[ "${fan[$cn]}" -lt "${tfan}" ] && {
# old="${fan[$cn]}"
let fan[$cn]+=2
[ "${fan[$cn]}" -gt "${tfan}" ] && fan[$cn]="${tfan}"
# showdate >> /tmp/ffc.log
# echo "GPU ${cn} ${ct[$cn]}: setting fan $old -> ${fan[$cn]}" >> /tmp/ffc.log
debugme "Setting fan to ${fan[$cn]} on GPU $cn"
DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fan[$cn]}" 1>/dev/null
}
}
# If card temerature less than first target - fan goes idle
[ "${ct[$cn]}" -lt "$((${tt1}-${hyst}))" ] && {
# decrease fan speed
debugme "Low temp on $cn"
# old="${fan[$cn]}"
let fan[$cn]-=1
[ "${fan[$cn]}" -lt "${fmin}" ] && fan[$cn]="${fmin}"
# showdate >> /tmp/ffc.log
# echo "GPU ${cn} ${ct[$cn]}: setting fan $old -> ${fan[$cn]}" >> /tmp/ffc.log
DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fan[$cn]}" 1>>/dev/null
}
fi
done
# TODO: сделать это все как-то покрасивее; возможно - вынести 60 тут и выше (создание базы) в переменную
[ -z "${st}" ] && st='60'
let st-=$((60/$sleep))
[ "${st}" -le '0' ] && {
st='60'
$rrd update "${rrdb}" N$(
s=10
for t in ${!ct[@]}
do
echo -n ":${ct[$t]}"
let s-=1
done
for i in $(seq 1 $s)
do
echo -n :U
done
)
mkgraph "${rrdb}" "${site}"/gpustat.h.png 'normal' "GPU Temp, час" -60min
mkgraph "${rrdb}" "${site}"/gpustat.h.large.png 'large' "GPU Temp, час" -60min
mkgraph "${rrdb}" "${site}"/gpustat.d.png 'normal' "GPU Temp, день" -24h
mkgraph "${rrdb}" "${site}"/gpustat.d.large.png 'large' "GPU Temp, день" -24h
mkgraph "${rrdb}" "${site}"/gpustat.m.png 'normal' "GPU Temp, месяц" -30d
mkgraph "${rrdb}" "${site}"/gpustat.m.large.png 'large' "GPU Temp, месяц" -30d
mkgraph "${rrdb}" "${site}"/gpustat.y.png 'normal' "GPU Temp, год" -12mon
mkgraph "${rrdb}" "${site}"/gpustat.y.large.png 'large' "GPU Temp, год" -12mon
}
devdata="$((
# Exporting fan speed for 2-nd part of this script
for f in ${!fan[@]}
do
echo "Fan Speed $f ${fan[$f]}"
done
# ...and exporting frequency state
for c in ${!freqs[@]}
do
echo "Freq State $c ${freqs[$c]}"
done
# Transforming aticonfig data for sgminer API compatible format... With some additions
echo "${atidata}"
) | awk '
/^Adapter/{
a=$2
name=$0
gsub(/^Adapter.* - /, "", name)
}
/Temp/{
OFMT="%.f"
temp[a]=$5+0
}
/Speed/{
speed[$3]=$4
}
/Freq State/{
freqs[$3]=$4
}
/Clocks/{
core[a]=$4
mem[a]=$5
}
/load/{
load[a]=$4
gsub(/%/, "", load[a])
print "GPU: "a
print "Devname: "name
print "Temperature: " temp[a]
print "Fan Percent: " speed[a]
print "Freq State: " freqs[a]
print "GPU Clock: " core[a]
print "Memory Clock: " mem[a]
print "GPU Activity: " load[a]
print "Status: uAlive"
print "Enabled: U"
print "Utility"
}
')"
}
# Indication part
data="$(echo -n "${devdata}" | gawk '
function pbar(prog, min, max)
{
p=""
if (min=="") min=10
if (max=="") max=100
inc=((max-min)/10)+0.1
for (i=min; i<=max; i+=inc) {
if (i>prog) p=p" "
else p=p"|"
}
return p
}
/Temperature/{
OFMT="%.f"
temp=$2+0
if (temp>79) ctemp="\\e[5;91m"
else
if (temp>69) ctemp="\\e[0;33m"
else
ctemp="\\e[0;32m"
}
/Fan Percent/{
OFMT="%.f"
fan=$3+0
if (fan>75) cfan="\\e[31m"
else
if (fan>45) cfan="\\e[33m"
else
cfan="\\e[32m"
}
/Freq State/{
if ($3=="1") fstate="\\e[32m"
if ($3=="2") fstate="\\e[91m"
}
/GPU Activity/{
act=$3
if (act<80) cact="\\e[0;31m"
else
if (act<90) cact="\\e[0;33m"
else
cact="\\e[0;32m"
}
/GPU Clock/{
core=$3
}
/Devname/{
name=$0
gsub(/Devname: /, " | ", name)
}
/Status/{
stat=$2
if (stat=="uAlive") stat="\\e[36m"
else stat="\\e[5;31m"
}
/Memory Clock/{
mem=$3
}
/Enabled/{
en=$2
if (en=="uN") en="X"
else
if (en=="U") en="U"
else en="Y"
}
/^GPU:/{
gnum=$2
}
/Utility/{
printf "%-67s %-48s %-22s %-61s %s\n",
stat en " GPU "gnum": \\e[0;0m["ctemp pbar(temp, 30, 85) "\\e[0m] " ctemp temp"C\\e[0m",
"| FAN: ["cfan pbar(fan)"\\e[0m] " cfan fan"% \\e[0m",
"| "fstate core"/"mem"\\e[0m",
"| Load: \\e[0;0m["cact pbar(act, 50, 99) "\\e[0m] " cact act"%\\e[0;0m",
name
}'
)"
[ "${api}" == '1' ] &&\
data="\t\t\t=========== sgminer stat ===========\n${data}" ||\
data="\t\t\t=========== aticonfig stat ===========\n${data}"
[ "${1}" == 'single' ] && {
echo -e "${data}"
break
} || {
clear
echo -e "${data}"
# http://localhost/dyn.html generator
(echo -ne "\n* "
echo $(/home/rain/bin/ethspeed single) @ $(date +%H:%M:%S)
echo -ne "\n"
echo -e "${data}") | LANG=C awk '
BEGIN {
print "<html>"
print "<title>"strftime()"</title>"
print "<meta http-equiv=\"refresh\" content=\"10;\">"
print "<head><style> body '{'font-family: Courier, monospace;'}'</style></head><body>"
}
{
gsub(/ /, "\\ ", $0)
gsub(/\t/, "\\ ", $0)
gsub(/$/, "<br>", $0)
gsub(/\[([0-9];)?31m/, "</font><font color=red>", $0)
gsub(/\[([0-9];)?91m/, "</font><font color=red><span class=redcode>", $0)
gsub(/\[([0-9];)?33m/, "</font><font color=darkorange><span class=yellowcode>", $0)
gsub(/\[([0-9];)?32m/, "</font><font color=green><span class=greencode>", $0)
gsub(/\[36m/, "<font color=blue>", $0)
gsub(/\[(0;)?0m/, "</font></span>", $0)
print $0
}
END {
print "</body></html>"
}' > /tmp/dyn.html
}
debugme "Sleeping..."
sleep "${sleep}"
done
;;
esac