-- Filer>FilerTransferImpl.mesa (August 4, 1982 10:59 am by Levin) -- Things to consider: -- 1) Large XWire request (>Transfer.maxConcurrency) will hang forever; should split up as with label requests (must deal with multiple processes however...simply making this module a monitor would seem to introduce deadlocks ...?) -- 2) Main routines for data and label transfers should be combined. Note that combined routine will be an external procedure and will execute within monitor for label transfers but outside it for data... -- 3) Current code in Initiate will fail if a page group crosses a physical volume boundary. DIRECTORY DiskChannel USING [ Address, CompletionHandle, CompletionStatus, Create, CreateCompletionObject, Delete, Drive, Handle, InitiateIO, IORequest, Label, PLabel, WaitAny], Environment USING [PageCount, PageNumber, wordsPerPage], File USING [PageCount, PageNumber], FileCache USING [GetFilePtrs, GetPageGroup, ReturnFilePtrs], FileInternal USING [Descriptor, FilePtr, PageGroup], FilePageTransfer USING [Request], FilerException USING [Report], FilerPrograms USING [], FileTask USING [LabelWait -- ,XWireStart--], Inline USING [LowHalf], LabelTransfer USING [Operation], MStore USING [Promise], PilotDisk USING [Label], --RemotePageTransfer USING [Initiate, Suggest], ResidentMemory USING [Allocate], SubVolume USING [Find, Handle, PageNumber, StartIO, IO], --System USING [nullNetworkAddress], --SystemInternal USING [altoFPSeries, UniversalID], Utilities USING [PageFromLongPointer], VolumeInternal USING [--altoVolume,-- PageNumber]; FilerTransferImpl: MONITOR IMPORTS DiskChannel, FileCache, FilerException, FileTask, Inline, MStore, ResidentMemory, SubVolume, Utilities EXPORTS FilePageTransfer, FilerPrograms, LabelTransfer SHARES File = BEGIN Bug: ERROR [type: BugType] = CODE; BugType: TYPE = {subVolWentAway}; -- Temporary histogram to record statistics on runs of pages transferred logging: BOOLEAN ← FALSE; hist: ARRAY [0..16] OF LONG CARDINAL ← ALL[0]; Log: PRIVATE PROCEDURE [size: CARDINAL] = BEGIN FOR i: CARDINAL IN [0..16] DO IF size = 0 THEN {hist[i] ← hist[i] + 1; EXIT} ELSE size ← size/2; ENDLOOP END; -- hist[j] counts requests of size [2↑(j-1)..(2↑j)-1] Initiate: PUBLIC PROCEDURE [req: FilePageTransfer.Request] = BEGIN groupSize: Environment.PageCount; -- OK to be a short cardinal since we only do I/O from pages of virtual memory. fileFound, groupFound: BOOLEAN; fileP: FileInternal.FilePtr; --fileD: FileInternal.Descriptor; rReq: FilePageTransfer.Request = req; group: FileInternal.PageGroup; IF req.count = 0 THEN RETURN; --IF LOOPHOLE[file.fID, SystemInternal.UniversalID].series = -- SystemInternal.altoFPSeries THEN ++ Alto files are special remote files -- BEGIN -- fileP ← @fileD; -- fileD ← FileInternal.Descriptor[file.fID, VolumeInternal.altoVolume, remote[]]; -- END --ELSE -- BEGIN [fileFound, fileP] ← FileCache.GetFilePtrs[1, req.file.fID]; IF ~fileFound THEN { FilerException.Report[req]; RETURN }; -- END; WHILE req.count > 0 DO -- Break request into page groups and start transfers: WITH fileP SELECT FROM local => BEGIN svH: SubVolume.Handle; svFound: BOOLEAN; svPage: SubVolume.PageNumber; [groupFound, group] ← FileCache.GetPageGroup[req.file.fID, req.filePage]; IF ~groupFound THEN { FileCache.ReturnFilePtrs[1, fileP]; FilerException.Report[req]; EXIT }; groupSize ← MIN[req.count, Inline.LowHalf[group.nextFilePage - req.filePage]]; IF req.promise THEN MStore.Promise[groupSize]; -- tell the SwapOutProcess this page group is on the way. IF logging THEN Log[groupSize]; [svFound, svH] ← SubVolume.Find[fileP.volumeID, group.volumePage]; IF ~svFound THEN ERROR Bug[subVolWentAway]; svPage ← group.volumePage - svH.lvPage + (req.filePage - group.filePage); BEGIN io: SubVolume.IO ← [ op: req.operation, subVolume: svH, subVolumePage: svPage, filePtr: fileP, memPage: req.memoryPage, filePage: req.filePage, pageCount: groupSize]; SubVolume.StartIO[@io]; END; END; remote => BEGIN -- groupSize ← 1; ++ yuck! This should be in runs, too -- FileTask.XWireStart[req.memoryPage, fileP]; -- RemotePageTransfer.Suggest[ -- [req.operation, req.file.fID, req.filePage, req.memoryPage]]; -- Alto file hack: remove later END; ENDCASE; req.filePage ← req.filePage + groupSize; req.memoryPage ← req.memoryPage + groupSize; req.count ← req.count - groupSize; IF req.count > 0 THEN [] ← FileCache.GetFilePtrs[1, req.file.fID]; ENDLOOP; --WITH fileP SELECT FROM -- remote => RemotePageTransfer.Initiate[rReq, System.nullNetworkAddress]; -- ENDCASE; END; -- Start of monitor implementing LabelTransfer interface -- Empty page is source of zeros (sink for garbage) when writing (reading) labels. emptyPagePtr: LONG POINTER = ResidentMemory.Allocate[hyperspace, 1]; emptyPage: Environment.PageNumber = Utilities.PageFromLongPointer[emptyPagePtr]; completion: DiskChannel.CompletionHandle ← DiskChannel.CreateCompletionObject[]; LabelOperation: TYPE = RECORD[ operation: LabelTransfer.Operation, fileP: POINTER TO READONLY FileInternal.Descriptor, pageGroupPtr: POINTER TO READONLY FileInternal.PageGroup, memoryPage: Environment.PageNumber, label: POINTER TO PilotDisk.Label]; ReadLabel: PUBLIC ENTRY PROCEDURE [ file: FileInternal.Descriptor, filePage: File.PageNumber, volumePage: VolumeInternal.PageNumber, handleErrors: BOOLEAN] RETURNS [label: PilotDisk.Label, status: DiskChannel.CompletionStatus] = BEGIN pageGroup: FileInternal.PageGroup ← [filePage, volumePage, filePage + 1]; op: LabelOperation; op ← [readLabel, @file, @pageGroup, emptyPage, @label]; status ← Perform[@op, handleErrors].status; END; ReadRootLabel: PUBLIC ENTRY PROCEDURE [ drive: DiskChannel.Drive, rootPage: VolumeInternal.PageNumber] RETURNS [label: PilotDisk.Label, status: DiskChannel.CompletionStatus] = BEGIN OPEN DiskChannel; rootChannel: Handle ← Create[drive, completion]; -- temporary channel for root page request: IORequest; pLabel: PLabel; labelStorage: ARRAY [0..SIZE[Label] + 3) OF WORD; -- space for label plus extra for quad-alignment pLabel ← LOOPHOLE[((LOOPHOLE[LONG[@labelStorage[0]], LONG INTEGER] + 3)/4)*4]; --quad-align label BEGIN OPEN request; -- Can't use constructor because of blasted PRIVATE fields... channel ← rootChannel; diskPage ← rootPage; memoryPage ← emptyPage; count ← 1; command ← vrr; label ← pLabel; dontIncrement ← TRUE; END; InitiateIO[@request]; status ← WaitAny[completion].status; Delete[rootChannel]; label ← LOOPHOLE[pLabel↑]; END; WriteLabels: PUBLIC ENTRY PROCEDURE [ file: FileInternal.Descriptor, pageGroup: FileInternal.PageGroup, handleErrors: BOOLEAN ← TRUE] RETURNS [status: DiskChannel.CompletionStatus] = BEGIN OPEN Environment, pageGroup; label: PilotDisk.Label; op: LabelOperation ← [writeLabel, @file, @pageGroup, emptyPage, @label]; DataPage: TYPE = LONG POINTER TO ARRAY [0..wordsPerPage) OF WORD; LOOPHOLE[emptyPagePtr, DataPage]↑ ← ALL[0]; status ← Perform[@op, handleErrors].status; END; -- This is an imperfect simulation for passing expectErrorAfterFirstPage -- down the old disk channel (lacks recalibrates at the least) VerifyLabels: PUBLIC ENTRY PROCEDURE [ file: FileInternal.Descriptor, pageGroup: FileInternal.PageGroup, expectErrorAfterFirstPage: BOOLEAN ← FALSE, handleErrors: BOOLEAN ← TRUE] RETURNS [countValid: File.PageCount, status: DiskChannel.CompletionStatus] = BEGIN label: PilotDisk.Label; op: LabelOperation ← [verifyLabel, @file, @pageGroup, emptyPage, @label]; THROUGH [0..10) DO [countValid, status] ← Perform[@op, handleErrors]; IF status = goodCompletion OR (expectErrorAfterFirstPage AND countValid # 0) THEN EXIT; ENDLOOP; END; ReadLabelAndData: PUBLIC ENTRY PROCEDURE [ file: FileInternal.Descriptor, filePage: File.PageNumber, volumePage: VolumeInternal.PageNumber, memoryPage: Environment.PageNumber, handleErrors: BOOLEAN] RETURNS [label: PilotDisk.Label, status: DiskChannel.CompletionStatus] = BEGIN pageGroup: FileInternal.PageGroup ← [filePage, volumePage, filePage + 1]; op: LabelOperation ← [readLabelAndData, @file, @pageGroup, memoryPage, @label]; status ← Perform[@op, handleErrors].status END; WriteLabelAndData: PUBLIC ENTRY PROCEDURE [ file: FileInternal.Descriptor, filePage: File.PageNumber, volumePage: VolumeInternal.PageNumber, memoryPage: Environment.PageNumber, bootChainLink: DiskChannel.Address, handleErrors: BOOLEAN] RETURNS[status: DiskChannel.CompletionStatus] = BEGIN label: PilotDisk.Label; pageGroup: FileInternal.PageGroup ← [ filePage: filePage, volumePage: volumePage, nextFilePage: filePage+1]; op: LabelOperation ← [writeLabelsAndData, @file, @pageGroup, memoryPage, @label]; status ← Perform[@op, handleErrors, chained, bootChainLink].status; END; Perform: INTERNAL PROCEDURE [ -- Should be combined with Initiate (above) operation: POINTER TO LabelOperation, handleErrors: BOOLEAN, labelType: {normal, chained} ← normal, bootChainLink: DiskChannel.Address ← NULL] RETURNS [countValid: File.PageCount, status: DiskChannel.CompletionStatus] = BEGIN count: File.PageCount ← operation.pageGroupPtr.nextFilePage-operation.pageGroupPtr.filePage; subVol: SubVolume.Handle; subVolumeFound: BOOLEAN; countValid ← 0; -- Initialize return values in case no I/O is necessary (count=0) status ← goodCompletion; IF logging THEN Log[Inline.LowHalf[count]]; WITH operation.fileP SELECT FROM remote => ERROR; -- can't transfer remote labels local => BEGIN [subVolumeFound, subVol] ← SubVolume.Find[ operation.fileP.volumeID, operation.pageGroupPtr.volumePage]; IF ~ subVolumeFound THEN ERROR; -- local volumes must be found IF count > 0 THEN BEGIN io: SubVolume.IO ← [ op: operation.operation, subVolume: subVol, subVolumePage: operation.pageGroupPtr.volumePage, memPage: operation.memoryPage, filePtr: operation.fileP, filePage: operation.pageGroupPtr.filePage, pageCount: count, fixedMemPage: operation.operation IN [readLabel..verifyLabel], chained: labelType = chained, link: bootChainLink]; SubVolume.StartIO[@io]; [countValid, status] ← FileTask.LabelWait[operation.label, handleErrors]; END; END; ENDCASE; END; END. February 28, 1979 9:28 AM Redell Create from FilePageTransferImpl and LabelTransferImpl. March 21, 1979 12:01 PM Redell Convert to Mesa 5.0. August 13, 1979 3:59 PM Redell Minor mods for boot-chain machinery. September 5, 1979 3:13 PM McJones AR1593: Add countInitiated result to Initiate. September 17, 1979 4:42 PM Forrest Change Read Root Label. October 17, 1979 8:41 PM Redell Add histogram. November 26, 1979 3:49 PM Gobbel Initial changes for runs of pages: SubVolume.StartIO takes count arg, always 1 for now. December 5, 1979 4:24 PM Gobbel Runs of pages for real. January 9, 1980 2:29 PM Gobbel Add countValid to VerifyLabels and Perform. January 30, 1980 3:16 PM Gobbel Remove Alto file stuff. August 14, 1980 9:54 AM McJones WriteLabelsAndData=>WriteLabelAndData; FilePageLabel=>PilotDisk. October 11, 1980 9:30 PM Forrest Add expectLabelCheck to VerifyLabels (and hack implementation until we come up with correct solution). January 14, 1981 9:17 AM Luniewski New LabelTransfer interface. February 11, 1981 4:47 PM Knutsen Initiate must MStore.Promise the pages, and indeed *before* starting the I/O. August 4, 1982 10:59 am Levin Log now has short CARDINAL parameter.