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.
 
 
 
 
 
 

4141 lines
108 KiB

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
deadlock.c
Abstract:
This module implements a deadlock verification package for
critical section operations. The initial version is based on
the driver verifier deadlock checking package for kernel
synchornization objects.
Author:
Silviu Calinoiu (SilviuC) 6-Feb-2002
Revision History:
--*/
/*++
silviuc: update this comment
The Deadlock Verifier
The deadlock verifier is used to detect potential deadlocks. It does this
by acquiring the history of how resources are acquired and trying to figure
out on the fly if there are any potential lock hierarchy issues. The algorithms
for finding cycles in the lock dependency graph is totally "blind". This means
that if a driver acquires lock A then B in one place and lock B then A in
another this will be triggered as a deadlock issue. This will happen even if you
can build a proof based on other contextual factors that the deadlock can never
happen.
The deadlock verifier assumes there are four operations during the lifetime
of a resource: initialize(), acquire(), release() and free(). The only one that
is caught 100% of the time is free() due to special support from the kernel
pool manager. The other ones can be missed if the operations are performed
by an unverified driver or by kernel with kernel verifier disabled. The most
typical of these misses is the initialize(). For example the kernel initializes
a resource and then passes it to a driver to be used in acquire()/releae() cycles.
This situation is covered 100% by the deadlock verifier. It will never complain
about "resource uninitialized" issues.
Missing acquire() or release() operations is trickier to deal with.
This can happen if the a verified driver acquires a resource and then another
driver that is not verified releases it or viceversa. This is in and on itself
a very bad programming practive and therefore the deadlock verifier will flag
these issues. As a side note we cannot do too much about working around them
given that we would like to. Also, because missing acquire() or release()
operations puts deadlock verifier internal structures into inconsistent
states these failures are difficult to debug.
The deadlock verifier stores the lock dependency graph using three types
of structures: THREAD, RESOURCE, NODE.
For every active thread in the system that holds at least one resource
the package maintains a THREAD structure. This gets created when a thread
acquires first resource and gets destroyed when thread releases the last
resource. If a thread does not hold any resource it will not have a
corresponding THREAD structure.
For every resource in the system there is a RESOURCE structure. This is created
when Initialize() is called in a verified driver or we first encounter an
Acquire() in a verified driver. Note that a resource can be initialized in
an unverified driver and then passed to a verified driver for use. Therefore
we can encounter Acquire() operations for resources that are not in the
deadlock verifier database. The resource gets deleted from the database when
the memory containing it is freed either because ExFreePool gets called or
Every acquisition of a resource is modeled by a NODE structure. When a thread
acquires resource B while holding A the deadlock verifier will create a NODE
for B and link it to the node for A.
There are three important functions that make the interface with the outside
world.
AVrfpDeadlockInitializeResource hook for resource initialization
AVrfpDeadlockAcquireResource hook for resource acquire
AVrfpDeadlockReleaseResource hook for resource release
VerifierDeadlockFreePool hook called from ExFreePool for every free()
--*/
#include "pch.h"
#include "support.h"
#include "deadlock.h"
#include "logging.h"
//
// Enable/disable the deadlock detection package. This can be used
// to disable temporarily the deadlock detection package.
//
BOOLEAN AVrfpDeadlockDetectionEnabled;
//
// If true we will complain about release() without acquire() or acquire()
// while we think the resource is still owned. This can happen legitimately
// if a lock is shared between drivers and for example acquire() happens in
// an unverified driver and release() in a verified one or viceversa. The
// safest thing would be to enable this checks only if kernel verifier and
// dirver verifier for all drivers are enabled.
//
BOOLEAN AVrfpDeadlockStrict; //silviuc: needed?
//
// If true we will complain about uninitialized and double initialized
// resources. If false we resolve quitely these issues on the fly by
// simulating an initialize ourselves during the acquire() operation.
// This can happen legitimately if the resource is initialized in an
// unverified driver and passed to a verified one to be used. Therefore
// the safest thing would be to enable this only if kernel verifier and
// all driver verifier for all dirvers are enabled.
//
BOOLEAN AVrfpDeadlockVeryStrict; //silviuc: needed?
//
// AgeWindow is used while trimming the graph nodes that have not
// been accessed in a while. If the global age minus the age of the node
// is bigger than the age window then the node is a candidate for trimming.
//
// The TrimThreshold variable controls if the trimming will start for a
// resource. As long as a resource has less than TrimThreshold nodes we will
// not apply the ageing algorithm to trim nodes for that resource.
//
ULONG AVrfpDeadlockAgeWindow = 0x2000;
ULONG AVrfpDeadlockTrimThreshold = 0x100;
//
// Various deadlock verification flags flags
//
// Recursive aquisition ok: mutexes can be recursively acquired
//
// No initialization function: if resource type does not have such a function
// we cannot expect that in acquire() the resource is already initialized
// by a previous call to initialize(). Fast mutexes are like this.
//
// Reverse release ok: release is not done in the same order as acquire
//
// Reinitialize ok: sometimes they reinitialize the resource.
//
// Note that a resource might appear as uninitialized if it is initialized
// in an unverified driver and then passed to a verified driver that calls
// acquire(). This is for instance the case with device extensions that are
// allocated by the kernel but used by a particular driver.
//
// silviuc: based on this maybe we should drop the whole not initialized thing?
//
// silviuc: do we need all these flags?
#define AVRF_DEADLOCK_FLAG_RECURSIVE_ACQUISITION_OK 0x0001
#define AVRF_DEADLOCK_FLAG_NO_INITIALIZATION_FUNCTION 0x0002
#define AVRF_DEADLOCK_FLAG_REVERSE_RELEASE_OK 0x0004
#define AVRF_DEADLOCK_FLAG_REINITIALIZE_OK 0x0008
//
// Specific verification flags for each resource type. The
// indeces in the vector match the values for the enumeration
// type AVRF_DEADLOCK_RESOURCE_TYPE from `deadlock.h'.
//
ULONG AVrfpDeadlockResourceTypeInfo[AVrfpDeadlockTypeMaximum] =
{
// AVrfpDeadlockTypeUnknown //
0,
// AVrfpDeadlockTypeCriticalSection//
AVRF_DEADLOCK_FLAG_RECURSIVE_ACQUISITION_OK |
AVRF_DEADLOCK_FLAG_REVERSE_RELEASE_OK |
// silviuc: delete this if not needed
// AVRF_DEADLOCK_FLAG_NO_INITIALIZATION_FUNCTION |
// AVRF_DEADLOCK_FLAG_REINITIALIZE_OK |
0,
};
//
// Control debugging behavior. A zero value means bugcheck for every failure.
//
ULONG AVrfpDeadlockDebug;
//
// Various health indicators
//
struct {
ULONG AllocationFailures : 1;
ULONG KernelVerifierEnabled : 1; //silviuc:delete
ULONG DriverVerifierForAllEnabled : 1; //silviuc:delete
ULONG SequenceNumberOverflow : 1;
ULONG DeadlockParticipantsOverflow : 1;
ULONG ResourceNodeCountOverflow : 1;
ULONG Reserved : 15;
} AVrfpDeadlockState;
//
// Maximum number of locks acceptable to be hold simultaneously
//
ULONG AVrfpDeadlockSimultaneousLocksLimit = 10;
//
// Deadlock verifier specific issues (bugs)
//
// SELF_DEADLOCK
//
// Acquiring the same resource recursively.
//
// DEADLOCK_DETECTED
//
// Plain deadlock. Need the previous information
// messages to build a deadlock proof.
//
// UNINITIALIZED_RESOURCE
//
// Acquiring a resource that was never initialized.
//
// UNEXPECTED_RELEASE
//
// Releasing a resource which is not the last one
// acquired by the current thread. Spinlocks are handled like this
// in a few drivers. It is not a bug per se.
//
// UNEXPECTED_THREAD
//
// Current thread does not have any resources acquired. This may be legit if
// we acquire in one thread and release in another. This would be bad programming
// practice but not a crash waiting to happen per se.
//
// MULTIPLE_INITIALIZATION
//
// Attempting to initialize a second time the same resource.
//
// THREAD_HOLDS_RESOURCES
//
// Thread was killed while holding resources or a resource is being
// deleted while holding resources.
//
#define AVRF_DEADLOCK_ISSUE_SELF_DEADLOCK 0x1000
#define AVRF_DEADLOCK_ISSUE_DEADLOCK_DETECTED 0x1001
#define AVRF_DEADLOCK_ISSUE_UNINITIALIZED_RESOURCE 0x1002
#define AVRF_DEADLOCK_ISSUE_UNEXPECTED_RELEASE 0x1003
#define AVRF_DEADLOCK_ISSUE_UNEXPECTED_THREAD 0x1004
#define AVRF_DEADLOCK_ISSUE_MULTIPLE_INITIALIZATION 0x1005
#define AVRF_DEADLOCK_ISSUE_THREAD_HOLDS_RESOURCES 0x1006
#define AVRF_DEADLOCK_ISSUE_UNACQUIRED_RESOURCE 0x1007
//
// Performance counters read from registry.
//
ULONG ViSearchedNodesLimitFromRegistry;//silviuc:delete
ULONG ViRecursionDepthLimitFromRegistry;//silviuc:delete
//
// Water marks for the cache of freed structures.
//
// N.B. The `MAX_FREE' value will trigger a trim and the
// `TRIM_TARGET' will be the trim goal. The trim target must
// be meaningfully lower than the free watermark to avoid a
// chainsaw effect where we get one above free highwater mark,
// we trim to the mark and next free will trigger a repeat.
// Since trimming is done in worker threads this will put a lot
// of unnecessary strain on the system.
//
#define AVRF_DEADLOCK_MAX_FREE_THREAD 0x40
#define AVRF_DEADLOCK_MAX_FREE_NODE 0x80
#define AVRF_DEADLOCK_MAX_FREE_RESOURCE 0x80
#define AVRF_DEADLOCK_TRIM_TARGET_THREAD 0x20
#define AVRF_DEADLOCK_TRIM_TARGET_NODE 0x40
#define AVRF_DEADLOCK_TRIM_TARGET_RESOURCE 0x40
WORK_QUEUE_ITEM ViTrimDeadlockPoolWorkItem;
//
// Amount of memory preallocated if kernel verifier
// is enabled. If kernel verifier is enabled no memory
// is ever allocated from kernel pool except in the
// DeadlockDetectionInitialize() routine.
//
ULONG AVrfpDeadlockReservedThreads = 0x200;
ULONG AVrfpDeadlockReservedNodes = 0x4000;
ULONG AVrfpDeadlockReservedResources = 0x2000;
//
// Block types that can be allocated.
//
typedef enum {
AVrfpDeadlockUnknown = 0,
AVrfpDeadlockResource,
AVrfpDeadlockNode,
AVrfpDeadlockThread
} AVRF_DEADLOCK_ALLOC_TYPE;
//
// AVRF_DEADLOCK_GLOBALS
//
// silviuc: should have diff numbers for threads and resources.
#define AVRF_DEADLOCK_HASH_BINS 0x1F
PAVRF_DEADLOCK_GLOBALS AVrfpDeadlockGlobals;
//
// Default maximum recursion depth for the deadlock
// detection algorithm. This can be overridden by registry.
//
#define AVRF_DEADLOCK_MAXIMUM_DEGREE 4
//
// Default maximum number of searched nodes for the deadlock
// detection algorithm. This can be overridden by registry.
//
#define AVRF_DEADLOCK_MAXIMUM_SEARCH 1000
//
// Verifier deadlock detection pool tag.
//
#define AVRF_DEADLOCK_TAG 'kclD'
//
// Controls how often ForgetResourceHistory gets called.
//
#define AVRF_DEADLOCK_FORGET_HISTORY_FREQUENCY 16
//
// Function to capture runtime stack traces.
//
NTSYSAPI
USHORT
NTAPI
RtlCaptureStackBackTrace(
IN ULONG FramesToSkip,
IN ULONG FramesToCapture,
OUT PVOID *BackTrace,
OUT PULONG BackTraceHash
);
/////////////////////////////////////////////////////////////////////
/////////////////////////////// Internal deadlock detection functions
/////////////////////////////////////////////////////////////////////
VOID
AVrfpDeadlockDetectionInitialize (
VOID
);
VOID
AVrfpDeadlockDetectionCleanup (
VOID
);
PLIST_ENTRY
AVrfpDeadlockDatabaseHash (
IN PLIST_ENTRY Database,
IN PVOID Address
);
PAVRF_DEADLOCK_RESOURCE
AVrfpDeadlockSearchResource (
IN PVOID ResourceAddress
);
BOOLEAN
AVrfpDeadlockSimilarNode (
IN PVOID Resource,
IN BOOLEAN TryNode,
IN PAVRF_DEADLOCK_NODE Node
);
BOOLEAN
AVrfpDeadlockCanProceed (
VOID
);
BOOLEAN
AVrfpDeadlockAnalyze (
IN PVOID ResourceAddress,
IN PAVRF_DEADLOCK_NODE CurrentNode,
IN BOOLEAN FirstCall,
IN ULONG Degree
);
PAVRF_DEADLOCK_THREAD
AVrfpDeadlockSearchThread (
HANDLE Thread
);
PAVRF_DEADLOCK_THREAD
AVrfpDeadlockAddThread (
HANDLE Thread,
PVOID ReservedThread
);
VOID
AVrfpDeadlockDeleteThread (
PAVRF_DEADLOCK_THREAD Thread,
BOOLEAN Cleanup
);
BOOLEAN
AVrfpDeadlockAddResource(
IN PVOID Resource,
IN AVRF_DEADLOCK_RESOURCE_TYPE Type,
IN PVOID Caller,
IN PVOID ReservedResource
);
PVOID
AVrfpDeadlockAllocate (
AVRF_DEADLOCK_ALLOC_TYPE Type
);
VOID
AVrfpDeadlockFree (
PVOID Object,
AVRF_DEADLOCK_ALLOC_TYPE Type
);
VOID
AVrfpDeadlockTrimPoolCache (
VOID
);
VOID
AVrfpDeadlockTrimPoolCacheWorker (
PVOID
);
PVOID
AVrfpDeadlockAllocateFromPoolCache (
PULONG Count,
ULONG MaximumCount,
PLIST_ENTRY List,
SIZE_T Offset
);
VOID
AVrfpDeadlockFreeIntoPoolCache (
PVOID Object,
PULONG Count,
PLIST_ENTRY List,
SIZE_T Offset
);
VOID
AVrfpDeadlockReportIssue (
ULONG_PTR Param1,
ULONG_PTR Param2,
ULONG_PTR Param3,
ULONG_PTR Param4
);
VOID
AVrfpDeadlockAddParticipant(
PAVRF_DEADLOCK_NODE Node
);
VOID
AVrfpDeadlockDeleteResource (
PAVRF_DEADLOCK_RESOURCE Resource,
BOOLEAN Cleanup
);
VOID
AVrfpDeadlockDeleteNode (
PAVRF_DEADLOCK_NODE Node,
BOOLEAN Cleanup
);
ULONG
AVrfpDeadlockNodeLevel (
PAVRF_DEADLOCK_NODE Node
);
BOOLEAN
AVrfpDeadlockCertify(
VOID
);
VOID
AVrfpDeadlockDetectionLock (
VOID
);
VOID
AVrfpDeadlockDetectionUnlock (
VOID
);
VOID
AVrfpDeadlockCheckThreadConsistency (
PAVRF_DEADLOCK_THREAD Thread,
BOOLEAN Recursion
);
VOID
AVrfpDeadlockCheckNodeConsistency (
PAVRF_DEADLOCK_NODE Node,
BOOLEAN Recursion
);
VOID
AVrfpDeadlockCheckResourceConsistency (
PAVRF_DEADLOCK_RESOURCE Resource,
BOOLEAN Recursion
);
PAVRF_DEADLOCK_THREAD
AVrfpDeadlockCheckThreadReferences (
PAVRF_DEADLOCK_NODE Node
);
VOID
AVrfpDeadlockCheckDuplicatesAmongChildren (
PAVRF_DEADLOCK_NODE Parent,
PAVRF_DEADLOCK_NODE Child
);
VOID
AVrfpDeadlockCheckDuplicatesAmongRoots (
PAVRF_DEADLOCK_NODE Root
);
LOGICAL
AVrfpDeadlockSimilarNodes (
PAVRF_DEADLOCK_NODE NodeA,
PAVRF_DEADLOCK_NODE NodeB
);
VOID
AVrfpDeadlockMergeNodes (
PAVRF_DEADLOCK_NODE NodeTo,
PAVRF_DEADLOCK_NODE NodeFrom
);
VOID
AVrfpDeadlockTrimResources (
PLIST_ENTRY HashList
);
VOID
AVrfpDeadlockForgetResourceHistory (
PAVRF_DEADLOCK_RESOURCE Resource,
ULONG TrimThreshold,
ULONG AgeThreshold
);
VOID
AVrfpDeadlockCheckStackLimits (
VOID
);
BOOLEAN
AVrfpDeadlockResourceInitialize(
IN PVOID Resource,
IN AVRF_DEADLOCK_RESOURCE_TYPE Type,
IN PVOID Caller
);
VOID
AVrfpDeadlockAcquireResource (
IN PVOID Resource,
IN AVRF_DEADLOCK_RESOURCE_TYPE Type,
IN HANDLE Thread,
IN BOOLEAN TryAcquire,
IN PVOID Caller
);
VOID
AVrfpDeadlockReleaseResource(
IN PVOID Resource,
IN AVRF_DEADLOCK_RESOURCE_TYPE Type,
IN HANDLE Thread,
IN PVOID Caller
);
/////////////////////////////////////////////////////////////////////
/////////////////////////////// Deadlock verifier public entry points
/////////////////////////////////////////////////////////////////////
LOGICAL
AVrfDeadlockResourceInitialize (
PVOID Resource,
PVOID Caller
)
{
return AVrfpDeadlockResourceInitialize(Resource,
AVrfpDeadlockTypeCriticalSection,
Caller);
}
LOGICAL
AVrfDeadlockResourceDelete (
PVOID Resource,
PVOID Caller
)
{
PAVRF_DEADLOCK_RESOURCE Descriptor;
UNREFERENCED_PARAMETER (Caller);
AVrfpDeadlockDetectionLock ();
Descriptor = AVrfpDeadlockSearchResource (Resource);
if (Descriptor == NULL) {
//silviuc: whine about bogus address.
}
else {
AVrfpDeadlockDeleteResource (Descriptor, FALSE);
}
AVrfpDeadlockDetectionUnlock ();
return TRUE;
}
LOGICAL
AVrfDeadlockResourceAcquire (
PVOID Resource,
PVOID Caller,
LOGICAL TryAcquire
)
{ // silviuc: should use only LOGICAL instead of BOOLEAN
AVrfpDeadlockAcquireResource (Resource,
AVrfpDeadlockTypeCriticalSection,
NtCurrentTeb()->ClientId.UniqueThread,
(BOOLEAN)TryAcquire,
Caller);
return TRUE;
}
LOGICAL
AVrfDeadlockResourceRelease (
PVOID Resource,
PVOID Caller
)
{
AVrfpDeadlockReleaseResource (Resource,
AVrfpDeadlockTypeCriticalSection,
NtCurrentTeb()->ClientId.UniqueThread,
Caller);
return TRUE;
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////// Lock/unlock deadlock verifier
/////////////////////////////////////////////////////////////////////
//
// Global `deadlock lock database' lock
//
RTL_CRITICAL_SECTION AVrfpDeadlockDatabaseLock;
VOID
AVrfpDeadlockDetectionLock (
VOID
)
{
RtlEnterCriticalSection (&AVrfpDeadlockDatabaseLock);
}
VOID
AVrfpDeadlockDetectionUnlock (
VOID
)
{
RtlLeaveCriticalSection (&AVrfpDeadlockDatabaseLock);
}
/////////////////////////////////////////////////////////////////////
///////////////////// Initialization and deadlock database management
/////////////////////////////////////////////////////////////////////
PLIST_ENTRY
AVrfpDeadlockDatabaseHash(
IN PLIST_ENTRY Database,
IN PVOID Address
)
/*++
Routine Description:
This routine hashes the resource address into the deadlock database.
The hash bin is represented by a list entry.
Arguments:
ResourceAddress: Address of the resource that is being hashed
Return Value:
PLIST_ENTRY -- the list entry associated with the hash bin we land in.
--*/
{
return Database + ((((ULONG_PTR)Address)) % AVRF_DEADLOCK_HASH_BINS);
}
VOID
AVrfDeadlockDetectionInitialize(
VOID
)
/*++
Routine Description:
This routine initializes the data structures necessary for detecting
deadlocks in usage of synchronization objects.
Arguments:
None.
Return Value:
None. If successful AVrfpDeadlockGlobals will point to a fully initialized
structure.
Environment:
Application verifier initialization only.
--*/
{
ULONG I;
SIZE_T TableSize;
//
// Allocate the globals structure. AVrfpDeadlockGlobals value is
// used to figure out if the whole initialization was successful
// or not.
//
AVrfpDeadlockGlobals = AVrfpAllocate (sizeof (AVRF_DEADLOCK_GLOBALS));
if (AVrfpDeadlockGlobals == NULL) {
goto Failed;
}
RtlZeroMemory (AVrfpDeadlockGlobals, sizeof (AVRF_DEADLOCK_GLOBALS));
//
// Allocate hash tables for resources and threads.
//
TableSize = sizeof (LIST_ENTRY) * AVRF_DEADLOCK_HASH_BINS;
AVrfpDeadlockGlobals->ResourceDatabase = AVrfpAllocate (TableSize);
if (AVrfpDeadlockGlobals->ResourceDatabase == NULL) {
goto Failed;
}
AVrfpDeadlockGlobals->ThreadDatabase = AVrfpAllocate (TableSize);
if (AVrfpDeadlockGlobals->ThreadDatabase == NULL) {
goto Failed;
}
//
// Initialize the free lists.
//
InitializeListHead(&AVrfpDeadlockGlobals->FreeResourceList);
InitializeListHead(&AVrfpDeadlockGlobals->FreeThreadList);
InitializeListHead(&AVrfpDeadlockGlobals->FreeNodeList);
//
// Initialize hash bins and database lock.
//
for (I = 0; I < AVRF_DEADLOCK_HASH_BINS; I += 1) {
InitializeListHead(&(AVrfpDeadlockGlobals->ResourceDatabase[I]));
InitializeListHead(&AVrfpDeadlockGlobals->ThreadDatabase[I]);
}
RtlInitializeCriticalSection (&AVrfpDeadlockDatabaseLock);
//
// Initialize deadlock analysis parameters
//
AVrfpDeadlockGlobals->RecursionDepthLimit = AVRF_DEADLOCK_MAXIMUM_DEGREE;
AVrfpDeadlockGlobals->SearchedNodesLimit = AVRF_DEADLOCK_MAXIMUM_SEARCH;
//
// Mark that everything went fine and return
//
AVrfpDeadlockDetectionEnabled = TRUE;
return;
Failed:
//
// Cleanup if any of our allocations failed
//
if (AVrfpDeadlockGlobals) {
if (AVrfpDeadlockGlobals->ResourceDatabase != NULL) {
AVrfpFree (AVrfpDeadlockGlobals->ResourceDatabase);
}
if (AVrfpDeadlockGlobals->ThreadDatabase != NULL) {
AVrfpFree (AVrfpDeadlockGlobals->ThreadDatabase);
}
if (AVrfpDeadlockGlobals != NULL) {
AVrfpFree (AVrfpDeadlockGlobals);
//
// Important to set this to null for failure because it is
// used to figure out if the package got initialized or not.
//
AVrfpDeadlockGlobals = NULL;
}
}
return;
}
VOID
AVrfpDeadlockDetectionCleanup (
VOID
)
/*++
Routine Description:
This routine tears down all deadlock verifier internal structures.
Arguments:
None.
Return Value:
None.
--*/
{
ULONG Index;
PLIST_ENTRY Current;
PAVRF_DEADLOCK_RESOURCE Resource;
PAVRF_DEADLOCK_THREAD Thread;
PVOID Block;
// silviuc: no locks?
//
// If we are not initialized then nothing to do.
//
if (AVrfpDeadlockGlobals == NULL) {
return;
}
//
// Iterate all resources and delete them. This will also delete
// all nodes associated with resources.
//
for (Index = 0; Index < AVRF_DEADLOCK_HASH_BINS; Index += 1) {
Current = AVrfpDeadlockGlobals->ResourceDatabase[Index].Flink;
while (Current != &(AVrfpDeadlockGlobals->ResourceDatabase[Index])) {
Resource = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_RESOURCE,
HashChainList);
Current = Current->Flink;
AVrfpDeadlockDeleteResource (Resource, TRUE);
}
}
//
// Iterate all threads and delete them.
//
for (Index = 0; Index < AVRF_DEADLOCK_HASH_BINS; Index += 1) {
Current = AVrfpDeadlockGlobals->ThreadDatabase[Index].Flink;
while (Current != &(AVrfpDeadlockGlobals->ThreadDatabase[Index])) {
Thread = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_THREAD,
ListEntry);
Current = Current->Flink;
AVrfpDeadlockDeleteThread (Thread, TRUE);
}
}
//
// Everything should be in the pool caches by now.
//
ASSERT (AVrfpDeadlockGlobals->BytesAllocated == 0);
//
// Free pool caches.
//
Current = AVrfpDeadlockGlobals->FreeNodeList.Flink;
while (Current != &(AVrfpDeadlockGlobals->FreeNodeList)) {
Block = (PVOID) CONTAINING_RECORD (Current,
AVRF_DEADLOCK_NODE,
FreeListEntry);
Current = Current->Flink;
AVrfpFree (Block);
}
Current = AVrfpDeadlockGlobals->FreeResourceList.Flink;
while (Current != &(AVrfpDeadlockGlobals->FreeResourceList)) {
Block = (PVOID) CONTAINING_RECORD (Current,
AVRF_DEADLOCK_RESOURCE,
FreeListEntry);
Current = Current->Flink;
AVrfpFree (Block);
}
Current = AVrfpDeadlockGlobals->FreeThreadList.Flink;
while (Current != &(AVrfpDeadlockGlobals->FreeThreadList)) {
Block = (PVOID) CONTAINING_RECORD (Current,
AVRF_DEADLOCK_THREAD,
FreeListEntry);
Current = Current->Flink;
AVrfpFree (Block);
}
//
// Free databases and global structure
//
AVrfpFree (AVrfpDeadlockGlobals->ResourceDatabase);
AVrfpFree (AVrfpDeadlockGlobals->ThreadDatabase);
AVrfpFree (AVrfpDeadlockGlobals);
AVrfpDeadlockGlobals = NULL;
AVrfpDeadlockDetectionEnabled = FALSE;
}
BOOLEAN
AVrfpDeadlockCanProceed (
VOID
)
/*++
Routine Description:
This routine is called by deadlock verifier exports (initialize,
acquire, release) to figure out if deadlock verification should
proceed for the current operation. There are several reasons
why the return should be false. For example we failed to initialize
the deadlock verifier package etc.
Arguments:
None.
Return Value:
True if deadlock verification should proceed for the current
operation.
Environment:
Internal. Called by deadlock verifier exports.
--*/
{
//
// Skip if process is shutting down.
//
if (RtlDllShutdownInProgress()) {
return FALSE;
}
//
// Skip if package not initialized
//
if (AVrfpDeadlockGlobals == NULL) {
return FALSE;
}
//
// Skip if package is disabled
//
if (! AVrfpDeadlockDetectionEnabled) {
return FALSE;
}
//
// Skip if we ever encountered an allocation failure
//
if (AVrfpDeadlockGlobals->AllocationFailures > 0) {
return FALSE;
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////
//////////////////////////////////////////// Deadlock detection logic
/////////////////////////////////////////////////////////////////////
BOOLEAN
AVrfpDeadlockAnalyze(
IN PVOID ResourceAddress,
IN PAVRF_DEADLOCK_NODE AcquiredNode,
IN BOOLEAN FirstCall,
IN ULONG Degree
)
/*++
Routine Description:
This routine determines whether the acquisition of a certain resource
could result in a deadlock.
The routine assumes the deadlock database lock is held.
Arguments:
ResourceAddress - address of the resource that will be acquired
AcquiredNode - a node representing the most recent resource acquisition
made by the thread trying to acquire `ResourceAddress'.
FirstCall - true if this is not a recursive call made from within the
function. It is used for doing one time per analysis only operations.
Degree - depth of recursion.
Return Value:
True if deadlock detected, false otherwise.
--*/
{
PAVRF_DEADLOCK_NODE CurrentNode;
PAVRF_DEADLOCK_RESOURCE CurrentResource;
PAVRF_DEADLOCK_NODE CurrentParent;
BOOLEAN FoundDeadlock;
PLIST_ENTRY Current;
ASSERT (AcquiredNode);
//
// Setup global counters.
//
if (FirstCall) {
AVrfpDeadlockGlobals->NodesSearched = 0;
AVrfpDeadlockGlobals->SequenceNumber += 1;
AVrfpDeadlockGlobals->NumberOfParticipants = 0;
AVrfpDeadlockGlobals->Instigator = NULL;
if (AVrfpDeadlockGlobals->SequenceNumber == ((1 << 30) - 2)) {
AVrfpDeadlockState.SequenceNumberOverflow = 1;
}
}
//
// If our node is already stamped with the current sequence number
// then we have been here before in the current search. There is a very
// remote possibility that the node was not touched in the last
// 2^N calls to this function and the sequence number counter
// overwrapped but we can live with this.
//
if (AcquiredNode->SequenceNumber == AVrfpDeadlockGlobals->SequenceNumber) {
return FALSE;
}
//
// Update the counter of nodes touched in this search
//
AVrfpDeadlockGlobals->NodesSearched += 1;
//
// Stamp node with current sequence number.
//
AcquiredNode->SequenceNumber = AVrfpDeadlockGlobals->SequenceNumber;
//
// Stop recursion if it gets too deep.
//
if (Degree > AVrfpDeadlockGlobals->RecursionDepthLimit) {
AVrfpDeadlockGlobals->DepthLimitHits += 1;
return FALSE;
}
//
// Stop recursion if it gets too lengthy
//
if (AVrfpDeadlockGlobals->NodesSearched >= AVrfpDeadlockGlobals->SearchedNodesLimit) {
AVrfpDeadlockGlobals->SearchLimitHits += 1;
return FALSE;
}
//
// Check if AcquiredNode's resource equals ResourceAddress.
// This is the final point for a deadlock detection because
// we managed to find a path in the graph that leads us to the
// same resource as the one to be acquired. From now on we
// will start returning from recursive calls and build the
// deadlock proof along the way.
//
ASSERT (AcquiredNode->Root);
if (ResourceAddress == AcquiredNode->Root->ResourceAddress) {
if (AcquiredNode->ReleasedOutOfOrder == 0) {
ASSERT (FALSE == FirstCall);
FoundDeadlock = TRUE;
AVrfpDeadlockAddParticipant (AcquiredNode);
goto Exit;
}
}
//
// Iterate all nodes in the graph using the same resource from AcquiredNode.
//
FoundDeadlock = FALSE;
CurrentResource = AcquiredNode->Root;
Current = CurrentResource->ResourceList.Flink;
while (Current != &(CurrentResource->ResourceList)) {
CurrentNode = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_NODE,
ResourceList);
ASSERT (CurrentNode->Root);
ASSERT (CurrentNode->Root == CurrentResource);
//
// Mark node as visited
//
CurrentNode->SequenceNumber = AVrfpDeadlockGlobals->SequenceNumber;
//
// Check recursively the parent of the CurrentNode. This will check the
// whole parent chain eventually through recursive calls.
//
CurrentParent = CurrentNode->Parent;
if (CurrentParent != NULL) {
//
// If we are traversing the Parent chain of AcquiredNode we do not
// increment the recursion Degree because we know the chain will
// end. For calls to other similar nodes we have to protect against
// too much recursion (time consuming).
//
if (CurrentNode != AcquiredNode) {
//
// Recurse across the graph
//
FoundDeadlock = AVrfpDeadlockAnalyze (ResourceAddress,
CurrentParent,
FALSE,
Degree + 1);
}
else {
//
// Recurse down the graph
//
FoundDeadlock = AVrfpDeadlockAnalyze (ResourceAddress,
CurrentParent,
FALSE,
Degree);
}
if (FoundDeadlock) {
//
// Here we might skip adding a node that was released out of order.
// This will make cycle reporting cleaner but it will be more
// difficult to understand the actual issue. So we will pass
// for now.
//
AVrfpDeadlockAddParticipant(CurrentNode);
if (CurrentNode != AcquiredNode) {
AVrfpDeadlockAddParticipant(AcquiredNode);
}
goto Exit;
}
}
Current = Current->Flink;
}
Exit:
if (FoundDeadlock && FirstCall) {
//
// Make sure that the deadlock does not look like ABC - ACB.
// These sequences are protected by a common resource and therefore
// this is not a real deadlock.
//
if (AVrfpDeadlockCertify ()) {
//
// Print deadlock information and save the address so the
// debugger knows who caused the deadlock.
//
AVrfpDeadlockGlobals->Instigator = ResourceAddress;
DbgPrint("****************************************************************************\n");
DbgPrint("** **\n");
DbgPrint("** Potential deadlock detected! **\n");
DbgPrint("** Type !avrf -dlck in the debugger for more information. **\n");
DbgPrint("** **\n");
DbgPrint("****************************************************************************\n");
AVrfpDeadlockReportIssue (AVRF_DEADLOCK_ISSUE_DEADLOCK_DETECTED,
(ULONG_PTR)ResourceAddress,
(ULONG_PTR)AcquiredNode,
0);
//
// It is impossible to continue at this point.
//
return FALSE;
}
else {
//
// If we decided that this was not a deadlock after all, set the return value
// to not return a deadlock
//
FoundDeadlock = FALSE;
}
}
if (FirstCall) {
if (AVrfpDeadlockGlobals->NodesSearched > AVrfpDeadlockGlobals->MaxNodesSearched) {
AVrfpDeadlockGlobals->MaxNodesSearched = AVrfpDeadlockGlobals->NodesSearched;
}
}
return FoundDeadlock;
}
BOOLEAN
AVrfpDeadlockCertify(
VOID
)
/*++
Routine Description:
A potential deadlock has been detected. However our algorithm will generate
false positives in a certain case -- if two deadlocking nodes are ever taken
after the same node -- i.e. A->B->C A->C->B. While this can be considered
bad programming practice it is not really a deadlock and we should not
bugcheck.
Also we must check to make sure that there are no nodes at the top of the
deadlock chains that have only been acquired with try-acquire... this does
not cause a real deadlock.
The deadlock database lock should be held.
Arguments:
None.
Return Value:
True if this is really a deadlock, false to exonerate.
--*/
{
PAVRF_DEADLOCK_NODE innerNode,outerNode;
ULONG innerParticipant,outerParticipant;
ULONG numberOfParticipants;
ULONG currentParticipant;
numberOfParticipants = AVrfpDeadlockGlobals->NumberOfParticipants;
//
// Note -- this isn't a particularly efficient way to do this. However,
// it is a particularly easy way to do it. This function should be called
// extremely rarely -- so IMO there isn't really a problem here.
//
//
// Outer loop
//
outerParticipant = numberOfParticipants;
while (outerParticipant > 1) {
outerParticipant--;
for (outerNode = AVrfpDeadlockGlobals->Participant[outerParticipant]->Parent;
outerNode != NULL;
outerNode = outerNode->Parent ) {
//
// Inner loop
//
innerParticipant = outerParticipant-1;
while (innerParticipant) {
innerParticipant--;
for (innerNode = AVrfpDeadlockGlobals->Participant[innerParticipant]->Parent;
innerNode != NULL;
innerNode = innerNode->Parent) {
if (innerNode->Root->ResourceAddress == outerNode->Root->ResourceAddress) {
//
// The twain shall meet -- this is not a deadlock
//
AVrfpDeadlockGlobals->ABC_ACB_Skipped++;
return FALSE;
}
}
}
}
}
for (currentParticipant = 1; currentParticipant < numberOfParticipants; currentParticipant += 1) {
if (AVrfpDeadlockGlobals->Participant[currentParticipant]->Root->ResourceAddress ==
AVrfpDeadlockGlobals->Participant[currentParticipant-1]->Root->ResourceAddress) {
//
// This is the head of a chain...
//
if (AVrfpDeadlockGlobals->Participant[currentParticipant-1]->OnlyTryAcquireUsed == TRUE) {
//
// Head of a chain used only try acquire. This can never cause a deadlock.
//
return FALSE;
}
}
}
return TRUE;
}
/////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////// Resource management
/////////////////////////////////////////////////////////////////////
PAVRF_DEADLOCK_RESOURCE
AVrfpDeadlockSearchResource(
IN PVOID ResourceAddress
)
/*++
Routine Description:
This routine finds the resource descriptor structure for a
resource if one exists.
Arguments:
ResourceAddress: Address of the resource in question (as used by
the kernel).
Return Value:
PAVRF_DEADLOCK_RESOURCE structure describing the resource, if available,
or else NULL
Note. The caller of the function should hold the database lock.
--*/
{
PLIST_ENTRY ListHead;
PLIST_ENTRY Current;
PAVRF_DEADLOCK_RESOURCE Resource;
ListHead = AVrfpDeadlockDatabaseHash (AVrfpDeadlockGlobals->ResourceDatabase,
ResourceAddress);
if (IsListEmpty (ListHead)) {
return NULL;
}
//
// Trim resources from this hash list. It has nothing to do with searching
// but it is a good place to do this operation.
//
AVrfpDeadlockTrimResources (ListHead);
//
// Now search the bucket for our resource.
//
Current = ListHead->Flink;
while (Current != ListHead) {
Resource = CONTAINING_RECORD(Current,
AVRF_DEADLOCK_RESOURCE,
HashChainList);
if (Resource->ResourceAddress == ResourceAddress) {
return Resource;
}
Current = Current->Flink;
}
return NULL;
}
BOOLEAN
AVrfpDeadlockResourceInitialize(
IN PVOID Resource,
IN AVRF_DEADLOCK_RESOURCE_TYPE Type,
IN PVOID Caller
)
/*++
Routine Description:
This routine adds an entry for a new resource to our deadlock detection
database.
Arguments:
Resource: Address of the resource in question as used by the kernel.
Type: Type of the resource.
Caller: address of the caller
DoNotAcquireLock: if true it means the call is done internally and the
deadlock verifier lock is already held.
Return Value:
True if we created and initialized a new RESOURCE structure.
--*/
{
PVOID ReservedResource;
BOOLEAN Result;
//
// If we are not initialized or package is not enabled
// we return immediately.
//
if (! AVrfpDeadlockCanProceed()) {
return FALSE;
}
// silviuc: I do not need all this gymnastics with allocations out of locks.
ReservedResource = AVrfpDeadlockAllocate (AVrfpDeadlockResource);
AVrfpDeadlockDetectionLock ();
Result = AVrfpDeadlockAddResource (Resource,
Type,
Caller,
ReservedResource);
AVrfpDeadlockDetectionUnlock ();
return Result;
}
BOOLEAN
AVrfpDeadlockAddResource(
IN PVOID Resource,
IN AVRF_DEADLOCK_RESOURCE_TYPE Type,
IN PVOID Caller,
IN PVOID ReservedResource
)
/*++
Routine Description:
This routine adds an entry for a new resource to our deadlock detection
database.
Arguments:
Resource: Address of the resource in question as used by the kernel.
Type: Type of the resource.
Caller: address of the caller
ReservedResource: block of memory to be used by the new resource.
Return Value:
True if we created and initialized a new RESOURCE structure.
--*/
{
PLIST_ENTRY HashBin;
PAVRF_DEADLOCK_RESOURCE ResourceRoot;
ULONG HashValue;
ULONG DeadlockFlags;
BOOLEAN ReturnValue = FALSE;
//
// Check if this resource was initialized before.
// This would be a bug in most of the cases.
//
ResourceRoot = AVrfpDeadlockSearchResource (Resource);
if (ResourceRoot) {
DeadlockFlags = AVrfpDeadlockResourceTypeInfo[Type];
//
// Check if we are reinitializing a good resource.
//
AVrfpDeadlockReportIssue (AVRF_DEADLOCK_ISSUE_MULTIPLE_INITIALIZATION,
(ULONG_PTR)Resource,
(ULONG_PTR)ResourceRoot,
0);
ReturnValue = TRUE;
goto Exit;
}
//
// At this point we know for sure the resource is not represented in the
// deadlock verifier database.
//
ASSERT (AVrfpDeadlockSearchResource (Resource) == NULL);
//
// Use reserved memory for the new resource.
// Set ReservedResource to null to signal that memory has
// been used. This will prevent freeing it at the end.
//
ResourceRoot = ReservedResource;
ReservedResource = NULL;
if (ResourceRoot == NULL) {
ReturnValue = FALSE;
goto Exit;
}
//
// Fill information about resource.
//
RtlZeroMemory (ResourceRoot, sizeof(AVRF_DEADLOCK_RESOURCE));
ResourceRoot->Type = Type;
ResourceRoot->ResourceAddress = Resource;
InitializeListHead (&ResourceRoot->ResourceList);
//
// Capture the stack trace of the guy that creates the resource first.
// This should happen when resource gets initialized or during the first
// acquire.
//
RtlCaptureStackBackTrace (2,
MAX_TRACE_DEPTH,
ResourceRoot->StackTrace,
&HashValue);
ResourceRoot->StackTrace[0] = Caller;
//
// Figure out which hash bin this resource corresponds to.
//
HashBin = AVrfpDeadlockDatabaseHash (AVrfpDeadlockGlobals->ResourceDatabase, Resource);
//
// Now add to the corresponding hash bin
//
InsertHeadList(HashBin, &ResourceRoot->HashChainList);
ReturnValue = TRUE;
Exit:
if (ReservedResource) {
AVrfpDeadlockFree (ReservedResource, AVrfpDeadlockResource);
}
return ReturnValue;
}
BOOLEAN
AVrfpDeadlockSimilarNode (
IN PVOID Resource,
IN BOOLEAN TryNode,
IN PAVRF_DEADLOCK_NODE Node
)
/*++
Routine description:
This routine determines if an acquisition with the (resource, try)
characteristics is already represented in the Node parameter.
We used to match nodes based on (resource, thread, stack trace, try)
4-tuplet but this really causes an explosion in the number of nodes.
Such a method would yield more accurate proofs but does not affect
the correctness of the deadlock detection algorithms.
Return value:
True if similar node.
--*/
{
ASSERT (Node);
ASSERT (Node->Root);
if (Resource == Node->Root->ResourceAddress
&& TryNode == Node->OnlyTryAcquireUsed) {
//
// Second condition is important to keep nodes for TryAcquire operations
// separated from normal acquires. A TryAcquire cannot cause a deadlock
// and therefore we have to be careful not to report bogus deadlocks.
//
return TRUE;
}
else {
return FALSE;
}
}
VOID
AVrfpDeadlockAcquireResource (
IN PVOID Resource,
IN AVRF_DEADLOCK_RESOURCE_TYPE Type,
IN HANDLE Thread,
IN BOOLEAN TryAcquire,
IN PVOID Caller
)
/*++
Routine Description:
This routine makes sure that it is ok to acquire the resource without
causing a deadlock. It will also update the resource graph with the new
resource acquisition.
Arguments:
Resource: Address of the resource in question as used by kernel.
Type: Type of the resource.
Thread: thread attempting to acquire the resource
TryAcquire: true if this is a tryacquire() operation
Caller: address of the caller
Return Value:
None.
--*/
{
HANDLE CurrentThread;
PAVRF_DEADLOCK_THREAD ThreadEntry;
PAVRF_DEADLOCK_NODE CurrentNode;
PAVRF_DEADLOCK_NODE NewNode;
PAVRF_DEADLOCK_RESOURCE ResourceRoot;
PLIST_ENTRY Current;
ULONG HashValue;
ULONG DeadlockFlags;
BOOLEAN CreatingRootNode = FALSE;
BOOLEAN ThreadCreated = FALSE;
BOOLEAN AddResult;
PVOID ReservedThread;
PVOID ReservedNode;
PVOID ReservedResource;
PAVRF_DEADLOCK_NODE ThreadCurrentNode;
CurrentNode = NULL;
ThreadEntry = NULL;
ThreadCurrentNode = NULL;
//
// If we are not initialized or package is not enabled
// we return immediately.
//
if (! AVrfpDeadlockCanProceed()) {
return;
}
CurrentThread = Thread;
DeadlockFlags = AVrfpDeadlockResourceTypeInfo[Type];
//
// Before getting into the real stuff trim the pool cache.
// This needs to happen out of any locks.
//
AVrfpDeadlockTrimPoolCache ();
//
// Reserve resources that might be needed. If upon exit these
// variables are null it means the allocation either failed or was used.
// In both cases we do not need to free anything.
//
ReservedThread = AVrfpDeadlockAllocate (AVrfpDeadlockThread);
ReservedNode = AVrfpDeadlockAllocate (AVrfpDeadlockNode);
ReservedResource = AVrfpDeadlockAllocate (AVrfpDeadlockResource);
//
// Lock the deadlock database.
//
AVrfpDeadlockDetectionLock();
//
// Allocate a node that might be needed. If we will not use it
// we will deallocate it at the end. If we fail to allocate
// we will return immediately.
//
NewNode = ReservedNode;
ReservedNode = NULL;
if (NewNode == NULL) {
goto Exit;
}
//
// Find the thread descriptor. If there is none we will create one.
//
ThreadEntry = AVrfpDeadlockSearchThread (CurrentThread);
if (ThreadEntry == NULL) {
ThreadEntry = AVrfpDeadlockAddThread (CurrentThread, ReservedThread);
ReservedThread = NULL;
if (ThreadEntry == NULL) {
//
// If we cannot allocate a new thread entry then
// no deadlock detection will happen.
//
goto Exit;
}
ThreadCreated = TRUE;
}
#if DBG
if (ThreadEntry->CurrentTopNode != NULL) {
ASSERT(ThreadEntry->CurrentTopNode->Root->ThreadOwner == ThreadEntry);
ASSERT(ThreadEntry->CurrentTopNode->ThreadEntry == ThreadEntry);
ASSERT(ThreadEntry->NodeCount != 0);
ASSERT(ThreadEntry->CurrentTopNode->Active != 0);
ASSERT(ThreadEntry->CurrentTopNode->Root->NodeCount != 0);
}
#endif
//
// Find the resource descriptor. If we do not find a descriptor
// we will create one on the fly.
//
ResourceRoot = AVrfpDeadlockSearchResource (Resource);
if (ResourceRoot == NULL) {
//
// Complain about the resource not being initialized. After that
// in order to continue we initialize a resource.
//
AVrfpDeadlockReportIssue (AVRF_DEADLOCK_ISSUE_UNINITIALIZED_RESOURCE,
(ULONG_PTR) Resource,
(ULONG_PTR) NULL,
(ULONG_PTR) NULL);
AddResult = AVrfpDeadlockAddResource (Resource,
Type,
Caller,
ReservedResource);
ReservedResource = NULL;
if (AddResult == FALSE) {
//
// If we failed to add the resource then no deadlock detection.
//
if (ThreadCreated) {
AVrfpDeadlockDeleteThread (ThreadEntry, FALSE);
}
goto Exit;
}
//
// Search again the resource. This time we should find it.
//
ResourceRoot = AVrfpDeadlockSearchResource (Resource);
}
//
// At this point we have a THREAD and a RESOURCE to play with.
// In addition we are just about to acquire the resource which means
// there should not be another thread owning unless it is a recursive
// acquisition.
//
ASSERT (ResourceRoot);
ASSERT (ThreadEntry);
ThreadCurrentNode = ThreadEntry->CurrentTopNode;
//
// silviuc: update comment and maybe break?
// Since we just acquired the resource the valid value for ThreadOwner is
// null or ThreadEntry (for a recursive acquisition). This might not be
// true if we missed a release() from an unverified driver. So we will
// not complain about it. We will just put the resource in a consistent
// state and continue;
//
if (ResourceRoot->ThreadOwner) {
if (ResourceRoot->ThreadOwner != ThreadEntry) {
ResourceRoot->RecursionCount = 0;
}
else {
ASSERT (ResourceRoot->RecursionCount > 0);
}
}
else {
ASSERT (ResourceRoot->RecursionCount == 0);
}
ResourceRoot->ThreadOwner = ThreadEntry;
ResourceRoot->RecursionCount += 1;
//
// Check if thread holds any resources. If it does we will have to determine
// at that local point in the dependency graph if we need to create a
// new node. If this is the first resource acquired by the thread we need
// to create a new root node or reuse one created in the past.
//
if (ThreadCurrentNode != NULL) {
//
// If we get here, the current thread had already acquired resources.
// Check to see if this resource has already been acquired.
//
if (ResourceRoot->RecursionCount > 1) {
//
// Recursive acquisition is OK for some resources...
//
if ((DeadlockFlags & AVRF_DEADLOCK_FLAG_RECURSIVE_ACQUISITION_OK) != 0) {
//
// Recursion can't cause a deadlock. Don't set CurrentNode
// since we don't want to move any pointers.
//
goto Exit;
}
else {
//
// This is a recursive acquire for a resource type that is not allowed
// to acquire recursively. Note on continuing from here: we have a recursion
// count of two which will come in handy when the resources are released.
//
AVrfpDeadlockReportIssue (AVRF_DEADLOCK_ISSUE_SELF_DEADLOCK,
(ULONG_PTR)Resource,
(ULONG_PTR)ResourceRoot,
(ULONG_PTR)ThreadEntry);
goto Exit;
}
}
//
// If link already exists, update pointers and exit.
// otherwise check for deadlocks and create a new node
//
Current = ThreadCurrentNode->ChildrenList.Flink;
while (Current != &(ThreadCurrentNode->ChildrenList)) {
CurrentNode = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_NODE,
SiblingsList);
Current = Current->Flink;
if (AVrfpDeadlockSimilarNode (Resource, TryAcquire, CurrentNode)) {
//
// We have found a link. A link that already exists doesn't have
// to be checked for a deadlock because it would have been caught
// when the link was created in the first place. We can just update
// the pointers to reflect the new resource acquired and exit.
//
// We apply our graph compression function to minimize duplicates.
//
AVrfpDeadlockCheckDuplicatesAmongChildren (ThreadCurrentNode,
CurrentNode);
goto Exit;
}
}
//
// Now we know that we're in it for the long haul. We must create a new
// link and make sure that it doesn't cause a deadlock. Later in the
// function CurrentNode being null will signify that we need to create
// a new node.
//
CurrentNode = NULL;
//
// We will analyze deadlock if the resource just about to be acquired
// was acquired before and there are nodes in the graph for the
// resource. Try acquire can not be the cause of a deadlock.
// Don't analyze on try acquires.
//
if (ResourceRoot->NodeCount > 0 && TryAcquire == FALSE) {
if (AVrfpDeadlockAnalyze (Resource, ThreadCurrentNode, TRUE, 0)) {
//
// If we are here we detected deadlock. The analyze() function
// does all the reporting. Being here means we hit `g' in the
// debugger. We will just exit and do not add this resource
// to the graph.
//
goto Exit;
}
}
}
else {
//
// Thread does not have any resources acquired. We have to figure out
// if this is a scenario we have encountered in the past by looking
// at all nodes (that are roots) for the resource to be acquired.
// Note that all this is bookkeeping but we cannot encounter a deadlock
// from now on.
//
PLIST_ENTRY CurrentListEntry;
PAVRF_DEADLOCK_NODE Node = NULL;
BOOLEAN FoundNode = FALSE;
CurrentListEntry = ResourceRoot->ResourceList.Flink;
while (CurrentListEntry != &(ResourceRoot->ResourceList)) {
Node = CONTAINING_RECORD (CurrentListEntry,
AVRF_DEADLOCK_NODE,
ResourceList);
CurrentListEntry = Node->ResourceList.Flink;
if (Node->Parent == NULL) {
if (AVrfpDeadlockSimilarNode (Resource, TryAcquire, Node)) {
//
// We apply our graph compression function to minimize duplicates.
//
AVrfpDeadlockCheckDuplicatesAmongRoots (Node);
FoundNode = TRUE;
break;
}
}
}
if (FoundNode) {
CurrentNode = Node;
goto Exit;
}
else {
CreatingRootNode = TRUE;
}
}
//
// At this moment we know for sure the new link will not cause
// a deadlock. We will create the new resource node.
//
if (NewNode != NULL) {
CurrentNode = NewNode;
//
// Set newnode to NULL to signify it has been used -- otherwise it
// will get freed at the end of this function.
//
NewNode = NULL;
//
// Initialize the new resource node
//
RtlZeroMemory (CurrentNode, sizeof *CurrentNode);
CurrentNode->Active = 0;
CurrentNode->Parent = ThreadCurrentNode;
CurrentNode->Root = ResourceRoot;
CurrentNode->SequenceNumber = AVrfpDeadlockGlobals->SequenceNumber;
InitializeListHead (&(CurrentNode->ChildrenList));
//
// Mark the TryAcquire type of the node.
//
CurrentNode->OnlyTryAcquireUsed = TryAcquire;
//
// Add to the children list of the parent.
//
if (! CreatingRootNode) {
InsertHeadList(&(ThreadCurrentNode->ChildrenList),
&(CurrentNode->SiblingsList));
}
//
// Register the new resource node in the list of nodes maintained
// for this resource.
//
InsertHeadList(&(ResourceRoot->ResourceList),
&(CurrentNode->ResourceList));
ResourceRoot->NodeCount += 1;
if (ResourceRoot->NodeCount > 0xFFF0) {
AVrfpDeadlockState.ResourceNodeCountOverflow = 1;
}
//
// Add to the graph statistics.
//
{
ULONG Level;
Level = AVrfpDeadlockNodeLevel (CurrentNode);
if (Level < 8) {
AVrfpDeadlockGlobals->GraphNodes[Level] += 1;
}
}
}
//
// Exit point.
//
Exit:
//
// Add information we use to identify the culprit should
// a deadlock occur
//
if (CurrentNode) {
ASSERT (ThreadEntry);
ASSERT (ThreadCurrentNode == CurrentNode->Parent);
CurrentNode->Active = 1;
//
// The node should have thread entry field null either because
// it was newly created or because the node was released in the
// past and therefore the field was zeroed.
//
// silviuc: true? What about if we miss release() operations.
//
ASSERT (CurrentNode->ThreadEntry == NULL);
CurrentNode->ThreadEntry = ThreadEntry;
ThreadEntry->CurrentTopNode = CurrentNode;
ThreadEntry->NodeCount += 1;
if (ThreadEntry->NodeCount <= 8) {
AVrfpDeadlockGlobals->NodeLevelCounter[ThreadEntry->NodeCount - 1] += 1;
}
else {
AVrfpDeadlockGlobals->NodeLevelCounter[7] += 1;
}
//
// If we have a parent, save the parent's stack trace
//
if (CurrentNode->Parent) {
RtlCopyMemory(CurrentNode->ParentStackTrace,
CurrentNode->Parent->StackTrace,
sizeof (CurrentNode->ParentStackTrace));
}
//
// Capture stack trace for the current acquire.
//
RtlCaptureStackBackTrace (2,
MAX_TRACE_DEPTH,
CurrentNode->StackTrace,
&HashValue);
if (CurrentNode->Parent) {
CurrentNode->ParentStackTrace[0] = CurrentNode->Parent->StackTrace[0];
}
CurrentNode->StackTrace[0] = Caller;
//
// Copy the trace for the last acquire in the resource object.
//
RtlCopyMemory (CurrentNode->Root->LastAcquireTrace,
CurrentNode->StackTrace,
sizeof (CurrentNode->Root->LastAcquireTrace));
}
//
// We allocated space for a new node but it didn't get used -- put it back
// in the list (don't worry this doesn't do a real 'free' it just puts it
// in a free list).
//
if (NewNode != NULL) {
AVrfpDeadlockFree (NewNode, AVrfpDeadlockNode);
}
//
// Free up unused reserved resources.
// Release deadlock database and return.
//
if (ReservedResource) {
AVrfpDeadlockFree (ReservedResource, AVrfpDeadlockResource);
}
if (ReservedNode) {
AVrfpDeadlockFree (ReservedNode, AVrfpDeadlockNode);
}
if (ReservedThread) {
AVrfpDeadlockFree (ReservedThread, AVrfpDeadlockThread);
}
AVrfpDeadlockDetectionUnlock();
return;
}
VOID
AVrfpDeadlockReleaseResource(
IN PVOID Resource,
IN AVRF_DEADLOCK_RESOURCE_TYPE Type,
IN HANDLE Thread,
IN PVOID Caller
)
/*++
Routine Description:
This routine does the maintenance necessary to release resources from our
deadlock detection database.
Arguments:
Resource: Address of the resource in question.
Thread: thread releasing the resource. In most of the cases this is the
current thread but it might be different for resources that can be
acquired in one thread and released in another one.
Caller: address of the caller of release()
Return Value:
None.
--*/
{
HANDLE CurrentThread;
PAVRF_DEADLOCK_THREAD ThreadEntry;
PAVRF_DEADLOCK_RESOURCE ResourceRoot;
PAVRF_DEADLOCK_NODE ReleasedNode;
ULONG HashValue;
PAVRF_DEADLOCK_NODE ThreadCurrentNode;
UNREFERENCED_PARAMETER (Caller);
UNREFERENCED_PARAMETER (Type);
//
// If we aren't initialized or package is not enabled
// we return immediately.
//
if (! AVrfpDeadlockCanProceed()) {
return;
}
ReleasedNode = NULL;
CurrentThread = Thread;
ThreadEntry = NULL;
AVrfpDeadlockDetectionLock();
ResourceRoot = AVrfpDeadlockSearchResource (Resource);
if (ResourceRoot == NULL) {
// silviuc: we should report an issue. It cannot happen in u-mode.
//
// Release called with a resource address that was never
// stored in our resource database. This can happen in
// the following circumstances:
//
// (a) resource is released but we never seen it before
// because it was acquired in an unverified driver.
//
// (b) we have encountered allocation failures that prevented
// us from completing an acquire() or initialize().
//
// All are legitimate cases and therefore we just ignore the
// release operation.
//
goto Exit;
}
//
// Check if we are trying to release a resource that was never
// acquired.
//
if (ResourceRoot->RecursionCount == 0) {
AVrfpDeadlockReportIssue (AVRF_DEADLOCK_ISSUE_UNACQUIRED_RESOURCE,
(ULONG_PTR)Resource,
(ULONG_PTR)ResourceRoot,
(ULONG_PTR)AVrfpDeadlockSearchThread(CurrentThread));
goto Exit;
}
//
// Look for this thread in our thread list. Note we are looking actually
// for the thread that acquired the resource -- not the current one
// It should, in fact be the current one, but if the resource is being released
// in a different thread from the one it was acquired in, we need the original.
//
ASSERT (ResourceRoot->RecursionCount > 0);
ASSERT (ResourceRoot->ThreadOwner);
ThreadEntry = ResourceRoot->ThreadOwner;
if (ThreadEntry->Thread != CurrentThread) {
//
// silviuc: we have to report this. It is not allowed in U-mode.
//
// Someone acquired a resource that is released in another thread.
// This is bad design but we have to live with it.
//
// NB. If this occurrs, we may call a non-deadlock a deadlock.
// For example, we see a simple deadlock -- AB BA
// If another thread releases B, there won't actually
// be a deadlock. Kind of annoying and ugly.
//
#if DBG
DbgPrint("Thread %p acquired resource %p but thread %p released it\n",
ThreadEntry->Thread, Resource, CurrentThread );
AVrfpDeadlockReportIssue (AVRF_DEADLOCK_ISSUE_UNEXPECTED_THREAD,
(ULONG_PTR)Resource,
(ULONG_PTR)ThreadEntry->Thread,
(ULONG_PTR)CurrentThread
);
#endif
//
// If we don't want this to be fatal, in order to
// continue we must pretend that the current
// thread is the resource's owner.
//
CurrentThread = ThreadEntry->Thread;
}
//
// In this moment we have a resource (ResourceRoot) and a
// thread (ThreadEntry) to play with.
//
ThreadCurrentNode = ThreadEntry->CurrentTopNode;
ASSERT (ResourceRoot && ThreadEntry);
ASSERT (ThreadCurrentNode);
ASSERT (ThreadCurrentNode->Root);
ASSERT (ThreadEntry->NodeCount > 0);
ResourceRoot->RecursionCount -= 1;
if (ResourceRoot->RecursionCount > 0) {
//
// Just decrement the recursion count and do not change any state
//
goto Exit;
}
//
// Wipe out the resource owner.
//
ResourceRoot->ThreadOwner = NULL;
AVrfpDeadlockGlobals->TotalReleases += 1;
//
// Check for out of order releases
//
if (ThreadCurrentNode->Root != ResourceRoot) {
AVrfpDeadlockGlobals->OutOfOrderReleases += 1;
//
// Getting here means that somebody acquires a then b then tries
// to release a before b. This is bad for certain kinds of resources,
// and for others we have to look the other way.
//
if ((AVrfpDeadlockResourceTypeInfo[ThreadCurrentNode->Root->Type] &
AVRF_DEADLOCK_FLAG_REVERSE_RELEASE_OK) == 0) {
// silviuc: In u-mode is always allowed.
DbgPrint("Deadlock detection: Must release resources in reverse-order\n");
DbgPrint("Resource %p acquired before resource %p -- \n"
"Current thread (%p) is trying to release it first\n",
Resource,
ThreadCurrentNode->Root->ResourceAddress,
ThreadEntry);
AVrfpDeadlockReportIssue (AVRF_DEADLOCK_ISSUE_UNEXPECTED_RELEASE,
(ULONG_PTR)Resource,
(ULONG_PTR)ThreadCurrentNode->Root->ResourceAddress,
(ULONG_PTR)ThreadEntry);
}
//
// We need to mark the node for the out of order released resource as
// not active so that other threads will be able to acquire it.
//
{
PAVRF_DEADLOCK_NODE Current;
ASSERT (ThreadCurrentNode->Active == 1);
ASSERT (ThreadCurrentNode->ThreadEntry == ThreadEntry);
Current = ThreadCurrentNode;
while (Current != NULL) {
if (Current->Root == ResourceRoot) {
ASSERT (Current->Active == 1);
ASSERT (Current->Root->RecursionCount == 0);
ASSERT (Current->ThreadEntry == ThreadEntry);
Current->Active = 0;
ReleasedNode = Current;
break;
}
Current = Current->Parent;
}
if (Current == NULL) {
//
// If we do not manage to find an active node we must be in an
// weird state. The resource must be here or else we would have
// gotten an `unxpected release' bugcheck.
//
ASSERT (0);
}
else {
//
// Mark the fact that this node represents a resource
// that can be released out of order. This information is
// important while looking for cycles because this type of
// nodes cannot cause a deadlock.
//
if (Current->ReleasedOutOfOrder == 0) {
AVrfpDeadlockGlobals->NodesReleasedOutOfOrder += 1;
}
Current->ReleasedOutOfOrder = 1;
}
}
}
else {
//
// We need to release the top node held by the thread.
//
ASSERT (ThreadCurrentNode->Active);
ReleasedNode = ThreadCurrentNode;
ReleasedNode->Active = 0;
}
//
// Put the `CurrentNode' field of the thread in a consistent state.
// It should point to the most recent active node that it owns.
//
while (ThreadEntry->CurrentTopNode) {
if (ThreadEntry->CurrentTopNode->Active == 1) {
if (ThreadEntry->CurrentTopNode->ThreadEntry == ThreadEntry) {
break;
}
}
ThreadEntry->CurrentTopNode = ThreadEntry->CurrentTopNode->Parent;
}
Exit:
//
// Properly release the node if there is one to be released.
//
if (ReleasedNode) {
ASSERT (ReleasedNode->Active == 0);
ASSERT (ReleasedNode->Root->ThreadOwner == 0);
ASSERT (ReleasedNode->Root->RecursionCount == 0);
ASSERT (ReleasedNode->ThreadEntry == ThreadEntry);
ASSERT (ThreadEntry->NodeCount > 0);
ASSERT (ThreadEntry->CurrentTopNode != ReleasedNode);
ReleasedNode->ThreadEntry = NULL;
ThreadEntry->NodeCount -= 1;
#if DBG
AVrfpDeadlockCheckNodeConsistency (ReleasedNode, FALSE);
AVrfpDeadlockCheckResourceConsistency (ReleasedNode->Root, FALSE);
AVrfpDeadlockCheckThreadConsistency (ThreadEntry, FALSE);
#endif
if (ThreadEntry && ThreadEntry->NodeCount == 0) {
AVrfpDeadlockDeleteThread (ThreadEntry, FALSE);
}
//
// N.B. Since this is a root node with no children we can delete
// the node too. This would be important to keep memory low. A single node
// can never be the cause of a deadlock. However there are thousands of
// resources used like this and constantly creating and deleting them
// will create a bottleneck. So we prefer to keep them around.
//
#if 0
if (ReleasedNode->Parent == NULL && IsListEmpty(&(ReleasedNode->ChildrenList))) {
AVrfpDeadlockDeleteNode (ReleasedNode, FALSE);
AVrfpDeadlockGlobals->RootNodesDeleted += 1;
}
#endif
}
//
// Capture the trace for the last release in the resource object.
//
if (ResourceRoot) {
RtlCaptureStackBackTrace (2,
MAX_TRACE_DEPTH,
ResourceRoot->LastReleaseTrace,
&HashValue);
}
AVrfpDeadlockDetectionUnlock ();
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////// Thread management
/////////////////////////////////////////////////////////////////////
PAVRF_DEADLOCK_THREAD
AVrfpDeadlockSearchThread (
HANDLE Thread
)
/*++
Routine Description:
This routine searches for a thread in the thread database.
The function assumes the deadlock database lock is held.
Arguments:
Thread - thread address
Return Value:
Address of AVRF_DEADLOCK_THREAD structure if thread was found.
Null otherwise.
--*/
{
PLIST_ENTRY Current;
PLIST_ENTRY ListHead;
PAVRF_DEADLOCK_THREAD ThreadInfo;
ThreadInfo = NULL;
ListHead = AVrfpDeadlockDatabaseHash (AVrfpDeadlockGlobals->ThreadDatabase, Thread);
if (IsListEmpty(ListHead)) {
return NULL;
}
Current = ListHead->Flink;
while (Current != ListHead) {
ThreadInfo = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_THREAD,
ListEntry);
if (ThreadInfo->Thread == Thread) {
return ThreadInfo;
}
Current = Current->Flink;
}
return NULL;
}
PAVRF_DEADLOCK_THREAD
AVrfpDeadlockAddThread (
HANDLE Thread,
PVOID ReservedThread
)
/*++
Routine Description:
This routine adds a new thread to the thread database.
The function assumes the deadlock database lock is held.
Arguments:
Thread - thread address
Return Value:
Address of the AVRF_DEADLOCK_THREAD structure just added.
Null if allocation failed.
--*/
{
PAVRF_DEADLOCK_THREAD ThreadInfo;
PLIST_ENTRY HashBin;
//
// Use reserved block for the new thread. Set ReservedThread
// to null to signal that block was used.
//
ThreadInfo = ReservedThread;
ReservedThread = NULL;
if (ThreadInfo == NULL) {
return NULL;
}
RtlZeroMemory (ThreadInfo, sizeof *ThreadInfo);
ThreadInfo->Thread = Thread;
HashBin = AVrfpDeadlockDatabaseHash (AVrfpDeadlockGlobals->ThreadDatabase, Thread);
InsertHeadList(HashBin, &ThreadInfo->ListEntry);
return ThreadInfo;
}
VOID
AVrfpDeadlockDeleteThread (
PAVRF_DEADLOCK_THREAD Thread,
BOOLEAN Cleanup
)
/*++
Routine Description:
This routine deletes a thread.
Arguments:
Thread - thread address
Cleanup - true if this is a call generated from DeadlockDetectionCleanup().
Return Value:
None.
--*/
{
if (Cleanup == FALSE) {
if (Thread->NodeCount != 0
|| Thread->CurrentTopNode != NULL) {
//
// A thread should not be deleted while it has resources acquired.
//
AVrfpDeadlockReportIssue (AVRF_DEADLOCK_ISSUE_THREAD_HOLDS_RESOURCES,
(ULONG_PTR)(Thread->Thread),
(ULONG_PTR)(Thread),
(ULONG_PTR)0);
}
else {
ASSERT (Thread->NodeCount == 0);
}
}
RemoveEntryList (&(Thread->ListEntry));
AVrfpDeadlockFree (Thread, AVrfpDeadlockThread);
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////// Allocate/Free
/////////////////////////////////////////////////////////////////////
PVOID
AVrfpDeadlockAllocateFromPoolCache (
PULONG Count,
ULONG MaximumCount,
PLIST_ENTRY List,
SIZE_T Offset
)
{
PVOID Address = NULL;
PLIST_ENTRY Entry;
UNREFERENCED_PARAMETER (MaximumCount);
if (*Count > 0) {
*Count -= 1;
Entry = RemoveHeadList (List);
Address = (PVOID)((SIZE_T)Entry - Offset);
}
return Address;
}
VOID
AVrfpDeadlockFreeIntoPoolCache (
PVOID Object,
PULONG Count,
PLIST_ENTRY List,
SIZE_T Offset
)
{
PLIST_ENTRY Entry;
Entry = (PLIST_ENTRY)((SIZE_T)Object + Offset);
*Count += 1;
InsertHeadList(List, Entry);
}
PVOID
AVrfpDeadlockAllocate (
AVRF_DEADLOCK_ALLOC_TYPE Type
)
/*++
Routine Description:
This routine is used to allocate deadlock verifier structures,
that is nodes, resources and threads.
Arguments:
Type - what structure do we need to allocate (node, resource or thread).
Return Value:
Address of the newly allocate structure or null if allocation failed.
Side effects:
If allocation fails the routine will bump the AllocationFailures field
from AVrfpDeadlockGlobals.
--*/
{
PVOID Address = NULL;
SIZE_T Offset;
SIZE_T Size = 0;
//
// If it is a resource, thread, or node alocation, see
// if we have a pre-allocated one on the free list.
//
AVrfpDeadlockDetectionLock ();
switch (Type) {
case AVrfpDeadlockThread:
Offset = (SIZE_T)(&(((PAVRF_DEADLOCK_THREAD)0)->FreeListEntry));
Size = sizeof (AVRF_DEADLOCK_THREAD);
Address = AVrfpDeadlockAllocateFromPoolCache (&(AVrfpDeadlockGlobals->FreeThreadCount),
AVRF_DEADLOCK_MAX_FREE_THREAD,
&(AVrfpDeadlockGlobals->FreeThreadList),
Offset);
break;
case AVrfpDeadlockResource:
Offset = (SIZE_T)(&(((PAVRF_DEADLOCK_RESOURCE)0)->FreeListEntry));
Size = sizeof (AVRF_DEADLOCK_RESOURCE);
Address = AVrfpDeadlockAllocateFromPoolCache (&(AVrfpDeadlockGlobals->FreeResourceCount),
AVRF_DEADLOCK_MAX_FREE_RESOURCE,
&(AVrfpDeadlockGlobals->FreeResourceList),
Offset);
break;
case AVrfpDeadlockNode:
Offset = (SIZE_T)(&(((PAVRF_DEADLOCK_NODE)0)->FreeListEntry));
Size = sizeof (AVRF_DEADLOCK_NODE);
Address = AVrfpDeadlockAllocateFromPoolCache (&(AVrfpDeadlockGlobals->FreeNodeCount),
AVRF_DEADLOCK_MAX_FREE_NODE,
&(AVrfpDeadlockGlobals->FreeNodeList),
Offset);
break;
default:
ASSERT (0);
break;
}
//
// If we did not find anything then go to the process heap for a
// direct allocation.
//
if (Address == NULL) {
// silviuc: it is nice to release the lock but should we?
AVrfpDeadlockDetectionUnlock ();
Address = AVrfpAllocate (Size);
AVrfpDeadlockDetectionLock ();
}
if (Address) {
switch (Type) {
case AVrfpDeadlockThread:
AVrfpDeadlockGlobals->Threads[0] += 1;
if (AVrfpDeadlockGlobals->Threads[0] > AVrfpDeadlockGlobals->Threads[1]) {
AVrfpDeadlockGlobals->Threads[1] = AVrfpDeadlockGlobals->Threads[0];
}
break;
case AVrfpDeadlockResource:
AVrfpDeadlockGlobals->Resources[0] += 1;
if (AVrfpDeadlockGlobals->Resources[0] > AVrfpDeadlockGlobals->Resources[1]) {
AVrfpDeadlockGlobals->Resources[1] = AVrfpDeadlockGlobals->Resources[0];
}
break;
case AVrfpDeadlockNode:
AVrfpDeadlockGlobals->Nodes[0] += 1;
if (AVrfpDeadlockGlobals->Nodes[0] > AVrfpDeadlockGlobals->Nodes[1]) {
AVrfpDeadlockGlobals->Nodes[1] = AVrfpDeadlockGlobals->Nodes[0];
}
break;
default:
ASSERT (0);
break;
}
}
else {
AVrfpDeadlockState.AllocationFailures = 1;
AVrfpDeadlockGlobals->AllocationFailures += 1;
//
// Note that making the AllocationFailures counter bigger than zero
// essentially disables deadlock verification because the CanProceed()
// routine will start returning false.
//
}
//
// Update statistics. No need to zero the block since every
// call site takes care of this.
//
if (Address) {
#if DBG
RtlFillMemory (Address, Size, 0xFF);
#endif
AVrfpDeadlockGlobals->BytesAllocated += Size;
}
AVrfpDeadlockDetectionUnlock ();
return Address;
}
VOID
AVrfpDeadlockFree (
PVOID Object,
AVRF_DEADLOCK_ALLOC_TYPE Type
)
/*++
Routine Description:
This routine deallocates a deadlock verifier structure (node, resource
or thread). The function will place the block in the corrsponding cache
based on the type of the structure. The routine never calls ExFreePool.
The reason for not calling ExFreePool is that we get notifications from
ExFreePool every time it gets called. Sometimes the notification comes
with pool locks held and therefore we cannot call again.
Arguments:
Object - block to deallocate
Type - type of object (node, resource, thread).
Return Value:
None.
--*/
//
// silviuc: update comment
// Note ... if a thread, node, or resource is being freed, we must not
// call ExFreePool. Since the pool lock may be already held, calling ExFreePool
// would cause a recursive spinlock acquisition (which is bad).
// Instead, we move everything to a 'free' list and try to reuse.
// Non-thread-node-resource frees get ExFreePooled
//
{
SIZE_T Offset;
SIZE_T Size = 0;
switch (Type) {
case AVrfpDeadlockThread:
AVrfpDeadlockGlobals->Threads[0] -= 1;
Size = sizeof (AVRF_DEADLOCK_THREAD);
Offset = (SIZE_T)(&(((PAVRF_DEADLOCK_THREAD)0)->FreeListEntry));
AVrfpDeadlockFreeIntoPoolCache (Object,
&(AVrfpDeadlockGlobals->FreeThreadCount),
&(AVrfpDeadlockGlobals->FreeThreadList),
Offset);
break;
case AVrfpDeadlockResource:
AVrfpDeadlockGlobals->Resources[0] -= 1;
Size = sizeof (AVRF_DEADLOCK_RESOURCE);
Offset = (SIZE_T)(&(((PAVRF_DEADLOCK_RESOURCE)0)->FreeListEntry));
AVrfpDeadlockFreeIntoPoolCache (Object,
&(AVrfpDeadlockGlobals->FreeResourceCount),
&(AVrfpDeadlockGlobals->FreeResourceList),
Offset);
break;
case AVrfpDeadlockNode:
AVrfpDeadlockGlobals->Nodes[0] -= 1;
Size = sizeof (AVRF_DEADLOCK_NODE);
Offset = (SIZE_T)(&(((PAVRF_DEADLOCK_NODE)0)->FreeListEntry));
AVrfpDeadlockFreeIntoPoolCache (Object,
&(AVrfpDeadlockGlobals->FreeNodeCount),
&(AVrfpDeadlockGlobals->FreeNodeList),
Offset);
break;
default:
ASSERT (0);
break;
}
AVrfpDeadlockGlobals->BytesAllocated -= Size;
}
VOID
AVrfpDeadlockTrimPoolCache (
VOID
)
/*++
Routine Description:
// silviuc: update comment
This function trims the pool caches to decent levels. It is carefully
written to queue a work item to do the actual processing (freeing of pool)
because the caller may hold various pool mutexes above us.
Arguments:
None.
Return Value:
None.
--*/
{
LOGICAL ShouldTrim = FALSE;
AVrfpDeadlockDetectionLock ();
if (AVrfpDeadlockGlobals->CacheReductionInProgress == TRUE) {
AVrfpDeadlockDetectionUnlock ();
return;
}
if ((AVrfpDeadlockGlobals->FreeThreadCount > AVRF_DEADLOCK_MAX_FREE_THREAD) ||
(AVrfpDeadlockGlobals->FreeNodeCount > AVRF_DEADLOCK_MAX_FREE_NODE) ||
(AVrfpDeadlockGlobals->FreeResourceCount > AVRF_DEADLOCK_MAX_FREE_RESOURCE)) {
ShouldTrim = TRUE;
AVrfpDeadlockGlobals->CacheReductionInProgress = TRUE;
AVrfpDeadlockGlobals->PoolTrimCounter += 1;
}
AVrfpDeadlockDetectionUnlock ();
if (ShouldTrim) {
AVrfpDeadlockTrimPoolCacheWorker (NULL);
}
return;
}
VOID
AVrfpDeadlockTrimPoolCacheWorker (
PVOID Parameter
)
/*++
Routine Description:
This function trims the pool caches to decent levels. It is carefully
written so that ExFreePool is called without holding any deadlock
verifier locks.
Arguments:
None.
Return Value:
None.
Environment:
Worker thread, PASSIVE_LEVEL, no locks held.
--*/
{
LIST_ENTRY ListOfThreads;
LIST_ENTRY ListOfNodes;
LIST_ENTRY ListOfResources;
PLIST_ENTRY Entry;
LOGICAL CacheReductionNeeded;
UNREFERENCED_PARAMETER (Parameter);
CacheReductionNeeded = FALSE;
InitializeListHead (&ListOfThreads);
InitializeListHead (&ListOfNodes);
InitializeListHead (&ListOfResources);
AVrfpDeadlockDetectionLock ();
while (AVrfpDeadlockGlobals->FreeThreadCount > AVRF_DEADLOCK_TRIM_TARGET_THREAD) {
Entry = RemoveHeadList (&(AVrfpDeadlockGlobals->FreeThreadList));
InsertTailList (&ListOfThreads, Entry);
AVrfpDeadlockGlobals->FreeThreadCount -= 1;
CacheReductionNeeded = TRUE;
}
while (AVrfpDeadlockGlobals->FreeNodeCount > AVRF_DEADLOCK_TRIM_TARGET_NODE) {
Entry = RemoveHeadList (&(AVrfpDeadlockGlobals->FreeNodeList));
InsertTailList (&ListOfNodes, Entry);
AVrfpDeadlockGlobals->FreeNodeCount -= 1;
CacheReductionNeeded = TRUE;
}
while (AVrfpDeadlockGlobals->FreeResourceCount > AVRF_DEADLOCK_TRIM_TARGET_RESOURCE) {
Entry = RemoveHeadList (&(AVrfpDeadlockGlobals->FreeResourceList));
InsertTailList (&ListOfResources, Entry);
AVrfpDeadlockGlobals->FreeResourceCount -= 1;
CacheReductionNeeded = TRUE;
}
//
// Don't clear CacheReductionInProgress until the pool allocations are
// freed to prevent needless recursion.
//
if (CacheReductionNeeded == FALSE) {
AVrfpDeadlockGlobals->CacheReductionInProgress = FALSE;
AVrfpDeadlockDetectionUnlock ();
return;
}
AVrfpDeadlockDetectionUnlock ();
//
// Now, out of the deadlock verifier lock we can deallocate the
// blocks trimmed.
//
Entry = ListOfThreads.Flink;
while (Entry != &ListOfThreads) {
PAVRF_DEADLOCK_THREAD Block;
Block = CONTAINING_RECORD (Entry,
AVRF_DEADLOCK_THREAD,
FreeListEntry);
Entry = Entry->Flink;
AVrfpFree (Block);
}
Entry = ListOfNodes.Flink;
while (Entry != &ListOfNodes) {
PAVRF_DEADLOCK_NODE Block;
Block = CONTAINING_RECORD (Entry,
AVRF_DEADLOCK_NODE,
FreeListEntry);
Entry = Entry->Flink;
AVrfpFree (Block);
}
Entry = ListOfResources.Flink;
while (Entry != &ListOfResources) {
PAVRF_DEADLOCK_RESOURCE Block;
Block = CONTAINING_RECORD (Entry,
AVRF_DEADLOCK_RESOURCE,
FreeListEntry);
Entry = Entry->Flink;
AVrfpFree (Block);
}
//
// It's safe to clear CacheReductionInProgress now that the pool
// allocations are freed.
//
AVrfpDeadlockDetectionLock ();
AVrfpDeadlockGlobals->CacheReductionInProgress = FALSE;
AVrfpDeadlockDetectionUnlock ();
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////// Error reporting and debugging
/////////////////////////////////////////////////////////////////////
//
// Variable accessed by the !deadlock debug extension to investigate
// failures.
//
ULONG_PTR AVrfpDeadlockIssue[4];
VOID
AVrfpDeadlockReportIssue (
ULONG_PTR Param1,
ULONG_PTR Param2,
ULONG_PTR Param3,
ULONG_PTR Param4
)
/*++
Routine Description:
This routine is called to report a deadlock verifier issue.
If we are in debug mode we will just break in debugger.
Otherwise we will bugcheck,
Arguments:
Param1..Param4 - relevant information for the point of failure.
Return Value:
None.
--*/
{
AVrfpDeadlockIssue[0] = Param1;
AVrfpDeadlockIssue[1] = Param2;
AVrfpDeadlockIssue[2] = Param3;
AVrfpDeadlockIssue[3] = Param4;
if (AVrfpDeadlockDebug) {
DbgPrint ("AVRF: deadlock: stop: %p %p %p %p %p \n",
DRIVER_VERIFIER_DETECTED_VIOLATION,
Param1,
Param2,
Param3,
Param4);
DbgBreakPoint ();
}
else {
// silviuc: APPLICATION_VERIFIER_DEADLOCK_ISSUE
VERIFIER_STOP (APPLICATION_VERIFIER_UNKNOWN_ERROR,
"Application verifier deadlock/resource issue",
Param1, "",
Param2, "",
Param3, "",
Param4, "");
}
}
VOID
AVrfpDeadlockAddParticipant(
PAVRF_DEADLOCK_NODE Node
)
/*++
Routine Description:
Adds a new node to the set of nodes involved in a deadlock.
The function is called only from AVrfpDeadlockAnalyze().
Arguments:
Node - node to be added to the deadlock participants collection.
Return Value:
None.
--*/
{
ULONG Index;
Index = AVrfpDeadlockGlobals->NumberOfParticipants;
if (Index >= NO_OF_DEADLOCK_PARTICIPANTS) {
AVrfpDeadlockState.DeadlockParticipantsOverflow = 1;
return;
}
AVrfpDeadlockGlobals->Participant[Index] = Node;
AVrfpDeadlockGlobals->NumberOfParticipants += 1;
}
/////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////// Resource cleanup
/////////////////////////////////////////////////////////////////////
VOID
AVrfpDeadlockDeleteResource (
PAVRF_DEADLOCK_RESOURCE Resource,
BOOLEAN Cleanup
)
/*++
Routine Description:
This routine deletes a routine and all nodes representing
acquisitions of that resource.
Arguments:
Resource - resource to be deleted
Cleanup - true if are called from AVrfpDeadlockDetectionCleanup
Return Value:
None.
--*/
{
PLIST_ENTRY Current;
PAVRF_DEADLOCK_NODE Node;
ASSERT (Resource != NULL);
//
// Check if the resource being deleted is still acquired.
// If it is we will release it ourselves in order to put in
// order all internal deadlock verifier structures. Unfortunately
// it is not a bug to delete a critical section that is not released.
// Too many people already do it to change the rules in mid flight.
//
if (Cleanup == FALSE && Resource->ThreadOwner != NULL) {
while (Resource->RecursionCount > 0) {
AVrfDeadlockResourceRelease (Resource->ResourceAddress,
_ReturnAddress());
}
}
ASSERT (Resource->ThreadOwner == NULL);
ASSERT (Resource->RecursionCount == 0);
//
// If this is a normal delete (not a cleanup) we will collapse all trees
// containing nodes for this resource. If it is a cleanup we will just
// wipe out the node.
//
Current = Resource->ResourceList.Flink;
while (Current != &(Resource->ResourceList)) {
Node = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_NODE,
ResourceList);
Current = Current->Flink;
ASSERT (Node->Root == Resource);
AVrfpDeadlockDeleteNode (Node, Cleanup);
}
//
// There should not be any NODEs for the resource at this moment.
//
ASSERT (&(Resource->ResourceList) == Resource->ResourceList.Flink);
ASSERT (&(Resource->ResourceList) == Resource->ResourceList.Blink);
//
// Remote the resource from the hash table and
// delete the resource structure.
//
RemoveEntryList (&(Resource->HashChainList));
AVrfpDeadlockFree (Resource, AVrfpDeadlockResource);
}
VOID
AVrfpDeadlockTrimResources (
PLIST_ENTRY HashList
)
{
PLIST_ENTRY Current;
PAVRF_DEADLOCK_RESOURCE Resource;
ULONG Counter;
AVrfpDeadlockGlobals->ForgetHistoryCounter += 1;
Counter = AVrfpDeadlockGlobals->ForgetHistoryCounter;
Counter %= AVRF_DEADLOCK_FORGET_HISTORY_FREQUENCY;
if (Counter == 0) {
Current = HashList->Flink;
while (Current != HashList) {
Resource = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_RESOURCE,
HashChainList);
Current = Current->Flink;
AVrfpDeadlockForgetResourceHistory (Resource,
AVrfpDeadlockTrimThreshold,
AVrfpDeadlockAgeWindow);
}
}
}
VOID
AVrfpDeadlockForgetResourceHistory (
PAVRF_DEADLOCK_RESOURCE Resource,
ULONG TrimThreshold,
ULONG AgeThreshold
)
/*++
Routine Description:
This routine deletes sone of the nodes representing
acquisitions of that resource. In essence we forget
part of the history of that resource.
Arguments:
Resource - resource for which we wipe out nodes.
TrimThreshold - how many nodes should remain
AgeThreshold - nodes older than this will go away
Return Value:
None.
--*/
{
PLIST_ENTRY Current;
PAVRF_DEADLOCK_NODE Node;
ULONG NodesTrimmed = 0;
ULONG SequenceNumber;
ASSERT (Resource != NULL);
//
// If resource is owned we cannot do anything,
//
if (Resource->ThreadOwner) {
return;
}
//
// If resource has less than TrimThreshold nodes it is still fine.
//
if (Resource->NodeCount < TrimThreshold) {
return;
}
//
// Delete some nodes of the resource based on ageing.
//
SequenceNumber = AVrfpDeadlockGlobals->SequenceNumber;
Current = Resource->ResourceList.Flink;
while (Current != &(Resource->ResourceList)) {
Node = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_NODE,
ResourceList);
Current = Current->Flink;
ASSERT (Node->Root == Resource);
//
// Special care here because the sequence numbers are 32bits
// and they can overflow. In an ideal world the global sequence
// is always greater or equal to the node sequence but if it
// overwrapped it can be the other way around.
//
if (SequenceNumber > Node->SequenceNumber) {
if (SequenceNumber - Node->SequenceNumber > AgeThreshold) {
AVrfpDeadlockDeleteNode (Node, FALSE);
NodesTrimmed += 1;
}
}
else {
if (Node->SequenceNumber - SequenceNumber < AgeThreshold) {
AVrfpDeadlockDeleteNode (Node, FALSE);
NodesTrimmed += 1;
}
}
}
AVrfpDeadlockGlobals->NodesTrimmedBasedOnAge += NodesTrimmed;
//
// If resource has less than TrimThreshold nodes it is fine.
//
if (Resource->NodeCount < TrimThreshold) {
return;
}
//
// If we did not manage to trim the nodes by the age algorithm then
// we trim everything that we encounter.
//
NodesTrimmed = 0;
Current = Resource->ResourceList.Flink;
while (Current != &(Resource->ResourceList)) {
if (Resource->NodeCount < TrimThreshold) {
break;
}
Node = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_NODE,
ResourceList);
Current = Current->Flink;
ASSERT (Node->Root == Resource);
AVrfpDeadlockDeleteNode (Node, FALSE);
NodesTrimmed += 1;
}
AVrfpDeadlockGlobals->NodesTrimmedBasedOnCount += NodesTrimmed;
}
VOID
AVrfpDeadlockDeleteNode (
PAVRF_DEADLOCK_NODE Node,
BOOLEAN Cleanup
)
/*++
Routine Description:
This routine deletes a node from a graph and collapses the tree,
that is connects its childrend with its parent.
If we are during a cleanup we will just delete the node without
collapsing the tree.
Arguments:
Node - node to be deleted.
Cleanup - true if we are during a total cleanup
Return Value:
None.
--*/
{
PLIST_ENTRY Current;
PAVRF_DEADLOCK_NODE Child;
ULONG Children;
ASSERT (Node);
//
// If are during a cleanup just delete the node and return.
//
if (Cleanup) {
RemoveEntryList (&(Node->ResourceList));
AVrfpDeadlockFree (Node, AVrfpDeadlockNode);
return;
}
//
// If we are here we need to collapse the tree
//
if (Node->Parent) {
//
// All Node's children must become Parent's children
//
Current = Node->ChildrenList.Flink;
while (Current != &(Node->ChildrenList)) {
Child = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_NODE,
SiblingsList);
Current = Current->Flink;
RemoveEntryList (&(Child->SiblingsList));
Child->Parent = Node->Parent;
InsertTailList (&(Node->Parent->ChildrenList),
&(Child->SiblingsList));
}
RemoveEntryList (&(Node->SiblingsList));
}
else {
//
// All Node's children must become roots of the graph
//
Current = Node->ChildrenList.Flink;
Children = 0;
Child = NULL;
while (Current != &(Node->ChildrenList)) {
Children += 1;
Child = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_NODE,
SiblingsList);
Current = Current->Flink;
RemoveEntryList (&(Child->SiblingsList));
Child->Parent = NULL;
Child->SiblingsList.Flink = NULL;
Child->SiblingsList.Blink = NULL;
}
}
ASSERT (Node->Root);
ASSERT (Node->Root->NodeCount > 0);
Node->Root->NodeCount -= 1;
RemoveEntryList (&(Node->ResourceList));
AVrfpDeadlockFree (Node, AVrfpDeadlockNode);
}
ULONG
AVrfpDeadlockNodeLevel (
PAVRF_DEADLOCK_NODE Node
)
/*++
Routine Description:
This routine computes the level of a graph node.
Arguments:
Node - graph node
Return Value:
Level of the node. A root node has level zero.
--*/
{
PAVRF_DEADLOCK_NODE Current;
ULONG Level = 0;
Current = Node->Parent;
while (Current) {
Level += 1;
Current = Current->Parent;
}
return Level;
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////// Incremental graph compression
/////////////////////////////////////////////////////////////////////
//
// SilviuC: should write a comment about graph compression
// This is a very smart and tricky algorithm :-)
//
VOID
AVrfpDeadlockCheckDuplicatesAmongChildren (
PAVRF_DEADLOCK_NODE Parent,
PAVRF_DEADLOCK_NODE Child
)
{
PLIST_ENTRY Current;
PAVRF_DEADLOCK_NODE Node;
LOGICAL FoundOne;
FoundOne = FALSE;
Current = Parent->ChildrenList.Flink;
while (Current != &(Parent->ChildrenList)) {
Node = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_NODE,
SiblingsList);
ASSERT (Current->Flink);
Current = Current->Flink;
if (AVrfpDeadlockSimilarNodes (Node, Child)) {
if (FoundOne == FALSE) {
ASSERT (Node == Child);
FoundOne = TRUE;
}
else {
AVrfpDeadlockMergeNodes (Child, Node);
}
}
}
}
VOID
AVrfpDeadlockCheckDuplicatesAmongRoots (
PAVRF_DEADLOCK_NODE Root
)
{
PLIST_ENTRY Current;
PAVRF_DEADLOCK_NODE Node;
PAVRF_DEADLOCK_RESOURCE Resource;
LOGICAL FoundOne;
FoundOne = FALSE;
Resource = Root->Root;
Current = Resource->ResourceList.Flink;
while (Current != &(Resource->ResourceList)) {
Node = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_NODE,
ResourceList);
ASSERT (Current->Flink);
Current = Current->Flink;
if (Node->Parent == NULL && AVrfpDeadlockSimilarNodes (Node, Root)) {
if (FoundOne == FALSE) {
ASSERT (Node == Root);
FoundOne = TRUE;
}
else {
AVrfpDeadlockMergeNodes (Root, Node);
}
}
}
}
LOGICAL
AVrfpDeadlockSimilarNodes (
PAVRF_DEADLOCK_NODE NodeA,
PAVRF_DEADLOCK_NODE NodeB
)
{
if (NodeA->Root == NodeB->Root
&& NodeA->OnlyTryAcquireUsed == NodeB->OnlyTryAcquireUsed) {
return TRUE;
}
else {
return FALSE;
}
}
VOID
AVrfpDeadlockMergeNodes (
PAVRF_DEADLOCK_NODE NodeTo,
PAVRF_DEADLOCK_NODE NodeFrom
)
{
PLIST_ENTRY Current;
PAVRF_DEADLOCK_NODE Node;
//
// If NodeFrom is currently acquired then copy the same
// characteristics to NodeTo. Since the locks are exclusive
// it is impossible to have NodeTo also acquired.
//
if (NodeFrom->ThreadEntry) {
ASSERT (NodeTo->ThreadEntry == NULL);
NodeTo->ThreadEntry = NodeFrom->ThreadEntry;
RtlCopyMemory (NodeTo->StackTrace,
NodeFrom->StackTrace,
sizeof (NodeTo->StackTrace));
RtlCopyMemory (NodeTo->ParentStackTrace,
NodeFrom->ParentStackTrace,
sizeof (NodeTo->ParentStackTrace));
}
if (NodeFrom->Active) {
ASSERT (NodeTo->Active == 0);
NodeTo->Active = NodeFrom->Active;
}
//
// Move each child of NodeFrom as a child of NodeTo.
//
Current = NodeFrom->ChildrenList.Flink;
while (Current != &(NodeFrom->ChildrenList)) {
Node = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_NODE,
SiblingsList);
ASSERT (Current->Flink);
Current = Current->Flink;
RemoveEntryList (&(Node->SiblingsList));
ASSERT (Node->Parent == NodeFrom);
Node->Parent = NodeTo;
InsertTailList (&(NodeTo->ChildrenList),
&(Node->SiblingsList));
}
//
// NodeFrom is empty. Delete it.
//
ASSERT (IsListEmpty(&(NodeFrom->ChildrenList)));
if (NodeFrom->Parent) {
RemoveEntryList (&(NodeFrom->SiblingsList));
}
NodeFrom->Root->NodeCount -= 1;
RemoveEntryList (&(NodeFrom->ResourceList));
AVrfpDeadlockFree (NodeFrom, AVrfpDeadlockNode);
}
/////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////// Consistency checks
/////////////////////////////////////////////////////////////////////
//
// Node Resource Thread
//
// Root ThreadOwner CurrentNode
// ThreadEntry RecursionCount NodeCount
// Active ResourceAddress Thread
//
//
//
//
VOID
AVrfpDeadlockCheckThreadConsistency (
PAVRF_DEADLOCK_THREAD Thread,
BOOLEAN Recursion
)
{
if (Thread->CurrentTopNode == NULL) {
ASSERT (Thread->NodeCount == 0);
return;
}
if (Thread->CurrentTopNode) {
ASSERT (Thread->NodeCount > 0);
ASSERT (Thread->CurrentTopNode->Active);
if (Recursion == FALSE) {
AVrfpDeadlockCheckNodeConsistency (Thread->CurrentTopNode, TRUE);
AVrfpDeadlockCheckResourceConsistency (Thread->CurrentTopNode->Root, TRUE);
}
}
if (Thread->CurrentTopNode) {
ASSERT (Thread->NodeCount > 0);
ASSERT (Thread->CurrentTopNode->Active);
if (Recursion == FALSE) {
AVrfpDeadlockCheckNodeConsistency (Thread->CurrentTopNode, TRUE);
AVrfpDeadlockCheckResourceConsistency (Thread->CurrentTopNode->Root, TRUE);
}
}
}
VOID
AVrfpDeadlockCheckNodeConsistency (
PAVRF_DEADLOCK_NODE Node,
BOOLEAN Recursion
)
{
if (Node->ThreadEntry) {
ASSERT (Node->Active == 1);
if (Recursion == FALSE) {
AVrfpDeadlockCheckThreadConsistency (Node->ThreadEntry, TRUE);
AVrfpDeadlockCheckResourceConsistency (Node->Root, TRUE);
}
}
else {
ASSERT (Node->Active == 0);
if (Recursion == FALSE) {
AVrfpDeadlockCheckResourceConsistency (Node->Root, TRUE);
}
}
}
VOID
AVrfpDeadlockCheckResourceConsistency (
PAVRF_DEADLOCK_RESOURCE Resource,
BOOLEAN Recursion
)
{
if (Resource->ThreadOwner) {
ASSERT (Resource->RecursionCount > 0);
if (Recursion == FALSE) {
AVrfpDeadlockCheckThreadConsistency (Resource->ThreadOwner, TRUE);
AVrfpDeadlockCheckNodeConsistency (Resource->ThreadOwner->CurrentTopNode, TRUE);
}
}
else {
ASSERT (Resource->RecursionCount == 0);
}
}
PAVRF_DEADLOCK_THREAD
AVrfpDeadlockCheckThreadReferences (
PAVRF_DEADLOCK_NODE Node
)
/*++
Routine Description:
This routine iterates all threads in order to check if `Node' is
referred in the `CurrentNode' field in any of them.
Arguments:
Node - node to search
Return Value:
If everything goes ok we should not find the node and the return
value is null. Otherwise we return the thread referring to the node.
--*/
{
ULONG Index;
PLIST_ENTRY Current;
PAVRF_DEADLOCK_THREAD Thread;
for (Index = 0; Index < AVRF_DEADLOCK_HASH_BINS; Index += 1) {
Current = AVrfpDeadlockGlobals->ThreadDatabase[Index].Flink;
while (Current != &(AVrfpDeadlockGlobals->ThreadDatabase[Index])) {
Thread = CONTAINING_RECORD (Current,
AVRF_DEADLOCK_THREAD,
ListEntry);
if (Thread->CurrentTopNode == Node) {
return Thread;
}
if (Thread->CurrentTopNode == Node) {
return Thread;
}
Current = Current->Flink;
}
}
return NULL;
}