-- 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.