Zur Kommunikation virtueller Maschinen unter besonderer Berücksichtigung von SR-IOV, DirectPath und VNLink

Kommentieren Drucken
Teil 15 von 19 aus der Serie "Virtualisierungstechniken"

Die in den letzten Folgen vorgestellte I/O-Virtualisierung mit SR/IOV ist eine Leistung der Hardware. Spannend ist aber jetzt die Frage, wie die virtuellen Maschinen diese Leistung auch nutzen können. Es gibt hier unterschiedliche Standardisierungsbestrebungen, die wir in einer späteren Folge betrachten. Greifbar sind aber heute vor allem DirectPath ab VMware vSphere 4.1 und VNLink von Cisco.

Die leistungssteigernden Instrumente SR-IOV, DirectPath und VNLink werden oft nicht verstanden. Das liegt an der verdunkelten Darstellung der Hersteller und daran, dass der eine oder andere auch nicht weiß, wie die I/O in einem nichtvirtualisierten Betriebssystem funktioniert.

Das Verständnis der I/O in einem klassischen Betriebssystem ist aber elementar für das Verständnis der neuen Funktionsgruppen im Rahmen virtualisierter Systeme. Also beginnen wir genau damit.

In Abbildung 1 sehen wir ein normales Betriebssystem. Eine Anwendung läuft in einer Laufzeitumgebung. Diese wird durch einen Elementarprozess realisiert, der, geregelt durch einen Dispatcher, mit anderen Elementarprozessen abwechselnd den Prozessor benutzt. Der Scheduler ist eine Menge von CPU-Prozessen, von denen jeweils bei einem Prozessor zu einer Zeit auch nur einer läuft und die sich einander abwechseln. Die Bindung eines Elementarprozesses an einen CPU-Prozess (auch Thread genannt) führt dazu, dass der Elementarprozess kurzzeitig die CPU benutzen kann. Die Elementarprozesse zerfallen in zwei Gruppen: anwendungsunterstützende Elementarprozesse und Systemprozesse. Letztere haben die Aufgabe, die Betriebsmittel in einer Form zu abstrahieren, die im Rahmen des Prozesssystems von den Prozessen genutzt werden kann.

Unter den Systemprozessen gibt es einen oder mehrere, die für die I/O zuständig sind. Nennen wir den, der für die Benutzung einer Netzwerkadapterkarte zuständig ist, NIC-Systemprozess.

Sehen wir uns jetzt auf der Abbildung 1 an, was genau passiert, wenn eine Anwendung an eine Stelle gekommen ist, wo sie Ein- oder Ausgabe machen möchte.

  1. Das I/O-Ereignis ist grundsätzlich ein Unterbrechungsereignis. Der aktive, aktuell an einen Thread gebundene anwendungsunterstützende Elementarprozess, hier hellgrün, wird sofort vom Scheduler in einen Wartezustand versetzt, auch wenn die ihm eigentlich zustehende Zeitscheibe noch nicht ausgeschöpft ist. Der Dispatcher löst daraufhin die Bindung dieses Prozesses zum Scheduler.
  2. Der zuständige I/O-Prozess wird darüber informiert, dass ein I/O-Ereignis zu verarbeiten ist. Wir nehmen hier an, dass er sich ebenfalls im Wartezustand befindet. Ist er momentan aktiv, bekommt er die Nachricht am Ende seiner aktuellen Aktivität.
  3. Damit er wirklich arbeiten kann, muss der NIC-Systemprozess aktiviert werden und nach einer Bindung durch den Dispatcher vom Scheduler eine Zeitscheibe bekommen.
  4. Der nunmehr aktive NIC-Systemprozess greift auf die physische NIC zu. Es gibt jetzt zwei Fälle. Ausgabe: der NIC-Systemprozess schickt der NIC die auszugebenden Daten. Diese hat er aus einem Register, welches nicht eingezeichnet ist. Der ausgeben wollende I/O-Prozess hat vorher diese Daten oder einen Verweis auf sie im Register abgelegt.
  5. Eingabe: es sind Daten für den anwendungsunterstützenden Elementarprozess in einem Speicher der NIC. Diese werden ausgelesen und an ein Register übertragen, wo der Anwendungsprozess sie später abholen kann. Diese Register sind normalerweise Bestandteil der Laufzeitumgebung eines Prozesses. Die IPC mittels RDMA ist ein Spezialfall zur Nutzung dieser Register. Eine andere Möglichkeit ist die Realisierung der Register als Bereiche der aktiven Umgebung eines Prozesses im Rahmen der Speicherverwaltung.
  6. Ist die Datenübertragung in diesem Sinne abgeschlossen, geht der NIC-Systemprozess wieder in den Wartezustand, unabhängig davon, ob seine Zeitscheibe ausgeschöpft ist. Generell ist es beim Design eines Betriebssystems wichtig, dass I/O-Prozesse in der ihnen zustehenden Zeit auch fertig werden und nicht wegen Zeitüberschreitung in einen Wartezustand übergehen müssen, aus dem sie erst nach vollständiger Abarbeitung des anstehenden Prozesszyklus wieder herauskommen dürfen.
  7. Er teilt dem Dispatcher mit, wo der anwendungsunterstützende Prozess die Daten finden kann, wenn es eine Eingabe gab.
  8. Der Dispatcher weiß jetzt, dass das I/O-Ereignis erledigt ist und bindet den anwendungsunterstützenden Prozess via Scheduler wieder an einen Thread. Sobald er dran ist, kann der anwendungsunterstützende Prozess sich (bei vorhergehender Eingabe) die Daten holen und weiterarbeiten.

