Fehlerverfolgungsjagd quer durch alle Komponenten der Firewall GeNUGate aus Stoßstangen­-Kameraperspektive

A Bug's Life
Kommentare

Beim Zusammenspiel von Hard- und Software ist man im Produktionsprozess oft sehr großen Unwägbarkeiten ausgesetzt. Hersteller und Kunde vertreten dabei oft unterschiedliche Sichtweisen. Um hier den Blick für die vielfältigen Probleme zu schärfen und gleichzeitig einiges an Unterhaltung zu bieten, versuchen wir einmal den Kampf mit einem besonders hartnäckigen Hardware- und Softwareproblem als interessante Geschichte aus Entwicklersicht zu erzählen.

Als Hersteller einer Firewall-Appliance (also Hardware und Software, die perfekt zusammenspielt und eine bestimmte Aufgabe erfüllt) stolpert man, wie bereits angesprochen, über diverse Unwägbarkeiten, die sich aber vor einem Projekt oftmals kaum offenbaren. Auch der Einkauf von Hardware anhand von technischen Datenblättern unter der Annahme, dass mit der anvisierten Konfiguration schon alles funktionieren wird, entpuppt sich meistens eher als russisches Roulette. Aus Kundenperspektive ist es dagegen oft kaum nachvollziehbar, warum denn der Hersteller schon wieder verzögert mit seinem Produkt auf den Markt kommt, oder warum sich die eben noch in den höchsten Tönen gelobte neue Version im Praxiseinsatz als instabil erweist.

Achtung: Der Artikel geht teilweise sehr in technische Details, daher werden Vorkenntnisse über Betriebssysteme und Rechnerarchitektur empfohlen.

Ein Kunde von GeNUA möchte die Hochsicherheits-Firewall GeNUGate in einer sehr anspruchsvollen Konfiguration einsetzen: Es wird ein Vierfach-HA-Cluster verlangt, bei dem jede Maschine aufgrund der hohen Anzahl von DMZs 17 (ja, siebzehn!) Netzwerkschnittstellen besitzen soll. Unsere Techniker bauen vor Ort ein Setup auf, das genau der vom Kunden gewünschten Konfiguration entspricht. Es basiert auf der zu diesem Zeitpunkt neuesten GeNUGate-Software (GeNUGate 6.2, die auf OpenBSD 4.1 aufsetzt), und die vielen Netzwerkschnittstellen werden so eingestellt, dass sie in der Kundenumgebung genau die jeweils anliegenden Netze adressieren. Testläufe des Aufbaus lassen keinen Grund zur Sorge entstehen, auch die im Hochverfügbarkeitseinsatz so essenziellen HA-Übernahmen funktionieren problemlos, nachdem durch vorsätzliches Abstecken von Kabeln eine solche Situation herbeigeführt wurde. Von der Spezifikation der Hardwarekomponenten her ist diese Konstellation natürlich kein Problem, auch der Hardwarehersteller sieht keine Schwierigkeiten. Denn es sind ja ausreichend PCI-Slots auf der Hauptplatine verfügbar, und mit entsprechend rigoroser Verwendung von Dreifach-Netzwerkkarten unter Einbeziehung der beiden Onboard-Interfaces lassen sich die 17 Netzwerkschnittstellen auch hardwaremäßig abbilden. Das alles funktioniert bis zum Produktivbetrieb der Firewall GeNUGate, dann jagt ein Absturz den anderen, das System ist instabil, es treten Paketverluste auf und manche Netzwerkschnittstellen funktionieren nach einiger Zeit gar nicht mehr – bis zum nächsten Reboot. Eine mittlere Katastrophe also, vor die unsere Techniker gestellt werden: Denn der Kunde erwartet natürlich die volle Funktionstüchtigkeit einer Maschine, die konform zur Spezifikation zusammengestellt worden ist – immerhin war von Problemen ja vorher auch nie die Rede. Ein Wettlauf gegen die Zeit beginnt.

Stabilitätsprobleme von Anfang an

Der Kunde meldet, dass sobald Last auf den neuen Firewall-Cluster geschwenkt wird, sporadische Abstürze auftreten, die in ihrer Häufigkeit für einen ernsthaften Produktivbetrieb inakzeptabel sind. Daraufhin versuchen unsere Kollegen von Customer Care, der Natur der Abstürze auf die Spur zu kommen. Wie man als Techniker weiß, gibt es nämlich bei Abstürzen sehr feine und entscheidende Unterschiede.

Livelock: Das System ist nicht abgestürzt, sondern läuft noch. Es ist nur nicht mehr in der Lage, Dienste zu erbringen, und ist daher für den Benutzer kaum noch erreichbar. Feststellbar ist das dadurch, wenn etwa Pings noch beantwortet oder TCP-Verbindungen noch einen korrekten 3-Wege-Handshake durchlaufen, aber dann durch die eigentliche Verbindung keinerlei Daten mehr laufen wollen. Grund für ein solches Verhalten kann beispielsweise sein, dass dem System irgendeine lebenswichtige Ressource ausgegangen ist (Prozesse, Dateideskriptoren oder Speicher) und es sich von selbst nicht mehr von dieser Situation erholen kann. Abhilfe ist hier in zwei Wegen möglich: Zum einen kann das System hart neugestartet werden (Reset), was aber meist mit dem Verlust der aktuell noch in der Verarbeitung befindlichen Daten sowie mit einem Dateisystem-Check nach dem Reboot einhergeht. Zum anderen kann versucht werden, doch noch Kontrolle über das Betriebssystem zu erlangen und einen Neustart aus dem System heraus zu initiieren. Hierbei hat man den Vorteil, dass das System selbst noch in der Lage ist, wichtige Sicherungsmaßnahmen zu treffen. Diese Kontrolle kann zum einen über den OpenBSD-Kernel-Debugger erlangt werden (dieser ist allerdings bei vielen Systemen aus Sicherheitsgründen deaktiviert, bei der GeNUGate ab Werk selbstverständlich auch). Eine andere Möglichkeit ist der Login über die serielle Konsole, falls dieser in dem vorliegenden Systemzustand überhaupt noch möglich ist.

