DIRECTORY Process USING [Detach, MsecToTicks, priorityFaultHandlers, SetPriority, Ticks, Yield], VM USING [AddressFault, CantDoIO, Clean, Interval, IOErrorType, LaundryError, PageCount, PageNumber, PageNumberForAddress, Pin], VMInternal USING [freePages, LaundryMode, GetCleaningCandidate, HasBackingStorage, PageCount, RealPageNumber, swapBufferSize], VMSideDoor USING [rmPages], VMStatistics USING [rmNewClean, rmOldClean]; VMLaundryImpl: MONITOR IMPORTS Process, VM, VMInternal, VMSideDoor, VMStatistics EXPORTS VM, VMInternal, VMStatistics = BEGIN OPEN VMInternal; normalHighMark: NAT = MIN[VMSideDoor.rmPages/10, 150]; bigHighMark: NAT _ MIN[VMSideDoor.rmPages/5, 400]; lowMark: NAT = normalHighMark/3; highMark: NAT _ bigHighMark; samplingInterval: Process.Ticks = Process.MsecToTicks[250]; dirtyVictims: BOOL; newVictims: BOOL; laundry: CONDITION _ [timeout: samplingInterval]; LastLaundryErrorRecord: VM.LaundryError _ [software, 0]; errorsNoted: INT _ 0; laundryWakeups, panicLaundryWakeups, uselessLaundryWakeups: PUBLIC INT _ 0; pagesCleaned, pagesCleanedPanic: PUBLIC INT _ 0; laundryCleanCalls: PUBLIC INT _ 0; NoteDirtyVictim: PUBLIC ENTRY PROC = { dirtyVictims _ TRUE; NOTIFY laundry; }; NoteNewVictim: PUBLIC ENTRY PROC = { newVictims _ TRUE; NOTIFY laundry; }; ForceCleaning: PUBLIC ENTRY SAFE PROC = CHECKED { newVictims _ TRUE; NOTIFY laundry; }; LaundryProcess: PROC = { prevCleanUsed: INT _ uselessLaundryWakeups ; oldRMNewClean: INT _ VMStatistics.rmNewClean; attitude: LaundryMode _ aggressive ; prevAttitude: LaundryMode _ casual ; prevMin: PageCount _ 1 ; Wait: ENTRY PROC RETURNS [LaundryMode] = { WaitReturn: PROC [mode: LaundryMode] RETURNS [LaundryMode]= { dirtyVictims _ FALSE; newVictims _ FALSE ; oldRMNewClean _ VMStatistics.rmNewClean; RETURN[mode]; }; IF dirtyVictims THEN RETURN[WaitReturn[panic]]; IF oldRMNewClean # VMStatistics.rmNewClean OR newVictims THEN RETURN[WaitReturn[aggressive]]; dirtyVictims _ FALSE; DO WAIT laundry; IF HasBackingStorage[0] THEN EXIT; ENDLOOP; IF dirtyVictims THEN RETURN[WaitReturn[panic]]; IF oldRMNewClean # VMStatistics.rmNewClean OR newVictims THEN RETURN[WaitReturn[aggressive]]; RETURN[casual]; }; Process.SetPriority[Process.priorityFaultHandlers]; DO cleanUsed: INT = VMStatistics.rmOldClean + VMStatistics.rmNewClean ; cleanedThisTime: INT _ 0; totalCleanSkipped: PageCount ; startPasses: INT _ -1 ; startRover: RealPageNumber _ 0; minCleaning: PageCount _ 1; knownFreeOrClean: INT _ lowMark; -- best start guess --*stats*-- laundryWakeups _ laundryWakeups.SUCC; IF attitude = panic THEN panicLaundryWakeups _ panicLaundryWakeups.SUCC; totalCleanSkipped _ 0 ; IF attitude # casual THEN { highMark _bigHighMark ; minCleaning _ IF prevAttitude = casual THEN 10 ELSE IF prevMin = 10 THEN 4 ELSE 1 ; }; IF attitude # casual OR (cleanUsed - prevCleanUsed) >= (highMark - lowMark) THEN { UNTIL cleanedThisTime >= highMark DO interval: VM.Interval; cleanSkipped: PageCount ; passes: INT ; rover: RealPageNumber ; [interval: interval, cleanSkipped: cleanSkipped, passes: passes, rover: rover] _ GetCleaningCandidate[ desired: swapBufferSize, -- larger run than this means multiple transfers in Clean comfortLevel: lowMark + cleanedThisTime, tryHard: attitude ]; IF startPasses < 0 THEN { startPasses _ passes; startRover _ rover;}; totalCleanSkipped _ totalCleanSkipped + cleanSkipped ; IF interval.count = 0 THEN EXIT; IF interval.count >= minCleaning THEN { VM.Clean[interval ! VM.AddressFault => { interval.count _ VM.PageNumberForAddress[address] - interval.page; CONTINUE }; VM.CantDoIO => { interval.count _ page - interval.page; VM.Pin[ [page,1] ]; ReportError[reason, page]; CONTINUE }; ]; cleanedThisTime _ cleanedThisTime + interval.count; laundryCleanCalls _ laundryCleanCalls.SUCC; }; knownFreeOrClean _ freePages + cleanedThisTime + totalCleanSkipped - (VMStatistics.rmOldClean + VMStatistics.rmNewClean - cleanUsed) ; IF ((attitude = casual) AND (knownFreeOrClean >= highMark)) THEN EXIT; IF (passes > startPasses + 1) OR (passes > startPasses AND rover <= startRover) THEN EXIT; Process.Yield[]; ENDLOOP; prevCleanUsed _ VMStatistics.rmOldClean + VMStatistics.rmNewClean; }; IF cleanedThisTime = 0 THEN uselessLaundryWakeups _ uselessLaundryWakeups.SUCC ELSE { pagesCleaned _ pagesCleaned + cleanedThisTime; IF attitude = panic THEN pagesCleanedPanic _ pagesCleanedPanic + cleanedThisTime; }; prevAttitude _ attitude; prevMin _ minCleaning ; IF knownFreeOrClean < lowMark AND cleanedThisTime >= highMark THEN attitude _ aggressive ELSE attitude _ Wait[]; highMark _ normalHighMark ; ENDLOOP; }; ReportError: ENTRY PROC [ errorType: VM.IOErrorType, page: VM.PageNumber ] = { LastLaundryErrorRecord _ [errorType,page] ; errorsNoted _ errorsNoted + 1; }; LastLaundryError: PUBLIC ENTRY SAFE PROC RETURNS [VM.LaundryError] = CHECKED { RETURN[LastLaundryErrorRecord]; }; Process.Detach[FORK LaundryProcess[]]; END.  VMLaundryImpl.mesa Copyright c 1984, 1985 by Xerox Corporation. All rights reserved. Levin on December 16, 1983 1:38 pm Bob Hagmann on January 30, 1985 11:05:32 am PST Russ Atkinson on January 30, 1985 10:22:11 pm PST Performance parameters Global variables protected by the monitor lock Exports to VMStatistics Exports to VMInternal Exports to VM Laundry Process The following initialization ensures that, on the first pass through the main loop, we will actually try to clean things. wake up with attitude aggressive to try to force some cleaning to avoid a frame fault with no unpinned clean memory We want to run above the client foreground processes. Perhaps we should multiplex with the guys at priorityClient3? No reserved state vector is required, since this process is a vanilla client of Clean. However, it would be nice to include one anyway, since it is potentially possible for a fault in this process to prohibit anyone else from getting into SwapIn. There is nothing to clean, either because no appropriate dirty, unpinned pages could be found or because there were sufficient clean, unreferenced pages aleady available for reclamation. error writing a page; shrink interval to written pages and pin bad page *stats*-- Stop loop if we are not in panic, and there appears to be enough clean pages or we have done a pass through memory in this loop (note that memory is scanned in decreasing order) Give up the processor to avoid hogging priority 5 *stats*-- Bob Hagmann January 30, 1985 10:22:11 pm PST eliminate VMExtras for 6.0 changes to: DIRECTORY, LastLaundryErrorRecord, VMLaundryImpl, normalHighMark, bigHighMark, lowMark, highMark, samplingInterval, dirtyVictims, newVictims, laundry, LastLaundryErrorRecord, errorsNoted, laundryWakeups, pagesCleaned, laundryCleanCalls, NoteDirtyVictim, NoteNewVictim, ForceCleaning, LaundryProcess, Wait (local of LaundryProcess), WaitReturn (local of Wait, local of LaundryProcess), ReportError, LastLaundryError, Process, END, LastLaundryErrorRecord, LastLaundryError, VMLaundryImpl ΚΑ˜codešœ™Kšœ Οmœ7™BK™"K™/K™1—K˜šΟk ˜ KšœžœI˜VKšžœžœy˜Kšœ žœn˜~Kšœ žœ ˜Kšœ žœ˜,—K˜šœž˜Kšžœ žœ&˜9Kšžœžœ˜$Kšœž œ ˜—K˜šœ™K™Kšœžœžœ˜6Kšœ žœžœ˜2Kšœ žœ˜ K˜Kšœ žœ˜K˜Kšœ;˜;K™—™.K˜Kšœžœ˜Kšœ žœ˜Kšœ ž œ˜1K˜Kšœžœ˜8Kšœ žœ˜K˜—šœ™K˜Kšœ<ž œ˜KKšœ!ž œ˜0Kšœžœžœ˜"K˜—Kšœ™K˜šΟnœžœžœžœ˜&Kšœžœ˜Kšžœ ˜Kšœ˜—K˜šŸ œžœžœžœ˜$Kšœ žœ˜Kšžœ ˜Kšœ˜—K˜Kšœ ™ K˜š Ÿ œžœžœž œžœ˜1Kšœ žœ˜Kšžœ ˜Kšœ˜—K˜Kšœ™K˜šŸœžœ˜Kšœy™yKšœžœ˜,šœžœ˜-Kšœs™s—Kšœ$˜$Kšœ$˜$Kšœ˜K˜šŸœžœžœžœ˜*šŸ œžœžœ˜=Kšœžœ˜Kšœ žœ˜Kšœ(˜(Kšžœ˜ K˜—Kšžœžœžœ˜/Kšžœ)žœ žœžœ˜]Kšœžœ˜šž˜Kšžœ ˜ Kšžœžœžœ˜"Kšžœ˜—Kšžœžœžœ˜/Kšžœ)žœ žœžœ˜]Kšžœ ˜Kšœ˜—Kšœν™νK˜K˜3šž˜Kšœ žœ6˜DKšœžœ˜Kšœ˜Kšœ žœ˜Kšœ˜Kšœ˜Kšœžœ Πci˜5šΟc ˜ Kšœ žœ˜%Kšžœžœ+žœ˜H—Kšœ˜šžœžœ˜Kšœ˜Kš œžœžœžœžœžœžœ˜UK˜—šžœžœ5žœ˜Ršžœž˜$Kšœ žœ ˜Kšœ˜Kšœžœ˜ Kšœ˜šœf˜fKšœ‘9˜SKšœ(˜(Kšœ˜Kšœ˜—Kšžœžœ.˜EKšœ6˜6šžœž˜KšœΊ™ΊKšžœ˜—šžœžœ˜'šžœ˜šœžœ˜Kšœžœ/˜BKšž˜K˜—šœ˜K™GKšœ&˜&Kšœ˜Kšœ˜Kšž˜Kšœ˜—Kšœ˜—Kšœ3˜3šœ ™ Kšœ&žœ˜+—K˜—Kšœ†˜†˜K™±—Kšžœžœ!žœžœ˜FKš žœžœžœžœžœ˜ZKšœ1™1K˜Kšžœ˜—KšœB˜BK˜—šœ ™ Kšžœžœ/ž˜Nšžœ˜Kšœ.˜.Kšžœžœ9˜QK˜——Kšœ˜Kšœ˜Kšžœžœžœžœ˜pKšœ˜Kšžœ˜ —K˜—K˜š Ÿ œžœžœžœžœ˜NKšœ+˜+Kšœ˜K˜—K˜šŸœžœžœžœžœžœžœžœ˜NKšžœ˜K˜—K˜K˜Kšœžœ˜&K˜Kšžœ˜™,K™Kšœ Οr°œ’ œ)’f™ρ—K™K™K™K™K™—…—@"