VMBackingImpl.mesa
last edited by Levin on September 20, 1983 12:37 pm
Last Edited by: Birrell, July 27, 1983 5:43 pm
DIRECTORY
DebuggerFormat USING [LabelChecksum, Run, VMRunTable],
DebuggerSwap USING [NoteVMRunTable],
Disk USING [
Add, Channel, DriveAttributes, DoIO, Label, labelCheck, ok, PageNumber, Request, SameDrive, Status],
VM USING [AddressForPageNumber, lowCore],
VMBacking USING [Run, RunTable, RunTableIndex, RunTableObject, RunTablePageNumber],
VMInternal USING [
Crash, Interval, IODirection, IOResult, PageCount, PageNumber, UpdateVMLimit];
VMBackingImpl: MONITOR
IMPORTS DebuggerFormat, DebuggerSwap, Disk, VM, VMInternal
EXPORTS VMBacking, VMInternal =
BEGIN
OPEN VMInternal, VMBacking;
Global variables protected by the monitor
backingLabel: Disk.Label;
firstBackingDataPage: RunTablePageNumber;
backingRunTable: LONG POINTER TO RunTableObject ← NIL;
debuggerRunTable: LONG POINTER TO DebuggerFormat.VMRunTable ← NIL;
Exports to VMBacking
AttachBackingStorage: PUBLIC ENTRY UNSAFE PROC [
label: Disk.Label, firstDataPage: RunTablePageNumber, runTable: RunTable] = {
Eventually, a more general mechanism may be necessary to permit mapped files. For now, we support only a single run.
IF backingRunTable ~= NIL THEN Crash[];
backingLabel ← label;
firstBackingDataPage ← firstDataPage;
runTable.nRuns is the number of extents, but (see comment in interface) the run table is assumed to contain runTable.nRuns+1 entries.
backingRunTable ← VM.lowCore.NEW[RunTableObject[runTable.nRuns+1]];
backingRunTable.nDataPages ← runTable.nDataPages;
backingRunTable.nRuns ← runTable.nRuns;
FOR i: RunTableIndex IN [RunTableIndex.FIRST..runTable.nRuns] DO
backingRunTable.runs[i] ← runTable.runs[i];
ENDLOOP;
debuggerRunTable ← VM.lowCore.NEW[DebuggerFormat.VMRunTable[runTable.nRuns]];
debuggerRunTable.nRuns ← runTable.nRuns;
FOR j: RunTableIndex IN [RunTableIndex.FIRST..runTable.nRuns) DO
IF firstDataPage IN [runTable.runs[j].filePage..runTable.runs[j+1].filePage) THEN {
FOR i: RunTableIndex IN [j..runTable.nRuns) DO
debuggerLabel: Disk.Label ← label;
filePage: RunTablePageNumber = MAX[runTable.runs[i].filePage, firstDataPage];
debuggerLabel.filePage ← filePage;
debuggerRunTable.runs[i-j] ← [
page: filePage-firstDataPage,
count:
IF i = runTable.nRuns-1 THEN runTable.nDataPages-(filePage-firstDataPage)
ELSE runTable.runs[i+1].filePage-filePage,
deviceType: Disk.DriveAttributes[runTable.runs[i].channel].type,
deviceOrdinal: Disk.DriveAttributes[runTable.runs[i].channel].ordinal,
diskPage: [runTable.runs[i].diskPage+filePage-runTable.runs[i].filePage],
labelCheck: DebuggerFormat.LabelChecksum[debuggerLabel,0]
];
ENDLOOP;
EXIT
};
REPEAT
FINISHED => Crash[];
ENDLOOP;
DebuggerSwap.NoteVMRunTable[debuggerRunTable];
UpdateVMLimit[backingRunTable.nDataPages];
};
Exports to VMInternal
DoIO: PUBLIC UNSAFE PROC [
direction: IODirection, backingPage: PageNumber, interval: Interval, subsequentSeek: PageNumber]
RETURNS [result: IOResult, done: PageCount] = {
Note: The implementation assumes that the disk page size equals the map unit size. If this is not the case, code is required to read multiple disk pages per map unit, worry about discontinuities in the middle of map units, end-of-file in the middle of a map unit, and other funny cases. We'll write this when, and if, we need to.
request: Disk.Request;
channel: Disk.Channel;
diskPage: Disk.PageNumber;
label: Disk.Label;
runLength: PageCount;
status: Disk.Status;
[channel: channel, diskPage: diskPage, runLength: runLength] ←
MapToBackingStorage[backingPage, @label];
request ← [
diskPage: diskPage,
data: VM.AddressForPageNumber[interval.page],
command:
IF direction = read THEN [header: verify, label: verify, data: read]
ELSE [header: verify, label: verify, data: write],
count: MIN[runLength, interval.count]
];
IF INT[request.count] ~= interval.count THEN subsequentSeek ← interval.page + request.count;
IF subsequentSeek ~= 0 THEN {
seekChannel: Disk.Channel;
[channel: seekChannel, diskPage: diskPage] ← MapToBackingStorage[subsequentSeek];
IF Disk.SameDrive[channel, seekChannel] THEN request.seek ← diskPage;
};
Initiate the transfer and wait for it to complete
[status, done] ← Disk.DoIO[channel, @label, @request];
result ← SELECT status FROM
Disk.ok => ok,
Disk.labelCheck => labelCheck,
ENDCASE => someOtherError;
};
HasBackingStorage: PUBLIC ENTRY SAFE PROC [page: PageNumber]
RETURNS [BOOL] = TRUSTED {
RETURN[backingRunTable ~= NIL AND page < backingRunTable.nDataPages];
};
Internal Procedures
MapToBackingStorage: PROC [page: PageNumber, label: POINTER TO Disk.Label ← NIL]
RETURNS [channel: Disk.Channel, diskPage: Disk.PageNumber, runLength: CARDINAL] = {
IF HasBackingStorage[page] THEN {
filePage: RunTablePageNumber = firstBackingDataPage + page;
low: RunTableIndex ← 0;
high: RunTableIndex ← backingRunTable.nRuns; -- "end marker"
nearest: RunTableIndex;
nearestRun: Run;
UNTIL low > high DO
nearest ← (low + high) / 2;
SELECT backingRunTable.runs[nearest].filePage FROM
< filePage => low ← nearest + 1;
> filePage => high ← nearest - 1;
ENDCASE -- = filePage -- => EXIT;
REPEAT
FINISHED => nearest ← low - 1;
ENDLOOP;
nearestRun ← backingRunTable[nearest];
IF label ~= NIL THEN {
label^ ← backingLabel;
label.filePage ← filePage - firstBackingDataPage + label.filePage;
};
RETURN[
channel: nearestRun.channel,
diskPage: Disk.Add[nearestRun.diskPage, filePage - nearestRun.filePage],
runLength: backingRunTable[nearest+1].filePage - filePage
]
}
ELSE Crash[]; -- no backing storage
};
END.