Spontan-Reboot: Dieser Fehler gehört zu den schlimmsten, die auftreten können: Das System stolpert bei der Ausführung von Betriebssystemcode über einen so eklatanten Fehlzustand, dass es den Reset der Maschine entweder bewusst herbeiführt oder dass dieser Reset ein ungewollter Nebeneffekt von vorhandenen korrupten Daten ist. Feststellbar ist der spontane Reboot bei der Firewall GeNUGate beispielsweise dadurch, dass das System nicht mehr funktioniert, weil es im Eingabe-Prompt der AUTOBOOT-Aufforderung auf eine Eingabe durch den Administrator wartet, der das System wieder zum Hochfahren freigibt. Hierbei handelt es sich um ein Sicherheitsmerkmal der GeNUGate. Die Gründe für einen spontanen Reboot können sehr vielfältig sein. So ist ein defektes Mainboard, korrupter Hauptspeicher oder eine instabile BIOS-Version oftmals ein Grund. Allerdings kann ein spontaner Reboot auch ein Anzeichen für einen sehr gut versteckten Fehler im Betriebssystem sein. Differenzierte Aussagen hierzu können meist erst nach der Beobachtung mehrerer solcher Reboots getroffen werden, weil dann entweder eine Systematik erkennbar ist oder gerade die Abwesenheit derselben. Abhilfe bei spontanen Reboots kann ein Hardwarecheck bringen, etwa mit Memtest, Festplattentest oder durch das Austauschen von Komponenten oder des Gesamtsystems auf Verdacht.

Freeze: Freeze bedeutet das komplette Einfrieren des Systems unter Ausbleiben jeglicher testbarer Reaktion. Im Gegensatz zum Livelock, bei dem noch einzelne Teile des Betriebssystems laufen, kommt es hier zum vollständigen Anhalten. Feststellbar ist der Freeze, wenn nicht nur keinerlei Ping-Reaktionen von der Maschine feststellbar sind, sondern wenn auch auf der seriellen Konsole des Systems nicht einmal mehr Leerzeichen oder ENTER als Konsolen-Echo zum Benutzer zurückkommen. Oft ist auch ein Wechseln des Num-Lock-Status (setzt eine lokal an das System angeschlossene Tastatur voraus) in so einem Fall nicht mehr möglich. Der Grund für einen Freeze ist ebenso wenig eindeutig wie der von spontanen Reboots – es können hardwareabhängige Effekte ebenso der Auslöser sein wie versteckte Fehler im Betriebssystem.

Kernel Panic: Von allen Fehlersymptomen ist der Kernel Panic für einen Entwickler oder Support-Mitarbeiter das absolut Dankbarste. Feststellbar ist der Kernel Panic durch Ausbleiben sämtlicher externer Reaktion (ähnlich wie beim Freeze), allerdings wird in diesem Fall auf der Systemkonsole (entweder angeschlossener Bildschirm oder serielles Kabel) ein Debugger-Prompt ausgegeben, das den Experten die Operation der Maschine am offenen Herzen ermöglicht:

panic: kernel diagnostic assertion "ci->ci_fpcurproc == p"
failed: file "/usr/src/sys/arch/i386/isa/npx.c",
line 838 Stopped at Debugger+0x4: leave RUN AT LEAST
'trace' AND 'ps' AND INCLUDE OUTPUT WHEN REPORTING
THIS PANIC! DO NOT EVEN BOTHER REPORTING THIS
WITHOUT INCLUDING THAT INFORMATION! ddb>

Selbst Laien können den GeNUA-Mitarbeitern einen großen Gefallen tun, indem sie obige Meldung sowie (wie in der Meldung gefordert) die Ausgabe der Befehle trace und ps an den Support von GeNUA weiterleiten. Allerdings werden GeNUGate-Systeme aus Sicherheitsgründen standardmäßig nicht mit eingeschaltetem Kernel-Debugger ausgeliefert, sodass man als Benutzer derartige Meldungen im Normalfall gar nicht zu Gesicht bekommt (Für Interessierte: Eingeschaltet werden kann der Kernel-Debugger übrigens mit den sysctl-Variablen ddb.console und ddb.panic.) Übrigens: Wer keine serielle Konsole besitzt, sondern diese Ausgabe am Bildschirm liest, kann von den Vorzügen des heutigen Digitalzeitalters Gebrauch machen: Schießen Sie mit Ihrer Digitalkamera oder Ihrem Handy ein Foto der Bildschirmseiten. Die Qualität mag nicht besonders gut sein, aber die dort sichtbaren Informationen helfen an anderer Stelle unglaublich weiter, auch wenn die Codes für den normalen Benutzer einfach nur kryptisch aussehen mögen.

Aufmacherbild: Computer Bug von Shutterstock / Urheberrecht: AleXandarM

[ header = Installation eines seriellen Konsolenzugangs ]

Installation eines seriellen Konsolenzugangs

Da der Kunde die Firewalls bisher einfach ohne serielle Konsole betrieben hatte, konnte die oben erwähnte Differenzierung der Abstürze nicht vorgenommen werden. Daher wurde als erste Maßnahme ein serieller Konsolenzugang eingerichtet, über den die Admins vor Ort (und in letzter Konsequenz der Support von GeNUA) genauere Aufschlüsse über die Absturzursache erlangen konnten. Der Konsolenzugang brachte dann gleichzeitig Klarheit sowie Ernüchterung: Die Abstürze der GeNUGate waren im Sinne der obigen Taxonomie harte Freezes (siehe Abschnitt Freeze). Es war daher vollkommen offen, ob die beobachteten Symptome auf ein Hardware- oder ein Softwareproblem zurückzuführen waren. Aus diesem Grund musste sehr breit und umfangreich recherchiert werden, und es wurden viele Versuche durchgeführt, bis man dem Problem näher kam.

