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: BOOLTRUE] 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: BOOLTRUE] 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.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
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: BOOLFALSE]
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: 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 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 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] =
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.