Leaked source code of windows server 2003
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.

1144 lines
25 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. dwd.c
  5. Abstract:
  6. This is the NT Watchdog driver implementation.
  7. Author:
  8. Michael Maciesowicz (mmacie) 05-May-2000
  9. Environment:
  10. Kernel mode only.
  11. Notes:
  12. Revision History:
  13. --*/
  14. #include "wd.h"
  15. #ifdef ALLOC_PRAGMA
  16. #pragma alloc_text (PAGE, WdAllocateDeferredWatchdog)
  17. #pragma alloc_text (PAGE, WdFreeDeferredWatchdog)
  18. #endif
  19. #ifdef WDD_TRACE_ENABLED
  20. ULONG g_ulWddIndex = 0;
  21. WDD_TRACE g_aWddTrace[WDD_TRACE_SIZE] = {0};
  22. #endif // WDD_TRACE_ENABLED
  23. WATCHDOGAPI
  24. PDEFERRED_WATCHDOG
  25. WdAllocateDeferredWatchdog(
  26. IN PDEVICE_OBJECT pDeviceObject,
  27. IN WD_TIME_TYPE timeType,
  28. IN ULONG ulTag
  29. )
  30. /*++
  31. Routine Description:
  32. This function allocates storage and initializes
  33. a deferred watchdog object.
  34. Arguments:
  35. pDeviceObject - Points to DEVICE_OBJECT associated with watchdog.
  36. timeType - Kernel, User, Both thread time to monitor.
  37. ulTag - A tag identifying owner.
  38. Return Value:
  39. Pointer to allocated deferred watchdog object or NULL.
  40. --*/
  41. {
  42. PDEFERRED_WATCHDOG pWatch;
  43. PAGED_CODE();
  44. ASSERT(NULL != pDeviceObject);
  45. ASSERT((timeType >= WdKernelTime) && (timeType <= WdFullTime));
  46. WDD_TRACE_CALL(NULL, WddWdAllocateDeferredWatchdog);
  47. //
  48. // Allocate storage for deferred watchdog from non-paged pool.
  49. //
  50. pWatch = (PDEFERRED_WATCHDOG)ExAllocatePoolWithTag(NonPagedPool, sizeof (DEFERRED_WATCHDOG), ulTag);
  51. //
  52. // Set initial state of deferred watchdog.
  53. //
  54. if (NULL != pWatch)
  55. {
  56. //
  57. // Set initial state of watchdog.
  58. //
  59. WdpInitializeObject(pWatch,
  60. pDeviceObject,
  61. WdDeferredWatchdog,
  62. timeType,
  63. ulTag);
  64. pWatch->Period = 0;
  65. pWatch->SuspendCount = 0;
  66. pWatch->InCount = 0;
  67. pWatch->OutCount = 0;
  68. pWatch->LastInCount = 0;
  69. pWatch->LastOutCount = 0;
  70. pWatch->LastKernelTime = 0;
  71. pWatch->LastUserTime = 0;
  72. pWatch->TimeIncrement = KeQueryTimeIncrement();
  73. pWatch->Trigger = 0;
  74. pWatch->State = WdStopped;
  75. pWatch->Thread = NULL;
  76. pWatch->ClientDpc = NULL;
  77. //
  78. // Initialize encapsulated DPC object.
  79. //
  80. KeInitializeDpc(&(pWatch->TimerDpc), WdpDeferredWatchdogDpcCallback, pWatch);
  81. //
  82. // Initialize encapsulated timer object.
  83. //
  84. KeInitializeTimerEx(&(pWatch->Timer), NotificationTimer);
  85. }
  86. return pWatch;
  87. } // WdAllocateDeferredWatchdog()
  88. WATCHDOGAPI
  89. VOID
  90. WdFreeDeferredWatchdog(
  91. PDEFERRED_WATCHDOG pWatch
  92. )
  93. /*++
  94. Routine Description:
  95. This function deallocates storage for deferred watchdog object.
  96. It will also stop started deferred watchdog if needed.
  97. Arguments:
  98. pWatch - Supplies a pointer to a watchdog object.
  99. Return Value:
  100. None.
  101. --*/
  102. {
  103. PAGED_CODE();
  104. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  105. ASSERT(NULL != pWatch);
  106. ASSERT(pWatch->Header.ReferenceCount > 0);
  107. WDD_TRACE_CALL(pWatch, WddWdFreeDeferredWatchdog);
  108. //
  109. // Stop deferred watch just in case somebody forgot.
  110. // If the watch is stopped already then this is a no-op.
  111. //
  112. WdStopDeferredWatch(pWatch);
  113. //
  114. // Make sure all DPCs on all processors executed to completion.
  115. //
  116. KeFlushQueuedDpcs();
  117. //
  118. // Drop reference count and remove the object if fully dereferenced.
  119. //
  120. if (InterlockedDecrement(&(pWatch->Header.ReferenceCount)) == 0)
  121. {
  122. WdpDestroyObject(pWatch);
  123. }
  124. return;
  125. } // WdFreeDeferredWatchdog()
  126. WATCHDOGAPI
  127. VOID
  128. WdStartDeferredWatch(
  129. IN PDEFERRED_WATCHDOG pWatch,
  130. IN PKDPC pDpc,
  131. IN LONG lPeriod
  132. )
  133. /*++
  134. Routine Description:
  135. This function starts deferred watchdog poller.
  136. Arguments:
  137. pWatch - Supplies a pointer to a deferred watchdog object.
  138. pDpc - Supplies a pointer to a control object of type DPC.
  139. ulPeriod - Supplies maximum time in millisecondes that thread
  140. can spend in the monitored section. If this time expires a DPC
  141. will we queued.
  142. Return Value:
  143. None.
  144. --*/
  145. {
  146. KIRQL oldIrql;
  147. LARGE_INTEGER liDueTime;
  148. #ifdef WD_FAILURE_TEST
  149. //
  150. // Code to test EA failure handling. To trigger failure set REG_DWORD FailureTest
  151. // to watchdog's tag we are interested in and force code path which starts
  152. // that watchdog (e.g. switch video mode for 'dwdG' = 0x64776447 tag).
  153. // This code should be compiled out for the production version.
  154. //
  155. ULONG ulFailureTest = 0;
  156. ULONG ulDefaultFailureTest = 0;
  157. RTL_QUERY_REGISTRY_TABLE queryTable[] =
  158. {
  159. {NULL, RTL_QUERY_REGISTRY_DIRECT, L"FailureTest", &ulFailureTest, REG_DWORD, &ulDefaultFailureTest, 4},
  160. {NULL, 0, NULL}
  161. };
  162. #endif // WD_FAILURE_TEST
  163. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  164. ASSERT(NULL != pWatch);
  165. ASSERT(NULL != pDpc);
  166. WDD_TRACE_CALL(pWatch, WddWdStartDeferredWatch);
  167. #ifdef WD_FAILURE_TEST
  168. RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
  169. WD_KEY_WATCHDOG,
  170. queryTable,
  171. NULL,
  172. NULL);
  173. if (ulFailureTest == pWatch->Header.OwnerTag)
  174. {
  175. RtlDeleteRegistryValue(RTL_REGISTRY_ABSOLUTE,
  176. WD_KEY_WATCHDOG,
  177. L"FailureTest");
  178. WdpFlushRegistryKey(pWatch, WD_KEY_WATCHDOG);
  179. }
  180. else
  181. {
  182. ulFailureTest = 0;
  183. }
  184. #endif // WD_FAILURE_TEST
  185. //
  186. // Raise IRQL to dispatcher level and lock dispatcher database.
  187. //
  188. KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
  189. WD_DBG_SUSPENDED_WARNING(pWatch, "WdStartDeferredWatch");
  190. //
  191. // We shouldn't hot swap DPCs without stopping first.
  192. //
  193. ASSERT((NULL == pWatch->ClientDpc) || (pDpc == pWatch->ClientDpc));
  194. pWatch->Period = lPeriod;
  195. pWatch->InCount = 0;
  196. pWatch->OutCount = 0;
  197. pWatch->LastInCount = 0;
  198. pWatch->LastOutCount = 0;
  199. pWatch->LastKernelTime = 0;
  200. pWatch->LastUserTime = 0;
  201. pWatch->Trigger = 0;
  202. pWatch->State = WdStarted;
  203. pWatch->Thread = NULL;
  204. pWatch->ClientDpc = pDpc;
  205. #ifdef WD_FAILURE_TEST
  206. if (ulFailureTest)
  207. {
  208. //
  209. // Force timeout condition.
  210. //
  211. pWatch->Thread = KeGetCurrentThread();
  212. WdpQueueDeferredEvent(pWatch, WdTimeoutEvent);
  213. KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
  214. return;
  215. }
  216. #endif // WD_FAILURE_TEST
  217. //
  218. // Unlock the dispatcher database and lower IRQL to its previous value.
  219. //
  220. KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
  221. //
  222. // Set first fire to lPeriod.
  223. //
  224. liDueTime.QuadPart = -(lPeriod * 1000 * 10);
  225. KeSetTimerEx(&(pWatch->Timer), liDueTime, lPeriod, &(pWatch->TimerDpc));
  226. return;
  227. } // WdStartDeferredWatch()
  228. WATCHDOGAPI
  229. VOID
  230. WdStopDeferredWatch(
  231. IN PDEFERRED_WATCHDOG pWatch
  232. )
  233. /*++
  234. Routine Description:
  235. This function stops deferred watchdog poller.
  236. Arguments:
  237. pWatch - Supplies a pointer to a watchdog object.
  238. Return Value:
  239. None.
  240. --*/
  241. {
  242. KIRQL oldIrql;
  243. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  244. ASSERT(NULL != pWatch);
  245. WDD_TRACE_CALL(pWatch, WddWdStopDeferredWatch);
  246. //
  247. // Raise IRQL to dispatcher level and lock dispatcher database.
  248. //
  249. KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
  250. WD_DBG_SUSPENDED_WARNING(pWatch, "WdStopDeferredWatch");
  251. if (WdStarted == pWatch->State)
  252. {
  253. KeCancelTimer(&(pWatch->Timer));
  254. //
  255. // Make sure we don't have timeout event pending.
  256. //
  257. if (NULL != pWatch->ClientDpc)
  258. {
  259. if (WdTimeoutEvent == pWatch->Header.LastEvent)
  260. {
  261. KeRemoveQueueDpc(pWatch->ClientDpc);
  262. WdpQueueDeferredEvent(pWatch, WdRecoveryEvent);
  263. }
  264. else if (KeRemoveQueueDpc(pWatch->ClientDpc) == TRUE)
  265. {
  266. //
  267. // Was in queue - call WdCompleteEvent() here since DPC won't be delivered.
  268. //
  269. WdCompleteEvent(pWatch, pWatch->Header.LastQueuedThread);
  270. }
  271. }
  272. pWatch->Period = 0;
  273. pWatch->InCount = 0;
  274. pWatch->OutCount = 0;
  275. pWatch->LastInCount = 0;
  276. pWatch->LastOutCount = 0;
  277. pWatch->LastKernelTime = 0;
  278. pWatch->LastUserTime = 0;
  279. pWatch->Trigger = 0;
  280. pWatch->State = WdStopped;
  281. pWatch->Thread = NULL;
  282. pWatch->ClientDpc = NULL;
  283. pWatch->Header.LastQueuedThread = NULL;
  284. }
  285. //
  286. // Unlock the dispatcher database and lower IRQL to its previous value.
  287. //
  288. KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
  289. return;
  290. } // WdStopDeferredWatch()
  291. WATCHDOGAPI
  292. VOID
  293. FASTCALL
  294. WdSuspendDeferredWatch(
  295. IN PDEFERRED_WATCHDOG pWatch
  296. )
  297. /*++
  298. Routine Description:
  299. This function suspends deferred watchdog poller.
  300. Arguments:
  301. pWatch - Supplies a pointer to a watchdog object.
  302. Return Value:
  303. None.
  304. --*/
  305. {
  306. ASSERT(NULL != pWatch);
  307. ASSERT((ULONG)(pWatch->SuspendCount) < (ULONG)(-1));
  308. InterlockedIncrement(&(pWatch->SuspendCount));
  309. return;
  310. } // WdSuspendDeferredWatch()
  311. WATCHDOGAPI
  312. VOID
  313. FASTCALL
  314. WdResumeDeferredWatch(
  315. IN PDEFERRED_WATCHDOG pWatch,
  316. IN BOOLEAN bIncremental
  317. )
  318. /*++
  319. Routine Description:
  320. This function resumes deferred watchdog poller.
  321. Arguments:
  322. pWatch - Supplies a pointer to a watchdog object.
  323. bIncremental - If TRUE the watchdog will resume only when
  324. SuspendCount reaches 0, if FALSE watchdog resumes
  325. immediately and SuspendCount is forced to 0.
  326. Return Value:
  327. None.
  328. --*/
  329. {
  330. ASSERT(NULL != pWatch);
  331. if (TRUE == bIncremental)
  332. {
  333. //
  334. // Make sure we won't roll under.
  335. //
  336. if (InterlockedDecrement(&(pWatch->SuspendCount)) == -1)
  337. {
  338. InterlockedIncrement(&(pWatch->SuspendCount));
  339. }
  340. }
  341. else
  342. {
  343. InterlockedExchange(&(pWatch->SuspendCount), 0);
  344. }
  345. return;
  346. } // WdResumeDeferredWatch()
  347. WATCHDOGAPI
  348. VOID
  349. FASTCALL
  350. WdResetDeferredWatch(
  351. IN PDEFERRED_WATCHDOG pWatch
  352. )
  353. /*++
  354. Routine Description:
  355. This function resets deferred watchdog poller, i.e. it starts
  356. timeout measurement from the scratch if we are in the monitored
  357. section.
  358. Note: If the watchdog is suspened it will remain suspended.
  359. Arguments:
  360. pWatch - Supplies a pointer to a watchdog object.
  361. Return Value:
  362. None.
  363. --*/
  364. {
  365. KIRQL oldIrql;
  366. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  367. ASSERT(NULL != pWatch);
  368. WDD_TRACE_CALL(pWatch, WddWdResetDeferredWatch);
  369. //
  370. // Raise IRQL to dispatcher level and lock dispatcher database.
  371. //
  372. KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
  373. pWatch->InCount = 0;
  374. pWatch->OutCount = 0;
  375. pWatch->Trigger = 0;
  376. //
  377. // Unlock the dispatcher database and lower IRQL to its previous value.
  378. //
  379. KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
  380. return;
  381. } // WdResetDeferredWatch()
  382. WATCHDOGAPI
  383. VOID
  384. FASTCALL
  385. WdEnterMonitoredSection(
  386. IN PDEFERRED_WATCHDOG pWatch
  387. )
  388. /*++
  389. Routine Description:
  390. This function starts monitoring of the code section for time-out
  391. condition.
  392. Note: To minimize an overhead it is caller's resposibility to make
  393. sure thread remains valid when we are in the monitored section.
  394. Arguments:
  395. pWatch - Supplies a pointer to a deferred watchdog object.
  396. Return Value:
  397. None.
  398. --*/
  399. {
  400. PKTHREAD pThread;
  401. KIRQL oldIrql;
  402. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  403. ASSERT(NULL != pWatch);
  404. ASSERT(WdStarted == pWatch->State);
  405. //
  406. // We have to remove this warning, I hope temporarily, since win32k
  407. // is calling this entry point now with suspended watchdog.
  408. //
  409. // WD_DBG_SUSPENDED_WARNING(pWatch, "WdEnterMonitoredSection");
  410. //
  411. pThread = KeGetCurrentThread();
  412. if (pThread != pWatch->Thread)
  413. {
  414. //
  415. // Raise IRQL to dispatcher level and lock dispatcher database.
  416. //
  417. KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
  418. //
  419. // We shouldn't swap threads in the monitored section.
  420. //
  421. ASSERT(pWatch->OutCount == pWatch->InCount);
  422. pWatch->Trigger = 0;
  423. pWatch->Thread = pThread;
  424. //
  425. // Unlock the dispatcher database and lower IRQL to its previous value.
  426. //
  427. KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
  428. }
  429. InterlockedIncrement(&(pWatch->InCount));
  430. return;
  431. } // WdEnterMonitoredSection()
  432. WATCHDOGAPI
  433. VOID
  434. FASTCALL
  435. WdExitMonitoredSection(
  436. IN PDEFERRED_WATCHDOG pWatch
  437. )
  438. /*++
  439. Routine Description:
  440. This function stops monitoring of the code section for time-out
  441. condition.
  442. Arguments:
  443. pWatch - Supplies a pointer to a deferred watchdog object.
  444. Return Value:
  445. None.
  446. --*/
  447. {
  448. ASSERT(NULL != pWatch);
  449. ASSERT((pWatch->OutCount < pWatch->InCount) ||
  450. ((pWatch->OutCount > 0) && (pWatch->InCount < 0)));
  451. //
  452. // We have to remove this warning, I hope temporarily, since win32k
  453. // is calling this entry point now with suspended watchdog.
  454. //
  455. // WD_DBG_SUSPENDED_WARNING(pWatch, "WdExitMonitoredSection");
  456. //
  457. InterlockedIncrement(&(pWatch->OutCount));
  458. return;
  459. } // WdExitMonitoredSection()
  460. VOID
  461. WdpDeferredWatchdogDpcCallback(
  462. IN PKDPC pDpc,
  463. IN PVOID pDeferredContext,
  464. IN PVOID pSystemArgument1,
  465. IN PVOID pSystemArgument2
  466. )
  467. /*++
  468. Routine Description:
  469. This function is a DPC callback routine for timer object embedded in the
  470. deferred watchdog object. It checks thread time and if the wait condition
  471. is satisfied it queues original (client) DPC.
  472. Arguments:
  473. pDpc - Supplies a pointer to a DPC object.
  474. pDeferredContext - Supplies a pointer to a deferred watchdog object.
  475. pSystemArgument1/2 - Supply time when embedded KTIMER expired.
  476. Return Value:
  477. None.
  478. --*/
  479. {
  480. PDEFERRED_WATCHDOG pWatch;
  481. LARGE_INTEGER liThreadTime;
  482. ULONG ulKernelTime;
  483. ULONG ulUserTime;
  484. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  485. ASSERT(NULL != pDeferredContext);
  486. pWatch = (PDEFERRED_WATCHDOG)pDeferredContext;
  487. WDD_TRACE_CALL(pWatch, WddWdpDeferredWatchdogDpcCallback);
  488. //
  489. // Lock dispatcher database.
  490. //
  491. KeAcquireSpinLockAtDpcLevel(&(pWatch->Header.SpinLock));
  492. if ((WdStarted == pWatch->State) && (NULL != pWatch->Thread))
  493. {
  494. switch (pWatch->Trigger)
  495. {
  496. case 0:
  497. //
  498. // Everything fine so far, check if we are suspended.
  499. //
  500. if (pWatch->SuspendCount)
  501. {
  502. //
  503. // We're suspended - do nothing.
  504. //
  505. break;
  506. }
  507. //
  508. // Check if the last event was a timeout event.
  509. //
  510. if (WdTimeoutEvent == pWatch->Header.LastEvent)
  511. {
  512. //
  513. // Check if we made any progress.
  514. //
  515. if ((pWatch->InCount != pWatch->LastInCount) ||
  516. (pWatch->OutCount != pWatch->LastOutCount) ||
  517. (pWatch->InCount == pWatch->OutCount))
  518. {
  519. //
  520. // We recovered - queue recovery event.
  521. //
  522. WdpQueueDeferredEvent(pWatch, WdRecoveryEvent);
  523. }
  524. }
  525. //
  526. // Check if we are in the monitored section.
  527. //
  528. if (pWatch->InCount == pWatch->OutCount)
  529. {
  530. //
  531. // We're outside monitored section - we're fine.
  532. //
  533. break;
  534. }
  535. //
  536. // We're inside monitored section - bump up trigger indicator,
  537. // and take snapshots of counters and thread's time.
  538. //
  539. pWatch->Trigger = 1;
  540. pWatch->LastInCount = pWatch->InCount;
  541. pWatch->LastOutCount = pWatch->OutCount;
  542. pWatch->LastKernelTime = KeQueryRuntimeThread(pWatch->Thread, &(pWatch->LastUserTime));
  543. break;
  544. case 1:
  545. //
  546. // We were in the monitored section last time.
  547. //
  548. //
  549. // Check if we're out or suspended.
  550. //
  551. if ((pWatch->InCount == pWatch->OutCount) || pWatch->SuspendCount)
  552. {
  553. //
  554. // We're outside monitored section or suspended - we're fine.
  555. // Reset trigger counter and get out of here.
  556. //
  557. pWatch->Trigger = 0;
  558. break;
  559. }
  560. //
  561. // Check if we made any progress, if so reset snapshots.
  562. //
  563. if ((pWatch->InCount != pWatch->LastInCount) ||
  564. (pWatch->OutCount != pWatch->LastOutCount))
  565. {
  566. pWatch->Trigger = 1;
  567. pWatch->LastInCount = pWatch->InCount;
  568. pWatch->LastOutCount = pWatch->OutCount;
  569. pWatch->LastKernelTime = KeQueryRuntimeThread(pWatch->Thread, &(pWatch->LastUserTime));
  570. break;
  571. }
  572. //
  573. // Check if we're stuck long enough.
  574. //
  575. ulKernelTime = KeQueryRuntimeThread(pWatch->Thread, &ulUserTime);
  576. switch (pWatch->Header.TimeType)
  577. {
  578. case WdKernelTime:
  579. liThreadTime.QuadPart = ulKernelTime;
  580. //
  581. // Handle counter rollovers.
  582. //
  583. if (ulKernelTime < pWatch->LastKernelTime)
  584. {
  585. liThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastKernelTime + 1;
  586. }
  587. liThreadTime.QuadPart -= pWatch->LastKernelTime;
  588. break;
  589. case WdUserTime:
  590. liThreadTime.QuadPart = ulUserTime;
  591. //
  592. // Handle counter rollovers.
  593. //
  594. if (ulUserTime < pWatch->LastUserTime)
  595. {
  596. liThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastUserTime + 1;
  597. }
  598. liThreadTime.QuadPart -= pWatch->LastUserTime;
  599. break;
  600. case WdFullTime:
  601. liThreadTime.QuadPart = ulKernelTime + ulUserTime;
  602. //
  603. // Handle counter rollovers.
  604. //
  605. if (ulKernelTime < pWatch->LastKernelTime)
  606. {
  607. liThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastKernelTime + 1;
  608. }
  609. if (ulUserTime < pWatch->LastUserTime)
  610. {
  611. liThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastUserTime + 1;
  612. }
  613. liThreadTime.QuadPart -= (pWatch->LastKernelTime + pWatch->LastUserTime);
  614. break;
  615. default:
  616. ASSERT(FALSE);
  617. liThreadTime.QuadPart = 0;
  618. break;
  619. }
  620. //
  621. // Convert to milliseconds.
  622. //
  623. liThreadTime.QuadPart *= pWatch->TimeIncrement;
  624. liThreadTime.QuadPart /= 10000;
  625. if (liThreadTime.QuadPart >= pWatch->Period)
  626. {
  627. //
  628. // We've been stuck long enough - queue timeout event.
  629. //
  630. WdpQueueDeferredEvent(pWatch, WdTimeoutEvent);
  631. }
  632. break;
  633. case 2:
  634. //
  635. // We have event posted waiting for completion. Nothing to do.
  636. //
  637. break;
  638. default:
  639. //
  640. // This should never happen.
  641. //
  642. ASSERT(FALSE);
  643. pWatch->Trigger = 0;
  644. break;
  645. }
  646. }
  647. //
  648. // Unlock the dispatcher database.
  649. //
  650. KeReleaseSpinLockFromDpcLevel(&(pWatch->Header.SpinLock));
  651. return;
  652. } // WdpDeferredWatchdogDpcCallback()
  653. BOOLEAN
  654. WdpQueueDeferredEvent(
  655. IN PDEFERRED_WATCHDOG pWatch,
  656. IN WD_EVENT_TYPE eventType
  657. )
  658. /*++
  659. Routine Description:
  660. This function put watchdog event into client's DPC queue.
  661. Arguments:
  662. pWatch - Supplies a pointer to a watchdog object.
  663. eventType - Watchdog event type to put into client DPC queue.
  664. Return Value:
  665. TRUE - success, FALSE - failed.
  666. Note:
  667. Call to this routine must be synchronized by the caller.
  668. --*/
  669. {
  670. BOOLEAN bStatus;
  671. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  672. ASSERT(NULL != pWatch);
  673. WDD_TRACE_CALL(pWatch, WddWdpQueueDeferredEvent);
  674. //
  675. // Preset return value.
  676. //
  677. bStatus = FALSE;
  678. if (NULL != pWatch->ClientDpc)
  679. {
  680. switch (eventType)
  681. {
  682. case WdRecoveryEvent:
  683. //
  684. // We recovered - update event type and queue client DPC.
  685. //
  686. pWatch->Header.LastEvent = WdRecoveryEvent;
  687. //
  688. // Bump up references to objects we're going to touch in client DPC.
  689. //
  690. WdReferenceObject(pWatch);
  691. //
  692. // Queue client DPC.
  693. //
  694. // Note: In case of recovery the thread associated with watchdog
  695. // object may be deleted by the time we get here. We can't pass it
  696. // down to client DPC - we're passing NULL instead.
  697. //
  698. if (KeInsertQueueDpc(pWatch->ClientDpc, NULL, pWatch) == TRUE)
  699. {
  700. //
  701. // Keep track of qeueued thread in case we cancel this DPC.
  702. //
  703. pWatch->Header.LastQueuedThread = NULL;
  704. //
  705. // Make sure we queue DPC only once per event.
  706. //
  707. pWatch->Trigger = 2;
  708. bStatus = TRUE;
  709. }
  710. else
  711. {
  712. //
  713. // This should never happen.
  714. //
  715. WdDereferenceObject(pWatch);
  716. }
  717. break;
  718. case WdTimeoutEvent:
  719. //
  720. // We timed-out - update event type and queue client DPC.
  721. //
  722. pWatch->Header.LastEvent = WdTimeoutEvent;
  723. //
  724. // Bump up references to objects we're going to touch in client DPC.
  725. //
  726. ObReferenceObject(pWatch->Thread);
  727. WdReferenceObject(pWatch);
  728. //
  729. // Queue client DPC.
  730. //
  731. if (KeInsertQueueDpc(pWatch->ClientDpc, pWatch->Thread, pWatch) == TRUE)
  732. {
  733. //
  734. // Keep track of qeueued thread in case we cancel this DPC.
  735. //
  736. pWatch->Header.LastQueuedThread = pWatch->Thread;
  737. //
  738. // Make sure we queue DPC only once per event.
  739. //
  740. pWatch->Trigger = 2;
  741. bStatus = TRUE;
  742. }
  743. else
  744. {
  745. //
  746. // This should never happen.
  747. //
  748. ObDereferenceObject(pWatch->Thread);
  749. WdDereferenceObject(pWatch);
  750. }
  751. break;
  752. default:
  753. //
  754. // This should never happen.
  755. //
  756. ASSERT(FALSE);
  757. break;
  758. }
  759. }
  760. return bStatus;
  761. } // WdpQueueDeferredEvent()
  762. #ifdef WDD_TRACE_ENABLED
  763. VOID
  764. FASTCALL
  765. WddTrace(
  766. PDEFERRED_WATCHDOG pWatch,
  767. WDD_FUNCTION function
  768. )
  769. /*++
  770. Routine Description:
  771. This function is used for debugging purposes only to keep track of call sequence.
  772. Arguments:
  773. pWatch - Supplies a pointer to a watchdog object.
  774. function - Enumerator assigned to one of watchdog's routines.
  775. Return Value:
  776. None.
  777. --*/
  778. {
  779. static volatile LONG lFlag = 0;
  780. static volatile LONG lSpinLockReady = 0;
  781. static KSPIN_LOCK spinLock;
  782. KIRQL oldIrql;
  783. if (InterlockedExchange(&lFlag, 1) == 0)
  784. {
  785. //
  786. // First time - initialize spinlock.
  787. //
  788. KeInitializeSpinLock(&spinLock);
  789. lSpinLockReady = 1;
  790. }
  791. if (lSpinLockReady)
  792. {
  793. KeAcquireSpinLock(&spinLock, &oldIrql);
  794. if (g_ulWddIndex >= WDD_TRACE_SIZE)
  795. {
  796. g_ulWddIndex = 0;
  797. }
  798. g_aWddTrace[g_ulWddIndex].pWatch = pWatch;
  799. g_aWddTrace[g_ulWddIndex].function = function;
  800. g_ulWddIndex++;
  801. KeReleaseSpinLock(&spinLock, oldIrql);
  802. }
  803. } // WddTrace()
  804. #endif // WDD_TRACE_ENABLED