386 lines
8.7 KiB
C
386 lines
8.7 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
remlock.c
|
||
|
||
Abstract:
|
||
|
||
This is the NT SCSI port driver.
|
||
|
||
Authors:
|
||
|
||
Peter Wieland
|
||
Kenneth Ray
|
||
|
||
Environment:
|
||
|
||
kernel mode only
|
||
|
||
Notes:
|
||
|
||
This module is a driver dll for scsi miniports.
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include <nt.h>
|
||
#include <ntrtl.h>
|
||
#include <ntos.h>
|
||
|
||
#include <remlock.h>
|
||
|
||
#define MinutesToTicks(x) \
|
||
(ULONGLONG) KeQueryTimeIncrement() * \
|
||
10 * \
|
||
1000 * \
|
||
1000 * \
|
||
60 * \
|
||
x
|
||
// 10 -> microseconds, 1000 -> miliseconds, 1000 -> seconds, 60 -> minutes
|
||
|
||
// LIST_ENTRY RtlpRemoveLockList;
|
||
|
||
NTSYSAPI
|
||
PRTL_REMOVE_LOCK
|
||
NTAPI
|
||
RtlAllocateRemoveLock(
|
||
IN ULONG MaxLockedMinutes,
|
||
IN ULONG AllocateTag,
|
||
IN ULONG HighWatermark
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to initialize the remove lock for a device object.
|
||
|
||
--*/
|
||
{
|
||
PRTL_REMOVE_LOCK lock;
|
||
|
||
lock = ExAllocatePoolWithTag (NonPagedPool,
|
||
sizeof (RTL_REMOVE_LOCK),
|
||
AllocateTag);
|
||
|
||
if (lock) {
|
||
|
||
lock->Signature = RTL_REMOVE_LOCK_SIG;
|
||
lock->Removed = FALSE;
|
||
lock->IoCount = 1;
|
||
KeInitializeEvent(&lock->RemoveEvent, SynchronizationEvent, FALSE);
|
||
#if DBG
|
||
lock->HighWatermark = HighWatermark;
|
||
lock->MaxLockedMinutes = MaxLockedMinutes;
|
||
lock->AllocateTag = AllocateTag;
|
||
KeInitializeSpinLock (&lock->Spin);
|
||
lock->Blocks.Link = NULL;
|
||
#endif
|
||
}
|
||
|
||
return lock;
|
||
}
|
||
|
||
|
||
NTSYSAPI
|
||
NTSTATUS
|
||
NTAPI
|
||
RtlAcquireRemoveLockEx(
|
||
IN PRTL_REMOVE_LOCK RemoveLock,
|
||
IN OPTIONAL PVOID Tag,
|
||
IN PCSTR File,
|
||
IN ULONG Line
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to acquire the remove lock for a 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.
|
||
|
||
Arguments:
|
||
|
||
RemoveLock - A pointer to an initialized REMOVE_LOCK structure.
|
||
|
||
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.
|
||
|
||
File - set to __FILE__ as the location in the code where the lock was taken.
|
||
|
||
Line - set to __LINE__.
|
||
|
||
Return Value:
|
||
|
||
Returns whether or not the remove lock was obtained.
|
||
If successful the caller should continue with work calling
|
||
RtlReleaseRemoveLock when finished.
|
||
|
||
If not successful the lock was not obtained. The caller should abort the
|
||
work but not call RtlReleaseRemoveLock.
|
||
|
||
--*/
|
||
|
||
{
|
||
LONG lockValue;
|
||
NTSTATUS status;
|
||
|
||
#if DBG
|
||
PRTL_REMOVE_LOCK_TRACKING_BLOCK trackingBlock;
|
||
#endif
|
||
|
||
//
|
||
// Grab the remove lock
|
||
//
|
||
|
||
lockValue = InterlockedIncrement(&RemoveLock->IoCount);
|
||
|
||
ASSERTMSG("RtlAcquireRemoveLock - lock value was negative : ",
|
||
(lockValue > 0));
|
||
|
||
ASSERTMSG("RemoveLock increased to meet LockHighWatermark",
|
||
((0 == RemoveLock->HighWatermark) ||
|
||
(lockValue <= RemoveLock->HighWatermark)));
|
||
|
||
if (! RemoveLock->Removed) {
|
||
|
||
#if DBG
|
||
trackingBlock = ExAllocatePoolWithTag(
|
||
NonPagedPool,
|
||
sizeof(RTL_REMOVE_LOCK_TRACKING_BLOCK),
|
||
RemoveLock->AllocateTag);
|
||
|
||
RtlZeroMemory (trackingBlock,
|
||
sizeof (RTL_REMOVE_LOCK_TRACKING_BLOCK));
|
||
|
||
if (NULL == trackingBlock) {
|
||
|
||
ASSERTMSG ("insufficient resources", FALSE);
|
||
|
||
} else {
|
||
|
||
KIRQL oldIrql;
|
||
|
||
trackingBlock->Tag = Tag;
|
||
trackingBlock->File = File;
|
||
trackingBlock->Line = Line;
|
||
|
||
KeQueryTickCount(&trackingBlock->TimeLocked);
|
||
|
||
KeAcquireSpinLock (&RemoveLock->Spin, &oldIrql);
|
||
trackingBlock->Link = RemoveLock->Blocks.Link;
|
||
RemoveLock->Blocks.Link = trackingBlock;
|
||
KeReleaseSpinLock(&RemoveLock->Spin, oldIrql);
|
||
}
|
||
#endif
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
|
||
if (0 == InterlockedDecrement (&RemoveLock->IoCount)) {
|
||
KeSetEvent (&RemoveLock->RemoveEvent, 0, FALSE);
|
||
}
|
||
status = STATUS_DELETE_PENDING;
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
NTSYSAPI
|
||
VOID
|
||
NTAPI
|
||
RtlReleaseRemoveLock(
|
||
IN PRTL_REMOVE_LOCK RemoveLock,
|
||
IN PVOID Tag
|
||
)
|
||
|
||
/*++
|
||
|
||
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
|
||
event to release the waiting thread deleting the device object protected
|
||
by this lock.
|
||
|
||
Arguments:
|
||
|
||
DeviceObject - the device object to lock
|
||
|
||
Tag - The tag (if any) specified when acquiring the lock. This is used
|
||
for lock tracking purposes
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
--*/
|
||
|
||
{
|
||
LONG lockValue;
|
||
|
||
#if DBG
|
||
KIRQL oldIrql;
|
||
LARGE_INTEGER difference;
|
||
BOOLEAN found;
|
||
LONGLONG maxTime;
|
||
|
||
PRTL_REMOVE_LOCK_TRACKING_BLOCK last;
|
||
PRTL_REMOVE_LOCK_TRACKING_BLOCK current;
|
||
|
||
//
|
||
// Check the tick count and make sure this thing hasn't been locked
|
||
// for more than MaxLockedMinutes.
|
||
//
|
||
|
||
found = FALSE;
|
||
KeAcquireSpinLock(&RemoveLock->Spin, &oldIrql);
|
||
last = (&RemoveLock->Blocks);
|
||
current = last->Link;
|
||
//
|
||
// Note the first one is the sentinal
|
||
//
|
||
|
||
while (NULL != current) {
|
||
|
||
KeQueryTickCount((&difference));
|
||
difference.QuadPart -= current->TimeLocked.QuadPart;
|
||
maxTime = MinutesToTicks (RemoveLock->MaxLockedMinutes);
|
||
|
||
if (maxTime && (maxTime < difference.QuadPart)) {
|
||
|
||
KdPrint(("RtlReleaseRemoveLock: Lock %#08lx (tag %#08lx) locked "
|
||
"for %I64d ticks - TOO LONG\n",
|
||
RemoveLock,
|
||
current->Tag,
|
||
difference.QuadPart));
|
||
|
||
KdPrint(("RtlReleaseRemoveLock: Lock acquired in file "
|
||
"%s on line %d\n",
|
||
current->File,
|
||
current->Line));
|
||
ASSERT(FALSE);
|
||
}
|
||
|
||
if ((!found) && (current->Tag == Tag)) {
|
||
found = TRUE;
|
||
last->Link = current->Link;
|
||
ExFreePool (current);
|
||
current = last->Link;
|
||
continue;
|
||
}
|
||
|
||
last = current;
|
||
current = current->Link;
|
||
}
|
||
|
||
KeReleaseSpinLock(&RemoveLock->Spin, oldIrql);
|
||
|
||
if (!found) {
|
||
|
||
KdPrint (("RtlReleaseRemoveLock: Couldn't find Tag %#08lx "
|
||
"in the lock tracking list\n",
|
||
Tag));
|
||
ASSERT(FALSE);
|
||
}
|
||
#endif
|
||
|
||
lockValue = InterlockedDecrement(&RemoveLock->IoCount);
|
||
|
||
ASSERT(0 <= lockValue);
|
||
|
||
if (0 == lockValue) {
|
||
|
||
ASSERT (RemoveLock->Removed);
|
||
|
||
//
|
||
// The device needs to be removed. Signal the remove event
|
||
// that it's safe to go ahead.
|
||
//
|
||
|
||
KeSetEvent(&RemoveLock->RemoveEvent,
|
||
IO_NO_INCREMENT,
|
||
FALSE);
|
||
}
|
||
return;
|
||
}
|
||
|
||
|
||
NTSYSAPI
|
||
VOID
|
||
NTAPI
|
||
RtlReleaseRemoveLockAndWait (
|
||
IN PRTL_REMOVE_LOCK RemoveLock,
|
||
IN PVOID Tag
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called when the client would like to delete the remove-
|
||
locked resource.
|
||
This routine will block until all the remove locks have completed.
|
||
|
||
This routine MUST be called after acquiring once more the lock.
|
||
|
||
Arguments:
|
||
|
||
RemoveLock -
|
||
|
||
Return Value:
|
||
|
||
none
|
||
|
||
--*/
|
||
{
|
||
LONG ioCount;
|
||
|
||
PAGED_CODE ();
|
||
|
||
RemoveLock->Removed = TRUE;
|
||
|
||
ioCount = InterlockedDecrement (&RemoveLock->IoCount);
|
||
ASSERT (0 < ioCount);
|
||
|
||
if (0 < InterlockedDecrement (&RemoveLock->IoCount)) {
|
||
KeWaitForSingleObject (&RemoveLock->RemoveEvent,
|
||
Executive,
|
||
KernelMode,
|
||
FALSE,
|
||
NULL);
|
||
}
|
||
|
||
#if DBG
|
||
ASSERT (RemoveLock->Blocks.Link);
|
||
if (Tag != RemoveLock->Blocks.Link->Tag) {
|
||
KdPrint (("RtlRelaseRemoveLockAndWait last tag invalid %x %x\n",
|
||
Tag,
|
||
RemoveLock->Blocks.Link->Tag));
|
||
|
||
ASSERT (Tag != RemoveLock->Blocks.Link->Tag);
|
||
}
|
||
|
||
ExFreePool (RemoveLock->Blocks.Link);
|
||
#endif
|
||
|
||
}
|
||
|
||
|