[Podcast] Kernel Panic - Board on Fire ~~> #003: Ein Rennen um Nanosekunden - Nebenläufigkeit mit Hardwareeinheiten
In dieser Folge erzählt Michael Grzeschik die Geschichte eines USB-Controllers, der dann und wann einfach aufhört zu funktionieren und davon wie er dem Fehler mit Tracing, einem Disassembler und viel Codelektüre auf die Schliche gekommen ist. Wir stellen einmal mehr fest, dass Probleme mit Nebenläufigkeit schwierig zu debuggen sind und nicht nur Software-Software Interaktion betreffen, sondern manchmal auch Software-Hardware.
Über den Kernel Panic - Board on Fire Podcast
In wahrscheinlich jedem Berufsfeld gibt es Schauergeschichten, die man sich abends am Lagerfeuer mit einer Taschenlampe am Kinn erzählen kann. So auch in der Welt der Software. In diesem Podcast geben wir in unregelmäßigen Abständen Entwicklerinnen und Entwicklern die Möglichkeit ihre Schauergeschichten zu erzählen. Es geht um monatelange Fehlersuchen, deren Ergebnis nur eine Hand voll Zeilen falscher Code sind, um subtil fehlerhafte Hardware, die zu sporadisch auftretenden Geistern im System führt, um bröckelnde Software, deren Quellcode schon vor Jahren verloren gegangen ist, und manchmal auch um ganz was anderes.
Shownotes
Wer nicht die ganze Folge hören möchte kann sich an den folgenden Zeitmarken orientieren:
- 00:00
- Vorstellung und einleitende warme Worte. Michael beschäftigt sich viel mit der Device-Seite von USB unter Linux. In letzter Zeit insbesondere mit dem Fall, dass ein Linux Gerät sich verhält wie eine USB-Webcam und Video an einen anderen Rechner überträgt. Das gibt ihm sowohl Berührungspunkte mit Kernelthemen, als auch mit Grafikthemen.
- 07:00
Einleitung der Lagerfeuergeschichte. Beim Bug-Quartett wäre sie mit drei Zeilen angepasstem Code bei mehreren Wochen Suchzeit eine gute Karte.
Ausgangspunkt ist ein Linux-Computer, der sich einem anderen Computer gegenüber als USB-Gerät ausgeben sollte. An diesem sollte er dann ein serielles Kommunikationsinterface bereitstellen, über das man z.B. eine Linux Commandline benutzen kann. Nach einigen Stunden Laufzeit blieb allerdings die gesamte Kommunikation stecken und die USB-Einheit im Gerät ließ sich nicht mehr ansprechen.
- 11:00
- Weil das Problem so schlecht reproduzierbar ist war Trial and Error kein guter Ansatz. Die Alternative ist erstmal scharfes Hinsehen im Treibercode, um das Verhalten mit dem Datenblatt zu vergleichen. Besonderes Augenmerk hat er dabei auf die Datenstrukturen gelegt, die zwischen dem Treiber und der Hardware ausgetauscht werden.
- 13:00
Zusammenfassung des bisher gehörten:
- Das Gerät um das es geht spielt die Rolle eines USB-Device, ein anderes Gerät (z.B. ein Desktoprechner) den USB Host.
- Die Übertragungskette ist also Userspace-Software -> Kernel-Treiber -> USB-Einheit -> Kabel -> USB-Einheit -> Kernel-Treiber -> Userspace-Software.
- Der Fehler liegt zwischen Kernel-Treiber und USB-Einheit auf Geräteseite. In den Treiber kann man hinein schauen, in die USB-Einheit nicht.
- 16:00
- Weiter mit der Bestandsaufnahme. Es handelt sich nicht um einen der typischen USB-Fehler, wie einen leer gelaufenen Datenpuffer, die werden nämlich schon behandelt. Es bleibt wirklich die ganze USB-Einheit hängen.
- 21:00
- Peripherieeinheiten wie USB oder Netzwerkcontroller werden von Chipherstellern oft eingekauft und in ihr eigenes Design integriert. So finden sich die selben Einheiten oft in ganz unterschiedlichen Chips. Bei USB waren in der Zeit besonders der dwc2 und der Chipidea Controller relevant.
- 30:00
- Ansatz um die fehlerhafte Stelle einzugrenzen: Einbau von Tracing in den Treiber, um Daten aufnehmen zu können im Moment in dem der Fehler tatsächlich passiert. Das Tracing ergibt, dass die Hardware nach Einhängen neuer Daten stehen bleibt.
- 39:00
Der selbe Treiber und die Peripherieeinheit werden auch auf anderen Systemen genutzt, warum ist es da nicht aufgefallen? Die CPU in der problematischen Hardware kann Daten nicht unaligned schreiben/ lesen. D.h. wenn z.B. ein 32 Bit Wert aus dem Speicher in ein Register gelesen werden soll geht das nur dann in einer einzigen Prozessor-Instruktion, wenn die Speicheradresse (in Bytes) durch vier teilbar ist (also durch 32 Bit). Ansonsten muss es durch vier einzelne byteweise Reads passieren. Auf anderen Platformen gibt es mitunter Befehle um auch unaligned in einer Instruktion lesen/schreiben zu können.
Im disassemblierten Code konnte man sehen, dass die Adresse der nächsten Datenstruktur als vier einzelne Bytes geschrieben wurde und nicht als ein 32 Bit Wert.
- 43:00
- In der Zeit in dem der neue Pointer Byte für Byte geschrieben wird kann die Hardware vorbei kommen und einen Wert auslesen, der nur teilweise die richtigen Daten enthält. Weil das Zeitfenster nur ein paar Nanosekunden lang ist, tritt der Fehler so selten auf.
- 50:00
- Warum hat der Compiler überhaupt angenommen, dass das Datenfeld nicht auf 32 Bit aligned war? Weil die Datenstruktur extra als packed markiert war. Mit der zusätzlichen Markierung aligned(4) wird das Feld wieder in einer Instruktion geschrieben.
- 58:00
- Mit dem Markieren als aligned(4) ist der Patch auch quasi schon fertig. Der Vollständigkeit halber fügt er auch noch ein wmb() (Write-Memory-Barrier) ein, um sicherzustellen, dass der Compiler das Schreiben der Daten in die Datenstruktur nicht hinter das Einhängen der Datenstruktur verschiebt.
- 60:00
- Zusammenfassung und Abschluss
Further Readings
[Podcast] Kernel Panic - Board on Fire ~~> #009: Ho Ho Ho VirtIO - QEMU, KVM, Barebox und wie sie zusammengefunden haben
Wir haben lange gewartet, um endlich wieder eine Weihnachtssonderfolge herausbringen zu können. Für diese Folge hat uns Ahmad mal wieder ein spannendes Thema mitgebracht, oder viel mehr einen Themenkomplex. Er erzählt uns nämlich wie sich, als Barebox in die oe-core Layer des Yocto Projekts gebracht wurde, die Gelegenheit ergeben hat spannendeende Dinge über Emulation und Virtualisierung mit QEMU und KVM und Paravirtualisierung mit VirtIO zu lernen.
[Podcast] Kernel Panic - Board on Fire ~~> #008: Aus dem Takt - Das Doppelpack Clock-Glitches
In dieser Folge reden Ahmad und Leonard über Takte / Clocks in Prozessoren. Darüber warum es so viele gibt, wie sie erzeugt und im Chip verteilt werden und darüber, was dabei eigentlich so schief gehen kann.
[Podcast] Kernel Panic - Board on Fire ~~> #007: GPU und nu? Der Weg zum offenen Grafiktreiber
In dieser Folge erzählt Lucas Stach uns wie er in die Entwicklung der offenen Grafiktreiber Nouveau und Etnaviv hineingeraten ist und was so ein Grafiktreiber eigentlich tut. Wir reden darüber warum Grafikkarten überhaupt Software ausführen und wie diese Software von der Anwendung bis in die Grafikkarte kommt.