mirror of
https://github.com/reactos/reactos.git
synced 2024-11-01 04:11:30 +00:00
561 lines
18 KiB
C
561 lines
18 KiB
C
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1990 - 1998
|
|
|
|
Module Name:
|
|
|
|
lock.c
|
|
|
|
Abstract:
|
|
|
|
This is the NT SCSI port driver.
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
This module is a driver dll for scsi miniports.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "classp.h"
|
|
#include "debug.h"
|
|
|
|
#ifdef DEBUG_USE_WPP
|
|
#include "lock.tmh"
|
|
#endif
|
|
|
|
|
|
LONG LockHighWatermark = 0;
|
|
LONG LockLowWatermark = 0;
|
|
LONG MaxLockedMinutes = 5;
|
|
|
|
//
|
|
// Structure used for tracking remove lock allocations in checked builds
|
|
//
|
|
typedef struct _REMOVE_TRACKING_BLOCK {
|
|
PVOID Tag;
|
|
LARGE_INTEGER TimeLocked;
|
|
PCSTR File;
|
|
ULONG Line;
|
|
} REMOVE_TRACKING_BLOCK, *PREMOVE_TRACKING_BLOCK;
|
|
|
|
/*++////////////////////////////////////////////////////////////////////////////
|
|
|
|
Classpnp RemoveLockRundown
|
|
|
|
RemoveLockRundown is a cacheaware rundown protection for the classpnp device object. While this
|
|
rundown protection is held successfully, the caller can assume that no pending pnp REMOVE
|
|
requests will be completed.
|
|
|
|
The RemoveLockRundown is a replacement of the original RemoveLock to improve the scalability.
|
|
For backward compatibility, we still keep the RemoveLock field in the device common extension structure.
|
|
However, the old RemoveLock is only being used in the DBG build.
|
|
|
|
The usage of the RemoveLockRundown is slightly different from the normal rundown protection usage.
|
|
The RemoveLockRundown is acquired via ClassAcquireRemoveLockEx() function
|
|
and released via ClassReleaseRemoveLock() function. Usually, we bail out when the acquisition
|
|
of rundown protection fails (calls to ExAcquireRundownProtectionCacheAware returns FALSE) and
|
|
will not release the rundown protection in acquisition failure. For the RemoveLockRundown,
|
|
the caller will always call ClassAcquireRemoveLockEx() and ClassReleaseRemoveLock() in a pair no
|
|
matter the return value of ClassAcquireRemoveLockEx(). Therefore, a thread may still call
|
|
ClassReleaseRemoveLock() even the previous acquisition RemoveLockRundown protection failed.
|
|
|
|
To deal with the previous acquisition failure case, we introduced a new field RemoveLockFailAcquire
|
|
as a counter for rundown acquisition failures. In the ClassReleaseRemoveLock() function, we only
|
|
release the rundown protection when this counter is decremented to zero. Since the change of RemoveLockFailAcquire
|
|
and release rundown protection is not protected by a lock as an atomic operation, we use a while loop over
|
|
InterlockedCompareExchange operation to make sure when we release the rundown protection, this counter is
|
|
actually zero.
|
|
|
|
--*/
|
|
|
|
/*++////////////////////////////////////////////////////////////////////////////
|
|
|
|
ClassAcquireRemoveLockEx()
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to acquire the remove lock on the device object.
|
|
While the lock is held, the caller can assume that no pending pnp REMOVE
|
|
requests will be completed.
|
|
|
|
The lock should be acquired immediately upon entering a dispatch routine.
|
|
It should also be acquired before creating any new reference to the
|
|
device object if there's a chance of releasing the reference before the
|
|
new one is done.
|
|
|
|
This routine will return TRUE if the lock was successfully acquired or
|
|
FALSE if it cannot be because the device object has already been removed.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the device object to lock
|
|
|
|
Tag - Used for tracking lock allocation and release. If an irp is
|
|
specified when acquiring the lock then the same Tag must be
|
|
used to release the lock before the Tag is completed.
|
|
|
|
Return Value:
|
|
|
|
The value of the IsRemoved flag in the device extension. If this is
|
|
non-zero then the device object has received a Remove irp and non-cleanup
|
|
IRP's should fail.
|
|
|
|
If the value is REMOVE_COMPLETE, the caller should not even release the
|
|
lock.
|
|
|
|
--*/
|
|
ULONG
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
ClassAcquireRemoveLockEx(
|
|
_In_ PDEVICE_OBJECT DeviceObject,
|
|
_In_ PVOID Tag,
|
|
_In_ PCSTR File,
|
|
_In_ ULONG Line
|
|
)
|
|
// This function implements the acquisition of Tag
|
|
#ifdef _MSC_VER
|
|
#pragma warning(suppress:28104)
|
|
#endif
|
|
{
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
|
BOOLEAN rundownAcquired;
|
|
PEX_RUNDOWN_REF_CACHE_AWARE removeLockRundown = NULL;
|
|
|
|
//
|
|
// Grab the remove lock
|
|
//
|
|
|
|
#if DBG
|
|
|
|
LONG lockValue;
|
|
|
|
lockValue = InterlockedIncrement(&commonExtension->RemoveLock);
|
|
|
|
|
|
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassAcquireRemoveLock: "
|
|
"Acquired for Object %p & irp %p - count is %d\n",
|
|
DeviceObject, Tag, lockValue));
|
|
|
|
NT_ASSERTMSG("ClassAcquireRemoveLock - lock value was negative : ",
|
|
(lockValue > 0));
|
|
|
|
NT_ASSERTMSG("RemoveLock increased to meet LockHighWatermark",
|
|
((LockHighWatermark == 0) ||
|
|
(lockValue != LockHighWatermark)));
|
|
|
|
if (commonExtension->IsRemoved != REMOVE_COMPLETE) {
|
|
PRTL_GENERIC_TABLE removeTrackingList = NULL;
|
|
REMOVE_TRACKING_BLOCK trackingBlock;
|
|
PREMOVE_TRACKING_BLOCK insertedTrackingBlock = NULL;
|
|
BOOLEAN newElement = FALSE;
|
|
|
|
KIRQL oldIrql;
|
|
|
|
trackingBlock.Tag = Tag;
|
|
|
|
trackingBlock.File = File;
|
|
trackingBlock.Line = Line;
|
|
|
|
KeQueryTickCount((&trackingBlock.TimeLocked));
|
|
|
|
KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
|
|
&oldIrql);
|
|
|
|
removeTrackingList = commonExtension->RemoveTrackingList;
|
|
|
|
if (removeTrackingList != NULL) {
|
|
insertedTrackingBlock = RtlInsertElementGenericTable(removeTrackingList,
|
|
&trackingBlock,
|
|
sizeof(REMOVE_TRACKING_BLOCK),
|
|
&newElement);
|
|
}
|
|
|
|
if (insertedTrackingBlock != NULL) {
|
|
if (!newElement) {
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassAcquireRemoveLock: "
|
|
"already tracking Tag %p\n", Tag));
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassAcquireRemoveLock: "
|
|
"acquired in file %s on line %d\n",
|
|
insertedTrackingBlock->File, insertedTrackingBlock->Line));
|
|
// NT_ASSERT(FALSE);
|
|
|
|
}
|
|
} else {
|
|
commonExtension->RemoveTrackingUntrackedCount++;
|
|
|
|
TracePrint((TRACE_LEVEL_WARNING, TRACE_FLAG_LOCK, ">>>>>ClassAcquireRemoveLock: "
|
|
"Cannot track Tag %p - currently %d untracked requsts\n",
|
|
Tag, commonExtension->RemoveTrackingUntrackedCount));
|
|
}
|
|
|
|
KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
|
|
}
|
|
#else
|
|
|
|
UNREFERENCED_PARAMETER(Tag);
|
|
UNREFERENCED_PARAMETER(File);
|
|
UNREFERENCED_PARAMETER(Line);
|
|
|
|
#endif
|
|
|
|
removeLockRundown = (PEX_RUNDOWN_REF_CACHE_AWARE)
|
|
((PCHAR)commonExtension->PrivateCommonData + sizeof(CLASS_PRIVATE_COMMON_DATA));
|
|
rundownAcquired = ExAcquireRundownProtectionCacheAware(removeLockRundown);
|
|
if (!rundownAcquired) {
|
|
InterlockedIncrement((volatile LONG*) &(commonExtension->PrivateCommonData->RemoveLockFailAcquire));
|
|
TracePrint((TRACE_LEVEL_VERBOSE,
|
|
TRACE_FLAG_LOCK,
|
|
"ClassAcquireRemoveLockEx: RemoveLockRundown acquisition failed"
|
|
"RemoveLockFailAcquire = %d\n",
|
|
commonExtension->PrivateCommonData->RemoveLockFailAcquire));
|
|
}
|
|
|
|
return (commonExtension->IsRemoved);
|
|
}
|
|
|
|
/*++////////////////////////////////////////////////////////////////////////////
|
|
|
|
ClassReleaseRemoveLock()
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to release the remove lock on the device object. It
|
|
must be called when finished using a previously locked reference to the
|
|
device object. If an Tag was specified when acquiring the lock then the
|
|
same Tag must be specified when releasing the lock.
|
|
|
|
When the lock count reduces to zero, this routine will signal the waiting
|
|
remove Tag to delete the device object. As a result the DeviceObject
|
|
pointer should not be used again once the lock has been released.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the device object to lock
|
|
|
|
Tag - The irp (if any) specified when acquiring the lock. This is used
|
|
for lock tracking purposes
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
VOID
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
ClassReleaseRemoveLock(
|
|
_In_ PDEVICE_OBJECT DeviceObject,
|
|
_In_opt_ PIRP Tag
|
|
)
|
|
// This function implements the release of Tag
|
|
#ifdef _MSC_VER
|
|
#pragma warning(suppress:28103)
|
|
#endif
|
|
{
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
|
LONG lockValue;
|
|
LONG oldValue;
|
|
PEX_RUNDOWN_REF_CACHE_AWARE removeLockRundown = NULL;
|
|
|
|
#if DBG
|
|
PRTL_GENERIC_TABLE removeTrackingList = NULL;
|
|
REMOVE_TRACKING_BLOCK searchDataBlock;
|
|
|
|
BOOLEAN found = FALSE;
|
|
|
|
BOOLEAN isRemoved = (commonExtension->IsRemoved == REMOVE_COMPLETE);
|
|
|
|
KIRQL oldIrql;
|
|
|
|
if (isRemoved) {
|
|
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassReleaseRemoveLock: REMOVE_COMPLETE set; this should never happen"));
|
|
InterlockedDecrement(&(commonExtension->RemoveLock));
|
|
return;
|
|
}
|
|
|
|
KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock,
|
|
&oldIrql);
|
|
|
|
removeTrackingList = commonExtension->RemoveTrackingList;
|
|
|
|
if (removeTrackingList != NULL) {
|
|
searchDataBlock.Tag = Tag;
|
|
found = RtlDeleteElementGenericTable(removeTrackingList, &searchDataBlock);
|
|
}
|
|
|
|
if (!found) {
|
|
if(commonExtension->RemoveTrackingUntrackedCount == 0) {
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassReleaseRemoveLock: "
|
|
"Couldn't find Tag %p in the lock tracking list\n", Tag));
|
|
//
|
|
// This might happen if the device is being removed and the tracking list
|
|
// has already been freed. Don't assert if that is the case.
|
|
//
|
|
NT_ASSERT((removeTrackingList == NULL) && (commonExtension->IsRemoved != NO_REMOVE));
|
|
} else {
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassReleaseRemoveLock: "
|
|
"Couldn't find Tag %p in the lock tracking list - "
|
|
"may be one of the %d untracked requests still outstanding\n",
|
|
Tag, commonExtension->RemoveTrackingUntrackedCount));
|
|
|
|
commonExtension->RemoveTrackingUntrackedCount--;
|
|
NT_ASSERT(commonExtension->RemoveTrackingUntrackedCount >= 0);
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock,
|
|
oldIrql);
|
|
|
|
lockValue = InterlockedDecrement(&commonExtension->RemoveLock);
|
|
|
|
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassReleaseRemoveLock: "
|
|
"Released for Object %p & irp %p - count is %d\n",
|
|
DeviceObject, Tag, lockValue));
|
|
|
|
NT_ASSERT(lockValue >= 0);
|
|
|
|
NT_ASSERTMSG("RemoveLock decreased to meet LockLowWatermark",
|
|
((LockLowWatermark == 0) || !(lockValue == LockLowWatermark)));
|
|
|
|
if (lockValue == 0) {
|
|
|
|
NT_ASSERT(commonExtension->IsRemoved);
|
|
|
|
//
|
|
// The device needs to be removed. Signal the remove event
|
|
// that it's safe to go ahead.
|
|
//
|
|
|
|
TracePrint((TRACE_LEVEL_VERBOSE, TRACE_FLAG_LOCK, "ClassReleaseRemoveLock: "
|
|
"Release for object %p & irp %p caused lock to go to zero\n",
|
|
DeviceObject, Tag));
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
UNREFERENCED_PARAMETER(Tag);
|
|
|
|
#endif
|
|
|
|
//
|
|
// Decrement the RemoveLockFailAcquire by 1 when RemoveLockFailAcquire is non-zero.
|
|
// Release the RemoveLockRundown only when RemoveLockFailAcquire is zero.
|
|
//
|
|
|
|
oldValue = 1;
|
|
lockValue = commonExtension->PrivateCommonData->RemoveLockFailAcquire;
|
|
while (lockValue != 0) {
|
|
oldValue =
|
|
InterlockedCompareExchange((volatile LONG *) &commonExtension->PrivateCommonData->RemoveLockFailAcquire,
|
|
lockValue - 1,
|
|
lockValue);
|
|
|
|
if (oldValue == lockValue) {
|
|
break;
|
|
}
|
|
|
|
lockValue = oldValue;
|
|
}
|
|
|
|
if (lockValue == 0) {
|
|
removeLockRundown = (PEX_RUNDOWN_REF_CACHE_AWARE)
|
|
((PCHAR)commonExtension->PrivateCommonData + sizeof(CLASS_PRIVATE_COMMON_DATA));
|
|
ExReleaseRundownProtectionCacheAware(removeLockRundown);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*++////////////////////////////////////////////////////////////////////////////
|
|
|
|
ClassCompleteRequest()
|
|
|
|
Routine Description:
|
|
|
|
This routine is a wrapper around (and should be used instead of)
|
|
IoCompleteRequest. It is used primarily for debugging purposes.
|
|
The routine will assert if the Irp being completed is still holding
|
|
the release lock.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the device object that was handling this request
|
|
|
|
Irp - the irp to be completed by IoCompleteRequest
|
|
|
|
PriorityBoost - the priority boost to pass to IoCompleteRequest
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
VOID
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
ClassCompleteRequest(
|
|
_In_ PDEVICE_OBJECT DeviceObject,
|
|
_In_ PIRP Irp,
|
|
_In_ CCHAR PriorityBoost
|
|
)
|
|
{
|
|
#if DBG
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
|
|
|
PRTL_GENERIC_TABLE removeTrackingList = NULL;
|
|
REMOVE_TRACKING_BLOCK searchDataBlock;
|
|
PREMOVE_TRACKING_BLOCK foundTrackingBlock;
|
|
|
|
KIRQL oldIrql;
|
|
|
|
KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, &oldIrql);
|
|
|
|
removeTrackingList = commonExtension->RemoveTrackingList;
|
|
|
|
if (removeTrackingList != NULL)
|
|
{
|
|
searchDataBlock.Tag = Irp;
|
|
|
|
foundTrackingBlock = RtlLookupElementGenericTable(removeTrackingList, &searchDataBlock);
|
|
|
|
if(foundTrackingBlock != NULL) {
|
|
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassCompleteRequest: "
|
|
"Irp %p completed while still holding the remove lock\n", Irp));
|
|
TracePrint((TRACE_LEVEL_ERROR, TRACE_FLAG_LOCK, ">>>>>ClassCompleteRequest: "
|
|
"Lock acquired in file %s on line %d\n",
|
|
foundTrackingBlock->File, foundTrackingBlock->Line));
|
|
NT_ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
|
|
#endif
|
|
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
IoCompleteRequest(Irp, PriorityBoost);
|
|
return;
|
|
} // end ClassCompleteRequest()
|
|
|
|
|
|
RTL_GENERIC_COMPARE_RESULTS
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
RemoveTrackingCompareRoutine(
|
|
PRTL_GENERIC_TABLE Table,
|
|
PVOID FirstStruct,
|
|
PVOID SecondStruct
|
|
)
|
|
{
|
|
PVOID tag1, tag2;
|
|
|
|
UNREFERENCED_PARAMETER(Table);
|
|
|
|
tag1 = ((PREMOVE_TRACKING_BLOCK)FirstStruct)->Tag;
|
|
tag2 = ((PREMOVE_TRACKING_BLOCK)SecondStruct)->Tag;
|
|
|
|
if (tag1 < tag2)
|
|
{
|
|
return GenericLessThan;
|
|
}
|
|
else if (tag1 > tag2)
|
|
{
|
|
return GenericGreaterThan;
|
|
}
|
|
|
|
return GenericEqual;
|
|
}
|
|
|
|
PVOID
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
RemoveTrackingAllocateRoutine(
|
|
PRTL_GENERIC_TABLE Table,
|
|
CLONG ByteSize
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(Table);
|
|
|
|
return ExAllocatePoolWithTag(NonPagedPoolNx, ByteSize, CLASS_TAG_LOCK_TRACKING);
|
|
}
|
|
|
|
VOID
|
|
NTAPI /* ReactOS Change: GCC Does not support STDCALL by default */
|
|
RemoveTrackingFreeRoutine(
|
|
PRTL_GENERIC_TABLE Table,
|
|
PVOID Buffer
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(Table);
|
|
|
|
FREE_POOL(Buffer);
|
|
}
|
|
|
|
VOID
|
|
ClasspInitializeRemoveTracking(
|
|
_In_ PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
|
|
|
#if DBG
|
|
KeInitializeSpinLock(&commonExtension->RemoveTrackingSpinlock);
|
|
|
|
commonExtension->RemoveTrackingList = ExAllocatePoolWithTag(NonPagedPoolNx, sizeof(RTL_GENERIC_TABLE), CLASS_TAG_LOCK_TRACKING);
|
|
|
|
if (commonExtension->RemoveTrackingList != NULL)
|
|
{
|
|
RtlInitializeGenericTable(commonExtension->RemoveTrackingList,
|
|
RemoveTrackingCompareRoutine,
|
|
RemoveTrackingAllocateRoutine,
|
|
RemoveTrackingFreeRoutine,
|
|
NULL);
|
|
}
|
|
#else
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
commonExtension->RemoveTrackingSpinlock = (ULONG_PTR) -1;
|
|
commonExtension->RemoveTrackingList = NULL;
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
ClasspUninitializeRemoveTracking(
|
|
_In_ PDEVICE_OBJECT DeviceObject
|
|
)
|
|
{
|
|
#if DBG
|
|
PCOMMON_DEVICE_EXTENSION commonExtension = DeviceObject->DeviceExtension;
|
|
PRTL_GENERIC_TABLE removeTrackingList = commonExtension->RemoveTrackingList;
|
|
|
|
ASSERTMSG("Removing the device while still holding remove locks",
|
|
commonExtension->RemoveTrackingUntrackedCount == 0 &&
|
|
removeTrackingList != NULL ? RtlNumberGenericTableElements(removeTrackingList) == 0 : TRUE);
|
|
|
|
if (removeTrackingList != NULL)
|
|
{
|
|
KIRQL oldIrql;
|
|
KeAcquireSpinLock(&commonExtension->RemoveTrackingSpinlock, &oldIrql);
|
|
|
|
FREE_POOL(removeTrackingList);
|
|
commonExtension->RemoveTrackingList = NULL;
|
|
|
|
KeReleaseSpinLock(&commonExtension->RemoveTrackingSpinlock, oldIrql);
|
|
}
|
|
|
|
#else
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|