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.

4800 lines
115 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. idletsks.c
  5. Abstract:
  6. This module implements the idle detection / task server.
  7. Author:
  8. Dave Fields (davidfie) 26-July-1998
  9. Cenk Ergan (cenke) 14-June-2000
  10. Revision History:
  11. --*/
  12. #include <nt.h>
  13. #include <ntrtl.h>
  14. #include <nturtl.h>
  15. #include <windows.h>
  16. #include <stdlib.h>
  17. #include <stdio.h>
  18. #include <time.h>
  19. #include "idletsks.h"
  20. //
  21. // Define WMI Guids, e.g. DiskPerfGuid.
  22. //
  23. #ifndef INITGUID
  24. #define INITGUID 1
  25. #endif
  26. #include <wmiguid.h>
  27. //
  28. // Global variables.
  29. //
  30. //
  31. // This is the idle detection global context. It is declared as a
  32. // single element array so that ItSrvGlobalContext can be used as a
  33. // pointer (allowing us to switch to allocating it from heap etc. in
  34. // the future).
  35. //
  36. ITSRV_GLOBAL_CONTEXT ItSrvGlobalContext[1] = {0};
  37. //
  38. // Reference count protecting the global context.
  39. //
  40. ITSRV_REFCOUNT ItSrvRefCount = {0};
  41. //
  42. // Implementation of server side exposed functions.
  43. //
  44. DWORD
  45. ItSrvInitialize (
  46. VOID
  47. )
  48. /*++
  49. Routine Description:
  50. Initializes the global idle detection context. This function
  51. should be called after at least one ncalrpc binding has been
  52. registered.
  53. Arguments:
  54. None.
  55. Return Value:
  56. Win32 error code.
  57. --*/
  58. {
  59. DWORD ErrorCode;
  60. PITSRV_GLOBAL_CONTEXT GlobalContext;
  61. PIT_IDLE_DETECTION_PARAMETERS Parameters;
  62. ULONG StatusIdx;
  63. BOOLEAN StartedServer;
  64. BOOLEAN AcquiredLock;
  65. //
  66. // Initialize locals.
  67. //
  68. GlobalContext = ItSrvGlobalContext;
  69. StartedServer = FALSE;
  70. AcquiredLock = FALSE;
  71. DBGPR((ITID,ITTRC,"IDLE: SrvInitialize(%p)\n",GlobalContext));
  72. //
  73. // Initialize reference count for the global context structure.
  74. //
  75. ItSpInitializeRefCount(&ItSrvRefCount);
  76. //
  77. // Initialize the server context structure. Before we do anything
  78. // that can fail we initialize fields to reasonable values so we
  79. // know what to cleanup. The following fields really have to be
  80. // initialized to zero:
  81. //
  82. // StatusVersion
  83. // GlobalLock
  84. // IdleDetectionTimerHandle
  85. // StopIdleDetection
  86. // IdleDetectionStopped
  87. // RemovedRunningIdleTask
  88. // DiskPerfWmiHandle
  89. // WmiQueryBuffer
  90. // WmiQueryBufferSize
  91. // NumProcessors
  92. // IsIdleDetectionCallbackRunning
  93. // Parameters
  94. // ServiceHandlerNotRunning
  95. // ProcessIdleTasksNotifyRoutine
  96. // RpcBindingVector
  97. // RegisteredRPCInterface
  98. // RegisteredRPCEndpoint
  99. //
  100. RtlZeroMemory(GlobalContext, sizeof(ITSRV_GLOBAL_CONTEXT));
  101. //
  102. // Initialize the idle tasks list.
  103. //
  104. InitializeListHead(&GlobalContext->IdleTasksList);
  105. //
  106. // Initialize the status now (so cleanup does not complain). From
  107. // this point on, UpdateStatus should be called to set the status.
  108. //
  109. GlobalContext->Status = ItSrvStatusInitializing;
  110. for (StatusIdx = 0; StatusIdx < ITSRV_GLOBAL_STATUS_HISTORY_SIZE; StatusIdx++) {
  111. GlobalContext->LastStatus[StatusIdx] = ItSrvStatusInitializing;
  112. }
  113. //
  114. // Initialize the system snapshot buffer.
  115. //
  116. ItSpInitializeSystemSnapshot(&GlobalContext->LastSystemSnapshot);
  117. //
  118. // Initialize the server global context lock. We need at least a
  119. // lock to protect the idle task list. Since nearly all operations
  120. // will involve the list, to make the code simple, we just have a
  121. // single lock for the list and for other operations on the global
  122. // context (e.g. uninitialization etc).
  123. //
  124. GlobalContext->GlobalLock = CreateMutex(NULL, FALSE, NULL);
  125. if (GlobalContext->GlobalLock == NULL) {
  126. ErrorCode = GetLastError();
  127. goto cleanup;
  128. }
  129. //
  130. // Initialize the event that will be set when we should not be
  131. // doing idle detection anymore (e.g. due to server shutdown, or
  132. // no more idle tasks remaining). It is set by default, since
  133. // there are no idle tasks to start with. It signals a running
  134. // idle detection callback to quickly exit.
  135. //
  136. GlobalContext->StopIdleDetection = CreateEvent(NULL,
  137. TRUE,
  138. TRUE,
  139. NULL);
  140. if (GlobalContext->StopIdleDetection == NULL) {
  141. ErrorCode = GetLastError();
  142. goto cleanup;
  143. }
  144. //
  145. // Initialize the event that gets set when idle detection is fully
  146. // stopped and it is OK to start a new callback. It is set by
  147. // default.
  148. //
  149. GlobalContext->IdleDetectionStopped = CreateEvent(NULL,
  150. TRUE,
  151. TRUE,
  152. NULL);
  153. if (GlobalContext->IdleDetectionStopped == NULL) {
  154. ErrorCode = GetLastError();
  155. goto cleanup;
  156. }
  157. //
  158. // Initialize the event that gets set when a currently running
  159. // idle task is removed/unregistered to signal the idle detection
  160. // callback to move on to other idle tasks.
  161. //
  162. GlobalContext->RemovedRunningIdleTask = CreateEvent(NULL,
  163. TRUE,
  164. FALSE,
  165. NULL);
  166. if (GlobalContext->RemovedRunningIdleTask == NULL) {
  167. ErrorCode = GetLastError();
  168. goto cleanup;
  169. }
  170. //
  171. // This manual reset event is reset when ItSrvServiceHandler is
  172. // running and is signaled when this function is done.
  173. //
  174. GlobalContext->ServiceHandlerNotRunning = CreateEvent(NULL,
  175. TRUE,
  176. TRUE,
  177. NULL);
  178. if (GlobalContext->ServiceHandlerNotRunning == NULL) {
  179. ErrorCode = GetLastError();
  180. goto cleanup;
  181. }
  182. //
  183. // Set the default parameters.
  184. //
  185. Parameters = &GlobalContext->Parameters;
  186. Parameters->IdleDetectionPeriod = IT_DEFAULT_IDLE_DETECTION_PERIOD;
  187. Parameters->IdleVerificationPeriod = IT_DEFAULT_IDLE_VERIFICATION_PERIOD;
  188. Parameters->NumVerifications = IT_DEFAULT_NUM_IDLE_VERIFICATIONS;
  189. Parameters->IdleInputCheckPeriod = IT_DEFAULT_IDLE_USER_INPUT_CHECK_PERIOD;
  190. Parameters->IdleTaskRunningCheckPeriod = IT_DEFAULT_IDLE_TASK_RUNNING_CHECK_PERIOD;
  191. Parameters->MinCpuIdlePercentage = IT_DEFAULT_MIN_CPU_IDLE_PERCENTAGE;
  192. Parameters->MinDiskIdlePercentage = IT_DEFAULT_MIN_DISK_IDLE_PERCENTAGE;
  193. Parameters->MaxNumRegisteredTasks = IT_DEFAULT_MAX_REGISTERED_TASKS;
  194. //
  195. // Acquire the lock to avoid any race conditions after we mark the
  196. // server started.
  197. //
  198. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  199. AcquiredLock = TRUE;
  200. //
  201. // We are done until an idle task that we can run gets
  202. // registered. We will start idle detection (e.g. get initial
  203. // snapshot, queue a timer etc) then.
  204. //
  205. ItSpUpdateStatus(GlobalContext, ItSrvStatusWaitingForIdleTasks);
  206. //
  207. // After this point, if we fail, we cannot just cleanup: we have
  208. // to stop the server.
  209. //
  210. StartedServer = TRUE;
  211. //
  212. // We have to start up the RPC server only after we have
  213. // initialized everything else, so when RPC calls come the server
  214. // is ready.
  215. //
  216. //
  217. // We don't want to register any well known end points because
  218. // each LPC endpoint will cause another thread to be spawned to
  219. // listen on it. We try to bind through only the existing
  220. // bindings.
  221. //
  222. ErrorCode = RpcServerInqBindings(&GlobalContext->RpcBindingVector);
  223. if (ErrorCode != RPC_S_OK) {
  224. //
  225. // At least one binding should have been registered before we
  226. // got called. It would be cool if we could check to see if
  227. // there is an ncalrpc binding registered.
  228. //
  229. IT_ASSERT(ErrorCode != RPC_S_NO_BINDINGS);
  230. goto cleanup;
  231. }
  232. ErrorCode = RpcEpRegister(idletask_ServerIfHandle,
  233. GlobalContext->RpcBindingVector,
  234. NULL,
  235. NULL);
  236. if (ErrorCode != RPC_S_OK) {
  237. goto cleanup;
  238. }
  239. GlobalContext->RegisteredRPCEndpoint = TRUE;
  240. //
  241. // Register an auto-listen interface so we are not dependant on
  242. // others calling RpcMgmtStart/Stop listening.
  243. //
  244. ErrorCode = RpcServerRegisterIfEx(idletask_ServerIfHandle,
  245. NULL,
  246. NULL,
  247. RPC_IF_AUTOLISTEN | RPC_IF_ALLOW_SECURE_ONLY,
  248. RPC_C_LISTEN_MAX_CALLS_DEFAULT,
  249. ItSpRpcSecurityCallback);
  250. if (ErrorCode != RPC_S_OK) {
  251. goto cleanup;
  252. }
  253. //
  254. // Register default security principal name for this process, e.g.
  255. // NT Authority\LocalSystem.
  256. //
  257. ErrorCode = RpcServerRegisterAuthInfo(NULL, RPC_C_AUTHN_WINNT, NULL, NULL);
  258. if (ErrorCode != RPC_S_OK) {
  259. goto cleanup;
  260. }
  261. GlobalContext->RegisteredRPCInterface = TRUE;
  262. //
  263. // We are done.
  264. //
  265. ErrorCode = ERROR_SUCCESS;
  266. cleanup:
  267. if (AcquiredLock) {
  268. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  269. }
  270. if (ErrorCode != ERROR_SUCCESS) {
  271. if (StartedServer) {
  272. ItSrvUninitialize();
  273. } else {
  274. ItSpCleanupGlobalContext(GlobalContext);
  275. }
  276. }
  277. DBGPR((ITID,ITTRC,"IDLE: SrvInitialize(%p)=%x\n",GlobalContext,ErrorCode));
  278. return ErrorCode;
  279. }
  280. VOID
  281. ItSrvUninitialize (
  282. VOID
  283. )
  284. /*++
  285. Routine Description:
  286. Winds down and uninitializes the server global context.
  287. Do not call this from the idle detection timer callback function
  288. thread, because there will be a deadlock when we try to delete the
  289. timer.
  290. This function should not be called before ItSrvInitialize
  291. completes. It should be called only once per ItSrvInitialize.
  292. Arguments:
  293. None.
  294. Return Value:
  295. None.
  296. --*/
  297. {
  298. PITSRV_GLOBAL_CONTEXT GlobalContext;
  299. BOOLEAN AcquiredLock;
  300. DWORD ErrorCode;
  301. //
  302. // Initialize locals.
  303. //
  304. GlobalContext = ItSrvGlobalContext;
  305. AcquiredLock = FALSE;
  306. DBGPR((ITID,ITTRC,"IDLE: SrvUninitialize(%p)\n",GlobalContext));
  307. //
  308. // Acquire the global lock and update status.
  309. //
  310. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  311. AcquiredLock = TRUE;
  312. //
  313. // Make sure we get uninitialized only once.
  314. //
  315. IT_ASSERT(GlobalContext->Status != ItSrvStatusUninitializing);
  316. IT_ASSERT(GlobalContext->Status != ItSrvStatusUninitialized);
  317. ItSpUpdateStatus(GlobalContext, ItSrvStatusUninitializing);
  318. //
  319. // If idle detection is alive, we need to stop it before we tell
  320. // RPCs to go away. We need to do this even if there are
  321. // registered idle tasks. Since we have set the state to
  322. // ItSrvStatusUninitializing, new "register idle task"s
  323. // won't get stuck.
  324. //
  325. if (GlobalContext->IdleDetectionTimerHandle) {
  326. ItSpStopIdleDetection(GlobalContext);
  327. }
  328. //
  329. // Release the lock so rpc call-ins can grab the lock to
  330. // uninitialize/exit as necessary.
  331. //
  332. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  333. AcquiredLock = FALSE;
  334. //
  335. // Make sure incoming client register/unregister calls are stopped.
  336. //
  337. if (GlobalContext->RegisteredRPCInterface) {
  338. //
  339. // If we registered an interface, we should have registered
  340. // our endpoint in the local database too.
  341. //
  342. IT_ASSERT(GlobalContext->RegisteredRPCEndpoint);
  343. //
  344. // Calling UnregisterIfEx makes sure all the context handles
  345. // are run down so we don't get rundown calls after we have
  346. // uninitialized.
  347. //
  348. RpcServerUnregisterIfEx(idletask_ServerIfHandle, NULL, TRUE);
  349. }
  350. if (GlobalContext->RegisteredRPCEndpoint) {
  351. //
  352. // We could have registered an endpoint only if we
  353. // successfully queried bindings.
  354. //
  355. IT_ASSERT(GlobalContext->RpcBindingVector);
  356. RpcEpUnregister(idletask_ServerIfHandle,
  357. GlobalContext->RpcBindingVector,
  358. NULL);
  359. }
  360. //
  361. // Wait until idle detection is fully stopped (e.g. the callback
  362. // exits, the timer is dequeued etc.)
  363. //
  364. WaitForSingleObject(GlobalContext->IdleDetectionStopped, INFINITE);
  365. //
  366. // Wait for active service handler call in to exit (if there is
  367. // one).
  368. //
  369. WaitForSingleObject(GlobalContext->ServiceHandlerNotRunning, INFINITE);
  370. //
  371. // Acquire the reference count for the global context
  372. // exclusively. This shuts off ItSrvServiceHandler callbacks and
  373. // such. We should not have to poll for long in this call since
  374. // everybody should have already exit or should be winding down.
  375. //
  376. ErrorCode = ItSpAcquireExclusiveRef(&ItSrvRefCount);
  377. IT_ASSERT(ErrorCode == ERROR_SUCCESS);
  378. //
  379. // At this point no workers should be active and no new requests
  380. // should be coming. Now we will be breaking down the global state
  381. // structure (e.g. freeing memory, closing events etc.) they
  382. // would be using.
  383. //
  384. ItSpCleanupGlobalContext(GlobalContext);
  385. DBGPR((ITID,ITTRC,"IDLE: SrvUninitialize(%p)=0\n",GlobalContext));
  386. return;
  387. }
  388. BOOLEAN
  389. ItSrvServiceHandler(
  390. IN DWORD ControlCode,
  391. IN DWORD EventType,
  392. IN LPVOID EventData,
  393. IN LPVOID Context,
  394. OUT PDWORD ErrorCode
  395. )
  396. /*++
  397. Routine Description:
  398. This function is called with service/window messages to give idle
  399. detection a chance to filter/handle them. This is not a full blown
  400. service handler function as idle detection is piggy backed on
  401. another (e.g. scheduler) service that is responsible for
  402. starting/stopping idle detection. This function returns TRUE only
  403. if it wants ErrorCode to be returned from the caller to the
  404. system. If everything is successful and it does not need the
  405. caller to do anything special it returns FALSE.
  406. Only one ItSrvServiceHandler call should be outstanding.
  407. Arguments:
  408. ControlCode - Service control code.
  409. EventType - Type of event that occured.
  410. EventData - Additional information based on ControlCode and EventType.
  411. Context - Ignored.
  412. ErrorCode - If the function return TRUE, this ErrorCode should be
  413. propagated to the system.
  414. Return Value:
  415. TRUE - ErrorCode should be propagated to the system.
  416. FALSE - No need to do anything.
  417. --*/
  418. {
  419. PITSRV_GLOBAL_CONTEXT GlobalContext;
  420. BOOLEAN PropagateErrorCode;
  421. BOOLEAN AcquiredLock;
  422. BOOLEAN ClearedNotRunningEvent;
  423. //
  424. // Initialize locals.
  425. //
  426. GlobalContext = ItSrvGlobalContext;
  427. PropagateErrorCode = FALSE;
  428. AcquiredLock = FALSE;
  429. ClearedNotRunningEvent = FALSE;
  430. //
  431. // We cannot do anything if idle detection is not initialized.
  432. //
  433. if (GlobalContext->Status == 0 ||
  434. GlobalContext->Status == ItSrvStatusInitializing) {
  435. PropagateErrorCode = FALSE;
  436. goto cleanup;
  437. }
  438. //
  439. // Get a reference on the global context so it does not go away
  440. // beneath our feet.
  441. //
  442. *ErrorCode = ItSpAddRef(&ItSrvRefCount);
  443. if (*ErrorCode != ERROR_SUCCESS) {
  444. PropagateErrorCode = FALSE;
  445. goto cleanup;
  446. }
  447. //
  448. // Clear the event that says we are not running.
  449. //
  450. ResetEvent(GlobalContext->ServiceHandlerNotRunning);
  451. ClearedNotRunningEvent = TRUE;
  452. //
  453. // If server started uninitializing after we reset the event, we
  454. // should not do anything. By setting/checking for things in the
  455. // opposite order the server checks/sets them we minimize how long
  456. // it may have to poll when getting the exclusive reference.
  457. //
  458. if (GlobalContext->Status == ItSrvStatusUninitializing ||
  459. GlobalContext->Status == ItSrvStatusUninitialized) {
  460. PropagateErrorCode = FALSE;
  461. goto cleanup;
  462. }
  463. //
  464. // See if we have to do something based on input ControlCode and
  465. // EventType. The ControlCode is ignored by default if
  466. // PropagateErrorCode is not set otherwise.
  467. //
  468. //
  469. // Currently we don't handle any control codes. This is where we
  470. // would put the SERVICE_CONTROL_POWEREVENT/PBT_APMQUERYSUSPEND
  471. // handlers as well as service pause/resume requests and such.
  472. //
  473. PropagateErrorCode = FALSE;
  474. cleanup:
  475. if (AcquiredLock) {
  476. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  477. }
  478. if (ClearedNotRunningEvent) {
  479. SetEvent(GlobalContext->ServiceHandlerNotRunning);
  480. }
  481. return PropagateErrorCode;
  482. }
  483. DWORD
  484. ItSrvRegisterIdleTask (
  485. ITRPC_HANDLE Reserved,
  486. IT_HANDLE *ItHandle,
  487. PIT_IDLE_TASK_PROPERTIES IdleTaskProperties
  488. )
  489. /*++
  490. Routine Description:
  491. Adds a new idle task to be run when the system is idle.
  492. Arguments:
  493. Reserved - Ignored.
  494. ItHandle - Context handle returned to the client.
  495. IdleTaskProperties - Pointer to properties for the idle task.
  496. Return Value:
  497. Status.
  498. --*/
  499. {
  500. PITSRV_IDLE_TASK_CONTEXT IdleTask;
  501. PITSRV_GLOBAL_CONTEXT GlobalContext;
  502. HANDLE ClientProcess;
  503. ULONG FailedCheckId;
  504. DWORD ErrorCode;
  505. DWORD WaitResult;
  506. LONG StatusVersion;
  507. ULONG NumLoops;
  508. BOOL Result;
  509. BOOLEAN AcquiredLock;
  510. BOOLEAN ImpersonatingClient;
  511. BOOLEAN OpenedThreadToken;
  512. //
  513. // Initialize locals.
  514. //
  515. IdleTask = NULL;
  516. AcquiredLock = FALSE;
  517. ImpersonatingClient = FALSE;
  518. ClientProcess = NULL;
  519. GlobalContext = ItSrvGlobalContext;
  520. //
  521. // Initialize parameters.
  522. //
  523. *ItHandle = NULL;
  524. DBGPR((ITID,ITTRC,"IDLE: SrvRegisterTask(%p)\n",IdleTaskProperties));
  525. //
  526. // Allocate a new idle task context.
  527. //
  528. IdleTask = IT_ALLOC(sizeof(ITSRV_IDLE_TASK_CONTEXT));
  529. if (!IdleTask) {
  530. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  531. goto cleanup;
  532. }
  533. //
  534. // Initialize the fields to safe values so we know what to
  535. // cleanup.
  536. //
  537. IdleTask->Status = ItIdleTaskInitializing;
  538. IdleTask->StartEvent = NULL;
  539. IdleTask->StopEvent = NULL;
  540. //
  541. // Copy and verify input buffer.
  542. //
  543. IdleTask->Properties = *IdleTaskProperties;
  544. FailedCheckId = ItpVerifyIdleTaskProperties(&IdleTask->Properties);
  545. if (FailedCheckId != 0) {
  546. ErrorCode = ERROR_BAD_FORMAT;
  547. goto cleanup;
  548. }
  549. //
  550. // Impersonate the client to open the start/stop events. They
  551. // should have been created by the client.
  552. //
  553. ErrorCode = RpcImpersonateClient(NULL);
  554. if (ErrorCode != RPC_S_OK) {
  555. goto cleanup;
  556. }
  557. ImpersonatingClient = TRUE;
  558. //
  559. // Open the client process. Since we impersonated the client, it
  560. // is safe to use the client id it specifies.
  561. //
  562. ClientProcess = OpenProcess(PROCESS_ALL_ACCESS,
  563. FALSE,
  564. IdleTaskProperties->ProcessId);
  565. if (!ClientProcess) {
  566. ErrorCode = GetLastError();
  567. goto cleanup;
  568. }
  569. //
  570. // Get handle to the start event.
  571. //
  572. Result = DuplicateHandle(ClientProcess,
  573. (HANDLE) IdleTaskProperties->StartEventHandle,
  574. GetCurrentProcess(),
  575. &IdleTask->StartEvent,
  576. EVENT_ALL_ACCESS,
  577. FALSE,
  578. 0);
  579. if (!Result) {
  580. ErrorCode = GetLastError();
  581. goto cleanup;
  582. }
  583. //
  584. // Get handle to the stop event.
  585. //
  586. Result = DuplicateHandle(ClientProcess,
  587. (HANDLE) IdleTaskProperties->StopEventHandle,
  588. GetCurrentProcess(),
  589. &IdleTask->StopEvent,
  590. EVENT_ALL_ACCESS,
  591. FALSE,
  592. 0);
  593. if (!Result) {
  594. ErrorCode = GetLastError();
  595. goto cleanup;
  596. }
  597. //
  598. // No need to impersonate any longer.
  599. //
  600. RpcRevertToSelf();
  601. ImpersonatingClient = FALSE;
  602. //
  603. // Make sure the handle is for an event and it is in the right
  604. // state.
  605. //
  606. if (!ResetEvent(IdleTask->StartEvent)) {
  607. ErrorCode = GetLastError();
  608. goto cleanup;
  609. }
  610. if (!SetEvent(IdleTask->StopEvent)) {
  611. ErrorCode = GetLastError();
  612. goto cleanup;
  613. }
  614. //
  615. // Acquire the server lock to check the status and insert new task
  616. // into the list.
  617. //
  618. NumLoops = 0;
  619. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  620. AcquiredLock = TRUE;
  621. do {
  622. //
  623. // We should be holding the GlobalLock when we come here the
  624. // first time or after looping. Inside the loop we may let go
  625. // of the lock, but we have to reacquire it before we loop.
  626. //
  627. IT_ASSERT(AcquiredLock);
  628. //
  629. // If there are already too many idle tasks registered, bail
  630. // out.
  631. //
  632. if (GlobalContext->NumIdleTasks >=
  633. GlobalContext->Parameters.MaxNumRegisteredTasks) {
  634. ErrorCode = ERROR_TOO_MANY_OPEN_FILES;
  635. goto cleanup;
  636. }
  637. switch (GlobalContext->Status) {
  638. case ItSrvStatusInitializing:
  639. //
  640. // We should not have gotten called if the server is still
  641. // initializing!
  642. //
  643. IT_ASSERT(FALSE);
  644. ErrorCode = ERROR_NOT_READY;
  645. goto cleanup;
  646. case ItSrvStatusUninitializing:
  647. //
  648. // If the server is uninitializing, we should not add a
  649. // new idle task.
  650. //
  651. ErrorCode = ERROR_NOT_READY;
  652. goto cleanup;
  653. break;
  654. case ItSrvStatusUninitialized:
  655. //
  656. // The server should not have uninitialized while we could be
  657. // running.
  658. //
  659. IT_ASSERT(FALSE);
  660. ErrorCode = ERROR_NOT_READY;
  661. goto cleanup;
  662. case ItSrvStatusStoppingIdleDetection:
  663. //
  664. // The idle task list should be empty if we are stopping
  665. // idle detection. There should not be a
  666. // IdleDetectionTimerHandle either.
  667. //
  668. IT_ASSERT(IsListEmpty(&GlobalContext->IdleTasksList));
  669. IT_ASSERT(GlobalContext->IdleDetectionTimerHandle == NULL);
  670. //
  671. // We must wait until idle detection is fully stopped. We
  672. // need to release our lock to do so. But note the status
  673. // version first.
  674. //
  675. StatusVersion = GlobalContext->StatusVersion;
  676. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  677. AcquiredLock = FALSE;
  678. WaitResult = WaitForSingleObject(GlobalContext->IdleDetectionStopped,
  679. INFINITE);
  680. if (WaitResult != WAIT_OBJECT_0) {
  681. DBGPR((ITID,ITERR,"IDLE: SrvRegisterTask-FailedWaitStop\n"));
  682. ErrorCode = ERROR_INVALID_FUNCTION;
  683. goto cleanup;
  684. }
  685. //
  686. // Reacquire the lock.
  687. //
  688. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  689. AcquiredLock = TRUE;
  690. //
  691. // If nobody woke before us and updated the status, update
  692. // it now.
  693. //
  694. if (StatusVersion == GlobalContext->StatusVersion) {
  695. IT_ASSERT(GlobalContext->Status == ItSrvStatusStoppingIdleDetection);
  696. ItSpUpdateStatus(GlobalContext, ItSrvStatusWaitingForIdleTasks);
  697. }
  698. //
  699. // Loop to do what is necessary next.
  700. //
  701. break;
  702. case ItSrvStatusWaitingForIdleTasks:
  703. //
  704. // The idle task list should be empty if we are waiting
  705. // for idle tasks.
  706. //
  707. IT_ASSERT(IsListEmpty(&GlobalContext->IdleTasksList));
  708. //
  709. // If we are waiting for idle tasks, start idle detection
  710. // so we can add our task.
  711. //
  712. ErrorCode = ItSpStartIdleDetection(GlobalContext);
  713. if (ErrorCode != ERROR_SUCCESS) {
  714. goto cleanup;
  715. }
  716. //
  717. // Update the status.
  718. //
  719. ItSpUpdateStatus(GlobalContext, ItSrvStatusDetectingIdle);
  720. //
  721. // Loop and insert our idle task into the list. Note that
  722. // we are not releasing the lock, so we will not be in a
  723. // state where the status is ItSrvStatusDetectingIdle but
  724. // there are no idle tasks in the list.
  725. //
  726. break;
  727. case ItSrvStatusDetectingIdle:
  728. case ItSrvStatusRunningIdleTasks:
  729. //
  730. // If we are detecting idle, we just need to insert our
  731. // task into the list and break out.
  732. //
  733. //
  734. // This operation currently does not fail. If in the
  735. // future it may, make sure to handle the case we started
  736. // idle detection for this task. It is not an acceptable
  737. // state to have idle detection but no tasks in the list.
  738. //
  739. //
  740. // Insert the task into the list. We do not check for
  741. // duplicates and such as RPC helps us maintain context
  742. // per registration.
  743. //
  744. GlobalContext->NumIdleTasks++;
  745. InsertTailList(&GlobalContext->IdleTasksList,
  746. &IdleTask->IdleTaskLink);
  747. IdleTask->Status = ItIdleTaskQueued;
  748. break;
  749. default:
  750. //
  751. // We should be handling all valid cases above.
  752. //
  753. IT_ASSERT(FALSE);
  754. ErrorCode = ERROR_INVALID_FUNCTION;
  755. goto cleanup;
  756. }
  757. //
  758. // We should be still holding the global lock.
  759. //
  760. IT_ASSERT(AcquiredLock);
  761. //
  762. // Break out if we could queue our task.
  763. //
  764. if (IdleTask->Status == ItIdleTaskQueued) {
  765. break;
  766. }
  767. //
  768. // We should not loop too many times.
  769. //
  770. NumLoops++;
  771. if (NumLoops > 128) {
  772. DBGPR((ITID,ITERR,"IDLE: SrvRegisterTask-LoopTooMuch\n"));
  773. ErrorCode = ERROR_INVALID_FUNCTION;
  774. goto cleanup;
  775. }
  776. } while (TRUE);
  777. //
  778. // We should be still holding the lock.
  779. //
  780. IT_ASSERT(AcquiredLock);
  781. //
  782. // If we came here, we successfully inserted the task into the
  783. // list.
  784. //
  785. IT_ASSERT(!IsListEmpty(&GlobalContext->IdleTasksList));
  786. IT_ASSERT(IdleTask->Status == ItIdleTaskQueued);
  787. //
  788. // Release the lock.
  789. //
  790. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  791. AcquiredLock = FALSE;
  792. //
  793. // We are done.
  794. //
  795. ErrorCode = ERROR_SUCCESS;
  796. cleanup:
  797. if (ClientProcess) {
  798. CloseHandle(ClientProcess);
  799. }
  800. if (ImpersonatingClient) {
  801. RpcRevertToSelf();
  802. }
  803. if (ErrorCode != ERROR_SUCCESS) {
  804. if (IdleTask) {
  805. ItSpCleanupIdleTask(IdleTask);
  806. IT_FREE(IdleTask);
  807. }
  808. } else {
  809. *ItHandle = (IT_HANDLE)IdleTask;
  810. }
  811. if (AcquiredLock) {
  812. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  813. }
  814. DBGPR((ITID,ITTRC,"IDLE: SrvRegisterTask(%p)=%x\n",IdleTaskProperties,ErrorCode));
  815. return ErrorCode;
  816. }
  817. VOID
  818. ItSrvUnregisterIdleTask (
  819. ITRPC_HANDLE Reserved,
  820. IT_HANDLE *ItHandle
  821. )
  822. /*++
  823. Routine Description:
  824. This function is a stub for ItSpUnregisterIdleTask that does the
  825. real work. Please see that function for comments.
  826. Arguments:
  827. See ItSpUnregisterIdleTask.
  828. Return Value:
  829. See ItSpUnregisterIdleTask.
  830. --*/
  831. {
  832. ItSpUnregisterIdleTask(Reserved, ItHandle, FALSE);
  833. }
  834. DWORD
  835. ItSrvSetDetectionParameters (
  836. ITRPC_HANDLE Reserved,
  837. PIT_IDLE_DETECTION_PARAMETERS Parameters
  838. )
  839. /*++
  840. Routine Description:
  841. This debug routine is used by test programs to set the idle detection
  842. parameters. It will return ERROR_INVALID_FUNCTION on retail build.
  843. Arguments:
  844. Reserved - Ignored.
  845. Parameters - New idle detection parameters.
  846. Return Value:
  847. Win32 error code.
  848. --*/
  849. {
  850. DWORD ErrorCode;
  851. PITSRV_GLOBAL_CONTEXT GlobalContext;
  852. //
  853. // Initialize locals.
  854. //
  855. GlobalContext = ItSrvGlobalContext;
  856. DBGPR((ITID,ITTRC,"IDLE: SrvSetParameters(%p)\n",Parameters));
  857. #ifndef IT_DBG
  858. //
  859. // This is a debug only API.
  860. //
  861. ErrorCode = ERROR_INVALID_FUNCTION;
  862. #else // !IT_DBG
  863. //
  864. // Acquire the lock and copy the new parameters.
  865. //
  866. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  867. GlobalContext->Parameters = *Parameters;
  868. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  869. ErrorCode = ERROR_SUCCESS;
  870. #endif // !IT_DBG
  871. DBGPR((ITID,ITTRC,"IDLE: SrvSetParameters(%p)=%d\n",Parameters,ErrorCode));
  872. return ErrorCode;
  873. }
  874. DWORD
  875. ItSrvProcessIdleTasks (
  876. ITRPC_HANDLE Reserved
  877. )
  878. /*++
  879. Routine Description:
  880. This routine forces all queued tasks (if there are any) to be processed
  881. right away.
  882. Arguments:
  883. Reserved - Ignored.
  884. Return Value:
  885. Win32 error code.
  886. --*/
  887. {
  888. PITSRV_GLOBAL_CONTEXT GlobalContext;
  889. ITSRV_IDLE_DETECTION_OVERRIDE Overrides;
  890. DWORD ErrorCode;
  891. DWORD WaitResult;
  892. BOOLEAN AcquiredLock;
  893. //
  894. // Initialize locals.
  895. //
  896. AcquiredLock = FALSE;
  897. GlobalContext = ItSrvGlobalContext;
  898. DBGPR((ITID,ITTRC,"IDLE: SrvProcessAll()\n"));
  899. //
  900. // If a notification routine was specified, call it.
  901. //
  902. if (GlobalContext->ProcessIdleTasksNotifyRoutine) {
  903. GlobalContext->ProcessIdleTasksNotifyRoutine();
  904. }
  905. //
  906. // Acquire the server lock.
  907. //
  908. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  909. AcquiredLock = TRUE;
  910. //
  911. // The server should not have shutdown while we could be called.
  912. //
  913. IT_ASSERT(GlobalContext->Status != ItSrvStatusUninitialized);
  914. //
  915. // If the server is shutting down, we should not do anything.
  916. //
  917. if (GlobalContext->Status == ItSrvStatusUninitializing) {
  918. ErrorCode = ERROR_NOT_READY;
  919. goto cleanup;
  920. }
  921. //
  922. // If there are no tasks queued, we are done.
  923. //
  924. if (IsListEmpty(&GlobalContext->IdleTasksList)) {
  925. ErrorCode = ERROR_SUCCESS;
  926. goto cleanup;
  927. }
  928. //
  929. // There should be a timer queued if the list is not empty.
  930. //
  931. IT_ASSERT(GlobalContext->IdleDetectionTimerHandle);
  932. //
  933. // Set idle detection overrides:
  934. //
  935. Overrides = 0;
  936. Overrides |= ItSrvOverrideIdleDetection;
  937. Overrides |= ItSrvOverrideIdleVerification;
  938. Overrides |= ItSrvOverrideUserInputCheckToStopTask;
  939. Overrides |= ItSrvOverridePostTaskIdleCheck;
  940. Overrides |= ItSrvOverrideLongRequeueTime;
  941. Overrides |= ItSrvOverrideBatteryCheckToStopTask;
  942. GlobalContext->IdleDetectionOverride = Overrides;
  943. //
  944. // If an idle detection callback is not running, try to start the next one
  945. // sooner (e.g. after 50ms).
  946. //
  947. if (!GlobalContext->IsIdleDetectionCallbackRunning) {
  948. if (!ChangeTimerQueueTimer(NULL,
  949. GlobalContext->IdleDetectionTimerHandle,
  950. 50,
  951. IT_VERYLONG_TIMER_PERIOD)) {
  952. ErrorCode = GetLastError();
  953. goto cleanup;
  954. }
  955. }
  956. //
  957. // Release the lock.
  958. //
  959. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  960. AcquiredLock = FALSE;
  961. //
  962. // Wait for all tasks to be processed: i.e. when StopIdleDetection event is set.
  963. //
  964. WaitResult = WaitForSingleObject(GlobalContext->StopIdleDetection, INFINITE);
  965. if (WaitResult != WAIT_OBJECT_0) {
  966. ErrorCode = GetLastError();
  967. goto cleanup;
  968. }
  969. ErrorCode = ERROR_SUCCESS;
  970. cleanup:
  971. GlobalContext->IdleDetectionOverride = 0;
  972. if (AcquiredLock) {
  973. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  974. }
  975. DBGPR((ITID,ITTRC,"IDLE: SrvProcessAll()=%x\n",ErrorCode));
  976. return ErrorCode;
  977. }
  978. VOID
  979. __RPC_USER
  980. IT_HANDLE_rundown (
  981. IT_HANDLE ItHandle
  982. )
  983. /*++
  984. Routine Description:
  985. This routine gets called by RPC when a client dies without
  986. unregistering.
  987. Arguments:
  988. ItHandle - Context handle for the client.
  989. Return Value:
  990. None.
  991. --*/
  992. {
  993. DWORD ErrorCode;
  994. //
  995. // Unregister the registered task.
  996. //
  997. ItSpUnregisterIdleTask(NULL, &ItHandle, TRUE);
  998. DBGPR((ITID,ITTRC,"IDLE: SrvHandleRundown(%p)\n",ItHandle));
  999. return;
  1000. }
  1001. //
  1002. // Implementation of server side support functions.
  1003. //
  1004. RPC_STATUS
  1005. __stdcall
  1006. ItSpRpcSecurityCallback (
  1007. IN PVOID Interface,
  1008. IN PVOID Context
  1009. )
  1010. /*++
  1011. Routine Description:
  1012. Arguments:
  1013. Return Value:
  1014. Win32 error code.
  1015. --*/
  1016. {
  1017. SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
  1018. PSID AdministratorsSid;
  1019. HANDLE ThreadToken;
  1020. WCHAR *BindingString;
  1021. WCHAR *ProtocolSequence;
  1022. DWORD ErrorCode;
  1023. BOOL ClientIsAdmin;
  1024. BOOLEAN ImpersonatingClient;
  1025. BOOLEAN OpenedThreadToken;
  1026. //
  1027. // Initialize locals.
  1028. //
  1029. BindingString = NULL;
  1030. ProtocolSequence = NULL;
  1031. ImpersonatingClient = FALSE;
  1032. OpenedThreadToken = FALSE;
  1033. AdministratorsSid = NULL;
  1034. //
  1035. // Make sure that the caller is calling over LRPC. We do this by
  1036. // determining the protocol sequence used from the string binding.
  1037. //
  1038. ErrorCode = RpcBindingToStringBinding(Context, &BindingString);
  1039. if (ErrorCode != RPC_S_OK) {
  1040. ErrorCode = RPC_S_ACCESS_DENIED;
  1041. goto cleanup;
  1042. }
  1043. ErrorCode = RpcStringBindingParse(BindingString,
  1044. NULL,
  1045. &ProtocolSequence,
  1046. NULL,
  1047. NULL,
  1048. NULL);
  1049. if (ErrorCode != RPC_S_OK) {
  1050. ErrorCode = RPC_S_ACCESS_DENIED;
  1051. goto cleanup;
  1052. }
  1053. if (_wcsicmp(ProtocolSequence, L"ncalrpc") != 0) {
  1054. ErrorCode = RPC_S_ACCESS_DENIED;
  1055. goto cleanup;
  1056. }
  1057. //
  1058. // Make sure the caller has admin priviliges:
  1059. //
  1060. //
  1061. // Build the Administrators group SID so we can check if the
  1062. // caller is has administrator priviliges.
  1063. //
  1064. if (!AllocateAndInitializeSid(&NtAuthority,
  1065. 2,
  1066. SECURITY_BUILTIN_DOMAIN_RID,
  1067. DOMAIN_ALIAS_RID_ADMINS,
  1068. 0, 0, 0, 0, 0, 0,
  1069. &AdministratorsSid)) {
  1070. ErrorCode = GetLastError();
  1071. goto cleanup;
  1072. }
  1073. //
  1074. // Impersonate the client to check for membership/privilige.
  1075. //
  1076. ErrorCode = RpcImpersonateClient(NULL);
  1077. if (ErrorCode != RPC_S_OK) {
  1078. goto cleanup;
  1079. }
  1080. ImpersonatingClient = TRUE;
  1081. //
  1082. // Get the thread token and check to see if the client has admin
  1083. // priviliges.
  1084. //
  1085. if (!OpenThreadToken(GetCurrentThread(),
  1086. TOKEN_READ,
  1087. FALSE,
  1088. &ThreadToken)) {
  1089. ErrorCode = GetLastError();
  1090. goto cleanup;
  1091. }
  1092. OpenedThreadToken = TRUE;
  1093. if (!CheckTokenMembership(ThreadToken,
  1094. AdministratorsSid,
  1095. &ClientIsAdmin)) {
  1096. ErrorCode = GetLastError();
  1097. goto cleanup;
  1098. }
  1099. if (!ClientIsAdmin) {
  1100. ErrorCode = ERROR_ACCESS_DENIED;
  1101. goto cleanup;
  1102. }
  1103. //
  1104. // Everything looks good: allow this call to proceed.
  1105. //
  1106. ErrorCode = RPC_S_OK;
  1107. cleanup:
  1108. if (BindingString) {
  1109. RpcStringFree(&BindingString);
  1110. }
  1111. if (ProtocolSequence) {
  1112. RpcStringFree(&ProtocolSequence);
  1113. }
  1114. if (OpenedThreadToken) {
  1115. CloseHandle(ThreadToken);
  1116. }
  1117. if (AdministratorsSid) {
  1118. FreeSid (AdministratorsSid);
  1119. }
  1120. if (ImpersonatingClient) {
  1121. RpcRevertToSelf();
  1122. }
  1123. return ErrorCode;
  1124. };
  1125. VOID
  1126. ItSpUnregisterIdleTask (
  1127. ITRPC_HANDLE Reserved,
  1128. IT_HANDLE *ItHandle,
  1129. BOOLEAN CalledInternally
  1130. )
  1131. /*++
  1132. Routine Description:
  1133. Removes the specified idle task from the idle tasks list.
  1134. As well as from a client RPC, this function can also be called
  1135. from the idle detection callback to unregister an unresponsive
  1136. idle task etc.
  1137. Arguments:
  1138. Reserved - Ignored.
  1139. ItHandle - Registration RPC Context handle. NULL on return.
  1140. CalledInternally - Whether this function was called internally and
  1141. not from an RPC client.
  1142. Return Value:
  1143. None.
  1144. --*/
  1145. {
  1146. PITSRV_IDLE_TASK_CONTEXT IdleTask;
  1147. PITSRV_GLOBAL_CONTEXT GlobalContext;
  1148. HANDLE ClientProcess;
  1149. DWORD ErrorCode;
  1150. BOOLEAN AcquiredLock;
  1151. BOOLEAN ImpersonatingClient;
  1152. //
  1153. // Initialize locals.
  1154. //
  1155. GlobalContext = ItSrvGlobalContext;
  1156. AcquiredLock = FALSE;
  1157. ImpersonatingClient = FALSE;
  1158. ClientProcess = NULL;
  1159. DBGPR((ITID,ITTRC,"IDLE: SrvUnregisterTask(%p)\n",(ItHandle)?*ItHandle:0));
  1160. //
  1161. // Grab the lock to walk through the list.
  1162. //
  1163. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  1164. AcquiredLock = TRUE;
  1165. //
  1166. // The server should not have shutdown while we could be called.
  1167. //
  1168. IT_ASSERT(GlobalContext->Status != ItSrvStatusUninitialized);
  1169. //
  1170. // If the server is shutting down, we should not do anything.
  1171. //
  1172. if (GlobalContext->Status == ItSrvStatusUninitializing) {
  1173. ErrorCode = ERROR_NOT_READY;
  1174. goto cleanup;
  1175. }
  1176. //
  1177. // Find the task.
  1178. //
  1179. IdleTask = ItSpFindIdleTask(GlobalContext, *ItHandle);
  1180. if (!IdleTask) {
  1181. ErrorCode = ERROR_NOT_FOUND;
  1182. goto cleanup;
  1183. }
  1184. if (!CalledInternally) {
  1185. //
  1186. // To check security, impersonate the client and try to open the
  1187. // client process for this idle task.
  1188. //
  1189. ErrorCode = RpcImpersonateClient(NULL);
  1190. if (ErrorCode != RPC_S_OK) {
  1191. goto cleanup;
  1192. }
  1193. ImpersonatingClient = TRUE;
  1194. ClientProcess = OpenProcess(PROCESS_ALL_ACCESS,
  1195. FALSE,
  1196. IdleTask->Properties.ProcessId);
  1197. if (!ClientProcess) {
  1198. ErrorCode = GetLastError();
  1199. goto cleanup;
  1200. }
  1201. //
  1202. // If we could open the client process for this task, it is safe
  1203. // to go on with unregistration. Now revert back to ourselves.
  1204. //
  1205. CloseHandle(ClientProcess);
  1206. ClientProcess = NULL;
  1207. RpcRevertToSelf();
  1208. ImpersonatingClient = FALSE;
  1209. }
  1210. //
  1211. // Remove it from the list, cleanup its fields and free
  1212. // the memory allocated for it.
  1213. //
  1214. GlobalContext->NumIdleTasks--;
  1215. RemoveEntryList(&IdleTask->IdleTaskLink);
  1216. //
  1217. // As a safety feature, to prevent from holding back
  1218. // someone from running, set the run event and clear the
  1219. // stop event from the task to be removed.
  1220. //
  1221. SetEvent(IdleTask->StartEvent);
  1222. ResetEvent(IdleTask->StopEvent);
  1223. //
  1224. // If this is a running task, set the event that signals
  1225. // we are removing the running idle task. This way some
  1226. // other idle task may be started if the system is still
  1227. // idle.
  1228. //
  1229. if (IdleTask->Status == ItIdleTaskRunning) {
  1230. SetEvent(GlobalContext->RemovedRunningIdleTask);
  1231. }
  1232. ItSpCleanupIdleTask(IdleTask);
  1233. IT_FREE(IdleTask);
  1234. //
  1235. // Check if the list is empty.
  1236. //
  1237. if (IsListEmpty(&GlobalContext->IdleTasksList)) {
  1238. //
  1239. // If we removed the task and the list became empty, we have
  1240. // to update the status.
  1241. //
  1242. //
  1243. // The current status should not be "waiting for idle
  1244. // tasks" or "stopping idle detection", since the list was
  1245. // NOT empty.
  1246. //
  1247. IT_ASSERT(GlobalContext->Status != ItSrvStatusWaitingForIdleTasks);
  1248. IT_ASSERT(GlobalContext->Status != ItSrvStatusStoppingIdleDetection);
  1249. //
  1250. // Update the status.
  1251. //
  1252. ItSpUpdateStatus(GlobalContext, ItSrvStatusStoppingIdleDetection);
  1253. //
  1254. // Stop idle detection (e.g. close the timer handle, set event
  1255. // etc.)
  1256. //
  1257. ItSpStopIdleDetection(GlobalContext);
  1258. } else {
  1259. //
  1260. // The status should not be waiting for idle tasks or stopping
  1261. // idle detection if the list is not empty.
  1262. //
  1263. IT_ASSERT(GlobalContext->Status != ItSrvStatusWaitingForIdleTasks &&
  1264. GlobalContext->Status != ItSrvStatusStoppingIdleDetection);
  1265. }
  1266. ErrorCode = ERROR_SUCCESS;
  1267. cleanup:
  1268. if (ClientProcess) {
  1269. CloseHandle(ClientProcess);
  1270. }
  1271. if (ImpersonatingClient) {
  1272. RpcRevertToSelf();
  1273. }
  1274. if (AcquiredLock) {
  1275. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  1276. }
  1277. //
  1278. // NULL the context handle, so RPC stubs know to end the
  1279. // connection.
  1280. //
  1281. *ItHandle = NULL;
  1282. DBGPR((ITID,ITTRC,"IDLE: SrvUnregisterTask(%p)=%x\n",(ItHandle)?*ItHandle:0,ErrorCode));
  1283. return;
  1284. }
  1285. VOID
  1286. ItSpUpdateStatus (
  1287. PITSRV_GLOBAL_CONTEXT GlobalContext,
  1288. ITSRV_GLOBAL_CONTEXT_STATUS NewStatus
  1289. )
  1290. /*++
  1291. Routine Description:
  1292. This routine updates the current status and status history on the
  1293. global context. Global contexts GlobalLock should be held while
  1294. calling this function.
  1295. Arguments:
  1296. GlobalContext - Pointer to server context structure.
  1297. NewStatus - New status.
  1298. Return Value:
  1299. None.
  1300. --*/
  1301. {
  1302. LONG StatusIdx;
  1303. DBGPR((ITID,ITTRC,"IDLE: SrvUpdateStatus(%p,%x)\n",GlobalContext,NewStatus));
  1304. //
  1305. // Verify new status.
  1306. //
  1307. IT_ASSERT(NewStatus > ItSrvStatusMinStatus);
  1308. IT_ASSERT(NewStatus < ItSrvStatusMaxStatus);
  1309. //
  1310. // Update history.
  1311. //
  1312. IT_ASSERT(ITSRV_GLOBAL_STATUS_HISTORY_SIZE >= 1);
  1313. for (StatusIdx = ITSRV_GLOBAL_STATUS_HISTORY_SIZE - 1;
  1314. StatusIdx > 0;
  1315. StatusIdx--) {
  1316. IT_ASSERT(GlobalContext->LastStatus[StatusIdx - 1] > ItSrvStatusMinStatus);
  1317. IT_ASSERT(GlobalContext->LastStatus[StatusIdx - 1] < ItSrvStatusMaxStatus);
  1318. GlobalContext->LastStatus[StatusIdx] = GlobalContext->LastStatus[StatusIdx - 1];
  1319. }
  1320. //
  1321. // Verify current status and save it.
  1322. //
  1323. IT_ASSERT(GlobalContext->Status > ItSrvStatusMinStatus);
  1324. IT_ASSERT(GlobalContext->Status < ItSrvStatusMaxStatus);
  1325. GlobalContext->LastStatus[0] = GlobalContext->Status;
  1326. //
  1327. // Update current status.
  1328. //
  1329. GlobalContext->Status = NewStatus;
  1330. //
  1331. // Update status version.
  1332. //
  1333. GlobalContext->StatusVersion++;
  1334. DBGPR((ITID,ITTRC,"IDLE: SrvUpdateStatus(%p,%x)=%d\n",
  1335. GlobalContext, NewStatus,GlobalContext->StatusVersion));
  1336. return;
  1337. }
  1338. VOID
  1339. ItSpCleanupGlobalContext (
  1340. PITSRV_GLOBAL_CONTEXT GlobalContext
  1341. )
  1342. /*++
  1343. Routine Description:
  1344. This function cleans up the various fields of the ITSRV_GLOBAL_CONTEXT
  1345. structure passed in. It does not free the structure itself.
  1346. You should not be holding the global lock when calling this
  1347. function, as it will be freed too. No workers etc. should be
  1348. active. The idle detection timer should have already been
  1349. removed. The structure should not be used after cleanup until it
  1350. is initialized again.
  1351. The current status of the global context should either be
  1352. initializing or uninitializing. It will be set to uninitialized.
  1353. Arguments:
  1354. GlobalContext - Pointer to server context structure.
  1355. Return Value:
  1356. None.
  1357. --*/
  1358. {
  1359. PITSRV_IDLE_TASK_CONTEXT IdleTask;
  1360. PLIST_ENTRY ListHead;
  1361. HANDLE EventHandle;
  1362. DBGPR((ITID,ITSRVD,"IDLE: SrvCleanupContext(%p)\n",GlobalContext));
  1363. //
  1364. // Make sure there is no active idle detection timer.
  1365. //
  1366. if (GlobalContext->IdleDetectionTimerHandle) {
  1367. IT_ASSERT(FALSE);
  1368. return;
  1369. }
  1370. //
  1371. // Verify the status of the global context.
  1372. //
  1373. if (GlobalContext->Status != ItSrvStatusInitializing &&
  1374. GlobalContext->Status != ItSrvStatusUninitializing) {
  1375. IT_ASSERT(FALSE);
  1376. return;
  1377. }
  1378. //
  1379. // Close the handle to global lock.
  1380. //
  1381. if (GlobalContext->GlobalLock) {
  1382. CloseHandle(GlobalContext->GlobalLock);
  1383. }
  1384. //
  1385. // Close the handle to the various events.
  1386. //
  1387. if (GlobalContext->StopIdleDetection) {
  1388. CloseHandle(GlobalContext->StopIdleDetection);
  1389. }
  1390. if (GlobalContext->IdleDetectionStopped) {
  1391. CloseHandle(GlobalContext->IdleDetectionStopped);
  1392. }
  1393. if (GlobalContext->RemovedRunningIdleTask) {
  1394. CloseHandle(GlobalContext->RemovedRunningIdleTask);
  1395. }
  1396. if (GlobalContext->ServiceHandlerNotRunning) {
  1397. CloseHandle(GlobalContext->ServiceHandlerNotRunning);
  1398. }
  1399. //
  1400. // Close WMI DiskPerf handle.
  1401. //
  1402. if (GlobalContext->DiskPerfWmiHandle) {
  1403. WmiCloseBlock(GlobalContext->DiskPerfWmiHandle);
  1404. }
  1405. //
  1406. // Free WMI query buffer.
  1407. //
  1408. if (GlobalContext->WmiQueryBuffer) {
  1409. IT_FREE(GlobalContext->WmiQueryBuffer);
  1410. }
  1411. //
  1412. // Cleanup the snapshot buffer.
  1413. //
  1414. ItSpCleanupSystemSnapshot(&GlobalContext->LastSystemSnapshot);
  1415. //
  1416. // Walk through the list of registered idle tasks.
  1417. //
  1418. while (!IsListEmpty(&GlobalContext->IdleTasksList)) {
  1419. GlobalContext->NumIdleTasks--;
  1420. ListHead = RemoveHeadList(&GlobalContext->IdleTasksList);
  1421. IdleTask = CONTAINING_RECORD(ListHead,
  1422. ITSRV_IDLE_TASK_CONTEXT,
  1423. IdleTaskLink);
  1424. //
  1425. // Cleanup and free the idle task structure.
  1426. //
  1427. ItSpCleanupIdleTask(IdleTask);
  1428. IT_FREE(IdleTask);
  1429. }
  1430. //
  1431. // Free the RPC binding vector.
  1432. //
  1433. if (GlobalContext->RpcBindingVector) {
  1434. RpcBindingVectorFree(&GlobalContext->RpcBindingVector);
  1435. }
  1436. //
  1437. // Update status.
  1438. //
  1439. ItSpUpdateStatus(GlobalContext, ItSrvStatusUninitialized);
  1440. DBGPR((ITID,ITSRVD,"IDLE: SrvCleanupContext(%p)=0\n",GlobalContext));
  1441. return;
  1442. }
  1443. VOID
  1444. ItSpCleanupIdleTask (
  1445. PITSRV_IDLE_TASK_CONTEXT IdleTask
  1446. )
  1447. /*++
  1448. Routine Description:
  1449. This function cleans up the various fields of the ITSRV_IDLE_TASK_CONTEXT
  1450. structure. It does not free the structure itself.
  1451. Arguments:
  1452. IdleTask - Pointer to idle task server context.
  1453. Return Value:
  1454. None.
  1455. --*/
  1456. {
  1457. DBGPR((ITID,ITSRVD,"IDLE: SrvCleanupTask(%p)\n",IdleTask));
  1458. //
  1459. // Close handles to start/stop events.
  1460. //
  1461. if (IdleTask->StartEvent) {
  1462. CloseHandle(IdleTask->StartEvent);
  1463. }
  1464. if (IdleTask->StopEvent) {
  1465. CloseHandle(IdleTask->StopEvent);
  1466. }
  1467. }
  1468. ULONG
  1469. ItpVerifyIdleTaskProperties (
  1470. PIT_IDLE_TASK_PROPERTIES IdleTaskProperties
  1471. )
  1472. /*++
  1473. Routine Description:
  1474. Verifies the specified structure.
  1475. Arguments:
  1476. IdleTaskProperties - Pointer to properties for the idle task.
  1477. Return Value:
  1478. 0 - Verification passed.
  1479. FailedCheckId - Id of the check that failed.
  1480. --*/
  1481. {
  1482. ULONG FailedCheckId;
  1483. //
  1484. // Initialize locals.
  1485. //
  1486. FailedCheckId = 1;
  1487. //
  1488. // Verify the structure size/version.
  1489. //
  1490. if (IdleTaskProperties->Size != sizeof(*IdleTaskProperties)) {
  1491. FailedCheckId = 10;
  1492. goto cleanup;
  1493. }
  1494. //
  1495. // Verify that the idle task ID is valid.
  1496. //
  1497. if (IdleTaskProperties->IdleTaskId >= ItMaxIdleTaskId) {
  1498. FailedCheckId = 20;
  1499. goto cleanup;
  1500. }
  1501. //
  1502. // We passed all the checks.
  1503. //
  1504. FailedCheckId = 0;
  1505. cleanup:
  1506. DBGPR((ITID,ITSRVD,"IDLE: SrvVerifyTaskProp(%p)=%d\n",
  1507. IdleTaskProperties, FailedCheckId));
  1508. return FailedCheckId;
  1509. }
  1510. DWORD
  1511. ItSpStartIdleDetection (
  1512. PITSRV_GLOBAL_CONTEXT GlobalContext
  1513. )
  1514. /*++
  1515. Routine Description:
  1516. This function is called to start idle detection.
  1517. GlobalContext's GlobalLock should be held while calling this
  1518. function.
  1519. The current state should be ItSrvStatusWaitingForIdleTasks when
  1520. calling this function. The caller should ensure that stopping
  1521. previous idle detection has been completed.
  1522. The caller should update the state to "detecting idle" etc, if
  1523. this function returns success.
  1524. Arguments:
  1525. GlobalContext - Pointer to server context structure.
  1526. Return Value:
  1527. Win32 error code.
  1528. --*/
  1529. {
  1530. DWORD ErrorCode;
  1531. NTSTATUS Status;
  1532. SYSTEM_BASIC_INFORMATION BasicInfo;
  1533. DWORD TimerPeriod;
  1534. DBGPR((ITID,ITTRC,"IDLE: SrvStartIdleDetection(%p)\n",GlobalContext));
  1535. //
  1536. // Make sure the current status is ItSrvStatusWaitingForIdleTasks.
  1537. //
  1538. IT_ASSERT(GlobalContext->Status == ItSrvStatusWaitingForIdleTasks);
  1539. //
  1540. // If we do not already have a diskperf wmi handle, try to get
  1541. // one.
  1542. //
  1543. if (!GlobalContext->DiskPerfWmiHandle) {
  1544. //
  1545. // Get WMI context so we can query disk performance.
  1546. //
  1547. ErrorCode = WmiOpenBlock((GUID *)&DiskPerfGuid,
  1548. GENERIC_READ,
  1549. &GlobalContext->DiskPerfWmiHandle);
  1550. if (ErrorCode != ERROR_SUCCESS) {
  1551. //
  1552. // Disk counters may not be initiated. We'll have to do
  1553. // without them.
  1554. //
  1555. GlobalContext->DiskPerfWmiHandle = NULL;
  1556. }
  1557. }
  1558. //
  1559. // Determine the number of processors on the system.
  1560. //
  1561. if (!GlobalContext->NumProcessors) {
  1562. Status = NtQuerySystemInformation(SystemBasicInformation,
  1563. &BasicInfo,
  1564. sizeof(BasicInfo),
  1565. NULL);
  1566. if (!NT_SUCCESS(Status)) {
  1567. ErrorCode = RtlNtStatusToDosError(Status);
  1568. goto cleanup;
  1569. }
  1570. GlobalContext->NumProcessors = BasicInfo.NumberOfProcessors;
  1571. }
  1572. //
  1573. // Get initial snapshot only when this is the very first time we
  1574. // are starting idle detection. Otherwise we'll keep the last
  1575. // snapshot we got.
  1576. //
  1577. IT_ASSERT(ITSRV_GLOBAL_STATUS_HISTORY_SIZE >= 1);
  1578. if (GlobalContext->LastStatus[0] == ItSrvStatusInitializing) {
  1579. ErrorCode = ItSpGetSystemSnapshot(GlobalContext,
  1580. &GlobalContext->LastSystemSnapshot);
  1581. if (ErrorCode != ERROR_SUCCESS) {
  1582. goto cleanup;
  1583. }
  1584. }
  1585. //
  1586. // Make sure the StopIdleDetection event is cleared.
  1587. //
  1588. ResetEvent(GlobalContext->StopIdleDetection);
  1589. //
  1590. // There should not be a timer-queue timer. We'll create one.
  1591. //
  1592. IT_ASSERT(!GlobalContext->IdleDetectionTimerHandle);
  1593. //
  1594. // Set the default timer period.
  1595. //
  1596. TimerPeriod = GlobalContext->Parameters.IdleDetectionPeriod;
  1597. //
  1598. // Adjust timer period to something small if we were idling when
  1599. // idle detection was stopped.
  1600. //
  1601. IT_ASSERT(ITSRV_GLOBAL_STATUS_HISTORY_SIZE >= 2);
  1602. if (GlobalContext->LastStatus[0] == ItSrvStatusStoppingIdleDetection &&
  1603. GlobalContext->LastStatus[1] == ItSrvStatusRunningIdleTasks) {
  1604. //
  1605. // Set small wake up time in ms. We'll still check if we were idle
  1606. // since the last snapshot and verify it over small periods.
  1607. //
  1608. if (TimerPeriod > (60 * 1000)) {
  1609. TimerPeriod = 60 * 1000; // 1 minute.
  1610. }
  1611. }
  1612. DBGPR((ITID,ITTRC,"IDLE: SrvStartIdleDetection(%p)-TimerPeriod=%d\n",GlobalContext,TimerPeriod));
  1613. if (!CreateTimerQueueTimer(&GlobalContext->IdleDetectionTimerHandle,
  1614. NULL,
  1615. ItSpIdleDetectionCallbackRoutine,
  1616. GlobalContext,
  1617. TimerPeriod,
  1618. IT_VERYLONG_TIMER_PERIOD,
  1619. WT_EXECUTELONGFUNCTION)) {
  1620. GlobalContext->IdleDetectionTimerHandle = NULL;
  1621. ErrorCode = GetLastError();
  1622. goto cleanup;
  1623. }
  1624. //
  1625. // We are done.
  1626. //
  1627. ErrorCode = ERROR_SUCCESS;
  1628. cleanup:
  1629. DBGPR((ITID,ITTRC,"IDLE: SrvStartIdleDetection(%p)=%x\n",GlobalContext,ErrorCode));
  1630. return ErrorCode;
  1631. }
  1632. VOID
  1633. ItSpStopIdleDetection (
  1634. PITSRV_GLOBAL_CONTEXT GlobalContext
  1635. )
  1636. /*++
  1637. Routine Description:
  1638. This function is called to stop idle detection. Even though it
  1639. returns immediately, idle detection may not have stopped
  1640. completely (i.e. the callback may be running). The
  1641. IdleDetectionStopped event on the GlobalContext will be set when
  1642. the idle detection will be fully stopped.
  1643. GlobalContext's GlobalLock should be held while calling this
  1644. function.
  1645. The status before calling this function should be set to
  1646. ItSrvStatusStoppingIdleDetection or ItSrvStatusUninitializing.
  1647. Arguments:
  1648. GlobalContext - Pointer to server context structure.
  1649. Return Value:
  1650. None.
  1651. --*/
  1652. {
  1653. DBGPR((ITID,ITTRC,"IDLE: SrvStopIdleDetection(%p)\n",GlobalContext));
  1654. //
  1655. // Make sure the status is set right.
  1656. //
  1657. IT_ASSERT(GlobalContext->Status == ItSrvStatusStoppingIdleDetection ||
  1658. GlobalContext->Status == ItSrvStatusUninitializing);
  1659. //
  1660. // Clear the event that will be set when idle detection has been
  1661. // fully stoped.
  1662. //
  1663. ResetEvent(GlobalContext->IdleDetectionStopped);
  1664. //
  1665. // Signal the event that signals the idle detection callback to go
  1666. // away asap.
  1667. //
  1668. if (GlobalContext->StopIdleDetection) {
  1669. SetEvent(GlobalContext->StopIdleDetection);
  1670. }
  1671. //
  1672. // Close the handle to the timer-queue timer.
  1673. //
  1674. IT_ASSERT(GlobalContext->IdleDetectionTimerHandle);
  1675. DeleteTimerQueueTimer(NULL,
  1676. GlobalContext->IdleDetectionTimerHandle,
  1677. GlobalContext->IdleDetectionStopped);
  1678. GlobalContext->IdleDetectionTimerHandle = NULL;
  1679. DBGPR((ITID,ITTRC,"IDLE: SrvStopIdleDetection(%p)=0\n",GlobalContext));
  1680. return;
  1681. }
  1682. PITSRV_IDLE_TASK_CONTEXT
  1683. ItSpFindRunningIdleTask (
  1684. PITSRV_GLOBAL_CONTEXT GlobalContext
  1685. )
  1686. /*++
  1687. Routine Description:
  1688. If there is an idle task marked "running" this routine finds and
  1689. returns it. GlobalContext's GlobalLock should be held while
  1690. calling this function.
  1691. Arguments:
  1692. GlobalContext - Pointer to server context structure.
  1693. Return Value:
  1694. Pointer to running idle task or NULL if no idle tasks are marked
  1695. running.
  1696. --*/
  1697. {
  1698. PITSRV_IDLE_TASK_CONTEXT IdleTask;
  1699. PITSRV_IDLE_TASK_CONTEXT RunningIdleTask;
  1700. PLIST_ENTRY HeadEntry;
  1701. PLIST_ENTRY NextEntry;
  1702. //
  1703. // Initialize locals.
  1704. //
  1705. RunningIdleTask = NULL;
  1706. HeadEntry = &GlobalContext->IdleTasksList;
  1707. NextEntry = HeadEntry->Flink;
  1708. while (NextEntry != HeadEntry) {
  1709. IdleTask = CONTAINING_RECORD(NextEntry,
  1710. ITSRV_IDLE_TASK_CONTEXT,
  1711. IdleTaskLink);
  1712. NextEntry = NextEntry->Flink;
  1713. if (IdleTask->Status == ItIdleTaskRunning) {
  1714. //
  1715. // There should be a single running task.
  1716. //
  1717. IT_ASSERT(RunningIdleTask == NULL);
  1718. //
  1719. // We found it. Loop through the remaining entries to make
  1720. // sure there is really only one if not debugging.
  1721. //
  1722. RunningIdleTask = IdleTask;
  1723. #ifndef IT_DBG
  1724. break;
  1725. #endif // !IT_DBG
  1726. }
  1727. }
  1728. //
  1729. // Fall through with RunningIdleTask found when walking the list
  1730. // or NULL as initialized at the top.
  1731. //
  1732. DBGPR((ITID,ITSRVD,"IDLE: SrvFindRunningTask(%p)=%p\n",GlobalContext,RunningIdleTask));
  1733. return RunningIdleTask;
  1734. }
  1735. PITSRV_IDLE_TASK_CONTEXT
  1736. ItSpFindIdleTask (
  1737. PITSRV_GLOBAL_CONTEXT GlobalContext,
  1738. IT_HANDLE ItHandle
  1739. )
  1740. /*++
  1741. Routine Description:
  1742. If there is an idle task registered with ItHandle, this routine
  1743. finds and returns it. GlobalContext's GlobalLock should be held
  1744. while calling this function.
  1745. Arguments:
  1746. GlobalContext - Pointer to server context structure.
  1747. ItHandle - Registration handle.
  1748. Return Value:
  1749. Pointer to found idle task or NULL if no matching idle tasks were
  1750. found.
  1751. --*/
  1752. {
  1753. PITSRV_IDLE_TASK_CONTEXT IdleTask;
  1754. PITSRV_IDLE_TASK_CONTEXT FoundIdleTask;
  1755. PLIST_ENTRY HeadEntry;
  1756. PLIST_ENTRY NextEntry;
  1757. //
  1758. // Initialize locals.
  1759. //
  1760. FoundIdleTask = NULL;
  1761. HeadEntry = &GlobalContext->IdleTasksList;
  1762. NextEntry = HeadEntry->Flink;
  1763. while (NextEntry != HeadEntry) {
  1764. IdleTask = CONTAINING_RECORD(NextEntry,
  1765. ITSRV_IDLE_TASK_CONTEXT,
  1766. IdleTaskLink);
  1767. NextEntry = NextEntry->Flink;
  1768. if ((IT_HANDLE) IdleTask == ItHandle) {
  1769. FoundIdleTask = IdleTask;
  1770. break;
  1771. }
  1772. }
  1773. //
  1774. // Fall through with FoundIdleTask found when walking the list or
  1775. // NULL as initialized at the top.
  1776. //
  1777. DBGPR((ITID,ITSRVD,"IDLE: SrvFindTask(%p,%p)=%p\n",GlobalContext,ItHandle,FoundIdleTask));
  1778. return FoundIdleTask;
  1779. }
  1780. VOID
  1781. CALLBACK
  1782. ItSpIdleDetectionCallbackRoutine (
  1783. PVOID Parameter,
  1784. BOOLEAN TimerOrWaitFired
  1785. )
  1786. /*++
  1787. Routine Description:
  1788. While there are idle tasks to run, this function is called every
  1789. IdleDetectionPeriod to determine if the system is idle. It uses
  1790. the last system snapshot saved in the global context. If it
  1791. determines that the system was idle in the time it was called, it
  1792. samples system activity for smaller intervals, to make sure system
  1793. activity that started as the (possible very long) idle detection
  1794. period expired, is not ignored.
  1795. As long as we are not told to go away (checked by the macro
  1796. ITSP_SHOULD_STOP_IDLE_DETECTION) this function always tries to
  1797. queue a timer to get called back in IdleDetectionPeriod. This
  1798. macro should be called each time the lock is acquired in this
  1799. function. Also, we should not sleep blindly when we need to let
  1800. time pass, but wait on an event that will get signaled when we are
  1801. asked to stop.
  1802. Arguments:
  1803. Parameter - Pointer to an idle detection context.
  1804. TimerOrWaitFired - Reason why we were called. This should be TRUE
  1805. (i.e. our timer expired)
  1806. Return Value:
  1807. None.
  1808. --*/
  1809. {
  1810. DWORD ErrorCode;
  1811. PITSRV_GLOBAL_CONTEXT GlobalContext;
  1812. ITSRV_SYSTEM_SNAPSHOT CurrentSystemSnapshot;
  1813. SYSTEM_POWER_STATUS SystemPowerStatus;
  1814. LASTINPUTINFO LastUserInput;
  1815. LASTINPUTINFO CurrentLastUserInput;
  1816. BOOLEAN SystemIsIdle;
  1817. BOOLEAN AcquiredLock;
  1818. BOOLEAN MarkedIdleTaskRunning;
  1819. BOOLEAN OrderedToStop;
  1820. ULONG VerificationIdx;
  1821. DWORD WaitResult;
  1822. PITSRV_IDLE_TASK_CONTEXT IdleTask;
  1823. ULONG NumTasksRun;
  1824. ULONG DuePeriod;
  1825. BOOLEAN NotIdleBecauseOfUserInput;
  1826. BOOLEAN MisfiredCallback;
  1827. IT_HANDLE ItHandle;
  1828. NTSTATUS Status;
  1829. SYSTEM_POWER_INFORMATION PowerInfo;
  1830. //
  1831. // Initialize locals.
  1832. //
  1833. GlobalContext = Parameter;
  1834. AcquiredLock = FALSE;
  1835. MarkedIdleTaskRunning = FALSE;
  1836. ItSpInitializeSystemSnapshot(&CurrentSystemSnapshot);
  1837. LastUserInput.cbSize = sizeof(LASTINPUTINFO);
  1838. CurrentLastUserInput.cbSize = sizeof(LASTINPUTINFO);
  1839. NumTasksRun = 0;
  1840. SystemIsIdle = FALSE;
  1841. MisfiredCallback = FALSE;
  1842. NotIdleBecauseOfUserInput = FALSE;
  1843. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback(%p)\n",GlobalContext));
  1844. //
  1845. // We should not be called without an idle detection context.
  1846. //
  1847. IT_ASSERT(GlobalContext);
  1848. //
  1849. // We should be called only because IdleDetectionPeriod passed and
  1850. // our timer expired.
  1851. //
  1852. IT_ASSERT(TimerOrWaitFired);
  1853. //
  1854. // Get the server lock.
  1855. //
  1856. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  1857. AcquiredLock = TRUE;
  1858. //
  1859. // If there is an idle detection callback already running simply
  1860. // exit without doing anything.
  1861. //
  1862. if (GlobalContext->IsIdleDetectionCallbackRunning) {
  1863. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-Misfired!\n"));
  1864. MisfiredCallback = TRUE;
  1865. ErrorCode = ERROR_ALREADY_EXISTS;
  1866. goto cleanup;
  1867. }
  1868. GlobalContext->IsIdleDetectionCallbackRunning = TRUE;
  1869. //
  1870. // Make sure the current state is feasible if we are running.
  1871. //
  1872. IT_ASSERT(GlobalContext->Status == ItSrvStatusDetectingIdle ||
  1873. GlobalContext->Status == ItSrvStatusUninitializing ||
  1874. GlobalContext->Status == ItSrvStatusStoppingIdleDetection);
  1875. //
  1876. // If we are told to go away, do so.
  1877. //
  1878. if (ITSP_SHOULD_STOP_IDLE_DETECTION(GlobalContext)) {
  1879. ErrorCode = ERROR_SUCCESS;
  1880. goto cleanup;
  1881. }
  1882. //
  1883. // Get initial last input time that will be used later if we
  1884. // decide to run idle tasks.
  1885. //
  1886. ErrorCode = ItSpGetLastInputInfo(&LastUserInput);
  1887. if (ErrorCode != ERROR_SUCCESS) {
  1888. goto cleanup;
  1889. }
  1890. //
  1891. // Perform idle detection over the period we've been sleeping (if
  1892. // it is not overriden.)
  1893. //
  1894. if (!(GlobalContext->IdleDetectionOverride & ItSrvOverrideIdleDetection)) {
  1895. //
  1896. // Get current system snapshot.
  1897. //
  1898. ErrorCode = ItSpGetSystemSnapshot(GlobalContext,
  1899. &CurrentSystemSnapshot);
  1900. if (ErrorCode != ERROR_SUCCESS) {
  1901. goto cleanup;
  1902. }
  1903. //
  1904. // See if system looks idle since the last snapshot.
  1905. //
  1906. SystemIsIdle = ItSpIsSystemIdle(GlobalContext,
  1907. &CurrentSystemSnapshot,
  1908. &GlobalContext->LastSystemSnapshot,
  1909. ItSrvInitialIdleCheck);
  1910. //
  1911. // If the last input times don't match and that's why we are not
  1912. // idle, make a note.
  1913. //
  1914. if ((CurrentSystemSnapshot.GotLastInputInfo &&
  1915. GlobalContext->LastSystemSnapshot.GotLastInputInfo) &&
  1916. (CurrentSystemSnapshot.LastInputInfo.dwTime !=
  1917. GlobalContext->LastSystemSnapshot.LastInputInfo.dwTime)) {
  1918. NotIdleBecauseOfUserInput = TRUE;
  1919. ASSERT(!SystemIsIdle);
  1920. }
  1921. //
  1922. // Save snapshot.
  1923. //
  1924. ErrorCode = ItSpCopySystemSnapshot(&GlobalContext->LastSystemSnapshot,
  1925. &CurrentSystemSnapshot);
  1926. if (ErrorCode != ERROR_SUCCESS) {
  1927. goto cleanup;
  1928. }
  1929. //
  1930. // If the system does not look idle over the detection period
  1931. // we'll poll again later.
  1932. //
  1933. if (!SystemIsIdle) {
  1934. ErrorCode = ERROR_SUCCESS;
  1935. goto cleanup;
  1936. }
  1937. }
  1938. //
  1939. // If we were not asked to override idle verification, verify that
  1940. // the system is idle for a while.
  1941. //
  1942. if (!(GlobalContext->IdleDetectionOverride & ItSrvOverrideIdleVerification)) {
  1943. //
  1944. // Loop for a while getting system snapshots over shorter
  1945. // durations. This helps us catch recent significant system
  1946. // activity that seemed insignificant when viewed over the whole
  1947. // IdleDetectionPeriod.
  1948. //
  1949. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-Verifying\n"));
  1950. for (VerificationIdx = 0;
  1951. VerificationIdx < GlobalContext->Parameters.NumVerifications;
  1952. VerificationIdx ++) {
  1953. //
  1954. // Release the lock.
  1955. //
  1956. IT_ASSERT(AcquiredLock);
  1957. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  1958. AcquiredLock = FALSE;
  1959. //
  1960. // Sleep for the verification period.
  1961. //
  1962. WaitResult = WaitForSingleObject(GlobalContext->StopIdleDetection,
  1963. GlobalContext->Parameters.IdleVerificationPeriod);
  1964. if (WaitResult != WAIT_TIMEOUT) {
  1965. if (WaitResult == WAIT_OBJECT_0) {
  1966. ErrorCode = ERROR_SUCCESS;
  1967. } else {
  1968. ErrorCode = GetLastError();
  1969. }
  1970. goto cleanup;
  1971. }
  1972. //
  1973. // Acquire the lock.
  1974. //
  1975. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  1976. AcquiredLock = TRUE;
  1977. //
  1978. // Are we told to go away (this may happen from the time the
  1979. // wait returns till we acquire the lock.)
  1980. //
  1981. if (ITSP_SHOULD_STOP_IDLE_DETECTION(GlobalContext)) {
  1982. ErrorCode = ERROR_SUCCESS;
  1983. goto cleanup;
  1984. }
  1985. //
  1986. // Get the new snapshot.
  1987. //
  1988. ErrorCode = ItSpGetSystemSnapshot(GlobalContext,
  1989. &CurrentSystemSnapshot);
  1990. if (ErrorCode != ERROR_SUCCESS) {
  1991. goto cleanup;
  1992. }
  1993. //
  1994. // See if system looks idle since the last snapshot.
  1995. //
  1996. SystemIsIdle = ItSpIsSystemIdle(GlobalContext,
  1997. &CurrentSystemSnapshot,
  1998. &GlobalContext->LastSystemSnapshot,
  1999. ItSrvIdleVerificationCheck);
  2000. //
  2001. // Save snapshot.
  2002. //
  2003. ErrorCode = ItSpCopySystemSnapshot(&GlobalContext->LastSystemSnapshot,
  2004. &CurrentSystemSnapshot);
  2005. if (ErrorCode != ERROR_SUCCESS) {
  2006. goto cleanup;
  2007. }
  2008. //
  2009. // If the system was not idle over the detection period we'll
  2010. // try again later.
  2011. //
  2012. if (!SystemIsIdle) {
  2013. ErrorCode = ERROR_SUCCESS;
  2014. goto cleanup;
  2015. }
  2016. }
  2017. }
  2018. //
  2019. // The system has become idle. Update the status.
  2020. //
  2021. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-RunningIdleTasks\n"));
  2022. IT_ASSERT(GlobalContext->Status == ItSrvStatusDetectingIdle);
  2023. ItSpUpdateStatus(GlobalContext, ItSrvStatusRunningIdleTasks);
  2024. //
  2025. // While we are not told to go away...
  2026. //
  2027. while (!ITSP_SHOULD_STOP_IDLE_DETECTION(GlobalContext)) {
  2028. //
  2029. // We should be holding the lock when we are making the above
  2030. // check and whenever we come here.
  2031. //
  2032. IT_ASSERT(AcquiredLock);
  2033. //
  2034. // The list should not be empty.
  2035. //
  2036. IT_ASSERT(!IsListEmpty(&GlobalContext->IdleTasksList));
  2037. if (IsListEmpty(&GlobalContext->IdleTasksList)) {
  2038. ErrorCode = ERROR_INVALID_FUNCTION;
  2039. goto cleanup;
  2040. }
  2041. //
  2042. // Mark the first idle task in the list running and signal its
  2043. // event.
  2044. //
  2045. IdleTask = CONTAINING_RECORD(GlobalContext->IdleTasksList.Flink,
  2046. ITSRV_IDLE_TASK_CONTEXT,
  2047. IdleTaskLink);
  2048. //
  2049. // It should not be uninitialized or already running!
  2050. //
  2051. IT_ASSERT(IdleTask->Status == ItIdleTaskQueued);
  2052. IdleTask->Status = ItIdleTaskRunning;
  2053. MarkedIdleTaskRunning = TRUE;
  2054. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-Running %d\n",IdleTask->Properties.IdleTaskId));
  2055. NumTasksRun++;
  2056. //
  2057. // Signal its events.
  2058. //
  2059. ResetEvent(IdleTask->StopEvent);
  2060. SetEvent(IdleTask->StartEvent);
  2061. //
  2062. // Reset the event that will get set when the idle task we
  2063. // mark running gets unregistered.
  2064. //
  2065. ResetEvent(GlobalContext->RemovedRunningIdleTask);
  2066. //
  2067. // Release the lock.
  2068. //
  2069. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  2070. AcquiredLock = FALSE;
  2071. //
  2072. // Poll frequently for user input while system background
  2073. // activity should be taking place. We cannot poll for
  2074. // anything else because the running idle task is supposed to
  2075. // be using CPU, issuing I/Os etc. As soon as user input comes
  2076. // we want to signal background threads to stop their
  2077. // activity. We will do this until the running idle task is
  2078. // completed and unregistered.
  2079. //
  2080. do {
  2081. //
  2082. // We should not be holding the lock while polling.
  2083. //
  2084. IT_ASSERT(!AcquiredLock);
  2085. //
  2086. // Note that since we set MarkedIdleTaskRunning, going to
  2087. // "cleanup" will end up marking this idle task not
  2088. // running and setting the stop event.
  2089. //
  2090. if (!(GlobalContext->IdleDetectionOverride & ItSrvOverrideUserInputCheckToStopTask)) {
  2091. //
  2092. // Get last user input.
  2093. //
  2094. ErrorCode = ItSpGetLastInputInfo(&CurrentLastUserInput);
  2095. if (ErrorCode != ERROR_SUCCESS) {
  2096. goto cleanup;
  2097. }
  2098. if (LastUserInput.dwTime != CurrentLastUserInput.dwTime) {
  2099. //
  2100. // There is new input.
  2101. //
  2102. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-NewUserInput\n"));
  2103. SystemIsIdle = FALSE;
  2104. ErrorCode = ERROR_SUCCESS;
  2105. goto cleanup;
  2106. }
  2107. //
  2108. // We don't need to update last input since it should
  2109. // be same as current.
  2110. //
  2111. }
  2112. //
  2113. // Wait for a while to poll for user input again. We
  2114. // should not be holding the lock while waiting.
  2115. //
  2116. IT_ASSERT(!AcquiredLock);
  2117. WaitResult = WaitForSingleObject(GlobalContext->RemovedRunningIdleTask,
  2118. GlobalContext->Parameters.IdleInputCheckPeriod);
  2119. if (WaitResult == WAIT_OBJECT_0) {
  2120. //
  2121. // Break out of this loop to pick up a new idle
  2122. // task to run.
  2123. //
  2124. MarkedIdleTaskRunning = FALSE;
  2125. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-TaskRemoved\n"));
  2126. break;
  2127. }
  2128. if (WaitResult != WAIT_TIMEOUT) {
  2129. //
  2130. // Something went wrong...
  2131. //
  2132. ErrorCode = ERROR_INVALID_FUNCTION;
  2133. goto cleanup;
  2134. }
  2135. //
  2136. // Check to see if the system has started running from battery.
  2137. //
  2138. if (!(GlobalContext->IdleDetectionOverride & ItSrvOverrideBatteryCheckToStopTask)) {
  2139. if (GetSystemPowerStatus(&SystemPowerStatus)) {
  2140. if (SystemPowerStatus.ACLineStatus == 0) {
  2141. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-SystemOnBattery\n"));
  2142. SystemIsIdle = FALSE;
  2143. ErrorCode = ERROR_SUCCESS;
  2144. goto cleanup;
  2145. }
  2146. }
  2147. }
  2148. //
  2149. // If the kernel is about to enter standby or hibernate because
  2150. // it has also detected the system idle, stop this task.
  2151. //
  2152. if (!(GlobalContext->IdleDetectionOverride & ItSrvOverrideAutoPowerCheckToStopTask)) {
  2153. Status = NtPowerInformation(SystemPowerInformation,
  2154. NULL,
  2155. 0,
  2156. &PowerInfo,
  2157. sizeof(PowerInfo));
  2158. if (NT_SUCCESS(Status)) {
  2159. if (PowerInfo.TimeRemaining < IT_DEFAULT_MAX_TIME_REMAINING_TO_SLEEP) {
  2160. SystemIsIdle = FALSE;
  2161. ErrorCode = ERROR_SUCCESS;
  2162. goto cleanup;
  2163. }
  2164. }
  2165. }
  2166. //
  2167. // The idle task is still running. Loop to check for user
  2168. // input.
  2169. //
  2170. } while (TRUE);
  2171. if (!AcquiredLock) {
  2172. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  2173. AcquiredLock = TRUE;
  2174. }
  2175. if (!(GlobalContext->IdleDetectionOverride & ItSrvOverridePostTaskIdleCheck)) {
  2176. //
  2177. // Get the latest snapshot of the system. This snapshot will
  2178. // be used to determine if the system is still idle before
  2179. // picking up a new task.
  2180. //
  2181. ErrorCode = ItSpGetSystemSnapshot(GlobalContext,
  2182. &GlobalContext->LastSystemSnapshot);
  2183. if (ErrorCode != ERROR_SUCCESS) {
  2184. goto cleanup;
  2185. }
  2186. //
  2187. // Release the lock.
  2188. //
  2189. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  2190. AcquiredLock = FALSE;
  2191. //
  2192. // Wait for the verification period.
  2193. //
  2194. WaitResult = WaitForSingleObject(GlobalContext->StopIdleDetection,
  2195. GlobalContext->Parameters.IdleVerificationPeriod);
  2196. if (WaitResult != WAIT_TIMEOUT) {
  2197. if (WaitResult == WAIT_OBJECT_0) {
  2198. ErrorCode = ERROR_SUCCESS;
  2199. } else {
  2200. ErrorCode = GetLastError();
  2201. }
  2202. goto cleanup;
  2203. }
  2204. //
  2205. // Acquire the lock and get new snapshot.
  2206. //
  2207. IT_ASSERT(!AcquiredLock);
  2208. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  2209. AcquiredLock = TRUE;
  2210. ErrorCode = ItSpGetSystemSnapshot(GlobalContext,
  2211. &CurrentSystemSnapshot);
  2212. if (ErrorCode != ERROR_SUCCESS) {
  2213. goto cleanup;
  2214. }
  2215. //
  2216. // See if system looks idle since the last snapshot.
  2217. //
  2218. SystemIsIdle = ItSpIsSystemIdle(GlobalContext,
  2219. &CurrentSystemSnapshot,
  2220. &GlobalContext->LastSystemSnapshot,
  2221. ItSrvIdleVerificationCheck);
  2222. //
  2223. // Save snapshot.
  2224. //
  2225. ErrorCode = ItSpCopySystemSnapshot(&GlobalContext->LastSystemSnapshot,
  2226. &CurrentSystemSnapshot);
  2227. if (ErrorCode != ERROR_SUCCESS) {
  2228. goto cleanup;
  2229. }
  2230. //
  2231. // If the system is no longer idle, we should not start a new task.
  2232. //
  2233. if (!SystemIsIdle) {
  2234. ErrorCode = ERROR_SUCCESS;
  2235. goto cleanup;
  2236. }
  2237. }
  2238. //
  2239. // Loop to try to run more idle tasks. The lock should be acquired.
  2240. //
  2241. IT_ASSERT(AcquiredLock);
  2242. }
  2243. //
  2244. // We should come here only if we were asked to stop, i.e. the
  2245. // check in while() causes us to break from looping.
  2246. //
  2247. IT_ASSERT(AcquiredLock);
  2248. IT_ASSERT(ITSP_SHOULD_STOP_IDLE_DETECTION(GlobalContext));
  2249. cleanup:
  2250. //
  2251. // Simply cleanup and exit if this is a misfired callback.
  2252. //
  2253. if (!MisfiredCallback) {
  2254. //
  2255. // We'll have to check status to see if we have to requeue
  2256. // ourselves. Make sure we have the lock.
  2257. //
  2258. if (AcquiredLock == FALSE) {
  2259. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  2260. AcquiredLock = TRUE;
  2261. }
  2262. //
  2263. // If we marked an idle task running, make sure we reset its state
  2264. // back to queued.
  2265. //
  2266. if (MarkedIdleTaskRunning) {
  2267. //
  2268. // We may have gone to cleanup after the idle task we were
  2269. // running was removed, but before we realized it. See if
  2270. // the running idle task was removed. We are not waiting,
  2271. // we are just checking if the event has been signaled.
  2272. //
  2273. WaitResult = WaitForSingleObject(GlobalContext->RemovedRunningIdleTask,
  2274. 0);
  2275. if (WaitResult != WAIT_OBJECT_0) {
  2276. //
  2277. // The running idle was not removed. Reset its state.
  2278. //
  2279. IdleTask = ItSpFindRunningIdleTask(GlobalContext);
  2280. //
  2281. // To be safe, we try to cleanup even if the above
  2282. // check fails with another result. We don't want the
  2283. // assert to fire then, but only if the event is
  2284. // really not set.
  2285. //
  2286. if (WaitResult == WAIT_TIMEOUT) {
  2287. IT_ASSERT(IdleTask);
  2288. }
  2289. if (IdleTask) {
  2290. ResetEvent(IdleTask->StartEvent);
  2291. SetEvent(IdleTask->StopEvent);
  2292. IdleTask->Status = ItIdleTaskQueued;
  2293. //
  2294. // Put this task to the end of the list. If a single task
  2295. // is taking too long to run, this gives more chance to other
  2296. // tasks.
  2297. //
  2298. RemoveEntryList(&IdleTask->IdleTaskLink);
  2299. InsertTailList(&GlobalContext->IdleTasksList, &IdleTask->IdleTaskLink);
  2300. }
  2301. }
  2302. }
  2303. //
  2304. // If we set the status to running idle tasks, revert it to
  2305. // detecting idle.
  2306. //
  2307. if (GlobalContext->Status == ItSrvStatusRunningIdleTasks) {
  2308. ItSpUpdateStatus(GlobalContext, ItSrvStatusDetectingIdle);
  2309. }
  2310. //
  2311. // Queue ourselves to fire up after another idle detection
  2312. // period. We'll try every once a while until we get it or we
  2313. // are ordered to stop.
  2314. //
  2315. while (!ITSP_SHOULD_STOP_IDLE_DETECTION(GlobalContext)) {
  2316. IT_ASSERT(GlobalContext->IdleDetectionTimerHandle);
  2317. DuePeriod = GlobalContext->Parameters.IdleDetectionPeriod;
  2318. //
  2319. // Try to detect idle quicker for the case when the last user
  2320. // input was just seconds after the last snapshot. In that case
  2321. // instead of waiting for another full "DetectionPeriod", we'll
  2322. // wait up to "DetectionPeriod" after the last user input. Note
  2323. // that we'll attempt this optimization only if the reason we
  2324. // say the system is not idle is recent user input. E.g. We don't
  2325. // want to poll more often if we are on battery and that's why
  2326. // we say that the system is not idle.
  2327. //
  2328. if (NotIdleBecauseOfUserInput &&
  2329. (ERROR_SUCCESS == ItSpGetLastInputInfo(&LastUserInput))) {
  2330. ULONG DuePeriod2;
  2331. ULONG TimeSinceLastInput;
  2332. //
  2333. // Calculate how long it's been since last user input.
  2334. //
  2335. TimeSinceLastInput = GetTickCount() - LastUserInput.dwTime;
  2336. //
  2337. // Subtract this time from the idle detection period to account
  2338. // for time that has already past since last input.
  2339. //
  2340. DuePeriod2 = 0;
  2341. if (TimeSinceLastInput < GlobalContext->Parameters.IdleDetectionPeriod) {
  2342. DuePeriod2 = GlobalContext->Parameters.IdleDetectionPeriod - TimeSinceLastInput;
  2343. }
  2344. //
  2345. // The last user input we check gets updated only every so
  2346. // often (e.g. every minute). Add a fudge factor for this and to
  2347. // protect us from scheduling the next idle check too soon.
  2348. //
  2349. #ifdef IT_DBG
  2350. if (ItSrvGlobalContext->Parameters.IdleDetectionPeriod >= 60*1000) {
  2351. #endif // IT_DBG
  2352. DuePeriod2 += 65 * 1000;
  2353. #ifdef IT_DBG
  2354. }
  2355. #endif // IT_DBG
  2356. if (DuePeriod > DuePeriod2) {
  2357. DuePeriod = DuePeriod2;
  2358. }
  2359. }
  2360. //
  2361. // If we are forcing all tasks to be processed, requeue ourself to
  2362. // run again in a short time.
  2363. //
  2364. if (GlobalContext->IdleDetectionOverride & ItSrvOverrideLongRequeueTime) {
  2365. DuePeriod = 50;
  2366. }
  2367. if (ChangeTimerQueueTimer(NULL,
  2368. GlobalContext->IdleDetectionTimerHandle,
  2369. DuePeriod,
  2370. IT_VERYLONG_TIMER_PERIOD)) {
  2371. DBGPR((ITID,ITTRC,"IDLE: SrvIdleDetectionCallback-Requeued: DuePeriod=%d\n", DuePeriod));
  2372. break;
  2373. }
  2374. //
  2375. // Release the lock.
  2376. //
  2377. IT_ASSERT(AcquiredLock);
  2378. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  2379. AcquiredLock = FALSE;
  2380. //
  2381. // Sleep for sometime and try again.
  2382. //
  2383. WaitResult = WaitForSingleObject(GlobalContext->StopIdleDetection,
  2384. GlobalContext->Parameters.IdleDetectionPeriod);
  2385. //
  2386. // Get the lock again.
  2387. //
  2388. IT_ACQUIRE_LOCK(GlobalContext->GlobalLock);
  2389. AcquiredLock = TRUE;
  2390. //
  2391. // Now check the result of the wait.
  2392. //
  2393. if (WaitResult != WAIT_OBJECT_0 &&
  2394. WaitResult != WAIT_TIMEOUT) {
  2395. //
  2396. // This is an error too! The world is going down on us,
  2397. // let us get carried away... This will make it easier for
  2398. // the server to shutdown (i.e. no callback running).
  2399. //
  2400. break;
  2401. }
  2402. }
  2403. IT_ASSERT(AcquiredLock);
  2404. //
  2405. // Check if we were ordered to stop.
  2406. //
  2407. OrderedToStop = ITSP_SHOULD_STOP_IDLE_DETECTION(GlobalContext);
  2408. //
  2409. // Mark us not running anymore.
  2410. //
  2411. GlobalContext->IsIdleDetectionCallbackRunning = FALSE;
  2412. }
  2413. //
  2414. // Release the lock if we are holding it.
  2415. //
  2416. if (AcquiredLock) {
  2417. IT_RELEASE_LOCK(GlobalContext->GlobalLock);
  2418. }
  2419. //
  2420. // Cleanup intermediate snapshot structure if necessary.
  2421. //
  2422. ItSpCleanupSystemSnapshot(&CurrentSystemSnapshot);
  2423. DBGPR((ITID,ITSRVD,"IDLE: SrvIdleDetectionCallback(%p)=%d,%d,%d,%d\n",
  2424. GlobalContext,ErrorCode,OrderedToStop,SystemIsIdle,NumTasksRun));
  2425. return;
  2426. }
  2427. VOID
  2428. ItSpInitializeSystemSnapshot (
  2429. PITSRV_SYSTEM_SNAPSHOT SystemSnapshot
  2430. )
  2431. /*++
  2432. Routine Description:
  2433. This routine initializes a system snapshot structure.
  2434. Arguments:
  2435. SystemSnapshot - Pointer to structure.
  2436. Return Value:
  2437. None.
  2438. --*/
  2439. {
  2440. //
  2441. // Initialize the disk performance data array.
  2442. //
  2443. SystemSnapshot->NumPhysicalDisks = 0;
  2444. SystemSnapshot->DiskPerfData = NULL;
  2445. //
  2446. // We don't have any valid data.
  2447. //
  2448. SystemSnapshot->GotLastInputInfo = 0;
  2449. SystemSnapshot->GotSystemPerformanceInfo = 0;
  2450. SystemSnapshot->GotDiskPerformanceInfo = 0;
  2451. SystemSnapshot->GotSystemPowerStatus = 0;
  2452. SystemSnapshot->GotSystemExecutionState = 0;
  2453. SystemSnapshot->GotDisplayPowerStatus = 0;
  2454. SystemSnapshot->SnapshotTime = -1;
  2455. }
  2456. VOID
  2457. ItSpCleanupSystemSnapshot (
  2458. PITSRV_SYSTEM_SNAPSHOT SystemSnapshot
  2459. )
  2460. /*++
  2461. Routine Description:
  2462. This routine cleans up fields of a system snapshot structure. It
  2463. does not free the structure itself. The structure should have been
  2464. initialized with a call to ItSpCleanupSystemSnapshot.
  2465. Arguments:
  2466. SystemSnapshot - Pointer to structure.
  2467. Return Value:
  2468. None.
  2469. --*/
  2470. {
  2471. //
  2472. // If a disk performance data array is allocated free it.
  2473. //
  2474. if (SystemSnapshot->DiskPerfData) {
  2475. IT_ASSERT(SystemSnapshot->NumPhysicalDisks);
  2476. IT_FREE(SystemSnapshot->DiskPerfData);
  2477. }
  2478. }
  2479. DWORD
  2480. ItSpCopySystemSnapshot (
  2481. PITSRV_SYSTEM_SNAPSHOT DestSnapshot,
  2482. PITSRV_SYSTEM_SNAPSHOT SourceSnapshot
  2483. )
  2484. /*++
  2485. Routine Description:
  2486. This routine attempts to copy SourceSnapshot to DestSnapshot. If
  2487. the copy fails, DestSnapshot remains intact.
  2488. Arguments:
  2489. DestSnapshot - Pointer to snapshot to be updated.
  2490. SourceSnapshot - Pointer to snapshot to copy.
  2491. Return Value:
  2492. Win32 error code.
  2493. --*/
  2494. {
  2495. DWORD ErrorCode;
  2496. ULONG OrgNumDisks;
  2497. PITSRV_DISK_PERFORMANCE_DATA OrgDiskPerfData;
  2498. PITSRV_DISK_PERFORMANCE_DATA NewDiskPerfData;
  2499. ULONG AllocationSize;
  2500. ULONG CopySize;
  2501. //
  2502. // Initialize locals.
  2503. //
  2504. NewDiskPerfData = NULL;
  2505. //
  2506. // Do we have to copy disk performance data?
  2507. //
  2508. if (SourceSnapshot->GotDiskPerformanceInfo) {
  2509. //
  2510. // Allocate a new array if the disk performance data won't fit.
  2511. //
  2512. if (SourceSnapshot->NumPhysicalDisks > DestSnapshot->NumPhysicalDisks) {
  2513. AllocationSize = SourceSnapshot->NumPhysicalDisks *
  2514. sizeof(ITSRV_DISK_PERFORMANCE_DATA);
  2515. NewDiskPerfData = IT_ALLOC(AllocationSize);
  2516. if (!NewDiskPerfData) {
  2517. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  2518. goto cleanup;
  2519. }
  2520. }
  2521. }
  2522. //
  2523. // Beyond this point we should not fail because we modify
  2524. // DestSnapshot.
  2525. //
  2526. //
  2527. // Save original disk performance data array.
  2528. //
  2529. OrgDiskPerfData = DestSnapshot->DiskPerfData;
  2530. OrgNumDisks = DestSnapshot->NumPhysicalDisks;
  2531. //
  2532. // Copy the whole structure over and put back original disk
  2533. // performance data array.
  2534. //
  2535. RtlCopyMemory(DestSnapshot,
  2536. SourceSnapshot,
  2537. sizeof(ITSRV_SYSTEM_SNAPSHOT));
  2538. DestSnapshot->DiskPerfData = OrgDiskPerfData;
  2539. DestSnapshot->NumPhysicalDisks = OrgNumDisks;
  2540. //
  2541. // Determine if/how we will copy over disk performance data.
  2542. //
  2543. if (SourceSnapshot->GotDiskPerformanceInfo) {
  2544. if (SourceSnapshot->NumPhysicalDisks > DestSnapshot->NumPhysicalDisks) {
  2545. //
  2546. // Free old array and use the new one we allocated above.
  2547. //
  2548. if (DestSnapshot->DiskPerfData) {
  2549. IT_FREE(DestSnapshot->DiskPerfData);
  2550. }
  2551. DestSnapshot->DiskPerfData = NewDiskPerfData;
  2552. }
  2553. if (SourceSnapshot->NumPhysicalDisks == 0) {
  2554. //
  2555. // This does not make sense... (They got disk data and
  2556. // there are 0 physical disks in the system?) Yet we go
  2557. // with it and to be consistent, free our data array.
  2558. //
  2559. if (DestSnapshot->DiskPerfData) {
  2560. IT_FREE(DestSnapshot->DiskPerfData);
  2561. }
  2562. DestSnapshot->DiskPerfData = NULL;
  2563. }
  2564. //
  2565. // Copy over their disk data and update NumPhysicalDisks.
  2566. //
  2567. CopySize = SourceSnapshot->NumPhysicalDisks *
  2568. sizeof(ITSRV_DISK_PERFORMANCE_DATA);
  2569. RtlCopyMemory(DestSnapshot->DiskPerfData,
  2570. SourceSnapshot->DiskPerfData,
  2571. CopySize);
  2572. DestSnapshot->NumPhysicalDisks = SourceSnapshot->NumPhysicalDisks;
  2573. }
  2574. //
  2575. // Done.
  2576. //
  2577. ErrorCode = ERROR_SUCCESS;
  2578. cleanup:
  2579. if (ErrorCode != ERROR_SUCCESS) {
  2580. if (NewDiskPerfData) {
  2581. IT_FREE(NewDiskPerfData);
  2582. }
  2583. }
  2584. DBGPR((ITID,ITSRVDD,"IDLE: SrvCopySnapshot()=%d\n",ErrorCode));
  2585. return ErrorCode;
  2586. }
  2587. DWORD
  2588. ItSpGetSystemSnapshot (
  2589. PITSRV_GLOBAL_CONTEXT GlobalContext,
  2590. PITSRV_SYSTEM_SNAPSHOT SystemSnapshot
  2591. )
  2592. /*++
  2593. Routine Description:
  2594. This routine fills input system snapshot with data queried from
  2595. various sources. The snapshot structure should have been
  2596. initialized by ItSpInitializeSystemSnapshot. The output
  2597. SystemSnapshot can be passed back in.
  2598. Arguments:
  2599. GlobalContext - Pointer to idle detection context.
  2600. SystemSnapshot - Pointer to structure to fill.
  2601. Return Value:
  2602. Win32 error code.
  2603. --*/
  2604. {
  2605. DWORD ErrorCode;
  2606. NTSTATUS Status;
  2607. //
  2608. // Query disk performance counters.
  2609. //
  2610. if (GlobalContext->DiskPerfWmiHandle) {
  2611. ErrorCode = ItSpGetWmiDiskPerformanceData(GlobalContext->DiskPerfWmiHandle,
  2612. &SystemSnapshot->DiskPerfData,
  2613. &SystemSnapshot->NumPhysicalDisks,
  2614. &GlobalContext->WmiQueryBuffer,
  2615. &GlobalContext->WmiQueryBufferSize);
  2616. if (ErrorCode == ERROR_SUCCESS) {
  2617. SystemSnapshot->GotDiskPerformanceInfo = TRUE;
  2618. } else {
  2619. SystemSnapshot->GotDiskPerformanceInfo = FALSE;
  2620. }
  2621. } else {
  2622. SystemSnapshot->GotDiskPerformanceInfo = FALSE;
  2623. }
  2624. //
  2625. // Get system performance information.
  2626. //
  2627. Status = NtQuerySystemInformation(SystemPerformanceInformation,
  2628. &SystemSnapshot->SystemPerformanceInfo,
  2629. sizeof(SYSTEM_PERFORMANCE_INFORMATION),
  2630. NULL);
  2631. if (NT_SUCCESS(Status)) {
  2632. SystemSnapshot->GotSystemPerformanceInfo = TRUE;
  2633. } else {
  2634. SystemSnapshot->GotSystemPerformanceInfo = FALSE;
  2635. }
  2636. //
  2637. // Get last input time.
  2638. //
  2639. SystemSnapshot->LastInputInfo.cbSize = sizeof(LASTINPUTINFO);
  2640. ErrorCode = ItSpGetLastInputInfo(&SystemSnapshot->LastInputInfo);
  2641. if (ErrorCode == ERROR_SUCCESS) {
  2642. SystemSnapshot->GotLastInputInfo = TRUE;
  2643. } else {
  2644. SystemSnapshot->GotLastInputInfo = FALSE;
  2645. }
  2646. //
  2647. // Get system power status to determine if we are running on
  2648. // battery etc.
  2649. //
  2650. if (GetSystemPowerStatus(&SystemSnapshot->SystemPowerStatus)) {
  2651. SystemSnapshot->GotSystemPowerStatus = TRUE;
  2652. } else {
  2653. SystemSnapshot->GotSystemPowerStatus = FALSE;
  2654. }
  2655. //
  2656. // Get system power information to see if the system is close to
  2657. // entering standby or hibernate automatically.
  2658. //
  2659. Status = NtPowerInformation(SystemPowerInformation,
  2660. NULL,
  2661. 0,
  2662. &SystemSnapshot->PowerInfo,
  2663. sizeof(SYSTEM_POWER_INFORMATION));
  2664. if (NT_SUCCESS(Status)) {
  2665. SystemSnapshot->GotSystemPowerInfo = TRUE;
  2666. } else {
  2667. SystemSnapshot->GotSystemPowerInfo = FALSE;
  2668. }
  2669. //
  2670. // Get system execution state to determine if somebody's running a
  2671. // presentation, burning a cd etc.
  2672. //
  2673. Status = NtPowerInformation(SystemExecutionState,
  2674. NULL,
  2675. 0,
  2676. &SystemSnapshot->ExecutionState,
  2677. sizeof(EXECUTION_STATE));
  2678. if (NT_SUCCESS(Status)) {
  2679. SystemSnapshot->GotSystemExecutionState = TRUE;
  2680. } else {
  2681. SystemSnapshot->GotSystemExecutionState = FALSE;
  2682. }
  2683. //
  2684. // Get display power status.
  2685. //
  2686. ErrorCode = ItSpGetDisplayPowerStatus(&SystemSnapshot->ScreenSaverIsRunning);
  2687. if (ErrorCode == ERROR_SUCCESS) {
  2688. SystemSnapshot->GotDisplayPowerStatus = TRUE;
  2689. } else {
  2690. SystemSnapshot->GotDisplayPowerStatus = FALSE;
  2691. }
  2692. //
  2693. // Fill in the time when this snapshot was taken as the last thing
  2694. // to be conservative. This function may have taken long, and the
  2695. // values we snapshoted may have changed by now.
  2696. //
  2697. SystemSnapshot->SnapshotTime = GetTickCount();
  2698. DBGPR((ITID,ITSRVDD,"IDLE: SrvGetSnapshot()=%d,%d,%d\n",
  2699. (ULONG) SystemSnapshot->GotLastInputInfo,
  2700. (ULONG) SystemSnapshot->GotSystemPerformanceInfo,
  2701. (ULONG) SystemSnapshot->GotDiskPerformanceInfo));
  2702. return ERROR_SUCCESS;
  2703. }
  2704. BOOLEAN
  2705. ItSpIsSystemIdle (
  2706. PITSRV_GLOBAL_CONTEXT GlobalContext,
  2707. PITSRV_SYSTEM_SNAPSHOT CurrentSnapshot,
  2708. PITSRV_SYSTEM_SNAPSHOT LastSnapshot,
  2709. ITSRV_IDLE_CHECK_REASON IdleCheckReason
  2710. )
  2711. /*++
  2712. Routine Description:
  2713. This routine compares two snapshots to determine if the system has
  2714. been idle between them.
  2715. This function acts very conservatively in saying that the system
  2716. is idle.
  2717. Arguments:
  2718. GlobalContext - Pointer to server context structure.
  2719. CurrentSnapshot - Pointer to system snapshot.
  2720. LastSnapshot - Pointer to system snapshot taken before
  2721. CurrentSnapshot.
  2722. IdleCheckReason - Where this function is being called from. We may
  2723. do things differently when we get called to do the initial check
  2724. to see if the system idle, or to verify it is really idle, or to
  2725. make sure the idle task we started is still running and is not
  2726. stuck.
  2727. Return Value:
  2728. TRUE - System was idle between the two snapshots.
  2729. FALSE - The system was not idle between two snapshots.
  2730. --*/
  2731. {
  2732. DWORD SnapshotTimeDifference;
  2733. BOOLEAN SystemIsIdle;
  2734. LARGE_INTEGER IdleProcessRunTime;
  2735. ULONG CpuIdlePercentage;
  2736. ULONG DiskIdx;
  2737. ULONG DiskIdleTimeDifference;
  2738. ULONG DiskIdlePercentage;
  2739. PIT_IDLE_DETECTION_PARAMETERS Parameters;
  2740. //
  2741. // Initialize locals.
  2742. //
  2743. Parameters = &GlobalContext->Parameters;
  2744. SystemIsIdle = FALSE;
  2745. SnapshotTimeDifference = CurrentSnapshot->SnapshotTime -
  2746. LastSnapshot->SnapshotTime;
  2747. //
  2748. // Verify parameters.
  2749. //
  2750. IT_ASSERT(IdleCheckReason < ItSrvMaxIdleCheckReason);
  2751. //
  2752. // If system tick count wrapped or they are passing in bogus
  2753. // times, or the snapshots seem to be taken nearly at the same
  2754. // time, say the system is not idle to avoid any weird issues.
  2755. //
  2756. if (CurrentSnapshot->SnapshotTime <= LastSnapshot->SnapshotTime) {
  2757. goto cleanup;
  2758. }
  2759. IT_ASSERT(SnapshotTimeDifference);
  2760. //
  2761. // If either snapshot does not have last user input (mouse or
  2762. // keyboard) info, we cannot reliably say the system was idle.
  2763. //
  2764. if (!CurrentSnapshot->GotLastInputInfo ||
  2765. !LastSnapshot->GotLastInputInfo) {
  2766. goto cleanup;
  2767. }
  2768. //
  2769. // If there has been user input between the two snapshots, the
  2770. // system was not idle. We don't care when the user input
  2771. // happened (e.g. right after the last snapshot).
  2772. //
  2773. DBGPR((ITID,ITSNAP,"IDLE: UserInput: Last %u Current %u\n",
  2774. LastSnapshot->LastInputInfo.dwTime,
  2775. CurrentSnapshot->LastInputInfo.dwTime));
  2776. if (LastSnapshot->LastInputInfo.dwTime !=
  2777. CurrentSnapshot->LastInputInfo.dwTime) {
  2778. goto cleanup;
  2779. }
  2780. //
  2781. // If we are running on battery, don't run idle tasks.
  2782. //
  2783. if (CurrentSnapshot->GotSystemPowerStatus) {
  2784. if (CurrentSnapshot->SystemPowerStatus.ACLineStatus == 0) {
  2785. DBGPR((ITID,ITSNAP,"IDLE: Snapshot: Running on battery\n"));
  2786. goto cleanup;
  2787. }
  2788. }
  2789. //
  2790. // If system will automatically enter standby or hibernate soon
  2791. // it is too late for us to run our tasks.
  2792. //
  2793. if (CurrentSnapshot->GotSystemPowerInfo) {
  2794. if (CurrentSnapshot->PowerInfo.TimeRemaining < IT_DEFAULT_MAX_TIME_REMAINING_TO_SLEEP) {
  2795. DBGPR((ITID,ITSNAP,"IDLE: Snapshot: System will standby / hibernate soon\n"));
  2796. goto cleanup;
  2797. }
  2798. }
  2799. //
  2800. // If the screen saver is running, assume the system is
  2801. // idle. Otherwise, if a heavy-weight OpenGL screen saver is
  2802. // running our CPU checks may bail us out of realizing that the
  2803. // system is idle. We skip this check when trying to determine an
  2804. // idle task is stuck or if it is really running. Note that the
  2805. // screen saver activity may make us think the idle task is still
  2806. // running, even if it is stuck.
  2807. //
  2808. if (IdleCheckReason != ItSrvIdleTaskRunningCheck) {
  2809. if (CurrentSnapshot->GotDisplayPowerStatus) {
  2810. if (CurrentSnapshot->ScreenSaverIsRunning) {
  2811. DBGPR((ITID,ITSNAP,"IDLE: Snapshot: ScreenSaverRunning\n"));
  2812. SystemIsIdle = TRUE;
  2813. goto cleanup;
  2814. }
  2815. }
  2816. }
  2817. //
  2818. // If system may look idle but somebody's running a powerpoint
  2819. // presentation, watching hardware-decoded DVD etc don't run idle
  2820. // tasks. Note that we do not check for ES_SYSTEM_REQUIRED, since
  2821. // it is set by fax servers, answering machines and such as well.
  2822. // ES_DISPLAY_REQUIRED is the one supposed to be used by
  2823. // multimedia/presentation applications.
  2824. //
  2825. if (CurrentSnapshot->GotSystemExecutionState) {
  2826. if ((CurrentSnapshot->ExecutionState & ES_DISPLAY_REQUIRED)) {
  2827. DBGPR((ITID,ITSNAP,"IDLE: Snapshot: Execution state:%x\n",CurrentSnapshot->ExecutionState));
  2828. goto cleanup;
  2829. }
  2830. }
  2831. //
  2832. // We definitely want CPU & general system performance data as
  2833. // well.
  2834. //
  2835. if (!CurrentSnapshot->GotSystemPerformanceInfo ||
  2836. !LastSnapshot->GotSystemPerformanceInfo) {
  2837. goto cleanup;
  2838. }
  2839. //
  2840. // Calculate how many ms the idle process ran. The IdleProcessTime
  2841. // on system performance information structures is in 100ns. To
  2842. // convert it to ms we divide by (10 * 1000).
  2843. //
  2844. IdleProcessRunTime.QuadPart =
  2845. (CurrentSnapshot->SystemPerformanceInfo.IdleProcessTime.QuadPart -
  2846. LastSnapshot->SystemPerformanceInfo.IdleProcessTime.QuadPart);
  2847. IdleProcessRunTime.QuadPart = IdleProcessRunTime.QuadPart / 10000;
  2848. //
  2849. // Adjust it for the number of CPUs in the system.
  2850. //
  2851. IT_ASSERT(GlobalContext->NumProcessors);
  2852. if (GlobalContext->NumProcessors) {
  2853. IdleProcessRunTime.QuadPart = IdleProcessRunTime.QuadPart / GlobalContext->NumProcessors;
  2854. }
  2855. //
  2856. // Calculate idle cpu percentage this translates to.
  2857. //
  2858. CpuIdlePercentage = (ULONG) (IdleProcessRunTime.QuadPart * 100 / SnapshotTimeDifference);
  2859. DBGPR((ITID,ITSNAP,"IDLE: Snapshot: CPU %d\n", CpuIdlePercentage));
  2860. if (CpuIdlePercentage < Parameters->MinCpuIdlePercentage) {
  2861. goto cleanup;
  2862. }
  2863. //
  2864. // We may not have disk performance data because WMI thingies were
  2865. // not initiated etc.
  2866. //
  2867. if (CurrentSnapshot->GotDiskPerformanceInfo &&
  2868. LastSnapshot->GotDiskPerformanceInfo) {
  2869. //
  2870. // If a new disk was added / removed since last snapshot, say
  2871. // the system is not idle.
  2872. //
  2873. if (CurrentSnapshot->NumPhysicalDisks !=
  2874. LastSnapshot->NumPhysicalDisks) {
  2875. goto cleanup;
  2876. }
  2877. //
  2878. // We assume that the disk data is in the same order in both
  2879. // snapshots. If the ordering of disks changed etc, it will
  2880. // most likely cause us to say the system is not idle. It may
  2881. // cause us to ignore some real activity with very low
  2882. // possibility. That is why we verify several times when we
  2883. // see system idle.
  2884. //
  2885. for (DiskIdx = 0;
  2886. DiskIdx < CurrentSnapshot->NumPhysicalDisks;
  2887. DiskIdx++) {
  2888. DiskIdleTimeDifference =
  2889. CurrentSnapshot->DiskPerfData[DiskIdx].DiskIdleTime -
  2890. LastSnapshot->DiskPerfData[DiskIdx].DiskIdleTime;
  2891. DiskIdlePercentage = (DiskIdleTimeDifference * 100) /
  2892. SnapshotTimeDifference;
  2893. DBGPR((ITID,ITSNAP,"IDLE: Snapshot: Disk %d:%d\n",
  2894. DiskIdx, DiskIdlePercentage));
  2895. if (DiskIdlePercentage < Parameters->MinDiskIdlePercentage) {
  2896. goto cleanup;
  2897. }
  2898. }
  2899. }
  2900. //
  2901. // We passed all the checks.
  2902. //
  2903. SystemIsIdle = TRUE;
  2904. cleanup:
  2905. DBGPR((ITID,ITSRVDD,"IDLE: SrvIsSystemIdle()=%d\n",(ULONG)SystemIsIdle));
  2906. return SystemIsIdle;
  2907. }
  2908. DWORD
  2909. ItSpGetLastInputInfo (
  2910. PLASTINPUTINFO LastInputInfo
  2911. )
  2912. /*++
  2913. Routine Description:
  2914. This function retrieves the time of the last user input event.
  2915. Arguments:
  2916. LastInputInfo - Pointer to structure to update.
  2917. Return Value:
  2918. Win32 error code.
  2919. --*/
  2920. {
  2921. DWORD ErrorCode;
  2922. //
  2923. // Verify parameter.
  2924. //
  2925. if (LastInputInfo->cbSize != sizeof(LASTINPUTINFO)) {
  2926. ErrorCode = ERROR_BAD_FORMAT;
  2927. goto cleanup;
  2928. }
  2929. //
  2930. // We get the last input info from the shared system page that is
  2931. // updated by all terminal server sessions.
  2932. //
  2933. LastInputInfo->dwTime = USER_SHARED_DATA->LastSystemRITEventTickCount;
  2934. #ifdef IT_DBG
  2935. //
  2936. // On the checked build, if we are stressing, we will set the detection
  2937. // period below the period with which the system last input time is
  2938. // updated. If it is so, use the Win32 GetLastInputInfo call. This call
  2939. // will get the user input info only for the current session, but when
  2940. // stressing this is OK.
  2941. //
  2942. if (ItSrvGlobalContext->Parameters.IdleDetectionPeriod < 60*1000) {
  2943. if (!GetLastInputInfo(LastInputInfo)) {
  2944. ErrorCode = GetLastError();
  2945. goto cleanup;
  2946. }
  2947. }
  2948. #endif // IT_DBG
  2949. ErrorCode = ERROR_SUCCESS;
  2950. cleanup:
  2951. return ErrorCode;
  2952. }
  2953. BOOLEAN
  2954. ItSpIsPhysicalDrive (
  2955. PDISK_PERFORMANCE DiskPerformanceData
  2956. )
  2957. /*++
  2958. Routine Description:
  2959. This function attempts to determine if the specified disk is a
  2960. logical or physical disk.
  2961. Arguments:
  2962. DiskPerformanceData - Pointer to disk performance data for the disk.
  2963. Return Value:
  2964. TRUE - The disk is a physical disk.
  2965. FALSE - The disk is not a physical disk.
  2966. --*/
  2967. {
  2968. ULONG ComparisonLength;
  2969. //
  2970. // Initialize locals.
  2971. //
  2972. ComparisonLength =
  2973. sizeof(DiskPerformanceData->StorageManagerName) / sizeof(WCHAR);
  2974. //
  2975. // We have to determine if this is a physical disk or not from the
  2976. // storage manager's name.
  2977. //
  2978. if (!wcsncmp(DiskPerformanceData->StorageManagerName,
  2979. L"Partmgr ",
  2980. ComparisonLength)) {
  2981. return TRUE;
  2982. }
  2983. if (!wcsncmp(DiskPerformanceData->StorageManagerName,
  2984. L"PhysDisk",
  2985. ComparisonLength)) {
  2986. return TRUE;
  2987. }
  2988. return FALSE;
  2989. }
  2990. DWORD
  2991. ItSpGetWmiDiskPerformanceData(
  2992. IN WMIHANDLE DiskPerfWmiHandle,
  2993. IN OUT PITSRV_DISK_PERFORMANCE_DATA *DiskPerfData,
  2994. IN OUT ULONG *NumPhysicalDisks,
  2995. OPTIONAL IN OUT PVOID *InputQueryBuffer,
  2996. OPTIONAL IN OUT ULONG *InputQueryBufferSize
  2997. )
  2998. /*++
  2999. Routine Description:
  3000. This function queries disk performance counters and updates input
  3001. parameters.
  3002. Arguments:
  3003. DiskPerfWmiHandle - WMI handle to DiskPerf.
  3004. DiskPerfData - This array is updated with data from all registered
  3005. physical disks' WMI performance data blocks. If it is not big
  3006. enough, it is freed and reallocated using IT_FREE/IT_ALLOC.
  3007. NumPhysicalDisks - This is the size of DiskPerfData array on
  3008. input. If the number of registered physical disks change, it is
  3009. updated on return.
  3010. InputQueryBuffer, InputQueryBufferSize - If specified, they describe a
  3011. query buffer to be used and updated when querying WMI. The
  3012. buffer must be allocated with IT_ALLOC. The returned buffer may
  3013. be relocated/resized and should be freed with IT_FREE. The
  3014. buffer's contents on input and output are trash.
  3015. Return Value:
  3016. Win32 error code.
  3017. --*/
  3018. {
  3019. DWORD ErrorCode;
  3020. PVOID QueryBuffer;
  3021. ULONG QueryBufferSize;
  3022. ULONG RequiredSize;
  3023. ULONG NumTries;
  3024. PWNODE_ALL_DATA DiskWmiDataCursor;
  3025. PDISK_PERFORMANCE DiskPerformanceData;
  3026. LARGE_INTEGER PerformanceCounterFrequency;
  3027. BOOLEAN UsingInputBuffer;
  3028. ULONG NumDiskData;
  3029. PITSRV_DISK_PERFORMANCE_DATA NewDataBuffer;
  3030. //
  3031. // Initialize locals.
  3032. //
  3033. QueryBuffer = NULL;
  3034. QueryBufferSize = 0;
  3035. UsingInputBuffer = FALSE;
  3036. NewDataBuffer = NULL;
  3037. //
  3038. // Determine if we will be using the query buffer input by the
  3039. // user. In case we are using them it is crucial that QueryBuffer
  3040. // and QueryBufferSize are not set to bogus values during the
  3041. // function.
  3042. //
  3043. if (InputQueryBuffer && InputQueryBufferSize) {
  3044. UsingInputBuffer = TRUE;
  3045. QueryBuffer = *InputQueryBuffer;
  3046. QueryBufferSize = *InputQueryBufferSize;
  3047. }
  3048. //
  3049. // Query disk performance data.
  3050. //
  3051. NumTries = 0;
  3052. do {
  3053. RequiredSize = QueryBufferSize;
  3054. __try {
  3055. ErrorCode = WmiQueryAllData(DiskPerfWmiHandle,
  3056. &RequiredSize,
  3057. QueryBuffer);
  3058. } __except (EXCEPTION_EXECUTE_HANDLER) {
  3059. //
  3060. // There is something wrong if we got an exception.
  3061. //
  3062. ErrorCode = GetExceptionCode();
  3063. if (ErrorCode == ERROR_SUCCESS) {
  3064. ErrorCode = ERROR_INVALID_FUNCTION;
  3065. }
  3066. goto cleanup;
  3067. }
  3068. if (ErrorCode == ERROR_SUCCESS) {
  3069. //
  3070. // We got the data.
  3071. //
  3072. break;
  3073. }
  3074. //
  3075. // Check to see if we failed for a real reason other than that
  3076. // our input buffer was too small.
  3077. //
  3078. if (ErrorCode != ERROR_INSUFFICIENT_BUFFER) {
  3079. goto cleanup;
  3080. }
  3081. //
  3082. // Reallocate the buffer to the required size.
  3083. //
  3084. if (QueryBufferSize) {
  3085. IT_FREE(QueryBuffer);
  3086. QueryBufferSize = 0;
  3087. }
  3088. QueryBuffer = IT_ALLOC(RequiredSize);
  3089. if (!QueryBuffer) {
  3090. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  3091. goto cleanup;
  3092. }
  3093. QueryBufferSize = RequiredSize;
  3094. //
  3095. // Don't loop forever...
  3096. //
  3097. NumTries++;
  3098. if (NumTries >= 1000) {
  3099. ErrorCode = ERROR_INVALID_FUNCTION;
  3100. goto cleanup;
  3101. }
  3102. } while (TRUE);
  3103. //
  3104. // We have gotten WMI disk performance data. Verify it makes sense.
  3105. //
  3106. DiskWmiDataCursor = QueryBuffer;
  3107. if (DiskWmiDataCursor->WnodeHeader.BufferSize < sizeof(WNODE_ALL_DATA)) {
  3108. IT_ASSERT(FALSE);
  3109. ErrorCode = ERROR_BAD_FORMAT;
  3110. goto cleanup;
  3111. }
  3112. if (DiskWmiDataCursor->InstanceCount == 0) {
  3113. //
  3114. // There are no disks?
  3115. //
  3116. ErrorCode = ERROR_BAD_FORMAT;
  3117. goto cleanup;
  3118. }
  3119. //
  3120. // Determine the number of disks we have data for.
  3121. //
  3122. NumDiskData = 0;
  3123. do {
  3124. DiskPerformanceData = (PDISK_PERFORMANCE)
  3125. ((PUCHAR) DiskWmiDataCursor + DiskWmiDataCursor->DataBlockOffset);
  3126. //
  3127. // Count only physical disk data. Otherwise we will double
  3128. // count disk I/Os for logical disks on the physical disk.
  3129. //
  3130. if (ItSpIsPhysicalDrive(DiskPerformanceData)) {
  3131. NumDiskData++;
  3132. }
  3133. if (DiskWmiDataCursor->WnodeHeader.Linkage == 0) {
  3134. break;
  3135. }
  3136. DiskWmiDataCursor = (PWNODE_ALL_DATA)
  3137. ((LPBYTE)DiskWmiDataCursor + DiskWmiDataCursor->WnodeHeader.Linkage);
  3138. } while (TRUE);
  3139. //
  3140. // Do we have enough space in the input buffer?
  3141. //
  3142. if (NumDiskData > *NumPhysicalDisks) {
  3143. NewDataBuffer = IT_ALLOC(NumDiskData *
  3144. sizeof(ITSRV_DISK_PERFORMANCE_DATA));
  3145. if (!NewDataBuffer) {
  3146. ErrorCode = ERROR_NOT_ENOUGH_MEMORY;
  3147. goto cleanup;
  3148. }
  3149. //
  3150. // Update old buffer & its max size.
  3151. //
  3152. if (*DiskPerfData) {
  3153. IT_FREE(*DiskPerfData);
  3154. }
  3155. *DiskPerfData = NewDataBuffer;
  3156. *NumPhysicalDisks = NumDiskData;
  3157. }
  3158. //
  3159. // Reset cursor and walk through the WMI data copying into the
  3160. // target buffer.
  3161. //
  3162. DiskWmiDataCursor = QueryBuffer;
  3163. *NumPhysicalDisks = 0;
  3164. do {
  3165. DiskPerformanceData = (PDISK_PERFORMANCE)
  3166. ((PUCHAR) DiskWmiDataCursor + DiskWmiDataCursor->DataBlockOffset);
  3167. //
  3168. // Count only physical disk data. Otherwise we will double
  3169. // count disk I/Os for logical disks on the physical disk.
  3170. //
  3171. if (ItSpIsPhysicalDrive(DiskPerformanceData)) {
  3172. if (*NumPhysicalDisks >= NumDiskData) {
  3173. //
  3174. // We calculated this above. Did the data change
  3175. // beneath our feet?
  3176. //
  3177. IT_ASSERT(FALSE);
  3178. ErrorCode = ERROR_INVALID_FUNCTION;
  3179. goto cleanup;
  3180. }
  3181. //
  3182. // Convert idle time in 100ns to ms.
  3183. //
  3184. (*DiskPerfData)[*NumPhysicalDisks].DiskIdleTime =
  3185. (ULONG) (DiskPerformanceData->IdleTime.QuadPart / 10000);
  3186. (*NumPhysicalDisks)++;
  3187. }
  3188. if (DiskWmiDataCursor->WnodeHeader.Linkage == 0) {
  3189. break;
  3190. }
  3191. DiskWmiDataCursor = (PWNODE_ALL_DATA)
  3192. ((LPBYTE)DiskWmiDataCursor + DiskWmiDataCursor->WnodeHeader.Linkage);
  3193. } while (TRUE);
  3194. IT_ASSERT(*NumPhysicalDisks == NumDiskData);
  3195. ErrorCode = ERROR_SUCCESS;
  3196. cleanup:
  3197. if (UsingInputBuffer) {
  3198. //
  3199. // Update the callers query buffer info.
  3200. //
  3201. *InputQueryBuffer = QueryBuffer;
  3202. *InputQueryBufferSize = QueryBufferSize;
  3203. } else {
  3204. //
  3205. // Free temporary buffer.
  3206. //
  3207. if (QueryBuffer) {
  3208. IT_FREE(QueryBuffer);
  3209. }
  3210. }
  3211. if (ErrorCode != ERROR_SUCCESS) {
  3212. if (NewDataBuffer) {
  3213. IT_FREE(NewDataBuffer);
  3214. }
  3215. }
  3216. DBGPR((ITID,ITSRVDD,"IDLE: SrvGetDiskData()=%d\n",ErrorCode));
  3217. return ErrorCode;
  3218. }
  3219. DWORD
  3220. ItSpGetDisplayPowerStatus(
  3221. PBOOL ScreenSaverIsRunning
  3222. )
  3223. /*++
  3224. Routine Description:
  3225. This routine determines power status of the default display.
  3226. Arguments:
  3227. ScreenSaverIsRunning - Whether a screen saver is running is
  3228. returned here.
  3229. Return Value:
  3230. Win32 error code.
  3231. --*/
  3232. {
  3233. DWORD ErrorCode;
  3234. //
  3235. // Determine whether the screen saver is running.
  3236. //
  3237. if (!SystemParametersInfo(SPI_GETSCREENSAVERRUNNING,
  3238. 0,
  3239. ScreenSaverIsRunning,
  3240. 0)) {
  3241. ErrorCode = GetLastError();
  3242. goto cleanup;
  3243. }
  3244. ErrorCode = ERROR_SUCCESS;
  3245. cleanup:
  3246. return ErrorCode;
  3247. }
  3248. //
  3249. // Reference count implementation:
  3250. //
  3251. VOID
  3252. ItSpInitializeRefCount(
  3253. PITSRV_REFCOUNT RefCount
  3254. )
  3255. /*++
  3256. Routine Description:
  3257. This routine initializes a reference count structure.
  3258. Arguments:
  3259. RefCount - Pointer to reference count structure.
  3260. Return Value:
  3261. None.
  3262. --*/
  3263. {
  3264. //
  3265. // Start reference count from 1. When somebody wants to gain
  3266. // exclusive access they decrement it one extra so it may become
  3267. // 0.
  3268. //
  3269. RefCount->RefCount = 1;
  3270. //
  3271. // Nobody has exclusive access to start with.
  3272. //
  3273. RefCount->Exclusive = 0;
  3274. RefCount->ExclusiveOwner = 0;
  3275. }
  3276. DWORD
  3277. FASTCALL
  3278. ItSpAddRef(
  3279. PITSRV_REFCOUNT RefCount
  3280. )
  3281. /*++
  3282. Routine Description:
  3283. This routine tries to bump the reference count if it has not been
  3284. acquired exclusive.
  3285. Arguments:
  3286. RefCount - Pointer to reference count structure.
  3287. Return Value:
  3288. Status.
  3289. --*/
  3290. {
  3291. //
  3292. // Do a fast check if the lock was acquire exclusive. If so just
  3293. // return.
  3294. //
  3295. if (RefCount->Exclusive) {
  3296. return ERROR_ACCESS_DENIED;
  3297. }
  3298. //
  3299. // Bump the reference count.
  3300. //
  3301. InterlockedIncrement(&RefCount->RefCount);
  3302. //
  3303. // If it was acquired exclusive, pull back.
  3304. //
  3305. if (RefCount->Exclusive) {
  3306. InterlockedDecrement(&RefCount->RefCount);
  3307. //
  3308. // Reference count should never go negative.
  3309. //
  3310. IT_ASSERT(RefCount->RefCount >= 0);
  3311. return ERROR_ACCESS_DENIED;
  3312. } else {
  3313. //
  3314. // We got our reference.
  3315. //
  3316. return ERROR_SUCCESS;
  3317. }
  3318. }
  3319. VOID
  3320. FASTCALL
  3321. ItSpDecRef(
  3322. PITSRV_REFCOUNT RefCount
  3323. )
  3324. /*++
  3325. Routine Description:
  3326. This routine decrements the reference count.
  3327. Arguments:
  3328. RefCount - Pointer to reference count structure.
  3329. Return Value:
  3330. None.
  3331. --*/
  3332. {
  3333. //
  3334. // Decrement the reference count.
  3335. //
  3336. InterlockedDecrement(&RefCount->RefCount);
  3337. //
  3338. // Reference count should never go negative.
  3339. //
  3340. IT_ASSERT(RefCount->RefCount >= 0);
  3341. }
  3342. DWORD
  3343. ItSpAcquireExclusiveRef(
  3344. PITSRV_REFCOUNT RefCount
  3345. )
  3346. /*++
  3347. Routine Description:
  3348. This routine attempts to get exclusive reference. If there is
  3349. already an exclusive reference, it fails. Othwerwise it waits for
  3350. all normal references to go away.
  3351. Arguments:
  3352. RefCount - Pointer to reference count structure.
  3353. Return Value:
  3354. Status.
  3355. --*/
  3356. {
  3357. LONG OldValue;
  3358. //
  3359. // Try to get exclusive access by setting Exclusive from 0 to 1.
  3360. //
  3361. OldValue = InterlockedCompareExchange(&RefCount->Exclusive, 1, 0);
  3362. if (OldValue != 0) {
  3363. //
  3364. // Somebody already had the lock.
  3365. //
  3366. return ERROR_ACCESS_DENIED;
  3367. }
  3368. //
  3369. // Decrement the reference count once so it may become 0.
  3370. //
  3371. InterlockedDecrement(&RefCount->RefCount);
  3372. //
  3373. // No new references will be given away. We poll until existing
  3374. // references are released.
  3375. //
  3376. do {
  3377. if (RefCount->RefCount == 0) {
  3378. break;
  3379. } else {
  3380. //
  3381. // Sleep for a while.
  3382. //
  3383. Sleep(50);
  3384. }
  3385. } while(TRUE);
  3386. //
  3387. // We have exclusive access now. Note that we are the exclusive
  3388. // owner.
  3389. //
  3390. RefCount->ExclusiveOwner = GetCurrentThread();
  3391. return ERROR_SUCCESS;
  3392. }
  3393. BOOL
  3394. ItSpSetProcessIdleTasksNotifyRoutine (
  3395. PIT_PROCESS_IDLE_TASKS_NOTIFY_ROUTINE NotifyRoutine
  3396. )
  3397. /*++
  3398. Routine Description:
  3399. This routine is called by an internal component (prefetcher) to set a
  3400. notification routine that will get called if processing of all idle
  3401. tasks are requested. The routine should be set once, and it cannot be
  3402. removed.
  3403. Arguments:
  3404. NotifyRoutine - Routine to be called. This routine will be called
  3405. and has to return before we start launching queued idle tasks.
  3406. Return Value:
  3407. Success.
  3408. --*/
  3409. {
  3410. BOOL Success;
  3411. if (!ItSrvGlobalContext->ProcessIdleTasksNotifyRoutine) {
  3412. ItSrvGlobalContext->ProcessIdleTasksNotifyRoutine = NotifyRoutine;
  3413. }
  3414. return (ItSrvGlobalContext->ProcessIdleTasksNotifyRoutine == NotifyRoutine);
  3415. }