Balancierung eines "vollen" btrfs-Dateisystems

Balancierung eines "vollen" btrfs-Dateisystems

Mitunter kommt es bei Verwendung des btrfs-Dateisystems zu Fehlermeldungen, die Festplatte sei voll, obwohl (sub-)volumes bzw. darin enthaltene Dateien kaum Platz auf der Festplatte belegen.

Es entstehen Fehlermeldungen der Art

  • No space left on device
  • can't access tty

Beispielsweise meldet df, dass nur 19% der Festplatte belegt sind:

df -h

Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf
/dev/sda2       101G     19G   82G   19% /

Jedoch meldet btrfs selbst eine volle Festplatte:

btrfs filesystem show

[...]
devid    1 size 100.61GiB used 100.61GiB path /dev/sda2

Das heißt, für btrfs ist die Festplatte scheinbar "voll". Der Grund hierfür ist, dass btrfs Daten fortlaufend wegschreibt, bis die Festplatte voll ist. Bereits benutzte Bereiche werden nicht zwingend in einer Form freigegeben, die weiteres Beschreiben der Festplatte sicherstellt. Insbesondere unterscheidet btrfs zwischen Bereichen für Daten und Metadaten und teilt entsprechende Speicherbereiche zu. Überschreiten zu schreibende Metadaten den hierfür verfügbaren Speicherbereich, dann führt dies zu einem entsprechenden Fehler, in diesem Fall zur Fehlermeldung, dass die Festplatte "voll" sei.

Nun muss (bislang noch) der Benutzer eingreifen, damit der für btrfs nicht nutzbare Speicher wieder zur Benutzung freigegeben wird. Dies wird im Folgenden beschrieben. /dev/sda2 sei im Folgenden die "volle" btrfs-Partition. Befehle sind als root durchzuführen oder jeweils sudo voranzustellen. Die Balancierung kann gestartet werden mit:

cd /mnt
mkdir fulldisk
mount /dev/sda2 fulldisk
btrfs balance start /mnt/fulldisk

Jedoch können folgende Probleme in diesem Szenario bei btrfs entstehen, da btrfs für folgende Operationen freien Speicherplatz benötigt:

  • Löschen: Dateien, Verzeichnisse und Subvolumes lassen sich nicht löschen
  • Balancierung: Balancierung lässt sich nicht durchführen

Wurde eine Balancierung gestartet und es steht nicht genügend Speicherplatz für eine Balancierung zur Verfügung, dann äußert sich dies meist wie folgt:

  • dmesg|tail liefert btrfs: 1 enospc errors during balance und btrfs balance terminiert unverrichteter Dinge
  • dmesg|tail liefert NMI watchdog: BUG: soft lockup, das Dateisystem wird read-only gemounted, es entstehen Input/Output-Fehler, Verzeichnisse sind nicht lesbar und das System reagiert mit unerwartetem Verhalten.

Vermeintlich hat man nun das Problem, dass man auf dem vollen Dateisystem nichts löschen und auch keine Balancierung durchführen kann, um Speicher wieder freizugeben. Im Folgenden wird daher ein Weg beschrieben, eine Balancierung auch bei einem "vollen" btrfs-Dateisystem durchzuführen, indem ein "volles" btrfs-Dateisystem mit einem zusätzlichen Datenträger erweitert wird. Die Speicherkapazität dieses Datenträgers kann von btrfs zur Durchführung einer Balancierung benutzt werden. Der zusätzliche Datenträger muss im Anschluss wieder aus dem btrfs-Dateisystem entfernt werden (sofern eine dauerhafte Nutzung nicht vorgesehen ist).

Alle Schritte lassen sich theoretisch auf einem laufenden System ausführen, d. h. es ist kein unmount der entsprechenden Dateisysteme nötig - btrfs erlaubt, ein Dateisystem mehrfach zu mounten. Wenn man vorsichtig sein möchte, kann man das System jedoch auch mit einer Live-CD booten. Der Kernel der Live-CD sollte für btrfs idealerweise ungefähr gleich alt sein wie der des Systems. Ein zu alter Kernel könnte fehlerbehaftet sein. Ein zu neuer Kernel könnte Änderungen auf dem Filesystem durchführen, mit denen das System dann später nicht zurecht kommt. Im Zweifel sollte man bei btrfs lieber zu einem zu neuen Kernel greifen statt zu einem zu alten. Vergleichbar verhält es sich mit den btrfs-tools, welche nicht Teil des Kernels sind.

