DIRECTORY Disk USING[ DoIO, Channel, DriveAttributes, InspectDiskShape, Label, labelCheck, NextChannel, ok, PageCount, PageNumber, Request, Status, Valid ], DiskFace USING[ wordsPerPage ], File USING[ VolumeID ], FileInternal USING[ ReadRootPage, TranslateStatus, VolumeObject ], VolumeFormat, PhysicalVolume USING[ Credentials, PhysicalRC, SubVolumeDetails, SubVolumeDetailsObject ], VM USING[ AddressToPageNumber, Allocate, Free, PageNumberToAddress, Pin, Unpin, wordsPerPage ], Rope USING[ ROPE, FromRefText ]; PhysicalVolumeImpl: CEDAR MONITOR IMPORTS Disk, FileInternal, Rope, VM EXPORTS DiskFace--AbsID--, File, PhysicalVolume = BEGIN PageData: TYPE = LONG POINTER; pagePages: INT = (DiskFace.wordsPerPage+VM.wordsPerPage-1)/VM.wordsPerPage; AccessPage: UNSAFE PROC[channel: Disk.Channel, page: Disk.PageNumber, label: LONG POINTER TO 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: LONG POINTER TO 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: File.VolumeID] RETURNS[Disk.Label] = TRUSTED BEGIN RETURN[ [ fileID: [abs[AbsID[id]]], filePage: 0, attributes: Attributes[physicalRoot], dontCare: LOOPHOLE[LONG[0]] ] ] END; CredentialsLabel: PROC[id: File.VolumeID] RETURNS[Disk.Label] = TRUSTED BEGIN 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; ReadPhysicalRoot: PROC[channel: Disk.Channel] RETURNS[ status: Disk.Status, root: PhysRoot ] = TRUSTED BEGIN label: 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; --PhysicalVolume.--PhysicalObject: PUBLIC TYPE = RECORD[ channel: Disk.Channel, rootStatus: PhysicalVolume.PhysicalRC _ ok, root: PhysRoot _ NIL, name: Rope.ROPE _ 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]; 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] }; 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: PhysicalVolume.SubVolumeDetails = NEW[PhysicalVolume.SubVolumeDetailsObject _ [ id: svd.lvID, start: svd.lvPage, size: svd.nPages, physical: new, channel: channel, address: svd.pvPage] ]; prev: LIST OF PhysicalVolume.SubVolumeDetails _ NIL; FOR sv: LIST OF PhysicalVolume.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.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 { new.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; ENDLOOP; END; 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 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 size _ Disk.DriveAttributes[physical.channel].nPages; channel _ physical.channel; rootStatus _ physical.rootStatus; IF rootStatus = ok THEN BEGIN lastSV: VolumeFormat.SubVolumeDesc = physical.root.subVolumes[physical.root.subVolumeCount-1]; id _ physical.root.pvID; name _ physical.name; free _ size - (lastSV.pvPage + lastSV.nPages); timeValid _ physical.root.timeParametersValid; time _ physical.root.timeParameters; END; END; WriteTimeParameters: PUBLIC PROC[physical: Physical, timeValid: BOOL, time: VolumeFormat.TimeParameters] RETURNS[ PhysicalVolume.PhysicalRC ] = TRUSTED BEGIN label: Disk.Label _ PhysRootLabel[physical.root.pvID]; status: Disk.Status; physical.root.timeParametersValid _ timeValid; IF timeValid THEN physical.root.timeParameters _ time; status _ AccessPage[physical.channel, VolumeFormat.rootPageNumber, @label, physical.root, write, TRUE]; RETURN[ FileInternal.TranslateStatus[status] ] END; ReadCredentials: PUBLIC UNSAFE PROC[physical: Physical, credentials: PhysicalVolume.Credentials] RETURNS[ PhysicalVolume.PhysicalRC ] = UNCHECKED BEGIN label: Disk.Label _ CredentialsLabel[physical.root.pvID]; 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[physical.root.pvID]; status: Disk.Status = AccessPage[physical.channel, VolumeFormat.credentialsPageNumber, @label, credentials, write, TRUE]; RETURN[ FileInternal.TranslateStatus[status] ] END; LogicalObject: TYPE = RECORD[ id: File.VolumeID, size: VolumeFormat.LogicalPageCount, subVols: LIST OF PhysicalVolume.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 PhysicalVolume.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 PhysicalVolume.SubVolumeDetails] RETURNS[BOOL] = TRUSTED BEGIN FOR sv: LIST OF PhysicalVolume.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 -- --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 NOT next.initialised THEN { FileInternal.ReadRootPage[next]; NoteRootRead[next] }; 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 UNTIL next.initialised 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[volume: Volume] = { volume.initialised _ TRUE; BROADCAST rootRead }; NewVolume: INTERNAL PROC[logical: Logical] RETURNS[volume: Volume] = BEGIN volume _ NEW[VolumeObject _ [id: logical.id, subVolumes: logical.subVols, size: logical.size]]; END; --PhysicalVolume.--GetPhysical: PUBLIC PROC[volume: Volume, page: VolumeFormat.LogicalPage _ [0]] RETURNS[Physical] = BEGIN FOR sv: LIST OF PhysicalVolume.SubVolumeDetails _ volume.subVolumes, sv.rest UNTIL sv = NIL DO IF sv.first.start <= page AND sv.first.start + sv.first.size > page THEN RETURN[sv.first.physical]; ENDLOOP; RETURN[NIL] END; END. ¢Cedar Nucleus (Files): access to physical and logical volumes PhysicalVolumeImpl.mesa Andrew Birrell July 8, 1983 2:55 pm 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 ******** I'd prefer using the pvID and a separate file type. For now, compatibility wins (ADB) now enumerate its sub-volumes to see what logical volume they belong to. ******** Accumulation of sub-volumes into logical volumes ******** ******** Top-level: list of File.Volume objects ******** If "next" is newly created, we're responsible for reading its root page. Note that if next.initialised = FALSE, we have exclusive access to next.root (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. ʈ˜Jšœ>™>Jšœ™Jšœ$™$J™šÏk ˜ Jšœœˆ˜’Jšœ œ˜Jšœœ ˜Jšœ œ0˜BJšœ ˜ JšœœF˜ZJšœœW˜_Jšœœœ˜ —J˜šœœ˜!Jšœ˜$Jšœ Ïc œ˜1—J˜Jš˜J˜Jšœ©™©J˜J˜Jšœ0™0˜Jšœ œœœ˜Jšœ œœœ˜KJ˜šÏn œœœ˜.J˜Jšœœœœ ˜"J˜J˜Jšœ œžœ˜&Jšœ˜(Jš˜Jšœœœœ/˜RJ˜˜J˜J˜ ˜Jšœœ œœ˜,Jšœœœœ˜.—J˜ —Jšœ œ/˜?J˜6Jšœœ/˜AJšœ˜—J˜šŸœœœ˜/Jšœœ œ1˜D—J˜šŸœœ˜%J˜Jšœœœœ ˜"Jšœ œ˜Jšœ*˜8Jš˜Jšœœœ˜;J˜CJšœœœ˜8Jšœ˜—J˜Jš œ œœœœ˜;J˜Jšž œœœ˜5J˜Jšž œ œœ˜?J˜šŸ œœœ˜DJš˜šœ˜ J˜J˜ J˜%Jšœ œœ˜J˜—Jšœ˜—J˜šŸœœœ˜GJš˜JšœV™Vš œ œ œœ œ˜9Jšœ>ž ˜I—šœ˜ Jšœœ˜)J˜ Jšœœ˜$Jšœ œœ˜J˜—Jšœ˜—J˜šŸœœ˜-Jšœ*˜8Jš˜Jšœ˜J˜JšœHœ˜OJšœœ˜Jšœœ!˜8Jšœ4œ˜>Jšœ˜—J˜šžœœœœ˜8J˜Jšœ+˜+Jšœœ˜Jšœ œœ˜Jšœœœ˜!—J˜Jšœ œœ˜$J˜Jšœœœž˜DJ˜šŸ œœœœ˜5Jšœ˜!Jš˜šœ˜J˜)Jš œœœœœœ˜$Jšœœ˜$—Jšœ˜Jšœ˜—J˜Jšœœ˜ J˜šŸœœœ˜-Jšœ(˜/J˜-—J˜šŸœœœ˜0Jšœ)˜7Jš˜Jš œœœœœ ˜6Jšž2˜2Jšœœœœ˜1Jšœ4œ˜>Jšœ˜ šœ˜ Jšž˜J˜2Jšœœœ?˜PJš œœœœœ ˜6Jšœ˜—J˜Jšœ˜—J˜šŸœœœ!˜>Jš œœœœœ˜?—J˜šŸ œœœ˜1Jšœ˜"Jš˜J˜Jšœœ&˜/Jšœœ/œœ˜@J˜/Jšœ6˜6Jšœœœ˜#Jšœ$˜&Jšœ1˜3Jšœ"œ˜/Jšœ>˜EJšœH™HJšœœœ˜*šœœœœ6˜MJšœœœ˜Jšœœ˜)Jšœœœ˜šœœ˜Jš˜Jšœœ8œ˜EJšœœœœ˜DJ˜Jšœ˜—Jšœ˜Jšž+˜+š˜šœ(œ*˜UJ˜ J˜J˜J˜J˜J˜—Jšœœœ#œ˜4Jš œœœ6œ˜Sšœœœ˜&šœœž˜"Jšœ˜ Jšœ˜Jšœ˜Jš˜Jšœ˜—Jšœ˜šœœž˜#Jšœ(˜*Jšœ"œ˜/Jšœ˜ Jšœ œ˜,Jšœ œ˜-Jš˜Jšœ˜—Jšœ,˜.Jšœ"œ˜/J˜ —šœœ˜Jšœ˜ Jšœ œœ˜-Jšœ œœ˜-—Jšœ˜—Jšœ˜—Jšœ˜Jšœ˜—J˜šŸœœ˜Jšœœœœœœ%œœ˜PJšœœ˜ Jšœœ˜Jš˜Jš œœœœœ ˜#Jš œœœ œœ˜8J˜Jšœ˜Jšœ˜—J˜šŸ œœœœ˜6J˜Jšœ&˜&Jšœž'˜:Jšœ œ˜J˜J˜Jšœ œ˜Jšœ%˜,Jš˜J˜5J˜J˜!Jšœ˜šœ˜ šœ$˜$Jšœ9˜9—J˜Jšœ˜Jšœ.˜.Jšœ.˜.Jšœ$˜$Jšœ˜—Jšœ˜—J˜š Ÿœœœ œ%œ ˜—Jš˜Jšœ6˜6Jšœ˜Jšœ.˜.Jšœ œ%˜6šœB˜BJšœœ˜$—Jšœ(˜.Jšœ˜—J˜š Ÿœœœœ>œ ˜‘Jš˜Jšœ9˜9šœV˜VJšœœ˜!—Jšœ(˜.Jšœ˜—J˜š Ÿœœœ>œ ˜‰Jš˜Jšœ9˜9šœV˜VJšœœ˜"—Jšœ(˜.Jšœ˜—J˜—J˜JšœB™B˜šœœœ˜J˜J˜$Jšœ œœ#œž˜WJšœœ˜—J˜Jšœ œœ˜"J˜Jšœ œœž˜AJ˜šŸ œœœ˜,Jšœ˜Jš˜šœ˜J˜'Jš œœœœœœ˜$Jšœœ˜—Jšœ˜Jšœ˜—J˜Jšœœœ˜'J˜šŸœœœœ"˜UJšœ+˜+—J˜šŸœœœœ"˜XJš˜Jšž)˜)JšœN˜QJšœ˜ Jšœœ˜J˜ šœ œ˜šœœ˜Jšœ ˜Jšœ ˜—Jš œœœœœ˜-Jšž"˜"Jš œœœ9œ˜Vš œœœœœœ˜LJ˜ —Jšœ˜Jšœœœ˜ —Jšœ˜Jšœ˜Jšœ˜—J˜šŸœœœœœ"œœ˜[Jš˜Jš œœœ1œ˜NJšœœœœœœœ˜BJšœœ˜ Jšœ˜—J˜—J˜Jšž8™8˜Jšœœœ˜ J˜Jšž œœœ˜?J˜Jšœœž˜-J˜š ž Ÿ œœœœœ˜DJšœ˜Jš˜šœ˜J˜$Jš œœœœœœ˜$Jšœœ˜ —Jšœ˜Jšœ¦™¦Jšœœœœ˜&Jšœ9˜=Jšœ˜—J˜Jšœœ˜J˜Jšœ œ˜J˜šŸ œœœ˜#Jšœ"˜)Jš˜Jš œœœœ œ ˜4Jšž0˜0Jšœœœœ˜/Jšœ,œ˜6Jšœ˜ šœ˜ Jšž˜Jšœ2˜2Jšœœœ5˜FJš œœœœ œ ˜4Jš˜—Jš œœœœ œ˜5J˜Jšœ˜—J˜šŸœœœ˜2Jš œœœœœ˜>—J˜šŸ œœœ˜*Jšœœ œ ˜2—J˜šŸ œœœ˜*Jšœ˜Jš˜šœ˜JšœS˜V—Jšœ˜—J˜š žŸ œœœ7œ ˜uJš˜Jšœœœ=˜LJšœ˜šœœœ&˜FJšœœ˜—Jšœ˜Jšœœ˜ Jšœ˜—J˜—Jšœ˜J˜J˜—…—0’D¼