<> <> <> <> 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 <> <<******** 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 }; <> 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: 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 # 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.