Cedar Nucleus (Files): access to physical and logical volumes
VolumesImpl.mesa
Andrew Birrell May 2, 1983 11:41 am
Last Edited by: Levin, May 20, 1983 5:43 pm
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 ];
VolumesImpl: CEDAR MONITOR
IMPORTS Disk, FileInternal, VM
EXPORTS DiskFace--AbsID--, NewFile, Volumes =
BEGIN
This implementation currently doesn't really do the right things, particularly about volumes going offline. It will get rewritten sometime. (ADB May 2, 1983 11:48 am).
******** List of known physical volumes ********
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;
******** Accumulation of sub-volumes into logical volumes ********
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;
******** Top-level: list of File.Volume objects ********
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: BOOLFALSE]
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] };
END.