#include #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); }