PhysicalVolumeImpl.mesa - access to physical and logical volumes
Copyright © 1985 by Xerox Corporation. All rights reserved.
Andrew Birrell December 7, 1983 4:49 pm
Levin, August 8, 1983 6:04 pm
Willie-sue, August 20, 1984 4:42:56 pm PDT
Bob Hagmann January 31, 1985 10:56:13 am PST
Russ Atkinson (RRA) May 14, 1985 12:49:48 pm PDT
DIRECTORY
BootFile USING [ DiskFileID, Location ],
Disk USING [ DoIO, Channel, DriveAttributes, InspectDiskShape, Label, labelCheck, NextChannel, ok, PageCount, PageNumber, Request, SameDrive, Status ],
DiskFace USING [ PageCount, wordsPerPage ],
File USING [ Error, RC, VolumeID ],
FileBackdoor USING [],
FileInternal USING [ InitRootPage, ReadRootPage, TranslateStatus, VolumeObject ],
VolumeFormat,
PhysicalVolume USING [ Credentials, PhysicalRC, SubVolumeDetails, SubVolumeDetailsObject, SubVolumes, SubVolumesObject ],
Process USING [ SecondsToTicks ],
VM USING [ AddressForPageNumber, Allocate, Free, PageNumberForAddress, Pin, Unpin, wordsPerPage ],
Rope USING [ Fetch, FromRefText, Length, ROPE ];
PhysicalVolumeImpl: CEDAR MONITOR
IMPORTS Disk, File--Error--, FileInternal, Process, Rope, VM
EXPORTS DiskFace--AbsID--, File, FileBackdoor, FileInternal--GetPhysicalLocation--, PhysicalVolume = {
ROPE: TYPE = Rope.ROPE;
This implementation currently doesn't handle correctly volumes going offline. (ADB November 22, 1983 2:30 pm).
******** Raw access to global pages of the volume ********
PageData: TYPE = LONG POINTER;
pagePages: INT = (DiskFace.wordsPerPage+VM.wordsPerPage-1)/VM.wordsPerPage;
--DiskFace.--AbsID: PUBLIC TYPE = VolumeFormat.AbsID;
--DiskFace.--Attributes: PUBLIC TYPE = VolumeFormat.Attributes;
PhysRootLabel: PROC [id: File.VolumeID] RETURNS [Disk.Label] = TRUSTED {
RETURN[ [
fileID: [abs[AbsID[id]]],
filePage: 0,
attributes: Attributes[physicalRoot],
dontCare: LOOPHOLE[LONG[0]]
] ]
};
BadPageTableLabel: PROC [id: File.VolumeID] RETURNS [label: Disk.Label] = TRUSTED {
label ← PhysRootLabel[id];
label.filePage ← 1;
};
CredentialsLabel: PROC RETURNS [Disk.Label] = TRUSTED {
I'd prefer using the pvID and a separate file type. For now, compatibility wins (ADB)
cannedID: MACHINE DEPENDENT RECORD[a,b,c,d,e: CARDINAL] =
[a: 076543B, b: 021076B, c: 054321B, d: 076543B, e: 021076B]; -- ask Roy!
RETURN[ [
fileID: [abs[AbsID[LOOPHOLE[cannedID]]]],
filePage: 0,
attributes: Attributes[LOOPHOLE[0]],
dontCare: LOOPHOLE[LONG[0]]
] ]
};
AccessPage: UNSAFE PROC [
channel: Disk.Channel,
page: Disk.PageNumber,
label: LONG POINTER TO Disk.Label,
data: PageData,
direction: { read, write },
verifyLabel: BOOL -- else read/write label depending on "direction" --] RETURNS [ status: Disk.Status ] = TRUSTED {
ENABLE UNWIND => VM.Unpin[[page: VM.PageNumberForAddress[data], count: pagePages]];
countDone: Disk.PageCount;
req: Disk.Request ← [
diskPage: page,
data: data,
command: [header: verify,
label: IF verifyLabel THEN verify ELSE (IF direction=read THEN read ELSE write),
data: IF direction=read THEN read ELSE write],
count: 1 ];
VM.Pin[[page: VM.PageNumberForAddress[data], count: pagePages]];
[status, countDone] ← Disk.DoIO[channel, label, @req];
VM.Unpin[[page: VM.PageNumberForAddress[data], count: pagePages]];
};
FreePage: UNSAFE PROC [data: PageData] = TRUSTED {
VM.Free[[page: VM.PageNumberForAddress[data], count: pagePages]] };
ReadPage: PROC [channel: Disk.Channel, page: Disk.PageNumber, label: LONG POINTER TO Disk.Label, verifyLabel: BOOL] RETURNS [ status: Disk.Status, data: PageData ] = TRUSTED {
data ← VM.AddressForPageNumber[VM.Allocate[pagePages].page];
status ← AccessPage[channel, page, label, data, read, verifyLabel];
IF status # Disk.ok THEN { FreePage[data]; data ← NIL };
};
ReadPhysicalRoot: PROC [channel: Disk.Channel] RETURNS [ status: Disk.Status, root: LONG POINTER TO VolumeFormat.PhysicalRoot ] = TRUSTED {
label: Disk.Label;
data: PageData;
[status, data] ← ReadPage[channel, VolumeFormat.rootPageNumber, @label, FALSE];
root ← LOOPHOLE[data];
IF status = Disk.ok THEN {
Need to do a second transfer to get the label verify. Beware of Dolphin 8 word labels!
label ← PhysRootLabel[root.pvID];
status ← AccessPage[channel, VolumeFormat.rootPageNumber, @label, data, read, TRUE];
IF status # Disk.ok THEN { FreePage[data]; root ← NIL };
};
};
WriteRoot: INTERNAL PROC [physical: Physical, verifyLabel: BOOLTRUE] RETURNS [ PhysicalVolume.PhysicalRC ] = TRUSTED {
label: Disk.Label ← PhysRootLabel[physical.root.pvID];
status: Disk.Status;
status ← AccessPage[physical.channel, VolumeFormat.rootPageNumber,
@label, physical.root, write, verifyLabel];
RETURN[ FileInternal.TranslateStatus[status] ]
};
badPageNumber: Disk.PageNumber = [VolumeFormat.rootPageNumber+1];
ReadBadPageTable: INTERNAL PROC [physical: Physical] RETURNS [PhysicalVolume.PhysicalRC] = TRUSTED {
status: Disk.Status;
label: Disk.Label ← BadPageTableLabel[physical.root.pvID];
[status, physical.bad] ←
ReadPage[physical.channel, badPageNumber, @label, TRUE];
RETURN[FileInternal.TranslateStatus[status]]
};
WriteBadPageTable: INTERNAL PROC [physical: Physical, verifyLabel: BOOLTRUE] RETURNS [PhysicalVolume.PhysicalRC] = TRUSTED {
status: Disk.Status;
label: Disk.Label ← BadPageTableLabel[physical.root.pvID];
status ←
AccessPage[physical.channel, badPageNumber, @label, physical.bad, write, verifyLabel];
RETURN[FileInternal.TranslateStatus[status]]
};
ReadCredentials: PUBLIC UNSAFE PROC [physical: Physical, credentials: PhysicalVolume.Credentials] RETURNS [ PhysicalVolume.PhysicalRC ] = UNCHECKED {
label: Disk.Label ← CredentialsLabel[];
status: Disk.Status = AccessPage[physical.channel, VolumeFormat.credentialsPageNumber,
@label, credentials, read, TRUE];
RETURN[ FileInternal.TranslateStatus[status] ]
};
WriteCredentials: PUBLIC PROC [physical: Physical, credentials: PhysicalVolume.Credentials] RETURNS [ PhysicalVolume.PhysicalRC ] = TRUSTED {
label: Disk.Label ← CredentialsLabel[];
status: Disk.Status = AccessPage[physical.channel, VolumeFormat.credentialsPageNumber,
@label, credentials, write, FALSE--write label unconditionally--];
RETURN[ FileInternal.TranslateStatus[status] ]
};
******** Global list of Physical Volumes ******** --
--PhysicalVolume.--PhysicalObject: PUBLIC TYPE = RECORD[
channel: Disk.Channel,
rootStatus: PhysicalVolume.PhysicalRC ← ok,
root: LONG POINTER TO VolumeFormat.PhysicalRoot ← NIL,
bad: LONG POINTER TO VolumeFormat.BadPageList ← NIL,
name: ROPENIL,
reserved: ReservedList ← NIL,
rest: REF PhysicalObject ← NIL ];
ReservedList: TYPE = LIST OF RECORD[start: Disk.PageNumber, size: Disk.PageCount];
Physical: TYPE = REF PhysicalObject;
physicalList: REF PhysicalObject ← NIL; -- known physical volumes --
NextPhysical: PUBLIC ENTRY PROC [prev: Physical, wait: BOOL] RETURNS [next: Physical] = TRUSTED {
ENABLE UNWIND => NULL;
WHILE (next ← InnerNextPhysical[prev]) = NIL AND wait DO WAIT physicalWait ENDLOOP;
};
physicalWait: CONDITION ← [timeout: Process.SecondsToTicks[1]];
Broadcast on new physical volume; timeout causes poll for new channel.
lastChannel: Disk.Channel ← NIL;
InnerNextPhysical: INTERNAL PROC [prev: Physical] RETURNS [next: Physical] = TRUSTED {
DO
next ← IF prev = NIL THEN physicalList ELSE prev.rest;
IF next # NIL THEN EXIT;
{
new: Disk.Channel = Disk.NextChannel[lastChannel];
IF new # NIL
THEN { RegisterPhysical[NewPhysical[new]]; lastChannel ← new }
ELSE EXIT;
};
ENDLOOP;
};
RegisterPhysical: INTERNAL PROC [new: Physical] = {
tail: Physical ← NIL;
FOR p: Physical ← physicalList, p.rest UNTIL p = NIL DO tail ← p ENDLOOP;
IF tail = NIL THEN physicalList ← new ELSE tail.rest ← new;
BROADCAST physicalWait;
};
NewPhysical: INTERNAL PROC [channel: Disk.Channel] RETURNS [new: Physical] = TRUSTED {
status: Disk.Status;
new ← NEW[PhysicalObject ← [channel: channel]];
IF NOT Disk.InspectDiskShape[channel, quickReadOnly] THEN ERROR;
[status, new.root] ← ReadPhysicalRoot[channel];
new.rootStatus ← FileInternal.TranslateStatus[status];
IF new.rootStatus # ok THEN RETURN;
IF new.root.seal # VolumeFormat.PRSeal
OR new.root.version # VolumeFormat.PRCurrentVersion
THEN { new.rootStatus ← inconsistent; RETURN };
TRUSTED{ new.name ← GetName[@new.root.label, new.root.labelLength] };
now register the existence of its sub-volumes
FOR s: INT IN [0..new.root.subVolumeCount) DO
RegisterSubVolume[new, @new.root.subVolumes[s]];
ENDLOOP;
};
******** Operations on Physical root and bad page table ********
GetName: PROC [chars: LONG POINTER TO PACKED ARRAY [0..VolumeFormat.volumeLabelLength) OF CHAR, length: INT] RETURNS [ROPE] = TRUSTED {
text: REF TEXT = NEW[TEXT[length]];
FOR i: INT IN [0..length) DO text[i] ← chars[i] ENDLOOP;
text.length ← length;
RETURN[Rope.FromRefText[text]]
};
PhysicalInfo: PUBLIC ENTRY PROC [physical: Physical] RETURNS [ channel: Disk.Channel, rootStatus: PhysicalVolume.PhysicalRC, id: File.VolumeID, -- unique id of this physical volume -- name: ROPE, size: Disk.PageCount, free: Disk.PageCount, timeValid: BOOL, time: VolumeFormat.TimeParameters] = TRUSTED {
ENABLE UNWIND => NULL;
size ← Disk.DriveAttributes[physical.channel].nPages;
channel ← physical.channel;
rootStatus ← physical.rootStatus;
IF rootStatus = ok THEN {
Count free pages by repeated allocations. AllocatePages doesn't update anything!
origin: Disk.PageNumber ← [0];
free ← 0;
DO start: Disk.PageNumber;
thisTime: Disk.PageCount;
[start, thisTime] ← AllocatePages[physical, size, origin];
IF thisTime <= 0 THEN EXIT;
free ← free + thisTime - 1--don't report last page: it will be needed for a marker page--;
origin ← [start + thisTime];
ENDLOOP;
id ← physical.root.pvID;
name ← physical.name;
timeValid ← physical.root.timeParametersValid;
time ← physical.root.timeParameters;
};
};
--PhysicalVolume.--GetSubVolumes: PUBLIC ENTRY PROC [physical: Physical] RETURNS [sv: PhysicalVolume.SubVolumes] = TRUSTED {
ENABLE UNWIND => NULL;
IF physical.rootStatus # ok THEN RETURN WITH ERROR File.Error[physical.rootStatus];
sv ← NEW[PhysicalVolume.SubVolumesObject[physical.root.subVolumeCount]];
FOR i: CARDINAL IN [0..sv.count) DO
svd: VolumeFormat.SubVolumeDesc = physical.root.subVolumes[i];
sv[i] ← [id: svd.lvID,
start: svd.lvPage,
size: svd.nPages,
physical: physical,
channel: physical.channel,
address: svd.pvPage];
ENDLOOP;
};
--FileInternal.--GetPhysicalLocation: PUBLIC ENTRY PROC [physical: Physical, root: VolumeFormat.PVBootFile] RETURNS [location: BootFile.Location] = TRUSTED {
ENABLE UNWIND => NULL;
IF physical.rootStatus # ok THEN RETURN WITH ERROR File.Error[physical.rootStatus];
location.diskFileID ← physical.root.bootingInfo[root];
[type: location.deviceType, ordinal: location.deviceOrdinal] ←
Disk.DriveAttributes[physical.channel];
};
--FileInternal.--SetPhysicalLocation: PUBLIC ENTRY PROC [physical: Physical, root: VolumeFormat.PVBootFile, diskFileID: BootFile.DiskFileID] RETURNS [ rc: PhysicalVolume.PhysicalRC ] = TRUSTED {
ENABLE UNWIND => NULL;
IF physical.rootStatus # ok THEN RETURN[physical.rootStatus];
physical.root.bootingInfo[root] ← diskFileID;
rc ← WritePhysicalMarkers[physical, update];
IF rc = ok THEN rc ← WriteRoot[physical];
};
WriteTimeParameters: PUBLIC ENTRY PROC [physical: Physical, timeValid: BOOL, time: VolumeFormat.TimeParameters] RETURNS [ PhysicalVolume.PhysicalRC ] = TRUSTED {
ENABLE UNWIND => NULL;
IF physical.rootStatus # ok THEN RETURN[physical.rootStatus];
physical.root.timeParametersValid ← timeValid;
IF timeValid THEN physical.root.timeParameters ← time;
RETURN[ WriteRoot[physical] ]
};
PhysicalPageBad: PUBLIC ENTRY PROC [physical: Physical, address: Disk.PageNumber] RETURNS [ badTableFull: BOOL ← FALSE, status: PhysicalVolume.PhysicalRC ← ok ] = TRUSTED {
--FileBackdoor.--
ENABLE UNWIND => NULL;
IF physical.rootStatus # ok THEN RETURN[FALSE, physical.rootStatus];
IF physical.bad = NIL THEN status ← ReadBadPageTable[physical];
IF status = ok THEN
FOR i: Disk.PageCount IN [0..physical.root.badPageCount) DO
IF physical.bad[i] = address THEN EXIT;
REPEAT FINISHED => {
IF physical.root.badPageCount >= VolumeFormat.allocatedBadPages THEN RETURN[TRUE, ok];
physical.bad[physical.root.badPageCount] ← address;
status ← WriteBadPageTable[physical];
IF status = ok THEN {
physical.root.badPageCount ← physical.root.badPageCount+1;
status ← WriteRoot[physical];
IF status # ok THEN physical.root.badPageCount ← physical.root.badPageCount-1;
};
}
ENDLOOP;
};
--PhysicalVolume.--GetBadPages: PUBLIC ENTRY PROC [subVolume: PhysicalVolume.SubVolumeDetails, work: PROC [VolumeFormat.LogicalPage]] = TRUSTED {
need to read the bad page table and count from disk because they might have changed since the volume was full-booted
ENABLE UNWIND => NULL;
physical: Physical = subVolume.physical;
root: LONG POINTER TO VolumeFormat.PhysicalRoot;
data: PageData;
status: Disk.Status;
badPageCount: DiskFace.PageCount;
IF physical.rootStatus # ok THEN RETURN WITH ERROR File.Error[physical.rootStatus];
IF physical.bad = NIL THEN [] ← ReadBadPageTable[physical];
[status, root]← ReadPhysicalRoot[physical.channel];
IF status # Disk.ok THEN RETURN WITH ERROR File.Error[physical.rootStatus];
badPageCount← root.badPageCount;
FreePage[data← LOOPHOLE[root]];
physical.bad← NIL;
[] ← ReadBadPageTable[physical];
IF physical.bad # NIL THEN
FOR i: Disk.PageCount IN [0..badPageCount) DO
bad: Disk.PageNumber = physical.bad[i];
IF bad >= subVolume.address AND bad < subVolume.address + subVolume.size THEN
work[[bad-subVolume.address+subVolume.start]];
ENDLOOP;
};
******** Accumulation of sub-volumes into logical volumes ********
As logical sub-volumes are encountered (by physical volumes coming on-line, or by being created), they are accumulated in the list of "LogicalObject"s. Calling "GetNextLogical" will find if any "LogicalObject" describes a complete logical volume, and if so will (optionally) remove it from the list of "LogicalObject"s and return it.
LogicalObject: TYPE = RECORD[
id: File.VolumeID,
size: VolumeFormat.LogicalPageCount,
subVols: LIST OF PhysicalVolume.SubVolumeDetails ← NIL, -- ordered list of fragments --
rest: REF LogicalObject ← NIL ];
Logical: TYPE = REF LogicalObject;
logicalList: REF LogicalObject ← NIL;
logicalWait: CONDITION ← [timeout: Process.SecondsToTicks[1]];
Broadcast on new sub-volume; timeout causes poll for new channel.
RegisterSubVolume: INTERNAL PROC [physical: Physical, svd: LONG POINTER TO VolumeFormat.SubVolumeDesc] = TRUSTED {
l: Logical ← NIL;
prev: Logical ← NIL;
FOR l ← logicalList, l.rest UNTIL l = NIL DO
IF l.id = svd.lvID THEN EXIT;
prev ← l;
REPEAT FINISHED => {
l ← NEW[LogicalObject ← [id: svd.lvID, size: svd.lvSize, rest: NIL]];
IF prev = NIL THEN logicalList ← l ELSE prev.rest ← l;
};
ENDLOOP;
AddSubVolume[l, physical, svd];
};
AddSubVolume: INTERNAL PROC [l: Logical, physical: Physical, svd: LONG POINTER TO VolumeFormat.SubVolumeDesc] = TRUSTED {
this: PhysicalVolume.SubVolumeDetails = NEW[PhysicalVolume.SubVolumeDetailsObject ← [
id: svd.lvID,
start: svd.lvPage,
size: svd.nPages,
physical: physical,
channel: physical.channel,
address: svd.pvPage] ];
prev: LIST OF PhysicalVolume.SubVolumeDetails ← NIL;
BROADCAST logicalWait;
FOR sv: LIST OF PhysicalVolume.SubVolumeDetails ← l.subVols, sv.rest UNTIL sv = NIL DO
IF sv.first.start > this.start THEN {
insert before "sv"
IF this.start + this.size > sv.first.start THEN {
physical.rootStatus ← inconsistent;
RETURN };
IF prev = NIL
THEN l.subVols ← CONS[first: this, rest: sv]
ELSE prev.rest ← CONS[first: this, rest: sv];
EXIT
};
IF sv.first.start + sv.first.size > this.start THEN {
physical.rootStatus ← inconsistent;
RETURN };
prev ← sv;
REPEAT FINISHED =>
IF prev = NIL
THEN l.subVols ← CONS[first: this, rest: NIL]
ELSE prev.rest ← CONS[first: this, rest: NIL]
ENDLOOP;
};
lastPhysical: REF PhysicalObject ← NIL; -- last physical volume inspected by InnerNextLogical
NextLogical: INTERNAL PROC RETURNS [next: Logical] = {
First, check for any more physical volumes; InnerNextPhysical may call RegisterSubVolume
prev: Logical ← NIL;
FOR p: Physical ← InnerNextPhysical[lastPhysical].next, InnerNextPhysical[p].next UNTIL p = NIL DO
lastPhysical ← p;
ENDLOOP;
Search for complete logical volumes
FOR next ← logicalList, next.rest UNTIL next = NIL DO
wanted: INT ← 0;
check entire volume is known --
FOR sv: LIST OF PhysicalVolume.SubVolumeDetails ← next.subVols, sv.rest UNTIL sv = NIL DO
IF wanted # sv.first.start THEN EXIT;
wanted ← wanted + sv.first.size;
ENDLOOP;
IF wanted = next.size THEN EXIT;
prev ← next;
ENDLOOP;
IF next # NIL THEN {
IF prev = NIL THEN logicalList ← next.rest ELSE prev.rest ← next.rest;
next.rest ← NIL;
};
};
******** Global list of File.Volume objects ********
Volume: TYPE = REF VolumeObject;
--File.--VolumeObject: PUBLIC TYPE = FileInternal.VolumeObject;
volumeList: Volume ← NIL; -- known volumes --
--File.--NextVolume: PUBLIC ENTRY PROC [volume: Volume, wait: BOOLFALSE] RETURNS [next: Volume] = {
ENABLE UNWIND => NULL;
DO
next ← IF volume = NIL THEN volumeList ELSE volume.rest;
IF next # NIL THEN EXIT;
{
new: Logical = NextLogical[];
IF new # NIL
THEN FoundVolume[new]
ELSE { IF wait THEN WAIT logicalWait ELSE EXIT };
}
ENDLOOP;
};
FoundVolume: INTERNAL PROC [logical: Logical] = {
volume: Volume = RegisterVolume[logical];
FileInternal.ReadRootPage[volume];
volume.initialised ← TRUE;
};
RegisterVolume: INTERNAL PROC [logical: Logical] RETURNS [volume: Volume] = {
Construct a File.VolumeObject from the sub-volumes accumulated in a "LogicalObject" and add it to the global list.
volume ← NEW[VolumeObject ← [id: logical.id, subVolumes: logical.subVols, size: logical.size]];
IF volumeList = NIL
THEN volumeList ← volume
ELSE FOR v: Volume ← volumeList, v.rest DO
IF v.rest = NIL THEN { v.rest ← volume; EXIT };
ENDLOOP;
};
******** Creation of physical volumes ********
--FileBackdoor.--CreatePhysicalVolume: PUBLIC ENTRY PROC [where: Disk.Channel, name: ROPE, id: File.VolumeID] RETURNS [new: Physical] = TRUSTED {
ENABLE UNWIND => NULL;
rc: PhysicalVolume.PhysicalRC ← ok;
old: Physical;
new ← NEW[PhysicalObject ← [channel: where, name: name]];
new.root ← LOOPHOLE[VM.AddressForPageNumber[VM.Allocate[pagePages].page]];
new.root^ ← [
pvID: id,
labelLength: MIN[name.Length[], VolumeFormat.volumeLabelLength],
subVolumeCount: 0,
subVolumes: ALL[NULL] ];
FOR i: CARDINAL IN [0..new.root.labelLength)
DO new.root.label[i] ← name.Fetch[i] ENDLOOP;
new.bad ← LOOPHOLE[VM.AddressForPageNumber[VM.Allocate[pagePages].page]];
FOR old ← InnerNextPhysical[NIL], InnerNextPhysical[old] UNTIL old = NIL DO
IF Disk.SameDrive[old.channel, where] THEN EXIT;
ENDLOOP;
IF old # NIL AND old.rootStatus = ok AND (old.bad # NIL OR ReadBadPageTable[old] = ok) THEN {
new.bad^ ← old.bad^;
new.root.badPageCount ← old.root.badPageCount;
};
IF old # NIL THEN AbolishPhysical[old];
rc ← WriteBadPageTable[new, FALSE--write label directly--];
IF rc = ok THEN rc ← WriteRoot[new, FALSE--write label directly--];
IF rc # ok THEN RETURN WITH ERROR File.Error[rc];
RegisterPhysical[new];
};
AbolishPhysical: INTERNAL PROC [old: Physical] = {
Remove physical volume and its sub-volumes from various lists
prevP: Physical ← NIL;
prevV: Volume ← NIL;
FOR p: Physical ← physicalList, p.rest UNTIL p = NIL DO
IF p = old
THEN { IF prevP = NIL THEN physicalList ← p.rest ELSE prevP.rest ← p.rest; EXIT }
ELSE prevP ← p;
ENDLOOP;
IF old.rootStatus # ok THEN RETURN;
FOR v: Volume ← volumeList, v.rest UNTIL v = NIL DO
match: BOOLFALSE;
prevSV: LIST OF PhysicalVolume.SubVolumeDetails ← NIL;
FOR sv: LIST OF PhysicalVolume.SubVolumeDetails ← v.subVolumes, sv.rest UNTIL sv = NIL DO
IF sv.first.physical = old
THEN { match ← TRUE;
IF prevSV = NIL THEN v.subVolumes ← sv.rest ELSE prevSV.rest ← sv.rest }
ELSE prevSV ← sv;
ENDLOOP;
IF match
THEN {
Remove from list of complete volumes, and build "LogicalObject" for remaining sv's
IF prevV = NIL THEN volumeList ← v.rest ELSE prevV.rest ← v.rest;
v.destroyed ← TRUE; -- tell clients volume object is no longer valid
IF v.subVolumes # NIL THEN
logicalList ← NEW[LogicalObject ← [id: v.id, size: v.size, subVols: v.subVolumes, rest: logicalList]];
}
ELSE prevV ← v;
ENDLOOP;
};
******** Creation of logical volumes ********
--FileBackdoor.--ReservePages: PUBLIC ENTRY PROC [physical: Physical, start: Disk.PageNumber, size: Disk.PageCount] = {
Called by high-level client to prevent allocation of various reserved areas of disk (initial microcode, Alto partitions)
ENABLE UNWIND => NULL;
prev: ReservedList ← NIL;
new: ReservedList = CONS[first: [start: start, size: size], rest: NIL];
IF physical.rootStatus # ok THEN RETURN WITH ERROR File.Error[physical.rootStatus];
FOR res: ReservedList ← physical.reserved, res.rest UNTIL res = NIL DO
IF res.first.start >= start THEN EXIT;
prev ← res;
ENDLOOP;
IF prev = NIL
THEN { new.rest ← physical.reserved; physical.reserved ← new }
ELSE { new.rest ← prev.rest; prev.rest ← new }
};
--FileBackdoor.--ReserveNoPages: PUBLIC ENTRY PROC [physical: Physical] = {
ENABLE UNWIND => NULL;
physical.reserved ← NIL;
};
FirstFreePage: INTERNAL PROC [physical: Physical] RETURNS [Disk.PageNumber] = TRUSTED {
IF physical.root.subVolumeCount = 0
THEN RETURN[[3]] -- 0=>root, 1=>badPageTable, 2=>credentials
ELSE {
lastSV: VolumeFormat.SubVolumeDesc =
physical.root.subVolumes[physical.root.subVolumeCount-1];
RETURN[[lastSV.pvPage + lastSV.nPages + 1--marker page--]]
};
};
AllocatePages: INTERNAL PROC [physical: Physical, wanted: Disk.PageCount, origin: Disk.PageNumber] RETURNS [start: Disk.PageNumber, size: Disk.PageCount] = TRUSTED {
Returns the first free run of pages, not exceeding the wanted size. This procedure assumes that "physical.reserved" is sorted by non-decreasing start address.
Loop 1: ensure start isn't inside a reserved area:
start ← IF origin = 0 THEN FirstFreePage[physical] ELSE origin;
FOR res: ReservedList ← physical.reserved, res.rest UNTIL res = NIL DO
IF start >= res.first.start AND start < res.first.start + res.first.size THEN
start ← [res.first.start + res.first.size];
ENDLOOP;
Loop 2: restrict size to not reach into a reserved area
size ← MIN[Disk.DriveAttributes[physical.channel].nPages - start, wanted];
FOR res: ReservedList ← physical.reserved, res.rest UNTIL res = NIL DO
IF start < res.first.start AND start+size >= res.first.start THEN
size ← res.first.start - start
ENDLOOP;
};
WriteSubVolume: INTERNAL PROC [logical: Logical, physical: Physical, lvPage: VolumeFormat.LogicalPage, start: Disk.PageNumber, size: Disk.PageCount--excludes marker page--] RETURNS [rc: PhysicalVolume.PhysicalRC] = TRUSTED {
svd: LONG POINTER TO VolumeFormat.SubVolumeDesc =
@physical.root.subVolumes[physical.root.subVolumeCount];
svd^ ← [
lvID: logical.id,
lvSize: logical.size,
lvPage: lvPage,
pvPage: start,
nPages: size];
rc ← WritePhysicalMarkers[physical, init];
IF rc # ok THEN RETURN;
physical.root.subVolumeCount ← physical.root.subVolumeCount+1;
rc ← WriteRoot[physical];
IF rc = ok
THEN AddSubVolume[logical, physical, svd]
ELSE physical.root.subVolumeCount ← physical.root.subVolumeCount-1;
};
--FileBackdoor.--CreateLogicalVolume: PUBLIC ENTRY PROC [where: LIST OF Physical, size: INT, name: ROPE, id: File.VolumeID] RETURNS [volume: Volume ← NIL] = {
ENABLE UNWIND => NULL;
rc: File.RC ← ok;
logical: Logical ← NEW[LogicalObject ← [id: id, size: size] ];
SubCreate: INTERNAL PROC [which: {check, doIt}] = TRUSTED {
wanted: INT ← size;
FOR p: LIST OF Physical ← where, p.rest UNTIL p = NIL OR wanted <= 0 DO
physical: Physical = p.first;
origin: Disk.PageNumber ← [0];
IF physical.rootStatus # ok THEN { rc ← physical.rootStatus; GOTO bad };
FOR i: CARDINAL IN [physical.root.subVolumeCount..VolumeFormat.maxSubVols) UNTIL wanted <= 0 DO
start: Disk.PageNumber;
thisTime: Disk.PageCount;
[start, thisTime] ← AllocatePages[physical, wanted+1--marker page--, origin];
IF thisTime <= 0 THEN EXIT;
IF thisTime > 1 THEN {
ignore single-page runs, because they only allow the marker page!
IF which = doIt THEN
rc ← WriteSubVolume[logical, physical, [size - wanted], start, thisTime-1];
IF rc # ok THEN GOTO bad;
wanted ← wanted - (thisTime - 1--marker page--);
};
origin ← [start + thisTime];
ENDLOOP;
ENDLOOP;
IF wanted > 0 THEN rc ← volumeFull;
EXITS bad => NULL
};
SubCreate[check]; -- don't do anything to the permanent data structures yet!
IF rc = ok THEN {
SubCreate[doIt];
volume ← RegisterVolume[logical];
rc ← FileInternal.InitRootPage[volume, name];
};
IF rc # ok THEN RETURN WITH ERROR File.Error[rc];
};
******** Marker Page Stuff ********
MarkerPage: TYPE = MACHINE DEPENDENT RECORD[ -- This should be in VolumeFormat
logical (0): VolumeFormat.LogicalSubvolumeMarker,
fill1 (SIZE[VolumeFormat.LogicalSubvolumeMarker]):
ARRAY [SIZE[VolumeFormat.LogicalSubvolumeMarker]..200B) OF WORDALL[0],
physical(200B): VolumeFormat.PhysicalMarker,
fill2 (200B+SIZE[VolumeFormat.PhysicalMarker]):
ARRAY [200B+SIZE[VolumeFormat.PhysicalMarker]..377B) OF WORDALL[0],
checksum(377B): CARDINAL ← 0
];
WritePhysicalMarkers: INTERNAL PROC [physical: Physical, which: {init, update}] RETURNS [rc: PhysicalVolume.PhysicalRC ← ok] = {
};
--FileInternal.--WriteLogicalMarkers: PUBLIC ENTRY PROC [volume: Volume] RETURNS [rc: File.RC ← ok] = {
ENABLE UNWIND => NULL;
};
--FileInternal.--InitLogicalMarkers: PUBLIC INTERNAL PROC [volume: Volume] RETURNS [rc: File.RC ← ok] = {
NOTE: this is called only from inside FileInternal.InitRootPage, which is called from CreateLogicalVolume, which is inside our monitor. Hence this is "INTERNAL". This mess is caused by the physical and logical marker info being on a single page.
};
}.
Bob Hagmann January 29, 1985 1:55:55 pm PST
Cedar 6.0 conversion- change to FileBackdoor interface