825 lines
20 KiB
C
825 lines
20 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991-1994 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
balmgr.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the NT balance set manager. Normally the kernel
|
|||
|
does not contain "policy" code. However, the balance set manager needs
|
|||
|
to be able to traverse the kernel data structures and, therefore, the
|
|||
|
code has been located as logically part of the kernel.
|
|||
|
|
|||
|
The balance set manager performs the following operations:
|
|||
|
|
|||
|
1. Makes the kernel stack of threads that have been waiting for a
|
|||
|
certain amount of time, nonresident.
|
|||
|
|
|||
|
2. Removes processes from the balance set when memory gets tight
|
|||
|
and brings processes back into the balance set when there is
|
|||
|
more memory available.
|
|||
|
|
|||
|
3. Makes the kernel stack resident for threads whose wait has been
|
|||
|
completed, but whose stack is nonresident.
|
|||
|
|
|||
|
4. Arbitrarily boosts the priority of a selected set of threads
|
|||
|
to prevent priority inversion in variable priority levels.
|
|||
|
|
|||
|
In general, the balance set manager only is active during periods when
|
|||
|
memory is tight.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
David N. Cutler (davec) 13-Jul-1991
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ki.h"
|
|||
|
|
|||
|
//
|
|||
|
// Define balance set wait object types.
|
|||
|
//
|
|||
|
|
|||
|
typedef enum _BALANCE_OBJECT {
|
|||
|
TimerExpiration,
|
|||
|
WorkingSetManagerEvent,
|
|||
|
MaximumObject
|
|||
|
} BALANCE_OBJECT;
|
|||
|
|
|||
|
//
|
|||
|
// Define maximum number of thread stacks that can be out swapped in
|
|||
|
// a single time period.
|
|||
|
//
|
|||
|
|
|||
|
#define MAXIMUM_THREAD_STACKS 20
|
|||
|
|
|||
|
//
|
|||
|
// Define periodic wait interval value.
|
|||
|
//
|
|||
|
|
|||
|
#define PERIODIC_INTERVAL (1 * 1000 * 1000 * 10)
|
|||
|
|
|||
|
//
|
|||
|
// Define amount of time a thread can be in the ready state without having
|
|||
|
// is priority boosted (approximately 4 seconds).
|
|||
|
//
|
|||
|
|
|||
|
#define READY_WITHOUT_RUNNING (4 * 75)
|
|||
|
|
|||
|
//
|
|||
|
// Define kernel stack protect time. For small systems the protect time
|
|||
|
// is 3 seconds. For all other systems, the protect time is 7 seconds.
|
|||
|
//
|
|||
|
|
|||
|
#define SMALL_SYSTEM_STACK_PROTECT_TIME (3 * 75)
|
|||
|
#define STACK_PROTECT_TIME (7 * 75)
|
|||
|
#define STACK_SCAN_PERIOD 4
|
|||
|
ULONG KiStackProtectTime;
|
|||
|
|
|||
|
//
|
|||
|
// Define number of threads to scan each period and the priority boost bias.
|
|||
|
//
|
|||
|
|
|||
|
#define THREAD_BOOST_BIAS 2
|
|||
|
#define THREAD_BOOST_PRIORITY (LOW_REALTIME_PRIORITY - THREAD_BOOST_BIAS)
|
|||
|
#define THREAD_SCAN_PRIORITY (THREAD_BOOST_PRIORITY - 1)
|
|||
|
#define THREAD_READY_COUNT 10
|
|||
|
#define THREAD_SCAN_COUNT 16
|
|||
|
|
|||
|
//
|
|||
|
// Define local procedure prototypes.
|
|||
|
//
|
|||
|
|
|||
|
VOID
|
|||
|
KiInSwapKernelStacks (
|
|||
|
IN KIRQL PreviousIrql
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
KiInSwapProcesses (
|
|||
|
IN KIRQL PreviousIrql
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
KiOutSwapKernelStacks (
|
|||
|
IN KIRQL PreviousIrql
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
KiOutSwapProcesses (
|
|||
|
IN KIRQL PreviousIrql
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
KiScanReadyQueues (
|
|||
|
VOID
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Define thread table index static data.
|
|||
|
//
|
|||
|
|
|||
|
ULONG KiReadyQueueIndex = 1;
|
|||
|
|
|||
|
//
|
|||
|
// Define swap request flag.
|
|||
|
//
|
|||
|
|
|||
|
BOOLEAN KiStackOutSwapRequest = FALSE;
|
|||
|
|
|||
|
VOID
|
|||
|
KeBalanceSetManager (
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is the startup code for the balance set manager. The
|
|||
|
balance set manager thread is created during system initialization
|
|||
|
and begins execution in this function.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Supplies a pointer to an arbitrary data structure (NULL).
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
LARGE_INTEGER DueTime;
|
|||
|
KTIMER PeriodTimer;
|
|||
|
KIRQL OldIrql;
|
|||
|
ULONG StackScanPeriod;
|
|||
|
NTSTATUS Status;
|
|||
|
KWAIT_BLOCK WaitBlockArray[MaximumObject];
|
|||
|
PVOID WaitObjects[MaximumObject];
|
|||
|
|
|||
|
//
|
|||
|
// Raise the thread priority to the lowest realtime level.
|
|||
|
//
|
|||
|
|
|||
|
KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY);
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the periodic timer, set it to expire one period from
|
|||
|
// now, and set the stack scan period.
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeTimer(&PeriodTimer);
|
|||
|
DueTime.QuadPart = - PERIODIC_INTERVAL;
|
|||
|
KeSetTimer(&PeriodTimer, DueTime, NULL);
|
|||
|
StackScanPeriod = STACK_SCAN_PERIOD;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the wait objects array.
|
|||
|
//
|
|||
|
|
|||
|
WaitObjects[TimerExpiration] = (PVOID)&PeriodTimer;
|
|||
|
WaitObjects[WorkingSetManagerEvent] = (PVOID)&MmWorkingSetManagerEvent;
|
|||
|
|
|||
|
//
|
|||
|
// Loop forever processing balance set manager events.
|
|||
|
//
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// Wait for a memory management memory low event, a swap event,
|
|||
|
// or the expiration of the period timout rate that the balance
|
|||
|
// set manager runs at.
|
|||
|
//
|
|||
|
|
|||
|
Status = KeWaitForMultipleObjects(MaximumObject,
|
|||
|
&WaitObjects[0],
|
|||
|
WaitAny,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL,
|
|||
|
&WaitBlockArray[0]);
|
|||
|
|
|||
|
//
|
|||
|
// Switch on the wait status.
|
|||
|
//
|
|||
|
|
|||
|
switch (Status) {
|
|||
|
|
|||
|
//
|
|||
|
// Periodic timer expiration.
|
|||
|
//
|
|||
|
|
|||
|
case TimerExpiration:
|
|||
|
|
|||
|
//
|
|||
|
// Attempt to initiate outswaping of kernel stacks.
|
|||
|
//
|
|||
|
|
|||
|
StackScanPeriod -= 1;
|
|||
|
if (StackScanPeriod == 0) {
|
|||
|
StackScanPeriod = STACK_SCAN_PERIOD;
|
|||
|
KiLockDispatcherDatabase(&OldIrql);
|
|||
|
if (KiStackOutSwapRequest == FALSE) {
|
|||
|
KiStackOutSwapRequest = TRUE;
|
|||
|
KiUnlockDispatcherDatabase(OldIrql);
|
|||
|
KeSetEvent(&KiSwapEvent, 0, FALSE);
|
|||
|
|
|||
|
} else {
|
|||
|
KiUnlockDispatcherDatabase(OldIrql);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Adjust the depth of lookaside lists.
|
|||
|
//
|
|||
|
|
|||
|
ExAdjustLookasideDepth();
|
|||
|
|
|||
|
//
|
|||
|
// Scan ready queues and boost thread priorities as appropriate.
|
|||
|
//
|
|||
|
|
|||
|
KiScanReadyQueues();
|
|||
|
|
|||
|
//
|
|||
|
// Execute the virtual memory working set manager.
|
|||
|
//
|
|||
|
|
|||
|
MmWorkingSetManager();
|
|||
|
|
|||
|
//
|
|||
|
// Set the timer to expire at the next periodic interval.
|
|||
|
//
|
|||
|
|
|||
|
KeSetTimer(&PeriodTimer, DueTime, NULL);
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// Working set manager event.
|
|||
|
//
|
|||
|
|
|||
|
case WorkingSetManagerEvent:
|
|||
|
|
|||
|
//
|
|||
|
// Call the working set manager to trim working sets.
|
|||
|
//
|
|||
|
|
|||
|
MmWorkingSetManager();
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// Illegal return status.
|
|||
|
//
|
|||
|
|
|||
|
default:
|
|||
|
KdPrint(("BALMGR: Illegal wait status, %lx =\n", Status));
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
} while (TRUE);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KeSwapProcessOrStack (
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This thread controls the swapping of processes and kernel stacks. The
|
|||
|
order of evaluation is:
|
|||
|
|
|||
|
Outswap kernel stacks
|
|||
|
Outswap processes
|
|||
|
Inswap processes
|
|||
|
Inswap kernel stacks
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Supplies a pointer to the routine context - not used.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
KIRQL OldIrql;
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
//
|
|||
|
// Raise the thread priority to the lowest realtime level + 7 (i.e.,
|
|||
|
// priority 23).
|
|||
|
//
|
|||
|
|
|||
|
KeSetPriorityThread(KeGetCurrentThread(), LOW_REALTIME_PRIORITY + 7);
|
|||
|
|
|||
|
if (MmQuerySystemSize() == MmSmallSystem) {
|
|||
|
KiStackProtectTime = SMALL_SYSTEM_STACK_PROTECT_TIME;
|
|||
|
} else {
|
|||
|
KiStackProtectTime = STACK_PROTECT_TIME;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Loop for ever processing swap events.
|
|||
|
//
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// Wait for a swap event to occur.
|
|||
|
//
|
|||
|
|
|||
|
Status = KeWaitForSingleObject(&KiSwapEvent,
|
|||
|
Executive,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL);
|
|||
|
|
|||
|
//
|
|||
|
// Raise IRQL to dispatcher level and lock dispatcher database.
|
|||
|
//
|
|||
|
|
|||
|
KiLockDispatcherDatabase(&OldIrql);
|
|||
|
|
|||
|
//
|
|||
|
// Loop until all of the four possible actions cannot be initiated.
|
|||
|
//
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// If a request has been made to out swap kernel stacks, then
|
|||
|
// attempt to outswap kernel stacks. Otherwise, if the process
|
|||
|
// out swap list is not empty, then initiate process outswapping.
|
|||
|
// Otherwise, if the process inswap list is not empty, then start
|
|||
|
// process inswapping. Otherwise, if the kernal stack inswap list
|
|||
|
// is not active, then initiate kernel stack inswapping. Otherwise,
|
|||
|
// no work is available.
|
|||
|
//
|
|||
|
|
|||
|
if (KiStackOutSwapRequest != FALSE) {
|
|||
|
KiStackOutSwapRequest = FALSE;
|
|||
|
KiOutSwapKernelStacks(OldIrql);
|
|||
|
continue;
|
|||
|
|
|||
|
} else if (IsListEmpty(&KiProcessOutSwapListHead) == FALSE) {
|
|||
|
KiOutSwapProcesses(OldIrql);
|
|||
|
continue;
|
|||
|
|
|||
|
} else if (IsListEmpty(&KiProcessInSwapListHead) == FALSE) {
|
|||
|
KiInSwapProcesses(OldIrql);
|
|||
|
continue;
|
|||
|
|
|||
|
} else if (IsListEmpty(&KiStackInSwapListHead) == FALSE) {
|
|||
|
KiInSwapKernelStacks(OldIrql);
|
|||
|
continue;
|
|||
|
|
|||
|
} else {
|
|||
|
break;
|
|||
|
}
|
|||
|
} while (TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// Unlock the dispatcher database and lower IRQL to its previous
|
|||
|
// value.
|
|||
|
//
|
|||
|
|
|||
|
KiUnlockDispatcherDatabase(OldIrql);
|
|||
|
} while (TRUE);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KiInSwapKernelStacks (
|
|||
|
IN KIRQL PreviousIrql
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function in swaps the kernel stack for threads whose wait has been
|
|||
|
completed and whose kernel stack is nonresident.
|
|||
|
|
|||
|
N.B. The dispatcher data lock is held on entry to this routine and must
|
|||
|
be help on exit to this routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PreviousIrql - Supplies the previous IRQL.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PLIST_ENTRY NextEntry;
|
|||
|
KIRQL OldIrql;
|
|||
|
PKTHREAD Thread;
|
|||
|
|
|||
|
//
|
|||
|
// Process the stack in swap list and for each thread removed from the
|
|||
|
// list, make its kernel stack resident, and ready it for execution.
|
|||
|
//
|
|||
|
|
|||
|
OldIrql = PreviousIrql;
|
|||
|
NextEntry = KiStackInSwapListHead.Flink;
|
|||
|
while (NextEntry != &KiStackInSwapListHead) {
|
|||
|
Thread = CONTAINING_RECORD(NextEntry, KTHREAD, WaitListEntry);
|
|||
|
RemoveEntryList(NextEntry);
|
|||
|
KiUnlockDispatcherDatabase(OldIrql);
|
|||
|
MmInPageKernelStack(Thread);
|
|||
|
KiLockDispatcherDatabase(&OldIrql);
|
|||
|
Thread->KernelStackResident = TRUE;
|
|||
|
KiReadyThread(Thread);
|
|||
|
NextEntry = KiStackInSwapListHead.Flink;
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KiInSwapProcesses (
|
|||
|
IN KIRQL PreviousIrql
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function in swaps processes.
|
|||
|
|
|||
|
N.B. The dispatcher data lock is held on entry to this routine and must
|
|||
|
be help on exit to this routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PreviousIrql - Supplies the previous IRQL.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PLIST_ENTRY NextEntry;
|
|||
|
KIRQL OldIrql;
|
|||
|
PKPROCESS Process;
|
|||
|
PKTHREAD Thread;
|
|||
|
|
|||
|
//
|
|||
|
// Process the process in swap list and for each process removed from
|
|||
|
// the list, make the process resident, and process its ready list.
|
|||
|
//
|
|||
|
|
|||
|
OldIrql = PreviousIrql;
|
|||
|
NextEntry = KiProcessInSwapListHead.Flink;
|
|||
|
while (NextEntry != &KiProcessInSwapListHead) {
|
|||
|
Process = CONTAINING_RECORD(NextEntry, KPROCESS, SwapListEntry);
|
|||
|
RemoveEntryList(NextEntry);
|
|||
|
KiUnlockDispatcherDatabase(OldIrql);
|
|||
|
MmInSwapProcess(Process);
|
|||
|
KiLockDispatcherDatabase(&OldIrql);
|
|||
|
Process->State = ProcessInMemory;
|
|||
|
NextEntry = Process->ReadyListHead.Flink;
|
|||
|
while (NextEntry != &Process->ReadyListHead) {
|
|||
|
Thread = CONTAINING_RECORD(NextEntry, KTHREAD, WaitListEntry);
|
|||
|
RemoveEntryList(NextEntry);
|
|||
|
Thread->ProcessReadyQueue = FALSE;
|
|||
|
KiReadyThread(Thread);
|
|||
|
NextEntry = Process->ReadyListHead.Flink;
|
|||
|
}
|
|||
|
|
|||
|
NextEntry = KiProcessInSwapListHead.Flink;
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KiOutSwapKernelStacks (
|
|||
|
IN KIRQL PreviousIrql
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function attempts to out swap the kernel stack for threads whose
|
|||
|
wait mode is user and which have been waiting longer than the stack
|
|||
|
protect time.
|
|||
|
|
|||
|
N.B. The dispatcher data lock is held on entry to this routine and must
|
|||
|
be help on exit to this routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PreviousIrql - Supplies the previous IRQL.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
ULONG CurrentTick;
|
|||
|
PLIST_ENTRY NextEntry;
|
|||
|
ULONG NumberOfThreads;
|
|||
|
KIRQL OldIrql;
|
|||
|
PKPROCESS Process;
|
|||
|
PKTHREAD Thread;
|
|||
|
PKTHREAD ThreadObjects[MAXIMUM_THREAD_STACKS];
|
|||
|
ULONG WaitTime;
|
|||
|
|
|||
|
//
|
|||
|
// Scan the waiting in list and check if the wait time exceeds the
|
|||
|
// stack protect time. If the protect time is exceeded, then make
|
|||
|
// the kernel stack of the waiting thread nonresident. If the count
|
|||
|
// of the number of stacks that are resident for the process reaches
|
|||
|
// zero, then insert the process in the outswap list and set its state
|
|||
|
// to transition.
|
|||
|
//
|
|||
|
|
|||
|
CurrentTick = KiQueryLowTickCount();
|
|||
|
OldIrql = PreviousIrql;
|
|||
|
NextEntry = KiWaitInListHead.Flink;
|
|||
|
NumberOfThreads = 0;
|
|||
|
while ((NextEntry != &KiWaitInListHead) &&
|
|||
|
(NumberOfThreads < MAXIMUM_THREAD_STACKS)) {
|
|||
|
Thread = CONTAINING_RECORD(NextEntry, KTHREAD, WaitListEntry);
|
|||
|
|
|||
|
ASSERT(Thread->WaitMode == UserMode);
|
|||
|
|
|||
|
NextEntry = NextEntry->Flink;
|
|||
|
WaitTime = CurrentTick - Thread->WaitTime;
|
|||
|
if ((WaitTime >= KiStackProtectTime) &&
|
|||
|
KiIsThreadNumericStateSaved(Thread)) {
|
|||
|
Thread->KernelStackResident = FALSE;
|
|||
|
ThreadObjects[NumberOfThreads] = Thread;
|
|||
|
NumberOfThreads += 1;
|
|||
|
RemoveEntryList(&Thread->WaitListEntry);
|
|||
|
InsertTailList(&KiWaitOutListHead, &Thread->WaitListEntry);
|
|||
|
Process = Thread->ApcState.Process;
|
|||
|
Process->StackCount -= 1;
|
|||
|
if (Process->StackCount == 0) {
|
|||
|
Process->State = ProcessInTransition;
|
|||
|
InsertTailList(&KiProcessOutSwapListHead,
|
|||
|
&Process->SwapListEntry);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Unlock the dispatcher database and lower IRQL to its previous
|
|||
|
// value.
|
|||
|
//
|
|||
|
|
|||
|
KiUnlockDispatcherDatabase(OldIrql);
|
|||
|
|
|||
|
//
|
|||
|
// Out swap the kernels stack for the selected set of threads.
|
|||
|
//
|
|||
|
|
|||
|
while (NumberOfThreads > 0) {
|
|||
|
NumberOfThreads -= 1;
|
|||
|
Thread = ThreadObjects[NumberOfThreads];
|
|||
|
MmOutPageKernelStack(Thread);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Raise IRQL to dispatcher level and lock dispatcher database.
|
|||
|
//
|
|||
|
|
|||
|
KiLockDispatcherDatabase(&OldIrql);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KiOutSwapProcesses (
|
|||
|
IN KIRQL PreviousIrql
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function out swaps processes.
|
|||
|
|
|||
|
N.B. The dispatcher data lock is held on entry to this routine and must
|
|||
|
be help on exit to this routine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
PreviousIrql - Supplies the previous IRQL.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PLIST_ENTRY NextEntry;
|
|||
|
KIRQL OldIrql;
|
|||
|
PKPROCESS Process;
|
|||
|
PKTHREAD Thread;
|
|||
|
|
|||
|
//
|
|||
|
// Process the process out swap list and for each process removed from
|
|||
|
// the list, make the process nonresident, and process its ready list.
|
|||
|
//
|
|||
|
|
|||
|
OldIrql = PreviousIrql;
|
|||
|
NextEntry = KiProcessOutSwapListHead.Flink;
|
|||
|
while (NextEntry != &KiProcessOutSwapListHead) {
|
|||
|
Process = CONTAINING_RECORD(NextEntry, KPROCESS, SwapListEntry);
|
|||
|
RemoveEntryList(NextEntry);
|
|||
|
NextEntry = Process->ReadyListHead.Flink;
|
|||
|
if (NextEntry != &Process->ReadyListHead) {
|
|||
|
Process->State = ProcessInMemory;
|
|||
|
while (NextEntry != &Process->ReadyListHead) {
|
|||
|
Thread = CONTAINING_RECORD(NextEntry, KTHREAD, WaitListEntry);
|
|||
|
RemoveEntryList(NextEntry);
|
|||
|
Thread->ProcessReadyQueue = FALSE;
|
|||
|
KiReadyThread(Thread);
|
|||
|
NextEntry = Process->ReadyListHead.Flink;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
Process->State = ProcessOutOfMemory;
|
|||
|
KiUnlockDispatcherDatabase(OldIrql);
|
|||
|
MmOutSwapProcess(Process);
|
|||
|
KiLockDispatcherDatabase(&OldIrql);
|
|||
|
}
|
|||
|
|
|||
|
NextEntry = KiProcessOutSwapListHead.Flink;
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
KiScanReadyQueues (
|
|||
|
VOID
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function scans a section of the ready queues and attempts to
|
|||
|
boost the priority of threads that run at variable priority levels.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
ULONG Count;
|
|||
|
ULONG CurrentTick;
|
|||
|
PLIST_ENTRY Entry;
|
|||
|
ULONG Index;
|
|||
|
PLIST_ENTRY ListHead;
|
|||
|
ULONG Number;
|
|||
|
KIRQL OldIrql;
|
|||
|
PKPROCESS Process;
|
|||
|
ULONG Summary;
|
|||
|
PKTHREAD Thread;
|
|||
|
ULONG WaitTime;
|
|||
|
|
|||
|
//
|
|||
|
// Lock the dispatcher database and check if there are any ready threads
|
|||
|
// queued at the scannable priority levels.
|
|||
|
//
|
|||
|
|
|||
|
KiLockDispatcherDatabase(&OldIrql);
|
|||
|
Summary = KiReadySummary & ((1 << THREAD_BOOST_PRIORITY) - 2);
|
|||
|
if (Summary != 0) {
|
|||
|
Count = THREAD_READY_COUNT;
|
|||
|
CurrentTick = KiQueryLowTickCount();
|
|||
|
Index = KiReadyQueueIndex;
|
|||
|
Number = THREAD_SCAN_COUNT;
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// If the current ready queue index is beyond the end of the range
|
|||
|
// of priorities that are scanned, then wrap back to the beginning
|
|||
|
// priority.
|
|||
|
//
|
|||
|
|
|||
|
if (Index > THREAD_SCAN_PRIORITY) {
|
|||
|
Index = 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If there are any ready threads queued at the current priority
|
|||
|
// level, then attempt to boost the thread priority.
|
|||
|
//
|
|||
|
|
|||
|
if (((Summary >> Index) & 1) != 0) {
|
|||
|
Summary ^= (1 << Index);
|
|||
|
ListHead = &KiDispatcherReadyListHead[Index];
|
|||
|
Entry = ListHead->Flink;
|
|||
|
|
|||
|
ASSERT(Entry != ListHead);
|
|||
|
|
|||
|
do {
|
|||
|
Thread = CONTAINING_RECORD(Entry, KTHREAD, WaitListEntry);
|
|||
|
|
|||
|
//
|
|||
|
// If the thread has been waiting for an extended period
|
|||
|
// and is not currently running with a boost, then boost
|
|||
|
// the priority of the current thread.
|
|||
|
//
|
|||
|
|
|||
|
WaitTime = CurrentTick - Thread->WaitTime;
|
|||
|
if ((WaitTime >= READY_WITHOUT_RUNNING) &&
|
|||
|
(Thread->PriorityDecrement == 0)) {
|
|||
|
|
|||
|
//
|
|||
|
// Remove the thread from the respective ready queue.
|
|||
|
//
|
|||
|
|
|||
|
Entry = Entry->Blink;
|
|||
|
RemoveEntryList(Entry->Flink);
|
|||
|
if (IsListEmpty(ListHead) != FALSE) {
|
|||
|
ClearMember(Index, KiReadySummary);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Compute the priority decrement value, set the new
|
|||
|
// thread priority, set the decrement count, set the
|
|||
|
// thread quantum, and ready the thread for execution.
|
|||
|
//
|
|||
|
|
|||
|
Thread->PriorityDecrement =
|
|||
|
THREAD_BOOST_PRIORITY - Thread->Priority;
|
|||
|
|
|||
|
Thread->DecrementCount = ROUND_TRIP_DECREMENT_COUNT;
|
|||
|
Thread->Priority = THREAD_BOOST_PRIORITY;
|
|||
|
Process = Thread->ApcState.Process;
|
|||
|
Thread->Quantum = Process->ThreadQuantum * 2;
|
|||
|
KiReadyThread(Thread);
|
|||
|
Count -= 1;
|
|||
|
}
|
|||
|
|
|||
|
Entry = Entry->Flink;
|
|||
|
Number -= 1;
|
|||
|
} while ((Entry != ListHead) & (Number != 0) & (Count != 0));
|
|||
|
}
|
|||
|
|
|||
|
Index += 1;
|
|||
|
} while ((Summary != 0) & (Number != 0) & (Count != 0));
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Unlock the dispatcher database and save the last read queue index
|
|||
|
// for the next scan.
|
|||
|
//
|
|||
|
|
|||
|
KiUnlockDispatcherDatabase(OldIrql);
|
|||
|
if ((Count != 0) && (Number != 0)) {
|
|||
|
KiReadyQueueIndex = 1;
|
|||
|
|
|||
|
} else {
|
|||
|
KiReadyQueueIndex = Index;
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|