Diese Darstellung ist allgemein. Es gibt auch Fälle, in denen auf das Ergebnis einer I/O-Operation nicht mehr gewartet werden muss. Das ist aber für das, was hier erklärt werden soll, nicht von Belang.

Abbildung 2 zeigt dann die grundsätzliche Situation in einem virtualisierten System. Ein Hypervisor ist nichts anderes als ein spezielles Systemprogramm. Es erzeugt, steuert und löscht die VMs.

Eine VM ist wiederum nichts anders als ein Programm, welches die gesamten Funktionen und die Logik eines Betriebssystems nachmacht, so dass ein Anwendungsprogramm auf der VM genau so laufen kann wie auf einem normalen Rechner.

Systemtechnisch gesehen haben die VMs exakt den gleichen Status wie vorher ein Anwendungsprogramm. Frühe Versionen von Virtualisierungssoftware haben ja auch (sog. gehostete Lösung) den gleichzeitigen Betrieb von normalen Anwendungen und VMs auf einem Rechner erlaubt. Das war aus verschiedenen Gründen ungünstig, drum gibt es das so nicht mehr mit der Ausnahme der VM-Systemkonsole.

Damit eine VM überhaupt laufen kann, benötigt sie also mindestens einen anwendungsunterstützenden Elementarprozess, der vom Basis-Betriebssystem zur Verfügung gestellt wird. Dieser unterliegt dann den gleichen Gesetzmäßigkeiten wie zuvor. Der Vorzug einer Multi-Core, Multi-Thread-CPU ist dann, dass dem Scheduler mehr als ein aktivierbarer Thread zur Verfügung steht.

Mit diesen Vorbereitungen können wir jetzt Abbildung 2 erklären.

Die Anwendung, die eine VM darstellt, bildet die gesamte Logik aus Dispatcher, Scheduler und Elementarprozessen genau nach, damit auch mehrere Anwendungsprogramme auf ihr laufen können.

Eine Anwendung, die auf einer VM läuft, macht also das Gleiche wie auf einer realen Maschine. Der sie realisierende virtualisierte anwendungsunterstützende Elementarprozess wird unterbrochen und vom virtualisierten Dispatcher in einen Wartezustand versetzt. Danach wird ein virtualisierter NIC-Systemprozess aktiviert. Der kann aber jetzt nicht direkt auf den physikalischen NIC zugreifen, sondern führt in dem anwendungsunterstützenden Prozess, der die VM implementiert, seinerseits zu einem Unterbrechungsereignis. Der Dispatcher, jetzt mit dem Namen Hypervisor, muss einen realen NIC-Systemprozess aktivieren, der die Daten in der oben beschriebenen Art und Weise abholt oder wegbringt. Ist dieser damit fertig, kann der anwendungsunterstützende Elementarprozess, der die VM realisiert, wieder aktiviert werden und dann dem virtualisierten NIC-Systemprozess die Daten oder einen Verweis geben.

Auf einer Multi-Core-Multi-Tread-CPU können gleichzeitig viele Prozesse real aktiv sein. Von daher können auch I/O-Vorgänge unterschiedlicher VMs gleichzeitig abgearbeitet werden.

Das Ganze hört sich nicht nur kompliziert an, sondern ist es auch. Beziehen sich die I/O-Vorgänge auf z. B. Ethernet-Pakete, die z. B. in einem virtuellen Switch verarbeitet werden sollen, ist dieser vSwitch natürlich wieder ein Programm im Status eines klassischen Anwendungsprogramms. Um wenigstens den Weg von Informationen zwischen VMs, die auf der gleichen physischen Maschine laufen, etwas abzukürzen, werden solche vSwitches in den Hypervisor/Dispatcher integriert. So kann man für diese Fälle wenigstens einen relativ direkten Übergang zwischen zwei kommunizierenden vNIC-Systemprozessen auf zwei VMs implementieren.

