Cedar Nucleus (Files): access to physical and logical volumes
PhysicalVolumeImpl.mesa
Andrew Birrell December 7, 1983 4:49 pm
Last Edited by: Levin, August 8, 1983 6:04 pm
DIRECTORY
BootFile USING[ DiskFileID, Location ],
Disk USING[ DoIO, Channel, DriveAttributes, InspectDiskShape, Label, labelCheck, NextChannel, ok, PageCount, PageNumber, Request, SameDrive, Status ],
DiskFace USING[ wordsPerPage ],
File USING[ Error, RC, VolumeID ],
FileExtra 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, FileExtra, FileInternal--GetPhysicalLocation--, PhysicalVolume =
BEGIN
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
BEGIN
RETURN[ [
fileID: [abs[AbsID[id]]],
filePage: 0,
attributes: Attributes[physicalRoot],
dontCare: LOOPHOLE[LONG[0]]
] ]
END;
BadPageTableLabel:
PROC[id: File.VolumeID]
RETURNS[label: Disk.Label] =
TRUSTED
BEGIN
label ← PhysRootLabel[id];
label.filePage ← 1;
END;
CredentialsLabel:
PROC
RETURNS[Disk.Label] =
TRUSTED
BEGIN
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]]
] ]
END;
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
BEGIN
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]];
END;
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
BEGIN
data ← VM.AddressForPageNumber[VM.Allocate[pagePages].page];
status ← AccessPage[channel, page, label, data, read, verifyLabel];
IF status # Disk.ok THEN { FreePage[data]; data ← NIL };
END;
ReadPhysicalRoot:
PROC[channel: Disk.Channel]
RETURNS[ status: Disk.Status, root: LONG POINTER TO VolumeFormat.PhysicalRoot ] = TRUSTED
BEGIN
label: Disk.Label;
data: PageData;
[status, data] ← ReadPage[channel, VolumeFormat.rootPageNumber, @label, FALSE];
root ← LOOPHOLE[data];
IF status = Disk.ok
THEN
BEGIN
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 };
END;
END;
WriteRoot:
INTERNAL
PROC[physical: Physical, verifyLabel:
BOOL ←
TRUE]
RETURNS[ PhysicalVolume.PhysicalRC ] =
TRUSTED
BEGIN
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] ]
END;
badPageNumber: Disk.PageNumber = [VolumeFormat.rootPageNumber+1];
ReadBadPageTable:
INTERNAL
PROC[physical: Physical]
RETURNS[PhysicalVolume.PhysicalRC] =
TRUSTED
BEGIN
status: Disk.Status;
label: Disk.Label ← BadPageTableLabel[physical.root.pvID];
[status, physical.bad] ←
ReadPage[physical.channel, badPageNumber, @label, TRUE];
RETURN[FileInternal.TranslateStatus[status]]
END;
WriteBadPageTable:
INTERNAL
PROC[physical: Physical, verifyLabel:
BOOL ←
TRUE]
RETURNS[PhysicalVolume.PhysicalRC] =
TRUSTED
BEGIN
status: Disk.Status;
label: Disk.Label ← BadPageTableLabel[physical.root.pvID];
status ←
AccessPage[physical.channel, badPageNumber, @label, physical.bad, write, verifyLabel];
RETURN[FileInternal.TranslateStatus[status]]
END;
ReadCredentials:
PUBLIC
UNSAFE
PROC[physical: Physical, credentials: PhysicalVolume.Credentials]
RETURNS[ PhysicalVolume.PhysicalRC ] =
UNCHECKED
BEGIN
label: Disk.Label ← CredentialsLabel[];
status: Disk.Status = AccessPage[physical.channel, VolumeFormat.credentialsPageNumber,
@label, credentials, read, TRUE];
RETURN[ FileInternal.TranslateStatus[status] ]
END;
WriteCredentials:
PUBLIC
PROC[physical: Physical, credentials: PhysicalVolume.Credentials]
RETURNS[ PhysicalVolume.PhysicalRC ] =
TRUSTED
BEGIN
label: Disk.Label ← CredentialsLabel[];
status: Disk.Status = AccessPage[physical.channel, VolumeFormat.credentialsPageNumber,
@label, credentials, write, FALSE--write label unconditionally--];
RETURN[ FileInternal.TranslateStatus[status] ]
END;
******** 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: Rope.ROPE ← NIL,
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
BEGIN
ENABLE UNWIND => NULL;
WHILE (next ← InnerNextPhysical[prev]) = NIL AND wait DO WAIT physicalWait ENDLOOP;
END;
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
BEGIN
DO next ←
IF prev =
NIL
THEN physicalList
ELSE prev.rest;
IF next # NIL THEN EXIT;
BEGIN
new: Disk.Channel = Disk.NextChannel[lastChannel];
IF new # NIL
THEN { RegisterPhysical[NewPhysical[new]]; lastChannel ← new }
ELSE EXIT;
END;
ENDLOOP;
END;
RegisterPhysical:
INTERNAL
PROC[new: Physical] =
BEGIN
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;
END;
NewPhysical:
INTERNAL
PROC[channel: Disk.Channel]
RETURNS[new: Physical] =
TRUSTED
BEGIN
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;
END;
******** 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.ROPE] = TRUSTED
BEGIN
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]]
END;
PhysicalInfo:
PUBLIC
ENTRY
PROC[physical: Physical]
RETURNS[
channel: Disk.Channel,
rootStatus: PhysicalVolume.PhysicalRC,
id: File.VolumeID, -- unique id of this physical volume --
name: Rope.ROPE,
size: Disk.PageCount,
free: Disk.PageCount,
timeValid: BOOL,
time: VolumeFormat.TimeParameters] = TRUSTED
BEGIN
ENABLE UNWIND => NULL;
size ← Disk.DriveAttributes[physical.channel].nPages;
channel ← physical.channel;
rootStatus ← physical.rootStatus;
IF rootStatus = ok
THEN
BEGIN
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;
END;
END;
--PhysicalVolume.--
GetSubVolumes:
PUBLIC
ENTRY
PROC[physical: Physical]
RETURNS[sv: PhysicalVolume.SubVolumes] =
TRUSTED
BEGIN
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;
END;
--FileInternal.--
GetPhysicalLocation:
PUBLIC
ENTRY
PROC[physical: Physical, root: VolumeFormat.PVBootFile]
RETURNS[location: BootFile.Location] =
TRUSTED
BEGIN
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];
END;
--FileInternal.--
SetPhysicalLocation:
PUBLIC
ENTRY
PROC[physical: Physical, root: VolumeFormat.PVBootFile, diskFileID: BootFile.DiskFileID]
RETURNS[ rc: PhysicalVolume.PhysicalRC ] =
TRUSTED
BEGIN
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];
END;
WriteTimeParameters:
PUBLIC
ENTRY
PROC[physical: Physical, timeValid:
BOOL, time: VolumeFormat.TimeParameters]
RETURNS[ PhysicalVolume.PhysicalRC ] =
TRUSTED
BEGIN
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] ]
END;
--PhysicalVolume.--
PhysicalPageBad:
PUBLIC
ENTRY
PROC[physical: Physical, address: Disk.PageNumber]
RETURNS[ status: PhysicalVolume.PhysicalRC ← ok ] =
TRUSTED
BEGIN
ENABLE UNWIND => NULL;
IF physical.rootStatus # ok THEN RETURN[physical.rootStatus];
IF physical.bad = NIL THEN status ← ReadBadPageTable[physical];
IF status = ok
THEN
BEGIN
FOR i: Disk.PageCount IN [0..physical.root.badPageCount)
DO IF physical.bad[i] = address THEN EXIT;
REPEAT
FINISHED =>
BEGIN
physical.bad[physical.root.badPageCount] ← address;
status ← WriteBadPageTable[physical];
IF status = ok
THEN
BEGIN
physical.root.badPageCount ← physical.root.badPageCount+1;
status ← WriteRoot[physical];
IF status # ok THEN physical.root.badPageCount ← physical.root.badPageCount-1;
END;
END
ENDLOOP;
END;
END;
--PhysicalVolume.--
GetBadPages:
PUBLIC
ENTRY
PROC[subVolume: PhysicalVolume.SubVolumeDetails, work:
PROC[VolumeFormat.LogicalPage]] =
TRUSTED
BEGIN
ENABLE UNWIND => NULL;
physical: Physical = subVolume.physical;
IF physical.rootStatus # ok THEN RETURN WITH ERROR File.Error[physical.rootStatus];
IF physical.bad = NIL THEN [] ← ReadBadPageTable[physical];
IF physical.bad # NIL
THEN
FOR i: Disk.PageCount
IN [0..physical.root.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;
END;
******** 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
BEGIN
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 =>
BEGIN
l ← NEW[LogicalObject ← [id: svd.lvID, size: svd.lvSize, rest: NIL]];
IF prev = NIL THEN logicalList ← l ELSE prev.rest ← l;
END;
ENDLOOP;
AddSubVolume[l, physical, svd];
END;
AddSubVolume:
INTERNAL
PROC[l: Logical, physical: Physical, svd:
LONG
POINTER
TO VolumeFormat.SubVolumeDesc] =
TRUSTED
BEGIN
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
BEGIN
-- 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
END;
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;
END;
lastPhysical: REF PhysicalObject ← NIL; -- last physical volume inspected by InnerNextLogical
NextLogical:
INTERNAL
PROC
RETURNS[next: Logical] =
BEGIN
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
BEGIN
IF prev = NIL THEN logicalList ← next.rest ELSE prev.rest ← next.rest;
next.rest ← NIL;
END;
END;
******** 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:
BOOL ←
FALSE]
RETURNS[next: Volume] =
BEGIN
ENABLE UNWIND => NULL;
DO next ←
IF volume =
NIL
THEN volumeList
ELSE volume.rest;
IF next # NIL THEN EXIT;
BEGIN
new: Logical = NextLogical[];
IF new # NIL
THEN FoundVolume[new]
ELSE { IF wait THEN WAIT logicalWait ELSE EXIT };
END
ENDLOOP;
END;
FoundVolume:
INTERNAL
PROC[logical: Logical] =
BEGIN
volume: Volume = RegisterVolume[logical];
FileInternal.ReadRootPage[volume];
volume.initialised ← TRUE;
END;
RegisterVolume:
INTERNAL
PROC[logical: Logical]
RETURNS[volume: Volume] =
BEGIN
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;
END;
******** Creation of physical volumes ********
--PhysicalVolume.--
CreatePhysicalVolume:
PUBLIC
ENTRY
PROC[where: Disk.Channel, name: Rope.
ROPE, id: File.VolumeID]
RETURNS[new: Physical] =
TRUSTED
BEGIN
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];
END;
AbolishPhysical:
INTERNAL
PROC[old: Physical] =
BEGIN
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:
BOOL ←
FALSE;
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
BEGIN
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]];
END
ELSE prevV ← v;
ENDLOOP;
END;
******** Creation of logical volumes ********
--PhysicalVolume.--
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)
BEGIN
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 }
END;
--PhysicalVolume.--
ReserveNoPages:
PUBLIC
ENTRY
PROC[physical: Physical] =
{ ENABLE UNWIND => NULL; physical.reserved ← NIL };
FirstFreePage:
INTERNAL
PROC[physical: Physical]
RETURNS[Disk.PageNumber] =
TRUSTED
BEGIN
IF physical.root.subVolumeCount = 0
THEN RETURN[[3]] -- 0=>root, 1=>badPageTable, 2=>credentials
ELSE
BEGIN
lastSV: VolumeFormat.SubVolumeDesc =
physical.root.subVolumes[physical.root.subVolumeCount-1];
RETURN[[lastSV.pvPage + lastSV.nPages + 1--marker page--]]
END;
END;
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.
BEGIN
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;
END;
WriteSubVolume:
INTERNAL
PROC[logical: Logical, physical: Physical, lvPage: VolumeFormat.LogicalPage, start: Disk.PageNumber, size: Disk.PageCount
--excludes marker page--]
RETURNS[rc: PhysicalVolume.PhysicalRC] =
TRUSTED
BEGIN
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;
END;
--PhysicalVolume.--
CreateLogicalVolume:
PUBLIC
ENTRY
PROC[where:
LIST
OF Physical, size:
INT, name: Rope.
ROPE, id: File.VolumeID]
RETURNS[volume: Volume ←
NIL] =
BEGIN
ENABLE UNWIND => NULL;
rc: File.RC ← ok;
logical: Logical ← NEW[LogicalObject ← [id: id, size: size] ];
SubCreate:
INTERNAL
PROC[which: {check, doIt}] =
TRUSTED
BEGIN
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 -- ignore single-page runs, because they only allow the marker page!
THEN BEGIN
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--);
END;
origin ← [start + thisTime];
ENDLOOP;
ENDLOOP;
IF wanted > 0 THEN rc ← volumeFull;
EXITS bad => NULL
END;
SubCreate[check]; -- don't do anything to the permanent data structures yet!
IF rc = ok
THEN
BEGIN
SubCreate[doIt];
volume ← RegisterVolume[logical];
rc ← FileInternal.InitRootPage[volume, name];
END;
IF rc # ok THEN RETURN WITH ERROR File.Error[rc];
END;
******** 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 WORD ← ALL[0],
physical(200B): VolumeFormat.PhysicalMarker,
fill2 (200B+
SIZE[VolumeFormat.PhysicalMarker]):
ARRAY [200B+SIZE[VolumeFormat.PhysicalMarker]..377B) OF WORD ← ALL[0],
checksum(377B): CARDINAL ← 0
];
WritePhysicalMarkers:
INTERNAL
PROC[physical: Physical, which: {init, update}]
RETURNS[rc: PhysicalVolume.PhysicalRC ← ok] =
BEGIN
END;
--FileInternal.--
WriteLogicalMarkers:
PUBLIC
ENTRY
PROC[volume: Volume]
RETURNS[rc: File.
RC ← ok] =
BEGIN
ENABLE UNWIND => NULL;
END;
--FileInternal.--
InitLogicalMarkers:
PUBLIC
INTERNAL
PROC[volume: Volume]
RETURNS[rc: File.
RC ← ok] =
BEGIN
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.
END;
END.