<> <> <> <> 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; <<-- ******** Creation and writing of the VAM file ******** -->> <<>> <> 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.