Nachbau im Testlabor von GeNUA

Zunächst wurde im Labor von GeNUA eine vergleichbare Teststellung nachgebaut: Unsere Techniker setzten eine SuperMicro-Maschine unseres Lieferanten Pyramid mit den erwähnten 17 Netzwerkschnittstellen unter Last, indem auf mehreren Schnittstellen gleichzeitig enormer Netzwerkverkehr erzeugt wurde, der von der Maschine bewältigt werden musste. Die Freezes ließen sich mit dieser Methode relativ zuverlässig reproduzieren, allerdings immer noch ohne zufriedenstellende Erklärung des Phänomens. Eine weitere Besonderheit gab es bei den Freezes: Nachdem die betroffenen Maschinen mehrere Minuten eingefroren in einem undefinierten Zustand verweilten, haben sie sich spontan selbst rebootet (möglicherweise wegen eines Hardware-Watchdogs, der auf einen Timeout gelaufen ist).

Um ein systematisches Vorgehen beizubehalten, versuchten unsere Techniker nun, statistische Information über das Problem zu erhalten. Der Traffic zum Testen wurde durch FloodPings erzeugt, sodass ein Variieren der Ping-Payload sehr einfach zum Variieren der Paketgröße verwendet werden konnte. Man fand heraus, dass sich die Firewall bereits innerhalb von 10 Sekunden komplett einfrieren lässt, wenn nur die Paketgröße in einem bestimmten Intervall (787 bis 802 Bytes) liegt. Außerhalb des genannten Intervalls dauert es Stunden statt Sekunden, bis ein Einfrieren zu beobachten ist. Dieser Effekt, zusammen mit einer früheren Spur aus dem Internet (Google lässt grüßen), führte dazu, dass nun die PCI-Bus-Taktung in den Fokus der Bemühungen rückte. Verstärkt wurden derartige Vermutungen noch dadurch, dass eine Maschine mit den beschriebenen Symptomen plötzlich völlig normal lief, nachdem man eine der PCIe-Netzwerkkarten steckplatztechnisch mit dem ebenfalls auf einer PCIe-Karte befindlichen RaidController von LSI vertauschte. Endgültige Klarheit erlangten unsere Spezialisten in ihren Versuchsreihen, als sich das Problem sowohl unter FreeBSD als auch unter Linux 2.6, und von unserem Hardwarelieferanten Pyramid sogar unter Windows reproduzieren ließ. Es lag also ein Hardwareproblem vor, jetzt mussten wir es nur noch finden.

Unsere Techniker wollten zunächst sicherstellen, dass auf Mainboard und Netzwerkkarten jeweils das aktuelle BIOS bzw. die aktuelle Firmware verwendet wurde. Das Update der Intel-Netzwerkkarten gestaltete sich durch das nicht gerade kommunikationsfreudige DOSTool eher kompliziert. Wir fanden außerdem heraus, dass die Tools, die bei Intel für das Flashen der Karte zur Verfügung standen, keine unserer drei Baureihen (82546G, 82571EB und 80003ES2) unterstützten. Darüber hinaus wurden auch noch mittels des Linux-Werkzeugs ethtool die Power-Management-Funktionen der Netzwerkchips außer Betrieb gesetzt – zum einen würden diese bei dem Serversystem kaum gebraucht werden, zum anderen stellen auch sie eine potenzielle Fehlerquelle dar. Leider brachte auch das Update der Firmware keine Veränderung der Symptome: Die Maschinen froren immer noch nach einer bestimmten Zeit ein, ohne dass die Gründe erkennbar gewesen wären.

Andere Netzwerkkarten-Konstellationen

Selbstverständlich wurden in dieser Zeit viele verschiedene Netzwerkkarten in das Laborsystem ein- und ausgebaut. Einerseits wollte man ausschließen, dass eine der Karten der Teststellung selbst defekt war, andererseits sollten die Versuche zeigen, ob vielleicht eine bestimmte Modellreihe für die Fehler verantwortlich war. Es zeigte sich zwar, dass stabilere Konfigurationen mit weniger Interfaces durchaus möglich waren – so zum Beispiel eine mit 16 Interfaces, zusammengesetzt aus zwei Quad-Port-Karten (PCIe) und drei Dual-Port-Karten (PCI-X). Allerdings war es für den Kunden vollkommen ausgeschlossen, weniger als 17 Interfaces zu verwenden, und außerdem hätte die Verwendung von Quad-port-Karten ein anderes Gehäuse erfordert: Die ausgelieferten Maschinen waren im 2HE-Format ausgeführt, und ab vier Ports kommt man nicht mehr mit dem Low-Profile-Format der Karten aus, sondern muss Karten in voller Bauhöhe verwenden. Diese passen aber senkrecht nicht in zwei Höheneinheiten hinein.

PCI-Bustakt

Also wurde nun im BIOS der Maschine mit verschiedenen Taktraten für den PCI-Bus experimentiert. Ab Werk wurden die Maschinen mit 133 MHz Bustakt ausgeliefert. Es war auffällig, dass ein Heruntersetzen der Taktung auf 100 bzw. 66 MHz jeweils zu deutlichen Verbesserungen der Situation führte. Im Zuge dieser Änderung wurde im BIOS auch gleich noch eine Einstellung für PCIe-I/O von Coalesce auf 256B vorgenommen. Dieser Tipp wurde in mehreren Foren im Netz bei ähnlich gelagerten Problemen gegeben, allerdings war in unserer Situation keine Verbesserung zu spüren. Die Probleme waren damit immer noch nicht vollständig abgestellt, wie Langzeittests bewiesen. Die Suche ging also weiter.

