DIRECTORY BootFile USING[ Location ], Disk USING[ Channel, DoIO, DriveAttributes, invalid, Label, labelCheck, ok, PageNumber, PageCount, Request, SameDrive, Status ], DiskFace USING[ DontCare, RelID, wordsPerPage ], File USING[ Create, Error, FileID, FP, nullFP, Handle, NextVolume, Open, PageCount, PageNumber, PagesForWords, RC, Read, SetRoot, VolumeFile, VolumeFlusher, VolumeID, Write ], FileInternal, PhysicalVolume USING[ Physical, PhysicalRC, SubVolumeDetails ], Process USING[ Detach, MsecToTicks, Pause, SecondsToTicks, Ticks ], ProcessorFace USING[ GetClockPulses, microsecondsPerHundredPulses ], Rope USING[ Equal, Fetch, FromRefText, Length, ROPE ], VolumeFormat USING[ AbsID, Attributes, LogicalPage, LogicalPageCount, LogicalRoot, LogicalRun, LRCurrentVersion, LRSeal, LVBootFile, nullDiskFileID, RunPageCount, VAMObject, VAMChunk, vamChunkPos, volumeLabelLength ], VM USING[ AddressForPageNumber, Allocate, Interval, MakeUnchanged, PageCount, PageNumber, PageNumberForAddress, PagesForWords, State, SwapIn, Unpin], VMBacking USING[ Run ]; LogicalVolumeImpl: CEDAR MONITOR LOCKS volume USING volume: Volume IMPORTS Disk, File, Process, ProcessorFace, FileInternal, Rope, VM EXPORTS DiskFace--RelID,AbsID,Attributes--, File, FileInternal, PhysicalVolume--SetPhysicalRoot-- SHARES File = BEGIN -- ******** Data Types and minor subroutines ******** -- --DiskFace.--Attributes: PUBLIC TYPE = VolumeFormat.Attributes; --DiskFace.--AbsID: PUBLIC TYPE = VolumeFormat.AbsID; --File.--FileID: PUBLIC TYPE = FileInternal.FileID; Volume: TYPE = REF VolumeObject; --File.--VolumeObject: PUBLIC TYPE = FileInternal.VolumeObject; PhysicalRun: TYPE = VMBacking.Run; DoPinnedIO: UNSAFE PROC[channel: Disk.Channel, label: LONG POINTER TO Disk.Label, req: POINTER TO Disk.Request] RETURNS[ status: Disk.Status, countDone: Disk.PageCount] = TRUSTED BEGIN interval: VM.Interval = [ page: VM.PageNumberForAddress[req.data], count: VM.PagesForWords[ (IF req.incrementDataPtr THEN req.count ELSE 1)*DiskFace.wordsPerPage] ]; VM.SwapIn[interval: interval, kill: req.command.data=read, pin: TRUE]; [status, countDone] _ Disk.DoIO[channel, label, req ! UNWIND => VM.Unpin[interval] ]; VM.Unpin[interval]; END; notCheckpointing: CONDITION; --FileInternal.--LockVolume: PUBLIC ENTRY PROC[volume: Volume] = BEGIN WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; volume.vamFile _ NIL; -- cf. GrabVAMFile volume.checkpointing _ TRUE; END; -- ******** Access to volume root page and volume's list of sub-volumes ******** -- volRoot: VolumeFormat.LogicalPage = [0]; -- logical page number of volume root page -- --File.--GetVolumeID: PUBLIC PROC[volume: Volume] RETURNS[id: File.VolumeID] = { RETURN[volume.id] }; LogRootLabel: PROC[id: File.VolumeID] RETURNS[Disk.Label] = BEGIN RETURN[ [ fileID: [abs[AbsID[id]]], filePage: 0, attributes: Attributes[logicalRoot], dontCare: LOOPHOLE[LONG[0]] ] ] END; RootTransfer: INTERNAL PROC[volume: Volume, direction: {read, write, init} ] RETURNS[why: File.RC] = TRUSTED BEGIN status: Disk.Status; countDone: Disk.PageCount; label: Disk.Label _ LogRootLabel[volume.id]; req: Disk.Request _ [ diskPage: volume.subVolumes.first.address, data: volume.root, incrementDataPtr: TRUE, command: SELECT direction FROM read => [header: verify, label: verify, data: read], write => [header: verify, label: verify, data: write], init => [header: verify, label: write, data: write], ENDCASE => ERROR, count: 1 ]; [status, countDone] _ DoPinnedIO[volume.subVolumes.first.channel, @label, @req]; RETURN[ FileInternal.TranslateStatus[status] ] END; --FileInternal.--ReadRootPage: PUBLIC ENTRY PROC[volume: Volume] = TRUSTED BEGIN ENABLE UNWIND => NULL; IF volume.root = NIL THEN volume.root _ VM.AddressForPageNumber[VM.Allocate[ VM.PagesForWords[DiskFace.wordsPerPage]].page]; volume.rootStatus _ RootTransfer[volume, read]; IF volume.rootStatus = ok AND (volume.root.seal # VolumeFormat.LRSeal OR volume.root.version # VolumeFormat.LRCurrentVersion) THEN volume.rootStatus _ inconsistent; IF volume.rootStatus = ok THEN TRUSTED{ volume.name _ GetName[@volume.root.label, volume.root.labelLength] }; IF volume.rootStatus = ok AND volume.root.type # cedar THEN volume.rootStatus _ nonCedarVolume; IF volume.rootStatus = ok THEN BEGIN ENABLE File.Error => BEGIN volume.vamStatus _ SELECT why FROM unknownFile, unknownPage => inconsistent, ENDCASE => why; CONTINUE END; volume.lastFileID _ volume.root.lastFileID; ReadVAM[volume]; volume.vamStatus _ ok; END ELSE volume.vamStatus _ inconsistent; volume.checkpointing _ FALSE; BROADCAST notCheckpointing; ConsiderFlushing[volume]; END; --FileInternal.--InitRootPage: PUBLIC ENTRY PROC[volume: Volume, name: Rope.ROPE] RETURNS[File.RC] = TRUSTED BEGIN ENABLE UNWIND => NULL; IF volume.root = NIL THEN volume.root _ VM.AddressForPageNumber[VM.Allocate[ VM.PagesForWords[DiskFace.wordsPerPage]].page]; volume.root^ _ [ vID: volume.id, labelLength: MIN[name.Length[], VolumeFormat.volumeLabelLength], volumeSize: volume.size ]; FOR i: CARDINAL IN [0..volume.root.labelLength) DO volume.root.label[i] _ name.Fetch[i] ENDLOOP; volume.rootStatus _ RootTransfer[volume, init]; IF volume.rootStatus = ok THEN [] _ FileInternal.InitLogicalMarkers[volume]; volume.name _ name; volume.lastFileID _ volume.root.lastFileID; volume.vamStatus _ inconsistent; -- until someone calls EraseVolume volume.checkpointing _ FALSE; BROADCAST notCheckpointing; ConsiderFlushing[volume]; RETURN[volume.rootStatus] 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; --File.--GetVolumeName: PUBLIC ENTRY PROC[volume: Volume] RETURNS[Rope.ROPE] = BEGIN ENABLE UNWIND => NULL; IF volume.rootStatus # ok AND volume.rootStatus # nonCedarVolume THEN RETURN WITH ERROR File.Error[volume.rootStatus]; RETURN[volume.name] END; --File.--FindVolumeFromID: PUBLIC PROC[id: File.VolumeID] RETURNS[volume: Volume] = BEGIN volume _ NIL; DO volume _ File.NextVolume[volume]; IF volume = NIL THEN EXIT; IF GetVolumeID[volume] = id THEN EXIT; ENDLOOP; END; --File.--FindVolumeFromName: PUBLIC PROC[name: Rope.ROPE] RETURNS[volume: Volume] = BEGIN volume _ NIL; DO volume _ File.NextVolume[volume]; IF volume = NIL THEN EXIT; IF Rope.Equal[GetVolumeName[volume ! File.Error => CONTINUE], name, FALSE] THEN EXIT; ENDLOOP; END; --File.--LogicalInfo: PUBLIC ENTRY PROC[volume: Volume] RETURNS[ id: File.VolumeID, size: INT, rootStatus: File.RC, name: Rope.ROPE, vamStatus: File.RC, free: INT, freeboard: INT] = BEGIN ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; RETURN[ id: volume.id, size: volume.size, rootStatus: volume.rootStatus, name: volume.name, vamStatus: volume.vamStatus, free: volume.free, freeboard: volume.freeboard] END; --FileInternal.--TranslateLogicalRun: PUBLIC --EXTERNAL-- PROC[logicalRun: VolumeFormat.LogicalRun, volume: Volume] RETURNS[channel: Disk.Channel, diskPage: Disk.PageNumber] = BEGIN FOR sv: LIST OF PhysicalVolume.SubVolumeDetails _ volume.subVolumes, sv.rest UNTIL sv = NIL DO IF sv.first.start <= logicalRun.first AND sv.first.start + sv.first.size > logicalRun.first THEN RETURN[channel: sv.first.channel, diskPage: [sv.first.address + (logicalRun.first - sv.first.start)] ]; REPEAT FINISHED => RETURN WITH ERROR File.Error[inconsistent] ENDLOOP; END; --PhysicalVolume.--GetPhysical: PUBLIC PROC[volume: Volume, page: VolumeFormat.LogicalPage _ [0]] RETURNS[PhysicalVolume.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; --FileInternal.--RecordRootFile: PUBLIC ENTRY PROC[volume: Volume, root: File.VolumeFile, fp: File.FP, page: File.PageNumber, id: DiskFace.RelID, link: DiskFace.DontCare, channel: Disk.Channel] = TRUSTED BEGIN ENABLE UNWIND => NULL; why: File.RC; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus # ok THEN RETURN WITH ERROR File.Error[volume.rootStatus]; IF NOT Disk.SameDrive[channel, volume.subVolumes.first.channel] THEN RETURN WITH ERROR File.Error[mixedDevices]; volume.root.rootFile[root] _ [fp: fp, page: page]; IF root IN [FIRST[VolumeFormat.LVBootFile]..LAST[VolumeFormat.LVBootFile]] THEN volume.root.bootingInfo[root] _ [fID: [rel[id]], firstPage: page, firstLink: link]; why _ RootTransfer[volume, write]; IF why = ok THEN why _ FileInternal.WriteLogicalMarkers[volume]; IF why # ok THEN RETURN WITH ERROR File.Error[why]; END; --File.--GetRoot: PUBLIC ENTRY PROC[volume: Volume, root: File.VolumeFile] RETURNS[fp: File.FP, page: File.PageNumber _ [0]] = TRUSTED BEGIN ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus # ok THEN RETURN WITH ERROR File.Error[volume.rootStatus]; [fp: fp, page: page] _ volume.root.rootFile[root]; END; --FileInternal.--GetLogicalLocation: PUBLIC ENTRY PROC[volume: Volume, root: VolumeFormat.LVBootFile] RETURNS[location: BootFile.Location] = TRUSTED BEGIN ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus # ok AND volume.rootStatus # nonCedarVolume THEN RETURN WITH ERROR File.Error[volume.rootStatus]; location.diskFileID _ volume.root.bootingInfo[root]; [type: location.deviceType, ordinal: location.deviceOrdinal] _ Disk.DriveAttributes[volume.subVolumes.first.channel]; END; --PhysicalVolume.--SetPhysicalRoot: PUBLIC ENTRY PROC[volume: Volume, root: File.VolumeFile] = TRUSTED BEGIN ENABLE UNWIND => NULL; physRC: PhysicalVolume.PhysicalRC; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus # ok AND volume.rootStatus # nonCedarVolume THEN RETURN WITH ERROR File.Error[volume.rootStatus]; physRC _ FileInternal.SetPhysicalLocation[volume.subVolumes.first.physical, root, volume.root.bootingInfo[root]]; IF physRC # ok THEN RETURN WITH ERROR File.Error[physRC]; END; --File.--MarkDebugger: PUBLIC ENTRY PROC[volume: Volume, isDebugger: BOOL] = TRUSTED BEGIN ENABLE UNWIND => NULL; why: File.RC; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus # ok THEN RETURN WITH ERROR File.Error[volume.rootStatus]; volume.root.coCedar _ isDebugger; why _ RootTransfer[volume, write]; IF why = ok THEN why _ FileInternal.WriteLogicalMarkers[volume]; IF why # ok THEN RETURN WITH ERROR File.Error[why]; END; --File.--IsDebugger: PUBLIC ENTRY PROC[volume: Volume] RETURNS[BOOL] = TRUSTED BEGIN ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus = nonCedarVolume THEN RETURN[volume.root.type # pilot]; IF volume.rootStatus # ok THEN RETURN WITH ERROR File.Error[volume.rootStatus]; RETURN[volume.root.coCedar] END; EraseRoot: INTERNAL PROC[volume: Volume] = TRUSTED BEGIN volume.root.bootingInfo _ ALL[VolumeFormat.nullDiskFileID]; volume.root.rootFile _ ALL[]; END; idGroupSize: INT = 100; -- number if id's to allocate at a time. --FileInternal.--NewID: PUBLIC ENTRY PROC[volume: Volume] RETURNS [FileID] = TRUSTED BEGIN ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus # ok THEN RETURN WITH ERROR File.Error[volume.rootStatus]; DO lastRecorded: FileID = volume.root.lastFileID; why: File.RC; IF volume.lastFileID < lastRecorded THEN EXIT; volume.root.lastFileID _ FileID[lastRecorded + idGroupSize]; why _ RootTransfer[volume, write]; IF why # ok THEN RETURN WITH ERROR File.Error[why]; ENDLOOP -- This is a loop only to recover from garbage ID's --; RETURN[ volume.lastFileID _ [volume.lastFileID + 1] ] END; -- ******** Volume Allocation Map ******** -- VAM: TYPE = FileInternal.VAM; PageBits: TYPE = MACHINE DEPENDENT RECORD[ls(0): CARDINAL, ms(1): CARDINAL]; VAMChunks: PROC[pages: VolumeFormat.LogicalPageCount] RETURNS[CARDINAL] = TRUSTED { OPEN p: LOOPHOLE[pages+LAST[CARDINAL]-1, PageBits]; RETURN[p.ms] }; VAMWords: PROC[pages: VolumeFormat.LogicalPageCount] RETURNS[INT] = -- SIZE[VAMObject[VAMChunks[pages]]], but compiler can't handle it -- { RETURN[VolumeFormat.vamChunkPos + VAMChunks[pages]*4096] }; VAMFilePages: PROC[pages: VolumeFormat.LogicalPageCount] RETURNS[File.PageCount] = INLINE { RETURN[File.PagesForWords[VAMWords[pages]]] }; VAMVMPages: PROC[pages: VolumeFormat.LogicalPageCount] RETURNS[VM.PageCount] = INLINE { RETURN[VM.PagesForWords[VAMWords[pages]]] }; Used: PROC[vam: VAM, page: VolumeFormat.LogicalPage] RETURNS[BOOL] = TRUSTED INLINE -- vam.used[p.ms][p.ls]], but the compiler can't handle it! -- { OPEN p: LOOPHOLE[page, PageBits]; RETURN[LOOPHOLE[@vam.used + p.ms*SIZE[VolumeFormat.VAMChunk], LONG POINTER TO VolumeFormat.VAMChunk][p.ls]] }; SetUsed: INTERNAL PROC[volume: Volume, page: VolumeFormat.LogicalPage, inUse: BOOL] = TRUSTED INLINE -- vam.used[p.ms][p.ls]], but the compiler can't handle it! -- BEGIN OPEN p: LOOPHOLE[page, PageBits]; chunk: LONG POINTER TO VolumeFormat.VAMChunk = LOOPHOLE[@volume.vam.used + p.ms*SIZE[VolumeFormat.VAMChunk]]; IF chunk[p.ls] # inUse THEN BEGIN chunk[p.ls] _ inUse; IF inUse THEN volume.free _ volume.free-1 ELSE volume.free _ volume.free+1; END; END; AllocVAM: INTERNAL PROC[volume: Volume] = TRUSTED BEGIN IF volume.vam = NIL THEN BEGIN words: INT = VAMWords[volume.size]; volume.vam _ VM.AddressForPageNumber[VM.Allocate[VM.PagesForWords[words]].page]; volume.vam.size _ volume.size; END; volume.vam.rover _ [0]; END; ReadVAM: INTERNAL PROC[volume: Volume] = TRUSTED BEGIN this: VolumeFormat.LogicalPage _ [0]; IF volume.rootStatus # ok THEN ERROR; -- checked by our caller volume.vamFile _ File.Open[volume, volume.root.rootFile[VAM].fp --File.Error is caught by our caller--]; AllocVAM[volume]; File.Read[volume.vamFile, [0], VAMFilePages[volume.size], volume.vam]; SetUsed[volume, volRoot, TRUE]; volume.free _ 0; DO IF this = volume.vam.size THEN EXIT; IF NOT Used[volume.vam, this] THEN volume.free _ volume.free+1; this _ [this+1]; ENDLOOP; BROADCAST vamFileRelease; -- significant if we called GrabVAMFile for a checkpoint END; --File.--GetVolumePages: PUBLIC ENTRY PROC[volume: Volume] RETURNS[size, free, freeboard: INT] = BEGIN ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.rootStatus # ok THEN RETURN WITH ERROR File.Error[volume.rootStatus]; RETURN[size: volume.size, free: volume.free, freeboard: volume.freeboard] END; --File.--SetFlusher: PUBLIC ENTRY PROC[volume: Volume, flusher: File.VolumeFlusher, data: REF ANY] = BEGIN ENABLE UNWIND => NULL; volume.flusherData _ data; volume.flusher _ flusher; IF volume.freeboard = 0 THEN volume.freeboard _ MIN[volume.size/5, 1000]; ConsiderFlushing[volume]; END; ConsiderFlushing: INTERNAL PROC[volume: Volume] = BEGIN IF volume.rootStatus = ok AND volume.free < volume.freeboard - volume.freeboard / 10 -- be sure it's worth while AND volume.flusher # NIL AND NOT volume.flushing THEN TRUSTED{ volume.flushing _ TRUE; Process.Detach[ FORK FlusherProcess[volume] ] } END; FlusherProcess: PROC[volume: Volume] = BEGIN DO flusher: File.VolumeFlusher; data: REF ANY; lack: VolumeFormat.LogicalPageCount; continue: BOOL; urgent: BOOL; Examine: ENTRY PROC[volume: Volume] = BEGIN flusher _ volume.flusher; data _ volume.flusherData; lack _ MAX[volume.freeboard - volume.free, 0]; continue _ volume.free < volume.freeboard AND volume.flusher # NIL; urgent _ volume.free < volume.freeboard / 2; IF NOT continue THEN volume.flushing _ FALSE; END; start: LONG CARDINAL _ ProcessorFace.GetClockPulses[]; elapsed: LONG CARDINAL; progress: BOOL; Examine[volume]; IF NOT continue THEN EXIT; progress _ flusher[volume, lack, data]; elapsed _ ProcessorFace.GetClockPulses[] - start; IF progress THEN BEGIN IF NOT urgent THEN Process.Pause[Process.MsecToTicks[ MIN[(elapsed+99)/100 * ProcessorFace.microsecondsPerHundredPulses, 500]]]; END ELSE Process.Pause[Process.MsecToTicks[1000]]; ENDLOOP; END; --File.--GetFlusher: PUBLIC ENTRY PROC[volume: Volume] RETURNS[File.VolumeFlusher, REF ANY] = BEGIN ENABLE UNWIND => NULL; RETURN[volume.flusher, volume.flusherData] END; --FileInternal.--Flush: PUBLIC PROC[volume: Volume, lack: VolumeFormat.LogicalPageCount] RETURNS [BOOL] = BEGIN flusher: File.VolumeFlusher; data: REF ANY; [flusher, data] _ GetFlusher[volume]; IF flusher # NIL AND flusher[volume, lack, data] THEN RETURN[TRUE]; RETURN[FALSE] END; --File.--SetFreeboard: PUBLIC ENTRY PROC[volume: Volume, freeboard: INT] = BEGIN ENABLE UNWIND => NULL; volume.freeboard _ freeboard; ConsiderFlushing[volume]; END; --FileInternal.--Alloc: PUBLIC ENTRY PROC[volume: Volume, first: VolumeFormat.LogicalPage, size, min: VolumeFormat.LogicalPageCount] RETURNS[given: VolumeFormat.LogicalRun] = TRUSTED BEGIN ENABLE UNWIND => NULL; reqSize: VolumeFormat.RunPageCount = MIN[size,LAST[VolumeFormat.RunPageCount]]; pos: VolumeFormat.LogicalPage; vam: VAM; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.vamStatus # ok THEN RETURN WITH ERROR File.Error[volume.vamStatus]; IF volume.free < size THEN RETURN WITH ERROR File.Error[volumeFull]; IF size = 0 THEN RETURN[[first: first, size: 0]]; -- main algorithm gives at least one page vam _ volume.vam; pos _ IF first = 0 THEN vam.rover ELSE first; min _ MAX[ MIN[min, reqSize], 1 ]; -- otherwise the algorithm doesn't work! THROUGH [0 .. (vam.size+min-1)/min] DO IF pos >= vam.size THEN pos _ [0]; IF NOT Used[vam, pos] THEN BEGIN start, end: VolumeFormat.LogicalPage _ pos; given.size _ 1; WHILE start > 0 AND given.size < reqSize DO IF Used[vam, [start-1]] THEN EXIT; start _ [start-1]; given.size _ given.size + 1; ENDLOOP; WHILE end < vam.size-1 AND given.size < reqSize DO IF Used[vam, [end+1]] THEN EXIT; end _ [end+1]; given.size _ given.size + 1; ENDLOOP; IF given.size >= min THEN { given.first _ start; EXIT }; END; pos _ [pos+min]; REPEAT FINISHED => RETURN WITH ERROR File.Error[volumeFull] ENDLOOP; volume.vamChanged _ TRUE; FOR i: INT IN [0..given.size) DO SetUsed[volume, [given.first+i], TRUE] ENDLOOP; IF first = 0 THEN vam.rover _ given.first; ConsiderFlushing[volume]; END; --FileInternal.--Free: PUBLIC ENTRY PROC[volume: Volume, logicalRun: VolumeFormat.LogicalRun] = BEGIN ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.vamStatus # ok THEN RETURN WITH ERROR File.Error[volume.vamStatus]; volume.vamChanged _ TRUE; FOR i: VolumeFormat.RunPageCount IN [0..logicalRun.size) DO SetUsed[volume, [logicalRun.first + i], FALSE] ENDLOOP; END; vamFileRelease: CONDITION; GrabVAMFile: ENTRY PROC[volume: Volume] RETURNS [file: File.Handle] = TRUSTED BEGIN ENABLE UNWIND => NULL; DO WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; IF volume.vamStatus # ok THEN RETURN WITH ERROR File.Error[volume.vamStatus]; IF (file _ volume.vamFile) # NIL THEN { volume.vamFile _ NIL; EXIT }; WAIT vamFileRelease; -- because we must be in the process of erasing the volume ENDLOOP; volume.vamChanged _ FALSE; -- because our caller is about to write it to disk END; ReleaseVAMFile: ENTRY PROC[volume: Volume, file: File.Handle, changed: BOOL] = TRUSTED BEGIN ENABLE UNWIND => NULL; volume.vamChanged _ volume.vamChanged OR changed--indicates if commit failed--; IF (volume.vamFile _ file) = NIL THEN volume.vamStatus _ inconsistent; BROADCAST vamFileRelease; END; --FileInternal.--Commit: PUBLIC PROC[volume: Volume] = TRUSTED BEGIN -- Ensure VAM is up to date on disk -- -- Assumes volume.vam is vm-page-aligned. -- vamFile: File.Handle _ GrabVAMFile[volume]; -- raises File.Error if vamFile isn't ok. InnerCommit[volume, vamFile ! File.Error => { ReleaseVAMFile[volume, vamFile, TRUE]; vamFile _ NIL }; -- release our lock UNWIND => IF vamFile # NIL THEN ReleaseVAMFile[volume, vamFile, TRUE] ]; ReleaseVAMFile[volume, vamFile, FALSE]; END; InnerCommit: PROC[volume: Volume, vamFile: File.Handle] = TRUSTED BEGIN filePageCount: File.PageCount = VAMFilePages[volume.size]; vmPagesPerFilePage: INT = VM.PagesForWords[DiskFace.wordsPerPage]; FOR filePage: File.PageNumber _ [0], [filePage+1] UNTIL filePage = filePageCount DO fileBaseAddr: LONG POINTER = LOOPHOLE[volume.vam + filePage * DiskFace.wordsPerPage]; baseVMPage: VM.PageNumber = VM.PageNumberForAddress[fileBaseAddr]; FOR i: INT IN [0..vmPagesPerFilePage) DO IF VM.State[baseVMPage+i].dataState = changed THEN BEGIN VM.MakeUnchanged[[page: baseVMPage, count: vmPagesPerFilePage]]; File.Write[vamFile, filePage, 1, fileBaseAddr]; EXIT END; ENDLOOP; ENDLOOP; END; --File.--EraseVolume: PUBLIC PROC[volume: Volume] = TRUSTED BEGIN EntryErase[volume]; BEGIN ENABLE UNWIND => ReleaseVAMFile[volume, NIL, TRUE];-- vamState _ inconsistent vamFile: File.Handle _ NIL; NoteBad: PROC[bad: VolumeFormat.LogicalPage] = CHECKED { Silly: ENTRY PROC[volume: Volume] = { SetUsed[volume, bad, TRUE] }; Silly[volume] }; LongFree: PROC[first: VolumeFormat.LogicalPage, size: INT] = TRUSTED BEGIN -- FileInternal.FreeRun is restricted to LAST[CARDINAL] pages WHILE size > 0 DO amount: CARDINAL = MIN[size, LAST[VolumeFormat.RunPageCount]]; FileInternal.FreeRun[[first: first, size: amount], volume]; first _ [first+amount]; size _ size - amount; ENDLOOP; END; FOR sv: LIST OF PhysicalVolume.SubVolumeDetails _ volume.subVolumes, sv.rest UNTIL sv = NIL DO -- Don't free root (!), nor first page of sub-volumes (to break up runs) IF volRoot >= sv.first.start AND volRoot < sv.first.start + sv.first.size THEN BEGIN IF volRoot > 0 THEN LongFree[first: [sv.first.start+1], size: volRoot-sv.first.start-1]; LongFree[first: [volRoot+1], size: sv.first.size-(volRoot-sv.first.start)-1]; END ELSE LongFree[first: [sv.first.start+1], size: sv.first.size-1]; FileInternal.GetBadPages[sv.first, NoteBad]; ENDLOOP; vamFile _ File.Create[volume, VAMFilePages[volume.size]]; InnerCommit[volume, vamFile]; File.SetRoot[VAM, vamFile]; ReleaseVAMFile[volume, vamFile, FALSE]; END; END; EntryErase: ENTRY PROC[volume: Volume] = TRUSTED BEGIN ENABLE UNWIND => NULL; WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; WHILE volume.vamStatus = ok AND volume.vamFile = NIL DO WAIT vamFileRelease ENDLOOP; IF volume.rootStatus # ok THEN RETURN WITH ERROR File.Error[volume.rootStatus]; volume.vamStatus _ inconsistent; volume.vamFile _ NIL; EraseRoot[volume]; AllocVAM[volume]; volume.free _ volume.vam.size; -- to prevent underflow in SetUsed FOR i: INT IN [0..volume.vam.size) DO SetUsed[volume, [i], TRUE] ENDLOOP; volume.free _ 0; volume.vamStatus _ ok; -- but we still own the (not-yet-created) volume.vamFile END; CommitVAMFiles: PROC = BEGIN commitPause: Process.Ticks _ Process.SecondsToTicks[1]; DO FOR this: Volume _ File.NextVolume[NIL,FALSE], File.NextVolume[this,FALSE] UNTIL this = NIL DO IF this.vamChanged--in monitor!-- THEN Commit[this ! File.Error => CONTINUE] ENDLOOP; Process.Pause[commitPause]; ENDLOOP; END; vamCommiter: PROCESS = FORK CommitVAMFiles[]; END. úCedar Nucleus(Files): Access to per-volume data structures: root page and VAM LogicalVolumeImpl.mesa Andrew Birrell December 8, 1983 9:48 am Last Edited by: Levin, September 22, 1983 3:32 pm Called before a checkpoint; unlock by calling ReadRootPage see also the defaults in the declaration of VolumeFormat.LogicalRoot Following information is valid only if rootStatus is ok or nonCedarVolume Following information is valid only if vamStatus is ok Accesses only immutable fields of the volume If root = VAM then I hope you know what you're doing! Restrict run length to LAST[CARDINAL], to maximize runs per run-table page Loop probing for a free page at "min" intervals Expand around "pos" to see if there's a run of size at least "min" -- ******** Creation and writing of the VAM file ******** -- This is messy, because there is a recursion through the top-level file operations, particularly File.Create. So we must tread very carefully to avoid recursing onto our own monitor lock. Note that File.Create does not call Commit. We now own the (not-yet-created) volume.vamFile, but we're outside our monitor so File.Create won't deadlock! Avoid overwriting volume root page! Note: ideally, we shouldn't write free labels on bad pages, but it doesn't matter much Êò˜JšœN™NJšœ™Jšœ(™(J™1šÏk ˜ Jšœ œ ˜Jšœœv˜€Jšœ œ"˜0JšœœœJœ>˜¯J˜ Jšœœ+˜?Jšœœ6˜CJšœœ1˜DJšœœ%œ˜6Jšœ œÇ˜ÙJšœœ˜•Jšœ œ˜—J˜š œœœœœ˜BJšœ9˜BJšœ Ïcœ$ž˜aJšœ˜ —J˜š˜J˜—J˜Jšž8˜8˜Jšž œ œœ˜?J˜Jšž œœœ˜5J˜Jšž œœœ˜3J˜Jšœœœ˜ J˜Jšž œœœ˜?J˜Jšœ œ˜"J˜šÏn œœœœœœœœ˜oJšœ4˜BJš˜šœ œ ˜Jšœœ ˜(šœœ˜Jšœœœ œ˜I——Jšœ>œ˜FJšœ6œœ˜UJšœ˜Jšœ˜—J˜Jšœ œ˜J˜š žŸ œœœœ˜@Jš˜Jšœ:™:Jšœœœœ˜˜>Jšœ6˜6—Jšœ˜—J˜šžŸœœ œ*˜fJš˜Jšœœœ˜Jšœ"˜"Jšœœœœ˜˜>šœœœ˜#šœœœ˜=Jšœœœ!˜0———J˜š Ÿœœœ8œœ˜dJšž>˜>Jš˜Jšœœ˜!šœœœœ˜.Jšœœ˜>—Jšœ˜šœ˜ Jšœ˜Jšœœœ˜KJšœ˜—Jšœ˜—J˜šŸœœœ˜1Jš˜Jšœ˜šœ˜ Jšœœ˜#Jšœ œœ œ˜PJ˜Jšœ˜—J˜Jšœ˜—J˜šŸœœœ˜0Jš˜J˜%Jšœœœž˜>šœ˜Jšœ'œž&œ˜W—J˜J˜FJšœœ˜J˜šœœœœ˜'Jšœœœ˜?J˜—Jšœ˜Jš œž8˜RJšœ˜—J˜š ž Ÿœœœœœœ˜`Jš˜Jšœœœ˜Jšœœœœ˜Jš˜Jšž&˜&Jšž,˜,Jšœ,ž)˜U˜Jšœ0œ œž˜[Jš œœ œœ!œ˜EJ˜—Jšœ œ˜'Jšœ˜—J˜šŸ œœ)˜AJš˜J˜:Jšœœœ&˜BJšœ/œ˜Pšœœœœ0˜XJšœ œœ$˜BJšœœœ˜%šœœœ(˜0šœ˜ Jšœ>˜@J˜/Jš˜Jšœ˜——Jšœ˜—Jšœ˜Jšœ˜—J˜šž Ÿ œœœ˜;Jš˜Jšœ˜Jšœm™mš˜Jš œœœœž˜MJšœœ˜šŸœœ"˜6š œŸœœœ*œ˜EJšœ˜——šŸœœ(œ˜DJšœž=˜CJšœ ˜šœ œœœ˜AJ˜;J˜J˜—Jšœ˜Jšœ˜—Jš œœœ>œ˜[šœžH˜KJšœœ)˜Išœ˜ Jšœ#™#Jšœ œE˜XJšœM˜MJš˜—Jšœ<˜@JšœV™VJšœ,˜,—Jšœ˜Jšœ9˜9Jšœ˜Jšœ œ ˜Jšœ œ˜'—Jšœ˜Jšœ˜—J˜šŸ œœœ˜0Jš˜Jšœœœ˜Jšœœœœ˜