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:
BOOL ←
TRUE]
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:
BOOL ←
TRUE]
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: 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 {
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:
BOOL ←
FALSE]
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: 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 {
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 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] = {
};
--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