DIRECTORY Process USING [Detach, MsecToTicks, priorityFaultHandlers, SetPriority, Ticks, Yield], VM USING [AddressFault, CantDoIO, Clean, Interval, IOErrorType, PageCount, PageNumber, PageNumberForAddress, Pin], VMExtras USING [LaundryError], 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 VMInternal, VMStatistics, VMExtras = 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: VMExtras.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 [VMExtras.LaundryError] = CHECKED { RETURN[LastLaundryErrorRecord]; }; Process.Detach[FORK LaundryProcess[]]; END. ÈVMLaundryImpl.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Levin on December 16, 1983 1:38 pm Bob Hagmann on May 31, 1984 12:21:39 pm PDT Russ Atkinson on October 26, 1984 5:49:05 pm PDT Performance parameters Global variables protected by the monitor lock Exports to VMStatistics Exports to VMInternal Exports to VMExtras 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*-- ʘ˜šœ™Jšœ Ïmœ1™˜>Jšœ žœ˜J˜Jšœ™J˜Jšœ<ž œ˜KJšœ!ž œ˜0Jšœžœžœ˜"J˜Jšœ™J˜šÏnœžœžœžœ˜&Jšœžœ˜Jšžœ ˜Jšœ˜—J˜šŸ œžœžœžœ˜$Jšœ žœ˜Jšžœ ˜Jšœ˜—J˜Jšœ™J˜š Ÿ œžœžœž œžœ˜1Jšœ žœ˜Jšžœ ˜Jšœ˜—J˜Jšœ™J˜šŸœžœ˜Jšœy™yJšœžœ˜,šœžœ˜-Jšœs™s—Jšœ$˜$Jšœ$˜$Jšœ˜J˜šŸœžœžœžœ˜*šŸ œžœžœ˜=Jšœžœ˜Jšœ žœ˜Jšœ(˜(Jšžœ˜ J˜—Jšžœžœžœ˜/Jšžœ)žœ žœžœ˜]Jšœžœ˜šž˜Jšžœ ˜ Jšžœžœžœ˜"Jšžœ˜—Jšžœžœžœ˜/Jšžœ)žœ žœžœ˜]Jšžœ ˜Jšœ˜—Jšœí™íJ˜J˜3šž˜Jšœ žœ6˜DJšœžœ˜Jšœ˜Jšœ žœ˜Jšœ˜Jšœ˜Jšœžœ Ðci˜5šÏc ˜ Jšœ žœ˜%Jšžœžœ+žœ˜H—Jšœ˜šžœžœ˜Jšœ˜Jš œžœžœžœžœžœžœ˜UJ˜—šžœžœ5žœ˜Ršžœž˜$Jšœ žœ ˜Jšœ˜Jšœžœ˜ Jšœ˜šœf˜fJšœ¡9˜SJšœ(˜(Jšœ˜Jšœ˜—Jšžœžœ.˜EJšœ6˜6šžœž˜Jšœº™ºJšžœ˜—šžœžœ˜'šžœ˜šœžœ˜Jšœžœ/˜BJšž˜J˜—šœ˜J™GJšœ&˜&Jšœ˜Jšœ˜Jšž˜Jšœ˜—Jšœ˜—Jšœ3˜3šœ ™ Jšœ&žœ˜+—J˜—Jšœ†˜†˜J™±—Jšžœžœ!žœžœ˜FJš žœžœžœžœžœ˜ZJšœ1™1J˜Jšžœ˜—JšœB˜BJ˜—šœ ™ Jšžœžœ/ž˜Nšžœ˜Jšœ.˜.Jšžœžœ9˜QJ˜——Jšœ˜Jšœ˜Jšžœžœžœžœ˜pJšœ˜Jšžœ˜ —J˜—J˜š Ÿ œžœžœžœžœ˜NJšœ+˜+Jšœ˜J˜—J˜šŸœžœžœžœžœžœžœ˜TJšžœ˜J˜—J˜J˜Jšœžœ˜&J˜Jšžœ˜—…—dÄ