-- RTPageFaultImpl.Mesa -- ******* NOTE COMPILE THIS /-c ********* -- last edited 16-Mar-82 11:09:05 by Paul Rovner DIRECTORY Process USING [SetPriority, priorityInterrupt, Detach, Pause, MsecToTicks, priorityNormal], ProcessInternal USING[EnableInterrupts, DisableInterrupts], ProcessOperations USING[HandleToIndex, ReadPSB, LongNotify, LongWait, LongReEnter], PSB USING[PsbIndex, PsbNull, PDA, qPageFault, Condition], RTOS USING[UnregisterCedarProcess], RTProcess USING[], -- EXPORTS only RTProcessPrivate USING[], -- EXPORTS only Runtime USING[GlobalFrame], SpecialSpace USING[MakeCodeResident, MakeCodeSwappable, MakeGlobalFrameResident--, MakeGlobalFrameSwappable--]; RTPageFaultImpl: MONITOR IMPORTS Process, ProcessInternal, ProcessOperations, RTOS, Runtime, SpecialSpace EXPORTS RTProcess, RTProcessPrivate = BEGIN --constants, variables, TYPEs Int: TYPE = LONG INTEGER; recorderLock: MONITORLOCK; pPageFaultCondition: LONG POINTER TO PSB.Condition = @PSB.PDA.fault[PSB.qPageFault].condition; pPageFaultCONDITION: LONG POINTER TO CONDITION = LOOPHOLE[pPageFaultCondition]; totalPageFaults: Int ← 0; MaxPSBIVecIndex: NAT = 40; NextPSBIVecIndex: NAT ← 0; PSBIVec: ARRAY [0..MaxPSBIVecIndex] OF PSBIEntry; PSBIEntry: TYPE = RECORD[psbi: PSB.PsbIndex ← PSB.PsbNull, count: Int ← 0]; faultWatcherRunning: BOOLEAN ← FALSE; faultWatcherStartCount: NAT ← 0; -- PUBLIC procs StartWatchingFaults: PUBLIC ENTRY PROC = {ENABLE UNWIND => NULL; IF (faultWatcherStartCount ← faultWatcherStartCount + 1) = 1 THEN {faultWatcherRunning ← TRUE; Process.Detach[FORK FaultWatcher]}}; StopWatchingFaults: PUBLIC PROC = { firstCall: BOOLEAN ← TRUE; UNTIL DoStopWatchingFaults[firstCall] DO firstCall ← FALSE; Process.Pause[Process.MsecToTicks[120]] ENDLOOP}; DoStopWatchingFaults: ENTRY PROC[firstCall: BOOLEAN] RETURNS[success: BOOLEAN] = { ENABLE UNWIND => NULL; IF firstCall THEN {IF faultWatcherStartCount = 0 THEN RETURN[TRUE]; faultWatcherStartCount ← faultWatcherStartCount - 1}; RETURN[faultWatcherStartCount # 0 OR NOT faultWatcherRunning]}; -- NOTE not an ENTRY, to avoid deadlock InvalidateFaultingCedarProcess: PUBLIC PROC[psbi: PSB.PsbIndex] = { IF NOT faultWatcherRunning THEN RETURN; FOR i: NAT IN [0..NextPSBIVecIndex) DO IF PSBIVec[i].psbi = psbi THEN {PSBIVec[i] ← []; EXIT} ENDLOOP}; GetTotalPageFaults: PUBLIC PROC RETURNS[LONG INTEGER] = {RETURN[totalPageFaults]}; ClearFaultHistory: PUBLIC ENTRY PROC = { ENABLE UNWIND => NULL; totalPageFaults ← NextPSBIVecIndex ← 0}; GetPSBIPageFaults: PUBLIC ENTRY PROC[psbi: PSB.PsbIndex] RETURNS[Int] = { ENABLE UNWIND => NULL; FOR i: NAT IN [0..NextPSBIVecIndex) DO IF PSBIVec[i].psbi = psbi THEN RETURN[PSBIVec[i].count]; ENDLOOP; RETURN[0]}; EnumerateFaultingProcesses: PUBLIC PROC[p: PROC[psbi: PSB.PsbIndex, pageFaultCount, numberEnumerated: LONG INTEGER] RETURNS[stop: BOOLEAN]] RETURNS[stopped: BOOLEAN] = {npsbi: NAT = NextPSBIVecIndex; FOR i: NAT IN [0..npsbi) DO psbi: PSB.PsbIndex = PSBIVec[i].psbi; count: Int = PSBIVec[i].count; IF count # 0 AND psbi # PSB.PsbNull THEN IF p[psbi, count, npsbi] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]}; -- high priority stuff for monitoring page fault activity BumpPSBIFaultCounter: PROC[psbi: PSB.PsbIndex] = INLINE {empty: NAT ← NextPSBIVecIndex; totalPageFaults ← totalPageFaults + 1; FOR i: NAT IN [0..NextPSBIVecIndex) DO IF PSBIVec[i].psbi = psbi THEN {PSBIVec[i].count ← PSBIVec[i].count + 1; RETURN} ELSE IF PSBIVec[i].psbi = PSB.PsbNull THEN empty ← i; ENDLOOP; IF empty > MaxPSBIVecIndex THEN RETURN; -- forget it. PSBIVec[empty] ← [psbi: psbi, count: 1]; IF empty = NextPSBIVecIndex THEN NextPSBIVecIndex ← NextPSBIVecIndex + 1}; GetCurrent: PROC RETURNS[PSB.PsbIndex] = INLINE {RETURN[ProcessOperations.HandleToIndex[ProcessOperations.ReadPSB[]]]}; LongNakedNotify: PROC [pCondition: LONG POINTER TO CONDITION] = INLINE {-- used to notify a condition from outside the relevant monitor. pCond: LONG POINTER TO PSB.Condition = LOOPHOLE[pCondition]; ProcessInternal.DisableInterrupts[]; IF pCond↑.tail=PSB.PsbNull THEN { pCond↑.wakeup ← TRUE; ProcessInternal.EnableInterrupts[] } ELSE { ProcessInternal.EnableInterrupts[]; ProcessOperations.LongNotify[pCondition] } }; WaitForFault: PROC = INLINE {ProcessOperations.LongWait [@recorderLock, pPageFaultCONDITION, --timeout:-- 1]; UNTIL ProcessOperations.LongReEnter [@recorderLock, pPageFaultCONDITION] DO NULL ENDLOOP; -- either a new page fault came along or we timed out.. IF PSB.PDA.fault[PSB.qPageFault].queue.tail = PSB.PsbNull THEN RETURN; -- just timed out, so forget it BumpPSBIFaultCounter[PSB.PDA.fault[PSB.qPageFault].queue.tail]; -- conditionally wake up the Pilot fault handler: ProcessInternal.DisableInterrupts[]; IF pPageFaultCondition↑.tail # PSB.PsbNull THEN LongNakedNotify[pPageFaultCONDITION]; ProcessInternal.EnableInterrupts[]; }; -- this guy runs as a detached process. FaultWatcher: PROC = { -- make the fault watcher resident SpecialSpace.MakeGlobalFrameResident[RTPageFaultImpl]; SpecialSpace.MakeCodeResident[Runtime.GlobalFrame[FaultWatcher]]; -- unregister self RTOS.UnregisterCedarProcess[GetCurrent[]]; -- raise priority Process.SetPriority[Process.priorityInterrupt]; DO IF faultWatcherStartCount = 0 THEN EXIT ELSE WaitForFault[] ENDLOOP; Process.SetPriority[Process.priorityNormal]; SpecialSpace.MakeCodeSwappable[Runtime.GlobalFrame[FaultWatcher]]; -- SpecialSpace.MakeGlobalFrameSwappable[RTPageFaultImpl]; faultWatcherRunning ← FALSE}; END.