Jak na uspávání disku v Linuxu pomocí hdparm

Autor: | 8. 6. 2020 | IT, Linux

S uspáváním disku jsem se docela potrápil i díky souběhu několika chyba najednou. V příspěvku popíšu jak předejít problémům při uspávání otáčivého disku, se kterými jsem se setkal. Pro uspávání disku jsem se rozhodl proto, abych šetřil mechaniku disku a snížil spotřebu a hlučnost. Co se týče spotřeby, tak roční spotřeba nevytíženého disku až taková – propočtem ceny se tady zabývat nebudu, ale jsou to řádově stokoruny za rok.

Četl jsem diskusi, kde se účastníci zabývali tím, zda má smysl disk uspávat, při tak nízkých nákladech. Jedna strana věci je, že náklady jsou nízké, druhá strana je, že i tak dochází k opotřebení mechaniky. Do toho nám vstupuje fakt, že příliš časté probouzení disku také mechanice příliš nebude dělat dobře. Těžko říct, které straně dát víc za pravdu. Nicméně je tu ještě jeden faktor a to hlučnost. Uspaný disk není slyšet a to mě vedlo nastavení uspávání.

Disk používám na zálohování a mám ho permanentně připojený k mikropočítači Espressobin. Minimálně v noci je zbytečné, aby byl disk pořád zapnutý. Aktuálně jsem se rozhodnul, že pokud nebudu disk používat více jak 30 minut, nechť se uspí.

Seznámení s hdparm

Použití programu hdparm je perfektně popsáno v krátkém článku na ArchWiki. Nastavení uspávání po 30 minutách tedy udělám příkazem (počítám, že disk je zařízení /dev/sda).

# hdparm -S 241 /dev/sda

Faktem je, že na některých systémech mi toto stačilo. Disk se pak skutečně uspával po 30 minutách. Protože jsem byl fyzicky dál od disku, potřeboval jsem při testování v terminálu vidět, zda je disk uspaný či nikoli. Napsal jsem si jednoduchou smyčku, která každou vteřinu kontrolovala stav disku. Uvádím i s příkladem výstupu:

root@espressobin:~# while : ; do /sbin/hdparm -C /dev/sda; sleep 1; done

/dev/sda:
 drive state is:  active/idle

/dev/sda:
 drive state is:  standby

Nebo lze použít příkaz watch, který bude každou vteřinu spouštět příkaz na stav disk a obnovovat obrazovku.

watch -n 1 hdparm -C /dev/sda

Pro jistotu jsem ještě zkontroloval, zda dotazování na stav disku nezpůsobuje probuzení disku z režimu spánku. Což by bylo kontraproduktivní. Disk jsem tedy nejdříve pomocí příkazu hdparm uspal a pak teprve pustil dotazovací smyčku. S odstupem času jsem zjistil, že dotaz hdparm -C /dev/sda způsobí změnu hodnot v souboru /proc/diskstats o kterém budu psát níže. Okamžité uspání disku lze provést pomocí příkazu:

# hdparm -y /dev/sda

Hdparm a advanced power management

U některých systémů mi výše uvedené stačilo k úspěšnému automatickému uspávání po 30 minutách. V případě mého systému jsem bohužel nebyl úspěšný. Disk se neuspával vůbec. Dočetl jsem se o tzv Advanced Power Management. K nastavení Advanced power managementu se používání přepínač -B a hodnoty za přepínačem mohou být 1-255.

Hodnoty 1 až 127 dovolují uspání disku, hodnoty 128-254 ne. Hodnota 255 kompletně vypíná tuto funkci.

Z popisu jsem pochopil, že pokud chci disk uspávat, tak se tedy budu pohybovat maximálně do hodnoty 127. Čím menší hodnota, tím agresivnější power management. V rámci těchto hodnot bohužel nejde nastavovat čas, po kterém se má disk uspat. Alespoň se mi nepodařilo dohledat bližší informace. Takže i při nastavení hodnoty 127 se podle mě power management choval agresivně a během pár minut disk uspávat.

# hdparm -B 127 /dev/sda

Mým záměrem určitě nebylo vypínat disk po pár minutách. Zkoušel jsem i kombinaci parametrů -B a -S nicméně pak se uspávání chovalo absolutně nevyzpytatelně. Jak je v dokumentaci napsáno, když použiji oba parametry, tak se disk může uspat dříve, než určuje parametr -S. Takže ani tento příkaz nepomohl a disk se uspával před očekávaným limitem.

# hdparm -B 50 -S 241 /dev/sda

Čím víc jsem si na espressobinu hrál s těmito hodnotami, tím více jsem měl pocit, že se uspávání chová náhodně. Jedno se disk neuspal vůbec, když jsem hdparm spustil se stejnými parametry, tak se zase disk uspal výrazně před časovým limitem. Nepřišel jsem na to, co je špatně, tak jsem na tuto cestu rezignoval.

Prohledával jsem fóra, proč hdparm nefunguje dle očekávání. Někdo měl stejný problém a nakonec to dopadlo tak, že si napsal vlastní skript. Bohužel už nedohledám autora, abych mohl zpětně odkazovat.

