So da es ja Stimmen gibt die mein VMM Design als kaputt oder fehlerhaft bezeichnen
wollen wir dem mal auf den Grund gehen.
Im Endeffekt geht es ja "nur" darum wie und in welcher Reihenfolge ich locke.
Ich werde das Design erstmal nur grob umreißen, da der Code inzwischen fast 2000 Zeilen groß ist (ohne Kommentare).
Wichtigste Datenstruktur ist ein "struct vmmAddrSpace_t". Darin sind 2 AVL-Bäume mit freien virtuellen Bereichen, einmal nach Adresse sortiert und einmal nach der Größe, ansonsten zeigen die Nodes in beiden Bäumen auf die selbe Datenstruktur.
Dann sind noch 2 Locks mit dabei, einmal um die "Zonen" (also im Endeffekt die beiden Bäume) zu schützen und einmal um die PagingTables des Prozesses zu schützen.
Interessant ist noch, das der KernelSpace seinen eigenen AdressSpace hat.
An Funktionen wird nach außen hin folgendes bereitgestellt:
- vmmAllocMemory(uint32t pages, uint32t flags)
- vmmDeallocMemory(void *base, uint32t pages)
- vmmAllocRange(uint32t pages)
- vmmDeallocRange(void *base, uint32t pages)
- vmmMap(void *virt, void *phys, uint32t count, uint32t flags)
- vmmUnmap(void *virt, uint32t count, uint32t flags
vmmAllocMemory macht nichts anderes als eine Zone/Range zu allokieren (im Endeffekt könnte man sagen das vmmAllocRange aufgerufen wird) und in diese Zone dann Pages zu mappen (Aufruf von vmmMap).
Locks werden nicht direkt genutzt, sondern nur durch das Aufrufen von anderen Funktionen (die noch kommen). Einzige Ausnahme ist wenn ein Fehler auftritt und die bisherige Aktion rückgängig gemacht werden muss, aber da ist mir aufgefallen, dass das auch durch einen Aufruf von vmmDeallocMemory passieren kann.
vmmDeallocMemory unmappt erstmal alle Pages (Aufruf von vmmUnmap), holt sich dann den PageTableLock um die eventuell vorhandenen Pages freizugeben. Danach wird der Lock wieder freigegeben.
Zum Abschluss wird dann die Zone freigegeben (Aufruf von vmmDeallocRange).
vmmAllocRange holt sich den Lock für die Bäume. Dann wird halt nach einer Range gesucht und wird eine gefunden, wird die Node unter Umständen (Node entspricht der benötigten Range) aus dem Baum gelöscht und der Speicher der Node wird freigegeben.
Genau hier lag ein Problem meines Designs, wenn ich nämlich den SlabAllocator aufrufe, während ich den Lock für den KernelSpace habe (was ja passieren kann, wenn eine Range im KernelSpace allokiert werden soll), kann es passieren das es zu einem Deadlock kommt. Denn der SlabAllocator wiederrum könnte ja Speicherfreigeben wollen, aber da der Lock für die Bäume schon belegt ist, kann das nicht funktionieren -> DeadLock.
vmmDeallocRange holt sich den Lock für die Bäume. Dann wird geguckt ob die freizugebende Range mit einer oder zwei vorhanden Ranges zusammengefasst werden kann (dies kann wieder zu mehreren NodeAlloc´s und NodeDealloc´s führen).
Hier war mal genau das selbe Problem wie bei vmmAllocRange, nämlich das der VMM Speicher vom VMM braucht um seine Aufgabe zu erfüllen (klingt lustig war es aber nicht
).
vmmMap holt sich den Lock für die PagingTables und versucht halt die Pages zu mappen, das gleiche macht vmmUnmap, nur das es die Pages unmappt.
Beide rufen noch eine Funktion auf, die sich darum kümmert wie oft jede physikalische Page gemappt ist. Hierfür wird ein Lock pro Page verwendet und dort kann kein Deadlock auftreten, da kein anderes Lock benötigt wird (es wird das Lock geholt, nen Counter inkrementiert/dekrementiert) und das wars schon.
So viele Locks sind es gar nicht, die meinen VMM ausmachen (genau 2 bzw. 3).
Problem war halt mal, das ich für das Allokieren der VMM Datenstrukturen meinen allgemeinen SlabAllocator benutzt habe und der wiederrum braucht den VMM um arbeiten zu können was ja nicht funktionieren kann.
Gelöst habe ich es dadurch, das ich mir extra das Prinzip des SlabAllocators zu nutze gemacht habe und in einem festgelegten Bereich (steht schon zur Compilezeit fest) nur noch Pages gemappt oder geunmappt werden müssen.
Diese Funktion (vmmSlabAllocNode/vmmSlabAllocZone usw.) holt sich nen Lock um ein Element aus dem Cache (wo viele oder auch gar kein Objekt drin ist) holen zu können.
Ist der Cache leer, muss eine neue Page gemappt werden (Aufruf von vmmMap), dafür wird kein neuer Lock benötigt, da eh immer nur einer auf diesen Cache zugreifen kann.
So ich weiß ganz schön viel, aber jetzt dürft ihr mein Konzept niedermachen und könnt Kritik direkt am Konzept (oder zur Not am Code) bringen!