Obsah

Espressobin: uspávání rotačního HDD pomocí hdparm

Vytvořeno: 8.6.2020

Poznámka k aktuálnosti: Toto je dobový článek. Postup vychází z konkrétního nasazení na Espressobinu s Nextcloudem a nemusí odpovídat dnešnímu stavu systemd, NextcloudPi ani nástrojů kolem storage.

Důležité: Tento článek se týká klasického rotačního HDD. Pro SSD a NVMe se podobné uspávání přes hdparm běžně nepoužívá.

hdparm umožňuje nastavit standby režim rotačního disku. V mém případě šlo o HDD připojený k Espressobinu, kde jsem chtěl snížit hlučnost a zbytečné opotřebení mechaniky. Samotné nastavení ale komplikovalo několik souběžných problémů: Advanced Power Management, procesy probouzející disk a nepřesnosti při sledování /proc/diskstats.

Základní nastavení přes hdparm

Uspání po 30 minutách idle času odpovídalo hodnotě -S 241:

hdparm -S 241 /dev/sda

Okamžité uspání disku:

hdparm -y /dev/sda

Pro sledování stavu se hodilo průběžné ověřování:

while : ; do /sbin/hdparm -C /dev/sda; sleep 1; done

Nebo:

watch -n 1 hdparm -C /dev/sda

Později jsem zjistil důležitou věc: dotaz hdparm -C /dev/sda mění statistiky v /proc/diskstats, takže může rušit vlastní logiku pro vyhodnocení nečinnosti disku.

Advanced Power Management

Na některých systémech stačilo použít jen -S. Na Espressobinu se disk neuspával vůbec, takže jsem zkoušel i Advanced Power Management přes parametr -B.

Například:

hdparm -B 127 /dev/sda

Nebo kombinaci:

hdparm -B 50 -S 241 /dev/sda

V praxi se ale uspávání chovalo nevyzpytatelně. Disk se někdy neuspal vůbec a jindy usínal dřív, než jsem čekal. Na této cestě jsem nakonec rezignoval.

Vlastní skript nad /proc/diskstats

Nakonec se mi osvědčilo nečekat, že se disk bude uspávat jen podle interní logiky hdparm, ale pravidelně kontrolovat změny v /proc/diskstats a při nečinnosti ručně spustit hdparm -y.

Původní jednodušší verze skriptu:

#!/bin/bash
 
# Get new state from diskstats
devcode=$(findfs UUID=hFAc5i-yMtV-UC83-jF99-UnqA-ck7f-qYlnzo | cut -c 6-8)
 
NEWstate=$(cat /proc/diskstats | grep $devcode)
echo $NEWstate > /run/diskNEWstate.txt
 
# compare md5 sums
md5new=$(md5sum /run/diskNEWstate.txt | sed 's/ .*//')
md5old=$(md5sum /run/diskOLDstate.txt | sed 's/ .*//')
 
# if no changes, power down
if [ "$md5new" = "$md5old" ]; then
        hdparm -y /dev/disk/by-id/ata-ST4000VN008-2DR166_ZDH8BP5S
        echo "going to sleep"
fi
 
# Write current state to file
echo $NEWstate > /run/diskOLDstate.txt

Skript byl spouštěný přes cron každých 30 minut. Když se obsah /proc/diskstats od minulého běhu nezměnil, disk se uspal.

Vylepšená verze skriptu

Později jsem zjistil, že i u uspaného disku se mění některé sloupce v /proc/diskstats. Proto jsem skript rozšířil o odstranění sloupců, které nemělo smysl sledovat:

#!/bin/bash
 
# Get new state from diskstats
devcode=$(findfs UUID=hFAc5i-yMtV-UC83-jF99-UnqA-ck7f-qYlnzo | cut -c 6-8)
 
NEWstate=$(cat /proc/diskstats | grep $devcode)
 
del_column () {
       NEWstate=$(echo $NEWstate | sed -r 's/(\s+)?\S+//'$1)
}
 
del_column 14
del_column 13
del_column 7
del_column 6
del_column 4
 
echo $NEWstate > /run/diskNEWstate.txt
 
# compare md5 sums
md5new=$(md5sum /run/diskNEWstate.txt | sed 's/ .*//')
md5old=$(md5sum /run/diskOLDstate.txt | sed 's/ .*//')
 
# if no changes, power down
if [ "$md5new" = "$md5old" ]; then
       hdparm -y /dev/disk/by-id/ata-ST4000VN008-2DR166_ZDH8BP5S
       echo "going to sleep"
else
       #if changes
       date >> /etc/scripts/sleep.log
fi
 
# Write current state to file
echo $NEWstate > /run/diskOLDstate.txt

Automatické nastavení po restartu

Pokud se použije čistě hdparm, je dobré pamatovat na to, že nastavení po restartu mizí. U systemd systému jsem to tehdy řešil přes vlastní službu:

# /usr/lib/systemd/system/sda-spindown.service
[Unit]
Description=Set HDD spindown

[Service]
Type=oneshot
ExecStart=/sbin/hdparm -B 241 /dev/sdb
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target

Aktivace služby:

systemctl daemon-reload
systemctl enable sda-spindown.service
systemctl start sda-spindown.service

Co disk probouzelo

Nejvíc času zabralo hledání procesů, které disk probouzely. V mém případě šlo hlavně o údržbu Nextcloudu a monitoring stavu disku v NextcloudPi.

První krok bylo omezit frekvenci Nextcloud cronu. Místo každých 15 minut jsem ho nechal běžet jen jednou denně:

crontab -e -u www-data
12 10 * * * php -f /var/www/nextcloud/cron.php

Druhý problém byl v NextcloudPi: zapnutá volba automatického monitoringu zdraví HDD probouzela disk prakticky pořád. Tuto volbu jsem vypnul.

nextcloud-hdd-health-monitor.jpg

Zdroje