Interner Kartenbustakt

Bei genauerer Analyse der Situation wurde klar, dass auf den Netzwerkkarten selbst noch ein bisher unberücksichtigter Effekt auftrat. Hierzu muss man wissen, dass kein normales Server-Mainboard 17 PCI- oder PCIe-Slots aufweist, und man deshalb Karten mit zwei, drei oder vier Netzwerkschnittstellen verbauen muss, um auf die recht ungewöhnliche Zahl von Interfaces überhaupt erst zu kommen. Im Gegensatz zu einer Netzwerkkarte mit nur einer Buchse stellt aber eine Karte mit mehreren Buchsen nicht nur ein einfaches PCI-Gerät dar, sondern sie besitzt eine eigene, auf der Karte verbaute PCI-Bridge, die die Netzwerkchips der verschiedenen Interfaces verbindet und zum PCI-Bus des Systems führt. Diese PCI-Bridge und der ihr nachgelagerte Bus auf der Karte haben aber ebenfalls noch einmal eine Taktung, und auch diese war mit 133 MHz noch zu hoch eingestellt, um in unserem Setup stabil zu laufen. Normalerweise kann man diesen internen Takt der Karte per physikalischer Jumper-Einstellung ändern – nur nicht auf den an uns und unsere Kunden ausgelieferten Karten, denn da fehlten die Jumper schlicht und einfach. Wir haben uns daraufhin also mit dem Hersteller in Verbindung gesetzt und nach einer Beschreibung des Problems erwirkt, dass uns nachträglich Jumper aufgelötet wurden und in Zukunft nur noch Karten mit Jumpern geliefert werden. Setzt man noch den internen Kartentakt auf 66 MHz, so ist das Problem der sporadischen Freezes tatsächlich vollständig behoben. Das ist ein wichtiger Erfolg, leider nur insofern, als dass wir dadurch endlich das nächste, diesmal softwareseitig vorhandene Problem sehen konnten.

OACTIVE

Nun lief die Hardware also endlich stabil, da meldete der Kunde gleich das nächste Problem: Auf einmal verweigerten mitten im Betrieb einzelne Netzwerkschnittstellen die weitere Funktion. Dieser Fehler trat sporadisch auf, ließ sich auch nicht auf einzelne Interfaces festmachen (sonst wäre ein Hardwarefehler ja naheliegend gewesen) und war durch einen Reboot des Systems auch wieder aus der Welt zu schaffen. Leider trat der Fehler zunächst mehrmals pro Stunde auf (nach dem Absenken des PCI-Bustakts nur noch alle paar Stunden), und es ist vollkommen klar, dass eine Firewall nicht ständig rebootet werden kann, wenn man noch ernsthaft darüber arbeiten will. War der Bug mit den spontanen Freezes für den Kunden noch halbwegs verkraftbar, weil durch das Einfrieren einer Maschine sehr schnell eine HA-Übernahme initiiert wurde, so gehört der jetzt gefundene Bug eindeutig zu den subtileren Fehlern, die das Gesamtsystem nicht sofort beschädigen. Dadurch wird bei diesem neuen Fehler eben nicht sofort eine HA-Übernahme eingeleitet, und die Firewall steht sehr schnell in einem unbrauchbaren Zustand, weil einzelne Netzwerkschnittstellen nicht mehr funktionieren. Das einzige Symptom, das nach einiger Debugging-Arbeit gefunden werden konnte, ist, dass in der Ausgabe des Befehls ifconfig zu dem betroffenen Interface das OACTIVE-Flag als gesetzt angezeigt wurde:

em0: flags=8843<UP,BROADCAST,RUNNING,OACTIVE,
SIMPLEX,MULTICAST> mtu 1500
lladdr 00:30:48:66:55:e2
groups: egress
media: Ethernet autoselect
(1000baseT full-duplex,master)
status: active
inet 172.16.0.54 netmask 0xffff0000 broadcast
172.16.255.255
inet6 fe80::230:48ff:fe66:55e2%em0 prefixlen
64 scopeid 0x1

Diese Tatsache ist eigentlich nichts Besonderes, denn das OACTIVE-Flag ist in der Tat ein ganz normaler Bestandteil des Datentransfers auf der Netzwerkkarte: Immer, wenn der Netzwerkkartentreiber des Betriebssystems wieder ein Datenpaket zur Übertragung über das Netz an die Karte geschickt hat, wird das OACTIVE-Flag gesetzt, um zu signalisieren, dass die Karte momentan beschäftigt ist und keine weiteren Anforderungen bearbeiten wird. Ist der Datentransfer vorüber, meldet sich die Karte per Interrupt wieder beim Betriebssystem, das daraufhin das OACTIVE-Flag wieder löscht. Somit ist die Karte wieder bereit, Sendeanforderungen anzunehmen. Im Normalfall ist dieses Flag also nur für Bruchteile von Millisekunden überhaupt gesetzt und wird sofort wieder gelöscht. Beim Kunden dagegen ging kein Traffic mehr über die Karte und das Flag war dauerhaft gesetzt, was daraufhin auch jeden weiteren Datentransfer effektiv verhinderte.[ header = Workaround für den Kunden ]

Workaround für den Kunden

