De Mars Pathfinder Priority Inversion Bug: Wat er echt gebeurde

Belangrijkste punten

  • De Mars Pathfinder-missie kampte met herhaaldelijke systeemresets veroorzaakt door een priority inversion bug.
  • Een taak met lage prioriteit hield een gedeelde mutex vast terwijl een taak met hoge prioriteit wachtte, wat leidde tot een watchdog timeout.
  • De oplossing was het inschakelen van priority inheritance in de mutex-configuratie van het VxWorks RTOS.
  • Deze praktijkcasus laat zien waarom inzicht in RTOS-scheduling en priority inversion cruciaal is.

De Mars Pathfinder werd op 4 december 1996 gelanceerd door NASA aan boord van een Delta II-booster en landde op 4 juli 1997 in Ares Vallis op Mars, in een regio genaamd Chryse Planitia in het Oxia Palus-kwadrant. De lander opende zich vervolgens en liet de rover vrij, die diverse experimenten op het Martianse oppervlak uitvoerde.

De Mars Pathfinder landde tot veel media-aandacht en begon gegevens naar de aarde te verzenden. Enkele dagen later werd de stroom van informatie en beelden echter onderbroken door een reeks totale systeemresets. De bron van het probleem was priority inversion, wat vervolgens leidde tot het missen van een deadline van een kritieke taak. Dit werd gedetecteerd door een watchdog timer, waarna de actie in dit foutscenario was om het ruimtevaartuig te resetten.

Hoe dit probleem is gediagnosticeerd en opgelost, is een fascinerend verhaal en een belangrijke les voor embedded engineers.

Het diagnosticeren van het probleem

De applicaties van de Pathfinder werden gescheduld door het VxWorks RTOS. Omdat VxWorks pre-emptive priority scheduling van threads biedt, werden taken uitgevoerd als threads met prioriteiten die waren bepaald op basis van hun relatieve urgentie.

De taak voor het verzamelen van meteorologische gegevens draaide als een infrequent thread met lage prioriteit en maakte gebruik van de informatiebus die gesynchroniseerd was met semaforen. Andere threads met een hogere prioriteit kregen voorrang wanneer dat nodig was, waaronder een busmanagementtaak met een zeer hoge prioriteit, die ook toegang tot de bus kreeg door de semafoor te verwerven. Helaas werd in dit geval de uitvoering belemmerd door een langlopende communicatietaak, die een hogere prioriteit had dan de meteorologische taak, maar een lagere dan de busmanagementtaak.

Al snel merkte een watchdog timer op dat de busmanagementtaak al enige tijd niet was uitgevoerd. De timer concludeerde dat er iets mis was en beval een totale systeemreset. (Engineers bekenden later dat er tijdens de pre-flight tests ook systeemresets hadden plaatsgevonden. Zij schreven dit toe aan een hardwarefout en richtten zich vervolgens weer op de missiekritieke landingssoftware.)

Het vinden van een oplossing

Engineers werkten koortsachtig op een laboratoriumreplica om het probleem te diagnosticeren en op te lossen, waarbij ze uiteindelijk een priority inversion ontdekten. Een priority inversion treedt op wanneer een taak met hoge prioriteit indirect wordt gepreempt door een taak met gemiddelde prioriteit, waardoor de relatieve prioriteiten van de twee taken worden "omgekeerd" (zie Figuur 1). Dit is een duidelijke schending van het prioriteitsmodel, dat stelt dat taken met hoge prioriteit alleen kunnen worden tegengehouden door taken met een nog hogere prioriteit, of kortstondig door taken met lage prioriteit die snel hun gebruik van een resource beëindigen die door zowel de hoge als de lage prioriteitstaak wordt gedeeld.

Figuur 1: Priority inversion

Om het probleem op te lossen, schakelden ze een booleaanse parameter of vlag in. Deze vlag geeft aan of priority inheritance voor de taak met lage prioriteit moet worden uitgevoerd door de semafoor. De betreffende semafoor was geïnitialiseerd met deze parameter uitgeschakeld (dit was de standaardinstelling); als deze ingeschakeld was geweest, zou de priority inversion zijn voorkomen.

Bij priority inheritance erft de taak die de semafoor vasthoudt de prioriteit van een taak met hogere prioriteit zodra die laatste de semafoor aanvraagt. In Figuur 1 zou taak "low" de prioriteit van taak "high" erven wanneer die taak de semafoor aanvraagde. Hierdoor kan "low" de taak "medium" preempten.

Een globale variabele sloeg de initialisatieparameter op voor de semafoor die het probleem veroorzaakte. Omdat VxWorks een C-taalinterpreter bevat—bedoeld om ontwikkelaars in staat te stellen C-expressies en functies in te typen voor uitvoering tijdens het debuggen van het systeem—was het mogelijk om een kort C-programma naar het ruimtevaartuig te uploaden. Wanneer dit werd geïnterpreteerd, werden de waarden van deze variabelen gewijzigd van FALSE naar TRUE. Dit maakte een einde aan de systeemresets.

Wat hebben we hiervan geleerd?

  • Alleen gedetailleerde traces van het werkelijke systeemgedrag maakten het mogelijk om de foutieve uitvoeringsvolgorde vast te leggen en te identificeren. Een diagnose via een "black box" zonder traces zou onmogelijk zijn geweest.
  • De aanwezigheid van "debugging"-faciliteiten in het systeem was uiterst belangrijk. Het probleem had niet kunnen worden gecorrigeerd zonder de mogelijkheid om het systeem aan te passen.
  • Het besteden van extra tijd om de correctheid van priority inheritance in de testfase te garanderen, zelfs tegen wat extra prestatiekosten, zou onschatbaar zijn geweest.

Het probleem was al vóór dit incident geïdentificeerd

Toen de keynote speaker verwees naar een paper waarin het priority inversion-probleem voor het eerst werd geïdentificeerd en een oplossing werd voorgesteld, gebeurde er iets bijzonders: verbazingwekkend genoeg waren de auteurs allemaal in de zaal aanwezig en werden zij hartelijk ontvangen. Het oorspronkelijke paper was:

L. Sha, R. Rajkumar, and J. P. Lehoczky. Priority Inheritance Protocols: An Approach to Real-Time Synchronization. In IEEE Transactions on Computers, vol. 39, pp. 1175-1185, Sep. 1990.