You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
247 lines
8.2 KiB
247 lines
8.2 KiB
/*++
|
|
|
|
Copyright (c) 2002 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
idsched.c
|
|
|
|
Abstract:
|
|
|
|
This module implement idle scheduling. Idle scheduling is performed when
|
|
the current thread is entering a wait state or must be rescheduled for
|
|
any other reason (e.g., affinity change), and a suitable thread cannot be
|
|
found after searching a suitable subset of the processor ready queues.
|
|
|
|
Author:
|
|
|
|
David N. Cutler (davec) 25-Jan-2002
|
|
|
|
Environment:
|
|
|
|
Kernel mode only.
|
|
|
|
--*/
|
|
|
|
#include "ki.h"
|
|
|
|
#if !defined(NT_UP)
|
|
|
|
PKTHREAD
|
|
FASTCALL
|
|
KiIdleSchedule (
|
|
PKPRCB CurrentPrcb
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called when the idle schedule flag is set in the current
|
|
PRCB. This flag is set when the current thread is entering a wait state
|
|
or must be rescheduled for any other reason (e.g., affinity change) and
|
|
a suitable thread cannot be found after searching all the processor ready
|
|
queues outside the dispatcher database lock. A second pass over the ready
|
|
queues is required since the processor was not idle during the first scan,
|
|
and therefore, a potential candidate thread may have been missed.
|
|
|
|
Arguments:
|
|
|
|
CurrentPrcb - Supplies a pointer to the current processor block.
|
|
|
|
Return Value:
|
|
|
|
If a thread is found to run, then a pointer to the thread is returned.
|
|
Otherwise, NULL is returned.
|
|
|
|
N.B. If a thread is found, then IRQL is returned at SYNCH_LEVEL. If a
|
|
thread is not found, then IRQL is returned at DISPATCH_LEVEL.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
LONG Index;
|
|
LONG Limit;
|
|
LONG Number;
|
|
PKTHREAD NewThread;
|
|
ULONG Processor;
|
|
PKPRCB TargetPrcb;
|
|
|
|
ASSERT (CurrentPrcb == KeGetCurrentPrcb());
|
|
|
|
//
|
|
// Raise IRQL to SYNCH_LEVEL, acquire the current PRCB lock, and clear
|
|
// idle schedule.
|
|
//
|
|
|
|
KeRaiseIrqlToSynchLevel();
|
|
KiAcquirePrcbLock(CurrentPrcb);
|
|
CurrentPrcb->IdleSchedule = FALSE;
|
|
|
|
//
|
|
// If a thread has already been selected to run on the current processor,
|
|
// then select that thread. Otherwise, attempt to select a thread from
|
|
// the current processor dispatcher ready queues.
|
|
//
|
|
|
|
if ((NewThread = CurrentPrcb->NextThread) != NULL) {
|
|
|
|
//
|
|
// Clear the next thread address, set the current thread address, and
|
|
// set the thread state to running.
|
|
//
|
|
|
|
CurrentPrcb->NextThread = NULL;
|
|
CurrentPrcb->CurrentThread = NewThread;
|
|
NewThread->State = Running;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Attempt to select a thread from the current processor dispatcher
|
|
// ready queues.
|
|
//
|
|
|
|
NewThread = KiSelectReadyThread(0, CurrentPrcb);
|
|
if (NewThread != NULL) {
|
|
CurrentPrcb->NextThread = NULL;
|
|
CurrentPrcb->CurrentThread = NewThread;
|
|
NewThread->State = Running;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Release the current PRCB lock and attempt to select a thread
|
|
// from any processor dispatcher ready queues.
|
|
//
|
|
// If this is a multinode system, then start with the processors
|
|
// on the same node. Otherwise, start with the current processor.
|
|
//
|
|
|
|
KiReleasePrcbLock(CurrentPrcb);
|
|
Processor = CurrentPrcb->Number;
|
|
Index = Processor;
|
|
if (KeNumberNodes > 1) {
|
|
KeFindFirstSetLeftAffinity(CurrentPrcb->ParentNode->ProcessorMask,
|
|
(PULONG)&Index);
|
|
}
|
|
|
|
Limit = KeNumberProcessors - 1;
|
|
Number = Limit;
|
|
do {
|
|
TargetPrcb = KiProcessorBlock[Index];
|
|
if (CurrentPrcb != TargetPrcb) {
|
|
if (TargetPrcb->ReadySummary != 0) {
|
|
|
|
//
|
|
// Acquire both current and target PRCB locks in
|
|
// address order to prevent deadlock.
|
|
//
|
|
|
|
if (CurrentPrcb < TargetPrcb) {
|
|
KiAcquirePrcbLock(CurrentPrcb);
|
|
KiAcquirePrcbLock(TargetPrcb);
|
|
|
|
} else {
|
|
KiAcquirePrcbLock(TargetPrcb);
|
|
KiAcquirePrcbLock(CurrentPrcb);
|
|
}
|
|
|
|
//
|
|
// If a new thread has not been selected to run on
|
|
// the current processor, then attempt to select a
|
|
// thread to run on the current processor.
|
|
//
|
|
|
|
if ((NewThread = CurrentPrcb->NextThread) == NULL) {
|
|
if ((TargetPrcb->ReadySummary != 0) &&
|
|
(NewThread = KiFindReadyThread(Processor,
|
|
TargetPrcb)) != NULL) {
|
|
|
|
//
|
|
// A new thread has been found to run on the
|
|
// current processor.
|
|
//
|
|
|
|
NewThread->State = Running;
|
|
KiReleasePrcbLock(TargetPrcb);
|
|
CurrentPrcb->NextThread = NULL;
|
|
CurrentPrcb->CurrentThread = NewThread;
|
|
|
|
//
|
|
// Clear idle on the current processor
|
|
// and update the idle SMT summary set to
|
|
// indicate the set is not idle.
|
|
//
|
|
|
|
KiClearIdleSummary(AFFINITY_MASK(Processor));
|
|
KiClearSMTSummary(CurrentPrcb->MultiThreadProcessorSet);
|
|
goto ThreadFound;
|
|
|
|
} else {
|
|
KiReleasePrcbLock(CurrentPrcb);
|
|
KiReleasePrcbLock(TargetPrcb);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// A thread has already been selected to run on
|
|
// the current processor. It is possible that
|
|
// the thread is the idle thread due to a state
|
|
// change that made a scheduled runable thread
|
|
// unrunable.
|
|
//
|
|
// N.B. If the idle thread is selected, then the
|
|
// current processor is idle. Otherwise,
|
|
// the current processor is not idle.
|
|
//
|
|
|
|
if (NewThread == CurrentPrcb->IdleThread) {
|
|
CurrentPrcb->NextThread = NULL;
|
|
CurrentPrcb->IdleSchedule = FALSE;
|
|
KiReleasePrcbLock(CurrentPrcb);
|
|
KiReleasePrcbLock(TargetPrcb);
|
|
continue;
|
|
|
|
} else {
|
|
NewThread->State = Running;
|
|
KiReleasePrcbLock(TargetPrcb);
|
|
CurrentPrcb->NextThread = NULL;
|
|
CurrentPrcb->CurrentThread = NewThread;
|
|
goto ThreadFound;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Index -= 1;
|
|
if (Index < 0) {
|
|
Index = Limit;
|
|
}
|
|
|
|
Number -= 1;
|
|
} while (Number >= 0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If a new thread has been selected for execution, then release the
|
|
// PRCB lock and acquire the idle thread lock. Otherwise, lower IRQL
|
|
// to DISPATCH_LEVEL.
|
|
//
|
|
|
|
ThreadFound:;
|
|
if (NewThread == NULL) {
|
|
KeLowerIrql(DISPATCH_LEVEL);
|
|
|
|
} else {
|
|
KiSetContextSwapBusy(CurrentPrcb->IdleThread);
|
|
KiReleasePrcbLock(CurrentPrcb);
|
|
}
|
|
|
|
return NewThread;
|
|
}
|
|
|
|
#endif
|