Damit der Kunde überhaupt mit der neuen Anlage arbeiten konnte, wurde ein kleiner Hack als Zwischenlösung installiert, der einen Weiterbetrieb überhaupt erst ermöglichte: Per Shell-Skript wurde periodisch der OACTIVE-Status der Systeminterfaces überprüft. Stellte sich heraus, dass ein Interface länger als zwei Sekunden im OACTIVE hängt, so konnte davon ausgegangen werden, dass der beschriebene Fehler vorlag. In diesem Fall wurde dann das Interface einfach mit den Befehlen ifconfig down und ifconfig up herunter- und wieder hochgefahren. Natürlich wird das Interface bei jeder solchen Aktion komplett neu initialisiert, was unter anderem etwas an Betriebssystemressourcen benötigt. Außerdem gehen beim OACTIVE-Stau und dem Interface-Reset natürlich Pakete verloren, die entweder von der Netzwerkschicht (bei Verwendung von Diensten auf TCP-Basis) oder der Anwendungsschicht (bei Verwendung von Diensten auf UDP-Basis) neu gesendet werden müssen. All das trägt nicht zu einer stabilen Verbindung durch die Firewall hindurch bei. Hinzu kam noch, dass ein System, sobald es einmal in die OACTIVE-Falle getappt war, dieses Problem statt nach mehreren Stunden bereits in Minutenabständen immer wieder zeigte und die einzig verlässliche Lösung für dieses Problem ein Reboot war. Nach dem Reboot war man entweder in derselben Situation wie vorher oder aber man hatte das Problem dadurch bis zu seinem nächsten Auftreten in einigen Stunden erst einmal komplett abgestellt. Deshalb war klar, dass dieses Skript nur der äußerste Notnagel sein konnte und eine echte Lösung sehr schnell gefunden werden musste.

Reproduzieren des Kundenproblems

Zunächst war es für unsere Techniker schwierig, überhaupt den Effekt, der beim Kunden auftrat, in unserem Testlabor erneut hervorzurufen. Denn das Erzeugen von Netzwerk-Traffic alleine, erzeugte zwar für kurze Zeit einen OACTIVE-Status, aber dies war ja (wie oben beschrieben) nichts Ungewöhnliches, sondern gehörte zur normalen Funktionsweise des Zusammenspiels von Betriebssystem und Netzwerkkarte. Kaum nahm man die Netzwerklast vom System wieder weg, erholte sich auch der OACTIVE-Zustand. So einfach konnte es also nicht funktionieren. Was machte der Kunde anders als wir? Wegen der bereits vorangegangenen Probleme mit Bustakt und Jumperung der Karten gingen wir auch bei OACTIVE zunächst davon aus, dass es sich hier wieder um ein Hardwareproblem handelt. Diese Annahme sollte sich bald als falsch herausstellen.

Zu schwache Netzteile?

Eine weitere Spur, die wir verfolgten, war die Frage, ob vielleicht die in der GeNUGate verwendeten Netzteile (zwei 500W-Module im redundanten Active-Active-Betrieb) für unsere Konstellation mit 17 Interfaces unter Extremlastsituationen zu schwach waren. Vermutet wurde hier ein so genannter Brownout, also kein echter Spannungsausfall, sondern nur eine Schwankung in einer der verschiedenen Versorgungsspannungen (Stromschienen) des Netzteils, die die Spannung für kurze Zeit außerhalb der tolerierbaren Grenzen führt.

Ein solches Problem war mit unseren Mitteln kaum zu analysieren – hier sprang glücklicherweise unser Hardwarelieferant Pyramid ein, der uns auch sonst beim Debugging der ganzen Bug-Story oft eng unterstützt hatte. Ein bei Pyramid bekanntes Hardwarelabor führte daraufhin Messungen der Stromschienen unter Lastsituationen durch und stellte uns die Ergebnisse zur Verfügung. Hierbei wurden nicht nur statische Szenarien gemessen, sondern auch die Veränderung der Last, sodass geprüft werden konnte, ob die Spannungsregler des Mainboards schnell genug auf einen veränderten Stromverbrauch reagieren und die Spannung wieder korrigieren. Zwar erwies sich auch dieser Ansatz (wieder einmal) als Sackgasse, aber immerhin hatten wir Gewissheit, dass es nicht an den Netzteilen lag: Bei allen Messungen wurde der Toleranzbereich der Versorgungsspannungen stets eingehalten.

Tests mit anderen Betriebssystemen

Wie immer beim Versuch, derartige Fehler so gut wie möglich einzugrenzen, wollten unsere Techniker jetzt den Effekt unter FreeBSD reproduzieren (hier wird OACTIVE auch sehr zugänglich angezeigt, anders als bei Windows). Interessanterweise traten die Probleme hier aber nicht auf, daher mussten wir unsere Vermutung korrigieren. Es war also möglich, dass der Fehler tief im Betriebssystemkern von OpenBSD steckte, aber wie konnten wir das Problem weiter isolieren?

Nächster Workaround: Absenken der MTU

Die Suche nach den Ursachen des Problems zog sich also hin. Glücklicherweise fanden unsere Experten beim Experimentieren mit der Teststellung einen viel besseren Workaround als das bereits erwähnte Shell-Skript. Jedes Interface besitzt eine so genannte MTU (Maximum Transmission Unit). Diese Zahl besagt die Menge an Bytes, die auf einmal in einem Paket auf dem Link Layer übertragen werden können. Diese maximale Übertragungseinheit ist vom verwendeten Medium abhängig und variiert zwischen 576 und 9000 Bytes. Die Untergrenze von 576 Bytes wird von einem Internetstandard (RFC 879) garantiert. Für das beim Kunden verwendete Gigabit Ethernet auf Kupferkabel handelte es sich um eine MTU von 1492 Bytes. Senkte man jedoch diese Größe künstlich auf 800 Bytes ab, so ließ sich das Auftreten von OACTIVE komplett verhindern. Das Problem war zwar jetzt, dass kleinere Paketgrößen verwendet wurden als tatsächlich möglich, aber immerhin war das System stabil benutzbar. Dieser zweite Workaround hat für uns glücklicherweise den Druck aus der Situation genommen, denn der Kunde konnte jetzt erst einmal arbeiten und unsere Kernel-Experten konnten sich angemessen mit einer Lösung des Problems befassen.

Erfolgreiches Reproduzieren des Kundenproblems

