nikolausdulgeridis
  SSDidletrim
 
Trim SSD when idle
Putzen

Linux: SSD im Ruhezustand trimmen 

Putzaktion für das SSD Laufwerk

SSDs benötigen einen gelegentlichen trim. Das verbessert die Performance und das Wear Levelling und kommt daher auch der Lebensdauer zugute. Ubuntu und andere Systeme haben den Aufruf in einem wöchentlichen Zeitrahmen integriert. 

Warum eigentlich nur wöchentlich fragt man sich, aber bei häufigerer Ausführung von trim, steigt die Wahrscheinlichkeit, in einem ungünstigen Augenblick mitten in einen Dateitransfer hineinzuplatzen. Das ist im Grunde nicht schlimm, aber es erscheint aussichtsreicher, gezielt einen ruhigen Moment abzuwarten. Einerseits um den Dateitransfer nicht zu stören und andererseits dem SSD Controller etwas Zeit zur Reorganisation zu geben. Zufällig haben wir auf dieser Site bereits ein fertiges Ferstplattenidleprogramm, das wir für diesen Zweck nur anpassen müssen, indem wir 'sudo fstrim -va' im richtigen Moment ausführen.

Der Algorithmus wird für unsere Zwecke in entscheidenden Punkten abgewandelt
wiederhole alle 4h ->
Intervall 4/2/1 ... Stunden -> check if idle
-> Zwangstrim nach spaetestens 8h

Motivation:
Ich bin wahrscheinlich der letzte Mensch auf diesem Planeten, der seinem Board eine schnelle SSD spendiert hat. Nach zwei Wochen ungebrochener Geschwindigkeitsrekorde, fielen die Schreibraten auf einen Bruchteil des ursprünglichen Wertes. Auf einmal war aus der schnellsten Platte in meiner Kollektion die langsamste geworden. Auch ein trim Kommando half nicht. Was war geschehen? Meine Suche in Userforen führte mich lediglich zu der Erkenntnis, dass ich scheinbar der erste Mensch war, der je über das Problem gestolpert ist. Schliesslich entschied ich die Platte neu zu formatieren. Siehe da, alles wie am ersten Tag. Offenbar hatten Inkonsistenzen im Dateisystem dazu geführt, dass der Trim Befehl wirkungslos blieb (möglicherweise hätte ein fsck und nachfolgender trim ebenfalls getan). Jedenfalls wurde durch das Erlebnis mein Augenmerk auf den Wert einer gut aufgeräumten Disk gelenkt. 

Hintergrund:
Der Befehl trim markiert die Sektoren nur als geloescht, der SSD Controller wird die freigewordenen Blöcke später im Hintergrund reorganisieren und nach und nach physikalisch löschen, es kann einige Zeit dauern bis der getrimmte Bereich tatsächlich wieder zur Verfügung steht. Der Löschvorgang an sich ist vergleichsweise langwierig, aber da die Blöcke recht gross sind (64k+) und u.U. parallel gelöscht werden können, fällt das nicht so sehr ins Gewicht. Gelegentlich müssen die Inhalte von noch teilweise belegten Bereichen zusammengefasst werden, um die freigewordenen Blöcke löschen zu können (das ähnelt ein wenig dem altbekannten Defragmentieren, obwohl das Ziel etwas anders ist). Die Controller gehen hier teilweise unterschiedlich aggressiv gegen die Lücken in den bereits getrimmten Bereichen vor. Im Ergebnis wird sich der SSD Controller bemühen, möglichst viele freie Blöcke vorzuhalten, damit der nächste Schreibvorgang ohne Zeitverlust vonstatten gehen kann.

In der Vergangenheit wurden Laufwerke oft mit der Option discard gemountet, dann wird der trim direkt im Anschluss an eine Schreib/Löschoperation durchgeführt. Der Vorteil gegenueber einer wöchentlichen Trim Aktion, wie sie z.B. Ubuntu einplant, ist einfach dass die Bereiche unverzüglich an den Controller gemeldet werrden. Davon wird mittlerweile abgeraten, da die von discard ausgelöste Kommunikation zur Unzeit die Schnittstelle und die SSD belastet. Allerdings gehen die Auffassungen über den Nutzen von discard in einigen Quellen auseinander. Insbesonere Intel warnt sogar ausdrücklich davor, discard zu aktivieren, warum wird allerdings nicht gesagt. NVME ist eine performante Schnittstelle, die Intel gerne fürr SSDSs als Standard etablieren möchte. Möglicherweise ist Intels Sorge, dass die Geschwindigkeit durch die Verwendung von discard leidet, was NVME schlecht aussehen liesse.
(siehe u.a. wiki.ubuntuusers.de/SSD/TRIM oder intel.com/.../Intel_Linux_NVMe_Guide).

