596 lines
13 KiB
C
596 lines
13 KiB
C
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
sidle.c
|
|
|
|
Abstract:
|
|
|
|
This module implements system idle functionality
|
|
|
|
Author:
|
|
|
|
Ken Reneris (kenr) 17-Jan-1997
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "pop.h"
|
|
|
|
ULONG
|
|
PopSqrt(
|
|
IN ULONG value
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, PopInitSIdle)
|
|
#pragma alloc_text(PAGE, PopPolicySystemIdle)
|
|
#pragma alloc_text(PAGE, PoSystemIdleWorker)
|
|
#pragma alloc_text(PAGE, PopSqrt)
|
|
#endif
|
|
|
|
|
|
VOID
|
|
PopInitSIdle (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initializes state for idle system detection
|
|
|
|
Arguments:
|
|
|
|
Uses current policy
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
ULONG SystemIdleLimit;
|
|
ULONG IdleSensitivity;
|
|
ULONG i;
|
|
POWER_ACTION IdleAction;
|
|
LARGE_INTEGER li;
|
|
POP_SYSTEM_IDLE Idle;
|
|
|
|
|
|
ASSERT_POLICY_LOCK_OWNED();
|
|
|
|
//
|
|
// Assume system idle detection is not enabled
|
|
//
|
|
|
|
Idle.Action.Action = PowerActionNone;
|
|
Idle.MinState = PowerSystemSleeping1;
|
|
Idle.Timeout = (ULONG) -1;
|
|
Idle.Sensitivity = 100;
|
|
|
|
//
|
|
// Either set idle detection for the current policy or for
|
|
// re-entering a sleep state in the case of a quite wake
|
|
//
|
|
|
|
if (AnyBitsSet (PopFullWake, PO_FULL_WAKE_STATUS | PO_FULL_WAKE_PENDING)) {
|
|
|
|
//
|
|
// Set system idle detection for the current policy
|
|
//
|
|
|
|
if (PopPolicy->Idle.Action != PowerActionNone &&
|
|
PopPolicy->IdleTimeout &&
|
|
PopPolicy->IdleSensitivity) {
|
|
|
|
Idle.Action = PopPolicy->Idle;
|
|
Idle.Timeout = (PopPolicy->IdleTimeout + SYS_IDLE_WORKER - 1) / SYS_IDLE_WORKER;
|
|
Idle.MinState = PopPolicy->MinSleep;
|
|
Idle.Sensitivity = 66 + PopPolicy->IdleSensitivity / 3;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// System is not fully awake, set the system idle detection
|
|
// code to re-enter the sleeping state quickly unless a full
|
|
// wake happens
|
|
//
|
|
|
|
Idle.Action.Action = PopAction.Action;
|
|
Idle.MinState = PopAction.LightestState;
|
|
if (Idle.MinState == PowerSystemHibernate) {
|
|
//
|
|
// The timeout is a little longer for hibernate since it takes
|
|
// so much longer to enter & exit this state.
|
|
//
|
|
Idle.Timeout = SYS_IDLE_REENTER_TIMEOUT_S4 / SYS_IDLE_WORKER;
|
|
} else {
|
|
Idle.Timeout = SYS_IDLE_REENTER_TIMEOUT / SYS_IDLE_WORKER;
|
|
}
|
|
//
|
|
// Set Idle.Action.Flags to POWER_ACTION_QUERY_ALLOWED to insure
|
|
// that all normal power messages are broadcast when we re enter a low power state
|
|
//
|
|
Idle.Action.Flags = POWER_ACTION_QUERY_ALLOWED;
|
|
Idle.Action.EventCode = 0;
|
|
Idle.Sensitivity = SYS_IDLE_REENTER_SENSITIVITY;
|
|
}
|
|
|
|
//
|
|
// See if idle detection has changed
|
|
//
|
|
|
|
if (RtlCompareMemory (&PopSIdle.Action, &Idle.Action, sizeof(POWER_ACTION_POLICY)) !=
|
|
sizeof(POWER_ACTION_POLICY) ||
|
|
PopSIdle.Timeout != Idle.Timeout ||
|
|
PopSIdle.Sensitivity != Idle.Sensitivity) {
|
|
|
|
PoPrint (PO_SIDLE, ("PoSIdle: new idle params set\n"));
|
|
|
|
//
|
|
// Clear current detection
|
|
//
|
|
|
|
KeCancelTimer(&PoSystemIdleTimer);
|
|
PopSIdle.Time = 0;
|
|
PopSIdle.IdleWorker = TRUE;
|
|
|
|
//
|
|
// Set new idle detection
|
|
//
|
|
|
|
PopSIdle.Action = Idle.Action;
|
|
PopSIdle.MinState = Idle.MinState;
|
|
PopSIdle.Timeout = Idle.Timeout;
|
|
PopSIdle.Sensitivity = Idle.Sensitivity;
|
|
|
|
//
|
|
// If new action, enable system idle worker
|
|
//
|
|
|
|
if (PopSIdle.Action.Action) {
|
|
li.QuadPart = -1 * SYS_IDLE_WORKER * US2SEC * US2TIME;
|
|
KeSetTimerEx(&PoSystemIdleTimer, li, SYS_IDLE_WORKER*1000, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
ULONG
|
|
PopPolicySystemIdle (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Power policy worker thread to trigger the system idle power action
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN SystemIdle;
|
|
POP_ACTION_TRIGGER Trigger;
|
|
|
|
//
|
|
// Take out the policy lock and check to see if the system is
|
|
// idle
|
|
//
|
|
|
|
PopAcquirePolicyLock ();
|
|
SystemIdle = PoSystemIdleWorker(FALSE);
|
|
|
|
//
|
|
// If heuristics are dirty, save a new copy
|
|
//
|
|
|
|
if (PopHeuristics.Dirty) {
|
|
PopSaveHeuristics ();
|
|
}
|
|
|
|
//
|
|
// Put system idle detection back to idle worker
|
|
//
|
|
|
|
PopSIdle.IdleWorker = TRUE;
|
|
|
|
//
|
|
// If system idle, trigger system idle action
|
|
//
|
|
|
|
if (SystemIdle) {
|
|
|
|
//
|
|
// On success or failure, reset the trigger
|
|
//
|
|
|
|
PopSIdle.Time = 0;
|
|
PopSIdle.Sampling = FALSE;
|
|
|
|
//
|
|
// Invoke system state change
|
|
//
|
|
|
|
RtlZeroMemory (&Trigger, sizeof(Trigger));
|
|
Trigger.Type = PolicySystemIdle;
|
|
Trigger.Flags = PO_TRG_SET;
|
|
PopSetPowerAction (
|
|
&Trigger,
|
|
0,
|
|
&PopSIdle.Action,
|
|
PopSIdle.MinState,
|
|
SubstituteLightestOverallDownwardBounded
|
|
);
|
|
}
|
|
PopReleasePolicyLock (FALSE);
|
|
return 0;
|
|
}
|
|
|
|
|
|
VOID
|
|
PopCaptureCounts (
|
|
OUT PULONGLONG LastTick,
|
|
OUT PLARGE_INTEGER CurrentTick,
|
|
OUT PULONGLONG LastIoTransfer,
|
|
OUT PULONGLONG CurrentIoTransfer
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
// Capture current tick and IO count. Do it at dpc level so
|
|
// the IO count will be reasonable on track with the tick count.
|
|
//
|
|
|
|
KeRaiseIrql (DISPATCH_LEVEL, &OldIrql);
|
|
|
|
*LastTick = PopSIdle.LastTick;
|
|
*LastIoTransfer = PopSIdle.LastIoTransfer;
|
|
|
|
KeQueryTickCount (CurrentTick);
|
|
*CurrentIoTransfer = IoReadTransferCount.QuadPart + IoWriteTransferCount.QuadPart + IoOtherTransferCount.QuadPart;
|
|
|
|
KeLowerIrql (OldIrql);
|
|
}
|
|
|
|
BOOLEAN
|
|
PoSystemIdleWorker (
|
|
IN BOOLEAN IdleWorker
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Worker function called by an idle priority thread to watch
|
|
for an idle system.
|
|
|
|
N.B. To save on threads we use the zero paging thread from Mm
|
|
to perform this check. It can not be blocked.
|
|
|
|
Arguments:
|
|
|
|
IdleWorker - True if caller is at IdlePriority
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
LARGE_INTEGER CurrentTick;
|
|
ULONGLONG LastTick;
|
|
LONG TickDiff;
|
|
ULONG Processor;
|
|
KAFFINITY Mask;
|
|
ULONG NewTime;
|
|
LONG ProcIdleness, ProcBusy;
|
|
LONG percent;
|
|
BOOLEAN GoodSample;
|
|
BOOLEAN SystemIdle;
|
|
BOOLEAN GetWorker;
|
|
KAFFINITY Summary;
|
|
PPROCESSOR_POWER_STATE PState;
|
|
ULONG i;
|
|
LONG j;
|
|
ULONGLONG CurrentIoTransfer, LastIoTransfer;
|
|
LONGLONG IoTransferDiff;
|
|
LONGLONG li;
|
|
PKPRCB Prcb;
|
|
|
|
|
|
if (IdleWorker) {
|
|
|
|
//
|
|
// Clear idle worker timer's signalled state
|
|
//
|
|
|
|
KeClearTimer (&PoSystemIdleTimer);
|
|
}
|
|
|
|
//
|
|
// If the system required or user present attribute is set,
|
|
// then don't bother with any checks
|
|
//
|
|
|
|
if (PopAttributes[POP_SYSTEM_ATTRIBUTE].Count ||
|
|
PopAttributes[POP_USER_ATTRIBUTE].Count) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If this is the wrong worker, don't bother
|
|
//
|
|
|
|
if (IdleWorker != PopSIdle.IdleWorker) {
|
|
return FALSE;
|
|
}
|
|
|
|
GoodSample = FALSE;
|
|
SystemIdle = FALSE;
|
|
GetWorker = FALSE;
|
|
NewTime = PopSIdle.Time;
|
|
|
|
PopCaptureCounts(&LastTick, &CurrentTick, &LastIoTransfer, &CurrentIoTransfer);
|
|
|
|
//
|
|
// If this is an initial sample, initialize starting sample
|
|
//
|
|
|
|
if (!PopSIdle.Sampling) {
|
|
GoodSample = TRUE;
|
|
goto Done;
|
|
}
|
|
|
|
//
|
|
// Compute the number of ticks since the last check
|
|
//
|
|
|
|
TickDiff = (ULONG) (CurrentTick.QuadPart - LastTick);
|
|
IoTransferDiff = CurrentIoTransfer - LastIoTransfer;
|
|
|
|
//
|
|
// if it's a poor sample, skip it
|
|
//
|
|
|
|
if ((TickDiff <= 0) || (IoTransferDiff < 0)) {
|
|
PoPrint (PO_SIDLE, ("PoSIdle: poor sample\n"));
|
|
PopSIdle.Sampling = FALSE;
|
|
goto Done;
|
|
}
|
|
|
|
GoodSample = TRUE;
|
|
|
|
//
|
|
// Get lowest idleness of any processor
|
|
//
|
|
|
|
ProcIdleness = 100;
|
|
Summary = KeActiveProcessors;
|
|
Processor = 0;
|
|
while (Summary) {
|
|
if (Summary & 1) {
|
|
Prcb = KiProcessorBlock[Processor];
|
|
PState = &Prcb->PowerState;
|
|
|
|
percent = (Prcb->IdleThread->KernelTime - PState->LastSysTime) * 100 / TickDiff;
|
|
if (percent < ProcIdleness) {
|
|
ProcIdleness = percent;
|
|
}
|
|
|
|
}
|
|
|
|
Summary = Summary >> 1;
|
|
Processor = Processor + 1;
|
|
}
|
|
|
|
if (ProcIdleness > 100) {
|
|
ProcIdleness = 100;
|
|
}
|
|
ProcBusy = 100 - ProcIdleness;
|
|
|
|
//
|
|
// Normalize IO transfers to be some number per tick
|
|
//
|
|
|
|
IoTransferDiff = IoTransferDiff / TickDiff;
|
|
|
|
//
|
|
// If the system is loaded a bit, but not a lot calculate
|
|
// how many IO transfers can occur for each percentage point
|
|
// of being busy
|
|
//
|
|
|
|
if (ProcIdleness <= 90 && ProcIdleness >= 50) {
|
|
|
|
i = (ULONG) IoTransferDiff / ProcBusy;
|
|
|
|
//
|
|
// Make running average of the result
|
|
//
|
|
|
|
if (PopHeuristics.IoTransferSamples < SYS_IDLE_SAMPLES) {
|
|
|
|
if (PopHeuristics.IoTransferSamples == 0) {
|
|
|
|
PopHeuristics.IoTransferTotal = i;
|
|
}
|
|
PopHeuristics.IoTransferTotal += i;
|
|
PopHeuristics.IoTransferSamples += 1;
|
|
|
|
} else {
|
|
|
|
PopHeuristics.IoTransferTotal = PopHeuristics.IoTransferTotal + i -
|
|
(PopHeuristics.IoTransferTotal / PopHeuristics.IoTransferSamples);
|
|
|
|
}
|
|
|
|
//
|
|
// Determine weighting of transfers as percent busy and compare
|
|
// to current weighting. If the weighting has moved then update
|
|
// the heuristic
|
|
//
|
|
|
|
i = PopHeuristics.IoTransferTotal / PopHeuristics.IoTransferSamples;
|
|
j = PopHeuristics.IoTransferWeight - i;
|
|
if (j < 0) {
|
|
j = -j;
|
|
}
|
|
|
|
if (i > 0 && j > 2 && j > (LONG) PopHeuristics.IoTransferWeight/10) {
|
|
PoPrint (PO_SIDLE, ("PoSIdle: updated weighting = %d\n", i));
|
|
PopHeuristics.IoTransferWeight = i;
|
|
PopHeuristics.Dirty = TRUE;
|
|
GetWorker = TRUE;
|
|
}
|
|
}
|
|
|
|
PopSIdle.Idleness = ProcIdleness;
|
|
|
|
//
|
|
// Reduce system idleness by the weighted transfers occuring
|
|
//
|
|
|
|
i = (ULONG) ((ULONGLONG) IoTransferDiff / PopHeuristics.IoTransferWeight);
|
|
j = i - ProcBusy/2;
|
|
if (j > 0) {
|
|
PopSIdle.Idleness = ProcIdleness - PopSqrt(j * i);
|
|
}
|
|
|
|
//
|
|
// Count how long the system has been more idle then the sensitivity setting
|
|
//
|
|
|
|
if (PopSIdle.Idleness >= (LONG) PopSIdle.Sensitivity) {
|
|
|
|
NewTime = PopSIdle.Time + 1;
|
|
if (NewTime >= PopSIdle.Timeout) {
|
|
SystemIdle = TRUE;
|
|
GetWorker = TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// System is not idle enough, reset the timeout
|
|
//
|
|
|
|
NewTime = 0;
|
|
PopSIdle.Time = 0;
|
|
}
|
|
|
|
|
|
PoPrint (PO_SIDLE, ("PoSIdle: Proc %d, IoTran/Tick %d, IoAdjusted %d, Sens %d, count %d %d\n",
|
|
ProcIdleness,
|
|
(ULONG)IoTransferDiff,
|
|
PopSIdle.Idleness,
|
|
PopSIdle.Sensitivity,
|
|
NewTime,
|
|
PopSIdle.Timeout
|
|
));
|
|
|
|
|
|
Done:
|
|
//
|
|
// If we need a non-idle worker thread, queue it and don't update
|
|
// last values for this sample since the non-idle thread will make
|
|
// another sample shortly
|
|
//
|
|
|
|
if (GetWorker) {
|
|
PopSIdle.IdleWorker = FALSE;
|
|
PopGetPolicyWorker (PO_WORKER_SYS_IDLE);
|
|
|
|
if (IdleWorker) {
|
|
PopCheckForWork (TRUE);
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// If this was a good sample, update
|
|
//
|
|
|
|
if (GoodSample) {
|
|
PopSIdle.Time = NewTime;
|
|
PopSIdle.LastTick = CurrentTick.QuadPart;
|
|
PopSIdle.LastIoTransfer = CurrentIoTransfer;
|
|
PopSIdle.Sampling = TRUE;
|
|
|
|
Summary = KeActiveProcessors;
|
|
Processor = 0;
|
|
Mask = 1;
|
|
for (; ;) {
|
|
if (Summary & Mask) {
|
|
Prcb = KiProcessorBlock[Processor];
|
|
PState = &Prcb->PowerState;
|
|
|
|
PState->LastSysTime = Prcb->IdleThread->KernelTime;
|
|
Summary &= ~Mask;
|
|
if (!Summary) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
Mask = Mask << 1;
|
|
Processor = Processor + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
return SystemIdle;
|
|
}
|
|
|
|
|
|
ULONG
|
|
PopSqrt(
|
|
IN ULONG value
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns the integer square root of an operand between 0 and 9999.
|
|
|
|
Arguments:
|
|
|
|
value - Value to square root
|
|
|
|
Return Value:
|
|
|
|
Square root rounded down
|
|
|
|
--*/
|
|
{
|
|
ULONG h, l, i;
|
|
|
|
h = 100;
|
|
l = 0;
|
|
|
|
for (; ;) {
|
|
i = l + (h-l) / 2;
|
|
if (i*i > value) {
|
|
h = i;
|
|
} else {
|
|
if (l == i) {
|
|
break;
|
|
}
|
|
l = i;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|