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.

845 lines
18 KiB

  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. pwork.c
  5. Abstract:
  6. Main work dispatcher in the power policy manager
  7. Author:
  8. Ken Reneris (kenr) 17-Jan-1997
  9. Revision History:
  10. --*/
  11. #include "pop.h"
  12. #ifdef ALLOC_PRAGMA
  13. #pragma alloc_text(PAGE, PopAcquirePolicyLock)
  14. #pragma alloc_text(PAGE, PopReleasePolicyLock)
  15. #pragma alloc_text(PAGE, PopPolicyWorkerMain)
  16. #pragma alloc_text(PAGE, PopPolicyWorkerNotify)
  17. #pragma alloc_text(PAGE, PopPolicyTimeChange)
  18. #pragma alloc_text(PAGE, PopDispatchCallout)
  19. #pragma alloc_text(PAGE, PopDispatchCallback)
  20. #pragma alloc_text(PAGE, PopDispatchDisplayRequired)
  21. #pragma alloc_text(PAGE, PopDispatchFullWake)
  22. #pragma alloc_text(PAGE, PopDispatchEventCodes)
  23. #pragma alloc_text(PAGE, PopDispatchAcDcCallback)
  24. #pragma alloc_text(PAGE, PopDispatchPolicyCallout)
  25. #pragma alloc_text(PAGE, PopDispatchSetStateFailure)
  26. #endif
  27. VOID
  28. PopAcquirePolicyLock(
  29. VOID
  30. )
  31. {
  32. PAGED_CODE();
  33. KeEnterCriticalRegion();
  34. ExAcquireResourceExclusiveLite (&PopPolicyLock, TRUE);
  35. //
  36. // Make sure we are not acquiring this recursively
  37. //
  38. ASSERT(PopPolicyLockThread == NULL);
  39. PopPolicyLockThread = KeGetCurrentThread();
  40. }
  41. VOID
  42. PopReleasePolicyLock(
  43. IN BOOLEAN CheckForWork
  44. )
  45. {
  46. PAGED_CODE();
  47. ASSERT (PopPolicyLockThread == KeGetCurrentThread());
  48. PopPolicyLockThread = NULL;
  49. ExReleaseResourceLite(&PopPolicyLock);
  50. //
  51. // If CheckForWork is set, then this thread is about ready
  52. // to leave the policy manager and it may have set a worker
  53. // pending bit.
  54. //
  55. // N.B. the WorkerPending test is not synchronized, but
  56. // since we're only concered with bits the current thread
  57. // may have set that's OK.
  58. //
  59. if (CheckForWork && (PopWorkerPending & PopWorkerStatus)) {
  60. //
  61. // Worker bit is unmasked and pending. Turn this thread
  62. // into a worker.
  63. //
  64. //
  65. // Handle any pending work
  66. //
  67. PopPolicyWorkerThread (NULL);
  68. }
  69. KeLeaveCriticalRegion ();
  70. }
  71. VOID
  72. PopGetPolicyWorker (
  73. IN ULONG WorkerType
  74. )
  75. /*++
  76. Routine Description:
  77. This function enqueus a worker thread for the particular WorkerType.
  78. At a maximum one worker thread per type may be dispatched, and typically
  79. fewer threads are actually dispatched as any given worker thread will
  80. call the new highest priority non-busy dispatch function until all
  81. pending work is completed before existing.
  82. Arguments:
  83. WorkerType - Which worker to enqueue for dispatching
  84. Return Value:
  85. None
  86. --*/
  87. {
  88. KIRQL OldIrql;
  89. KeAcquireSpinLock (&PopWorkerSpinLock, &OldIrql);
  90. //
  91. // Set pending to get worker to dispatch to handler
  92. //
  93. PopWorkerPending |= WorkerType;
  94. KeReleaseSpinLock (&PopWorkerSpinLock, OldIrql);
  95. }
  96. NTSTATUS
  97. PopCompletePolicyIrp (
  98. IN PDEVICE_OBJECT DeviceObject,
  99. IN PIRP Irp,
  100. IN PVOID Context
  101. )
  102. /*++
  103. Routine Description:
  104. This function handles the completion of a policy manager IRP.
  105. Policy manager IRPs have a stack location containing the irp
  106. handler function to dispatch too. In this function the irp is
  107. queue to the irp complete queue and a main worker is allocated
  108. if needed to run the queue.
  109. Arguments:
  110. DeviceObject -
  111. Irp - The irp which has completed
  112. Context -
  113. Return Value:
  114. STATUS_MORE_PROCESSING_REQUIRED
  115. --*/
  116. {
  117. ULONG Mask;
  118. KIRQL OldIrql;
  119. //
  120. // Put the irp on a queue for a worker thread
  121. //
  122. KeAcquireSpinLock (&PopWorkerSpinLock, &OldIrql);
  123. InsertTailList (&PopPolicyIrpQueue, &Irp->Tail.Overlay.ListEntry);
  124. //
  125. // Wait until base drivers are loaded before dispatching any policy irps
  126. //
  127. if (PopDispatchPolicyIrps) {
  128. //
  129. // Set pending to get worker to dispatch to handler
  130. //
  131. PopWorkerPending |= PO_WORKER_MAIN;
  132. //
  133. // If worker is not already running queue a thread
  134. //
  135. if ((PopWorkerStatus & (PO_WORKER_MAIN | PO_WORKER_STATUS)) ==
  136. (PO_WORKER_MAIN | PO_WORKER_STATUS) ) {
  137. PopWorkerStatus &= ~PO_WORKER_STATUS;
  138. ExQueueWorkItem (&PopPolicyWorker, DelayedWorkQueue);
  139. }
  140. }
  141. //
  142. // If this irp has been cancelled, then make sure to clear the cancel flag
  143. //
  144. if (Irp->IoStatus.Status == STATUS_CANCELLED) {
  145. Irp->Cancel = FALSE;
  146. }
  147. KeReleaseSpinLock (&PopWorkerSpinLock, OldIrql);
  148. return STATUS_MORE_PROCESSING_REQUIRED;
  149. }
  150. VOID
  151. PopCheckForWork (
  152. IN BOOLEAN GetWorker
  153. )
  154. /*++
  155. Routine Description:
  156. Checks for outstanding work and dispatches a worker if needed.
  157. Arguments:
  158. None
  159. Return Value:
  160. None
  161. --*/
  162. {
  163. KIRQL Irql;
  164. //
  165. // If pending work, handle it
  166. //
  167. if (PopWorkerPending & PopWorkerStatus) {
  168. //
  169. // If current thread already owns the policy lock,
  170. // then just return - we will handle the work when the
  171. // lock is released
  172. //
  173. if (PopPolicyLockThread == KeGetCurrentThread()) {
  174. return ;
  175. }
  176. //
  177. // Handle the work
  178. //
  179. Irql = KeGetCurrentIrql();
  180. if (!GetWorker && Irql < DISPATCH_LEVEL) {
  181. //
  182. // Use calling thread
  183. //
  184. KeEnterCriticalRegion ();
  185. PopPolicyWorkerThread (NULL);
  186. KeLeaveCriticalRegion ();
  187. } else {
  188. //
  189. // Get worker thread to handle it
  190. //
  191. KeAcquireSpinLock (&PopWorkerSpinLock, &Irql);
  192. if (PopWorkerStatus & PO_WORKER_STATUS) {
  193. PopWorkerStatus &= ~PO_WORKER_STATUS;
  194. ExQueueWorkItem (&PopPolicyWorker, DelayedWorkQueue);
  195. }
  196. KeReleaseSpinLock (&PopWorkerSpinLock, Irql);
  197. }
  198. }
  199. }
  200. VOID
  201. PopPolicyWorkerThread (
  202. PVOID Context
  203. )
  204. /*++
  205. Routine Description:
  206. Main policy manager worker thread dispatcher. Sends the
  207. worker thread to the highest pending priority handler which
  208. does not already have a worker thread. Loops until no
  209. handler can be dispatched too.
  210. Arguments:
  211. Return Value:
  212. None
  213. --*/
  214. {
  215. ULONG WorkerType;
  216. ULONG Mask;
  217. KIRQL OldIrql;
  218. ULONG i;
  219. ULONG DelayedWork;
  220. PAGED_CODE();
  221. try {
  222. //
  223. // Dispatch
  224. //
  225. KeAcquireSpinLock (&PopWorkerSpinLock, &OldIrql);
  226. PopWorkerStatus |= (ULONG) ((ULONG_PTR)Context);
  227. DelayedWork = 0;
  228. while (WorkerType = (PopWorkerPending & PopWorkerStatus)) {
  229. //
  230. // Get highest priority worker
  231. //
  232. i = KeFindFirstSetRightMember(WorkerType);
  233. Mask = 1 << i;
  234. //
  235. // Clear pending and indicate busy status
  236. //
  237. PopWorkerPending &= ~Mask;
  238. PopWorkerStatus &= ~Mask;
  239. KeReleaseSpinLock (&PopWorkerSpinLock, OldIrql);
  240. //
  241. // Dispatch to handler
  242. //
  243. DelayedWork |= PopWorkerTypes[i] ();
  244. //
  245. // No longer in progress
  246. //
  247. KeAcquireSpinLock (&PopWorkerSpinLock, &OldIrql);
  248. PopWorkerStatus |= Mask;
  249. }
  250. PopWorkerPending |= DelayedWork;
  251. KeReleaseSpinLock (&PopWorkerSpinLock, OldIrql);
  252. } except (PopExceptionFilter(GetExceptionInformation(), FALSE)) {
  253. }
  254. }
  255. ULONG
  256. PopPolicyWorkerMain (
  257. VOID
  258. )
  259. /*++
  260. Routine Description:
  261. Main policy worker thread. Dispatches any completed policy
  262. manager irps.
  263. Arguments:
  264. None
  265. Return Value:
  266. None
  267. --*/
  268. {
  269. IN PIRP Irp;
  270. PIO_STACK_LOCATION IrpSp;
  271. POP_IRP_HANDLER IrpHandler;
  272. PLIST_ENTRY Entry;
  273. PopAcquirePolicyLock ();
  274. //
  275. // Dispatch any policy irps which have completed
  276. //
  277. while (Entry = ExInterlockedRemoveHeadList (&PopPolicyIrpQueue, &PopWorkerSpinLock)) {
  278. Irp = CONTAINING_RECORD (Entry, IRP, Tail.Overlay.ListEntry);
  279. IrpSp = IoGetCurrentIrpStackLocation(Irp);
  280. //
  281. // Dispatch irp to handler
  282. //
  283. IrpHandler = (POP_IRP_HANDLER) IrpSp->Parameters.Others.Argument3;
  284. IrpHandler ((PDEVICE_OBJECT) IrpSp->Parameters.Others.Argument1,
  285. Irp,
  286. (PVOID) IrpSp->Parameters.Others.Argument2);
  287. }
  288. PopReleasePolicyLock (FALSE);
  289. PopCheckForWork (TRUE);
  290. return 0;
  291. }
  292. VOID
  293. PopEventCalloutDispatch (
  294. IN PSPOWEREVENTTYPE EventNumber,
  295. IN ULONG_PTR Code
  296. )
  297. {
  298. WIN32_POWEREVENT_PARAMETERS Parms;
  299. ULONG Console;
  300. PVOID OpaqueSession;
  301. KAPC_STATE ApcState;
  302. NTSTATUS Status;
  303. Parms.EventNumber = EventNumber;
  304. Parms.Code = Code;
  305. ASSERT(MmIsSessionAddress((PVOID)PopEventCallout));
  306. if (EventNumber == PsW32GdiOn || EventNumber == PsW32GdiOff) {
  307. //
  308. // These events go to the console session only.
  309. // The ActiveConsoleId session is stored with the SharedUserData.
  310. //
  311. Console = SharedUserData->ActiveConsoleId;
  312. //
  313. // Unfortunately, it is not guaranteed to be valid during a console
  314. // session change, and there is no way to know when that is happening,
  315. // so if it's not valid, just default to session 0, which is always
  316. // there.
  317. //
  318. if (Console == ((ULONG)-1)) {
  319. Console = 0;
  320. }
  321. if ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION) &&
  322. (Console == PsGetCurrentProcessSessionId())) {
  323. //
  324. // If the caller is already in the specified session, call directly.
  325. //
  326. PopEventCallout(&Parms);
  327. } else {
  328. //
  329. // Attach to the console session and dispatch the event.
  330. //
  331. OpaqueSession = MmGetSessionById(Console);
  332. if (OpaqueSession) {
  333. Status = MmAttachSession(OpaqueSession, &ApcState);
  334. ASSERT(NT_SUCCESS(Status));
  335. if (NT_SUCCESS(Status)) {
  336. PopEventCallout(&Parms);
  337. Status = MmDetachSession(OpaqueSession, &ApcState);
  338. ASSERT(NT_SUCCESS(Status));
  339. }
  340. Status = MmQuitNextSession(OpaqueSession);
  341. ASSERT(NT_SUCCESS(Status));
  342. }
  343. }
  344. } else {
  345. //
  346. // All other events are broadcast to all sessions.
  347. //
  348. for (OpaqueSession = MmGetNextSession(NULL);
  349. OpaqueSession != NULL;
  350. OpaqueSession = MmGetNextSession(OpaqueSession)) {
  351. if ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION) &&
  352. (MmGetSessionId(OpaqueSession) == PsGetCurrentProcessSessionId())) {
  353. //
  354. // If the caller is already in the specified session, call directly.
  355. //
  356. PopEventCallout(&Parms);
  357. } else {
  358. //
  359. // Attach to the session and dispatch the event.
  360. //
  361. Status = MmAttachSession(OpaqueSession, &ApcState);
  362. ASSERT(NT_SUCCESS(Status));
  363. if (NT_SUCCESS(Status)) {
  364. PopEventCallout(&Parms);
  365. Status = MmDetachSession(OpaqueSession, &ApcState);
  366. ASSERT(NT_SUCCESS(Status));
  367. }
  368. }
  369. }
  370. }
  371. return;
  372. }
  373. ULONG
  374. PopPolicyTimeChange (
  375. VOID
  376. )
  377. {
  378. PopEventCalloutDispatch (PsW32SystemTime, 0);
  379. return 0;
  380. }
  381. VOID
  382. PopSetNotificationWork (
  383. IN ULONG Flags
  384. )
  385. /*++
  386. Routine Description:
  387. Sets notification flags for the USER notification worker thread.
  388. Each bit is a different type of outstanding notification that
  389. is to be processed.
  390. Arguments:
  391. Flags - The notifications to set
  392. Return Value:
  393. None
  394. --*/
  395. {
  396. //
  397. // Are the flags set
  398. //
  399. if ((PopNotifyEvents & Flags) != Flags) {
  400. PoPrint(PO_NOTIFY, ("PopSetNotificationWork: Queue notify of: %x\n", Flags));
  401. InterlockedOr (&PopNotifyEvents, Flags);
  402. PopGetPolicyWorker (PO_WORKER_NOTIFY);
  403. }
  404. }
  405. ULONG
  406. PopPolicyWorkerNotify (
  407. VOID
  408. )
  409. /*++
  410. Routine Description:
  411. USER notification worker. Processes each set bit in NotifyEvents.
  412. Arguments:
  413. None
  414. Return Value:
  415. None
  416. --*/
  417. {
  418. ULONG i;
  419. ULONG Flags;
  420. ULONG Mask;
  421. const POP_NOTIFY_WORK* NotifyWork;
  422. //
  423. // If Win32 event callout is registered, then don't dispatch right now
  424. //
  425. if (!PopEventCallout) {
  426. return PO_WORKER_NOTIFY;
  427. }
  428. //
  429. // While events are pending collect them and dispatch them
  430. //
  431. while (Flags = InterlockedExchange (&PopNotifyEvents, 0)) {
  432. while (Flags) {
  433. //
  434. // Get change
  435. //
  436. i = KeFindFirstSetRightMember(Flags);
  437. Mask = 1 << i;
  438. Flags &= ~Mask;
  439. NotifyWork = PopNotifyWork + i;
  440. //
  441. // Dispatch it
  442. //
  443. NotifyWork->Function (NotifyWork->Arg);
  444. }
  445. }
  446. return 0;
  447. }
  448. VOID
  449. PopDispatchCallout (
  450. IN ULONG Arg
  451. )
  452. {
  453. PopEventCalloutDispatch (Arg, 0);
  454. }
  455. VOID
  456. PopDispatchCallback (
  457. IN ULONG Arg
  458. )
  459. {
  460. // Sundown: Arg is zero-extended
  461. ExNotifyCallback (ExCbPowerState, ULongToPtr(Arg), 0);
  462. }
  463. VOID
  464. PopDispatchDisplayRequired (
  465. IN ULONG Arg
  466. )
  467. /*++
  468. Routine Description:
  469. Notify user32 of the current "display required" setting. Zero, means
  470. the display may timeout. Non-zero, means the display is in use
  471. until told otherwise.
  472. --*/
  473. {
  474. ULONG i;
  475. i = PopAttributes[POP_DISPLAY_ATTRIBUTE].Count;
  476. PoPrint(PO_NOTIFY, ("PopNotify: DisplayRequired %x\n", i));
  477. //
  478. // If the display is in use but has not yet been turned on, then do so now
  479. //
  480. if (((PopFullWake & (PO_GDI_STATUS | PO_GDI_ON_PENDING)) == PO_GDI_ON_PENDING)) {
  481. PoPrint(PO_PACT, ("PopEventDispatch: gdi on\n"));
  482. InterlockedOr (&PopFullWake, PO_GDI_STATUS);
  483. PopEventCalloutDispatch (PsW32GdiOn, 0);
  484. }
  485. PopEventCalloutDispatch (PsW32DisplayState, i);
  486. }
  487. VOID
  488. PopDispatchFullWake (
  489. IN ULONG Arg
  490. )
  491. /*++
  492. Routine Description:
  493. Notify user32 that the system has fully awoken.
  494. Also reset the idle detection to the current policy
  495. --*/
  496. {
  497. //
  498. // If we're not in the middle setting the system state, then check the pending
  499. // flags.
  500. //
  501. if (PopAction.State != PO_ACT_SET_SYSTEM_STATE) {
  502. //
  503. // Notify user32 of the wake events
  504. //
  505. if ((PopFullWake & (PO_GDI_STATUS | PO_GDI_ON_PENDING)) == PO_GDI_ON_PENDING) {
  506. PoPrint(PO_PACT, ("PopEventDispatch: gdi on\n"));
  507. InterlockedOr (&PopFullWake, PO_GDI_STATUS);
  508. PopEventCalloutDispatch (PsW32GdiOn, 0);
  509. }
  510. if ((PopFullWake & (PO_FULL_WAKE_STATUS | PO_FULL_WAKE_PENDING)) == PO_FULL_WAKE_PENDING) {
  511. PoPrint(PO_PACT, ("PopEventDispatch: full wake\n"));
  512. InterlockedOr (&PopFullWake, PO_FULL_WAKE_STATUS);
  513. PopEventCalloutDispatch (PsW32FullWake, 0);
  514. //
  515. // Reset the idle detection policy
  516. //
  517. PopAcquirePolicyLock();
  518. PopInitSIdle ();
  519. PopReleasePolicyLock (FALSE);
  520. }
  521. }
  522. }
  523. VOID
  524. PopDispatchEventCodes (
  525. IN ULONG Arg
  526. )
  527. /*++
  528. Routine Description:
  529. Notify user32 of the queued event codes.
  530. --*/
  531. {
  532. ULONG i;
  533. ULONG Code;
  534. PopAcquirePolicyLock();
  535. for (i=0; i < POP_MAX_EVENT_CODES; i++) {
  536. if (PopEventCode[i]) {
  537. Code = PopEventCode[i];
  538. PopEventCode[i] = 0;
  539. PopReleasePolicyLock (FALSE);
  540. PoPrint(PO_NOTIFY, ("PopNotify: Event %x\n", Code));
  541. PopEventCalloutDispatch (PsW32EventCode, Code);
  542. PopAcquirePolicyLock ();
  543. }
  544. }
  545. PopResetSwitchTriggers();
  546. PopReleasePolicyLock(FALSE);
  547. }
  548. VOID
  549. PopDispatchAcDcCallback (
  550. IN ULONG Arg
  551. )
  552. /*++
  553. Routine Description:
  554. Notify the system callback of the current policy as either
  555. being AC or DC
  556. --*/
  557. {
  558. ExNotifyCallback (
  559. ExCbPowerState,
  560. UIntToPtr(PO_CB_AC_STATUS),
  561. UIntToPtr((PopPolicy == &PopAcPolicy))
  562. );
  563. }
  564. VOID
  565. PopDispatchPolicyCallout (
  566. IN ULONG Arg
  567. )
  568. /*++
  569. Routine Description:
  570. Notify user32 that the active policy has changed
  571. --*/
  572. {
  573. PoPrint(PO_NOTIFY, ("PopNotify: PolicyChanged\n"));
  574. PopEventCalloutDispatch (PsW32PowerPolicyChanged, PopPolicy->VideoTimeout);
  575. }
  576. VOID
  577. PopDispatchProcessorPolicyCallout (
  578. IN ULONG Arg
  579. )
  580. /*++
  581. Routine Description:
  582. Not used right now. But required so that we don't have a NULL entry
  583. in the PopNotifyWork array
  584. --*/
  585. {
  586. PoPrint(PO_NOTIFY, ("PopNotify: ProcessorPolicyChanges\n"));
  587. Arg = Arg;
  588. }
  589. VOID
  590. PopDispatchSetStateFailure (
  591. IN ULONG Arg
  592. )
  593. /*++
  594. Routine Description:
  595. Notify user32 that there was a failure during an async system state
  596. operation. E.g., no error code was returned to anyone, yet the operation
  597. failed
  598. --*/
  599. {
  600. BOOLEAN IssueCallout;
  601. PO_SET_STATE_FAILURE Failure;
  602. RtlZeroMemory (&Failure, sizeof(Failure));
  603. PopAcquirePolicyLock();
  604. //
  605. // If the action state is idle, check to see if we should notify
  606. // win32 of the failure
  607. //
  608. if (PopAction.State == PO_ACT_IDLE && !NT_SUCCESS(PopAction.Status) &&
  609. (PopAction.Flags & (POWER_ACTION_UI_ALLOWED | POWER_ACTION_CRITICAL)) ) {
  610. Failure.Status = PopAction.Status;
  611. Failure.PowerAction = PopAction.Action;
  612. Failure.MinState = PopAction.LightestState;
  613. Failure.Flags = PopAction.Flags;
  614. }
  615. PopReleasePolicyLock (FALSE);
  616. if (!NT_SUCCESS(Failure.Status)) {
  617. PoPrint(PO_NOTIFY, ("PopNotify: set state failed (code %x)\n", Failure.Status));
  618. PopEventCalloutDispatch (PsW32SetStateFailed, (ULONG_PTR) &Failure);
  619. }
  620. }