Wenn wir nach einem Zustand relativer Ruhe suchen, müssen wir berücksichtigen, dass es Anwendungen geben kann, die kontinuierlich aber mit niedriger Frequenz auf die Platte zugreifen, in diesen Fällen kann es schwerfallen, einen absoluten Ruhepunkt zu finden. Genau deshalb ist das 'normale' Idle Skript für unseren Zweck nicht ausreichend  Stattdessen wird wiederholt in einem sich verkürzenden Intervall gemessen. Zwangstrim erfolgt nach spaetestens 8h.

Bei der idle Prüfung werden alle Laufwerke einbezogen. Im Grunde würde es reichen, die Prüfung auf die SSDs zu begrenzen, aber vorzugsweise suchen wir einen Zustand, in dem das Gesamtsystem in Ruhe ist. Man könnte auch Prozessorlast und Netzaktivität als Indikatoren hinzuziehen, das ist aber derzeit nicht vorgesehen (eine ähnliche Funktion bietet Xidle auf einigen Systemen, das trifft aber nicht genau unseren Bedarf).

Wahl des Intervalls
Zunächst einmal , warum 4 Stunden. Nun 4 Stunden sind einerseits weniger als 1 Woche, andererseits eine Zeitspanne in der ein Transfervolumen denkbar ist, das einen trim sinnvoll machen kann. Variante 1: kein Datentransfer in den letzten 4 Stunden. Eigentlich gut, denn wir haben den gewünschten Ruhezustand und können trimmen. Zurück zum Anfang, wir warten vier Stunden. Variante 2: Datentransfer hat stattgefunden, wir wissen aber nicht, ob das Sytem in diesem Moment idle ist, wir warten also ein weiteres  mal, diesmal mit verkürztem Intervall: Kein Datentransfer: Trim und zurueck zum Anfang, Sonst kürzeres Intervall und so weiter bis Intervall = 0. Man kann über die günstigste Intervalldauer spekulieren, der Vorteil des Algorithmus ist immerhin, er ist robust bei guter Trefferquote bezüglich der Vorhersage eines Ruhemoments. 

Also:
Intervall := 4*3600 Sekunden
Loop:
 warte Intervall
 kein Datentransfer oder Intervall=0 => Trim und alles auf Anfang
 Interval := Intervall/2
End Loop

Längstmögliche Dauer bis trim: 8 Stunden (Grenzwertsatz)

Berechnung im Skript:
let intervall=4*3600
let intervall=$intervall/2; if  [ $intervall == 0 ] ; then echo Ende! ; else echo Intervall ist $intervall Sekunden; fi

Risiken:
Praktisch Null. Das Skript belastet wegen seiner grossen Intervalldauer die Systemressourcen nur unmerklich. Der Trim Befehl selber ist unbedenklich. Auch ein versehentlich mehrfach gestartetes Skript hat keine erwähnenswerten Auswirkungen, deshalb ist eine Kontrolle beim Start überflüssig. Ebenfalls ist eine Kollision mit dem wöchentlichen Trim oder Ausführung bei aktivierter discard Option folgenlos. Den wöchentlichen Cronjob lassen wir einfach unangetestet, damit haben wir auch ein Fallback falls aus irgendeinem Grund das Skript nicht läuft.

Hinweis:
Trim wurde jetzt auch im Skript disk_spindown.sh hinzugefügt. Der Unterschied zu diesem Skript ist, dass disk_spindown.sh konservativer herangeht. Es wird nur bei absoluter Ruhe getrimmt. Ins Gehege kommen sich die Skripte nicht, beide koennen parallel betrieben werden.