Das nützt natürlich für die Kommunikation mit der Außenwelt wenig.

Um zur wirklichen Funktionsweise von SR-IOV vorzudringen, müssen wir zuerst einen Blick auf die Hardware-Unterstützung der Speicherverwaltung werfen.

In Abbildung 3 sehen wir die Speicherverwaltung in einem nicht virtualisierten Betriebssystem. Ein anwendungsunterstützender Elementarprozess arbeitet in der Regel nur auf einem kleinen Stück seines grundsätzlichen Speicherbereiches, dem sog. aktuellen Bereich. Das bedeutet, dass man nie den gesamten Speicherbereich vorhalten muss, sondern lediglich den aktuellen Bereich, der im Rahmen des Lokalitätsverhaltens der Anwendung genutzt wird. Eine Memory Management Unit MMU sorgt dann dafür, dass der aktuelle Bereich auch aktuell bleibt. Normalerweise schafft die MMU das, es sei denn, der anwendungsunterstützende Elementarprozess „springt“. Dann muss er solange in einen Wartezustand versetzt werden, bis die MMU „nachgeladen“ hat. Die MMU ist ein Systemprozess. Das Ganze funktioniert seit circa 50 Jahren sehr gut.

In einem virtualisierten System muss nun diese ganze Mimik nachempfunden werden, und zwar für jede VM einzeln. Der Hypervisor muss in diesem Falle alle Aktualisierungen für alle VMs und ihre vMMUs mit Hilfe von Shadow-Tabellen durchführen, siehe Abbildung 4.

Damit das überhaupt noch zu einem lauffähigen System führt, und nicht der Hypervisor die ganzen Nachladungen steuern und durchführen muss, gibt es eine Hardware-Unterstützung in Form der multiplen Speicherbilder, siehe Abbildung 5. Sie sind eine physikalische Implementierung der Shadow-Tabellen und Nachladeaufträge werden direkt an sie umgeleitet und ausgeführt.

Nach diesen vielen, aber notwendigen Vorbereitungen kommen wir jetzt zu den eigentlich spannenden Sachen.

In Abbildung 6 sehen wir die Intel-Implementierung von SR-IOV, wie ich sie ja schon mehrfach ausführlich beschrieben habe. Der Nachteil wird aber auch sofort offenbar. Sie sind in Abbildung 6 auf dem halben Wege stehen geblieben. Die aus einem Netz ankommenden Daten werden zwar wunderschön vorsortiert und in die Virtual Machine Device Queues VMDq abgelegt (vice versa für abgehende Daten), dann aber über einen, eigentlich unerwünschten Softswitch, an die VMs (im Bild Kästchen, in Wahrheit die vNIC-Systemprozesse der VM-Software) weitergereicht.

Direct Path ist nunmehr nichts anderes als die Möglichkeit der VMs, direkt auf die durch SR-IOV realisierten VMDqs zuzugreifen, ohne einen Softswitch zu benötigen!

Wie kann das funktionieren? Man erweitert dazu etwas, was man bereits hat, nämlich die Hardware-Speicherunterstützung.

Statt eines normalen Adapters wird in den Server ein SR-IOV-Adapter eingebaut, der die VMDqs enthält. In diesen Schlangen stehen die ankommenden Daten bzw. die abgehenden Daten kommen in diese Schlangen.

Jetzt kommt der Witz (siehe Abbildung 7): die Ein- und Ausgänge der Schlangen werden in einem besonderen Bereich an die Hardware-unterstützten Speicherbereiche gebunden, das sind die magenta-farbenen Kästchen in den Speicherbildern. Diese Bindung ist völlig unproblematisch, das ist für die Speicherbilder genauso, als würde eine Speicher-Kachel eingebunden.

Die HW-Unterstützung der Speicherbilder bedeutet ja, dass es die Shadow-Tabellen schon gibt. Also kann man jetzt die Shadow-Tabellen leicht um ein Bild des magenta Kästchens, welches die Enden der VMDqs repräsentiert, erweitern. Das ändert nichts an dem grundsätzlichen Mechanismus zwischen multiplen Speicherbildern und Shadow-Tabellen.

Damit sind wir eigentlich schon fertig, die MMUs müssen nur so erweitert werden, dass die vNIC-Systemprozesse jetzt auf die magenta Kästchen in den Shadow-Tabellen zugreifen. Das ist aber überhaupt kein Problem, weil das ja im Wirkungsbereich der Arbeitsweise des nachgemachten Dispatchers liegt.

Damit haben wir die direkte Kommunikation realisiert.

