Leaked source code of windows server 2003
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.
 
 
 
 
 
 

730 lines
17 KiB

#include <pch.h>
#include "irplist.h"
void
IrpList_InitEx(
PIRP_LIST IrpList,
PKSPIN_LOCK ListLock,
PDRIVER_CANCEL CancelRoutine,
PIRP_COMPLETION_ROUTINE IrpCompletionRoutine
)
/*++
Routine Description:
Initialize the IrpList
Arguments:
IrpList - Pointer to the IrpList structure
ListLock - Pointer to the spinlock for the IrpList
CancelRoutine - Routine to be called when an Irp on the IrpList
is cancelled
IrpCompletionRoutine - Optional Completion routine for an Irp on the IrpList
Return Value:
VOID
--*/
{
LockedList_Init(&IrpList->LockedList, ListLock);
ASSERT(CancelRoutine != NULL);
IrpList->CancelRoutine = CancelRoutine;
IrpList->IrpCompletionRoutine = IrpCompletionRoutine;
}
NTSTATUS
IrpList_EnqueueLocked(
PIRP_LIST IrpList,
PIRP Irp,
BOOLEAN StoreListInIrp,
BOOLEAN InsertTail
)
/*++
Routine Description:
Enqueues an Irp on the IrpList.
Assumes the caller has acquired the IrpList Spinlock.
Arguments:
IrpList - Pointer to the IrpList structure
Irp - Pointer to the Irp to Enqueue
StoreListInIrp - Set to TRUE is the Irp will be used to store the
list entry
InsertTail - Set to TRUE if Irp is to be enqueued at
the tail of the IrpList.
Return Value:
NTSTATUS - STATUS_SUCCESS or appropriate error code
--*/
{
PDRIVER_CANCEL oldCancelRoutine;
NTSTATUS status;
//
// must set a cancel routine before checking the Cancel flag
//
oldCancelRoutine = IoSetCancelRoutine(Irp, IrpList->CancelRoutine);
ASSERT(oldCancelRoutine == NULL);
if (Irp->Cancel) {
//
// This IRP has already been cancelled, so complete it now.
// We must clear the cancel routine before completing the IRP.
// We must release the spinlock before calling out of the driver.
//
oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
if (oldCancelRoutine != NULL) {
//
// The cancel routine was NOT called
//
ASSERT(oldCancelRoutine == IrpList->CancelRoutine);
status = STATUS_CANCELLED;
}
else {
//
// The cancel routine was called. As soon as we drop the spinlock,
// it will dequeue and complete the IRP. Increase the count because
// the cancel routine will decrement it.
//
IrpList->LockedList.Count++;
InitializeListHead(&Irp->Tail.Overlay.ListEntry);
IoMarkIrpPending(Irp);
status = Irp->IoStatus.Status = STATUS_PENDING;
//
// save a ptr to this structure in the Irp for the cancel routine.
//
if (StoreListInIrp) {
Irp->Tail.Overlay.DriverContext[IRP_LIST_INDEX] = IrpList;
}
}
}
else {
if (InsertTail) {
LL_ADD_TAIL(&IrpList->LockedList, &Irp->Tail.Overlay.ListEntry);
}
else {
LL_ADD_HEAD(&IrpList->LockedList, &Irp->Tail.Overlay.ListEntry);
}
IoMarkIrpPending(Irp);
status = Irp->IoStatus.Status = STATUS_PENDING;
//
// save a ptr to this structure in the Irp for the cancel routine.
//
if (StoreListInIrp) {
Irp->Tail.Overlay.DriverContext[IRP_LIST_INDEX] = IrpList;
}
}
return status;
}
NTSTATUS
IrpList_EnqueueEx(
PIRP_LIST IrpList,
PIRP Irp,
BOOLEAN StoreListInIrp
)
/*++
Routine Description:
Enqueues an Irp on the IrpList.
Arguments:
IrpList - Pointer to the IrpList structure
Irp - Pointer to the Irp to Enqueue
StoreListInIrp - Set to TRUE is the Irp will be used to store the
list entry
Return Value:
NTSTATUS - STATUS_SUCCESS or appropriate error code
--*/
{
NTSTATUS status;
KIRQL irql;
LL_LOCK(&IrpList->LockedList, &irql);
status = IrpList_EnqueueLocked(IrpList, Irp, StoreListInIrp, TRUE);
LL_UNLOCK(&IrpList->LockedList, irql);
return status;
}
BOOLEAN
IrpList_MakeNonCancellable(
PIRP_LIST IrpList,
PIRP Irp
)
/*++
Routine Description:
Sets the Irp Cancel routine to NULL, making it non-cancellable.
Arguments:
IrpList - Pointer to the IrpList structure
Irp - Pointer to the Irp to Enqueue
Return Value:
BOOLEAN - TRUE if we succeeded in making the Irp non-cancellable.
--*/
{
PDRIVER_CANCEL oldCancelRoutine;
BOOLEAN result;
result = FALSE;
oldCancelRoutine = IoSetCancelRoutine(Irp, NULL);
//
// IoCancelIrp() could have just been called on this IRP.
// What we're interested in is not whether IoCancelIrp() was called
// (ie, nextIrp->Cancel is 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 != NULL) {
//
// Cancel routine not called for this IRP. Return this IRP.
//
ASSERT (oldCancelRoutine == IrpList->CancelRoutine);
Irp->Tail.Overlay.DriverContext[IRP_LIST_INDEX] = NULL;
result = TRUE;
}
else {
//
// This IRP was just cancelled and the cancel routine was (or will
// be) called. The cancel routine will complete this IRP as soon as
// we drop the spinlock. 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(Irp->Cancel);
InitializeListHead(&Irp->Tail.Overlay.ListEntry);
result = FALSE;
}
return result;
}
PIRP
IrpList_DequeueLocked(
PIRP_LIST IrpList
)
{
/*++
Routine Description:
Dequeue an IRP from the head of the IrpList.
Assumes the caller has acquired the IrpList SpinLock.
Arguments:
IrpList - Pointer to the IrpList structure
Return Value:
PIRP - Pointer to the Irp dequeued form the IrpList.
NULL if no IRP is avaliable.
--*/
PIRP nextIrp = NULL;
PLIST_ENTRY ple;
nextIrp = NULL;
while (nextIrp == NULL && !IsListEmpty(&IrpList->LockedList.ListHead)){
ple = LL_REMOVE_HEAD(&IrpList->LockedList);
//
// Get the next IRP off the queue and clear the cancel routine
//
nextIrp = CONTAINING_RECORD(ple, IRP, Tail.Overlay.ListEntry);
if (IrpList_MakeNonCancellable(IrpList, nextIrp) == FALSE) {
nextIrp = NULL;
}
}
return nextIrp;
}
PIRP
IrpList_Dequeue(
PIRP_LIST IrpList
)
/*++
Routine Description:
Dequeue an IRP from the head of the IrpList.
Arguments:
IrpList - Pointer to the IrpList structure
Return Value:
PIRP - Pointer to the Irp dequeued form the IrpList.
NULL if no IRP is avaliable.
--*/
{
PIRP irp;
KIRQL irql;
LL_LOCK(&IrpList->LockedList, &irql);
irp = IrpList_DequeueLocked(IrpList);
LL_UNLOCK(&IrpList->LockedList, irql);
return irp;
}
BOOLEAN
IrpList_DequeueIrp(
PIRP_LIST IrpList,
PIRP Irp
)
/*++
Routine Description:
Dequeue a specific IRP from the IrpList.
Arguments:
IrpList - Pointer to the IrpList structure
Irp - Pointer to an IRP contained in the IrpList.
Return Value:
BOOLEAN - TRUE is the IRP was successfully removed off the IrpList
--*/
{
KIRQL irql;
BOOLEAN result;
LL_LOCK(&IrpList->LockedList, &irql);
result = IrpList_DequeueIrpLocked(IrpList, Irp);
LL_UNLOCK(&IrpList->LockedList, irql);
return result;
}
BOOLEAN
IrpList_DequeueIrpLocked(
PIRP_LIST IrpList,
PIRP Irp
)
/*++
Routine Description:
Dequeue a specific IRP from the IrpList
Assumes the caller has acquired the IrpList spinlock
Arguments:
IrpList - Pointer to the IrpList structure
Irp - Pointer to an IRP contained in the IrpList.
Return Value:
BOOLEAN - TRUE is the IRP was successfully removed off the IrpList
--*/
{
PLIST_ENTRY ple;
PIRP pIrp;
BOOLEAN result;
result = FALSE;
for (ple = IrpList->LockedList.ListHead.Flink;
ple != &IrpList->LockedList.ListHead;
ple = ple->Flink) {
pIrp = CONTAINING_RECORD(ple, IRP, Tail.Overlay.ListEntry);
if (pIrp == Irp) {
RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
IrpList->LockedList.Count--;
result = IrpList_MakeNonCancellable(IrpList, pIrp);
break;
}
}
return result;
}
ULONG
IrpList_ProcessAndDrain(
PIRP_LIST IrpList,
PFNPROCESSIRP FnProcessIrp,
PVOID Context,
PLIST_ENTRY DrainHead
)
/*++
Routine Description:
Remove all cancellable Irps from the IrpList and process.
Arguments:
IrpList - Pointer to the IrpList structure
FnProcessIrp - Function to process the Irp
Context - Context to pass into the FnProcessIrp
DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
Return Value:
ULONG - Number of IRPs processed
--*/
{
ULONG count;
KIRQL irql;
LL_LOCK(&IrpList->LockedList, &irql);
count = IrpList_ProcessAndDrainLocked(
IrpList, FnProcessIrp, Context, DrainHead);
LL_UNLOCK(&IrpList->LockedList, irql);
return count;
}
ULONG
IrpList_ProcessAndDrainLocked(
PIRP_LIST IrpList,
PFNPROCESSIRP FnProcessIrp,
PVOID Context,
PLIST_ENTRY DrainHead
)
/*++
Routine Description:
Remove all cancellable Irps from the IrpList and process
Assumes that the caller has acquired the IrpList Spinlock.
Arguments:
IrpList - Pointer to the IrpList structure
FnProcessIrp - Function to process the Irp
Context - Context to pass into the FnProcessIrp
DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
Return Value:
ULONG - Number of IRPs processed
--*/
{
PLIST_ENTRY ple;
PIRP pIrp;
NTSTATUS status;
ULONG count;
count = 0;
ASSERT(FnProcessIrp != NULL);
for (ple = IrpList->LockedList.ListHead.Flink;
ple != &IrpList->LockedList.ListHead;
/* ple = ple->Flink */) {
pIrp = CONTAINING_RECORD(ple, IRP, Tail.Overlay.ListEntry);
//
// Advance immediately so we don't lose the next link in the list in
// case we remove the current irp.
//
ple = ple->Flink;
if (FnProcessIrp(Context, pIrp)) {
RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
IrpList->LockedList.Count--;
if (IrpList_MakeNonCancellable(IrpList, pIrp)) {
InsertTailList(DrainHead, &pIrp->Tail.Overlay.ListEntry);
count++;
}
}
}
return count;
}
ULONG
IrpList_DrainLocked(
PIRP_LIST IrpList,
PLIST_ENTRY DrainHead
)
/*++
Routine Description:
Remove all cancellable Irps from the IrpList and queue to the DrainHead
Arguments:
IrpList - Pointer to the IrpList structure
DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
Return Value:
ULONG - Number of IRPs processed
--*/
{
PIRP irp;
ULONG count;
count = 0;
while (TRUE) {
irp = IrpList_DequeueLocked(IrpList);
if (irp != NULL) {
InsertTailList(DrainHead, &irp->Tail.Overlay.ListEntry);
count++;
}
else {
break;
}
}
ASSERT(LL_GET_COUNT(&IrpList->LockedList) == 0);
return count;
}
ULONG
IrpList_DrainLockedByFileObject(
PIRP_LIST IrpList,
PLIST_ENTRY DrainHead,
PFILE_OBJECT FileObject
)
/*++
Routine Description:
Remove all cancellable Irps for specified FileObject
from the IrpList and queue to the DrainHead
Arguments:
IrpList - Pointer to the IrpList structure
DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
FileObject - Pointer to specified FILE_OBJECT
Return Value:
ULONG - Number of IRPs processed
--*/
{
PIRP pIrp;
PDRIVER_CANCEL pOldCancelRoutine;
PIO_STACK_LOCATION pStack;
ULONG count;
PLIST_ENTRY ple;
count = 0;
ASSERT(FileObject != NULL);
for (ple = IrpList->LockedList.ListHead.Flink;
ple != &IrpList->LockedList.ListHead;
/* ple = ple->Flink */) {
pIrp = CONTAINING_RECORD(ple, IRP, Tail.Overlay.ListEntry);
//
// Advance immediately so we don't lose the next link in the list in
// case we remove the current irp.
//
ple = ple->Flink;
pStack = IoGetCurrentIrpStackLocation(pIrp);
if (pStack->FileObject == FileObject) {
RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
IrpList->LockedList.Count--;
if (IrpList_MakeNonCancellable(IrpList, pIrp)) {
InsertTailList(DrainHead, &pIrp->Tail.Overlay.ListEntry);
count++;
}
}
}
return count;
}
ULONG
IrpList_Drain(
PIRP_LIST IrpList,
PLIST_ENTRY DrainHead
)
/*++
Routine Description:
Remove all cancellable IRPs and queue to DrainHead
Arguments:
IrpList - Pointer to the IrpList structure
DrainHead - Pointer to LIST_ENTRY to hold dequeued IRPs
Return Value:
ULONG - Number of IRPs processed
--*/
{
ULONG count;
KIRQL irql;
LL_LOCK(&IrpList->LockedList, &irql);
count = IrpList_DrainLocked(IrpList, DrainHead);
LL_UNLOCK(&IrpList->LockedList, irql);
return count;
}
void
IrpList_HandleCancel(
PIRP_LIST IrpList,
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
/*++
Routine Description:
Cancel Routine for IRPs on the IrpList
Arguments:
IrpList - Pointer to the IrpList structure
DeviceObject - Device Object, used for IrpCompletionRoutine
Return Value:
VOID
--*/
{
KIRQL irql;
//
// Release the global cancel spinlock.
// Do this while not holding any other spinlocks 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.
//
LL_LOCK(&IrpList->LockedList, &irql);
if (!IsListEmpty(&Irp->Tail.Overlay.ListEntry)) {
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
IrpList->LockedList.Count--;
}
LL_UNLOCK(&IrpList->LockedList, irql);
if (IrpList->IrpCompletionRoutine != NULL) {
(void) IrpList->IrpCompletionRoutine(
DeviceObject, Irp, STATUS_CANCELLED);
}
else {
//
// Complete the IRP. This is a call outside the driver, so all
// spinlocks must be released by this point.
//
Irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
}
void
IrpList_CancelRoutine(
PDEVICE_OBJECT DeviceObject,
PIRP Irp
)
{
PSCUTIL_EXTENSION pExt;
KIRQL oldIrql;
PIO_STACK_LOCATION irpSp;
pExt = *((PSCUTIL_EXTENSION*) DeviceObject->DeviceExtension);
irpSp = IoGetNextIrpStackLocation(Irp);
if (irpSp->CompletionRoutine) {
Irp->IoStatus.Status = STATUS_CANCELLED;
irpSp->CompletionRoutine(DeviceObject,
Irp,
irpSp->Context);
}
IoReleaseRemoveLock(pExt->RemoveLock,
Irp);
DecIoCount(pExt);
IrpList_HandleCancel(IRP_LIST_FROM_IRP(Irp),
DeviceObject,
Irp);
}