Alternative CronJob:
Wem das alles zu umständlich ist, kann auch einfach einen CronJob z.B alle 6 Stunden (das wäre 4x am Tag) mit 'sudo fstrim -av' einrichten. Die Wahrscheinlichkeit mitten in einem Datentransfer zu landen ist auch hier gering. Jedenfalls ist die wöchentliche Ausführung nach meiner Erfahrung zu selten, um eine performante Leistung der SSD stets zu gewährleisten. Also, wer es einfach mag nimmt den CronJob, wem die gesteuerte Vorgehensweise lieber ist, stürzt sich auf das Skript. Mit beiden Varianten liegt man sicherlich richtig.

Windows: 
Der Vollständigkeit halber auch ein Hinweis für Windows Nutzer. Unter Windows 7/10 ist analog zu Linux eine Trim Funktion für SSDs integriert. Der wöchentliche Ausführungsturnus ist jedoch auch hier eher grosszügig bemessen. Manuell kann der Trim unter Laufwerkseigenschaften / Laufwerksoptimierung getriggert werden oder man fügt einen Aufruf im Task Scheduler hinzu. Auf alten Windows XP Systemen existiert keine Trim Unterstützung, hier muss bei Bedarf ein Defragmentier Tool (z.B. OO-Defrag) aushelfen oder man bootet zwischendurch eine Linux Live CD (z.B. Slax). Aufruf in der Konsole erfolgt dann mit 'sudo fstrim -av'. Für ntfs existiert Trim Unterstützung ab Version NTFS-3G 2015.3.14. Fat32 wird i.d.R. nicht unterstützt.

Verschlüsselte SSD:
Für verschlüsselte Partitionen könnnen die folgenden Artikel als Anhaltspunkt dienen:
https://www.heise.de/ct/hotline/Linux-Verschluesselte-SSD-trimmen-2405875.html
http://www.webupd8.org/2013/01/enable-trim-on-ssd-solid-state-drives.html

Installation:
a) Teste Ausfuehrung von fstrim auf der Befehlszeile
sudo fstrim -av
(a für alle, v für verbose, Linux erkennt freundlicherweise selber die in Frage kommenden SSDs) 
 
b) prüfen, ob das Laufwerk /run/shm ein Ramdrive ist:
Das Ramdrive dient zum Zwischenspeichern der Laufwerkstabelle
sudo df -h 
hier sollte neben dem Laufwerk die Bezeichnung Ramfs oder tmpfs (oder so ähnlich stehen), falls nicht, muss ein anderes Laufwerk gesucht werden (das ist übrigens der einzige Fall, in dem man an das Skript Hand anlegen muss). 
 
c) Skript speichern
das unten stehende Skript unter 
/usr/local/bin/SSDidletrim.sh
speichern. 
 
d) Skript ausführbar machen:
sudo chmod +x /usr/local/bin/SSDidletrim.sh
 
e) Test:
Programm starten (zum Testen wird eine verkürzte Zeit angegeben)
nohup sudo /usr/local/bin/SSDidletrim.sh 9 9 & # Erlaeuterung: 9/4/2/1 Sekunden, 9 Durchlaeufe, im Hintergrund
 
f) Logfile ausgeben:
cat /var/log/SSDidletrim.log
 
g) Skript im Autostart aktivieren
wenn der Test erfolgreich war, muss das Programm noch in der Autostart verankert werden,  
dazu in der Datei /etc/rc.local folgende Zeile am Ende einfügen (aber vor exit 0, sudo ist hier nicht erforderlich)
/usr/local/bin/SSDidletrim.sh & >/dev/null
Aufruf des Editors: 
sudo nano /etc/rc.local
 
h) Rechner neustarten und evtl. noch einmal testen. 
ps -ef | grep -ivw grep | grep -i SSDidletrim.sh #Pruefe ob Prozess läuft
 
Deinstallation:
Skript löschen: 
  sudo rm /usr/local/bin/SSDidletrim.sh
Logfile löschen:
 sudo rm /var/log/SSDidletrim.log
Aufruf aus dem Autostart entfernen:
 sudo nano  /etc/rc.local 
 
