|
|
#include "pch.h"
VOID P2InitIrpQueueContext( IN PIRPQUEUE_CONTEXT IrpQueueContext ) { InitializeListHead( &IrpQueueContext->irpQueue ); KeInitializeSpinLock( &IrpQueueContext->irpQueueSpinLock ); }
VOID P2CancelQueuedIrp( IN PIRPQUEUE_CONTEXT IrpQueueContext, IN PIRP Irp ) { KIRQL oldIrql; // Release the global cancel spin lock. Do this while not holding
// any other spin locks so that we exit at the right IRQL.
IoReleaseCancelSpinLock( Irp->CancelIrql );
//
// Dequeue and complete the IRP. The enqueue and dequeue
// functions synchronize properly so that if this cancel routine
// is called, the dequeue is safe and only the cancel routine
// will complete the IRP. Hold the spin lock for the IRP queue
// while we do this.
//
KeAcquireSpinLock( &IrpQueueContext->irpQueueSpinLock, &oldIrql ); RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); KeReleaseSpinLock( &IrpQueueContext->irpQueueSpinLock, oldIrql); // Complete the IRP. This is a call outside the driver, so all
// spin locks must be released by this point.
P4CompleteRequest( Irp, STATUS_CANCELLED, 0 ); return; }
NTSTATUS P2QueueIrp( IN PIRP Irp, IN PIRPQUEUE_CONTEXT IrpQueueContext, IN PDRIVER_CANCEL CancelRoutine ) { PDRIVER_CANCEL oldCancelRoutine; KIRQL oldIrql; NTSTATUS status = STATUS_PENDING; KeAcquireSpinLock( &IrpQueueContext->irpQueueSpinLock, &oldIrql ); // Queue the IRP and call IoMarkIrpPending to indicate that the
// IRP may complete on a different thread.
//
// N.B. It's okay to call these inside the spin lock because
// they're macros, not functions.
IoMarkIrpPending( Irp ); InsertTailList( &IrpQueueContext->irpQueue, &Irp->Tail.Overlay.ListEntry ); // Must set a Cancel routine before checking the Cancel flag.
#pragma warning( push )
#pragma warning( disable : 4054 4055 )
oldCancelRoutine = IoSetCancelRoutine( Irp, CancelRoutine ); #pragma warning( pop )
ASSERT( !oldCancelRoutine );
if( Irp->Cancel ){ // The IRP was canceled. Check whether our cancel routine was called.
#pragma warning( push )
#pragma warning( disable : 4054 4055 )
oldCancelRoutine = IoSetCancelRoutine( Irp, NULL ); #pragma warning( pop )
if( oldCancelRoutine ) { // The cancel routine was NOT called.
// So dequeue the IRP now and complete it after releasing the spinlock.
RemoveEntryList( &Irp->Tail.Overlay.ListEntry ); status = Irp->IoStatus.Status = STATUS_CANCELLED; } else { // The cancel routine WAS called. As soon as we drop our
// spin lock it will dequeue and complete the IRP. So
// leave the IRP in the queue and otherwise don't touch
// it. Return pending since we're not completing the IRP
// here.
} } KeReleaseSpinLock(&IrpQueueContext->irpQueueSpinLock, oldIrql); // Normally you shouldn't call IoMarkIrpPending and return a
// status other than STATUS_PENDING. But you can break this rule
// if you complete the IRP.
if( status != STATUS_PENDING ) { P4CompleteRequest( Irp, Irp->IoStatus.Status, Irp->IoStatus.Information ); } return status; }
PIRP P2DequeueIrp( IN PIRPQUEUE_CONTEXT IrpQueueContext, IN PDRIVER_CANCEL CancelRoutine ) { KIRQL oldIrql; PIRP nextIrp = NULL;
KeAcquireSpinLock( &IrpQueueContext->irpQueueSpinLock, &oldIrql );
while( !nextIrp && !IsListEmpty( &IrpQueueContext->irpQueue ) ){
PDRIVER_CANCEL oldCancelRoutine;
PLIST_ENTRY listEntry = RemoveHeadList( &IrpQueueContext ->irpQueue ); // Get the next IRP off the queue.
nextIrp = CONTAINING_RECORD( listEntry, IRP, Tail.Overlay.ListEntry ); // Clear the IRP's cancel routine
#pragma warning( push )
#pragma warning( disable : 4054 4055 )
oldCancelRoutine = IoSetCancelRoutine( nextIrp, NULL ); #pragma warning( pop )
// IoCancelIrp() could have just been called on this IRP.
// What we're interested in is not whether IoCancelIrp() was called (nextIrp->Cancel flag set),
// but whether IoCancelIrp() called (or is about to call) our cancel routine.
// To check that, check the result of the test-and-set macro IoSetCancelRoutine.
if( oldCancelRoutine ) { // Cancel routine not called for this IRP. Return this IRP.
#if DBG
ASSERT( oldCancelRoutine == CancelRoutine ); #else
UNREFERENCED_PARAMETER( CancelRoutine ); #endif
} else { // This IRP was just canceled and the cancel routine was (or will be) called.
// The cancel routine will complete this IRP as soon as we drop the spin lock,
// so don't do anything with the IRP.
// Also, the cancel routine will try to dequeue the IRP,
// so make the IRP's listEntry point to itself.
ASSERT( nextIrp->Cancel ); InitializeListHead( &nextIrp->Tail.Overlay.ListEntry ); nextIrp = NULL; } } KeReleaseSpinLock( &IrpQueueContext ->irpQueueSpinLock, oldIrql ); return nextIrp; }
VOID P2CancelRoutine( IN PDEVICE_OBJECT DevObj, IN PIRP Irp ) // this routine is driver specific - most other routines in this file are generic
{ PFDO_EXTENSION fdx = DevObj->DeviceExtension; PIRPQUEUE_CONTEXT irpQueueContext = &fdx->IrpQueueContext; P2CancelQueuedIrp( irpQueueContext, Irp ); }
|