Endlich fand einer unserer Techniker einen Befehl, mit dem der OACTIVE-Bug in kürzester Zeit reproduziert werden konnte. Der Schönheit halber drucken wir ihn hier ab:

while [ 1 ]; do i=0; while [ $i -lt 40 ]; do 
(ping -fs $(($RANDOM%65467)) 172.31.$
(($RANDOM%17)).1 2>/dev/null 1>&2) & 
let i=$i+1; done; sleep 5; pkill ping; done

Kurz erklärt: In einer Endlosschleife werden im Abstand von fünf Sekunden jeweils 40 Flood-Pings gestartet und in den Hintergrund geschickt. Hierbei wird für jeden Flood-Ping eine andere Paketgröße und ein anderes Inferface gewählt, über das hinausgeschickt wird (bestimmt durch die Empfängeradressen, die jeweils in anderen Subnetzen liegen).[ header = Workaround um Hardware-Errata im Treiber ]

Workaround um Hardware-Errata im Treiber

Es war also klar, dass wir uns mit dem Zusammenspiel zwischen Netzwerkkartenhardware und Treiber im Betriebssystem auf niedriger Ebene intensiv auseinandersetzen mussten, um der Lösung des Problems näher zu kommen. Einer unserer Kernel-Entwickler nahm also das Datenblatt sowie die Errata des auf der Karte verwendeten Intel-Netzwerkchips zur Hand. Man muss hierzu wissen, dass den Hardwareherstellern genau wie den Softwareherstellern auch ab und zu Fehler unterlaufen, die es dann in ihr fertiges Produkt schaffen. Nach ihrer Entdeckung müssen diese selbstverständlich behoben werden. Im Fall der Software ist das ganz einfach – man gibt eine gepatchte Version der Software heraus. Hardware dagegen liegt nun einmal so vor, wie sie vorliegt, und mit wenigen Ausnahmen (Microcode-Updates bei Prozessoren, Firmware- oder BIOS-Upgrades) kann man an ihr nicht mehr viel verändern. Das Einzige, was einem hier noch bleibt, ist eine Treiberänderung, die um die eigentlichen Hardwarefehler geschickt herumarbeitet (daher auch der Begriff „Workaround“).

Die Treiberentwicklung war ein hoher Aufwand, da auf dieser Ebene sehr präzise gearbeitet werden muss. Auch befindet man sich auf Kernel-Ebene, was bei einem monolithischen Kernel wie dem Linux- oder OpenBSD-Kernel bedeutet, dass man ohne Speicherschutz oder Prozesskontext arbeitet. Macht man also Fehler, stürzt nicht etwa eine einzelne Anwendung ab, sondern gleich das ganze Betriebssystem. Ernüchternderweise führte auch das Einpflegen aller Errata des Intel-Chips zu keinerlei Änderung an den Kundenproblemen. Es wurde also viel Zeit in eine Lösung investiert, der Kunde hat keinen spürbaren Fortschritt erfahren und wir waren so weit wie zuvor. Natürlich konnte man das vorher nicht wissen. Also weiter zum nächsten Versuch.

Errata für Host Bridge, Pyramid

In derselben Weise, wie unsere Entwicklung die Errata der Netzwerkkarte in unseren Treiber eingepflegt hatte, wurden nun Errata für Komponenten des Mainboards (hier: die Host Bridge) eingepflegt, da diese auch unmittelbar am Datentransfer beteiligt sind. Die Gewissheit, jetzt einen gut funktionierenden Treiber zu haben, der nahe an der Hardware korrekt arbeitet, gab uns zwar ein gutes Gefühl, änderte aber leider nichts an der OACTIVE-Problematik. Wir verließen also diese Sackgasse wieder und setzten die Experimente im Labor fort.

Flow Control

Unsere Entwickler haben einmal als Schuss ins Blaue die Flusskontrolle (Flow Control) des Netzwerkkartentreibers auf Ethernet-Ebene im Kernel deaktiviert. Im praktischen Versuch stellte sich dann heraus, dass OACTIVE bedeutend später auftrat, allerdings nicht vollständig abgestellt war. Zunächst konnte man mit dieser Beobachtung noch nicht viel anfangen, aber es kam noch eine zweite hinzu: Eines Tages mussten die Testmaschinen umverkabelt werden. Dies geschah zufällig, als noch einer der vielen Testläufe auf dem Gerät aktiv war. Es konnte beobachtet werden, dass ein Ab- und wieder Anstecken des Testrechners vom Switch das OACTIVE-Problem reproduzierbar sofort auslöste, und zwar auch, wenn Flow Control deaktiviert war.

DMA-Treiber fixen

Die Tatsache, dass unsere Techniker das Problem sehr schnell und zuverlässig durch Abstecken des Kabels reproduzieren konnten, liess unsere Kernel-Entwickler hellhörig werden: Offenbar werden die Probleme dadurch verursacht, dass sich zu verschickende Netzwerkpakete vor der Netzwerkkarte im Treiber stauen. Dies veranlasste unsere Entwickler, die interne Verwaltung der Datenpakete im Kernel ganz genau unter die Lupe zu nehmen. Hierzu muss man wissen, dass Netzwerkpakete prinzipiell per DMA-Transfer vom Speicher auf die Netzwerkkarte gebracht werden.

