<> <> <> <> <> <> <> <<>> <> <<>> DIRECTORY DebuggerSwap USING [WorryCallDebugger], PrincOps, VM USING [DataState, Interval, LogPageCount, PageCount, PageState, PageNumber, VMPartition, wordsPerPage]; VMInternal: DEFINITIONS LOCKS vmStateLock IMPORTS DebuggerSwap = BEGIN <<>> <> PageFlags: TYPE = PrincOps.PageFlags; PageState: TYPE = VM.PageState; PageValue: TYPE = PrincOps.PageValue; DataState: TYPE = VM.DataState; InterimPageState: TYPE = PrincOps.InterimPageState; Interval: TYPE = VM.Interval; PageNumber: TYPE = VM.PageNumber; PageCount: TYPE = VM.PageCount; RealPageNumber: TYPE = PrincOps.RealPageNumber; -- 16M words of real memory VMPartition: TYPE = VM.VMPartition; DataLocation: PRIVATE TYPE = {in, out}; PinCount: PRIVATE TYPE = CARDINAL; VMMapEntry: PRIVATE TYPE = MACHINE DEPENDENT RECORD [ state(0): PrincOps.PageState, body(1): SELECT COMPUTED DataLocation FROM in => [real(1): RealPageNumber], out => [ fill(1:0..11): [0..7777B] _ 0, <> checkedOut(1:12..12): BOOL, readOnly(1:13..13): BOOL, dataState(1:14..15): DataState], ENDCASE]; <> RMState: PRIVATE TYPE = {free, reclaimable, pinned}; RMEntryPointer: PRIVATE TYPE = LONG POINTER TO RMMapEntry; RMMapEntry: PRIVATE TYPE = MACHINE DEPENDENT RECORD [ <> dataState(0:0..1): DataState, needsBackingStoreWrite(0:2..2): BOOL, available(0:3..4): [0..3] _ 0, body(0:5..31): SELECT rmState(0:5..6): RMState FROM free => [ availableF(0:7..15): [0..777B] _ 0, next(1:0..15): RealPageNumber ], reclaimable => [ <> referenced(0:7..7): BOOL _ FALSE, <> virtualHigh(0:8..15): [0..377B] _ 0, -- will eventually extend 'virtual' to 24 bits. virtual(1:0..15): WORD ], pinned => [ pinReason(0:7..15): PinReason _ normal, -- not essential, could be encoded in large 'pinCount' values pinCount(1:0..15): PinCount ] ENDCASE]; PinReason: TYPE = { normal, <> noSuchRealPage, <> permanentlyPinned, <> specialRealPageAvailable, <> specialRealPageInUse, <> cantBeWritten <> }; maxPinCount: PinCount = PinCount.LAST; lastVMPage: READONLY PageNumber; <> <> RMMapEntries: TYPE = RECORD[SEQUENCE COMPUTED RealPageNumber OF RMMapEntry]; GetVMMap: PROC [vmPage: PageNumber] RETURNS [entry: VMMapEntry] = INLINE { <> RETURN[LOOPHOLE[GetPageValue[vmPage]]]; }; SetVMMap: PROC [vmPage: PageNumber, entry: VMMapEntry] = INLINE { SetPageValue[vmPage, LOOPHOLE[entry, PageValue] ]; }; InOut: PROC [entry: VMMapEntry] RETURNS [DataLocation] = INLINE {RETURN[IF entry.state.flags = PrincOps.flagsVacant THEN out ELSE in]}; <> <<>> vmStateLock: PRIVATE MONITORLOCK; checkIn: PRIVATE CONDITION; freeList: PRIVATE RealPageNumber; freePages: PRIVATE INT; rmMap: PRIVATE LONG POINTER TO RMMapEntries; lastRealPage: PRIVATE RealPageNumber; cleaningRover: PRIVATE RealPageNumber; AllocateRealMemoryForLocalFrames: ENTRY PROC [vmPage: PageNumber] = INLINE { <> victim: Victim = AllocateRealMemoryInternal[ vmPage: vmPage, dirtyVictimOK: FALSE, pin: TRUE]; SetPageFlags[ virtual: vmPage, real: victim.realPage, flags: PrincOps.flagsClean]; }; AllocateRealMemoryInternal: PRIVATE READONLY PROC [ vmPage: PageNumber, dirtyVictimOK: BOOL _ TRUE, pin: BOOL _ FALSE] RETURNS [victim: Victim]; <> Outcome: TYPE = {ok, addressFault}; <<>> Unpin: PROC [vmPage: PageNumber] RETURNS [outcome: Outcome]; AddToFreeList: PRIVATE PROC [realPage: RealPageNumber] = INLINE { rmMap[realPage].body _ free[next: freeList]; freeList _ realPage; freePages _ freePages.SUCC; }; SpecialMemoryOutcome: TYPE = { needsCopy, needsIO, noTransfer, addressFault, badParameter, noMemory}; PrepareToAllocateSpecialRealMemory: PROC [ vmPage: PageNumber, buffer: PageNumber, special: PageNumber] RETURNS [outcome: SpecialMemoryOutcome]; <> FinishAllocateSpecialRealMemory: PROC [ vmPage: PageNumber, buffer: PageNumber, special: PageNumber, worked: BOOL]; <> PrepareToReleaseSpecialRealMemory: PROC [ vmPage: PageNumber, special: PageNumber] RETURNS [outcome: SpecialMemoryOutcome]; <> FinishReleaseSpecialRealMemory: PROC [ vmPage: PageNumber, special: PageNumber, worked: BOOL]; <> <> <<>> pagesIn64K: PageCount = 65536/VM.wordsPerPage; logPagesIn64K: VM.LogPageCount = 16 - PrincOps.logWordsPerPage; allocationLock: MONITORLOCK; <> AllocationOutcome: TYPE = {ok, notFound, badParameters}; AllocCount: TYPE = RECORD [ --*stats*-- requests: INT, requestsSatisfied: INT, pagesAllocated: INT, pagesFreed: INT ]; Partitions: TYPE = ARRAY VMPartition OF Interval; --*stats*-- allocCounts: ARRAY VMPartition OF AllocCount; partitions: Partitions; <<>> NoteFreePages: PROC [interval: Interval]; UpdateVMLimit: PROC [pages: PageCount]; AllocateVirtualMemoryInternal: READONLY PROC [ count: PageCount, partition: VM.VMPartition _ normalVM, subRange: Interval _ [0, 0], start: PageNumber _ 0, alignment: VM.LogPageCount _ 0, in64K: BOOL _ FALSE] RETURNS [outcome: AllocationOutcome, interval: Interval]; <= count, then 'interval' is contained in 'subRange', (4) interval.page MOD 2^alignment is zero, (5) if start is non-zero, then interval.page-start is minimal, (6) if in64K is TRUE, then the interval doesn't span a 64K word boundary. If such an interval is found, it is returned ('outcome' is 'ok') and, for every page in 'interval', State[page] = [data: undefined, readOnly: FALSE, hasRealMemory: FALSE, pinCount: 0]. If no such interval can be found, 'outcome' becomes 'notFound'.>> IsFree: --ENTRY-- PROC [vmPage: PageNumber] RETURNS [BOOL] = INLINE { <> vmEntry: VMMapEntry = GetVMMap[vmPage]; WITH vmE: vmEntry SELECT InOut[vmEntry] FROM in => RETURN[FALSE]; out => RETURN[vmE.dataState = none AND ~vmE.checkedOut]; ENDCASE => ERROR; }; MarkAllocated: PROC [vmPage: PageNumber] = INLINE { <> vmEntry: VMMapEntry _ GetVMMap[vmPage]; WITH vmE: vmEntry SELECT InOut[vmEntry] FROM in => Crash[]; out => IF vmE.dataState = none THEN { <> vmE.dataState _ undefined; vmE.readOnly _ FALSE; SetVMMap[vmPage, vmE]; } ELSE Crash[]; ENDCASE; }; <> State: PROC [vmPage: PageNumber] RETURNS [state: PageState]; SetDataState: PROC [vmPage: PageNumber, dataState: DataState] RETURNS [outcome: Outcome]; < ReadWrite>> MakeReadOnly: PROC [vmPage: PageNumber] RETURNS [outcome: Outcome]; MakeReadWrite: PROC [vmPage: PageNumber] RETURNS [outcome: Outcome]; <> swapBufferSize: PageCount = 50; SwapInOutcome: TYPE = { noReadNecessary, -- victim has been claimed; read unnecessary (dataState = undefined) alreadyIn, -- page already swapped in; no victim has been claimed needsRead, -- victim claimed; read required addressFault, -- page not allocated writeFault, -- page is read only couldntCheckOut -- page checked out and AllocateForSwapIn called with dontWait TRUE }; VictimState: TYPE = {clean, dirty}; Victim: TYPE = RECORD [ realPage: RealPageNumber, body: SELECT victimState: VictimState FROM clean => NULL, dirty => [vmPage: PageNumber], ENDCASE]; AllocateForSwapIn: PROC [vmPage: PageNumber, kill, pin: BOOL, dontWait: BOOL] RETURNS [outcome: SwapInOutcome, victim: Victim]; SwapInDone: PROC [vmPage, bufferPage: PageNumber, worked: BOOL]; SwapInDoneWithoutIO: PROC [vmPage: PageNumber, victim: Victim]; VictimWriteDone: PROC [ vmPage, bufferPage: PageNumber, victim: dirty Victim, worked: BOOL]; CleanOutcome: TYPE = { cantWrite, -- none of the following cases apply checkedOutClean, -- page is clean and swappable and checkOutClean = TRUE needsWrite, -- page is dirty and swappable addressFault -- page not allocated }; ConsiderCleaning: PROC [vmPage: PageNumber, checkOutClean: BOOL] RETURNS [outcome: CleanOutcome, real: RealPageNumber]; CleanDone: PROC [vmPage, bufferPage: PageNumber, worked: BOOL]; <<>> <> Age: PROC [vmPage: PageNumber] RETURNS [outcome: Outcome]; AgeInternal: PROC [vmPage: PageNumber, vmE: in VMMapEntry] = INLINE { <> IF vmE.state.flags.readonly AND vmE.state.flags.dirty THEN { rmMap[vmE.real].needsBackingStoreWrite _ TRUE; vmE.state.flags.dirty _ FALSE; rmMap[vmE.real].dataState _ changed; }; vmE.state.flags.referenced _ FALSE; SetVMMap[vmPage, vmE]; }; <> LaundryMode: TYPE = {casual, aggressive, panic}; GetCleaningCandidate: PROC [ desired: PageCount, comfortLevel: PageCount, tryHard: LaundryMode] RETURNS [interval: Interval, cleanSkipped: PageCount, passes: INT, rover: RealPageNumber]; NoteDirtyVictim: PROC; NoteNewVictim: PROC; <<>> <> IODirection: TYPE = {read, write}; IOResult: TYPE = {ok, labelCheck, someOtherError}; DoIO: PROC [ direction: IODirection, backingPage: PageNumber, interval: Interval, subsequentSeek: PageNumber _ 0] RETURNS [result: IOResult, done: PageCount]; HasBackingStorage: SAFE PROC [page: PageNumber] RETURNS [BOOL]; <<>> <> <<>> AnyFrameSizeIndex: TYPE = CARDINAL [0..PrincOps.AllocationVectorSize); -- note that an AnyFrameSizeIndex immediately precedes a Frame. NormalFrameSizeIndex: TYPE = PrincOps.FrameSizeIndex; FsiFrame: TYPE = MACHINE DEPENDENT RECORD [ fsi(0): AnyFrameSizeIndex, -- must be at 3 MOD 4 boundary. frame(1): local PrincOps.Frame]; FrameHeapPiece: TYPE = POINTER TO FrameHeapPieceHeader; FrameHeapPieceHeader: TYPE = MACHINE DEPENDENT RECORD [ next(0): FrameHeapPiece, -- must be at 0 MOD 4 boundary. wordsAllocated(1): CARDINAL, -- actually [0..VM.PagesToWords[PrincOps.shortPointerSpan]] wordsInUse(2): CARDINAL, -- assert: wordsInUse MOD 4 = 3 fsiFrame(3): SEQUENCE COMPUTED NAT OF FsiFrame]; -- must be at 3 MOD 4 boundary. largeFrameThresholdFsi: NormalFrameSizeIndex = 14; -- frames this size or larger will be allocated in an integral number of pages, and reclaimed when they are freed. LargeFrame: TYPE = POINTER TO LargeFrameHeader; LargeFrameHeader: TYPE = MACHINE DEPENDENT RECORD [ next(0): LargeFrame, prev(1): LargeFrame, trueFsi(2): NormalFrameSizeIndex, fsiFrame(3): SEQUENCE COMPUTED NAT OF FsiFrame]; -- must be at 3 MOD 4 boundary. <> frameHeapPieceList: FrameHeapPiece; -- list of frame heap pieces, excluding large frames largeFrameList: LargeFrame; -- list of large frames (some allocated, some freed) <<>> <> aSETMAP: PrincOps.alpha = 150B; aGETMAPFLAGS: PrincOps.alpha = 151B; aSETMAPFLAGS: PrincOps.alpha = 152B; useLong: READONLY BOOL; <> ExchangePageState: PROC [virtual: PageNumber, newState: PrincOps.PageState] RETURNS [pv: PageValue] = INLINE { <> <> OldExchangePageState: PROC [virtual: CARDINAL, state: InterimPageState] RETURNS [oldState: InterimPageState] = MACHINE CODE {PrincOps.zMISC, PrincOps.aSETF}; NewExchangePageState: PROC [vp: LONG CARDINAL, flgs: PrincOps.PageState] RETURNS [tState: PrincOps.PageState, rp: LONG CARDINAL] = MACHINE CODE {PrincOps.zMISC, aSETMAPFLAGS}; IF useLong THEN [tState: pv.state, rp: pv.real] _ NewExchangePageState[virtual, newState] ELSE [flags: pv.state.flags, realPage: pv.real] _ OldExchangePageState[virtual, InterimPageState[FALSE, newState.flags, 0]].oldState; }; ExchangePageFlags: PROC [virtual: PageNumber, newFlags: PageFlags] RETURNS [PageValue] = INLINE { <> <> RETURN ExchangePageState[virtual, PageStateFromFlags[newFlags]]; }; GetPageValue: PROC [virtual: PageNumber] RETURNS [pv: PageValue] = INLINE { <> OldGetPageValue: PROC [virtual: CARDINAL] RETURNS [state: InterimPageState] = MACHINE CODE {PrincOps.zMISC, PrincOps.aGETF}; NewGetPageValue: PROC [vp: LONG CARDINAL] RETURNS [tState: PrincOps.PageState, rp: LONG CARDINAL] = MACHINE CODE {PrincOps.zMISC, aGETMAPFLAGS}; IF useLong THEN [tState: pv.state, rp: pv.real] _ NewGetPageValue[virtual] ELSE [flags: pv.state.flags, realPage: pv.real] _ OldGetPageValue[virtual].state; }; SetPageValue: PROC [virtual: PageNumber, pv: PageValue] = INLINE { <> OldSetPageValue: PROC [virtual: CARDINAL, state: InterimPageState] = MACHINE CODE {PrincOps.zMISC, PrincOps.aASSOC}; NewSetPageValue: PROC [vp, rp: LONG CARDINAL, flgs: PrincOps.PageState] = MACHINE CODE {PrincOps.zMISC, aSETMAP}; IF useLong THEN NewSetPageValue[virtual, pv.real, pv.state] ELSE OldSetPageValue[virtual, InterimPageState[FALSE, pv.state.flags, pv.real]]; }; SetPageFlags: PROC [virtual: PageNumber, real: RealPageNumber, flags: PageFlags] = INLINE { <> SetPageValue[virtual, [state: PageStateFromFlags[flags], real: real]]; }; IsMapped: SAFE PROC [virtual: PageNumber] RETURNS [BOOL] = TRUSTED INLINE {RETURN[GetPageValue[virtual].state.flags ~= PrincOps.flagsVacant]}; IsVacant: SAFE PROC [virtual: PageNumber] RETURNS [BOOL] = TRUSTED INLINE {RETURN[GetPageValue[virtual].state.flags = PrincOps.flagsVacant]}; PageStateFromFlags: SAFE PROC [flags: PageFlags] RETURNS [PrincOps.PageState] = TRUSTED INLINE {RETURN[LOOPHOLE[flags]]}; <<>> <<>> <> InitializeTables: PROC; Crash: PROC = INLINE {DebuggerSwap.WorryCallDebugger[NIL]}; END.