Man sieht jetzt ganz genau, dass der Hypervisor mit der ganzen Sache genauso wenig zu tun hat wie mit dem HW-unterstützten Speicher. Also wird er maximal entlastet. Im Status eines ganz normalen Anwendungsprogramms kann er natürlich ungehindert weiterhin kommunizieren, z. B. mit anderen HVs.
Es ergeben sich folgende unmittelbare Konsequenzen:

  • SR-IOV und DirectPath gehören eng zusammen. Ohne DP kann SR-IOV sozusagen nur halb funktionieren. Ohne SR-IOV könnte DP nicht funktionieren, weil es ja keine VMDqs gäbe
  • Die Leistung dieser Kombination übertrifft die aller anderen denkbaren Varianten deutlich. Mit viel Glück schafft SR-IOV mit Softswitch 10-GbE, SR-IOV mit DP aber 30-GbE
  • Die Latenz dieser Kombination ist sehr gering, weil es keinen Softswitch gibt, der in seiner Reaktionsfähigkeit dadurch beeinträchtigt wird, dass er im Status eines Anwendungsprogrammes auf die Bindung an einen anwendungsunterstützenden Elementarprozess angewiesen ist. Aktuell liegen von VMware und Intel noch keine Messungen dazu vor, aber es gibt keinen Grund, warum die Latenz abgesehen von einem geringen Premium nicht im Bereich eines physischen Servers liegen sollte. Ich schätze das jetzt mal mit 2-5 µs ab.
  • SR-IOV und DP machen aus vShpere 4.1 ein realzeitfähiges System!

Momentan harmoniert DP aber noch nicht mit vMotion. Müssen wir befürchten, entweder auf das beliebte vMotion oder auf das schnelle DP verzichten zu müssen? Nein, es ist nur noch nicht fertig programmiert verfügbar. Die Wanderung einer VM basiert auf dem Austausch eines Vektors, der die Laufzeitumgebung der VM beschreibt. Dieser Vektor ist sozusagen die DNA der VM und man kann mit ihm die VM auf einer fremden Maschine reproduzieren. Dieser Vektor kann erweitert werden. So gab es ja z. B. Erweiterungen von vNICs auf vHBAs und bald kommen auch die vHCAs für CEE und IB. Und jetzt muss man eben ein neues Feld definieren, in dem steht, dass der vNIC-Systemprozess nicht mehr auf das Register, was ihm der Softswitch gibt, zugreifen soll, sondern auf den speziellen Bereich in der Shadow-Tabelle. Das setzt natürlich voraus, dass die Zielmaschine für die Wanderung auch eine HW-Unterstützung für den Speicher hat, aber das ist schon länger normal.

Letzte Frage: was ist VNlink? Zunächst ja einmal eine Möglichkeit von Cisco, VMs via VMware-Softswitch oder Nexus1000V an Cisco-Adapter zu binden. Natürlich wissen die auch, dass das zu langsam ist. Also wurde VNlink so erweitert, dass es auch SR-IOV kann. Und damit kann es eben auch mit DP zusammen benutzt werden. Damit macht Cisco noch nichts anderes als andere Hersteller, wie z. B. Intel. Eine besondere Qualität erreicht es jedoch in Verbindung mit den Fabric Extendern. Diese sind ja letztlich Verlängerungen eines internen Switch-Busses und verlegen die Switch-Ports unter Weglassen einer normalen Adapterkarte direkt in den Server. Das heißt technisch gesehen, dass nicht mehr die VMDqs von SR-IOV, sondern direkt die E/A-Warteschlangen eines Switch-Ports an die Shadow-Tabellen gebunden werden. Damit sparen sie circa 2-3 µs. So können sie die Zeit, die sie in ihrem komplizierten Switch-ASIC z. B. gegenüber Blade verlieren, wieder wettmachen. Ein UCS mit Fabric Extender an einem 5500 wird damit eine Latenz haben, die einem vergleichbaren IBM-System mit Blade-Adapter und Blade-Switch nicht wesentlich nachsteht. Es wird aber auch nicht wirklich schneller sein. Man darf dabei nicht vergessen, dass sie an den durchgelegten Switch-Ports die Logik von SR-IOV nachempfinden müssen, denn sonst fehlt ja die notwendige Vorverteilung auf die Ziel-VMs.

« Teil 14: Prozessorarchitekturen und SR-IOVTeil 16: Hoch leistungsfähige VM-Migration »


zugeordnete Kategorien: Virtualisierung
zugeordnete Tags:

Sie fanden diesen Beitrag interessant? Sie können



Anmerkungen, Fragen, Kommentare, Lob und Kritik:

Ihre E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*

.