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]; 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. ΚVMFaultsImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. created by Levin Bob Hagmann, January 30, 1985 10:54:09 am PST Russ Atkinson (RRA) October 29, 1985 2:22:03 pm PST Global Variables Some VM tuning parameters. The desired size of the interval to swapin around a faulted page. A size of 1 means only swapin the faulted page. Sizes are set independantly by type of page faulted: readOnly pages (code) and readWrite pages (data). By convention ReadOnlyPageInSize >= ReadWritePageInSize. The values of these entries can be changed during initialization. Exports to VMStatistics Exports to VMInternal Fault-Handling Processes Debugging Info Reserve a state vector for this process Give this thing a retry with the exact page to avoid confusion with surrounding pages. Reserve a state vector for this process Reserve a state vector for this process. We will allocate a large frame (i.e., an integral number of pages). We will allocate a small frame, which may or may not require extension of the heap. Assert: frSize MOD 4 = 0. The remaining space in the most recent extension piece is insufficient for the faultee. We subdivide the available space into the smallest set of frames that will exhaust (or nearly exhaust) it. We now allocate a new piece from which to build frames. Fabricate a new frame Atomically chain the new frame onto the AV: Note that we can not do a Frame.Free[] here because large frames have an fsi different than that of the AV slot we put them in. Restart faulted process: This process can run at any priority and without a reserved state vector. Emergency frame stuff This process refills the emergency page list used by frame allocation when there are no available clean pages. We give this a foreground priority to try to stay ahead of greedy processes. Allocate and initialize the debugging information. This eliminates a fair chunk of MDS usage. This page needs a new allocation The page needs to have real memory pinned beneath it for the use of the frame allocator. We temporarily remove it from the emergency list to keep from being found in an unhappy state. Internal Procedures This fault does NOT require I/O, so enlarging it does us no real good If the successor of "process" on the queue differs from "process", we extract the page number on which it has faulted. Note that another process may come along and be inserted between the two, but this is unlikely and we're only looking for a hint to optimize disk arm motion. This routine can be called from a world-swap debugger to examine the current state of a page. It is also used inside of ReportFault to capture the state at the time of the fault. Variables for easy inspection from the debugger if something goes wrong There will be something funny if faultAddress # address AND error = VM.AddressFault! This lets us see the state roughly at the time of the fault. Remember the last page that gets an address fault (just in case the user recovers). Splice me in as top-of-stack of psb. Return to caller without disturbing this frame. Control returns here in the context of the faulting process. If it has a dirty cleanup link, disaster may ensue if a signal is raised, since it is almost certain to be uncaught and the AMEvents stuff will doubtless do a WAIT. If someone caught it and did a wait, similar bad things would happen. So, we call the world-swap debugger instead. Splice me in as top-of-stack of psb. Return to caller without disturbing this frame. Control returns here in the context of the faulting process. If it has a dirty cleanup link, disaster may ensue if a signal is raised, since it is almost certain to be uncaught and the AMEvents stuff will doubtless do a WAIT. If someone caught it and did a wait, similar bad things would happen. So, we call the world-swap debugger instead. Implementation note: The explicit use of PrincOpsUtils to invoke monitor instructions is required because FaultQueues do not have full CONDITION variables in them. Rather, they have only the Queue portion; there is no timeout field. Since the generated code for WAIT assumes a timeout word follows the Queue, the language construct can't be used. Of course, the use of monitor locks is really unnecessary anyway, since there is only a single process doing waits and the condition variable is notified by microcode with a naked notify. However, it is the easiest way to achieve the desired effect. Note: AV[LargeReturnSlot] can go from non-empty to empty only as a result of this procedure. Therefore, the test of AV[LargeReturnSlot] need not be made with interrupts disabled. The following are legal only because they are INLINEs. head of large frame list is being freed. This is a specialized procedure for use by the frame fault handler. It is semantically equivalent to {interval _ Allocate[count, mds]; SwapIn[interval, kill: TRUE, pin: TRUE]}, but must be implemented carefully because of the delicate state of the world at the time it is invoked. The following is actually a coroutine call (to avoid frame allocation). The following is actually a coroutine call (to avoid frame allocation). Initialization Bob Hagmann January 30, 1985 10:04:43 pm PST added code to PageFaultProcess to retry and help find the address fault sometimes encountered during booting Κ– "Cedar" style˜codešœ™Kšœ Οmœ1™˜@Kšœžœžœžœ˜,Kšœžœ6žœ˜DK˜—šžœžœžœ˜KšœV™VKšžœ žœžœ+˜Bšžœ/˜1šœžœ˜Kšœžœ*˜AKšžœ˜ K˜—Kšœžœ6žœ˜CK˜—K˜—K˜——Kšœ$˜$Kšœžœ˜Kšžœ˜—K˜—K˜š œžœ˜"Kšœ'™'K˜3šž˜Kšœ8˜8Kšœžœ˜+Kšœ,˜,KšŸ œžœ˜+Kšžœ˜—K˜—K˜K˜š œžœ˜Kšœ(™(Kšžœ ˜K˜3šž˜Kšœ1˜1Kšœžœžœ˜@Kšœžœ4žœ˜IKšœŸ4˜Lšžœžœ˜'KšœC™CKšœžœžœ)žœ˜CKšœT˜TKšœ$žœ˜(Kšœ˜Kšœ˜Kšœ/Ÿ+˜ZKšœ)˜)KšŸ œ-žœ˜=Kšœ˜—šžœ˜KšœS™SKšœžœ™Kšœ+˜+Kšœ žœžœ Ÿ"˜Ešžœ žœžœ2žœ˜KKšœΓ™Γšœžœ ˜Kšžœ-žœ˜5—Kšœžœžœ ˜>Kšœžœ2žœ˜MKšœžœ˜Kšœ%˜%KšœKžœ˜PšžœKž˜Ršœ žœ˜Kšœ,žœžœ˜K—šžœž˜#Kšœ˜Kšœ)Ÿ$˜MKšœ&˜&Kšœ0˜0Kšœ,˜,Kšžœ˜—Kšžœ˜—Kšœ7™7KšœžœE˜MKšœ ˜ Kšœ'˜'Kšœ(žœ˜-Kšœ˜KšŸ œ+žœ˜;Kšœ˜—Kšœ™KšœKžœ˜PK˜K˜Kšœ-˜-KšŸ œ-žœ˜=Kšœ˜—Kšœ+™+Kšœ@™@Kšœ>™>Kšœ"˜"Kšžœ žœžœžœ ˜;Kšžœ˜Kšžœ žœžœ9žœ˜UK˜!Kšœ™K˜%KšŸ œžœ˜+Kšžœ˜—K˜—K™š œžœ˜!KšœI™IKšœ3˜3šž˜Kšœ8˜8šžœ˜Kšœžœ"˜+Kšœžœ>˜G—KšŸ œ%žœ˜5Kšžœ˜—K˜K˜——šœ™K˜š œžœ˜ Kšœ»™»š œžœžœ žœžœž œžœ˜IKšžœžœžœžœ˜>Kšœ˜K˜—Kšœ žœ žœ˜4Kšœ žœ˜Kšœ#žœ%˜JKšœ˜Kšœ˜Kšœžœ˜Kšœ0˜0Kšžœ!žœ˜)Kšœ$Ÿ˜A˜Kšœ^™^Kš œ žœ žœžœžœ˜OKš œžœžœžœ žœ'˜KK˜ Kšžœ#žœ˜+K˜K˜—šž˜Kšœ!˜!Kšœ˜šžœžœžœž˜Kšœžœ˜%šžœ žœ˜Kšœ ™ Kšœžœ˜(K˜—šžœ3žœ˜;KšœΈ™ΈKšœ˜Kšžœ"žœ˜*Kšœ˜Kšœ$˜$K˜—Kšžœ˜—Kšžœ˜—K˜K˜—š  œžœžœ žœžœž œžœ˜FKšž œ˜K˜K˜—Kšœž œ˜Kšœž œ˜Kšœžœ˜K™—šœ™K˜š  œžœžœ žœžœ˜[Kšœ žœ˜(Kšœ žœ˜%Kšœ žœ žœ˜.Kšœ žœ!˜.Kšœ žœ˜Kšœ žœžœ*˜Kšžœ˜—Kšœ%žœžœ!˜SKšœ˜K˜—K™š œžœ#žœ˜?Kšœžœžœ ˜—Kšžœ˜Kšžœ˜—Kšœ$Ÿ˜>šœ žœ˜Kšžœ+žœžœ˜L—Kšžœžœ˜:Kšœ#Ÿ˜:šžœž˜#Kšœ(™(Kšžœ'žœžœž ˜U—šž˜šžœ-žœž˜7K˜'——K˜—Kšœ)˜)K˜—K˜š œžœ'žœ˜Eš œžœžœ žœžœž œžœ˜NKšœ!˜!Kšžœžœžœ"˜?Kšœ˜Kšœ;Ÿ˜TKšžœ˜K˜—Kšœ(˜(K˜—K™š  œžœ žœ žœ žœ žœ˜]K™™Kšœ&˜&š œžœžœ žœžœž œžœ˜TKšœG™GKšœL˜LK˜—Kšœ8˜8Kšžœžœ9˜Nšžœžœ žœ/ž˜KKšœG™GKšœ2˜2Kšžœ˜—Kšœ˜K˜—K˜š  œžœžœ žœ žœžœ˜XKšžœžœ˜h