reactos/sdk/lib/drivers/wdf/shared/inc/private/common/fxverifierlock.hpp
Victor Perevertkin 1f377076d7
[WDF] Fix KMDF so it can compile with ReactOS SDK
Not all files are included, but these are necessary to compile cdrom driver.
So far it can only be statically linked with drivers, a proper
implementation requires wdfldr helper driver
2020-11-03 00:06:27 +03:00

562 lines
18 KiB
C++

/*++
Copyright (c) Microsoft Corporation
Module Name:
FxVerifierLock.hpp
Abstract:
This is the C++ header for the verifier FxLock
This separate object allows the verifier overhead
to only be allocated when WdfVerifierLock is on.
Author:
Revision History:
Made it mode agnostic
New failure paths:
m_Mutex/m_Lock initialize
To enforce initialization hidden constructors
Callers are forced to use CreateAndInitialize methods
--*/
#ifndef _FXVERIFIERLOCK_HPP_
#define _FXVERIFIERLOCK_HPP_
extern "C" {
#if defined(EVENT_TRACING)
#include "FxVerifierLock.hpp.tmh"
#endif
}
/**
* These define the lock order used by verifier
* for basic objects internal to the driver frameworks.
*
* Higher numbers are "lower" locks in the hierachy, which means
* a lock can be acquired if its number greater than or equal
* to the current one.
*
* Correct Order:
*
* FX_LOCK_ORDER_DRIVER -> FX_LOCK_ORDER_QUEUE -> FX_LOCK_ORDER_REQUEST
*
* Incorrect Order:
*
* FX_LOCK_ORDER_DRIVER -> FX_LOCK_ORDER_QUEUE -> FX_LOCK_ORDER_DEVICE
*
* FX_LOCK_ORDER_UNKNOWN represents an object who has not (yet)
* defined a lock order. It has the highest number, meaning it
* can be acquired holding any other locks, including itself.
* At some point in time, this will cause a verifier break point,
* otherwise we can not fully test the frameworks.
*
* FX_LOCK_ORDER_NONE is a statement by the object that it will
* not use its Lock/Unlock routines. Use of locks on this object
* under verifier will cause a verifier breakpoint.
*
* There is a table mapping these from FX_TYPE_* to the lock orders
* define here in fx\core\FxVerifierLock.cpp
*
*/
//
// These locks are driver frameworks "internal" object
// locks and are not intended to be held across callbacks
// to the driver.
//
// They are acquired and released as the result of driver
// calls into the frameworks, which may be holding a driver
// callback lock.
//
#define FX_LOCK_ORDER_NONE 0x0000
#define FX_LOCK_ORDER_UNKNOWN 0xFFFF
#define FX_LOCK_ORDER_PACKAGE_PDO 0x1000
#define FX_LOCK_ORDER_PACKAGE_FDO 0x1000
#define FX_LOCK_ORDER_WMI_IRP_HANDLER 0x1000
#define FX_LOCK_ORDER_PACKAGE_GENERAL 0x1000
#define FX_LOCK_ORDER_IO_TARGET 0x1000
#define FX_LOCK_ORDER_WMI_PROVIDER 0x1001
#define FX_LOCK_ORDER_WMI_INSTANCE 0x1002
#define FX_LOCK_ORDER_DMA_ENABLER 0x1000
#define FX_LOCK_ORDER_DMA_TRANSACTION 0x1001
#define FX_LOCK_ORDER_COMMON_BUFFER 0x1001
//
// A USB device owns a bunch of pipes, so make sure that device can acquire a
// pipe lock while locked, but not vice versa
//
#define FX_LOCK_ORDER_USB_DEVICE_IO_TARGET 0x1000
#define FX_LOCK_ORDER_USB_PIPE_IO_TARGET 0x1001
#define FX_LOCK_ORDER_DRIVER 0x1010
#define FX_LOCK_ORDER_DEVICE 0x1020
#define FX_LOCK_ORDER_MP_DEVICE 0x1020
#define FX_LOCK_ORDER_DEFAULT_IRP_HANDLER 0x1030
#define FX_LOCK_ORDER_QUEUE 0x1030
#define FX_LOCK_ORDER_PACKAGE_IO 0x1031
#define FX_LOCK_ORDER_REQUEST 0x1040
#define FX_LOCK_ORDER_IRPQUEUE 0x1051
#define FX_LOCK_ORDER_TIMER 0x1059
#define FX_LOCK_ORDER_DPC 0x1060
#define FX_LOCK_ORDER_WORKITEM 0x1060
#define FX_LOCK_ORDER_CLEANUPLIST 0x1060
#define FX_LOCK_ORDER_INTERRUPT 0x1060
#define FX_LOCK_ORDER_FILEOBJECT 0x1060
#define FX_LOCK_ORDER_DEVICE_LIST 0x1061
#define FX_LOCK_ORDER_COLLECTION 0x1070 // collection can be used in any
// of the above object's callbacks
#define FX_LOCK_ORDER_USEROBJECT 0x2000
// dispose list is very far down in the list because pretty much any item can
// be added to the dispose list while that object's lock is being held
#define FX_LOCK_ORDER_DISPOSELIST 0x8000
#define FX_LOCK_ORDER_SYSTEMWORKITEM FX_LOCK_ORDER_UNKNOWN
#define FX_LOCK_ORDER_SYSTEMTHREAD FX_LOCK_ORDER_UNKNOWN // No lock level
//
// These are the device driver callback locks
// used to synchronize callback to the device
// driver. They are "higher" than the internal
// frameworks locks since an internal frameworks lock
// should not be held when these are acquired.
//
// (which should only be due to FxCallback.Invoke to a driver
// event handler)
//
// This means they must have a lower number than the internal frameworks
// locks.
//
// They may be held when a device driver is calling into a frameworks
// DDI while in an event callback handler.
//
// These levels enforce not only the level of locks acquired
// and released by and for the driver, but also the rules
// about holding internal frameworks locks when calling into
// a driver. If a frameworks bug is holding a frameworks lock
// when it goes to do a callback, the attempt to acquire the
// lower numbered lock will raise the error.
//
#define FX_CALLBACKLOCK_ORDER_DRIVER 0x10
#define FX_CALLBACKLOCK_ORDER_DEVICE 0x20
#define FX_CALLBACKLOCK_ORDER_PACKAGE 0x30
#define FX_CALLBACKLOCK_ORDER_QUEUE 0x31
#define FX_VERIFIER_LOCK_ENTRY(FX_OBJECT_TYPE, FX_LOCK_ORDER) { FX_OBJECT_TYPE, FX_LOCK_ORDER }
// Internal FxLock spinlock entries
#define FX_VERIFIER_LOCK_ENTRIES() \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_DRIVER, FX_LOCK_ORDER_DRIVER), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_DEVICE, FX_LOCK_ORDER_DEVICE), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_MP_DEVICE, FX_LOCK_ORDER_MP_DEVICE), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_PACKAGE_IO, FX_LOCK_ORDER_PACKAGE_IO), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_WMI_INSTANCE, FX_LOCK_ORDER_WMI_INSTANCE), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_WMI_PROVIDER, FX_LOCK_ORDER_WMI_PROVIDER), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_QUEUE, FX_LOCK_ORDER_QUEUE), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_REQUEST, FX_LOCK_ORDER_REQUEST), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_IRPQUEUE, FX_LOCK_ORDER_IRPQUEUE), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_PACKAGE_PDO, FX_LOCK_ORDER_PACKAGE_PDO), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_PACKAGE_FDO, FX_LOCK_ORDER_PACKAGE_FDO), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_WMI_IRP_HANDLER, FX_LOCK_ORDER_WMI_IRP_HANDLER), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_PACKAGE_GENERAL, FX_LOCK_ORDER_PACKAGE_GENERAL), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_DMA_ENABLER, FX_LOCK_ORDER_DMA_ENABLER), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_DMA_TRANSACTION, FX_LOCK_ORDER_DMA_TRANSACTION), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_COMMON_BUFFER, FX_LOCK_ORDER_COMMON_BUFFER), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_IO_TARGET, FX_LOCK_ORDER_IO_TARGET), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_IO_TARGET_SELF, FX_LOCK_ORDER_IO_TARGET), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_IO_TARGET_USB_DEVICE,FX_LOCK_ORDER_USB_DEVICE_IO_TARGET),\
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_IO_TARGET_USB_PIPE, FX_LOCK_ORDER_USB_PIPE_IO_TARGET), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_DPC, FX_LOCK_ORDER_DPC), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_WORKITEM, FX_LOCK_ORDER_WORKITEM), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_SYSTEMTHREAD, FX_LOCK_ORDER_SYSTEMTHREAD), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_CLEANUPLIST, FX_LOCK_ORDER_CLEANUPLIST), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_INTERRUPT, FX_LOCK_ORDER_INTERRUPT), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_TIMER, FX_LOCK_ORDER_TIMER), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_FILEOBJECT, FX_LOCK_ORDER_FILEOBJECT), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_CHILD_LIST, FX_LOCK_ORDER_DEVICE_LIST), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_SYSTEMWORKITEM, FX_LOCK_ORDER_SYSTEMWORKITEM), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_DEFAULT_IRP_HANDLER, FX_LOCK_ORDER_DEFAULT_IRP_HANDLER), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_COLLECTION, FX_LOCK_ORDER_COLLECTION), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_DISPOSELIST, FX_LOCK_ORDER_DISPOSELIST), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_USEROBJECT, FX_LOCK_ORDER_USEROBJECT), \
FX_VERIFIER_LOCK_ENTRY(0, 0)
// Device Driver Callback lock entries
#define FX_VERIFIER_CALLBACKLOCK_ENTRIES() \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_DRIVER, FX_CALLBACKLOCK_ORDER_DRIVER), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_DEVICE, FX_CALLBACKLOCK_ORDER_DEVICE), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_PACKAGE_IO, FX_CALLBACKLOCK_ORDER_PACKAGE), \
FX_VERIFIER_LOCK_ENTRY(FX_TYPE_QUEUE, FX_CALLBACKLOCK_ORDER_QUEUE), \
FX_VERIFIER_LOCK_ENTRY(0, 0)
//
// Mapping table structure between Fx object types and lock orders
//
struct FxVerifierOrderMapping {
USHORT ObjectType;
USHORT ObjectLockOrder;
};
typedef struct FxVerifierOrderMapping *pFxVerifierOrderMapping;
//
// This must be a power of two for hash algorithm
//
// Table size is dependent on the number of active threads
// in the frameworks at a given time. This can not execeed
// the number of (virtual due to hyperthreading) CPU's in the system.
//
// Setting this number lower just increases collisions, but does
// not impede correct function.
//
#define VERIFIER_THREAD_HASHTABLE_SIZE 64
//
// This structure is used by verifier hash table implementation
// for linking records per thread
//
struct FxVerifierThreadTableEntry {
MxThread Thread;
FxVerifierLock* PerThreadPassiveLockList;
FxVerifierLock* PerThreadDispatchLockList;
LIST_ENTRY HashChain;
};
typedef struct FxVerifierThreadTableEntry *pFxVerifierThreadTableEntry;
/*
* This lock performs the same actions as the base
* FxLock, but provides additional tracking of lock
* order to help detect and debug
* deadlock and recursive lock situations.
*
* It does not inherit from FxLock since this would
* cause a recursion chain.
*
* Since lock verification is already more costly than
* basic lock use, this verifier lock supports both the
* FAST_MUTEX and SpinLock modes in order to utilize common
* code to support verification of FxCallback locks.
*
* FxVerifierLocks are only allocated when WdfVerifierLock
* is turned on.
*/
class FxVerifierLock : public FxGlobalsStump {
private:
// Standard NT pool object identification
USHORT m_Type;
USHORT m_Size;
// Spinlock to perform our lock functionality
MxLock m_Lock;
KIRQL m_OldIrql;
// Fast Mutex for thread level verifier locks
MxPagedLock m_Mutex;
//
// Verifier per lock working values, protected
// by our m_Lock
//
FxObject* m_ParentObject;
MxThread m_OwningThread;
USHORT m_Order;
// True if its a Mutex based (thread context) lock
BOOLEAN m_UseMutex;
// True if its used as a Callback lock
BOOLEAN m_CallbackLock;
//
// This is the link for the per thread lock chain.
//
FxVerifierLock* m_OwnedLink;
private:
FxVerifierLock(
PFX_DRIVER_GLOBALS FxDriverGlobals
) :
FxGlobalsStump(FxDriverGlobals)
{
}
void
InitializeLockOrder(
VOID
);
void
FxVerifierLockDumpDetails(
__in FxVerifierLock* Lock,
__in PVOID curThread,
__in FxVerifierLock* PerThreadList
);
private:
//
// This constructor is used by internal object
// locks, which always use a spinlock.
//
FxVerifierLock(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in FxObject* ParentObject
) :
FxGlobalsStump(FxDriverGlobals)
{
m_Type = FX_TYPE_VERIFIERLOCK;
m_Size = sizeof(FxVerifierLock);
m_ParentObject = ParentObject;
m_OwningThread = NULL;
m_OwnedLink = NULL;
m_Order = FX_LOCK_ORDER_UNKNOWN;
m_ThreadTableEntry.Thread = NULL;
m_ThreadTableEntry.PerThreadPassiveLockList = NULL;
m_ThreadTableEntry.PerThreadDispatchLockList = NULL;
m_UseMutex = FALSE;
m_CallbackLock = FALSE;
InitializeLockOrder();
}
_Must_inspect_result_
__inline
NTSTATUS
Initialize(
)
{
NTSTATUS status;
if (m_UseMutex) {
status = m_Mutex.Initialize();
if (!NT_SUCCESS(status)) {
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE,
"Unable to initialize paged lock for VerifierLock 0x%p "
"status %!STATUS!",
this, status);
return status;
}
}
return STATUS_SUCCESS;
}
//
// This constructor is used by the Callback lock
// functions when verifer is on.
//
// The lock utlizes the callback lock order table entries to
// set its level.
//
FxVerifierLock(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in FxObject* ParentObject,
__in BOOLEAN UseMutex
) :
FxGlobalsStump(FxDriverGlobals)
{
m_Type = FX_TYPE_VERIFIERLOCK;
m_Size = sizeof(FxVerifierLock);
m_ParentObject = ParentObject;
m_OwningThread = NULL;
m_OwnedLink = NULL;
m_Order = FX_LOCK_ORDER_UNKNOWN;
m_ThreadTableEntry.Thread = NULL;
m_ThreadTableEntry.PerThreadPassiveLockList = NULL;
m_ThreadTableEntry.PerThreadDispatchLockList = NULL;
// Different verifier table
m_CallbackLock = TRUE;
if (UseMutex) {
m_UseMutex = TRUE;
}
else {
m_UseMutex = FALSE;
}
InitializeLockOrder();
}
public:
_Must_inspect_result_
__inline
static
NTSTATUS
CreateAndInitialize(
__out FxVerifierLock ** VerifierLock,
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in FxObject* ParentObject,
__in BOOLEAN UseMutex
)
{
NTSTATUS status;
FxVerifierLock * verifierLock;
verifierLock = new (FxDriverGlobals) FxVerifierLock(FxDriverGlobals,
ParentObject,
UseMutex);
if (NULL == verifierLock) {
status = STATUS_INSUFFICIENT_RESOURCES;
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
"Failed to allocate verifier lock, returning %!STATUS!",
status);
goto exit;
}
status = verifierLock->Initialize();
if (!NT_SUCCESS(status)) {
delete verifierLock;
goto exit;
}
*VerifierLock = verifierLock;
exit:
return status;
}
_Must_inspect_result_
__inline
static
NTSTATUS
CreateAndInitialize(
__out FxVerifierLock ** VerifierLock,
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in FxObject* ParentObject
)
{
NTSTATUS status;
FxVerifierLock * verifierLock;
verifierLock = new (FxDriverGlobals) FxVerifierLock(FxDriverGlobals,
ParentObject);
if (NULL == verifierLock) {
status = STATUS_INSUFFICIENT_RESOURCES;
DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
"Failed to allocate verifier lock, returning %!STATUS!",
status);
goto exit;
}
status = verifierLock->Initialize();
if (!NT_SUCCESS(status)) {
delete verifierLock;
goto exit;
}
*VerifierLock = verifierLock;
exit:
return status;
}
~FxVerifierLock(
VOID
)
{
if (m_OwningThread != NULL) {
DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE,
"Lock 0x%p is being destroyed while owned by "
"thread 0x%p, Owning Object 0x%p",
this, m_OwningThread, m_ParentObject);
FxVerifierDbgBreakPoint(GetDriverGlobals());
}
}
VOID
Lock(
__out PKIRQL PreviousIrql,
__in BOOLEAN AtDpc
);
VOID
Unlock(
__in KIRQL PreviousIrql,
__in BOOLEAN AtDpc
);
KIRQL
GetLockPreviousIrql(
VOID
);
//
// Instance data needed for the hash table chaining
//
FxVerifierThreadTableEntry m_ThreadTableEntry;
//
// Static data and methods for hash table chaining
//
static ULONG ThreadTableSize;
static KSPIN_LOCK ThreadTableLock;
static PLIST_ENTRY ThreadTable;
static
void
AllocateThreadTable(
__in PFX_DRIVER_GLOBALS FxDriverGlobals
);
static
void
FreeThreadTable(
__in PFX_DRIVER_GLOBALS FxDriverGlobals
);
static
void
DumpDetails(
__in FxVerifierLock* Lock,
__in MxThread curThread,
__in FxVerifierLock* PerThreadList
);
static
pFxVerifierThreadTableEntry
GetThreadTableEntry(
__in MxThread curThread,
__in FxVerifierLock* pLock,
__in BOOLEAN LookupOnly
);
static
void
ReleaseOrReplaceThreadTableEntry(
__in MxThread curThread,
__in FxVerifierLock* pLock
);
};
#endif // _FXVERIFIERLOCK_HPP_