Der beschriebene Rettungsversuch sollte nur unternommen werden, wenn eine Datensicherung vorliegt. Die zu rettende Partition kann beispielsweise mit dd physisch auf einen weiteren Datenträger gesichert werden. So lässt sich bei einem fehlgeschlagenen Rettungsversuch wenigstens der Originalzustand wieder herstellen.

Getestet mit Ubuntu 14.04.2, Kernel 3.16.0.

Balancierung eines "vollen" btrfs-Dateisystems mit USB-Stick oder Festplatte (extern oder intern)

ACHTUNG: Die Daten auf dem Datenträger (USB-Stick oder Festplatte) werden hierbei unbrauchbar! Wenn man dies nicht möchte, sollte man die Variante mittels Image-Datei (weiter unten beschrieben) verwenden.

Im vorliegenden Fall eines "vollen" 128GB-btrfs-Dateisystems waren 2 GB Speicherkapazität ausreichend.

/dev/sda2 sei das volle btrfs-Dateisystem, /dev/sdb sei ein leerer USB-Stick bzw. eine leere Festplatte

cd /mnt
mkdir fulldisk
#hier: die "volle" btrfs-Partition ist /dev/sda2
mount /dev/sda2 fulldisk
#Hinzufügen des USB-Sticks zum "vollen" btrfs-Dateisystem
btrfs device add /dev/sdb fulldisk
#Balancierung sollte jetzt klappen. Dauert einige Zeit
btrfs balance start fulldisk
#USB-Stick vom btrfs-Dateisystem wieder entfernen
btrfs device delete /dev/sdb fulldisk

Balancierung eines "vollen" btrfs-Dateisystems mittels Image-Datei

Wenn man keinen leeren bzw. zu überschreibenden Datenträger hat, kann man auch einen Datenträger bzw. Partition mit einem bestehenden Dateisystem verwenden. Im vorliegenden Fall ist es ausreichend, wenn dieser Datenträger noch ca. 2 GB freien Speicherplatz hat.

Folgende Schritte werden im Folgenden durchgeführt:

  • Mounten des zusätzlichen Dateisystems (sofern nicht schon geschehen)
  • Anlegen einer Image-Datei mit Größe 2GB auf dem zusätzlichen Dateisystem
  • Erstellung eines Loop-Devices über die Image-Datei
  • Hinzufügen des Loop-Devices zum "vollen" btrfs-Dateisystem
  • Balancierung des btrfs-Dateisystems
  • Entfernen des Loop-Devices vom btrfs-Dateisystem

/dev/sda2 sei das volle btrfs-Dateisystem, /dev/sdb1 sei die Partition mit dem zusätzlichen Dateisystem

#Anlegen eines Mountpoints
cd /mntmkdir fulldisk
#mounten des "vollen" btrfs-Dateisystems
mount /dev/sda2 fulldisk
#Anlegen eines Mountpoints
mkdir externalfs
#mounten des zusätzlichen Dateisystems
mount /dev/sdb1 externalfs
#2GB-Image-Datei erstellen
dd if=/dev/zero of=externalfs/extend.img bs=1M count=2048
#Loop-Device loop0 über die Image-Datei erstellen
losetup /dev/loop0 externalfs/extend.img
#Hinzufügen der Image-Datei zum "vollen" btrfs-Dateisystem
btrfs device add /dev/loop0 fulldisk
#Balancierung sollte jetzt klappen. Warten.
btrfs balance start fulldisk
#Image-Datei vom btrfs-Dateisystem wieder entfernen
btrfs device delete /dev/loop0 fulldisk

