Source code of Windows XP (NT5)
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.

595 lines
13 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. sidle.c
  5. Abstract:
  6. This module implements system idle functionality
  7. Author:
  8. Ken Reneris (kenr) 17-Jan-1997
  9. Revision History:
  10. --*/
  11. #include "pop.h"
  12. ULONG
  13. PopSqrt(
  14. IN ULONG value
  15. );
  16. #ifdef ALLOC_PRAGMA
  17. #pragma alloc_text(PAGE, PopInitSIdle)
  18. #pragma alloc_text(PAGE, PopPolicySystemIdle)
  19. #pragma alloc_text(PAGE, PoSystemIdleWorker)
  20. #pragma alloc_text(PAGE, PopSqrt)
  21. #endif
  22. VOID
  23. PopInitSIdle (
  24. VOID
  25. )
  26. /*++
  27. Routine Description:
  28. Initializes state for idle system detection
  29. Arguments:
  30. Uses current policy
  31. Return Value:
  32. None
  33. --*/
  34. {
  35. ULONG SystemIdleLimit;
  36. ULONG IdleSensitivity;
  37. ULONG i;
  38. POWER_ACTION IdleAction;
  39. LARGE_INTEGER li;
  40. POP_SYSTEM_IDLE Idle;
  41. ASSERT_POLICY_LOCK_OWNED();
  42. //
  43. // Assume system idle detection is not enabled
  44. //
  45. Idle.Action.Action = PowerActionNone;
  46. Idle.MinState = PowerSystemSleeping1;
  47. Idle.Timeout = (ULONG) -1;
  48. Idle.Sensitivity = 100;
  49. //
  50. // Either set idle detection for the current policy or for
  51. // re-entering a sleep state in the case of a quite wake
  52. //
  53. if (AnyBitsSet (PopFullWake, PO_FULL_WAKE_STATUS | PO_FULL_WAKE_PENDING)) {
  54. //
  55. // Set system idle detection for the current policy
  56. //
  57. if (PopPolicy->Idle.Action != PowerActionNone &&
  58. PopPolicy->IdleTimeout &&
  59. PopPolicy->IdleSensitivity) {
  60. Idle.Action = PopPolicy->Idle;
  61. Idle.Timeout = (PopPolicy->IdleTimeout + SYS_IDLE_WORKER - 1) / SYS_IDLE_WORKER;
  62. Idle.MinState = PopPolicy->MinSleep;
  63. Idle.Sensitivity = 66 + PopPolicy->IdleSensitivity / 3;
  64. }
  65. } else {
  66. //
  67. // System is not fully awake, set the system idle detection
  68. // code to re-enter the sleeping state quickly unless a full
  69. // wake happens
  70. //
  71. Idle.Action.Action = PopAction.Action;
  72. Idle.MinState = PopAction.LightestState;
  73. if (Idle.MinState == PowerSystemHibernate) {
  74. //
  75. // The timeout is a little longer for hibernate since it takes
  76. // so much longer to enter & exit this state.
  77. //
  78. Idle.Timeout = SYS_IDLE_REENTER_TIMEOUT_S4 / SYS_IDLE_WORKER;
  79. } else {
  80. Idle.Timeout = SYS_IDLE_REENTER_TIMEOUT / SYS_IDLE_WORKER;
  81. }
  82. //
  83. // Set Idle.Action.Flags to POWER_ACTION_QUERY_ALLOWED to insure
  84. // that all normal power messages are broadcast when we re enter a low power state
  85. //
  86. Idle.Action.Flags = POWER_ACTION_QUERY_ALLOWED;
  87. Idle.Action.EventCode = 0;
  88. Idle.Sensitivity = SYS_IDLE_REENTER_SENSITIVITY;
  89. }
  90. //
  91. // See if idle detection has changed
  92. //
  93. if (RtlCompareMemory (&PopSIdle.Action, &Idle.Action, sizeof(POWER_ACTION_POLICY)) !=
  94. sizeof(POWER_ACTION_POLICY) ||
  95. PopSIdle.Timeout != Idle.Timeout ||
  96. PopSIdle.Sensitivity != Idle.Sensitivity) {
  97. PoPrint (PO_SIDLE, ("PoSIdle: new idle params set\n"));
  98. //
  99. // Clear current detection
  100. //
  101. KeCancelTimer(&PoSystemIdleTimer);
  102. PopSIdle.Time = 0;
  103. PopSIdle.IdleWorker = TRUE;
  104. //
  105. // Set new idle detection
  106. //
  107. PopSIdle.Action = Idle.Action;
  108. PopSIdle.MinState = Idle.MinState;
  109. PopSIdle.Timeout = Idle.Timeout;
  110. PopSIdle.Sensitivity = Idle.Sensitivity;
  111. //
  112. // If new action, enable system idle worker
  113. //
  114. if (PopSIdle.Action.Action) {
  115. li.QuadPart = -1 * SYS_IDLE_WORKER * US2SEC * US2TIME;
  116. KeSetTimerEx(&PoSystemIdleTimer, li, SYS_IDLE_WORKER*1000, NULL);
  117. }
  118. }
  119. }
  120. ULONG
  121. PopPolicySystemIdle (
  122. VOID
  123. )
  124. /*++
  125. Routine Description:
  126. Power policy worker thread to trigger the system idle power action
  127. Arguments:
  128. None
  129. Return Value:
  130. None
  131. --*/
  132. {
  133. BOOLEAN SystemIdle;
  134. POP_ACTION_TRIGGER Trigger;
  135. //
  136. // Take out the policy lock and check to see if the system is
  137. // idle
  138. //
  139. PopAcquirePolicyLock ();
  140. SystemIdle = PoSystemIdleWorker(FALSE);
  141. //
  142. // If heuristics are dirty, save a new copy
  143. //
  144. if (PopHeuristics.Dirty) {
  145. PopSaveHeuristics ();
  146. }
  147. //
  148. // Put system idle detection back to idle worker
  149. //
  150. PopSIdle.IdleWorker = TRUE;
  151. //
  152. // If system idle, trigger system idle action
  153. //
  154. if (SystemIdle) {
  155. //
  156. // On success or failure, reset the trigger
  157. //
  158. PopSIdle.Time = 0;
  159. PopSIdle.Sampling = FALSE;
  160. //
  161. // Invoke system state change
  162. //
  163. RtlZeroMemory (&Trigger, sizeof(Trigger));
  164. Trigger.Type = PolicySystemIdle;
  165. Trigger.Flags = PO_TRG_SET;
  166. PopSetPowerAction (
  167. &Trigger,
  168. 0,
  169. &PopSIdle.Action,
  170. PopSIdle.MinState,
  171. SubstituteLightestOverallDownwardBounded
  172. );
  173. }
  174. PopReleasePolicyLock (FALSE);
  175. return 0;
  176. }
  177. VOID
  178. PopCaptureCounts (
  179. OUT PULONGLONG LastTick,
  180. OUT PLARGE_INTEGER CurrentTick,
  181. OUT PULONGLONG LastIoTransfer,
  182. OUT PULONGLONG CurrentIoTransfer
  183. )
  184. {
  185. KIRQL OldIrql;
  186. //
  187. // Capture current tick and IO count. Do it at dpc level so
  188. // the IO count will be reasonable on track with the tick count.
  189. //
  190. KeRaiseIrql (DISPATCH_LEVEL, &OldIrql);
  191. *LastTick = PopSIdle.LastTick;
  192. *LastIoTransfer = PopSIdle.LastIoTransfer;
  193. KeQueryTickCount (CurrentTick);
  194. *CurrentIoTransfer = IoReadTransferCount.QuadPart + IoWriteTransferCount.QuadPart + IoOtherTransferCount.QuadPart;
  195. KeLowerIrql (OldIrql);
  196. }
  197. BOOLEAN
  198. PoSystemIdleWorker (
  199. IN BOOLEAN IdleWorker
  200. )
  201. /*++
  202. Routine Description:
  203. Worker function called by an idle priority thread to watch
  204. for an idle system.
  205. N.B. To save on threads we use the zero paging thread from Mm
  206. to perform this check. It can not be blocked.
  207. Arguments:
  208. IdleWorker - True if caller is at IdlePriority
  209. Return Value:
  210. None
  211. --*/
  212. {
  213. LARGE_INTEGER CurrentTick;
  214. ULONGLONG LastTick;
  215. LONG TickDiff;
  216. ULONG Processor;
  217. KAFFINITY Mask;
  218. ULONG NewTime;
  219. LONG ProcIdleness, ProcBusy;
  220. LONG percent;
  221. BOOLEAN GoodSample;
  222. BOOLEAN SystemIdle;
  223. BOOLEAN GetWorker;
  224. KAFFINITY Summary;
  225. PPROCESSOR_POWER_STATE PState;
  226. ULONG i;
  227. LONG j;
  228. ULONGLONG CurrentIoTransfer, LastIoTransfer;
  229. LONGLONG IoTransferDiff;
  230. LONGLONG li;
  231. PKPRCB Prcb;
  232. if (IdleWorker) {
  233. //
  234. // Clear idle worker timer's signalled state
  235. //
  236. KeClearTimer (&PoSystemIdleTimer);
  237. }
  238. //
  239. // If the system required or user present attribute is set,
  240. // then don't bother with any checks
  241. //
  242. if (PopAttributes[POP_SYSTEM_ATTRIBUTE].Count ||
  243. PopAttributes[POP_USER_ATTRIBUTE].Count) {
  244. return FALSE;
  245. }
  246. //
  247. // If this is the wrong worker, don't bother
  248. //
  249. if (IdleWorker != PopSIdle.IdleWorker) {
  250. return FALSE;
  251. }
  252. GoodSample = FALSE;
  253. SystemIdle = FALSE;
  254. GetWorker = FALSE;
  255. NewTime = PopSIdle.Time;
  256. PopCaptureCounts(&LastTick, &CurrentTick, &LastIoTransfer, &CurrentIoTransfer);
  257. //
  258. // If this is an initial sample, initialize starting sample
  259. //
  260. if (!PopSIdle.Sampling) {
  261. GoodSample = TRUE;
  262. goto Done;
  263. }
  264. //
  265. // Compute the number of ticks since the last check
  266. //
  267. TickDiff = (ULONG) (CurrentTick.QuadPart - LastTick);
  268. IoTransferDiff = CurrentIoTransfer - LastIoTransfer;
  269. //
  270. // if it's a poor sample, skip it
  271. //
  272. if ((TickDiff <= 0) || (IoTransferDiff < 0)) {
  273. PoPrint (PO_SIDLE, ("PoSIdle: poor sample\n"));
  274. PopSIdle.Sampling = FALSE;
  275. goto Done;
  276. }
  277. GoodSample = TRUE;
  278. //
  279. // Get lowest idleness of any processor
  280. //
  281. ProcIdleness = 100;
  282. Summary = KeActiveProcessors;
  283. Processor = 0;
  284. while (Summary) {
  285. if (Summary & 1) {
  286. Prcb = KiProcessorBlock[Processor];
  287. PState = &Prcb->PowerState;
  288. percent = (Prcb->IdleThread->KernelTime - PState->LastSysTime) * 100 / TickDiff;
  289. if (percent < ProcIdleness) {
  290. ProcIdleness = percent;
  291. }
  292. }
  293. Summary = Summary >> 1;
  294. Processor = Processor + 1;
  295. }
  296. if (ProcIdleness > 100) {
  297. ProcIdleness = 100;
  298. }
  299. ProcBusy = 100 - ProcIdleness;
  300. //
  301. // Normalize IO transfers to be some number per tick
  302. //
  303. IoTransferDiff = IoTransferDiff / TickDiff;
  304. //
  305. // If the system is loaded a bit, but not a lot calculate
  306. // how many IO transfers can occur for each percentage point
  307. // of being busy
  308. //
  309. if (ProcIdleness <= 90 && ProcIdleness >= 50) {
  310. i = (ULONG) IoTransferDiff / ProcBusy;
  311. //
  312. // Make running average of the result
  313. //
  314. if (PopHeuristics.IoTransferSamples < SYS_IDLE_SAMPLES) {
  315. if (PopHeuristics.IoTransferSamples == 0) {
  316. PopHeuristics.IoTransferTotal = i;
  317. }
  318. PopHeuristics.IoTransferTotal += i;
  319. PopHeuristics.IoTransferSamples += 1;
  320. } else {
  321. PopHeuristics.IoTransferTotal = PopHeuristics.IoTransferTotal + i -
  322. (PopHeuristics.IoTransferTotal / PopHeuristics.IoTransferSamples);
  323. }
  324. //
  325. // Determine weighting of transfers as percent busy and compare
  326. // to current weighting. If the weighting has moved then update
  327. // the heuristic
  328. //
  329. i = PopHeuristics.IoTransferTotal / PopHeuristics.IoTransferSamples;
  330. j = PopHeuristics.IoTransferWeight - i;
  331. if (j < 0) {
  332. j = -j;
  333. }
  334. if (i > 0 && j > 2 && j > (LONG) PopHeuristics.IoTransferWeight/10) {
  335. PoPrint (PO_SIDLE, ("PoSIdle: updated weighting = %d\n", i));
  336. PopHeuristics.IoTransferWeight = i;
  337. PopHeuristics.Dirty = TRUE;
  338. GetWorker = TRUE;
  339. }
  340. }
  341. PopSIdle.Idleness = ProcIdleness;
  342. //
  343. // Reduce system idleness by the weighted transfers occuring
  344. //
  345. i = (ULONG) ((ULONGLONG) IoTransferDiff / PopHeuristics.IoTransferWeight);
  346. j = i - ProcBusy/2;
  347. if (j > 0) {
  348. PopSIdle.Idleness = ProcIdleness - PopSqrt(j * i);
  349. }
  350. //
  351. // Count how long the system has been more idle then the sensitivity setting
  352. //
  353. if (PopSIdle.Idleness >= (LONG) PopSIdle.Sensitivity) {
  354. NewTime = PopSIdle.Time + 1;
  355. if (NewTime >= PopSIdle.Timeout) {
  356. SystemIdle = TRUE;
  357. GetWorker = TRUE;
  358. }
  359. } else {
  360. //
  361. // System is not idle enough, reset the timeout
  362. //
  363. NewTime = 0;
  364. PopSIdle.Time = 0;
  365. }
  366. PoPrint (PO_SIDLE, ("PoSIdle: Proc %d, IoTran/Tick %d, IoAdjusted %d, Sens %d, count %d %d\n",
  367. ProcIdleness,
  368. (ULONG)IoTransferDiff,
  369. PopSIdle.Idleness,
  370. PopSIdle.Sensitivity,
  371. NewTime,
  372. PopSIdle.Timeout
  373. ));
  374. Done:
  375. //
  376. // If we need a non-idle worker thread, queue it and don't update
  377. // last values for this sample since the non-idle thread will make
  378. // another sample shortly
  379. //
  380. if (GetWorker) {
  381. PopSIdle.IdleWorker = FALSE;
  382. PopGetPolicyWorker (PO_WORKER_SYS_IDLE);
  383. if (IdleWorker) {
  384. PopCheckForWork (TRUE);
  385. }
  386. } else {
  387. //
  388. // If this was a good sample, update
  389. //
  390. if (GoodSample) {
  391. PopSIdle.Time = NewTime;
  392. PopSIdle.LastTick = CurrentTick.QuadPart;
  393. PopSIdle.LastIoTransfer = CurrentIoTransfer;
  394. PopSIdle.Sampling = TRUE;
  395. Summary = KeActiveProcessors;
  396. Processor = 0;
  397. Mask = 1;
  398. for (; ;) {
  399. if (Summary & Mask) {
  400. Prcb = KiProcessorBlock[Processor];
  401. PState = &Prcb->PowerState;
  402. PState->LastSysTime = Prcb->IdleThread->KernelTime;
  403. Summary &= ~Mask;
  404. if (!Summary) {
  405. break;
  406. }
  407. }
  408. Mask = Mask << 1;
  409. Processor = Processor + 1;
  410. }
  411. }
  412. }
  413. return SystemIdle;
  414. }
  415. ULONG
  416. PopSqrt(
  417. IN ULONG value
  418. )
  419. /*++
  420. Routine Description:
  421. Returns the integer square root of an operand between 0 and 9999.
  422. Arguments:
  423. value - Value to square root
  424. Return Value:
  425. Square root rounded down
  426. --*/
  427. {
  428. ULONG h, l, i;
  429. h = 100;
  430. l = 0;
  431. for (; ;) {
  432. i = l + (h-l) / 2;
  433. if (i*i > value) {
  434. h = i;
  435. } else {
  436. if (l == i) {
  437. break;
  438. }
  439. l = i;
  440. }
  441. }
  442. return i;
  443. }