<> <> <> <> <> DIRECTORY BootStartList USING [EntryPointer, Enumerate, IndexToEntryPointer, IndexToSpaceEntryPointer, Proc], DebuggerSwap USING [CallDebugger, WorryCallDebugger], GermSwap USING [switches], MPCodes USING [vmInitialized], PrincOps USING [AV, AVItem, FaultIndex, flagsVacant, FrameHandle, LargeReturnSlot, NoTimeout, PageValue, PDA, PsbHandle, PsbNull, qFrameFault, qPageFault, qWriteProtectFault, Queue, QueueEmpty, StateVector], PrincOpsUtils USING [DisableInterrupts, EnableInterrupts, FrameSize, Free, GetReturnFrame, LongEnter, LongExit, LongReEnter, LongWait, LowHalf, MyLocalFrame, PsbIndexToHandle, Requeue, SetReturnFrame], Process USING [Detach, DisableTimeout, priorityFaultHandlers, priorityForeground, SecondsToTicks, SetPriority, SetTimeout, Ticks, Yield], ProcessorFace USING [SetMP], VM USING [AddressFault, AddressForPageNumber, CantDoIO, DataState, Free, IOErrorType, Interval, PageCount, PageNumber, PageNumberForAddress, PagesForWords, PageState, SimpleAllocate, State, SwapIn, WordsForPages, WriteProtectFault], VMEmergency, VMInternal USING [AllocateRealMemoryForLocalFrames, AllocateVirtualMemoryInternal, allocationLock, AllocationOutcome, AnyFrameSizeIndex, FrameHeapPiece, FrameHeapPieceHeader, FsiFrame, GetPageValue, GetVMMap, InOut, LargeFrame, LargeFrameHeader, largeFrameThresholdFsi, lastVMPage, NormalFrameSizeIndex, PageStateFromFlags], VMSideDoor USING [rmPages], VMStatistics USING []; VMFaultsImpl: MONITOR LOCKS monitor^ USING monitor: POINTER TO MONITORLOCK IMPORTS BootStartList, DebuggerSwap, GermSwap, PrincOpsUtils, Process, ProcessorFace, VM, VMEmergency, VMInternal, VMSideDoor EXPORTS VMInternal, VMStatistics = BEGIN OPEN PrincOps; <<>> <> largeFramesLock: MONITORLOCK; largeFramesInUse: CONDITION; largeFrameTimeout: Process.Ticks = Process.SecondsToTicks[1]; <<>> <= ReadWritePageInSize. The values of these entries can be changed during initialization.>> <<>> ReadOnlyPageInSize: INT _ 4 ; ReadWritePageInSize: INT _ 2 ; <<>> <> pageFaults, writeFaults: PUBLIC INT _ 0; frameFaults: PUBLIC INT _ 0; smallFramesAllocated, frameHeapExtensions: PUBLIC INT _ 0; largeFramesAllocated, largeFramesFreed: PUBLIC INT _ 0; <<>> <> frameHeapPieceList: PUBLIC VMInternal.FrameHeapPiece _ NIL; largeFrameList: PUBLIC VMInternal.LargeFrame _ NIL; -- protected by largeFramesLock <<>> <> <> debugInfo: LONG POINTER TO DebugInfo _ NIL; DebugInfo: TYPE = RECORD [ lastFaultedPage: VM.PageNumber _ 0, lastAddressFaultPage: VM.PageNumber _ 0, addressFaultCounter: INT _ 0, lastPageFaultIndex: [0..FaultsInRing) _ FaultsInRing-1, retries: INT _ 0, frameAllocs: ARRAY VMInternal.NormalFrameSizeIndex OF INT _ ALL[0], faultRing: ARRAY [0..FaultsInRing) OF PageFaultInfo _ ALL[PageFaultInfo[-1, NIL, GetStateHelper[0]]] ]; FaultsInRing: NAT = 16; PageFaultInfo: TYPE = RECORD [ page: VM.PageNumber, addr: LONG POINTER, state: PrincOps.PageValue]; PageFaultProcess: PROC = { <> Process.SetPriority[Process.priorityFaultHandlers]; DO process: PsbHandle = WaitForFaultee[qPageFault]; faultAddress: LONG POINTER = PDA[PDA[process].context.state].memPointer; faultedPage: VM.PageNumber = VM.PageNumberForAddress[faultAddress]; IF debugInfo # NIL THEN debugInfo.lastFaultedPage _ faultedPage; IF faultedPage <= 0 OR faultedPage > VMInternal.lastVMPage THEN ReportFault[process, VM.AddressFault, faultedPage, faultAddress] ELSE { interval: VM.Interval _ MapAddressToContainingInterval[faultedPage]; ok: BOOL _ TRUE; IF debugInfo # NIL THEN { lpx: [0..FaultsInRing) _ debugInfo.lastPageFaultIndex _ (debugInfo.lastPageFaultIndex+1) MOD FaultsInRing; debugInfo.faultRing[lpx] _ [faultedPage, faultAddress, GetStateHelper[faultedPage]]; }; VM.SwapIn[interval: interval, nextPage: GetNextPageHint[process] ! VM.AddressFault => {ok _ FALSE; CONTINUE}; VM.CantDoIO => { ReportCantDoIO[process, reason, page]; CONTINUE}; ]; IF NOT ok THEN { <> IF debugInfo # NIL THEN debugInfo.retries _ debugInfo.retries + 1; VM.SwapIn[interval: [faultedPage, 1], nextPage: 0 ! VM.AddressFault => { ReportFault[process, VM.AddressFault, faultedPage, faultAddress]; CONTINUE; }; VM.CantDoIO => { ReportCantDoIO[process, reason, page]; CONTINUE}; ]; }; }; RestartFaultee[qPageFault, process]; pageFaults _ pageFaults.SUCC; ENDLOOP; }; WriteProtectFaultProcess: PROC = { <> Process.SetPriority[Process.priorityFaultHandlers]; DO process: PsbHandle = WaitForFaultee[qWriteProtectFault]; ReportFault[process, VM.WriteProtectFault]; RestartFaultee[qWriteProtectFault, process]; --*stats*-- writeFaults _ writeFaults.SUCC; ENDLOOP; }; FrameFaultProcess: PROC = { <> OPEN VMInternal; Process.SetPriority[Process.priorityFaultHandlers]; DO process: PsbHandle = WaitForFaultee[qFrameFault]; fsi: NormalFrameSizeIndex = PDA[PDA[process].context.state].fsi; frSize: CARDINAL = PrincOpsUtils.FrameSize[fsi] + AnyFrameSizeIndex.SIZE; newFrame: FrameHandle; -- the frame we will allocate and paste into the AV. IF fsi >= largeFrameThresholdFsi THEN { <> pages: CARDINAL = VM.PagesForWords[frSize + LargeFrameHeader.SIZE]; largeFrame: LargeFrame = PageNumberToMDSAddress[AllocateForLocalFrames[pages].page]; largeFrame.next _ largeFrame.prev _ NIL; largeFrame.trueFsi _ fsi; NotifyLargeFrame[largeFrame]; largeFrame.fsiFrame[0].fsi _ LargeReturnSlot; -- (a fsi never generated by the compiler.) newFrame _ @largeFrame.fsiFrame[0].frame; --*stats*-- largeFramesAllocated _ largeFramesAllocated.SUCC; } ELSE { <> <> piece: FrameHeapPiece _ frameHeapPieceList; newFsiFrame: POINTER TO FsiFrame; -- assert: newFsiFrame MOD 4 = 3 IF piece ~= NIL AND piece.wordsInUse + frSize > piece.wordsAllocated THEN { <> framePiecePages: VM.PageCount = VM.PagesForWords[frSize + FrameHeapPieceHeader.SIZE]; framePieceWords: CARDINAL = VM.WordsForPages[framePiecePages]; minFrameSize: CARDINAL = PrincOpsUtils.FrameSize[0] + AnyFrameSizeIndex.SIZE; wordsAvailable: CARDINAL; trialFsi: NormalFrameSizeIndex _ fsi; newFsiFrame _ @piece.fsiFrame[0] + piece.wordsInUse - FrameHeapPieceHeader.SIZE; UNTIL (wordsAvailable _ piece.wordsAllocated - piece.wordsInUse) < minFrameSize DO tFsiWords: CARDINAL = PrincOpsUtils.FrameSize[trialFsi _ trialFsi.PRED] + AnyFrameSizeIndex.SIZE; UNTIL wordsAvailable < tFsiWords DO newFsiFrame.fsi _ trialFsi; PrincOpsUtils.Free[@newFsiFrame.frame]; -- atomically paste onto frame heap. newFsiFrame _ newFsiFrame + tFsiWords; piece.wordsInUse _ piece.wordsInUse + tFsiWords; wordsAvailable _ wordsAvailable - tFsiWords; ENDLOOP; ENDLOOP; <> piece _ PageNumberToMDSAddress[AllocateForLocalFrames[framePiecePages].page]; piece.next _ frameHeapPieceList; piece.wordsAllocated _ framePieceWords; piece.wordsInUse _ FrameHeapPieceHeader.SIZE; frameHeapPieceList _ piece; --*stats*-- frameHeapExtensions _ frameHeapExtensions.SUCC; }; <> newFsiFrame _ @piece.fsiFrame[0] + piece.wordsInUse - FrameHeapPieceHeader.SIZE; newFsiFrame.fsi _ fsi; newFrame _ @newFsiFrame.frame; piece.wordsInUse _ piece.wordsInUse + frSize; --*stats*-- smallFramesAllocated _ smallFramesAllocated.SUCC; }; <> <> <> PrincOpsUtils.DisableInterrupts[]; LOOPHOLE[newFrame, POINTER TO AVItem]^.link _ AV[fsi].link; AV[fsi].frame _ newFrame; IF debugInfo # NIL THEN debugInfo.frameAllocs[fsi] _ debugInfo.frameAllocs[fsi].SUCC; PrincOpsUtils.EnableInterrupts[]; <> RestartFaultee[qFrameFault, process]; --*stats*-- frameFaults _ frameFaults.SUCC; ENDLOOP; }; <<>> FlushLargeFramesProcess: PROC = { <> Process.SetPriority[Process.priorityFaultHandlers]; DO largeFrame: VMInternal.LargeFrame = WaitForLargeFrame[]; VM.Free[ [page: VM.PageNumberForAddress[largeFrame], count: VM.PagesForWords[PrincOpsUtils.FrameSize[largeFrame.trueFsi]]]]; --*stats*-- largeFramesFreed _ largeFramesFreed.SUCC; ENDLOOP; }; <> EmergencyRefillProcess: PROC = { <> WaitForEmergency: ENTRY PROC [monitor: POINTER TO MONITORLOCK] = INLINE { WHILE lagChange = eList.change DO WAIT emergencyCond; ENDLOOP; lagChange _ eList.change; }; interval: VM.Interval = VM.SimpleAllocate[count: 1]; lagChange: INT _ -1; eList: VMEmergency.EmergencyList = VM.AddressForPageNumber[interval.page]; eList.change _ 0; eList.max _ 16; eList.pages _ ALL[0]; Process.SetPriority[Process.priorityForeground]; VM.SwapIn[interval: interval, pin: TRUE]; VMEmergency.emergencyList _ eList; -- install the emergency list { <> debugPages: VM.Interval = VM.SimpleAllocate[VM.PagesForWords[SIZE[DebugInfo]]]; temp: LONG POINTER TO DebugInfo _ VM.AddressForPageNumber[debugPages.page]; temp^ _ []; VM.SwapIn[interval: debugPages, pin: TRUE]; debugInfo _ temp; }; DO WaitForEmergency[@emergencyLock]; lagChange _ eList.change; FOR i: NAT IN [0..eList.max) DO page: VM.PageNumber _ eList.pages[i]; IF page = 0 THEN { <> page _ VM.SimpleAllocate[count: 1].page; }; IF VMInternal.InOut[VMInternal.GetVMMap[page]] = out THEN { <> eList.pages[i] _ 0; VM.SwapIn[interval: [page, 1], pin: TRUE]; eList.pages[i] _ page; emergencyCount _ emergencyCount + 1; }; ENDLOOP; ENDLOOP; }; NoteEmergency: ENTRY PROC [monitor: POINTER TO MONITORLOCK] = INLINE { BROADCAST emergencyCond; }; emergencyCond: CONDITION; emergencyLock: MONITORLOCK; emergencyCount: INT _ 0; <<>> <> MapAddressToContainingInterval: PROC [faultedPage: VM.PageNumber] RETURNS [VM.Interval] = { firstPage: VM.PageNumber _ faultedPage ; lastPage: VM.PageNumber _ firstPage ; pageState: VM.PageState _ VM.State[firstPage]; dataState: VM.DataState _ pageState.dataState; pageCount: INT _ 1 ; maxSize: INT = MAX[ReadOnlyPageInSize, ReadWritePageInSize]; IF firstPage > VMInternal.lastVMPage THEN RETURN [[page: firstPage, count: 0]]; IF pageState.hasRealMemory OR dataState = none OR dataState = undefined THEN <> RETURN [ [firstPage, 1] ]; WHILE pageCount < maxSize AND lastPage < VMInternal.lastVMPage DO page: VM.PageNumber _ lastPage.SUCC; pageState _ VM.State[page]; dataState _ pageState.dataState; IF pageState.hasRealMemory OR dataState = none OR dataState = undefined OR (NOT pageState.readOnly AND (pageCount >= ReadWritePageInSize)) THEN EXIT ; lastPage _ page ; pageCount _ pageCount.SUCC; ENDLOOP; WHILE pageCount < maxSize AND firstPage > 0 DO page: VM.PageNumber _ firstPage.PRED; pageState _ VM.State[page]; dataState _ pageState.dataState; IF pageState.hasRealMemory OR dataState = none OR dataState = undefined OR (NOT pageState.readOnly AND (pageCount >= ReadWritePageInSize)) THEN EXIT ; firstPage _ page ; pageCount _ pageCount.SUCC; ENDLOOP; RETURN[[page: firstPage, count: pageCount]] }; GetNextPageHint: PROC [process: PsbHandle] RETURNS [VM.PageNumber] = INLINE { <> nextProcess: PsbHandle = PrincOpsUtils.PsbIndexToHandle[PDA[process].link.next]; RETURN[ IF nextProcess = process THEN 0 ELSE VM.PageNumberForAddress[PDA[PDA[nextProcess].context.state].memPointer]] }; GetStateHelper: PROC [page: VM.PageNumber] RETURNS [PrincOps.PageValue] = { <> IF page <= 0 OR page > VMInternal.lastVMPage THEN RETURN [[ state: VMInternal.PageStateFromFlags[PrincOps.flagsVacant], real: 0 ]]; RETURN [VMInternal.GetPageValue[page]]; }; ReportFault: PROC [psb: PsbHandle, error: ERROR [LONG POINTER], faultedPage: VM.PageNumber _ 0, faultAddress: LONG POINTER _ NIL] = { Return: PROC _ LOOPHOLE[PrincOpsUtils.GetReturnFrame[]]; state: StateVector = PDA[PDA[psb].context.state]; <> address: LONG POINTER = state.memPointer; <> pageValue: PrincOps.PageValue _ GetStateHelper[faultedPage]; <> IF error = VM.AddressFault AND debugInfo # NIL THEN { <> debugInfo.lastAddressFaultPage _ faultedPage; debugInfo.addressFaultCounter _ debugInfo.addressFaultCounter + 1; }; <> PDA[PDA[psb].context.state].stkptr _ PDA[PDA[psb].context.state].instbyte _ 0; PrincOpsUtils.SetReturnFrame[state.frame]; PDA[PDA[psb].context.state].frame _ LOOPHOLE[PrincOpsUtils.MyLocalFrame[]]; <> Return[]; <> <> <> <> <> IF PDA[psb].flags.cleanup ~= PsbNull THEN DebuggerSwap.WorryCallDebugger["Address fault with dirty cleanup link (see a wizard)"L]; ERROR error[state.memPointer]; }; ReportCantDoIO: PROC [psb: PsbHandle, reason: VM.IOErrorType, page: VM.PageNumber] = { Return: PROC _ LOOPHOLE[PrincOpsUtils.GetReturnFrame[]]; state: StateVector = PDA[PDA[psb].context.state]; address: LONG POINTER = state.memPointer; -- for easy inspection from the debugger. <> PDA[PDA[psb].context.state].stkptr _ PDA[PDA[psb].context.state].instbyte _ 0; PrincOpsUtils.SetReturnFrame[state.frame]; PDA[PDA[psb].context.state].frame _ LOOPHOLE[PrincOpsUtils.MyLocalFrame[]]; <> Return[]; <> <> <> <> <> IF PDA[psb].flags.cleanup ~= PsbNull THEN DebuggerSwap.WorryCallDebugger["Address fault with dirty cleanup link (see a wizard)"L]; ERROR VM.CantDoIO[reason, page]; }; WaitForFaultee: PROC [q: FaultIndex] RETURNS [psb: PsbHandle] = INLINE { <> lock: MONITORLOCK; pCondition: LONG POINTER TO CONDITION = LOOPHOLE[@PDA.fault[q].condition]; --Temp kludge to allow Laundry to get in: -- Process.Yield[]; -- (ADB) UNTIL PrincOpsUtils.LongEnter[@lock] DO ENDLOOP; WHILE PDA.fault[q].queue = QueueEmpty DO PrincOpsUtils.LongWait[@lock, pCondition, NoTimeout]; UNTIL PrincOpsUtils.LongReEnter[@lock, pCondition] DO ENDLOOP; ENDLOOP; psb _ PrincOpsUtils.PsbIndexToHandle[PDA.block[PDA.fault[q].queue.tail].link.next]; PrincOpsUtils.LongExit[@lock]; }; <<>> RestartFaultee: PROC [q: FaultIndex, psb: PsbHandle] = INLINE { PrincOpsUtils.Requeue[@PDA.fault[q].queue, @PDA.ready, psb]; }; <<>> WaitForLargeFrame: PROC RETURNS [largeFrame: VMInternal.LargeFrame] = INLINE { WaitForLargeFrameEntry: ENTRY PROC [monitor: POINTER TO MONITORLOCK] = INLINE { <> OPEN VMInternal; DO IF AV[LargeReturnSlot].tag = frame THEN EXIT; <> IF largeFrameList = NIL THEN Process.DisableTimeout[@largeFramesInUse] ELSE Process.SetTimeout[@largeFramesInUse, largeFrameTimeout]; WAIT largeFramesInUse; ENDLOOP; PrincOpsUtils.DisableInterrupts[]; -- prevent AV manipulation largeFrame _ LOOPHOLE[ AV[LargeReturnSlot].frame - LargeFrameHeader.SIZE - AnyFrameSizeIndex.SIZE]; AV[LargeReturnSlot].link _ AV[LargeReturnSlot].link^.link; PrincOpsUtils.EnableInterrupts[]; -- AV is now consistent IF largeFrame = largeFrameList THEN <> IF (largeFrameList _ largeFrame.next) ~= NIL THEN largeFrameList.prev _ NIL ELSE NULL ELSE IF (largeFrame.prev.next _ largeFrame.next) ~= NIL THEN largeFrame.next.prev _ largeFrame.prev; }; WaitForLargeFrameEntry[@largeFramesLock]; }; NotifyLargeFrame: PROC [largeFrame: VMInternal.LargeFrame] = INLINE { NotifyLargeFrameEntry: ENTRY PROC [monitor: POINTER TO MONITORLOCK] = INLINE { largeFrame.next _ largeFrameList; IF largeFrameList ~= NIL THEN largeFrameList.prev _ largeFrame; largeFrameList _ largeFrame; Process.SetTimeout[@largeFramesInUse, largeFrameTimeout]; -- OK because it's INLINE NOTIFY largeFramesInUse; }; NotifyLargeFrameEntry[@largeFramesLock]; }; <<>> AllocateForLocalFrames: PROC [count: VM.PageCount] RETURNS [interval: VM.Interval] = INLINE { <> outcome: VMInternal.AllocationOutcome; AllocateForLocalFramesEntry: ENTRY PROC [monitor: POINTER TO MONITORLOCK] = INLINE { <> [outcome, interval] _ VMInternal.AllocateVirtualMemoryInternal[count, $mds]; }; AllocateForLocalFramesEntry[@VMInternal.allocationLock]; IF outcome ~= ok THEN DebuggerSwap.WorryCallDebugger["No VM for frame heap"L]; FOR page: VM.PageNumber IN [interval.page..interval.page+interval.count) DO <> VMInternal.AllocateRealMemoryForLocalFrames[page]; ENDLOOP; NoteEmergency[@emergencyLock]; }; PageNumberToMDSAddress: PROC [page: VM.PageNumber] RETURNS [address: POINTER] = INLINE { RETURN[PrincOpsUtils.LowHalf[VM.AddressForPageNumber[page]]] }; <> Initialize: PROC = { myFramePage: VM.PageNumber = VM.PageNumberForAddress[PrincOpsUtils.MyLocalFrame[]]; FindOriginalFrameHeap: BootStartList.Proc = { OPEN BootStartList; entry: EntryPointer = IndexToEntryPointer[index]; WITH e: entry SELECT FROM space => NULL; swapUnit => { suPage: VM.PageNumber = IndexToSpaceEntryPointer[e.parent].vmPage + e.base; IF myFramePage IN [suPage..suPage + e.pages) THEN { frameHeapPieceList _ PageNumberToMDSAddress[suPage]; RETURN[TRUE] }; }; ENDCASE; }; PDA.fault[qFrameFault] _ PDA.fault[qPageFault] _ PDA.fault[qWriteProtectFault] _ [queue: QueueEmpty, condition: [tail: PsbNull, abortable: FALSE, wakeup: FALSE]]; Process.Detach[FORK PageFaultProcess]; Process.Detach[FORK WriteProtectFaultProcess]; BootStartList.Enumerate[FindOriginalFrameHeap]; Process.Detach[FORK FrameFaultProcess]; Process.DisableTimeout[@largeFramesInUse]; Process.Detach[FORK FlushLargeFramesProcess]; Process.Detach[FORK EmergencyRefillProcess]; SELECT VMSideDoor.rmPages FROM IN [0 .. 3100] => {ReadOnlyPageInSize _ 2; ReadWritePageInSize _ 1}; IN (3100 .. 6200] => {ReadOnlyPageInSize _ 4; ReadWritePageInSize _ 2}; > 6200 => {ReadOnlyPageInSize _ 7; ReadWritePageInSize _ 4}; ENDCASE; IF GermSwap.switches[two] THEN DebuggerSwap.CallDebugger["Key stop 2 (VM)"L]; ProcessorFace.SetMP[MPCodes.vmInitialized]; }; Initialize[]; END. <> <> <<>> <<>>