mirror of https://github.com/lianthony/NT4.0
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.
770 lines
25 KiB
770 lines
25 KiB
/*++
|
|
|
|
*****************************************************************************
|
|
* *
|
|
* This software contains proprietary and confidential information of *
|
|
* *
|
|
* Digi International Inc. *
|
|
* *
|
|
* By accepting transfer of this copy, Recipient agrees to retain this *
|
|
* software in confidence, to prevent disclosure to others, and to make *
|
|
* no use of this software other than that for which it was delivered. *
|
|
* This is an unpublished copyrighted work of Digi International Inc. *
|
|
* Except as permitted by federal law, 17 USC 117, copying is strictly *
|
|
* prohibited. *
|
|
* *
|
|
*****************************************************************************
|
|
|
|
Module Name:
|
|
|
|
misc.c
|
|
|
|
Abstract:
|
|
|
|
|
|
Revision History:
|
|
|
|
* $Log: /Components/Windows/NT/Async/FEP5/MISC.C $
|
|
*
|
|
* 4 3/05/96 6:27p Stana
|
|
* Bugfix: When DigiCancelIrpQueue cancels its first read irp, if another
|
|
* read irp is immediately issued by the app, it also gets cancelled.
|
|
* This can actually go on forever, but usually stops within a few hours.
|
|
* Now I have a spinlock (NewIrpLock) to prevent this.
|
|
*
|
|
* 1 3/04/96 12:18p Stana
|
|
* Misc. functions required to help with NT issues such as Multi-processor
|
|
* support, timing problems, and other such things.
|
|
*
|
|
* Revision 1.15.1.5 1995/11/28 12:48:26 dirkh
|
|
* Adopt common header file.
|
|
*
|
|
* Revision 1.15.1.4 1995/10/04 18:25:10 dirkh
|
|
* DevExt->XcPreview must be reset when pXoffCounter is cleared. (Not sure why...)
|
|
*
|
|
* Revision 1.15.1.3 1995/09/19 12:49:52 dirkh
|
|
* Add IOCTL_SERIAL_XOFF_COUNTER support:
|
|
* {
|
|
* DigiStartIrpRequest (if WRITE or XOFF_COUNTER is queued behind a *transmitted* XOFF_COUNTER, complete the transmitted XOFF_COUNTER).
|
|
* DigiTryToCompleteIrp (if IRP is XOFF_COUNTER, clear DevExt->pXoffCounter).
|
|
* }
|
|
* Simplify interface to DigiCancelIrpQueue.
|
|
*
|
|
* Revision 1.15.1.2 1995/09/05 16:59:38 dirkh
|
|
* DigiTryToCompleteIrp: Fix recovery from failed assertion.
|
|
*
|
|
* Revision 1.15.1.1 1995/09/05 14:28:18 dirkh
|
|
* General: Minimize IoCancel spin lock window.
|
|
* General: Eliminate special handling for "fast RAS" flush IRP. (It's now fully realized for IoCompleteRequest.)
|
|
* DigiStartIrpRequest queues IMMEDIATE_CHAR IRPs at the head of the queue, others at the tail.
|
|
* DigiCancelQueuedIrp holds lock to fix DevExt->TotalCharsQueued.
|
|
* DigiRundownIrpRefs kills the cancel routine only if that's the only reference left.
|
|
*
|
|
* Revision 1.15 1995/04/19 13:09:27 rik
|
|
* Added an undeclared local variable.
|
|
*
|
|
* Revision 1.14 1995/04/18 18:15:05 rik
|
|
* Fixed potential timing hole with canceling an Irp in the generic cancel
|
|
* routine.
|
|
*
|
|
* Revision 1.13 1995/04/12 14:42:04 rik
|
|
* Take into account self-inserted flush irp's being cancelled.
|
|
*
|
|
* Revision 1.12 1994/09/13 07:40:58 rik
|
|
* Added debug tracking output for cancel irps.
|
|
*
|
|
* Revision 1.11 1993/06/14 14:42:22 rik
|
|
* Tightened up some spinlock windows, and fixed a problem with how
|
|
* I was calling the startroutine in the DigiTryToCompleteIrp routine.
|
|
*
|
|
* Revision 1.10 1993/06/06 14:17:03 rik
|
|
* Tightened up windows in the code which were causing problems. Primarily,
|
|
* changes were in the functions DigiTryToCompleteIrp, and DigiCancelIrpQueue.
|
|
* I use Cancel spinlocks more rigoursly to help eliminate windows which were
|
|
* seen on multi-processor machines. The problem could also happen on
|
|
* uni-processor machines, depending on which IRQL level the requests were
|
|
* done at.
|
|
*
|
|
* Revision 1.9 1993/05/18 05:08:00 rik
|
|
* Fixed spinlock problems where the device extension wasn't being protected
|
|
* by its spinlock. As a result, on multi-processor machines, the device
|
|
* extension was being changed when it was being accessed by the other
|
|
* processor causing faults.
|
|
*
|
|
* Revision 1.8 1993/05/09 09:22:11 rik
|
|
* Added debugging output for completing IRP.
|
|
*
|
|
* Revision 1.7 1993/03/08 07:23:04 rik
|
|
* Changed how I handle read/write/wait IRPs now. Instead of always marking
|
|
* the IRP, I have changed it such that I only mark an IRP pending if the
|
|
* value from the start routine is STATUS_PENDING or if there is all ready
|
|
* and outstanding IRP(s) present on the appropriate queue.
|
|
*
|
|
* Revision 1.6 1993/02/25 19:09:58 rik
|
|
* Added debugging for tracing IRPs better.
|
|
*
|
|
* Revision 1.5 1993/02/04 12:23:40 rik
|
|
* ??
|
|
*
|
|
* Revision 1.4 1993/01/28 10:36:44 rik
|
|
* Updated function to always return STATUS_PENDING since I always IRP requests
|
|
* status pending. This is a new requirement for NT build 354.
|
|
*
|
|
* Revision 1.3 1993/01/22 12:36:10 rik
|
|
* *** empty log message ***
|
|
*
|
|
* Revision 1.2 1992/12/10 16:12:08 rik
|
|
* Reorganized function names to better reflect how they are used through out
|
|
* the driver.
|
|
*
|
|
* Revision 1.1 1992/11/12 12:50:59 rik
|
|
* Initial revision
|
|
*
|
|
|
|
--*/
|
|
|
|
|
|
#include "header.h"
|
|
|
|
#ifndef _MISC_DOT_C
|
|
# define _MISC_DOT_C
|
|
static char RCSInfo_MiscDotC[] = "$Header: /Components/Windows/NT/Async/FEP5/MISC.C 4 3/05/96 6:27p Stana $";
|
|
#endif
|
|
|
|
/****************************************************************************/
|
|
/* Local Prototypes */
|
|
/****************************************************************************/
|
|
void __inline
|
|
DigiRundownIrpRefs( IN PIRP Irp,
|
|
IN PKTIMER IntervalTimer OPTIONAL,
|
|
IN PKTIMER TotalTimer OPTIONAL );
|
|
|
|
VOID DigiCancelQueuedIrp( PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp );
|
|
|
|
|
|
|
|
|
|
NTSTATUS DigiStartIrpRequest( IN PDIGI_CONTROLLER_EXTENSION ControllerExt,
|
|
IN PDIGI_DEVICE_EXTENSION DeviceExt,
|
|
IN PLIST_ENTRY Queue,
|
|
IN PIRP Irp,
|
|
IN PDIGI_START_ROUTINE StartRoutine )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
ControllerExt - Pointer to the controller object extension associated
|
|
with this device.
|
|
|
|
DeviceExt - Pointer to the device object extension for this device.
|
|
|
|
Queue - The queue of Irp requests.
|
|
|
|
StartRoutine - The routine to call if the queue is empty.
|
|
( i.e. if this is the first request possibly ).
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
KIRQL OldIrql;
|
|
NTSTATUS Status;
|
|
BOOLEAN EmptyList;
|
|
|
|
DigiDump( DIGIFLOW, ("Entering DigiStartIrpRequest\n") );
|
|
|
|
KeAcquireSpinLock( &DeviceExt->NewIrpLock, &OldIrql );
|
|
KeReleaseSpinLock( &DeviceExt->NewIrpLock, OldIrql );
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql);
|
|
|
|
// If we enqueue a WRITE or XOFF_COUNTER behind a transmitted XOFF_COUNTER,
|
|
// then the transmitted XOFF_COUNTER must be completed immediately.
|
|
if( Queue == &DeviceExt->WriteQueue
|
|
&& !IsListEmpty( Queue )
|
|
&& Queue->Flink->Flink == Queue // only one IRP on the queue
|
|
&& ( IrpSp->MajorFunction == IRP_MJ_WRITE
|
|
|| ( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_XOFF_COUNTER
|
|
&& IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL
|
|
)
|
|
)
|
|
)
|
|
{
|
|
PIRP HeadIrp = CONTAINING_RECORD( Queue->Flink, IRP, Tail.Overlay.ListEntry );
|
|
|
|
if( HeadIrp->IoStatus.Information == 1 ) // XOFF_COUNTER has been transmitted
|
|
{
|
|
PIO_STACK_LOCATION HeadIrpSp = IoGetCurrentIrpStackLocation( HeadIrp );
|
|
|
|
if( HeadIrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_XOFF_COUNTER
|
|
&& HeadIrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL )
|
|
{
|
|
DigiDump( (DIGIWRITE|DIGIIRP|DIGIDIAG1), ("DigiStartIrpRequest is absorbing transmitted XOFF_COUNTER.\n") );
|
|
|
|
// Absorb the XOFF_COUNTER.
|
|
DigiTryToCompleteIrp( DeviceExt, &OldIrql, STATUS_SERIAL_MORE_WRITES,
|
|
Queue, NULL, &DeviceExt->WriteRequestTotalTimer, StartRoutine );
|
|
|
|
// It's possible that some other IRP sneaked in ahead of us...
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql);
|
|
}
|
|
}
|
|
} // WRITE or XOFF_COUNTER is being added to non-empty WriteQueue
|
|
|
|
// IMMEDIATE_CHAR is queued at the head, all others at the tail of Queue.
|
|
if( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_IMMEDIATE_CHAR
|
|
&& IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL )
|
|
{
|
|
ASSERT( Queue == &DeviceExt->WriteQueue );
|
|
DeviceExt->TotalCharsQueued++;
|
|
EmptyList = TRUE; // Force StartRoutine to be called.
|
|
// DH Change cancel routine on former head IRP, and avoid re-sending or starving IRP.
|
|
InsertHeadList( Queue, &Irp->Tail.Overlay.ListEntry );
|
|
}
|
|
else
|
|
{
|
|
if( IrpSp->MajorFunction == IRP_MJ_WRITE )
|
|
{
|
|
ASSERT( Queue == &DeviceExt->WriteQueue );
|
|
DeviceExt->TotalCharsQueued += IrpSp->Parameters.Write.Length;
|
|
}
|
|
else
|
|
if( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_XOFF_COUNTER
|
|
&& IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL )
|
|
{
|
|
ASSERT( Queue == &DeviceExt->WriteQueue );
|
|
DeviceExt->TotalCharsQueued++;
|
|
}
|
|
EmptyList = IsListEmpty( Queue );
|
|
InsertTailList( Queue, &Irp->Tail.Overlay.ListEntry );
|
|
}
|
|
|
|
// Mark IRP as "never pending" to advise priority boost on IRP completion.
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
if( EmptyList )
|
|
{
|
|
DigiDump( DIGIFLOW, (" Calling Starter Routine\n") );
|
|
Status = StartRoutine( ControllerExt, DeviceExt, &OldIrql );
|
|
|
|
if( Status == STATUS_PENDING )
|
|
{
|
|
ASSERT( Irp->CancelRoutine != NULL ); // StartRoutine should have set this.
|
|
Irp->IoStatus.Status = Status; // STATUS_PENDING
|
|
DigiIoMarkIrpPending( Irp );
|
|
}
|
|
}
|
|
else // The IRP will be started later.
|
|
{
|
|
KIRQL OldCancelIrql;
|
|
|
|
DigiDump( DIGIFLOW, (" Queuing the Irp\n") );
|
|
|
|
Irp->IoStatus.Status = Status = STATUS_PENDING;
|
|
DigiIoMarkIrpPending( Irp );
|
|
|
|
IoAcquireCancelSpinLock( &OldCancelIrql );
|
|
IoSetCancelRoutine( Irp, DigiCancelQueuedIrp );
|
|
IoReleaseCancelSpinLock( OldCancelIrql );
|
|
}
|
|
|
|
DigiDump( DIGIFLOW, ("Exiting DigiStartIrpRequest\n") );
|
|
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
|
|
|
|
return( Status );
|
|
} // end DigiStartIrpRequest
|
|
|
|
|
|
|
|
VOID DigiCancelQueuedIrp( PDEVICE_OBJECT DeviceObject,
|
|
PIRP Irp )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to cancel Irps on the queue which are NOT the
|
|
head of the queue. I assume the head entry is the current Irp.
|
|
|
|
Arguments:
|
|
|
|
DeviceExt - Pointer to the device object for this device.
|
|
|
|
Irp - Pointer to the IRP to be cancelled.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION IrpSp;
|
|
PDIGI_DEVICE_EXTENSION DeviceExt;
|
|
KIRQL OldIrql;
|
|
|
|
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
|
|
|
DigiDump( (DIGIFLOW|DIGICANCELIRP), ("Canceling Queued Irp 0x%x\n",
|
|
Irp) );
|
|
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
DeviceExt = DeviceObject->DeviceExtension;
|
|
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, &OldIrql );
|
|
RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
|
|
if( IrpSp->MajorFunction == IRP_MJ_WRITE )
|
|
{
|
|
DeviceExt->TotalCharsQueued -= IrpSp->Parameters.Write.Length;
|
|
}
|
|
else
|
|
if( IrpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL
|
|
&& ( IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_IMMEDIATE_CHAR
|
|
|| IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SERIAL_XOFF_COUNTER
|
|
)
|
|
)
|
|
{
|
|
DeviceExt->TotalCharsQueued--;
|
|
}
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, OldIrql );
|
|
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
Irp->IoStatus.Information = 0;
|
|
DigiIoCompleteRequest( Irp, IO_NO_INCREMENT );
|
|
|
|
DigiDump( DIGIFLOW, ("Exiting DigiCancelQueuedIrp\n") );
|
|
|
|
} // end DigiCancelQueuedIrp
|
|
|
|
|
|
VOID DigiTryToCompleteIrp( PDIGI_DEVICE_EXTENSION DeviceExt,
|
|
PKIRQL pOldIrql,
|
|
NTSTATUS StatusToUse,
|
|
PLIST_ENTRY Queue,
|
|
PKTIMER IntervalTimer,
|
|
PKTIMER TotalTimer,
|
|
PDIGI_START_ROUTINE StartRoutine )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceExt - Pointer to the device object for this device.
|
|
|
|
Irp - Pointer to the IRP to be cancelled.
|
|
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PIRP Irp;
|
|
|
|
DigiDump( DIGIFLOW, ("Entering DigiTryToCompleteIrp\n") );
|
|
|
|
if( IsListEmpty( Queue ) )
|
|
{
|
|
ASSERT( !IsListEmpty( Queue ) );
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, *pOldIrql );
|
|
return;
|
|
}
|
|
|
|
Irp = CONTAINING_RECORD( Queue->Flink,
|
|
IRP,
|
|
Tail.Overlay.ListEntry );
|
|
|
|
//
|
|
// We can decrement the reference to "remove" the fact
|
|
// that the caller no longer will be accessing this irp.
|
|
//
|
|
DigiDump( DIGIREFERENCE, (" Dec Ref for entering\n") );
|
|
DIGI_DEC_REFERENCE( Irp );
|
|
|
|
//
|
|
// Try to run down all other references to this irp.
|
|
//
|
|
DigiRundownIrpRefs( Irp, IntervalTimer, TotalTimer );
|
|
|
|
//
|
|
// See if the ref count is zero after trying to kill everybody else.
|
|
//
|
|
if( !DIGI_REFERENCE_COUNT( Irp ) )
|
|
{
|
|
BOOLEAN discoveredIrp;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
#if DBG
|
|
LONG Extra;
|
|
char const *IrpType,
|
|
ReadIrp[] = "read",
|
|
WriteIrp[] = "write",
|
|
FlushIrp[] = "flush",
|
|
IoctlIrp[] = "ioctl",
|
|
UnknownIrp[] = "unknown";
|
|
#endif
|
|
|
|
//
|
|
// The ref count was zero so we should complete this
|
|
// request.
|
|
//
|
|
|
|
DigiDump( DIGIREFERENCE, (" Completing Irp!\n") );
|
|
|
|
RemoveHeadList( Queue );
|
|
|
|
// Race to start IRPs: user mode (Serial*) vs. DPC routines
|
|
//
|
|
// When DigiStartIrpRequest adds an IRP to an empty queue, it starts the IRP.
|
|
// If we uncover/discover an IRP, DigiStartIrpRequest(s) will not see
|
|
// an empty queue while the lock is down, so it(they) will not start the IRP.
|
|
// Thus, if the queue is not empty now, we must start the IRP.
|
|
//
|
|
// We don't race ourselves because only one DigiTryToCompleteIrp wins (completes)
|
|
// and there never are any references to "buried" IRPs that would trigger
|
|
// completions and starts.
|
|
if( StartRoutine )
|
|
discoveredIrp = !IsListEmpty( Queue );
|
|
else
|
|
discoveredIrp = FALSE;
|
|
|
|
if( Queue == &DeviceExt->WriteQueue
|
|
&& DeviceExt->pXoffCounter )
|
|
{
|
|
ASSERT( DeviceExt->pXoffCounter == Irp->AssociatedIrp.SystemBuffer );
|
|
DeviceExt->pXoffCounter = NULL;
|
|
#if 1 // DBG DH necessary, but haven't figured out why
|
|
DeviceExt->XcPreview = 0; // Looks a little nicer...
|
|
#endif
|
|
}
|
|
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, *pOldIrql );
|
|
|
|
Irp->IoStatus.Status = StatusToUse;
|
|
if( StatusToUse == STATUS_CANCELLED )
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
#if DBG
|
|
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
switch ( IrpSp->MajorFunction )
|
|
{
|
|
case IRP_MJ_READ:
|
|
IrpType = ReadIrp;
|
|
Extra = IrpSp->Parameters.Read.Length;
|
|
if (Irp->IoStatus.Information>IrpSp->Parameters.Read.Length)
|
|
{
|
|
DbgPrint("Returning too much data! Asked for (%d) gave (%d).\n",
|
|
IrpSp->Parameters.Read.Length,
|
|
Irp->IoStatus.Information);
|
|
DbgBreakPoint();
|
|
}
|
|
break;
|
|
case IRP_MJ_WRITE:
|
|
IrpType = WriteIrp;
|
|
Extra = IrpSp->Parameters.Write.Length;
|
|
break;
|
|
case IRP_MJ_FLUSH_BUFFERS:
|
|
IrpType = FlushIrp;
|
|
Extra = -1;
|
|
break;
|
|
case IRP_MJ_DEVICE_CONTROL:
|
|
IrpType = IoctlIrp;
|
|
Extra = IrpSp->Parameters.DeviceIoControl.IoControlCode;
|
|
break;
|
|
default:
|
|
IrpType = UnknownIrp;
|
|
Extra = IrpSp->MajorFunction;
|
|
break;
|
|
}
|
|
DigiDump( (DIGIFLOW|DIGIREAD|DIGIWRITE|DIGIIRP|DIGIWAIT|DIGIREFERENCE),
|
|
("Completing %s(%d) IRP 0x%x, Status = 0x%.8x, Information = %u\n",
|
|
IrpType, Extra, Irp, Irp->IoStatus.Status, Irp->IoStatus.Information ) );
|
|
#endif
|
|
|
|
if (StatusToUse==STATUS_SUCCESS)
|
|
{
|
|
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|
switch ( IrpSp->MajorFunction )
|
|
{
|
|
case IRP_MJ_READ:
|
|
ExInterlockedAddUlong(&DeviceExt->ParentControllerExt->PerfData.BytesRead,
|
|
Irp->IoStatus.Information,
|
|
&DeviceExt->ParentControllerExt->PerfLock);
|
|
ExInterlockedAddUlong(&DeviceExt->PerfData.BytesRead,
|
|
Irp->IoStatus.Information,
|
|
&DeviceExt->PerfLock);
|
|
break;
|
|
case IRP_MJ_WRITE:
|
|
ExInterlockedAddUlong(&DeviceExt->ParentControllerExt->PerfData.BytesWritten,
|
|
Irp->IoStatus.Information,
|
|
&DeviceExt->ParentControllerExt->PerfLock);
|
|
ExInterlockedAddUlong(&DeviceExt->PerfData.BytesWritten,
|
|
Irp->IoStatus.Information,
|
|
&DeviceExt->PerfLock);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
DigiIoCompleteRequest( Irp,
|
|
(char) ((StatusToUse == STATUS_SUCCESS) ? IO_SERIAL_INCREMENT : IO_NO_INCREMENT) );
|
|
|
|
if( discoveredIrp )
|
|
{
|
|
//
|
|
// We uncovered it, so we must start it.
|
|
// DH rare race: two IRPs, first completes, second cancels, a third is queued onto an empty queue -- will be started twice.
|
|
//
|
|
KeAcquireSpinLock( &DeviceExt->ControlAccess, pOldIrql );
|
|
// IRP may have timed out or have been cancelled while we dropped the lock.
|
|
if( !IsListEmpty( Queue ) )
|
|
StartRoutine( DeviceExt->ParentControllerExt, DeviceExt, pOldIrql );
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, *pOldIrql );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
KeReleaseSpinLock( &DeviceExt->ControlAccess, *pOldIrql );
|
|
}
|
|
|
|
//
|
|
// The expected behavior is for DigiTryToCompleteIrp to return
|
|
// with the passed in ControlAccess spinlock released.
|
|
//
|
|
|
|
DigiDump( DIGIFLOW, ("Exiting DigiTryToCompleteIrp\n") );
|
|
|
|
} // end DigiTryToCompleteIrp
|
|
|
|
|
|
|
|
void __inline
|
|
DigiRundownIrpRefs( IN PIRP Irp,
|
|
IN PKTIMER IntervalTimer OPTIONAL,
|
|
IN PKTIMER TotalTimer OPTIONAL )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine runs through the various items that *could*
|
|
have a reference to the current read/write. It try's to kill
|
|
the reason. If it does succeed in killing the reason it
|
|
will decrement the reference count on the irp.
|
|
|
|
NOTE: This routine assumes that it is called with the ControlAccess
|
|
spin lock held.
|
|
|
|
Arguments:
|
|
|
|
Irp - Pointer to current irp for this particular operation.
|
|
|
|
IntervalTimer - Pointer to the interval timer for the operation.
|
|
NOTE: This could be null.
|
|
|
|
TotalTimer - Pointer to the total timer for the operation.
|
|
NOTE: This could be null.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
if( IntervalTimer )
|
|
{
|
|
//
|
|
// Try to cancel the operations interval timer. If the operation
|
|
// returns true then the timer did have a reference to the
|
|
// irp. Since we've canceled this timer that reference is
|
|
// no longer valid and we can decrement the reference count.
|
|
//
|
|
// If the cancel returns false then this means either of two things:
|
|
//
|
|
// a) The timer has already fired.
|
|
//
|
|
// b) There never was an interval timer.
|
|
//
|
|
// In the case of "b" there is no need to decrement the reference
|
|
// count since the "timer" never had a reference to it.
|
|
//
|
|
// In the case of "a", then the timer itself will be coming
|
|
// along and decrement it's reference. Note that the caller
|
|
// of this routine might actually be the this timer, but it
|
|
// has already decremented the reference.
|
|
//
|
|
|
|
if( KeCancelTimer( IntervalTimer ) )
|
|
{
|
|
DigiDump( DIGIREFERENCE, (" Dec Ref for interval timer\n") );
|
|
DIGI_DEC_REFERENCE( Irp );
|
|
}
|
|
}
|
|
|
|
if( TotalTimer )
|
|
{
|
|
//
|
|
// Try to cancel the operations total timer. If the operation
|
|
// returns true then the timer did have a reference to the
|
|
// irp. Since we've canceled this timer that reference is
|
|
// no longer valid and we can decrement the reference count.
|
|
//
|
|
// If the cancel returns false then this means either of two things:
|
|
//
|
|
// a) The timer has already fired.
|
|
//
|
|
// b) There never was an total timer.
|
|
//
|
|
// In the case of "b" there is no need to decrement the reference
|
|
// count since the "timer" never had a reference to it.
|
|
//
|
|
// In the case of "a", then the timer itself will be coming
|
|
// along and decrement it's reference. Note that the caller
|
|
// of this routine might actually be the this timer, but it
|
|
// has already decremented the reference.
|
|
//
|
|
|
|
if( KeCancelTimer( TotalTimer ) )
|
|
{
|
|
DigiDump( DIGIREFERENCE, (" Dec Ref for total timer\n") );
|
|
DIGI_DEC_REFERENCE( Irp );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Don't kill the cancel routine until there's nothing else left.
|
|
//
|
|
if( DIGI_REFERENCE_COUNT( Irp ) == 1 )
|
|
{
|
|
KIRQL CancelIrql;
|
|
|
|
IoAcquireCancelSpinLock( &CancelIrql );
|
|
if( Irp->CancelRoutine )
|
|
{
|
|
DigiDump( DIGIREFERENCE, (" Dec Ref for cancel\n") );
|
|
DIGI_DEC_REFERENCE( Irp );
|
|
|
|
IoSetCancelRoutine( Irp, NULL );
|
|
IoReleaseCancelSpinLock( CancelIrql );
|
|
}
|
|
else
|
|
{
|
|
IoReleaseCancelSpinLock( CancelIrql );
|
|
}
|
|
}
|
|
} // end DigiRundownIrpRefs
|
|
|
|
|
|
void
|
|
DigiCancelIrpQueue( IN PDEVICE_OBJECT DeviceObject,
|
|
IN PLIST_ENTRY Queue )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
DeviceExt - Pointer to the device object extension for this device.
|
|
|
|
Queue - The queue of Irp requests.
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL cancelIrql;
|
|
KIRQL OldIrql;
|
|
PDIGI_DEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
|
|
|
|
DigiDump( DIGIFLOW, ("DigiBoard: Entering DigiCancelIrpQueue\n") );
|
|
|
|
KeAcquireSpinLock( &DeviceExt->NewIrpLock, &OldIrql );
|
|
KeAcquireSpinLockAtDpcLevel( &DeviceExt->ControlAccess );
|
|
//
|
|
// We acquire the cancel spin lock. This will prevent the
|
|
// irps from moving around.
|
|
//
|
|
IoAcquireCancelSpinLock( &cancelIrql );
|
|
|
|
while( !IsListEmpty( Queue ) )
|
|
{
|
|
PIRP currentLastIrp;
|
|
PDRIVER_CANCEL cancelRoutine;
|
|
|
|
currentLastIrp = CONTAINING_RECORD(
|
|
Queue->Blink,
|
|
IRP,
|
|
Tail.Overlay.ListEntry );
|
|
|
|
cancelRoutine = currentLastIrp->CancelRoutine;
|
|
currentLastIrp->Cancel = TRUE;
|
|
currentLastIrp->CancelRoutine = NULL;
|
|
|
|
IoReleaseCancelSpinLock( cancelIrql );
|
|
|
|
KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
|
|
|
|
if( cancelRoutine )
|
|
{
|
|
IoAcquireCancelSpinLock( &cancelIrql );
|
|
currentLastIrp->CancelIrql = cancelIrql;
|
|
//
|
|
// This routine will release the cancel spin lock.
|
|
//
|
|
cancelRoutine( DeviceObject, currentLastIrp );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Assume whoever nulled out the cancel routine
|
|
// is also going to complete the IRP.
|
|
//
|
|
|
|
#if DBG
|
|
DbgPrint( "DigiCancelIrpQueue(dev %s, ", DeviceExt->DeviceDbgString );
|
|
|
|
switch( (UCHAR *)Queue - (UCHAR *)DeviceExt )
|
|
{
|
|
case FIELD_OFFSET( DIGI_DEVICE_EXTENSION, ReadQueue ):
|
|
DbgPrint( "ReadQueue): " ); break;
|
|
case FIELD_OFFSET( DIGI_DEVICE_EXTENSION, WriteQueue ):
|
|
DbgPrint( "WriteQueue): " ); break;
|
|
case FIELD_OFFSET( DIGI_DEVICE_EXTENSION, WaitQueue ):
|
|
DbgPrint( "WaitQueue): " ); break;
|
|
default:
|
|
DbgPrint( "unknown queue at offset %d): ",
|
|
(UCHAR *)Queue - (UCHAR *)DeviceExt );
|
|
break;
|
|
}
|
|
|
|
DbgPrint( "no cancel routine for irp 0x%x!\n", currentLastIrp );
|
|
#endif // DBG
|
|
|
|
KeReleaseSpinLock( &DeviceExt->NewIrpLock, OldIrql );
|
|
return;
|
|
}
|
|
|
|
KeAcquireSpinLockAtDpcLevel( &DeviceExt->ControlAccess );
|
|
IoAcquireCancelSpinLock( &cancelIrql );
|
|
}
|
|
|
|
IoReleaseCancelSpinLock( cancelIrql );
|
|
KeReleaseSpinLockFromDpcLevel( &DeviceExt->ControlAccess );
|
|
KeReleaseSpinLock( &DeviceExt->NewIrpLock, OldIrql );
|
|
|
|
} // end DigiCancelIrpQueue
|
|
|