-- Copyright (C) 1981, 1982, 1984, 1985 by Xerox Corporation. All rights reserved. -- VMIO.mesa, HGM, 16-Sep-85 23:08:19 -- Last edited by Wobber: 2-Nov-82 10:38:19 -- Last edited by Gobbel: 18-May-81 12:18:20 DIRECTORY FileDefs USING [ComparePositions, Completer, FileHandle, PageNumber], Inline USING [LowHalf], LogDefs USING [DisplayNumber, Percentage], VMDefs USING [ CantReadBackingStore, Error, LookAheadCount, Page, PageAddress, Position, Problem], VMPrivate USING [ AcquireCache, AcquirePage, AllocateCacheIndex, CacheIndex, EnterInHashTable, EnterInPageTable, FileHandle, FileObject, IndexToHandle, LookupInHashTable, nilCacheIndex, PageHandle, ReleaseCache, ReleasePage, ValidateFile, ValidatePageNumber, WaitUntilStable]; VMIO: PROGRAM IMPORTS FileDefs, Inline, LogDefs, VMDefs, VMPrivate EXPORTS VMDefs, VMPrivate = BEGIN OPEN VMDefs, VMPrivate; -- Statistics Logging -- readPageCalls, readPageCacheHits: LONG CARDINAL; cacheHitPercent: LogDefs.Percentage; -- Miscellaneous Declarations -- BadAddress: ERROR = CODE; WaitUntilStableFailed: ERROR = CODE; -- Procedures, Signals, and Types Exported to VMDefs -- FileObject: PUBLIC TYPE = VMPrivate.FileObject; ReadPage: PUBLIC PROCEDURE [addr: PageAddress, lookAhead: LookAheadCount ← 0] RETURNS [Page] = BEGIN page: PageHandle; FixUseCount: PROCEDURE = INLINE -- decrements page.useCount after an error. BEGIN AcquirePage[page]; -- needn't remap 'addr' because useCount > 0 page.useCount ← page.useCount - 1; ReleasePage[page]; END; ValidateAddressAndAcquireCache[addr]; DoReads[addr, lookAhead, TRUE]; ReleaseCache[]; page ← IndexToHandle[LookupInHashTable[addr]]; -- Lookup can't fail; useCount > 0 readPageCalls ← readPageCalls + 1; IF page.state = stable THEN readPageCacheHits ← readPageCacheHits + 1; cacheHitPercent ← Inline.LowHalf[(readPageCacheHits * 100) / readPageCalls]; IF WaitUntilStable[page, reading ! CantReadBackingStore => FixUseCount[]] # stable THEN ERROR WaitUntilStableFailed; RETURN[page.pointer]; END; StartReading: PUBLIC PROCEDURE [ addr: PageAddress, lookAhead: LookAheadCount ← 0] = BEGIN ValidateAddressAndAcquireCache[addr]; DoReads[addr, lookAhead, FALSE]; ReleaseCache[]; END; -- Procedures and Signals Exported to VMPrivate -- -- Page I/O -- WritePageToFS: PUBLIC PROCEDURE [page: PageHandle, wait: BOOLEAN] = BEGIN vmFile: FileHandle = page.file; fFile: FileDefs.FileHandle = vmFile.fh; DO -- loops only in the case that the write is never started (which can't -- happen if extending) extending: BOOLEAN; newLength: Position; [extending, newLength] ← ValidatePageAddress[page]; page.errorStatus ← ok; IF extending THEN BEGIN vmFile.fs.ops.extend[ fFile, newLength, page.pointer ! VMDefs.Error => {page.errorStatus ← reason; CONTINUE}]; IF page.errorStatus = ok THEN page.dirty ← FALSE; ReleasePage[page]; END ELSE vmFile.fs.ops.startWrite[ fFile, page.page, page.pointer, FSWriteComplete, page]; IF ~wait OR WaitUntilStable[page, writing] = stable THEN EXIT; ENDLOOP; END; -- Start/Stop -- InitializeVMIO: PUBLIC PROCEDURE = BEGIN readPageCalls ← readPageCacheHits ← cacheHitPercent ← 0; LogDefs.DisplayNumber["VM Cache Hits"L, [percent[@cacheHitPercent]]]; END; FinalizeVMIO: PUBLIC PROCEDURE = {NULL}; -- Miscellaneous -- ValidatePageAddress: PUBLIC PROCEDURE [page: PageHandle] RETURNS [extending: BOOLEAN, newLength: Position] = BEGIN vmFile: FileHandle = page.file; length: Position = vmFile.fs.ops.getLength[vmFile.fh]; newLength ← [page.page + 1, 0]; IF ~(extending ← FileDefs.ComparePositions[newLength, length] = greater) THEN RETURN; IF newLength.page ~= length.page + 1 THEN ERROR BadAddress; END; -- Internal Procedures -- -- Page Input -- DoReads: PROCEDURE [ addr: PageAddress, lookAhead: LookAheadCount, bump: BOOLEAN] = -- does the common part of ReadPage and StartReading. If 'bump' is TRUE, the -- useCount for the page corresponding to 'addr' will be incremented (this prevents -- it from being stolen by any look-ahead). BEGIN vmFile: FileHandle = addr.file; fileLength: Position = vmFile.fs.ops.getLength[vmFile.fh]; pg: FileDefs.PageNumber; actualReads: CARDINAL; IF (pg ← addr.page + (IF fileLength.byte = 0 THEN 1 ELSE 0)) > fileLength.page THEN ERROR BadAddress; actualReads ← MIN[lookAhead, fileLength.page - pg] + 1; THROUGH [0..actualReads) DO page: PageHandle; oldindex, newindex: CacheIndex; BEGIN IF (oldindex ← LookupInHashTable[addr]) # nilCacheIndex THEN GO TO AlreadyIn; ReleaseCache[]; newindex ← AllocateCacheIndex[]; AcquireCache[]; page ← IndexToHandle[newindex]; IF (oldindex ← LookupInHashTable[addr]) # nilCacheIndex THEN { page.state ← stable; GO TO AlreadyIn}; page.file ← addr.file; page.page ← addr.page; page.dirty ← FALSE; EnterInPageTable[page, newindex]; EnterInHashTable[page]; vmFile.fs.ops.startRead[ vmFile.fh, page.page, page.pointer, FSReadComplete, page]; EXITS AlreadyIn => {page ← IndexToHandle[oldindex]}; END; page.age ← new; IF bump THEN {page.useCount ← page.useCount + 1; bump ← FALSE}; addr.page ← addr.page + 1; ENDLOOP; END; FSReadComplete: FileDefs.Completer = -- handles termination of remote file read operations initiated by DoReads. BEGIN page: PageHandle = LOOPHOLE[arg]; page.errorStatus ← outcome; ReleasePage[page]; END; -- Page Output -- FSWriteComplete: FileDefs.Completer = -- handles termination of remote file write operations initiated by WritePageToFS. BEGIN page: PageHandle = LOOPHOLE[arg]; IF (page.errorStatus ← outcome) = ok THEN page.dirty ← FALSE; ReleasePage[page]; END; -- Miscellaneous -- ValidateAddressAndAcquireCache: PROCEDURE [addr: PageAddress] = BEGIN ValidateFile[addr.file]; ValidatePageNumber[addr.page]; AcquireCache[]; END; END.