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, VolumeFile, VolumeID, Write ], FileBackdoor USING [ SetRoot, VolumeFlusher], 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, FileBackdoor, FileInternal, Rope, VM EXPORTS DiskFace--RelID,AbsID,Attributes--, File, FileBackdoor, FileInternal, PhysicalVolume--SetPhysicalRoot-- SHARES File = { ROPE: TYPE = Rope.ROPE; --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 { 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]; }; notCheckpointing: CONDITION; --FileInternal.--LockVolume: PUBLIC ENTRY PROC [volume: Volume] = { WHILE volume.checkpointing DO WAIT notCheckpointing ENDLOOP; volume.vamFile _ NIL; -- cf. GrabVAMFile volume.checkpointing _ TRUE; }; 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] = { RETURN[ [ fileID: [abs[AbsID[id]]], filePage: 0, attributes: Attributes[logicalRoot], dontCare: LOOPHOLE[LONG[0]] ] ] }; RootTransfer: INTERNAL PROC [volume: Volume, direction: {read, write, init} ] RETURNS [why: File.RC] = TRUSTED { 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] ] }; --FileInternal.--ReadRootPage: PUBLIC ENTRY PROC [volume: Volume] = TRUSTED { 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 { ENABLE File.Error => { volume.vamStatus _ SELECT why FROM unknownFile, unknownPage => inconsistent, ENDCASE => why; CONTINUE }; volume.lastFileID _ volume.root.lastFileID; ReadVAM[volume]; volume.vamStatus _ ok; } ELSE volume.vamStatus _ inconsistent; volume.checkpointing _ FALSE; BROADCAST notCheckpointing; ConsiderFlushing[volume]; }; --FileInternal.--InitRootPage: PUBLIC ENTRY PROC [volume: Volume, name: ROPE] RETURNS [File.RC] = TRUSTED { 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] }; GetName: PROC [ chars: LONG POINTER TO PACKED ARRAY [0..VolumeFormat.volumeLabelLength) OF CHAR, length: INT] RETURNS [ROPE] = TRUSTED { 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]] }; --File.--GetVolumeName: PUBLIC ENTRY PROC [volume: Volume] RETURNS [ROPE] = { ENABLE UNWIND => NULL; IF volume.rootStatus # ok AND volume.rootStatus # nonCedarVolume THEN RETURN WITH ERROR File.Error[volume.rootStatus]; RETURN[volume.name] }; --File.--FindVolumeFromID: PUBLIC PROC [id: File.VolumeID] RETURNS [volume: Volume] = { volume _ NIL; DO volume _ File.NextVolume[volume]; IF volume = NIL THEN EXIT; IF GetVolumeID[volume] = id THEN EXIT; ENDLOOP; }; --File.--FindVolumeFromName: PUBLIC PROC [name: ROPE] RETURNS [volume: Volume] = { 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; }; --File.--LogicalInfo: PUBLIC ENTRY PROC [volume: Volume] RETURNS [ id: File.VolumeID, size: INT, rootStatus: File.RC, name: ROPE, vamStatus: File.RC, free: INT, freeboard: INT] = { 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] }; --FileInternal.--TranslateLogicalRun: PUBLIC --EXTERNAL-- PROC [logicalRun: VolumeFormat.LogicalRun, volume: Volume] RETURNS [channel: Disk.Channel, diskPage: Disk.PageNumber] = { 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; }; --PhysicalVolume.--GetPhysical: PUBLIC PROC [volume: Volume, page: VolumeFormat.LogicalPage _ [0]] RETURNS [PhysicalVolume.Physical] = { 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] }; --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 { 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, page]; }; --FileBackdoor.--GetRoot: PUBLIC ENTRY PROC [volume: Volume, root: File.VolumeFile] RETURNS [fp: File.FP, page: File.PageNumber _ [0]] = TRUSTED { 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]; }; --FileInternal.--GetLogicalLocation: PUBLIC ENTRY PROC [volume: Volume, root: VolumeFormat.LVBootFile] RETURNS [location: BootFile.Location] = TRUSTED { 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]; }; --PhysicalVolume.--SetPhysicalRoot: PUBLIC ENTRY PROC [volume: Volume, root: File.VolumeFile] = TRUSTED { 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, volume.root.bootingInfo[root].firstPage]; }; --FileBackdoor.--MarkDebugger: PUBLIC ENTRY PROC [volume: Volume, isDebugger: BOOL] = TRUSTED { 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]; }; --FileBackdoor.--IsDebugger: PUBLIC ENTRY PROC [volume: Volume] RETURNS [BOOL] = TRUSTED { 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] }; EraseRoot: INTERNAL PROC [volume: Volume] = TRUSTED { volume.root.bootingInfo _ ALL[VolumeFormat.nullDiskFileID]; volume.root.rootFile _ ALL[]; }; idGroupSize: INT = 100; -- number if id's to allocate at a time. --FileInternal.--NewID: PUBLIC ENTRY PROC [volume: Volume] RETURNS [FileID] = TRUSTED { 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] ] }; 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] = { 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]]]; }; IsUsed: PUBLIC ENTRY PROC [volume: Volume, page: VolumeFormat.LogicalPage] RETURNS [inUse: BOOL] = { RETURN[Used[volume.vam, page]]; }; Used: PROC [vam: VAM, page: VolumeFormat.LogicalPage] RETURNS [BOOL] = TRUSTED INLINE { OPEN p: LOOPHOLE[page, PageBits]; RETURN[LOOPHOLE[@vam.used + p.ms*SIZE[VolumeFormat.VAMChunk], LONG POINTER TO VolumeFormat.VAMChunk][p.ls]]; }; SetPageUsed: PUBLIC ENTRY PROC [volume: Volume, page: VolumeFormat.LogicalPage, inUse: BOOL] RETURNS [wasInUse: BOOL] = { wasInUse _ Used[volume.vam, page]; SetUsed[volume: volume, page: page, inUse: inUse]; volume.vamChanged _ TRUE; }; SetUsed: INTERNAL PROC [volume: Volume, page: VolumeFormat.LogicalPage, inUse: BOOL] = TRUSTED INLINE { 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 { chunk[p.ls] _ inUse; IF inUse THEN volume.free _ volume.free-1 ELSE volume.free _ volume.free+1; }; }; AllocVAM: INTERNAL PROC [volume: Volume] = TRUSTED { IF volume.vam = NIL THEN { words: INT = VAMWords[volume.size]; volume.vam _ VM.AddressForPageNumber[VM.Allocate[VM.PagesForWords[words]].page]; volume.vam.size _ volume.size; }; volume.vam.rover _ [0]; }; ReadVAM: INTERNAL PROC [volume: Volume] = TRUSTED { 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 }; --FileBackdoor.--GetVolumePages: PUBLIC ENTRY PROC [volume: Volume] RETURNS [size, free, freeboard: INT] = { 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] }; --FileBackdoor.--SetFlusher: PUBLIC ENTRY PROC [volume: Volume, flusher: FileBackdoor.VolumeFlusher, data: REF ANY] = { ENABLE UNWIND => NULL; volume.flusherData _ data; volume.flusher _ flusher; IF volume.freeboard = 0 THEN volume.freeboard _ MIN[volume.size/5, 1000]; ConsiderFlushing[volume]; }; ConsiderFlushing: INTERNAL PROC [volume: Volume] = { 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] ]; } }; FlusherProcess: PROC [volume: Volume] = { DO flusher: FileBackdoor.VolumeFlusher; data: REF ANY; lack: VolumeFormat.LogicalPageCount; continue: BOOL; urgent: BOOL; Examine: ENTRY PROC [volume: Volume] = { 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; }; 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 { IF NOT urgent THEN Process.Pause[Process.MsecToTicks[ MIN[(elapsed+99)/100 * ProcessorFace.microsecondsPerHundredPulses, 500]]]; } ELSE Process.Pause[Process.MsecToTicks[1000]]; ENDLOOP; }; --FileBackdoor.--GetFlusher: PUBLIC ENTRY PROC [volume: Volume] RETURNS [FileBackdoor.VolumeFlusher, REF ANY] = { ENABLE UNWIND => NULL; RETURN[volume.flusher, volume.flusherData] }; --FileInternal.--Flush: PUBLIC PROC [volume: Volume, lack: VolumeFormat.LogicalPageCount] RETURNS [BOOL] = { flusher: FileBackdoor.VolumeFlusher; data: REF ANY; [flusher, data] _ GetFlusher[volume]; IF flusher # NIL AND flusher[volume, lack, data] THEN RETURN[TRUE]; RETURN[FALSE] }; --FileBackdoor.--SetFreeboard: PUBLIC ENTRY PROC [volume: Volume, freeboard: INT] = { ENABLE UNWIND => NULL; volume.freeboard _ freeboard; ConsiderFlushing[volume]; }; --FileInternal.--FindLargestFreeBlockInBiggestSubVolume: PUBLIC ENTRY PROC [volume: Volume] RETURNS [first: VolumeFormat.LogicalPage _ [0], count: VolumeFormat.LogicalPageCount _ 0, subVolume: PhysicalVolume.SubVolumeDetails _ NIL] = TRUSTED{ vam: VAM; pos: INT ; logicalPos: VolumeFormat.LogicalPage; currentFirst: INT ; vamLast: INT ; vam _ volume.vam; FOR sv: LIST OF PhysicalVolume.SubVolumeDetails _ volume.subVolumes, sv.rest UNTIL sv = NIL DO IF subVolume = NIL OR sv.first.size > subVolume.size THEN subVolume _ sv.first; ENDLOOP; vamLast _ subVolume.start + subVolume.size ; currentFirst _ vamLast + 1 ; FOR pos IN [ subVolume.start..vamLast ) DO logicalPos _ [pos]; IF Used[vam, logicalPos] OR pos = vamLast - 1 THEN { IF pos-currentFirst >= count THEN { first _ [currentFirst] ; count _ pos-currentFirst+1; } ELSE currentFirst _ vamLast + 1; } ELSE IF currentFirst = vamLast + 1 THEN currentFirst _ pos ; ENDLOOP; }; --FileInternal.--Alloc: PUBLIC ENTRY PROC [volume: Volume, first: VolumeFormat.LogicalPage, size, min: VolumeFormat.LogicalPageCount] RETURNS [given: VolumeFormat.LogicalRun] = TRUSTED { 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 { 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 }; }; 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]; }; --FileInternal.--Free: PUBLIC ENTRY PROC [volume: Volume, logicalRun: VolumeFormat.LogicalRun] = { 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; }; vamFileRelease: CONDITION; GrabVAMFile: ENTRY PROC [volume: Volume] RETURNS [file: File.Handle] = TRUSTED { 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 }; ReleaseVAMFile: ENTRY PROC [volume: Volume, file: File.Handle, changed: BOOL] = TRUSTED { ENABLE UNWIND => NULL; volume.vamChanged _ volume.vamChanged OR changed--indicates if commit failed--; IF (volume.vamFile _ file) = NIL THEN volume.vamStatus _ inconsistent; BROADCAST vamFileRelease; }; --FileInternal.--Commit: PUBLIC PROC [volume: Volume] = TRUSTED { 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]; }; InnerCommit: PROC [volume: Volume, vamFile: File.Handle] = TRUSTED { 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 { VM.MakeUnchanged[[page: baseVMPage, count: vmPagesPerFilePage]]; File.Write[vamFile, filePage, 1, fileBaseAddr]; EXIT }; ENDLOOP; ENDLOOP; }; --FileBackdoor.--EraseVolume: PUBLIC PROC [volume: Volume] = TRUSTED { EntryErase[volume]; { 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 { 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; }; FOR sv: LIST OF PhysicalVolume.SubVolumeDetails _ volume.subVolumes, sv.rest UNTIL sv = NIL DO IF volRoot >= sv.first.start AND volRoot < sv.first.start + sv.first.size THEN { 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]; } 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]; FileBackdoor.SetRoot[VAM, vamFile]; ReleaseVAMFile[volume, vamFile, FALSE]; }; }; EntryErase: ENTRY PROC [volume: Volume] = TRUSTED { 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 }; CommitVAMFiles: PROC = { 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; }; TRUSTED { Process.Detach[FORK CommitVAMFiles[]]; }; }. άLogicalVolumeImpl.mesa - Access to per-volume data structures: root page and VAM Copyright c 1985 by Xerox Corporation. All rights reserved. Andrew Birrell December 8, 1983 9:48 am Levin, September 22, 1983 3:32 pm Bob Hagmann, March 19, 1985 3:06:49 pm PST Doug Wyatt, February 27, 1985 10:03:29 am PST Russ Atkinson (RRA) May 14, 1985 1:10:00 pm PDT ******** Data Types and minor subroutines ******** -- Called before a checkpoint; unlock by calling ReadRootPage ******** Access to volume root page and volume's list of sub-volumes ******** -- see also the defaults in the declaration of VolumeFormat.LogicalRoot {name, vamStatus, free, freeboard} only valid if rootStatus is ok or nonCedarVolume {free, freeboard} only valid if vamStatus is ok Accesses only immutable fields of the volume If root = VAM then I hope you know what you're doing! ******** Volume Allocation Map ******** SIZE[VAMObject[VAMChunks[pages]]], but compiler can't handle it -- vam.used[p.ms][p.ls]], but the compiler can't handle it! vam.used[p.ms][p.ls]], but the compiler can't handle it! Special routine to find the largest free piece of disk for allocating the VM Backing File on biggest sub-volume 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. Ensure VAM is up to date on disk -- Assumes volume.vam is vm-page-aligned. -- We now own the (not-yet-created) volume.vamFile, but we're outside our monitor so File.Create won't deadlock! FileInternal.FreeRun is restricted to LAST[CARDINAL] pages Don't free root (!), nor first page of sub-volumes (to break up runs) Avoid overwriting volume root page! Note: ideally, we shouldn't write free labels on bad pages, but it doesn't matter much Bob Hagmann January 29, 1985 1:55:14 pm PST Cedar 6.0 conversion- change to FileBackdoor interface Bob Hagmann March 19, 1985 3:06:49 pm PST changes to: IsUsed Κ#˜codešœP™PKšœ Οmœ1™žœ˜FKšœ6žœžœ˜UKšžœ˜Kšœ˜—K˜Kšœž œ˜K˜š Ÿ  œžœžœžœ˜CKšœ:™:Kšžœžœžœžœ˜žœžœž˜^šžœ$žœ3ž˜`Kšžœb˜h—Kš žœžœžœžœžœ˜=Kšžœ˜—Kšœ˜—K˜š Ÿ  œžœžœ8žœ˜ˆš žœžœžœ>žœžœž˜^šžœžœ&ž˜HKšžœ˜—Kšžœ˜—Kšžœžœ˜ Kšœ˜—K˜š Ÿ œžœžœžœ2žœ_žœ˜ΞKšžœžœžœ˜Kšœ žœ˜ Kšžœžœžœžœ˜˜>Kšœ6˜6—Kšœ˜—K˜š Ÿ œžœžœžœ+žœ˜iKšžœžœžœ˜Kšœ"˜"Kšžœžœžœžœ˜<šžœžœ$ž˜EKšžœžœžœ˜0—šœK˜KKšœ%˜%—šžœ ž˜Kšžœžœžœ=˜N—Kšœ˜—K˜š Ÿ  œžœžœžœžœžœ˜_Kšžœžœžœ˜Kšœ žœ˜ Kšžœžœžœžœ˜—šžœžœ˜Kšœ˜Kšžœžœžœ˜KKšœ˜—Kšœ˜—K˜š œžœžœžœ˜4šžœžœžœ˜Kšœžœ˜#Kšœ žœžœ žœ˜PK˜Kšœ˜—K˜Kšœ˜—K˜š œžœžœžœ˜3K˜%KšžœžœžœŸ˜>šœ˜Kšœ'žœŸ&œ˜W—K˜K˜FKšœžœ˜K˜šž˜Kšžœžœžœ˜$Kšžœžœžœ˜?K˜Kšžœ˜—Kšž œŸ8˜RKšœ˜—K˜š Ÿ œžœžœžœžœžœ˜lKšžœžœžœ˜Kšžœžœžœžœ˜žœžœž˜^Kšžœ žœžœ žœ˜OKšžœ˜—Kšœ,˜,Kšœ˜šžœžœž˜*Kšœ˜šžœžœ˜-šžœ˜šžœ˜šžœ˜Kšœ˜Kšœ˜K˜—Kšžœ˜ —K˜—Kšžœžœžœ˜<—Kšžœ˜—˜K˜—K˜š Ÿ œžœžœžœ]žœ$žœ˜ΊKšžœžœžœ˜KšœJ™JKšœ%žœžœ˜OKšœ˜Kšœžœ˜ Kšžœžœžœžœ˜˜@K˜/Kšž˜Kšœ˜—Kšžœ˜—Kšžœ˜—Kšœ˜—K˜š Ÿ  œžœžœžœ˜FKšœ˜Kšœm™mšœ˜Kš žœžœžœžœŸ˜MKšœžœ˜š œžœ#žœ˜9Kš œžœžœ+žœ˜DKšœ ˜ Kšœ˜—š œžœ)žœžœ˜GKšœ:™:šžœ ž˜Kšœžœžœžœ˜>K˜;K˜K˜Kšžœ˜—Kšœ˜—š žœžœžœ>žœž˜^KšœE™Ešžœžœ)˜Išžœ˜Kšœ#™#Kšžœ žœE˜XKšœM˜MKšœ˜—Kšžœ<˜@—KšœV™VKšœ,˜,Kšžœ˜—Kšœ9˜9Kšœ˜Kšœžœ ˜#Kšœ žœ˜'Kšœ˜—Kšœ˜—K˜š  œžœžœžœ˜3Kšžœžœžœ˜Kšžœžœžœžœ˜