From: Udo Eberhardt [Udo.Eb@thesycon.de] Sent: Monday, February 18, 2002 9:12 AM To: NT Developers Interest List Subject: [ntdev] RE: A bit of confusion regarding IRP cancellation Hi all, Various algorithm for implementing IRP cancellation have been discussed in the related news groups many times. I would like to publish my favorite implementation. I attached the source code of an IRP queue object implementation. It is quite generic and flexible. I used it in many drivers. The implementation is based on a posting from Jamie Hanrahan in comp.os.ms-windows.programmer.nt.kernel-mode on 29 Jun 1999. I hope there will be some comments from the experts... -- Udo Eberhardt Thesycon GmbH, Germany Udo.Eberhardt@thesycon.de www.thesycon.de ######################################################### irpqueue.h ######################################################### /************************************************************************ * * Module: irpqueue.h * Long name: IRP Queue * Description: definition of IRP Queue object * * Runtime Env.: Kernel: WinNT 4.0, WDM * * Author(s): Udo Eberhardt, Udo.Eberhardt@thesycon.de * * Company: Thesycon GmbH, Ilmenau, Germany * www.thesycon.de * ************************************************************************/ #ifndef __IRPQUEUE_H__ #define __IRPQUEUE_H__ // // IRP Queue Object // // Implements a queue of IRPs. // Queued IRPs are hold in a cancellable state. // typedef struct _IRP_QUEUE { // spinlock that protects the lists KSPIN_LOCK Lock; // list of queued IRPs LIST_ENTRY IrpList; // list of cancelled IRPs LIST_ENTRY CancelledIrpList; } IRP_QUEUE; // // Initialize the IRP queue object // // MUST be called once before any other function // void IrpQueueInit( IRP_QUEUE* Queue ); // // Add an IRP to the queue // // The function calls IoMarkIrpPending with the IRP passed in. // The functions returns always STATUS_PENDING // The return status of this function should be the return // status of the dispatch function that handles the IRP. // // The IRP could get cancelled immediately after it was passed to this function. // Therefore, the IRP MUST NOT be accessed after the call. // // Note: The field Irp->Tail.Overlay.DriverContext[3] is used // by this function for internal purposes. // NTSTATUS IrpQueueAddReq( IRP_QUEUE* Queue, IRP* Irp, BOOLEAN AtFront // TRUE: insert at head, FALSE: insert at tail ); // shortcut macros #define IrpQueueAdd(Queue,Irp) IrpQueueAddReq(Queue,Irp,FALSE) #define IrpQueuePutBack(Queue,Irp) IrpQueueAddReq(Queue,Irp,TRUE) // // Remove an IRP from the head of queue // // The function returns a pointer to the IRP that was // unqueued or NULL if the queue is empty. // IRP* IrpQueueRemove( IRP_QUEUE* Queue ); // // Returns TRUE if the IRP queue is empty, FALSE otherwise // BOOLEAN IrpQueueEmpty( IRP_QUEUE* Queue ); // // Cleanup the IRP queue // // The function removes and completes (with STATUS_CANCELLED) all IRPs that // are related to the file object identified by CleanupFileObject. // If CleanupFileObject is NULL all queued IRPs are removed and completed. // void IrpQueueCleanup( IRP_QUEUE* Queue, FILE_OBJECT* CleanupFileObject ); #endif // __IRPQUEUE_H__ /*************************** EOF **************************************/ ######################################################### ######################################################### irpqueue.c ######################################################### /************************************************************************ * * Module: irpqueue.c * Long name: IRP Queue * Description: implementation of IRP Queue object * * Runtime Env.: Kernel: WinNT 4.0, WDM * * Author(s): Udo Eberhardt, Udo.Eberhardt@thesycon.de * * Company: Thesycon GmbH, Ilmenau, Germany * www.thesycon.de * ************************************************************************/ #include #include "irpqueue.h" static void IrpQueue_IrpCancelRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); // // Initialize the IRP queue object // void IrpQueueInit( IRP_QUEUE* Queue ) { KeInitializeSpinLock(&Queue->Lock); InitializeListHead(&Queue->IrpList); InitializeListHead(&Queue->CancelledIrpList); } // IrpQueueInit // // Add an IRP to the queue // NTSTATUS IrpQueueAddReq( IRP_QUEUE* Queue, IRP* Irp, BOOLEAN AtFront ) { NTSTATUS Status; KIRQL irql; void *OldCancel; // irp is pending IoMarkIrpPending(Irp); // lock lists KeAcquireSpinLock(&Queue->Lock,&irql); // install cancel handler OldCancel = IoSetCancelRoutine(Irp,IrpQueue_IrpCancelRoutine); ASSERT(OldCancel==NULL); // check cancel flag to determine if the IRP is already cancelled // if so remove our cancel routine if ( Irp->Cancel && (NULL!=IoSetCancelRoutine(Irp,NULL)) ) { // IRP has been cancelled and IO manager does not call our cancel routine Status = STATUS_CANCELLED; // unlock lists KeReleaseSpinLock(&Queue->Lock,irql); // complete IRP Irp->IoStatus.Status = Status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp,IO_NO_INCREMENT); } else { // queue IRP now, it will be removed by the CancelRoutine or UnqueueRoutine // save back pointer to queue inside IRP, it is needed by the cancel routine Irp->Tail.Overlay.DriverContext[3] = Queue; // add IRP to list if ( AtFront ) { InsertHeadList(&Queue->IrpList,&Irp->Tail.Overlay.ListEntry); } else { InsertTailList(&Queue->IrpList,&Irp->Tail.Overlay.ListEntry); } // unlock lists KeReleaseSpinLock(&Queue->Lock,irql); } // irp is pending Status = STATUS_PENDING; return Status; } // IrpQueueAddReq // // Remove an IRP from the head of queue // IRP* IrpQueueRemove( IRP_QUEUE* Queue ) { IRP* Irp; LIST_ENTRY* ListEntry; KIRQL irql; // lock lists KeAcquireSpinLock(&Queue->Lock,&irql); // loop until the list is empty or we got an IRP for (;;) { if ( IsListEmpty(&Queue->IrpList) ) { // list is empty Irp = NULL; break; } else { // remove first IRP from list ListEntry = RemoveHeadList(&Queue->IrpList); Irp = CONTAINING_RECORD(ListEntry,IRP,Tail.Overlay.ListEntry); // remove our cancel routine if ( NULL==IoSetCancelRoutine(Irp,NULL) ) { // cancel routine is running or will run soon // put this irp to the cancelled list, it will be removed // and completed by the cancel routine InsertTailList(&Queue->CancelledIrpList,&Irp->Tail.Overlay.ListEntry); } else { // cancel routine is not running and will not run // invalidate our back pointer and return this IRP Irp->Tail.Overlay.DriverContext[3] = NULL; break; } } } //for // unlock lists KeReleaseSpinLock(&Queue->Lock,irql); return Irp; } // IrpQueueRemove // // Returns TRUE if the IRP queue is empty, FALSE otherwise // BOOLEAN IrpQueueEmpty( IRP_QUEUE* Queue ) { BOOLEAN empty; KIRQL irql; KeAcquireSpinLock(&Queue->Lock,&irql); empty = IsListEmpty(&Queue->IrpList); KeReleaseSpinLock(&Queue->Lock,irql); return empty; } //IrpQueueEmpty // // Cleanup the IRP queue // void IrpQueueCleanup( IRP_QUEUE* Queue, FILE_OBJECT* CleanupFileObject ) { LIST_ENTRY CleanupList; // temp local list head IRP* Irp; LIST_ENTRY* ListHead; LIST_ENTRY* ThisEntry; LIST_ENTRY* NextEntry; KIRQL irql; InitializeListHead(&CleanupList); // lock lists KeAcquireSpinLock(&Queue->Lock,&irql); // scan list ListHead = &Queue->IrpList; ThisEntry = ListHead->Flink; while ( ThisEntry!=ListHead ) { NextEntry = ThisEntry->Flink; Irp = CONTAINING_RECORD(ThisEntry,IRP,Tail.Overlay.ListEntry); if ( (CleanupFileObject==NULL) || (CleanupFileObject==IoGetCurrentIrpStackLocation(Irp)->FileObject) ) { // remove this IRP from queue RemoveEntryList(ThisEntry); // remove our cancel routine if ( NULL==IoSetCancelRoutine(Irp,NULL) ) { // cancel routine is running or will run soon // put this irp to the cancelled list, it will be removed // and completed by the cancel routine InsertTailList(&Queue->CancelledIrpList,ThisEntry); } else { // cancel routine is not running and will not run // invalidate our back pointer and put this IRP on the local cleanup list Irp->Tail.Overlay.DriverContext[3] = NULL; InsertTailList(&CleanupList,ThisEntry); } } ThisEntry = NextEntry; } //while // unlock lists KeReleaseSpinLock(&Queue->Lock,irql); // now complete all IRPs from cleanup list with STATUS_CANCELLED while ( !IsListEmpty(&CleanupList) ) { ThisEntry = RemoveHeadList(&CleanupList); Irp = CONTAINING_RECORD(ThisEntry,IRP,Tail.Overlay.ListEntry); Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp,IO_NO_INCREMENT); } } // IrpQueueCleanup // // Cancel routine that is installed on queued IRPs. // static void IrpQueue_IrpCancelRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { IRP_QUEUE* Queue; KIRQL irql; // release the global cancel spinlock that IO manager has acquired IoReleaseCancelSpinLock(Irp->CancelIrql); // a pointer to the queue is stored within the IRP while we own the IRP Queue = Irp->Tail.Overlay.DriverContext[3]; ASSERT(Queue!=NULL); // invalidate back pointer Irp->Tail.Overlay.DriverContext[3] = NULL; // lock lists KeAcquireSpinLock(&Queue->Lock,&irql); // remove this IRP from its list, it is either in IrpList or in CancelledIrpList RemoveEntryList(&Irp->Tail.Overlay.ListEntry); // unlock lists KeReleaseSpinLock(&Queue->Lock,irql); // complete the irp with STATUS_CANCELLED Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp,IO_NO_INCREMENT); } // IrpQueue_IrpCancelRoutine /*************************** EOF **************************************/ ######################################################### --- You are currently subscribed to ntdev as: GlennEverhart@FirstUSA.com To unsubscribe send a blank email to leave-ntdev-247T@lists.osr.com