DIRECTORY
Disk USING[ DoIO, Channel, InspectDiskShape, Label, labelCheck, NextChannel, ok, PageCount, PageNumber, Request, Status, Valid ],
DiskFace USING[ wordsPerPage ],
NewFile USING[ VolumeID ],
FileInternal USING[ ReadRootPage, VolumeObject ],
VolumeFormat,
Volumes USING[ SubVolumeDetails, SubVolumeDetailsObject ],
VM USING[ AddressToPageNumber, Allocate, Free, PageNumberToAddress, Pin, Unpin, wordsPerPage ];
PageData: TYPE = LONG POINTER;
pagePages: INT = (DiskFace.wordsPerPage+VM.wordsPerPage-1)/VM.wordsPerPage;
AccessPage:
UNSAFE
PROC[channel: Disk.Channel,
page: Disk.PageNumber,
label: REF Disk.Label,
data: PageData,
direction: { read, write },
verifyLabel: BOOL --else read label--]
RETURNS[ status: Disk.Status ] = TRUSTED
BEGIN
ENABLE UNWIND => VM.Unpin[[page: VM.AddressToPageNumber[data], count: pagePages]];
countDone: Disk.PageCount;
req: Disk.Request ← [
diskPage: page,
data: data,
command: [header: verify,
label: IF verifyLabel THEN verify ELSE read,
data: IF direction=read THEN read ELSE write],
count: 1 ];
VM.Pin[[page: VM.AddressToPageNumber[data], count: pagePages]];
[status, countDone] ← Disk.DoIO[channel, label, @req];
VM.Unpin[[page: VM.AddressToPageNumber[data], count: pagePages]];
END;
FreePage:
UNSAFE
PROC[data: PageData] =
TRUSTED
{ VM.Free[[page: VM.AddressToPageNumber[data], count: pagePages]] };
ReadPage:
PROC[channel: Disk.Channel,
page: Disk.PageNumber,
label: REF Disk.Label,
verifyLabel: BOOL]
RETURNS[ status: Disk.Status, data: PageData ] = TRUSTED
BEGIN
data ← VM.PageNumberToAddress[VM.Allocate[pagePages].page];
status ← AccessPage[channel, page, label, data, read, verifyLabel];
IF status # Disk.ok THEN { FreePage[data]; data ← NIL };
END;
PhysRoot: TYPE = LONG POINTER TO VolumeFormat.PhysicalRoot;
--DiskFace.--AbsID: PUBLIC TYPE = VolumeFormat.AbsID;
--DiskFace.--Attributes: PUBLIC TYPE = VolumeFormat.Attributes;
PhysRootLabel:
PROC[id: NewFile.VolumeID]
RETURNS[
REF Disk.Label] =
TRUSTED
BEGIN
RETURN[
NEW[Disk.Label ← [
fileID: [abs[AbsID[id]]],
filePage: 0,
attributes: Attributes[physicalRoot],
dontCare: LOOPHOLE[LONG[0]]
] ] ]
END;
ReadPhysicalRoot:
PROC[channel: Disk.Channel]
RETURNS[ status: Disk.Status, root: PhysRoot ] = TRUSTED
BEGIN
label: REF Disk.Label = NEW[Disk.Label];
data: PageData;
[status, data] ← ReadPage[channel, VolumeFormat.rootPageNumber, label, FALSE];
root ← LOOPHOLE[data];
IF status = Disk.ok AND label^ # PhysRootLabel[root.pvID]^
THEN { status ← Disk.labelCheck; FreePage[data]; root ← NIL };
END;
--Volumes.--PhysicalObject:
PUBLIC
TYPE =
RECORD[
channel: Disk.Channel,
state: { ok, cantRead, badPhysical, badLogical } ← ok,
root: PhysRoot ← NIL,
rest: REF PhysicalObject ← NIL ];
Physical: TYPE = REF PhysicalObject;
physicalList: REF PhysicalObject ← NIL; -- known physical volumes --
NextPhysical:
PUBLIC
PROC[prev: Physical, wait:
BOOL]
RETURNS[next: Physical] = TRUSTED
BEGIN
DO lastCh: Disk.Channel;
[next, lastCh] ← EntryNextPhysical[prev];
IF next # NIL OR NOT wait THEN EXIT;
[] ← Disk.NextChannel[lastCh, TRUE];
ENDLOOP;
END;
lastChannel: Disk.Channel ← NIL;
EntryNextPhysical:
ENTRY
PROC[prev: Physical]
RETURNS[next: Physical, lastCh: Disk.Channel] =
{ [next, lastCh] ← InnerNextPhysical[prev] };
InnerNextPhysical:
INTERNAL
PROC[prev: Physical]
RETURNS[next: Physical, lastCh: Disk.Channel] = TRUSTED
BEGIN
next ← IF prev = NIL THEN physicalList ELSE prev.rest;
-- check whether found volume has changed state --
WHILE next # NIL AND NOT Disk.Valid[next.channel]
DO AssignPhysical[prev, next.rest]; next ← next.rest; ENDLOOP;
IF next = NIL
THEN
BEGIN
-- Check for a new channel --
new: Disk.Channel = Disk.NextChannel[lastChannel];
IF new # NIL THEN { AssignPhysical[prev, NewPhysical[new]]; lastChannel ← new };
next ← IF prev = NIL THEN physicalList ELSE prev.rest;
END;
lastCh ← lastChannel;
END;
AssignPhysical:
INTERNAL
PROC[tail: Physical, new: Physical] =
{ IF tail = NIL THEN physicalList ← new ELSE tail.rest ← new };
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];
IF status # Disk.ok THEN { new.state ← cantRead; RETURN };
IF new.root.seal # VolumeFormat.PRSeal
OR new.root.version # VolumeFormat.PRCurrentVersion
THEN { new.state ← badPhysical; RETURN };
now enumerate its sub-volumes to see what logical volume they belong to.
FOR s: INT IN [0..new.root.subVolumeCount)
DO svd:
LONG
POINTER
TO VolumeFormat.SubVolumeDesc = @new.root.subVolumes[s];
l: REF LogicalObject ← NIL;
FOR l ← logicalList, l.rest UNTIL l = NIL
DO IF l.id = svd.lvID THEN EXIT
REPEAT
FINISHED =>
BEGIN
l ← NEW[LogicalObject ← [id: svd.lvID, size: svd.lvSize, rest: NIL]];
IF lastLogical = NIL THEN logicalList ← l ELSE lastLogical.rest ← l;
lastLogical ← l;
END;
ENDLOOP;
-- Now, "l" is appropriate LogicalObject --
BEGIN
this: Volumes.SubVolumeDetails =
NEW[Volumes.SubVolumeDetailsObject ← [
id: svd.lvID,
start: svd.lvPage,
size: svd.nPages,
physical: new,
channel: channel,
address: svd.pvPage] ];
prev: LIST OF Volumes.SubVolumeDetails ← NIL;
FOR sv: LIST OF Volumes.SubVolumeDetails ← l.subVols, sv.rest UNTIL sv = NIL
DO
IF
NOT Disk.Valid[sv.first.channel]
THEN
BEGIN
-- remove from chain --
IF prev = NIL
THEN l.subVols ← sv.rest
ELSE prev.rest ← sv.rest;
LOOP
END;
IF sv.first.start > this.start
THEN
BEGIN
-- insert before "sv" --
IF this.start + this.size > sv.first.start THEN { new.state ← badLogical; 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 { new.state ← badLogical; 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;
ENDLOOP;
END;
LogicalObject:
TYPE =
RECORD[
id: NewFile.VolumeID,
size: VolumeFormat.LogicalPageCount,
subVols: LIST OF Volumes.SubVolumeDetails ← NIL, -- ordered list of fragments --
rest: REF LogicalObject ];
Logical: TYPE = REF LogicalObject;
logicalList: REF LogicalObject ← NIL; -- known logical volumes --
NextLogical:
PROC[prev: Logical, wait:
BOOL]
RETURNS[next: Logical] =
BEGIN
DO lastP: Physical;
[next, lastP] ← EntryNextLogical[prev];
IF next # NIL OR NOT wait THEN EXIT;
[] ← NextPhysical[lastP, TRUE];
ENDLOOP;
END;
lastPhysical: REF PhysicalObject ← NIL;
EntryNextLogical:
ENTRY
PROC[prev: Logical]
RETURNS[next: Logical, lastP: Physical] =
{ [next, lastP] ← InnerNextLogical[prev] };
InnerNextLogical:
INTERNAL
PROC[prev: Logical]
RETURNS[next: Logical, lastP: Physical] =
BEGIN
-- check for any more physical volumes --
FOR p: Physical ← InnerNextPhysical[lastPhysical].next, InnerNextPhysical[p].next
UNTIL p = NIL
DO lastPhysical ← p ENDLOOP;
next ← prev;
DO wanted:
INT ← 0;
next ←
IF next =
NIL
THEN logicalList
ELSE next.rest;
IF next = NIL THEN RETURN[NIL, lastPhysical];
-- check entire volume is known --
FOR sv: LIST OF Volumes.SubVolumeDetails ← next.subVols, sv.rest UNTIL sv = NIL
DO
IF wanted # sv.first.start
OR
NOT Disk.Valid[sv.first.channel]
THEN
EXIT;
wanted ← wanted + sv.first.size;
ENDLOOP;
IF wanted = next.size THEN EXIT;
ENDLOOP;
RETURN[next, lastPhysical]
END;
Valid:
INTERNAL
PROC[list:
LIST
OF Volumes.SubVolumeDetails]
RETURNS[
BOOL] =
TRUSTED
BEGIN
FOR sv: LIST OF Volumes.SubVolumeDetails ← list, sv.rest UNTIL sv = NIL
DO IF NOT Disk.Valid[sv.first.channel] THEN RETURN[FALSE] ENDLOOP;
RETURN[TRUE]
END;
Volume: TYPE = REF VolumeObject;
--File.--VolumeObject: PUBLIC TYPE = FileInternal.VolumeObject;
volumeList: Volume ← NIL; -- known volumes --
lastVolume: Volume ← NIL;
--File.--
NextVolume:
PUBLIC
PROC[volume: Volume, wait:
BOOL ←
FALSE]
RETURNS[next: Volume] =
BEGIN
DO lastLog: Logical;
[next, lastLog] ← EntryNext[volume];
IF next # NIL OR NOT wait THEN EXIT;
[] ← NextLogical[lastLog, TRUE];
ENDLOOP;
If "next" is newly created, we're responsible for reading its root page. Note that if next.root = NIL, we have exclusive access to it (others wait inside EntryNext until we're ready). If next.root # NIL, then it's immutable. So we can read next.root outside the monitor lock.
IF next # NIL AND next.root = NIL THEN { FileInternal.ReadRootPage[next]; NoteRootRead[] };
END;
lastLogical: Logical ← NIL;
rootRead: CONDITION;
EntryNext:
ENTRY
PROC[prev: Volume]
RETURNS[next: Volume, lastLog: Logical] =
BEGIN
next ← IF prev = NIL THEN volumeList ELSE prev.rest;
-- check whether next volume has gone offline --
WHILE next # NIL AND NOT Valid[next.subVolumes]
DO Assign[prev, next.rest]; next ← next.rest; ENDLOOP;
IF next = NIL
THEN
BEGIN
-- Check for a new volume --
new: Logical = InnerNextLogical[lastLogical].next;
IF new # NIL THEN { Assign[prev, NewVolume[new]]; lastLogical ← new };
next ← IF prev = NIL THEN volumeList ELSE prev.rest;
END
ELSE WHILE next.root = NIL DO WAIT rootRead ENDLOOP;
lastLog ← lastLogical;
END;
Assign:
INTERNAL
PROC[tail: Volume, new: Volume] =
{ IF tail = NIL THEN volumeList ← new ELSE tail.rest ← new };
NoteRootRead:
ENTRY
PROC =
{ BROADCAST rootRead };
NewVolume:
INTERNAL
PROC[logical: Logical]
RETURNS[volume: Volume] =
BEGIN
volume ←
NEW[VolumeObject ← [id: logical.id, subVolumes: logical.subVols, size: logical.size]];
END;
--File.--
SystemVolume:
PUBLIC
PROC
RETURNS[volume: Volume] =
{ RETURN[NIL] };