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.

918 lines
20 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. #endif
  18. WATCHDOGAPI
  19. PDEFERRED_WATCHDOG
  20. WdAllocateDeferredWatchdog(
  21. IN PDEVICE_OBJECT pDeviceObject,
  22. IN WD_TIME_TYPE timeType,
  23. IN ULONG ulTag
  24. )
  25. /*++
  26. Routine Description:
  27. This function allocates storage and initializes
  28. a deferred watchdog object.
  29. Arguments:
  30. pDeviceObject - Points to DEVICE_OBJECT associated with watchdog.
  31. timeType - Kernel, User, Both thread time to monitor.
  32. ulTag - A tag identifying owner.
  33. Return Value:
  34. Pointer to allocated deferred watchdog object or NULL.
  35. --*/
  36. {
  37. PDEFERRED_WATCHDOG pWatch;
  38. PAGED_CODE();
  39. ASSERT(NULL != pDeviceObject);
  40. ASSERT((timeType >= WdKernelTime) && (timeType <= WdFullTime));
  41. //
  42. // Allocate storage for deferred watchdog from non-paged pool.
  43. //
  44. pWatch = (PDEFERRED_WATCHDOG)ExAllocatePoolWithTag(NonPagedPool, sizeof (DEFERRED_WATCHDOG), ulTag);
  45. //
  46. // Set initial state of deferred watchdog.
  47. //
  48. if (NULL != pWatch)
  49. {
  50. //
  51. // Set initial state of watchdog.
  52. //
  53. WdInitializeObject(pWatch,
  54. pDeviceObject,
  55. WdDeferredWatchdog,
  56. timeType,
  57. ulTag);
  58. pWatch->Period = 0;
  59. pWatch->SuspendCount = 0;
  60. pWatch->InCount = 0;
  61. pWatch->OutCount = 0;
  62. pWatch->LastInCount = 0;
  63. pWatch->LastOutCount = 0;
  64. pWatch->LastKernelTime = 0;
  65. pWatch->LastUserTime = 0;
  66. pWatch->TimeIncrement = KeQueryTimeIncrement();
  67. pWatch->Trigger = 0;
  68. pWatch->Started = FALSE;
  69. pWatch->Thread = NULL;
  70. pWatch->ClientDpc = NULL;
  71. //
  72. // Initialize encapsulated DPC object.
  73. //
  74. KeInitializeDpc(&(pWatch->TimerDpc), WdDeferredWatchdogDpcCallback, pWatch);
  75. //
  76. // Initialize encapsulated timer object.
  77. //
  78. KeInitializeTimerEx(&(pWatch->Timer), NotificationTimer);
  79. }
  80. return pWatch;
  81. } // WdAllocateDeferredWatchdog()
  82. WATCHDOGAPI
  83. VOID
  84. WdFreeDeferredWatchdog(
  85. PDEFERRED_WATCHDOG pWatch
  86. )
  87. /*++
  88. Routine Description:
  89. This function deallocates storage for deferred watchdog object.
  90. It will also stop started deferred watchdog if needed.
  91. Arguments:
  92. pWatch - Supplies a pointer to a watchdog object.
  93. Return Value:
  94. None.
  95. --*/
  96. {
  97. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  98. ASSERT(NULL != pWatch);
  99. ASSERT(pWatch->Header.ReferenceCount > 0);
  100. //
  101. // Stop deferred watch just in case somebody forgot.
  102. // If the watch is stopped already then this is a no-op.
  103. //
  104. WdStopDeferredWatch(pWatch);
  105. //
  106. // Drop reference count and remove the object if fully dereferenced.
  107. //
  108. if (InterlockedDecrement(&(pWatch->Header.ReferenceCount)) == 0)
  109. {
  110. WdRemoveObject(pWatch);
  111. }
  112. return;
  113. } // WdFreeDeferredWatchdog()
  114. WATCHDOGAPI
  115. VOID
  116. WdStartDeferredWatch(
  117. IN PDEFERRED_WATCHDOG pWatch,
  118. IN PKDPC pDpc,
  119. IN LONG lPeriod
  120. )
  121. /*++
  122. Routine Description:
  123. This function starts deferred watchdog poller.
  124. Arguments:
  125. pWatch - Supplies a pointer to a deferred watchdog object.
  126. pDpc - Supplies a pointer to a control object of type DPC.
  127. ulPeriod - Supplies maximum time in millisecondes that thread
  128. can spend in the monitored section. If this time expires a DPC
  129. will we queued.
  130. Return Value:
  131. None.
  132. --*/
  133. {
  134. KIRQL oldIrql;
  135. LARGE_INTEGER liDueTime;
  136. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  137. ASSERT(NULL != pWatch);
  138. ASSERT(NULL != pDpc);
  139. //
  140. // Raise IRQL to dispatcher level and lock dispatcher database.
  141. //
  142. KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
  143. WD_DBG_SUSPENDED_WARNING(pWatch, "WdStartDeferredWatch");
  144. //
  145. // We shouldn't hot swap DPCs without stopping first.
  146. //
  147. ASSERT((NULL == pWatch->ClientDpc) || (pDpc == pWatch->ClientDpc));
  148. pWatch->Period = lPeriod;
  149. pWatch->InCount = 0;
  150. pWatch->OutCount = 0;
  151. pWatch->LastInCount = 0;
  152. pWatch->LastOutCount = 0;
  153. pWatch->LastKernelTime = 0;
  154. pWatch->LastUserTime = 0;
  155. pWatch->Trigger = 0;
  156. pWatch->Started = TRUE;
  157. pWatch->Thread = NULL;
  158. pWatch->ClientDpc = pDpc;
  159. //
  160. // Unlock the dispatcher database and lower IRQL to its previous value.
  161. //
  162. KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
  163. //
  164. // Set first fire to lPeriod.
  165. //
  166. liDueTime.QuadPart = -(lPeriod * 1000 * 10);
  167. KeSetTimerEx(&(pWatch->Timer), liDueTime, lPeriod, &(pWatch->TimerDpc));
  168. return;
  169. } // WdStartDeferredWatch()
  170. WATCHDOGAPI
  171. VOID
  172. WdStopDeferredWatch(
  173. IN PDEFERRED_WATCHDOG pWatch
  174. )
  175. /*++
  176. Routine Description:
  177. This function stops deferred watchdog poller.
  178. Arguments:
  179. pWatch - Supplies a pointer to a watchdog object.
  180. Return Value:
  181. None.
  182. --*/
  183. {
  184. KIRQL oldIrql;
  185. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  186. ASSERT(NULL != pWatch);
  187. //
  188. // Raise IRQL to dispatcher level and lock dispatcher database.
  189. //
  190. KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
  191. WD_DBG_SUSPENDED_WARNING(pWatch, "WdStopDeferredWatch");
  192. if (TRUE == pWatch->Started)
  193. {
  194. KeCancelTimer(&(pWatch->Timer));
  195. //
  196. // Make sure we don't have client's DPC pending.
  197. //
  198. if (NULL != pWatch->ClientDpc)
  199. {
  200. if (KeRemoveQueueDpc(pWatch->ClientDpc) == TRUE)
  201. {
  202. //
  203. // Was in queue - call WdCompleteEvent() here since DPC won't be delivered.
  204. //
  205. WdCompleteEvent(pWatch, pWatch->Header.LastQueuedThread);
  206. }
  207. }
  208. pWatch->Period = 0;
  209. pWatch->InCount = 0;
  210. pWatch->OutCount = 0;
  211. pWatch->LastInCount = 0;
  212. pWatch->LastOutCount = 0;
  213. pWatch->LastKernelTime = 0;
  214. pWatch->LastUserTime = 0;
  215. pWatch->Trigger = 0;
  216. pWatch->Started = FALSE;
  217. pWatch->Thread = NULL;
  218. pWatch->ClientDpc = NULL;
  219. pWatch->Header.LastEvent = WdNoEvent;
  220. pWatch->Header.LastQueuedThread = NULL;
  221. }
  222. //
  223. // Unlock the dispatcher database and lower IRQL to its previous value.
  224. //
  225. KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
  226. return;
  227. } // WdStopDeferredWatch()
  228. WATCHDOGAPI
  229. VOID
  230. FASTCALL
  231. WdSuspendDeferredWatch(
  232. IN PDEFERRED_WATCHDOG pWatch
  233. )
  234. /*++
  235. Routine Description:
  236. This function suspends deferred watchdog poller.
  237. Arguments:
  238. pWatch - Supplies a pointer to a watchdog object.
  239. Return Value:
  240. None.
  241. --*/
  242. {
  243. ASSERT(NULL != pWatch);
  244. ASSERT((ULONG)(pWatch->SuspendCount) < (ULONG)(-1));
  245. InterlockedIncrement(&(pWatch->SuspendCount));
  246. return;
  247. } // WdSuspendDeferredWatch()
  248. WATCHDOGAPI
  249. VOID
  250. FASTCALL
  251. WdResumeDeferredWatch(
  252. IN PDEFERRED_WATCHDOG pWatch,
  253. IN BOOLEAN bIncremental
  254. )
  255. /*++
  256. Routine Description:
  257. This function resumes deferred watchdog poller.
  258. Arguments:
  259. pWatch - Supplies a pointer to a watchdog object.
  260. bIncremental - If TRUE the watchdog will resume only when
  261. SuspendCount reaches 0, if FALSE watchdog resumes
  262. immediately and SuspendCount is forced to 0.
  263. Return Value:
  264. None.
  265. --*/
  266. {
  267. ASSERT(NULL != pWatch);
  268. if (TRUE == bIncremental)
  269. {
  270. //
  271. // Make sure we won't roll under.
  272. //
  273. if (InterlockedDecrement(&(pWatch->SuspendCount)) == -1)
  274. {
  275. InterlockedIncrement(&(pWatch->SuspendCount));
  276. }
  277. }
  278. else
  279. {
  280. InterlockedExchange(&(pWatch->SuspendCount), 0);
  281. }
  282. return;
  283. } // WdSuspendDeferredWatch()
  284. WATCHDOGAPI
  285. VOID
  286. FASTCALL
  287. WdResetDeferredWatch(
  288. IN PDEFERRED_WATCHDOG pWatch
  289. )
  290. /*++
  291. Routine Description:
  292. This function resets deferred watchdog poller, i.e. it starts
  293. timeout measurement from the scratch if we are in the monitored
  294. section.
  295. Note: If the watchdog is suspened it will remain suspended.
  296. Arguments:
  297. pWatch - Supplies a pointer to a watchdog object.
  298. Return Value:
  299. None.
  300. --*/
  301. {
  302. KIRQL oldIrql;
  303. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  304. ASSERT(NULL != pWatch);
  305. //
  306. // Raise IRQL to dispatcher level and lock dispatcher database.
  307. //
  308. KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
  309. pWatch->InCount = 0;
  310. pWatch->OutCount = 0;
  311. pWatch->Trigger = 0;
  312. //
  313. // Unlock the dispatcher database and lower IRQL to its previous value.
  314. //
  315. KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
  316. return;
  317. } // WdResetDeferredWatch()
  318. WATCHDOGAPI
  319. VOID
  320. FASTCALL
  321. WdEnterMonitoredSection(
  322. IN PDEFERRED_WATCHDOG pWatch
  323. )
  324. /*++
  325. Routine Description:
  326. This function starts monitoring of the code section for time-out
  327. condition.
  328. Note: To minimize an overhead it is caller's resposibility to make
  329. sure thread remains valid when we are in the monitored section.
  330. Arguments:
  331. pWatch - Supplies a pointer to a deferred watchdog object.
  332. Return Value:
  333. None.
  334. --*/
  335. {
  336. PKTHREAD pThread;
  337. KIRQL oldIrql;
  338. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  339. ASSERT(NULL != pWatch);
  340. ASSERT(pWatch->Started);
  341. //
  342. // We have to remove this warning, I hope temporarily, since win32k
  343. // is calling this entry point now with suspended watchdog.
  344. //
  345. // WD_DBG_SUSPENDED_WARNING(pWatch, "WdEnterMonitoredSection");
  346. //
  347. pThread = KeGetCurrentThread();
  348. if (pThread != pWatch->Thread)
  349. {
  350. //
  351. // Raise IRQL to dispatcher level and lock dispatcher database.
  352. //
  353. KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
  354. //
  355. // We shouldn't swap threads in the monitored section.
  356. //
  357. ASSERT(pWatch->OutCount == pWatch->InCount);
  358. pWatch->Trigger = 0;
  359. pWatch->Thread = pThread;
  360. //
  361. // Unlock the dispatcher database and lower IRQL to its previous value.
  362. //
  363. KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
  364. }
  365. InterlockedIncrement(&(pWatch->InCount));
  366. return;
  367. } // WdEnterMonitoredSection()
  368. WATCHDOGAPI
  369. VOID
  370. FASTCALL
  371. WdExitMonitoredSection(
  372. IN PDEFERRED_WATCHDOG pWatch
  373. )
  374. /*++
  375. Routine Description:
  376. This function stops monitoring of the code section for time-out
  377. condition.
  378. Arguments:
  379. pWatch - Supplies a pointer to a deferred watchdog object.
  380. Return Value:
  381. None.
  382. --*/
  383. {
  384. ASSERT(NULL != pWatch);
  385. ASSERT((pWatch->OutCount < pWatch->InCount) ||
  386. ((pWatch->OutCount > 0) && (pWatch->InCount < 0)));
  387. //
  388. // We have to remove this warning, I hope temporarily, since win32k
  389. // is calling this entry point now with suspended watchdog.
  390. //
  391. // WD_DBG_SUSPENDED_WARNING(pWatch, "WdExitMonitoredSection");
  392. //
  393. InterlockedIncrement(&(pWatch->OutCount));
  394. return;
  395. } // WdExitMonitoredSection()
  396. VOID
  397. WdDeferredWatchdogDpcCallback(
  398. IN PKDPC pDpc,
  399. IN PVOID pDeferredContext,
  400. IN PVOID pSystemArgument1,
  401. IN PVOID pSystemArgument2
  402. )
  403. /*++
  404. Routine Description:
  405. This function is a DPC callback routine for timer object embedded in the
  406. deferred watchdog object. It checks thread time and if the wait condition
  407. is satisfied it queues original (client) DPC.
  408. Arguments:
  409. pDpc - Supplies a pointer to a DPC object.
  410. pDeferredContext - Supplies a pointer to a deferred watchdog object.
  411. pSystemArgument1/2 - Supply time when embedded KTIMER expired.
  412. Return Value:
  413. None.
  414. --*/
  415. {
  416. PDEFERRED_WATCHDOG pWatch;
  417. LARGE_INTEGER liThreadTime;
  418. ULONG ulKernelTime;
  419. ULONG ulUserTime;
  420. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  421. ASSERT(NULL != pDeferredContext);
  422. pWatch = (PDEFERRED_WATCHDOG)pDeferredContext;
  423. //
  424. // Lock dispatcher database.
  425. //
  426. KeAcquireSpinLockAtDpcLevel(&(pWatch->Header.SpinLock));
  427. if (NULL != pWatch->Thread)
  428. {
  429. switch (pWatch->Trigger)
  430. {
  431. case 0:
  432. //
  433. // Everything fine so far, check if we are suspended.
  434. //
  435. if (pWatch->SuspendCount)
  436. {
  437. //
  438. // We're suspended - do nothing.
  439. //
  440. break;
  441. }
  442. //
  443. // Check if the last event was a timeout event.
  444. //
  445. if (WdTimeoutEvent == pWatch->Header.LastEvent)
  446. {
  447. //
  448. // Check if we made any progress.
  449. //
  450. if ((pWatch->InCount != pWatch->LastInCount) ||
  451. (pWatch->OutCount != pWatch->LastOutCount) ||
  452. (pWatch->InCount == pWatch->OutCount))
  453. {
  454. //
  455. // We recovered - update event type and queue client DPC.
  456. //
  457. pWatch->Header.LastEvent = WdRecoveryEvent;
  458. if (NULL != pWatch->ClientDpc)
  459. {
  460. //
  461. // Bump up references to objects we're going to touch in client DPC.
  462. //
  463. WdReferenceObject(pWatch);
  464. //
  465. // Queue client DPC.
  466. //
  467. // Note: In case of recovery the thread associated with watchdog
  468. // object may be deleted by the time we get here. We can't pass it
  469. // down to client DPC - we're passing NULL instead.
  470. //
  471. if (KeInsertQueueDpc(pWatch->ClientDpc, NULL, pWatch) == TRUE)
  472. {
  473. //
  474. // Keep track of qeueued thread in case we cancel this DPC.
  475. //
  476. pWatch->Header.LastQueuedThread = NULL;
  477. //
  478. // Make sure we queue DPC only once per event.
  479. //
  480. pWatch->Trigger = 2;
  481. }
  482. else
  483. {
  484. //
  485. // This should never happen.
  486. //
  487. WdDereferenceObject(pWatch);
  488. }
  489. }
  490. }
  491. }
  492. //
  493. // Check if we are in the monitored section.
  494. //
  495. if (pWatch->InCount == pWatch->OutCount)
  496. {
  497. //
  498. // We're outside monitored section - we're fine.
  499. //
  500. break;
  501. }
  502. //
  503. // We're inside monitored section - bump up trigger indicator,
  504. // and take snapshots of counters and thread's time.
  505. //
  506. pWatch->Trigger = 1;
  507. pWatch->LastInCount = pWatch->InCount;
  508. pWatch->LastOutCount = pWatch->OutCount;
  509. pWatch->LastKernelTime = KeQueryRuntimeThread(pWatch->Thread, &(pWatch->LastUserTime));
  510. break;
  511. case 1:
  512. //
  513. // We were in the monitored section last time.
  514. //
  515. //
  516. // Check if we're out or suspended.
  517. //
  518. if ((pWatch->InCount == pWatch->OutCount) || pWatch->SuspendCount)
  519. {
  520. //
  521. // We're outside monitored section or suspended - we're fine.
  522. // Reset trigger counter and get out of here.
  523. //
  524. pWatch->Trigger = 0;
  525. break;
  526. }
  527. //
  528. // Check if we made any progress, if so reset snapshots.
  529. //
  530. if ((pWatch->InCount != pWatch->LastInCount) ||
  531. (pWatch->OutCount != pWatch->LastOutCount))
  532. {
  533. pWatch->Trigger = 1;
  534. pWatch->LastInCount = pWatch->InCount;
  535. pWatch->LastOutCount = pWatch->OutCount;
  536. pWatch->LastKernelTime = KeQueryRuntimeThread(pWatch->Thread, &(pWatch->LastUserTime));
  537. break;
  538. }
  539. //
  540. // Check if we're stuck long enough.
  541. //
  542. ulKernelTime = KeQueryRuntimeThread(pWatch->Thread, &ulUserTime);
  543. switch (pWatch->Header.TimeType)
  544. {
  545. case WdKernelTime:
  546. liThreadTime.QuadPart = ulKernelTime;
  547. //
  548. // Handle counter rollovers.
  549. //
  550. if (ulKernelTime < pWatch->LastKernelTime)
  551. {
  552. liThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastKernelTime + 1;
  553. }
  554. liThreadTime.QuadPart -= pWatch->LastKernelTime;
  555. break;
  556. case WdUserTime:
  557. liThreadTime.QuadPart = ulUserTime;
  558. //
  559. // Handle counter rollovers.
  560. //
  561. if (ulUserTime < pWatch->LastUserTime)
  562. {
  563. liThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastUserTime + 1;
  564. }
  565. liThreadTime.QuadPart -= pWatch->LastUserTime;
  566. break;
  567. case WdFullTime:
  568. liThreadTime.QuadPart = ulKernelTime + ulUserTime;
  569. //
  570. // Handle counter rollovers.
  571. //
  572. if (ulKernelTime < pWatch->LastKernelTime)
  573. {
  574. liThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastKernelTime + 1;
  575. }
  576. if (ulUserTime < pWatch->LastUserTime)
  577. {
  578. liThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastUserTime + 1;
  579. }
  580. liThreadTime.QuadPart -= (pWatch->LastKernelTime + pWatch->LastUserTime);
  581. break;
  582. default:
  583. ASSERT(FALSE);
  584. liThreadTime.QuadPart = 0;
  585. break;
  586. }
  587. //
  588. // Convert to milliseconds.
  589. //
  590. liThreadTime.QuadPart *= pWatch->TimeIncrement;
  591. liThreadTime.QuadPart /= 10000;
  592. if (liThreadTime.QuadPart >= pWatch->Period)
  593. {
  594. //
  595. // We've been stuck long enough - update event type and queue client DPC.
  596. //
  597. pWatch->Header.LastEvent = WdTimeoutEvent;
  598. if (NULL != pWatch->ClientDpc)
  599. {
  600. //
  601. // Bump up references to objects we're going to touch in client DPC.
  602. //
  603. ObReferenceObject(pWatch->Thread);
  604. WdReferenceObject(pWatch);
  605. //
  606. // Queue client DPC.
  607. //
  608. if (KeInsertQueueDpc(pWatch->ClientDpc, pWatch->Thread, pWatch) == TRUE)
  609. {
  610. //
  611. // Keep track of qeueued thread in case we cancel this DPC.
  612. //
  613. pWatch->Header.LastQueuedThread = pWatch->Thread;
  614. //
  615. // Make sure we queue DPC only once per event.
  616. //
  617. pWatch->Trigger = 2;
  618. }
  619. else
  620. {
  621. //
  622. // This should never happen.
  623. //
  624. ObDereferenceObject(pWatch->Thread);
  625. WdDereferenceObject(pWatch);
  626. }
  627. }
  628. }
  629. break;
  630. case 2:
  631. //
  632. // We have event posted waiting for completion. Nothing to do.
  633. //
  634. break;
  635. default:
  636. //
  637. // This should never happen.
  638. //
  639. ASSERT(FALSE);
  640. pWatch->Trigger = 0;
  641. break;
  642. }
  643. }
  644. //
  645. // Unlock the dispatcher database.
  646. //
  647. KeReleaseSpinLockFromDpcLevel(&(pWatch->Header.SpinLock));
  648. return;
  649. } // WdDeferredWatchdogDpcCallback()