|
|
/*++
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
|