Troubleshooting:
Der Befehl 'sudo fstrim -av' muss auf dem System ausführbar sein.
Das Skript muss mit chmod +x ausführbar gemacht werden.
Pruefe ob Prozess läuft: sudo ps -ef | grep -ivw grep | grep -i trim
Logfile prüfen: sudo cat /var/log/SSDidletrim.log 
Hinweise:
Falls man das Skript auf einem Netzlaufwerk ablegt: 
 - Netzlaufwerke werden oft mit der Option -noexec gemounted 
 - beim Systemstart muss das Laufwerk rechtzeitig verfügbar sein, falls das Skript aus /etc/rc.local gestartet wird.
rc.local wird nicht als bash sondern als Bourne Shell nach Posix Standard ausgeführt, es gilt der entsprechende Befehlssatz.
 
Links:
https://wiki.ubuntuusers.de/SSD/TRIM 
https://www.intel.com/content/dam/support/us/en/documents/ssdc/data-center-ssds/Intel_Linux_NVMe_Guide_330602-002.pdf
https://askubuntu.com/questions/464306/a-command-which-checks-that-trim-is-working 

----------------------------------------------
Skript (or download here)
----------------------------------------------
#!/bin/bash
# Purpose of this script: 
# perform fstrim -av every 4-8 hours, preferably when disk is idle
# Installation:
# ---------------
# - Put it to /usr/local/bin/SSDidletrim.sh
# - Set the Permissons sudo chmod 744 /usr/local/bin/SSDidletrim.sh
# - Startup: we put a new line in /etc/rc.local for systemwide start.
# /usr/local/bin/SSDidletrim.sh & >/dev/null
# exit 0
#
# Now you can reboot the pc, for activate or to activate a change.
# # **************************************************
 
##################################################
### troubleshooting ###
# please check your PATH settings if you run into problems, also make sure your script is executing with root permissions
# especially when using crontab, it may happen, that the path settings differ from what you expect. 
# so you should  test under real conditions, to be sure its working 
#
### check the followung commands are working
# cat /proc/diskstats
# sudo fstrim -av
#
### logfile watching 
# watch "cat /var/log/SSDidletrim.log && echo --- && ps -ef | grep -i SSDidletrim | grep -ivw grep"
##################################################
 
### Logfile:
SSDidletrimLog="/var/log/SSDidletrim.log"
 
# Where is your Ramdrive? 
# Check it out with  df -h or mount command.
# Possible Values Examples: /dev/shm , /run/shm 
#ramdrive=/run/shm
ramdrive=/var/tmp
 
# The SSD trim Time in Seconds
# The Raktion done in $SSDtrimtime maximum of 2 times. Depending build by the script. Protects for the cpu load. 
let SSDtrimtimeInit=4*3600; 
if [ $1 ] ; then let SSDtrimtimeInit="$1"; fi
let SSDtrimtime=$SSDtrimtimeInit; 
 
# Counter
# counter, 0 means infinite  
let counter=0;
if [ $2 ] ; then let counter="$2"; fi
 
# Log 
# appendlog= append log, newlog=new log (default), nolog= no output to logfile 
logmode="newlog"; 
if [ $3 ] ; then logmode="$3"; fi
if [ "$logmode" == "nolog" ]; then SSDidletrimLog="/dev/null"; fi #!!caution!! stringcompare spaces: if_[_$1_==_ $2_]; then usw.
if [ "$logmode" == "newlog" ]; then rm $SSDidletrimLog; fi
 
# Which disks are handled 
diskvolumes="a b c d e f g h i j k l m n o p q r s t u v w x y z"
#watch "date && cat /proc/diskstats | grep sd"
 
# Build onece the tempfiles 
if [ ! -f $ramdrive/ssddiskstate1 ]; then touch $ramdrive/ssddiskstate1;fi 
if [ ! -f $ramdrive/ssddiskstate2 ]; then touch $ramdrive/ssddiskstate2;fi
 
