Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen RevisionVorhergehende ÜberarbeitungNächste Überarbeitung | Vorhergehende ÜberarbeitungNächste ÜberarbeitungBeide Seiten der Revision | ||
dnbd3_fuse_cow [2022/09/08 16:07 CEST] – dnbd3-Autostart korr. verschoben chr | dnbd3_fuse_cow [2022/09/09 18:28 CEST] – Threads und Locks mscherle | ||
---|---|---|---|
Zeile 77: | Zeile 77: | ||
== Ubuntu 22.04 == | == Ubuntu 22.04 == | ||
+ | |||
Beispiel Ubuntu 22.04 | Beispiel Ubuntu 22.04 | ||
Zeile 110: | Zeile 111: | ||
</ | </ | ||
- | === Bau des cow_merger_service | + | === Bau und Installation |
< | < | ||
Zeile 123: | Zeile 124: | ||
</ | </ | ||
- | Erzeugte Dateien (Serverprogramm und Konfigurationsdatei) werden in cow_merger_service/ | + | Erzeugte Dateien (Serverprogramm und Konfigurationsdatei) werden in cow_merger_service/ |
=== Autostart === | === Autostart === | ||
Zeile 149: | Zeile 150: | ||
… und führen anschließend den Befehl ' | … und führen anschließend den Befehl ' | ||
- | ==== Konfiguration ==== | + | ===== Konfiguration |
+ | |||
+ | ==== dnbd3-Server ==== | ||
+ | |||
+ | Die Konfigurationsdatei **server.conf** wird standardmässig in /etc/dnbd3 abgelegt. Eine Beispieldatei ist in im bauverzeichnis unter **pkg/ | ||
+ | |||
+ | Der dnbd3-Server wird üblicherweise beim Systemstart [[# | ||
+ | |||
+ | ==== cow_merger_service | ||
Serverseitig sollten 3 Verzeichnisse erstellt werden: | Serverseitig sollten 3 Verzeichnisse erstellt werden: | ||
Zeile 157: | Zeile 166: | ||
mkdir [OriginalImageDirectory] | mkdir [OriginalImageDirectory] | ||
mkdir [DestinationDirectory] | mkdir [DestinationDirectory] | ||
- | </ | ||
- | |||
- | === dnbd3-Server === | ||
- | |||
- | Die Konfiguration des dnbd3-Servers erfolgt per **server.conf**. Eine Beispielkonfiguration ist in **pkg/ | ||
- | |||
- | < | ||
- | dnbd3-server --config [Pfad zur Config] | ||
</ | </ | ||
Zeile 324: | Zeile 325: | ||
===== Funktionsweise ===== | ===== Funktionsweise ===== | ||
+ | ===Struktur der Daten=== | ||
+ | Die Datenstruktur ist in zwei Hauptteile unterteilt. Die eigentlichen Daten die die Änderungen enthalten und die dazugehörenden Metadaten. | ||
+ | Die Änderungen werden in der data Datei gespeichert während die Meta daten in der meta Datei gespeichert werden. | ||
+ | Die Daten werden in Blöcken gespeichert, | ||
+ | Ein cow block hat eine cow_block_metadata_t Struktur, die die entsprechenden Metadaten enthält. Die Metadaten werden verwendet, um festzustellen, | ||
+ | ob ein dnbd3-Block beschrieben wurde, wo dieser Block in der Datendatei gespeichert ist, wann er zuletzt geändert und wann er hochgeladen wurde. | ||
+ | Aber dazu später mehr. | ||
+ | |||
+ | |||
+ | ===Blockmetadata=== | ||
+ | Die Datenstruktur zur Speicherung von " | ||
+ | Das gesamte L1-Array wird zu Beginn initialisiert und kann nicht in der Größe verändert werden, daher begrenzt die Größe des L1-Arrays die Gesamtgröße des Images. | ||
+ | Die L2 werden dynamisch erstellt, wenn sie benötigt werden. Zu Beginn sind also alle L1-Pointer Null. Die L2's sind Arrays, die 1024 cow_block_metadata_t structs enthalten. | ||
+ | |||
+ | < | ||
+ | typedef struct cow_block_metadata | ||
+ | { | ||
+ | atomic_int_least64_t offset; | ||
+ | atomic_uint_least64_t timeChanged; | ||
+ | atomic_uint_least64_t uploads; | ||
+ | atomic_char bitfield[40]; | ||
+ | } cow_block_metadata_t; | ||
+ | </ | ||
+ | |||
+ | < | ||
+ | COW_L2_STORAGE_CAPACITY = 1024 * 320 * 4096 | ||
+ | COW_METADATA_STORAGE_CAPACITY = 320 * 4096 | ||
+ | </ | ||
+ | |||
+ | Jeder cow_block_metadata_t enthält ein 40 Byte langes, also 320 Bit großes Bitfeld. Das Bitfeld zeigt an, ob der entsprechende dnbd3-Block Daten enthält oder nicht. | ||
+ | Beginnt das Bitfeld z.B. mit 01..., so enthalten die ersten 4096 Byte keine Daten und die nächsten 4096 Byte enthalten Daten. | ||
+ | Jeder cow_block_metadata_t speichert also die Metadaten von bis zu 320*4096 Bytes, wenn alle Bits auf 1 gesetzt sind. | ||
+ | Das Offset-Feld enthält das Offset, an welcher Stelle | ||
+ | Die timeChanged Feld enthält die Unix-Zeit, zu der der Block zuletzt geändert wurde. | ||
+ | Sie ist 0, wenn er nie geändert wurde oder wenn die letzten Änderungen bereits hochgeladen wurden. | ||
+ | |||
+ | Die L2-Arrays und cow_block_metadata_t sind nach den ursprünglichen Offsets des Images sortiert. | ||
+ | So adressiert der erste L1-Zeiger, d.h. das erste L2-Array, die ersten 1024 * 320 * 4096 Bytes (L2Size * bitfieldsize * DNBD3Blocksize) der Daten und so weiter. | ||
+ | |||
+ | Um zum Beispiel die " | ||
+ | |||
+ | < | ||
+ | 4033085440 / ( COW_L2_STORAGE_CAPACITY ) ≈ 3.005 | ||
+ | </ | ||
+ | |||
+ | Dann würde man den fünften cow_block_metadata_t im L2-Array nehmen wegen | ||
+ | < | ||
+ | (4033085440 mod COW_L2_STORAGE_CAPACITY) / COW_METADATA_STORAGE_CAPACITY = 5 | ||
+ | </ | ||
+ | |||
+ | ===Leseanfrage=== | ||
+ | Wird eine Lese anfrage gestellt werden alle benötigten " | ||
+ | Falls ja werden die daten aus der Daten Datei geladen. Das Offset ist dabei das cow_block_metadata_t Offset plus 4096 * blocknummer im Cow Block. | ||
+ | Falls das bit im bitfield jedoch 0 ist, gibt es zwei Fälle. Ist das offset kleiner als die Origial Image Größe werden die Daten mit 0 aufegfüllt (die Origial Image Größe Variable kann bei einem truncate die kleiner ist als das uhrprünglich Image weiter reuziert werden). | ||
+ | Abdernfalls werden die Daten von dem dnbd3 server geladen. | ||
+ | Da die Leseanfragen an den dnbd3 server asyncron ausgeführt werden, gibt es eine workCounter variable, die Anzahl der Parallelen anfragen plus der einzelnen lokalen enthält. | ||
+ | Erst wenn diese Variable 0 ist, wird die Fuse-Anfrage beendet. | ||
+ | |||
+ | ===Schreibanfrage=== | ||
+ | Wenn bei einer Schreibanfrage der Anfang oder das Ende nicht mit einem Vielfachen von 4096 übereinstimmt, | ||
+ | Das liegt daran, dass jeder 4096-Byte-Block vollständige Daten benötigt, denn wenn das Bit im Bitfeld für diesen Block gesetzt ist, werden alle Daten lokal gelesen. | ||
+ | Um den Block aufzufüllen, | ||
+ | Liegt er außerhalb der ursprünglichen Imagegröße (weil z.B. das Image größer geworden ist), werden die fehlenden Bytes mit 0 aufgefüllt. | ||
+ | Die Schreibanfrage errechnet aus dem Offset die entsprechenden cow_block_metadata_t. | ||
+ | Wenn die entsprechende cow_block_metadata_t noch nicht existiert, wird sie angelegt. | ||
+ | Die Daten werden in die Datendatei geschrieben, | ||
+ | Dann wird das entsprechende Bit in den Bitfeldern gesetzt und die timeChanged aktualisiert. | ||
+ | Wenn mehr Daten zu schreiben sind, wird die nächste cow_block_metadata_t berechnet und die obigen Schritte werden wiederholt. | ||
+ | Die Variable workCounter wird auch hier verwendet, um sicherzustellen, | ||
+ | |||
+ | ===Block-Upload=== | ||
+ | Für das Hochladen von Blöcken gibt es einen Hintergrund-Thread, | ||
+ | ob timeChanged ungleich 0 ist und die Zeitdifferenz zwischen now und timeChanged größer als COW_MIN_UPLOAD_DELAY ist. | ||
+ | Ist dies der Fall, wird der Block hochgeladen. Die timeChanged Variable vor dem Upload wird zwischengespeichert. | ||
+ | Nach dem Hochladen wird timeChanged auf 0 gesetzt, wenn es noch die gleiche Zeit wie die zwischengespeicherte hat (wenn nicht, gab es eine Änderung während des Hochladens und es muss erneut hochgeladen werden). | ||
+ | Sobald das Image ausgehängt ist, wird COW_MIN_UPLOAD_DELAY ignoriert und alle Blöcke, die eine andere Zeit als 0 haben, werden hochgeladen. | ||
+ | Das Hochladen erfolgt über einen REST-Request. Es gibt zwei verschiedene Limits für die Anzahl der parallelen Uploads, diese können in der config.h konfiguriert werden. | ||
+ | |||
+ | ===Dateien=== | ||
+ | Wenn eine neue CoW-Sitzung gestartet wird, werden eine neue Meta-, Daten- und, falls in den Befehlszeilenargumenten festgelegt, eine status.txt-Datei erstellt. | ||
+ | |||
+ | ==status.txt== | ||
+ | Die Datei status.txt kann mit dem Kommandozeilenparameter --cowStatFile aktiviert werden. | ||
+ | |||
+ | Die Datei enthält die folgende Informationen: | ||
+ | < | ||
+ | uuid=< | ||
+ | state=backgroundUpload | ||
+ | inQueue=0 | ||
+ | modifiedBlocks=0 | ||
+ | idleBlocks=0 | ||
+ | totalBlocksUploaded=0 | ||
+ | activeUploads: | ||
+ | ulspeed=0.00 | ||
+ | </ | ||
+ | |||
+ | * Die **uuid** ist die Sitzungs-Uuid, | ||
+ | |||
+ | * Der **Status** ist **backgroundUpload**, | ||
+ | |||
+ | * **Queue** sind die CoW Blöcke, die gerade hochgeladen werden oder auf einen freien Slot warten. | ||
+ | |||
+ | * **ModifiedBlocks** sind CoW Blöcke, die Änderungen aufweisen, die noch nicht auf den Server hochgeladen wurden, weil die Änderungen zu aktuell sind. | ||
+ | |||
+ | * **totalBlocksUploaded** die Gesamtanzahl der CoW Blöcke, die seit dem Einhängen des Images hochgeladen wurden. | ||
+ | |||
+ | * **activeUploads** ist die Anzahl der Blöcke, die gerade hochgeladen werden. | ||
+ | |||
+ | * **ulspeed** die aktuelle Upload-Geschwindigkeit in kb/s. | ||
+ | |||
+ | Sobald alle Blöcke hochgeladen wurden, wird der Status auf erledigt gesetzt. Wenn Sie COW_DUMP_BLOCK_UPLOADS festlegen (in config.h), wird eine Liste aller Blöcke, sortiert nach der Anzahl der Uploads, in die Datei status.txt kopiert, nachdem der Block-Upload abgeschlossen ist. | ||
+ | |||
+ | Mit dem Kommandozeilenparameter --cowStatStdout wird die gleiche Ausgabe der Statistikdatei in stdout ausgegeben. | ||
+ | |||
+ | ==meta== | ||
+ | Die Metadatei enthält die folgenden Header: | ||
+ | < | ||
+ | // cowfile.h | ||
+ | typedef struct cowfile_metadata_header | ||
+ | { | ||
+ | uint64_t magicValue; | ||
+ | atomic_uint_least64_t imageSize; | ||
+ | int32_t version; | ||
+ | int32_t blocksize; | ||
+ | uint64_t originalImageSize; | ||
+ | uint64_t metaDataStart; | ||
+ | int32_t bitfieldSize; | ||
+ | int32_t nextL2; | ||
+ | atomic_uint_least64_t metadataFileSize; | ||
+ | atomic_uint_least64_t dataFileSize; | ||
+ | uint64_t maxImageSize; | ||
+ | uint64_t creationTime; | ||
+ | char uuid[40]; | ||
+ | char imageName[200]; | ||
+ | } cowfile_metadata_header_t; | ||
+ | </ | ||
+ | Nach diesem Header beginnt bei Byte 8192 die oben erwähnte l1- und dann die l2-Datenstruktur. | ||
+ | ==data== | ||
+ | Die Datendatei enthält den magicValue und am Offset 40 * 8 * 4096 (Kapazität eines cowfile_metadata_header_t) beginnt der erste Datenblock. | ||
+ | |||
+ | ==magic values in den Headern der Dateien== | ||
+ | Die magic values in beiden Dateien werden verwendet, um sicherzustellen, | ||
+ | < | ||
+ | //config.h | ||
+ | #define COW_FILE_META_MAGIC_VALUE ((uint64_t)0xEBE44D6E72F7825E) // Magic Value to recognize a Cow meta file | ||
+ | #define COW_FILE_DATA_MAGIC_VALUE ((uint64_t)0xEBE44D6E72F7825F) // Magic Value to recognize a Cow data file | ||
+ | </ | ||
+ | |||
+ | ===Threads=== | ||
+ | Diese Erweiterung verwendet zwei neue Threads: | ||
+ | < | ||
+ | tidCowUploader | ||
+ | tidStatUpdater | ||
+ | </ | ||
+ | * **tidCowUploader** ist der Thread, der die Blöcke auf den Cow-Server hochlädt. | ||
+ | |||
+ | * **tidStatUpdater** aktualisiert die Statistiken in stdout oder die Statistikdateien (je nach Parametern). | ||
+ | |||
+ | ===Locks=== | ||
+ | Diese Erweiterung verwendet einen neuen Lock cow.l2CreateLock. Er wird verwendet, wenn ein neues L2-Array zugewiesen wird. | ||
<note warning> | <note warning> |