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.

826 lines
17 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. watchdog.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 (INIT, DriverEntry)
  17. #pragma alloc_text (PAGE, WdAllocateWatchdog)
  18. #endif
  19. NTSTATUS
  20. DriverEntry(
  21. IN PDRIVER_OBJECT pDriverObject,
  22. IN PUNICODE_STRING wszRegistryPath
  23. )
  24. /*++
  25. Routine Description:
  26. Temporary entry point needed to initialize the watchdog driver.
  27. This function is never called because we are loaded as a DLL
  28. by other drivers.
  29. Arguments:
  30. pDriverObject - Not used.
  31. wszRegistryPath - Not used.
  32. Return Value:
  33. STATUS_SUCCESS
  34. --*/
  35. {
  36. UNREFERENCED_PARAMETER(pDriverObject);
  37. UNREFERENCED_PARAMETER(wszRegistryPath);
  38. ASSERT(FALSE);
  39. return STATUS_SUCCESS;
  40. } // DriverEntry()
  41. WATCHDOGAPI
  42. PWATCHDOG
  43. WdAllocateWatchdog(
  44. IN PDEVICE_OBJECT pDeviceObject,
  45. IN WD_TIME_TYPE timeType,
  46. IN ULONG ulTag
  47. )
  48. /*++
  49. Routine Description:
  50. This function allocates storage and initializes
  51. a watchdog object.
  52. Arguments:
  53. pDeviceObject - Points to DEVICE_OBJECT associated with watchdog.
  54. timeType - Kernel, User, Both thread time to monitor.
  55. ulTag - A tag identifying owner.
  56. Return Value:
  57. Pointer to allocated watchdog object or NULL.
  58. --*/
  59. {
  60. PWATCHDOG pWatch;
  61. PAGED_CODE();
  62. ASSERT((timeType >= WdKernelTime) && (timeType <= WdFullTime));
  63. //
  64. // Allocate storage for watchdog object from non-paged pool.
  65. //
  66. pWatch = (PWATCHDOG)ExAllocatePoolWithTag(NonPagedPool, sizeof (WATCHDOG), ulTag);
  67. //
  68. // Set initial state of watchdog object.
  69. //
  70. if (NULL != pWatch)
  71. {
  72. //
  73. // Set initial state of watchdog.
  74. //
  75. WdInitializeObject(pWatch,
  76. pDeviceObject,
  77. WdStandardWatchdog,
  78. timeType,
  79. ulTag);
  80. pWatch->StartCount = 0;
  81. pWatch->SuspendCount = 0;
  82. pWatch->LastKernelTime = 0;
  83. pWatch->LastUserTime = 0;
  84. pWatch->TimeIncrement = KeQueryTimeIncrement();
  85. pWatch->DueTime.QuadPart = 0;
  86. pWatch->InitialDueTime.QuadPart = 0;
  87. pWatch->Thread = NULL;
  88. pWatch->ClientDpc = NULL;
  89. //
  90. // Initialize encapsulated timer object.
  91. //
  92. KeInitializeTimerEx(&(pWatch->Timer), NotificationTimer);
  93. //
  94. // Initialize encapsulated DPC object.
  95. //
  96. KeInitializeDpc(&(pWatch->TimerDpc), WdWatchdogDpcCallback, pWatch);
  97. }
  98. return pWatch;
  99. } // WdAllocateWatchdog()
  100. WATCHDOGAPI
  101. VOID
  102. WdFreeWatchdog(
  103. PWATCHDOG pWatch
  104. )
  105. /*++
  106. Routine Description:
  107. This function deallocates storage for watchdog object.
  108. It will also stop started watchdog if needed.
  109. Arguments:
  110. pWatch - Supplies a pointer to a watchdog object.
  111. Return Value:
  112. None.
  113. --*/
  114. {
  115. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  116. ASSERT(NULL != pWatch);
  117. ASSERT(pWatch->Header.ReferenceCount > 0);
  118. //
  119. // Stop watch just in case somebody forgot.
  120. // If the watch is stopped already then this is a no-op.
  121. //
  122. WdStopWatch(pWatch, FALSE);
  123. if (InterlockedDecrement(&(pWatch->Header.ReferenceCount)) == 0)
  124. {
  125. WdRemoveObject(pWatch);
  126. }
  127. return;
  128. } // WdFreeWatchdog()
  129. WATCHDOGAPI
  130. VOID
  131. WdStartWatch(
  132. IN PWATCHDOG pWatch,
  133. IN LARGE_INTEGER liDueTime,
  134. IN PKDPC pDpc
  135. )
  136. /*++
  137. Routine Description:
  138. This function sets a watchdog to expire at a specified time. This
  139. function also increments start count of the watchdog object, to allow
  140. nested calls to Set / Cancel functions.
  141. Note: To minimize an overhead it is caller's resposibility to make
  142. sure thread remains valid when we are in the monitored section.
  143. Arguments:
  144. pWatch - Supplies a pointer to a watchdog object.
  145. liDueTime - Supplies relative time at which the timer is to expire.
  146. This time is in the 100ns units.
  147. pDpc - Supplies a pointer to a control object of type DPC.
  148. Return Value:
  149. None.
  150. --*/
  151. {
  152. PKTHREAD pThread;
  153. KIRQL oldIrql;
  154. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  155. ASSERT(NULL != pWatch);
  156. ASSERT(NULL != pDpc);
  157. //
  158. // Make sure we use a relative DueTime.
  159. //
  160. if (liDueTime.QuadPart > 0)
  161. {
  162. liDueTime.QuadPart = -liDueTime.QuadPart;
  163. }
  164. //
  165. // Raise IRQL to dispatcher level and lock dispatcher database.
  166. //
  167. KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
  168. WD_DBG_SUSPENDED_WARNING(pWatch, "WdStartWatch");
  169. if (pWatch->StartCount < (ULONG)(-1))
  170. {
  171. pWatch->StartCount++;
  172. }
  173. else
  174. {
  175. ASSERT(FALSE);
  176. }
  177. //
  178. // We shouldn't hot swap DPCs without stopping first.
  179. //
  180. ASSERT((NULL == pWatch->ClientDpc) || (pDpc == pWatch->ClientDpc));
  181. pThread = KeGetCurrentThread();
  182. //
  183. // We shouldn't swap threads in the monitored section.
  184. //
  185. ASSERT((pWatch->StartCount == 1) || (pThread == pWatch->Thread));
  186. pWatch->Thread = pThread;
  187. pWatch->ClientDpc = pDpc;
  188. pWatch->DueTime.QuadPart = liDueTime.QuadPart;
  189. pWatch->InitialDueTime.QuadPart = liDueTime.QuadPart;
  190. pWatch->LastKernelTime = KeQueryRuntimeThread(pThread, &(pWatch->LastUserTime));
  191. //
  192. // Make sure ULONG counters won't overflow.
  193. //
  194. if (liDueTime.QuadPart < -WD_MAX_WAIT)
  195. {
  196. liDueTime.QuadPart = -WD_MAX_WAIT;
  197. }
  198. if (0 == pWatch->SuspendCount)
  199. {
  200. KeSetTimerEx(&(pWatch->Timer), liDueTime, 0, &(pWatch->TimerDpc));
  201. }
  202. //
  203. // Unlock the dispatcher database and lower IRQL to its previous value.
  204. //
  205. KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
  206. return;
  207. } // WdStartWatch()
  208. WATCHDOGAPI
  209. VOID
  210. WdStopWatch(
  211. IN PWATCHDOG pWatch,
  212. IN BOOLEAN bIncremental
  213. )
  214. /*++
  215. Routine Description:
  216. This function cancels a watchdog that was previously set to expire
  217. at a specified time. If the watchdog is not currently set, then
  218. no operation is performed.
  219. Arguments:
  220. pWatch - Supplies a pointer to a watchdog object.
  221. bIncremental - If TRUE the watchdog will be cancelled only when
  222. ReferenceCounter reaches 0, if FALSE watchdog is cancelled
  223. immediately and ReferenceCounter is forced to 0.
  224. Return Value:
  225. None.
  226. --*/
  227. {
  228. KIRQL oldIrql;
  229. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  230. ASSERT(NULL != pWatch);
  231. //
  232. // Raise IRQL to dispatcher level and lock dispatcher database.
  233. //
  234. KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
  235. WD_DBG_SUSPENDED_WARNING(pWatch, "WdStopWatch");
  236. if (pWatch->StartCount > 0)
  237. {
  238. if (TRUE == bIncremental)
  239. {
  240. pWatch->StartCount--;
  241. }
  242. else
  243. {
  244. pWatch->StartCount = 0;
  245. }
  246. if (0 == pWatch->StartCount)
  247. {
  248. //
  249. // Cancel encapsulated timer object.
  250. //
  251. KeCancelTimer(&(pWatch->Timer));
  252. //
  253. // Make sure we don't have client's DPC pending.
  254. //
  255. if (NULL != pWatch->ClientDpc)
  256. {
  257. if (KeRemoveQueueDpc(pWatch->ClientDpc) == TRUE)
  258. {
  259. //
  260. // Was in queue - call WdCompleteEvent() here since DPC won't be delivered.
  261. //
  262. WdCompleteEvent(pWatch, pWatch->Header.LastQueuedThread);
  263. }
  264. }
  265. //
  266. // Set initial state of timer per thread.
  267. //
  268. pWatch->LastKernelTime = 0;
  269. pWatch->LastUserTime = 0;
  270. pWatch->DueTime.QuadPart = 0;
  271. pWatch->InitialDueTime.QuadPart = 0;
  272. pWatch->Thread = NULL;
  273. pWatch->ClientDpc = NULL;
  274. pWatch->Header.LastEvent = WdNoEvent;
  275. pWatch->Header.LastQueuedThread = NULL;
  276. }
  277. }
  278. //
  279. // Unlock the dispatcher database and lower IRQL to its previous value.
  280. //
  281. KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
  282. return;
  283. } // WdStopWatch()
  284. WATCHDOGAPI
  285. VOID
  286. WdSuspendWatch(
  287. IN PWATCHDOG pWatch
  288. )
  289. /*++
  290. Routine Description:
  291. This function suspends watchdog.
  292. Arguments:
  293. pWatch - Supplies a pointer to a watchdog object.
  294. Return Value:
  295. None.
  296. --*/
  297. {
  298. KIRQL oldIrql;
  299. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  300. ASSERT(NULL != pWatch);
  301. //
  302. // Raise IRQL to dispatcher level and lock dispatcher database.
  303. //
  304. KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
  305. ASSERT(pWatch->SuspendCount < (ULONG)(-1));
  306. //
  307. // If we are suspended for the first time and we have timer running
  308. // we havo to stop a timer.
  309. //
  310. if ((0 == pWatch->SuspendCount) && pWatch->StartCount)
  311. {
  312. KeCancelTimer(&(pWatch->Timer));
  313. }
  314. pWatch->SuspendCount++;
  315. //
  316. // Unlock the dispatcher database and lower IRQL to its previous value.
  317. //
  318. KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
  319. return;
  320. } // WdSuspendWatch()
  321. WATCHDOGAPI
  322. VOID
  323. WdResumeWatch(
  324. IN PWATCHDOG pWatch,
  325. IN BOOLEAN bIncremental
  326. )
  327. /*++
  328. Routine Description:
  329. This function resumes watchdog.
  330. Arguments:
  331. pWatch - Supplies a pointer to a watchdog object.
  332. bIncremental - If TRUE the watchdog will resume only when
  333. SuspendCount reaches 0, if FALSE watchdog resumes
  334. immediately and SuspendCount is forced to 0.
  335. Return Value:
  336. None.
  337. --*/
  338. {
  339. KIRQL oldIrql;
  340. BOOLEAN bResumed = FALSE;
  341. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  342. ASSERT(NULL != pWatch);
  343. //
  344. // Raise IRQL to dispatcher level and lock dispatcher database.
  345. //
  346. KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
  347. if (TRUE == bIncremental)
  348. {
  349. if (pWatch->SuspendCount)
  350. {
  351. pWatch->SuspendCount--;
  352. if (0 == pWatch->SuspendCount)
  353. {
  354. bResumed = TRUE;
  355. }
  356. }
  357. }
  358. else
  359. {
  360. if (pWatch->SuspendCount)
  361. {
  362. pWatch->SuspendCount = 0;
  363. bResumed = TRUE;
  364. }
  365. }
  366. //
  367. // If we had a timer running, and we are resuming for the first time,
  368. // and still have some due time left, we'll have to restart timer.
  369. //
  370. if (pWatch->StartCount && (TRUE == bResumed) && (0 != pWatch->DueTime.QuadPart))
  371. {
  372. LARGE_INTEGER liDueTime;
  373. //
  374. // Refresh currect time.
  375. //
  376. pWatch->LastKernelTime = KeQueryRuntimeThread(pWatch->Thread, &(pWatch->LastUserTime));
  377. //
  378. // Make sure ULONG counters won't overflow.
  379. //
  380. liDueTime.QuadPart = pWatch->DueTime.QuadPart;
  381. if (liDueTime.QuadPart < -WD_MAX_WAIT)
  382. {
  383. liDueTime.QuadPart = -WD_MAX_WAIT;
  384. }
  385. KeSetTimerEx(&(pWatch->Timer), liDueTime, 0, &(pWatch->TimerDpc));
  386. }
  387. //
  388. // Unlock the dispatcher database and lower IRQL to its previous value.
  389. //
  390. KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
  391. return;
  392. } // WdSuspendWatch()
  393. WATCHDOGAPI
  394. VOID
  395. WdResetWatch(
  396. IN PWATCHDOG pWatch
  397. )
  398. /*++
  399. Routine Description:
  400. This function resets a started watchdog, i.e. it restarts timeout
  401. measurement from the scratch.
  402. Note: If the watchdog is suspened it will remain suspended.
  403. Arguments:
  404. pWatch - Supplies a pointer to a watchdog object.
  405. Return Value:
  406. None.
  407. --*/
  408. {
  409. KIRQL oldIrql;
  410. ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
  411. ASSERT(NULL != pWatch);
  412. //
  413. // Raise IRQL to dispatcher level and lock dispatcher database.
  414. //
  415. KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
  416. if (pWatch->StartCount)
  417. {
  418. LARGE_INTEGER liDueTime;
  419. pWatch->DueTime.QuadPart = pWatch->InitialDueTime.QuadPart;
  420. pWatch->LastKernelTime = KeQueryRuntimeThread(pWatch->Thread, &(pWatch->LastUserTime));
  421. //
  422. // Make sure ULONG counters won't overflow.
  423. //
  424. liDueTime.QuadPart = pWatch->DueTime.QuadPart;
  425. if (liDueTime.QuadPart < -WD_MAX_WAIT)
  426. {
  427. liDueTime.QuadPart = -WD_MAX_WAIT;
  428. }
  429. if (0 == pWatch->SuspendCount)
  430. {
  431. KeSetTimerEx(&(pWatch->Timer), liDueTime, 0, &(pWatch->TimerDpc));
  432. }
  433. }
  434. //
  435. // Unlock the dispatcher database and lower IRQL to its previous value.
  436. //
  437. KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
  438. return;
  439. } // WdResetWatch()
  440. VOID
  441. WdWatchdogDpcCallback(
  442. IN PKDPC pDpc,
  443. IN PVOID pContext,
  444. IN PVOID pSystemArgument1,
  445. IN PVOID pSystemArgument2
  446. )
  447. /*++
  448. Routine Description:
  449. This function is a DPC callback routine for timer object embedded in the
  450. watchdog object. It checks thread time and if the wait condition is
  451. satisfied it queues original (client) DPC. In case if the wait condition
  452. is not yet satisfied it call KeSetTimerEx().
  453. Arguments:
  454. pDpc - Supplies a pointer to a DPC object.
  455. pContext - Supplies a pointer to a watchdog object.
  456. pSystemArgument1/2 - Supply time when embedded KTIMER expired.
  457. Return Value:
  458. None.
  459. --*/
  460. {
  461. PWATCHDOG pWatch;
  462. ULARGE_INTEGER uliThreadTime;
  463. LARGE_INTEGER liDelta;
  464. ULONG ulKernelTime;
  465. ULONG ulUserTime;
  466. ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
  467. ASSERT(NULL != pContext);
  468. pWatch = (PWATCHDOG)pContext;
  469. KeAcquireSpinLockAtDpcLevel(&(pWatch->Header.SpinLock));
  470. ASSERT(0 == pWatch->SuspendCount);
  471. //
  472. // Get thread's current time stamps.
  473. //
  474. ulKernelTime = KeQueryRuntimeThread(pWatch->Thread, &ulUserTime);
  475. switch (pWatch->Header.TimeType)
  476. {
  477. case WdKernelTime:
  478. uliThreadTime.QuadPart = ulKernelTime;
  479. //
  480. // Handle counter rollovers.
  481. //
  482. if (ulKernelTime < pWatch->LastKernelTime)
  483. {
  484. uliThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastKernelTime + 1;
  485. }
  486. liDelta.QuadPart = uliThreadTime.QuadPart - pWatch->LastKernelTime;
  487. break;
  488. case WdUserTime:
  489. uliThreadTime.QuadPart = ulUserTime;
  490. //
  491. // Handle counter rollovers.
  492. //
  493. if (ulUserTime < pWatch->LastUserTime)
  494. {
  495. uliThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastUserTime + 1;
  496. }
  497. liDelta.QuadPart = uliThreadTime.QuadPart - pWatch->LastUserTime;
  498. break;
  499. case WdFullTime:
  500. uliThreadTime.QuadPart = ulKernelTime + ulUserTime;
  501. //
  502. // Handle counter rollovers.
  503. //
  504. if (ulKernelTime < pWatch->LastKernelTime)
  505. {
  506. uliThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastKernelTime + 1;
  507. }
  508. if (ulUserTime < pWatch->LastUserTime)
  509. {
  510. uliThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastUserTime + 1;
  511. }
  512. liDelta.QuadPart = uliThreadTime.QuadPart - (pWatch->LastKernelTime +
  513. pWatch->LastUserTime);
  514. break;
  515. default:
  516. ASSERT(FALSE);
  517. liDelta.QuadPart = 0;
  518. break;
  519. }
  520. liDelta.QuadPart *= pWatch->TimeIncrement;
  521. //
  522. // Update time values stored in timer per thread object to current values.
  523. //
  524. pWatch->LastKernelTime = ulKernelTime;
  525. pWatch->LastUserTime = ulUserTime;
  526. pWatch->DueTime.QuadPart += liDelta.QuadPart;
  527. if (pWatch->DueTime.QuadPart >= 0)
  528. {
  529. //
  530. // We're done waiting - update event type and queue client DPC if defined.
  531. //
  532. pWatch->Header.LastEvent = WdTimeoutEvent;
  533. if (NULL != pWatch->ClientDpc)
  534. {
  535. //
  536. // Bump up references to objects we're going to touch in client DPC.
  537. //
  538. ObReferenceObject(pWatch->Thread);
  539. WdReferenceObject(pWatch);
  540. if (KeInsertQueueDpc(pWatch->ClientDpc, pWatch->Thread, pWatch) == FALSE)
  541. {
  542. //
  543. // Already in queue, drop references.
  544. //
  545. ObDereferenceObject(pWatch->Thread);
  546. WdDereferenceObject(pWatch);
  547. }
  548. else
  549. {
  550. //
  551. // Keep track of qeueued thread in case we cancel this DPC.
  552. //
  553. pWatch->Header.LastQueuedThread = pWatch->Thread;
  554. }
  555. }
  556. //
  557. // Make sure due time is zero (in case of suspend / resume).
  558. //
  559. pWatch->DueTime.QuadPart = 0;
  560. }
  561. else
  562. {
  563. //
  564. // Not there yet - wait some more.
  565. //
  566. liDelta.QuadPart = pWatch->DueTime.QuadPart;
  567. //
  568. // Make sure ULONG counters won't overflow.
  569. //
  570. if (liDelta.QuadPart < -WD_MAX_WAIT)
  571. {
  572. liDelta.QuadPart = -WD_MAX_WAIT;
  573. }
  574. KeSetTimerEx(&(pWatch->Timer), liDelta, 0, &(pWatch->TimerDpc));
  575. }
  576. KeReleaseSpinLockFromDpcLevel(&(pWatch->Header.SpinLock));
  577. return;
  578. } // WdWatchdogDpcCallback()