Es gibt generell im BSD-Kern zwei Möglichkeiten, Datenpakete temporär zu speichern, die verschickt werden sollen. Der Standardweg zur Speicherung solcher Pakete, ist die Verwendung von so genannten Mbuf Clusters, die meistens eine Größe von 2 KB haben. Ist aber aus irgendeinem Grund gerade kein solcher Cluster verfügbar, so ist es im Kernel ein absolut normales Verhalten, zur Speicherung der Daten auf die Mbuf Chains zurückzugreifen. Die einzelnen Elemente einer solchen Chain sind jedoch nur um die 200 Byte groß, weshalb zur Speicherung eines durchschnittlichen Netzwerkpakets bei einer MTU von 1492 Bytes bis zu acht solcher Elemente benötigt werden. Die Elemente selbst müssen auch noch in Verwaltungsstrukturen eingetragen werden, was den Aufwand der Datenhaltung zusätzlich noch vergrößert. Es leuchtet also ein, dass zwar auf Mbuf Chains ausgewichen werden kann, dies aber mit mehr Ressourcenverbrauch verbunden ist. Egal ob Clusters oder Chain – die derart vorbereiteten Daten dürfen nicht irgendwo im Speicher liegen, sondern müssen auf einer x86-Architektur unbedingt mit 32 Bit adressierbar sein (also unter 4 GByte liegen), um im Rahmen eines DMA-Transfers zugreifbar zu sein. Das sollte eigentlich kein Problem sein, wird man jetzt denken. Die meisten Maschinen heutzutage erreichen 4-GB-Hauptspeicher nur knapp, wo ist also die Klippe?

Die Tücke ist, dass Betriebssysteme sehr wohl trotzdem auf Hardware vorbereitet sein müssen, die die 4-GB-Schallmauer bereits genommen hat. In diesem Fall ist für normal allokierten Speicher nicht garantierbar, ob dieser Speicher komplett unterhalb von 4 GByte liegt oder teilweise oder ganz darüber. Aus diesem Grund wurden für Betriebssysteme, die mehr als 4GB verwalten können (entweder mittels Physical Address Extension (PAE) im i386-Modus oder nativ im amd64-Modus) so genannte Bounce Buffers eingeführt, deren Zweck es ist, definiert unterhalb der Grenze zu liegen. Hinweis: Die Verwendung von Bounce Buffers ist selbstverständlich nicht die einzige Möglichkeit, mit dem 4GB-Problem umzugehen. Andere Hardwarearchitekturen wie sparc oder sparc64 verwenden dafür ein Konzept namens iommu, also eine Input/Output Memory Management Unit, die sich speziell um DMA-Transfers von und zur Peripherie kümmert. Diese übersetzt – vergleichbar mit einer normalen MMU für die Adressierung des Hauptspeichers im Protected Mode – Adressen vor ihrer Verwendung und erlaubt somit eine Virtualisierung von Speicherzugriffen. Leider ist das Konzept der IOMMU auf der PC-Architektur nie wirklich in den Betrieb gegangen und daher für die Lösung unseres Problems unbrauchbar. Es existieren zwar einzelne Mainboards, die eine IOMMU besitzen, aber diese funktioniert meist nicht fehlerfrei.

Zurück zu unseren Bounce Buffers. Diese können dann speziell für DMA-Transfers verwendet werden, sodass garantiert ist, dass ein solcher Transfer nie an der Adresse scheitern wird. Bei der genaueren Untersuchung des Codes fanden unsere Kernel-Entwickler eine Stelle, an der die Bounce Buffer ein bisschen zu klein kalkuliert wurden. Dies führte dazu, dass die Transfers meistens funktionierten – nämlich dann, wenn sie über die platzsparenden MBuf-Cluster abgewickelt wurden. Wurden die Pakete aber hauptsächlich in MBuf-Chains gepackt, dann reichte der Platz knapp nicht mehr aus und der Transfer brach ab. In einem solchen Fall wurden die Daten kommentarlos verworfen und der Fehlerzähler des betroffenen Interfaces wurde um eins hochgesetzt. Ein weiterer, sehr wesentlicher Effekt war, dass bei einem solchen Fehler, der eigentlich nur durch einen falschen Größenentwurf des Puffers hervorgerufen wurde, auch das OACTIVE-Flag des Treibers zurückgesetzt wurde. Wir hatten also einen wesentlichen Teil des Problems gefunden.

Unsere Kernel-Entwickler machten sich daran, die Größe der Puffer zu korrigieren. Im Laborversuch lief der Kernel dann ohne OACTIVE-Problematik mehrere Tage stabil. Den Größenwahn, hiermit alle Kundenprobleme für erledigt zu erklären, hatten wir schon lange nicht mehr – es ist vollkommen klar, dass ein Labortest niemals den echten Einsatz beim Kunden ersetzen kann – aber wir waren auf jeden Fall guter Dinge.

Referenzzähler im Treiber defekt

Beim Reparieren der Bounce Buffer im Kernel fiel unseren Kernel-Entwicklern außerdem auf, dass ein bestimmter Fehlerpfad im DMA-Treiber nicht korrekt beschritten wird: Beim Abbruch einer Transaktion müssen bestimmte interne Zähler wieder auf Null gesetzt werden – dies ist aber nie passiert. Man muss dazu sagen, dass derartige Fixes im Betriebssystem meist als Beifang größerer Änderungen die Normalität sind. Gerade Codepfade, die nicht so oft benötigt werden, sind auch keiner so strengen Prüfung durch häufigen Gebrauch unterzogen worden.

em-Treiber 64 DMA-Deskriptoren nach 32

Beim Debugging des Netzwerkkartentreibers für die Intel-Gigabit-Ethernet-Interfaces fiel unseren Kernel-Entwicklern auf, dass der em-Treiber im Gegensatz zu allen anderen Kartentreibern 64 statt 32 DMA-Deskriptoren verwendet. Weil mit der doppelten Zahl der DMA-Deskriptoren ein enormer Speicherverbrauch einhergeht, der einem kaum merklichen Gewinn an Leistung entgegensteht, wurde diese Zahl von unseren Entwicklern ebenfalls auf 32 angepasst. Allerdings hatte auch diese Änderung auf die sonst beobachteten Symptome wenig Einfluss.[ header = PAE stört ]

PAE stört

Die extreme Konfiguration von 17 Interfaces hat uns noch einmal nachrechnen lassen: Pro Netzwerkinterface wird beim Start des Betriebssystems Platz für Puffer reserviert, sodass hier schon viel Speicher verbraucht wird, der ohne die physikalische Adresserweiterung PAE gar nicht nötig wäre. Wir wollten einmal wissen, wieviel Speicher das genau ist.

