VMLaundryImpl.mesa
Copyright © 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
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;
Performance parameters
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];
Global variables protected by the monitor lock
dirtyVictims: BOOL;
newVictims: BOOL;
laundry: CONDITION ← [timeout: samplingInterval];
LastLaundryErrorRecord: VMExtras.LaundryError ← [software, 0];
errorsNoted: INT ← 0;
Exports to VMStatistics
laundryWakeups, panicLaundryWakeups, uselessLaundryWakeups: PUBLIC INT ← 0;
pagesCleaned, pagesCleanedPanic: PUBLIC INT ← 0;
laundryCleanCalls: PUBLIC INT ← 0;
Exports to VMInternal
NoteDirtyVictim:
PUBLIC
ENTRY
PROC = {
dirtyVictims ← TRUE;
NOTIFY laundry;
};
NoteNewVictim:
PUBLIC
ENTRY
PROC = {
newVictims ← TRUE;
NOTIFY laundry;
};
Exports to VMExtras
ForceCleaning:
PUBLIC
ENTRY
SAFE PROC =
CHECKED {
newVictims ← TRUE;
NOTIFY laundry;
};
Laundry Process
LaundryProcess:
PROC = {
The following initialization ensures that, on the first pass through the main loop, we will actually try to clean things.
prevCleanUsed: INT ← uselessLaundryWakeups ;
oldRMNewClean:
INT ← VMStatistics.rmNewClean;
wake up with attitude aggressive to try to force some cleaning to avoid a frame fault with no unpinned clean memory
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];
};
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.
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 𡤋igHighMark ;
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
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.
EXIT;
IF interval.count >= minCleaning
THEN {
VM.Clean[interval
!
VM.AddressFault => {
interval.count ← VM.PageNumberForAddress[address] - interval.page;
CONTINUE
};
VM.CantDoIO => {
error writing a page; shrink interval to written pages and pin bad page
interval.count ← page - interval.page;
VM.Pin[ [page,1] ];
ReportError[reason, page];
CONTINUE
};
];
cleanedThisTime ← cleanedThisTime + interval.count;
*stats*--
laundryCleanCalls ← laundryCleanCalls.SUCC;
};
knownFreeOrClean ← freePages + cleanedThisTime + totalCleanSkipped - (VMStatistics.rmOldClean + VMStatistics.rmNewClean - cleanUsed) ;
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)
IF ((attitude = casual) AND (knownFreeOrClean >= highMark)) THEN EXIT;
IF (passes > startPasses + 1) OR (passes > startPasses AND rover <= startRover) THEN EXIT;
Give up the processor to avoid hogging priority 5
Process.Yield[];
ENDLOOP;
prevCleanUsed ← VMStatistics.rmOldClean + VMStatistics.rmNewClean;
};
*stats*--
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.