#!/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 je víceméně sebevysvětlující. Pokud dojde k přístupu na disk, tak systém mění statistiky, které lze vyčíst v /proc/diskstats. Pomocí Cronu jsem nastavil, že skript spouští každou půl hodinu. Porovná se, zda se statistiky od posledního záznamu nezměnily a pokud ne, spustí se uspání. Pozor, ve chvíli, kdy jsem se pokoušel neustále vyčítat, zda je disku uspaný (hdparm -C /dev/sda), tak se měnily hodnoty v /proc/diskstats a tím pádem skript nefungoval a disk se neuspal, protože hodnoty diskNEWstate.txt a diskOLDstate.txt byly vždycky jiné. 

K nedostatkům tohoto krátkého skriptu:

  1. neřeší počáteční stav – první inicializaci. Při prvním spuštění tedy nemá co porovnávat. Při druhém spuštění už je všechno v pořádku. V zásadě to ničemu nevadí.
  2. pokud je disk už uspaný, tak se stejně spouští hdparm -y. Vzhledem k tomu, že se příkazem disk neprobudí a zůstává uspaný, tak jsem neřešil. Jinak bych musel ještě přidat podmínku – pokud je disk uspaný, tak nic nedělej.
  3. pojmenování disku. Musíte si dohledat UUID vlastního disku a také ID. UUID jsem dohledal pomocí příkazu blkid. ID jsem vyčetl tak, že jsem vypsal všechna zařízení v /etc/disk/by-id/ a našel to svoje. Vzhledem k tomu, že mám jenom jeden disk, tak bych mohl použít příkaz hdparm i takto: hdparm -y /dev/sda
  4. disk se při nepoužívání nevypíná přesně za půl hodiny. V nejextrémnějším případě se vypíná za 59 minut a 59 vteřin. Je to právě frekvenci vykonávání skriptu. Pokud ihned vykonání skriptu dojde ke čtení či zápisu na disk a pak 29 minut nic, tak při dalším spuštění skriptu se vyhodnotí, že na disk bylo v posledních 30 minutách zapisováno. Takže se disk neuspí a skript se znovu spustí až za 30 minut, když už k uspání dojde. Přiznám se, že toto mi nevadí – vím, že disk se při nepoužívání vypne za 30 – 60 minut.

Při pozdější instalaci jsem zjistil další nedostatky skriptu a musel jsem ho mírně rozšířit. Zjistil jsem, že i když je disk uspaný, tak se mění některé sloupečky v souboru /proc/diskstats. Vyhledal jsem jaké sloupečky se mohou měnit a nemělo by se brát na tyto sloupečky zřetel při uspávání. Do skriptu jsem tedy přidal jednoduchou funkci, která příslušné sloupečky odstraní. Zároveň je dobré říct, ze začínám od posledního sloupečku. Kdybych odstranil nejprve 1 sloupeček, tak mi to změní pořadí všech sloupečků a musel bych je přepočítávat. Kdežto když začnu odstraňovat od posledního, tak se vyhnu těmto problémům.

 

#!/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

Nastavení uspávání po restartu počítače

Při řešení uspávání disku pomocí skriptu a Cronu není třeba víc řešit. Ovšem při použití čistě hdparm je dobré vědět, že nastavení je po restartu počítače ztraceno a po restartu je třeba spustit první inicializaci. Automaticky lze prvotní inicializaci řešit těmito způsoby:

 

  1. Konfigurace souboru /etc/hdparm.conf – přiznám se, že na konfigurační soubor mi systém nechtěl reagovat a nechtěl jsem se učit jak soubor pracuje. Prostě jsem potřeboval spustit jeden příkaz po startu.
  2. na sysvinit-based systémech bylo možné dát příkazy do /etc/rc.local. Po nastartování systému se provedly všechny příkazy v tomto souboru. Nicméně to nefunguje všude – zejména pokud se jedná o systemd-based systémy. Což byl zrovna můj případ.
  3. na systemd-based systému je potřeba nakonfigurovat spuštění příkazu jako službu. Čerpal jsem z tohoto zdroje:
# vi /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

ESC ZZ
# systemctl daemon-reload
# systemctl enable sda-spindown.service
# systemctl start sda-spindown.service

Procesy probouzející disk z režimu spánku

Asi nejvíc jsem se potrápil s hledáním procesů, které přistupují na disk. Disk používám jako úložiště pro data, včetně nextcloudu. Když nepotřebuji nic synchronizovat a na disk přímo nepřistupuji, tak může být disk uspaný. Není důvod, aby pracoval. Jenže proti této mojí idee se postavily některé procesy v rámci údržby systému.

Disk totiž neustále něco probouzelo z režimu spánku. První, co jsem identifikoval byla údržba nextcloudu. Prováděla se každých 15 minut. Protože nextcloud požívám jako automatické zálohování a za den přenesu pár drobných souborů, bude mi stačit frekvence údržby 1x denně. Zvolil jsem čas ve který je pravděpodobné, že disk bude v provozu – tedy v 10h a 12minut.

root@espressobin:~# crontab -e -u www-data

12 10 *  *  * php -f /var/www/nextcloud/cron.php

Ani to nebylo ale ještě řešení. Postupně jsem vypínal jednu službu za druhou, dokud jsem neodhalil viníka.

V rámci obsluhy nextcloudu jsem v nextcloudpi zapnul volbu Monitor HDD health automatically. To spouštělo smart deamona a neustále testovalo a přistupovalo na disk. Uspávání pak bylo prakticky nefunkční a naopak zatěžovalo disk, protože jen co jsem ho uspal, tak došlo k probuzení. Proto jsem tuto volbu v nastavení nextcloudpi vypnul.

Další články