Vor dem Entfernen der Image-Datei aus dem btrfs-Dateisystem werden hierbei die Daten der Image-Datei in die Speicherbereiche verschoben, welche im btrfs-Dateisystem (ohne die Image-Datei) verbleiben. Wenn btrfs device delete keinen Fehler liefert, hat das Verschieben der Daten geklappt und Loop-Device sowie Image-Datei können gefahrlos entfernt werden (andernfalls: Gefahr von Datenverlust!):

#Loop-Device entfernen.
losetup -d /dev/loop0
#Image-Datei löschen
rm externalfs/extend.img

Hinweis: Die Datei extend.img kann an einem beliebigen Ort liegen, z. B. in einem bereits gemounteten Filesystem. Pfade wären dann entsprechend anzupassen. Mutige können die Datei auch in einem tmpfs anlegen, was zusätzliche Datenträger bzw. Partitionen erübrigen würde (birgt jedoch die Gefahr von Datenverlust, z. B. bei Stromausfall).

Troubleshooting: Raid1 statt Raid0

Beim btrfs device delete erhält man mitunter folgende Meldung:

error removing the device '/dev/sdb' - unable to go below two devices on raid1

Diese Meldung bedeutet, das btrfs für Daten und/oder Metadaten eine Datenspiegelung über mehrere Geräte auf Ebene des Dateisystems sicherstellen möchte. Ist die zu entfernende Festplatte das vorletzte Gerät, dann wäre diese Zusicherung nicht mehr gegeben und daher lässt sich dieser Schritt nicht durchführen.

Bemerkenswert ist allerdings, dass btrfs im Labor ein raid1 sicherstellen möchte, obwohl dies nie festgelegt wurde und eigentlich nur von dem btrfs device add herrühren kann. Wie auch immer: Wenn man vorher keine Spiegelung hatte und auch beim Entfernen des Gerätes im Anschluss keine Spiegelung auf Ebene von btrfs möchte, dann kann man btrfs die Datenspiegelung austreiben mit

btrfs balance start -f -dconvert=single -mconvert=single /path/to/mountpoint

Mittels single wird für Daten und Metadaten nur eine Kopie sichergestellt. Außerdem versucht die Balancierung nicht, die Daten gleichmäßig auf mehrere Festplatten zu fragmentieren, wie es bei raid0 der Fall wäre.

Hinweis: Die oben beschriebene Situation entstand (unter Ubuntu 14.04.4, HES-Kernel 4.2.0-30, btrfs-tools 3.12) nach Abbruch der Balancierung mittels

btrfs balance cancel /path/to/mountpoint

Erfolgskontrolle

btrfs filesystem show liefert jetzt:

Label: none  uuid: xxxxxxxx-...
    Total devices 1 FS bytes used 18.13GiB
    devid    1 size 100.61GiB used 21.03GiB path /dev/sdb2

Vor der Balancierung waren 100,61GB benutzt (s.o.), jetzt sind es nur noch 21,03GB. Das bedeutet, 79GB sind für btrfs jetzt wieder nutzbar gemacht worden.

Testweise kann man einen Schreibversuch wagen, z. B.:

cd /mnt/fulldisk
#leere Datei anlegen
touch x
#leere Datei löschen
rm x

Problemvermeidung

Es sollte vermieden werden, dass ein btrfs-Dateisystem jemals "voll" wird. Entsprechend sollte die Balancierung regelmäßig durchgeführt werden, spätestens jedoch wenn btrfs filesystem show vermeldet, dass der Benutzungsgrad gegen 100% geht.

Auf den Unix-Befehl df sollte man sich bei btrfs keinesfalls verlassen.

Ausblick

Btrfs unterliegt derzeit noch umfangreichen Entwicklungsschritten und wurde von den Entwicklern bislang nicht als "stabil" gekennzeichnet. Es bleibt zu hoffen, dass die Balancierung irgendwann ohne Benutzereingriff automatisch bzw. "on-the-fly" erfolgt. Insbesondere wäre zu vermeiden, dass das btrfs-Dateisystem die Benutzer mangels rechtzeitiger Balancierung vor absehbare, aber unzumutbare Probleme stellt. Eine automatische Balancierung mittels cron-jobs erscheint wiederum gefährlich, denn bei voller Festplatte kann die Balancierung einen manuellen Benutzereingriff nötig machen.