Im Extremfall muss man davon ausgehen, dass alle Datenpakete auf einmal auch Platz haben müssen, also: Wir haben pro Netzwerkkarte eine Sende- und eine Empfangsrichtung. Pro Richtung gibt es 512 gleichzeitig mögliche Transaktionen (beschrieben durch Transaction Descriptors). Jede Transaktion kann aus bis zu 32 Einzelteilen (DMA-Segmenten) bestehen und jedes Einzelteil ist eine Speicherseite (4 KByte) groß. Vernachlässigt man den Verwaltungs-Overhead, der sonst noch so entsteht, kommt man auf einen reinen Speicherbedarf für die Daten von 5123241024 Bytes = 64 MB pro Netzwerkinterface. Bei 17 Interfaces kommen dann 1764 MB =1088 MB zusammen. Hat man also nicht gerade signifikant mehr Speicher als 4 GB, dann ist es eher ein Verlust, sein Betriebssystem mit PAE laufen zu lassen, als ein Gewinn. Aus diesem Grund wurde PAE für den Kernel der GeNUGate wieder deaktiviert, auch wenn diese paradoxe Situation manchen Kunden vielleicht nicht einfach vermittelbar sein dürfte, wenn diese ohne PAE von ihren vier Gigabyte physikalischem Speicher nur ca. 3,3 GB nutzen können.

Bei diesem Merkmal wurde wieder einmal klar, dass nicht alles, was in der Theorie vorteilhaft erscheint, in der Praxis auch brauchbar ist. Die physikalische Adresserweiterung wurde von uns zunächst selbst in der Entwicklung vorangetrieben, bis sich zeigte, dass in der echten Anwendung die Nachteile überwiegen.

Die Lösung

Seit 26. November 2008 läuft beim Kunden ein neuer Kernel, der die DMA-Transfers fixt, einen verbesserten Netzwerkkartentreiber besitzt, und neben anderen kleinen Patches PAE deaktiviert hat. Seitdem läuft das System stabil bei regulärer MTU.

Was ist passiert?

Anlass der Entdeckung des Bugs war die OACTIVE-Verklemmung im Zusammenhang mit Flow Control oder dem spontanen Abziehen von Netzwerkkabeln bei laufenden Datentransfers. Dieser Effekt konnte aber allenfalls als Symptom gelten, die wahren Ursachen fanden sich dann auch woanders. Auch ein Speicherüberlauf bei den unter PAE so wichtigen Bounce Buffers versetzte das System zuverlässig in den OACTIVE-Zustand. Nachdem das Vorgeplänkel mit dem Kartenbustakt als Nebenschauplatz erledigt war, lag die Lösung in letzter Konsequenz im Deaktivieren der PAE-Option. Durch die große Kette an Abhängigkeiten erledigte sich das Problem gewissermaßen von selbst: Ohne PAE gab es keine Bounce Buffer mehr und ohne diese konnte es auch keine Probleme mit der Verwaltung der Buffers geben. Verifizieren ließ sich die Lösung des Problems auch dadurch, dass es durch ein Abziehen des Netzwerkkabels oder durch die früher erwähnte Ping-Schleife keine Möglichkeit mehr gab, das System in einen permanenten OACTIVE-Zustand zu bringen.

Fazit

Moderne Computer sind Systeme von erheblicher Komplexität. Auch der bisher eher vernachlässigte Bereich der Peripherie gewinnt zunehmend an Autonomie und Eigendynamik. Dies rührt daher, dass moderne Bussysteme wie PCI um Längen intelligenter sind als beispielsweise so alte Busse wie ISA. Durch die gestiegene Komplexität in der Peripheriearchitektur, die Existenz von Bridges und eigenen untergeordneten Bussen steigt zwar die Gesamtleistung, das System ist aber gleichzeitig schwieriger zu überblicken und zu kontrollieren.

Obwohl wir stets versuchen, dass Hardware und Software, die unser Haus verlässt, gut getestet ist und bedenkenlos eingesetzt werden kann, gibt es trotzdem Spezialfälle, in denen Probleme erst beim echten Einsatz vor Ort auftreten – gerade bei weniger häufigen Konfigurationen. Sicher kann man im Labor den möglicherweise auftretenden Traffic durch eine Teststellung schicken und die vertraglich vereinbarte Anwendungslast im Reinraum sicherstellen. Dennoch kommt es aus Erfahrung doch immer anders, als sich Kunde und Hersteller die Sache vorgestellt haben. Ein nachgeschalteter iterativer Prozess zur Behebung der neu entdeckten Schwierigkeiten ist bei komplexeren Projekten eher die Regel als die Ausnahme. Dass eine solche Realität nicht immer marketingtechnisch einwandfrei vermittelbar ist, liegt auf der Hand. Es ist sicherlich angenehmer zu glauben, alle Herstellerfehler wären mit Standardisierungsmaßnahmen, Zertifizierungsprozessen und umfassenden Tests zu beheben. Bis zu einem gewissen Grad stimmt das zwar und sollte daher auch weiterhin energisch verfolgt werden, trotzdem muss klar sein, dass man sich hierbei lediglich dicht an eine 100-Prozent-Quote annähern kann – erreichen wird man sie nicht. Selbst für Hersteller, die in ihren Entwicklungsteams einiges an Expertenwissen versammelt haben, sind manche Effekte, die per Support-Anruf bei den Experten aufschlagen, schwer erklärbar. Trotzdem wird ihnen so gut wie möglich nachgegangen. Dass wir bei GeNUA in solchen Fällen nichts unversucht lassen, um Kundenproblemen auf die Spur zu kommen, hat dieser Artikel hoffentlich veranschaulichen können.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -