919 lines
20 KiB
C
919 lines
20 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 2000 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
dwd.c
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This is the NT Watchdog driver implementation.
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Michael Maciesowicz (mmacie) 05-May-2000
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
Kernel mode only.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include "wd.h"
|
||
|
|
||
|
#ifdef ALLOC_PRAGMA
|
||
|
#pragma alloc_text (PAGE, WdAllocateDeferredWatchdog)
|
||
|
#endif
|
||
|
|
||
|
|
||
|
WATCHDOGAPI
|
||
|
PDEFERRED_WATCHDOG
|
||
|
WdAllocateDeferredWatchdog(
|
||
|
IN PDEVICE_OBJECT pDeviceObject,
|
||
|
IN WD_TIME_TYPE timeType,
|
||
|
IN ULONG ulTag
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function allocates storage and initializes
|
||
|
a deferred watchdog object.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pDeviceObject - Points to DEVICE_OBJECT associated with watchdog.
|
||
|
|
||
|
timeType - Kernel, User, Both thread time to monitor.
|
||
|
|
||
|
ulTag - A tag identifying owner.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
Pointer to allocated deferred watchdog object or NULL.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PDEFERRED_WATCHDOG pWatch;
|
||
|
|
||
|
PAGED_CODE();
|
||
|
ASSERT(NULL != pDeviceObject);
|
||
|
ASSERT((timeType >= WdKernelTime) && (timeType <= WdFullTime));
|
||
|
|
||
|
//
|
||
|
// Allocate storage for deferred watchdog from non-paged pool.
|
||
|
//
|
||
|
|
||
|
pWatch = (PDEFERRED_WATCHDOG)ExAllocatePoolWithTag(NonPagedPool, sizeof (DEFERRED_WATCHDOG), ulTag);
|
||
|
|
||
|
//
|
||
|
// Set initial state of deferred watchdog.
|
||
|
//
|
||
|
|
||
|
if (NULL != pWatch)
|
||
|
{
|
||
|
//
|
||
|
// Set initial state of watchdog.
|
||
|
//
|
||
|
|
||
|
WdInitializeObject(pWatch,
|
||
|
pDeviceObject,
|
||
|
WdDeferredWatchdog,
|
||
|
timeType,
|
||
|
ulTag);
|
||
|
|
||
|
pWatch->Period = 0;
|
||
|
pWatch->SuspendCount = 0;
|
||
|
pWatch->InCount = 0;
|
||
|
pWatch->OutCount = 0;
|
||
|
pWatch->LastInCount = 0;
|
||
|
pWatch->LastOutCount = 0;
|
||
|
pWatch->LastKernelTime = 0;
|
||
|
pWatch->LastUserTime = 0;
|
||
|
pWatch->TimeIncrement = KeQueryTimeIncrement();
|
||
|
pWatch->Trigger = 0;
|
||
|
pWatch->Started = FALSE;
|
||
|
pWatch->Thread = NULL;
|
||
|
pWatch->ClientDpc = NULL;
|
||
|
|
||
|
//
|
||
|
// Initialize encapsulated DPC object.
|
||
|
//
|
||
|
|
||
|
KeInitializeDpc(&(pWatch->TimerDpc), WdDeferredWatchdogDpcCallback, pWatch);
|
||
|
|
||
|
//
|
||
|
// Initialize encapsulated timer object.
|
||
|
//
|
||
|
|
||
|
KeInitializeTimerEx(&(pWatch->Timer), NotificationTimer);
|
||
|
}
|
||
|
|
||
|
return pWatch;
|
||
|
} // WdAllocateDeferredWatchdog()
|
||
|
|
||
|
WATCHDOGAPI
|
||
|
VOID
|
||
|
WdFreeDeferredWatchdog(
|
||
|
PDEFERRED_WATCHDOG pWatch
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function deallocates storage for deferred watchdog object.
|
||
|
It will also stop started deferred watchdog if needed.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pWatch - Supplies a pointer to a watchdog object.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
||
|
ASSERT(NULL != pWatch);
|
||
|
ASSERT(pWatch->Header.ReferenceCount > 0);
|
||
|
|
||
|
//
|
||
|
// Stop deferred watch just in case somebody forgot.
|
||
|
// If the watch is stopped already then this is a no-op.
|
||
|
//
|
||
|
|
||
|
WdStopDeferredWatch(pWatch);
|
||
|
|
||
|
//
|
||
|
// Drop reference count and remove the object if fully dereferenced.
|
||
|
//
|
||
|
|
||
|
if (InterlockedDecrement(&(pWatch->Header.ReferenceCount)) == 0)
|
||
|
{
|
||
|
WdRemoveObject(pWatch);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
} // WdFreeDeferredWatchdog()
|
||
|
|
||
|
WATCHDOGAPI
|
||
|
VOID
|
||
|
WdStartDeferredWatch(
|
||
|
IN PDEFERRED_WATCHDOG pWatch,
|
||
|
IN PKDPC pDpc,
|
||
|
IN LONG lPeriod
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function starts deferred watchdog poller.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pWatch - Supplies a pointer to a deferred watchdog object.
|
||
|
|
||
|
pDpc - Supplies a pointer to a control object of type DPC.
|
||
|
|
||
|
ulPeriod - Supplies maximum time in millisecondes that thread
|
||
|
can spend in the monitored section. If this time expires a DPC
|
||
|
will we queued.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
KIRQL oldIrql;
|
||
|
LARGE_INTEGER liDueTime;
|
||
|
|
||
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
||
|
ASSERT(NULL != pWatch);
|
||
|
ASSERT(NULL != pDpc);
|
||
|
|
||
|
//
|
||
|
// Raise IRQL to dispatcher level and lock dispatcher database.
|
||
|
//
|
||
|
|
||
|
KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
|
||
|
|
||
|
WD_DBG_SUSPENDED_WARNING(pWatch, "WdStartDeferredWatch");
|
||
|
|
||
|
//
|
||
|
// We shouldn't hot swap DPCs without stopping first.
|
||
|
//
|
||
|
|
||
|
ASSERT((NULL == pWatch->ClientDpc) || (pDpc == pWatch->ClientDpc));
|
||
|
|
||
|
pWatch->Period = lPeriod;
|
||
|
pWatch->InCount = 0;
|
||
|
pWatch->OutCount = 0;
|
||
|
pWatch->LastInCount = 0;
|
||
|
pWatch->LastOutCount = 0;
|
||
|
pWatch->LastKernelTime = 0;
|
||
|
pWatch->LastUserTime = 0;
|
||
|
pWatch->Trigger = 0;
|
||
|
pWatch->Started = TRUE;
|
||
|
pWatch->Thread = NULL;
|
||
|
pWatch->ClientDpc = pDpc;
|
||
|
|
||
|
//
|
||
|
// Unlock the dispatcher database and lower IRQL to its previous value.
|
||
|
//
|
||
|
|
||
|
KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
|
||
|
|
||
|
//
|
||
|
// Set first fire to lPeriod.
|
||
|
//
|
||
|
|
||
|
liDueTime.QuadPart = -(lPeriod * 1000 * 10);
|
||
|
|
||
|
KeSetTimerEx(&(pWatch->Timer), liDueTime, lPeriod, &(pWatch->TimerDpc));
|
||
|
|
||
|
return;
|
||
|
} // WdStartDeferredWatch()
|
||
|
|
||
|
WATCHDOGAPI
|
||
|
VOID
|
||
|
WdStopDeferredWatch(
|
||
|
IN PDEFERRED_WATCHDOG pWatch
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function stops deferred watchdog poller.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pWatch - Supplies a pointer to a watchdog object.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
KIRQL oldIrql;
|
||
|
|
||
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
||
|
ASSERT(NULL != pWatch);
|
||
|
|
||
|
//
|
||
|
// Raise IRQL to dispatcher level and lock dispatcher database.
|
||
|
//
|
||
|
|
||
|
KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
|
||
|
|
||
|
WD_DBG_SUSPENDED_WARNING(pWatch, "WdStopDeferredWatch");
|
||
|
|
||
|
if (TRUE == pWatch->Started)
|
||
|
{
|
||
|
KeCancelTimer(&(pWatch->Timer));
|
||
|
|
||
|
//
|
||
|
// Make sure we don't have client's DPC pending.
|
||
|
//
|
||
|
|
||
|
if (NULL != pWatch->ClientDpc)
|
||
|
{
|
||
|
if (KeRemoveQueueDpc(pWatch->ClientDpc) == TRUE)
|
||
|
{
|
||
|
//
|
||
|
// Was in queue - call WdCompleteEvent() here since DPC won't be delivered.
|
||
|
//
|
||
|
|
||
|
WdCompleteEvent(pWatch, pWatch->Header.LastQueuedThread);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pWatch->Period = 0;
|
||
|
pWatch->InCount = 0;
|
||
|
pWatch->OutCount = 0;
|
||
|
pWatch->LastInCount = 0;
|
||
|
pWatch->LastOutCount = 0;
|
||
|
pWatch->LastKernelTime = 0;
|
||
|
pWatch->LastUserTime = 0;
|
||
|
pWatch->Trigger = 0;
|
||
|
pWatch->Started = FALSE;
|
||
|
pWatch->Thread = NULL;
|
||
|
pWatch->ClientDpc = NULL;
|
||
|
pWatch->Header.LastEvent = WdNoEvent;
|
||
|
pWatch->Header.LastQueuedThread = NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Unlock the dispatcher database and lower IRQL to its previous value.
|
||
|
//
|
||
|
|
||
|
KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
|
||
|
|
||
|
return;
|
||
|
} // WdStopDeferredWatch()
|
||
|
|
||
|
WATCHDOGAPI
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
WdSuspendDeferredWatch(
|
||
|
IN PDEFERRED_WATCHDOG pWatch
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function suspends deferred watchdog poller.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pWatch - Supplies a pointer to a watchdog object.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
ASSERT(NULL != pWatch);
|
||
|
ASSERT((ULONG)(pWatch->SuspendCount) < (ULONG)(-1));
|
||
|
|
||
|
InterlockedIncrement(&(pWatch->SuspendCount));
|
||
|
|
||
|
return;
|
||
|
} // WdSuspendDeferredWatch()
|
||
|
|
||
|
WATCHDOGAPI
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
WdResumeDeferredWatch(
|
||
|
IN PDEFERRED_WATCHDOG pWatch,
|
||
|
IN BOOLEAN bIncremental
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function resumes deferred watchdog poller.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pWatch - Supplies a pointer to a watchdog object.
|
||
|
|
||
|
bIncremental - If TRUE the watchdog will resume only when
|
||
|
SuspendCount reaches 0, if FALSE watchdog resumes
|
||
|
immediately and SuspendCount is forced to 0.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
ASSERT(NULL != pWatch);
|
||
|
|
||
|
if (TRUE == bIncremental)
|
||
|
{
|
||
|
//
|
||
|
// Make sure we won't roll under.
|
||
|
//
|
||
|
|
||
|
if (InterlockedDecrement(&(pWatch->SuspendCount)) == -1)
|
||
|
{
|
||
|
InterlockedIncrement(&(pWatch->SuspendCount));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
InterlockedExchange(&(pWatch->SuspendCount), 0);
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
} // WdSuspendDeferredWatch()
|
||
|
|
||
|
WATCHDOGAPI
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
WdResetDeferredWatch(
|
||
|
IN PDEFERRED_WATCHDOG pWatch
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function resets deferred watchdog poller, i.e. it starts
|
||
|
timeout measurement from the scratch if we are in the monitored
|
||
|
section.
|
||
|
Note: If the watchdog is suspened it will remain suspended.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pWatch - Supplies a pointer to a watchdog object.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
KIRQL oldIrql;
|
||
|
|
||
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
||
|
ASSERT(NULL != pWatch);
|
||
|
|
||
|
//
|
||
|
// Raise IRQL to dispatcher level and lock dispatcher database.
|
||
|
//
|
||
|
|
||
|
KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
|
||
|
|
||
|
pWatch->InCount = 0;
|
||
|
pWatch->OutCount = 0;
|
||
|
pWatch->Trigger = 0;
|
||
|
|
||
|
//
|
||
|
// Unlock the dispatcher database and lower IRQL to its previous value.
|
||
|
//
|
||
|
|
||
|
KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
|
||
|
|
||
|
return;
|
||
|
} // WdResetDeferredWatch()
|
||
|
|
||
|
WATCHDOGAPI
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
WdEnterMonitoredSection(
|
||
|
IN PDEFERRED_WATCHDOG pWatch
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function starts monitoring of the code section for time-out
|
||
|
condition.
|
||
|
|
||
|
Note: To minimize an overhead it is caller's resposibility to make
|
||
|
sure thread remains valid when we are in the monitored section.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pWatch - Supplies a pointer to a deferred watchdog object.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PKTHREAD pThread;
|
||
|
KIRQL oldIrql;
|
||
|
|
||
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
||
|
ASSERT(NULL != pWatch);
|
||
|
ASSERT(pWatch->Started);
|
||
|
|
||
|
//
|
||
|
// We have to remove this warning, I hope temporarily, since win32k
|
||
|
// is calling this entry point now with suspended watchdog.
|
||
|
//
|
||
|
// WD_DBG_SUSPENDED_WARNING(pWatch, "WdEnterMonitoredSection");
|
||
|
//
|
||
|
|
||
|
pThread = KeGetCurrentThread();
|
||
|
|
||
|
if (pThread != pWatch->Thread)
|
||
|
{
|
||
|
//
|
||
|
// Raise IRQL to dispatcher level and lock dispatcher database.
|
||
|
//
|
||
|
|
||
|
KeAcquireSpinLock(&(pWatch->Header.SpinLock), &oldIrql);
|
||
|
|
||
|
//
|
||
|
// We shouldn't swap threads in the monitored section.
|
||
|
//
|
||
|
|
||
|
ASSERT(pWatch->OutCount == pWatch->InCount);
|
||
|
|
||
|
pWatch->Trigger = 0;
|
||
|
pWatch->Thread = pThread;
|
||
|
|
||
|
//
|
||
|
// Unlock the dispatcher database and lower IRQL to its previous value.
|
||
|
//
|
||
|
|
||
|
KeReleaseSpinLock(&(pWatch->Header.SpinLock), oldIrql);
|
||
|
}
|
||
|
|
||
|
InterlockedIncrement(&(pWatch->InCount));
|
||
|
|
||
|
return;
|
||
|
} // WdEnterMonitoredSection()
|
||
|
|
||
|
WATCHDOGAPI
|
||
|
VOID
|
||
|
FASTCALL
|
||
|
WdExitMonitoredSection(
|
||
|
IN PDEFERRED_WATCHDOG pWatch
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function stops monitoring of the code section for time-out
|
||
|
condition.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pWatch - Supplies a pointer to a deferred watchdog object.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
ASSERT(NULL != pWatch);
|
||
|
ASSERT((pWatch->OutCount < pWatch->InCount) ||
|
||
|
((pWatch->OutCount > 0) && (pWatch->InCount < 0)));
|
||
|
|
||
|
//
|
||
|
// We have to remove this warning, I hope temporarily, since win32k
|
||
|
// is calling this entry point now with suspended watchdog.
|
||
|
//
|
||
|
// WD_DBG_SUSPENDED_WARNING(pWatch, "WdExitMonitoredSection");
|
||
|
//
|
||
|
|
||
|
InterlockedIncrement(&(pWatch->OutCount));
|
||
|
|
||
|
return;
|
||
|
} // WdExitMonitoredSection()
|
||
|
|
||
|
VOID
|
||
|
WdDeferredWatchdogDpcCallback(
|
||
|
IN PKDPC pDpc,
|
||
|
IN PVOID pDeferredContext,
|
||
|
IN PVOID pSystemArgument1,
|
||
|
IN PVOID pSystemArgument2
|
||
|
)
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
This function is a DPC callback routine for timer object embedded in the
|
||
|
deferred watchdog object. It checks thread time and if the wait condition
|
||
|
is satisfied it queues original (client) DPC.
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pDpc - Supplies a pointer to a DPC object.
|
||
|
|
||
|
pDeferredContext - Supplies a pointer to a deferred watchdog object.
|
||
|
|
||
|
pSystemArgument1/2 - Supply time when embedded KTIMER expired.
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None.
|
||
|
|
||
|
--*/
|
||
|
|
||
|
{
|
||
|
PDEFERRED_WATCHDOG pWatch;
|
||
|
LARGE_INTEGER liThreadTime;
|
||
|
ULONG ulKernelTime;
|
||
|
ULONG ulUserTime;
|
||
|
|
||
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
||
|
ASSERT(NULL != pDeferredContext);
|
||
|
|
||
|
pWatch = (PDEFERRED_WATCHDOG)pDeferredContext;
|
||
|
|
||
|
//
|
||
|
// Lock dispatcher database.
|
||
|
//
|
||
|
|
||
|
KeAcquireSpinLockAtDpcLevel(&(pWatch->Header.SpinLock));
|
||
|
|
||
|
if (NULL != pWatch->Thread)
|
||
|
{
|
||
|
switch (pWatch->Trigger)
|
||
|
{
|
||
|
case 0:
|
||
|
|
||
|
//
|
||
|
// Everything fine so far, check if we are suspended.
|
||
|
//
|
||
|
|
||
|
if (pWatch->SuspendCount)
|
||
|
{
|
||
|
//
|
||
|
// We're suspended - do nothing.
|
||
|
//
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check if the last event was a timeout event.
|
||
|
//
|
||
|
|
||
|
if (WdTimeoutEvent == pWatch->Header.LastEvent)
|
||
|
{
|
||
|
//
|
||
|
// Check if we made any progress.
|
||
|
//
|
||
|
|
||
|
if ((pWatch->InCount != pWatch->LastInCount) ||
|
||
|
(pWatch->OutCount != pWatch->LastOutCount) ||
|
||
|
(pWatch->InCount == pWatch->OutCount))
|
||
|
{
|
||
|
//
|
||
|
// We recovered - update event type and queue client DPC.
|
||
|
//
|
||
|
|
||
|
pWatch->Header.LastEvent = WdRecoveryEvent;
|
||
|
|
||
|
if (NULL != pWatch->ClientDpc)
|
||
|
{
|
||
|
//
|
||
|
// Bump up references to objects we're going to touch in client DPC.
|
||
|
//
|
||
|
|
||
|
WdReferenceObject(pWatch);
|
||
|
|
||
|
//
|
||
|
// Queue client DPC.
|
||
|
//
|
||
|
// Note: In case of recovery the thread associated with watchdog
|
||
|
// object may be deleted by the time we get here. We can't pass it
|
||
|
// down to client DPC - we're passing NULL instead.
|
||
|
//
|
||
|
|
||
|
if (KeInsertQueueDpc(pWatch->ClientDpc, NULL, pWatch) == TRUE)
|
||
|
{
|
||
|
//
|
||
|
// Keep track of qeueued thread in case we cancel this DPC.
|
||
|
//
|
||
|
|
||
|
pWatch->Header.LastQueuedThread = NULL;
|
||
|
|
||
|
//
|
||
|
// Make sure we queue DPC only once per event.
|
||
|
//
|
||
|
|
||
|
pWatch->Trigger = 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// This should never happen.
|
||
|
//
|
||
|
|
||
|
WdDereferenceObject(pWatch);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check if we are in the monitored section.
|
||
|
//
|
||
|
|
||
|
if (pWatch->InCount == pWatch->OutCount)
|
||
|
{
|
||
|
//
|
||
|
// We're outside monitored section - we're fine.
|
||
|
//
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We're inside monitored section - bump up trigger indicator,
|
||
|
// and take snapshots of counters and thread's time.
|
||
|
//
|
||
|
|
||
|
pWatch->Trigger = 1;
|
||
|
pWatch->LastInCount = pWatch->InCount;
|
||
|
pWatch->LastOutCount = pWatch->OutCount;
|
||
|
pWatch->LastKernelTime = KeQueryRuntimeThread(pWatch->Thread, &(pWatch->LastUserTime));
|
||
|
break;
|
||
|
|
||
|
case 1:
|
||
|
|
||
|
//
|
||
|
// We were in the monitored section last time.
|
||
|
//
|
||
|
|
||
|
//
|
||
|
// Check if we're out or suspended.
|
||
|
//
|
||
|
|
||
|
if ((pWatch->InCount == pWatch->OutCount) || pWatch->SuspendCount)
|
||
|
{
|
||
|
//
|
||
|
// We're outside monitored section or suspended - we're fine.
|
||
|
// Reset trigger counter and get out of here.
|
||
|
//
|
||
|
|
||
|
pWatch->Trigger = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check if we made any progress, if so reset snapshots.
|
||
|
//
|
||
|
|
||
|
if ((pWatch->InCount != pWatch->LastInCount) ||
|
||
|
(pWatch->OutCount != pWatch->LastOutCount))
|
||
|
{
|
||
|
pWatch->Trigger = 1;
|
||
|
pWatch->LastInCount = pWatch->InCount;
|
||
|
pWatch->LastOutCount = pWatch->OutCount;
|
||
|
pWatch->LastKernelTime = KeQueryRuntimeThread(pWatch->Thread, &(pWatch->LastUserTime));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Check if we're stuck long enough.
|
||
|
//
|
||
|
|
||
|
ulKernelTime = KeQueryRuntimeThread(pWatch->Thread, &ulUserTime);
|
||
|
|
||
|
switch (pWatch->Header.TimeType)
|
||
|
{
|
||
|
case WdKernelTime:
|
||
|
|
||
|
liThreadTime.QuadPart = ulKernelTime;
|
||
|
|
||
|
//
|
||
|
// Handle counter rollovers.
|
||
|
//
|
||
|
|
||
|
if (ulKernelTime < pWatch->LastKernelTime)
|
||
|
{
|
||
|
liThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastKernelTime + 1;
|
||
|
}
|
||
|
|
||
|
liThreadTime.QuadPart -= pWatch->LastKernelTime;
|
||
|
|
||
|
break;
|
||
|
|
||
|
case WdUserTime:
|
||
|
|
||
|
liThreadTime.QuadPart = ulUserTime;
|
||
|
|
||
|
//
|
||
|
// Handle counter rollovers.
|
||
|
//
|
||
|
|
||
|
if (ulUserTime < pWatch->LastUserTime)
|
||
|
{
|
||
|
liThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastUserTime + 1;
|
||
|
}
|
||
|
|
||
|
liThreadTime.QuadPart -= pWatch->LastUserTime;
|
||
|
|
||
|
break;
|
||
|
|
||
|
case WdFullTime:
|
||
|
|
||
|
liThreadTime.QuadPart = ulKernelTime + ulUserTime;
|
||
|
|
||
|
//
|
||
|
// Handle counter rollovers.
|
||
|
//
|
||
|
|
||
|
if (ulKernelTime < pWatch->LastKernelTime)
|
||
|
{
|
||
|
liThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastKernelTime + 1;
|
||
|
}
|
||
|
|
||
|
if (ulUserTime < pWatch->LastUserTime)
|
||
|
{
|
||
|
liThreadTime.QuadPart += (ULONG)(-1) - pWatch->LastUserTime + 1;
|
||
|
}
|
||
|
|
||
|
liThreadTime.QuadPart -= (pWatch->LastKernelTime + pWatch->LastUserTime);
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
|
||
|
ASSERT(FALSE);
|
||
|
liThreadTime.QuadPart = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Convert to milliseconds.
|
||
|
//
|
||
|
|
||
|
liThreadTime.QuadPart *= pWatch->TimeIncrement;
|
||
|
liThreadTime.QuadPart /= 10000;
|
||
|
|
||
|
if (liThreadTime.QuadPart >= pWatch->Period)
|
||
|
{
|
||
|
//
|
||
|
// We've been stuck long enough - update event type and queue client DPC.
|
||
|
//
|
||
|
|
||
|
pWatch->Header.LastEvent = WdTimeoutEvent;
|
||
|
|
||
|
if (NULL != pWatch->ClientDpc)
|
||
|
{
|
||
|
//
|
||
|
// Bump up references to objects we're going to touch in client DPC.
|
||
|
//
|
||
|
|
||
|
ObReferenceObject(pWatch->Thread);
|
||
|
WdReferenceObject(pWatch);
|
||
|
|
||
|
//
|
||
|
// Queue client DPC.
|
||
|
//
|
||
|
|
||
|
if (KeInsertQueueDpc(pWatch->ClientDpc, pWatch->Thread, pWatch) == TRUE)
|
||
|
{
|
||
|
//
|
||
|
// Keep track of qeueued thread in case we cancel this DPC.
|
||
|
//
|
||
|
|
||
|
pWatch->Header.LastQueuedThread = pWatch->Thread;
|
||
|
|
||
|
//
|
||
|
// Make sure we queue DPC only once per event.
|
||
|
//
|
||
|
|
||
|
pWatch->Trigger = 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// This should never happen.
|
||
|
//
|
||
|
|
||
|
ObDereferenceObject(pWatch->Thread);
|
||
|
WdDereferenceObject(pWatch);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
|
||
|
//
|
||
|
// We have event posted waiting for completion. Nothing to do.
|
||
|
//
|
||
|
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
|
||
|
//
|
||
|
// This should never happen.
|
||
|
//
|
||
|
|
||
|
ASSERT(FALSE);
|
||
|
pWatch->Trigger = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Unlock the dispatcher database.
|
||
|
//
|
||
|
|
||
|
KeReleaseSpinLockFromDpcLevel(&(pWatch->Header.SpinLock));
|
||
|
|
||
|
return;
|
||
|
} // WdDeferredWatchdogDpcCallback()
|