### 
#Wait before 1st run and initialize file ssddiskstate1
#Start logging
echo '***'
echo start disk monitoring: SSDidletrim.sh - ssd trim when idle
echo start disk monitoring: SSDidletrim.sh - ssd trim when idle >> $SSDidletrimLog
echo "date: $(date)  --- settings: "seconds:$SSDtrimtime repeat:$counter "
echo "date: $(date)  --- settings: "seconds:$SSDtrimtime repeat:$counter "  >> $SSDidletrimLog
echo '************************************************************************ '
echo '* usage:   sudo [/bin/bash] SSDidletrim.sh [ <delay> [ <count> [ [newlog|appendlog|nolog] ] ] ] '
echo '* example: sudo /usr/local/bin/SSDidletrim.sh 9 9 >/dev/null & # trim after 9/4/2/1 seconds, 9 rounds'
echo "* view log: cat /var/log/SSDidletrim.log"
echo '************************************************************************ '
echo '*** ps -ef | grep -i SSDidletrim | grep -ivw grep ***'
ps -ef | grep -i SSDidletrim | grep -ivw grep 
echo '*** ps -ef | grep -i SSDidletrim | grep -ivw grep ***'  >> $SSDidletrimLog
ps -ef | grep -i SSDidletrim | grep -ivw grep                 >> $SSDidletrimLog
echo '****************************************************************'
 
 
# Here we go... 
cat /proc/diskstats > $ramdrive/ssddiskstate1
#let SSDtrimtime=1;   # on the first run perform trim without delay
let count=0;
let shutdown=0;
 
while [ true ] ; do
sleep 1; #wait 1 sec min between every loop
# cycle diskstat to test for activity 
mv $ramdrive/ssddiskstate1 $ramdrive/ssddiskstate2 
cat /proc/diskstats > $ramdrive/ssddiskstate1
 
# Loop through all array disks and spin down idle disks. 
let allidle=1;
for disk in $diskvolumes 
do
   if [ "$(diff $ramdrive/ssddiskstate1 $ramdrive/ssddiskstate2 | grep -w sd$disk )" =  "" ] ; then 
# skip drive letters that are currently not used
if [ $(cat $ramdrive/ssddiskstate1 | grep -cw -m 1 sd$disk ) != "0" ] ; then 
  #-hdparm -y /dev/sd$disk >/dev/null 2>&1; 
  #echo hdparm -y /dev/sd$disk  
  #echo hdparm -C /dev/"sd$disk" 
  # removed: echo $(hdparm -C /dev/"sd$disk" 2> /dev/null | grep "sd$disk" -A 1)   
  # removed: echo $(hdparm -C /dev/"sd$disk" 2> /dev/null| grep "sd$disk" -A 1) >> $SSDidletrimLog
  #-echo spindown "sd$disk"  
  #-echo spindown "sd$disk"  >> $SSDidletrimLog 
  : # simply empty command
fi;
   else
let allidle=0;
   fi;
done 
 
let count+=1; 
if [ "$counter" -eq "$count" ]; then break; fi   
if [ $((count % 999)) -eq 0 ]; then rm $SSDidletrimLog; echo remove; fi  # simple logfile size limit
 
if [ $allidle -eq 1 ] ; then
  let allidlecount=$allidlecount+1;
else
  let allidlecount=0;
fi;
 
if [ $allidle -eq 1 -o $SSDtrimtime -lt 2 ] ; then 
  #trimresult=$(sudo fstrim -av)
  echo --- trim interval $SSDtrimtime --- | tee -a $SSDidletrimLog
  sudo fstrim -av                         | tee -a $SSDidletrimLog
  echo startloop':' SSDtrimtimeInit $SSDtrimtimeInit // date $(date) | tee -a $SSDidletrimLog 
  let SSDtrimtime=$SSDtrimtimeInit; 
else
  let SSDtrimtime=$SSDtrimtime/2; 
fi;
echo trimtime $SSDtrimtime count $count 
 
  #test only
  ##check networkactivity 1 sec
  #nwstat=($(dstat -n --bw 1 1 | grep -v e));  let nwaktiv=0; for ele in "${nwstat[@]}"; do if [ $ele != "0" ]; then let nwaktiv+=1 ;fi ; done ; 
  #for ele in "${nwstat[@]}"; do echo $i $ele; let i+=1; done
  #echo nwaktiv $nwaktiv
  #if [ $nwaktiv -ge 1 ] ; then 
  #  allidlecount=0;
  #fi
 
#Wait
sleep $SSDtrimtime
 
done
echo script finished':' $0 
exit 0
 
#Beta release 2.3.2020

 
#----------------------------
#Seite erstellt: 1.2.2020
 
  3 Besucher