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
xxxxx@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, xxxxx@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, xxxxx@thesycon.de
*
* Company: Thesycon GmbH, Ilmenau, Germany
* www.thesycon.de
*
************************************************************************/
#include <wdm.h>
#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: $subst(‘Recip.EmailAddr’)
To unsubscribe send a blank email to leave-ntdev-$subst(‘Recip.MemberIDChar’)@lists.osr.com</wdm.h>