// queue.c // // // Requires DDK Only // File created on 2/2/2005 // #include "pch.h" #ifdef CPR_WMI_TRACE #include "queue.tmh" #endif //#define RAISE_IRQL #define RELEASE_AT_DPC //#define RAISE_IRQL_ALL /////////////////////////////////////////////////////////////////////////////////////////////////// // Cancel Safe Driver Managed IRP Queue /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // CprInitializeQueue // Sets up a driver managed IRP queue. // // Arguments: // IN Queue // An instance of our queue structure // // IN StartIoRoutine // Routine where queue IRPs are sent to be processed // // IN DeviceObject // Device object for our driver // // IN bUseCprStartIoDpc // Flag to indicate that the queue should use queue a DPC for // calling StartIo if StartIo recursion is possible // // Return Value: // none // VOID CprInitializeQueue( IN PCPR_QUEUE Queue, IN PCPR_QUEUE_STARTIO StartIoRoutine, IN PCPR_QUEUE_STATUS_CHANGE StatusChangeRoutine, IN PDEVICE_OBJECT DeviceObject, IN BOOLEAN bUseCprStartIoDpc ) { // must provide StartIo routine ASSERT(StartIoRoutine != NULL); // save off the user info Queue->StartIoRoutine = StartIoRoutine; Queue->StatusChangeRoutine = StatusChangeRoutine; Queue->DeviceObject = DeviceObject; Queue->bUseCprStartIoDpc = bUseCprStartIoDpc; // queues are created in a stalled state // Start device will unstall them Queue->StallCount = 1; // initialize our queue lock KeInitializeSpinLock(&Queue->QueueLock); // initialize our IRP list InitializeListHead(&Queue->IrpQueue); // initialize our CprStartIoDpc if (bUseCprStartIoDpc) { KeInitializeDpc(&Queue->CprStartIoDpc, CprStartIoDpc, Queue); } // initialize stop event KeInitializeEvent(&Queue->StopEvent, NotificationEvent, FALSE); Queue->ErrorStatus = STATUS_SUCCESS; return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprQueueIrp // Inserts an IRP into the queue if the queue is busy, or sends IRP // to StartIo routine if the queue is not busy. // // Arguments: // IN Queue // An instance of our queue structure // // IN Irp // The IRP to add to the queue // // Return Value: // Status // NTSTATUS CprQueueIrp( IN PCPR_QUEUE Queue, IN PIRP Irp, BOOLEAN markPending ) { NTSTATUS status; KIRQL oldIrql; PCPR_DEVICE_EXTENSION deviceExtension; PIO_STACK_LOCATION irpStack; ULONG newBfWriteCount; #ifdef RAISE_IRQL_ALL KIRQL firstOldIrql; #endif deviceExtension = (PCPR_DEVICE_EXTENSION)Queue->DeviceObject->DeviceExtension; CprDebugPrint3(deviceExtension, DBG_QUEUE, DBG_TRACE, __FUNCTION__"++ Queue %p Irp %p markPending %d", Queue, Irp, markPending); #ifdef RAISE_IRQL_ALL KeRaiseIrql(DISPATCH_LEVEL, &firstOldIrql); #endif // grab the queue protection KeAcquireSpinLock(&Queue->QueueLock, &oldIrql); // If the queue has been invalidated, complete the IRP if (Queue->ErrorStatus != STATUS_SUCCESS) { status = Queue->ErrorStatus; // drop the queue protection KeReleaseSpinLock(&Queue->QueueLock, oldIrql); #ifdef RAISE_IRQL_ALL KeLowerIrql(firstOldIrql); #endif Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } // see if the queue is busy if ((Queue->CurrentIrp == NULL) && (Queue->StallCount == 0)) { ULONG txDataCnt; if (deviceExtension->BufferWrites) { txDataCnt = TxCount(&deviceExtension->Uart); } else txDataCnt = 0; // // queue is not busy, send to StartIo routine now // instead of queing it. // irpStack = IoGetCurrentIrpStackLocation(Irp); // the value of newBfWriteCount is valid only if we are buffering // and this is an IRP_MJ_WRITE IRP. // newBfWriteCount = txDataCnt + irpStack->Parameters.Write.Length; #ifdef BACK_TO_4_3_0_0 if ((deviceExtension->BufferWrites == TRUE) && (irpStack->Parameters.Write.Length < PAGE_SIZE) && (irpStack->MajorFunction == IRP_MJ_WRITE) && ((!IsListEmpty(&Queue->IrpQueue)) || (newBfWriteCount > (PAGE_SIZE - txDataCnt)))) #else if ((deviceExtension->BufferWrites == TRUE) && (irpStack->Parameters.Write.Length < PAGE_SIZE) && (irpStack->MajorFunction == IRP_MJ_WRITE) && (!IsListEmpty(&Queue->IrpQueue))) #endif //((!IsListEmpty(&Queue->IrpQueue)) || // (newBfWriteCount > (PAGE_SIZE - txDataCnt)))) { // If we are buffering AND // length of buffer in IRP will fit in the write buffer AND // This is a WRITE IRP AND // Either there is something in the queue OR there is not enough room in the write buffer // Then do Nothing here. Queue IRP below. } else if ((deviceExtension->BufferWrites == TRUE) && (irpStack->MajorFunction == IRP_MJ_FLUSH_BUFFERS) && (!IsListEmpty(&Queue->IrpQueue) || (txDataCnt > 0))) { // If we are buffering AND // This is a FLUSH_BUFFERS IRP AND // Either there is something in the queue OR there is data in the write buffer // Then do Nothing here. Queue IRP below. } else { if ((deviceExtension->BufferWrites == TRUE) && (irpStack->MajorFunction == IRP_MJ_WRITE) && (irpStack->Parameters.Write.Length < PAGE_SIZE)) { // This is Write IRP and we are buffering. // and the length of buffer in IRP will fit in the write buffer // Do not mark pending. // Do not populate Queue->CurrentIrp // We are going to write then complete IRP //DbgPrint(__FUNCTION__" CurrentIrp %p\n", Queue->CurrentIrp); Queue->CurrentIrp = NULL; } else { // mark irp pending since STATUS_PENDING is returned if (markPending) IoMarkIrpPending(Irp); status = STATUS_PENDING; // If BufferWrites is TRUE at this point, // then this is a Flush Buffers IRP. // We want to pend this IRP if there // is data in the write buffer. // set the current IRP Queue->CurrentIrp = Irp; } if ((g_Data.OsMajorVersion == 5) && (g_Data.OsMinorVersion == 0)) { // Windows 2000 // drop the queue protection // DbgPrint("Windows 2000\n"); #if defined(RELEASE_AT_DPC) && !defined(RAISE_IRQL) KeReleaseSpinLockFromDpcLevel(&Queue->QueueLock); #else KeReleaseSpinLock(&Queue->QueueLock, oldIrql); #ifdef RAISE_IRQL // raise our IRQL before calling StartIo KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); #endif #endif } else { // All OSes > Windows 2000 //DbgPrint("> Windows 2000\n"); KeReleaseSpinLockFromDpcLevel(&Queue->QueueLock); } // call the user's StartIo routine Queue->StartIoRoutine(Queue->DeviceObject, Irp); if ((deviceExtension->BufferWrites == TRUE) && (irpStack->MajorFunction == IRP_MJ_WRITE) && (irpStack->Parameters.Write.Length < PAGE_SIZE)) { // This is a Write IRP and we are buffering. status = Irp->IoStatus.Status; } if ((g_Data.OsMajorVersion == 5) && (g_Data.OsMinorVersion == 0)) { // Windows 2000 #if defined(RAISE_IRQL) || defined(RELEASE_AT_DPC) CprDebugPrint1(deviceExtension, DBG_QUEUE, DBG_INFO, __FUNCTION__" Calling KeLowerIrql(%d)", oldIrql); // drop our IRQL back if (oldIrql > 32) { ASSERT(FALSE); DbgPrint(__FUNCTION__" oldIrql seems corrupt, is %d\n", oldIrql); KeLowerIrql(0); } else { KeLowerIrql(oldIrql); } #endif } else { // All OSes > Windows 2000 KeLowerIrql(oldIrql); } #ifdef RAISE_IRQL_ALL KeLowerIrql(firstOldIrql); #endif return status; } } deviceExtension->ReturnReadPhase = 0; // WE ARE QUEUEING HERE!!!!!!! // put our queue pointer into the IRP Irp->Tail.Overlay.DriverContext[0] = Queue; // queue the IRP InsertTailList(&Queue->IrpQueue, &Irp->Tail.Overlay.ListEntry); // insert our queue cancel routine into the IRP IoSetCancelRoutine(Irp, CprQueueCancelRoutine); // // Make sure the IRP was not cancelled before // we inserted our cancel routine // if (Irp->Cancel) { // // If the IRP was cancelled after we put in our cancel routine we // will get back NULL here and we will let the cancel routine handle // the IRP. If NULL is not returned here then we know the IRP was // cancelled before we inserted the queue cancel routine, and we // need to call our cancel routine to handle the IRP. // if (IoSetCancelRoutine(Irp, NULL) != NULL) { RemoveEntryList(&Irp->Tail.Overlay.ListEntry); // drop the queue protection KeReleaseSpinLock(&Queue->QueueLock, oldIrql); // cancel the IRP status = STATUS_CANCELLED; Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); #ifdef RAISE_IRQL_ALL KeLowerIrql(firstOldIrql); #endif return status; } } // mark irp pending since STATUS_PENDING is returned if (markPending) IoMarkIrpPending(Irp); status = STATUS_PENDING; // drop the queue protection KeReleaseSpinLock(&Queue->QueueLock, oldIrql); if (Queue->StallCount != 0) { // io is stalled. Make sure it is not because of // device is powered down. if (deviceExtension->DevicePowerState > PowerDeviceD0) { POWER_STATE powerState; powerState.DeviceState = PowerDeviceD0; status = PoRequestPowerIrp( deviceExtension->PhysicalDeviceObject, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL ); } } #ifdef RAISE_IRQL_ALL KeLowerIrql(firstOldIrql); #endif CprDebugPrint3(deviceExtension, DBG_QUEUE, DBG_TRACE, __FUNCTION__"-- Queue %p Irp %p status %08X", Queue, Irp, status); return status; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprStartNext // Pulls the next available IRP from the queue and sends it to // StartIo for processing. // // Arguments: // IN Queue // An instance of our queue structure // // Return Value: // none // VOID CprStartNext( IN PCPR_QUEUE Queue ) { KIRQL oldIrql; PLIST_ENTRY entry; #ifdef RAISE_IRQL_ALL KIRQL firstOldIrql; KeRaiseIrql(DISPATCH_LEVEL, &firstOldIrql); #endif // grab the queue protection KeAcquireSpinLock(&Queue->QueueLock, &oldIrql); // set the current IRP pointer to NULL Queue->CurrentIrp = NULL; Queue->StatusChangeRoutine(Queue->DeviceObject, CURRENT_IRP_STATUS_REMOVED); // check if there are entries in the queue if (IsListEmpty(&Queue->IrpQueue) || (Queue->StallCount > 0) || (Queue->ErrorStatus != STATUS_SUCCESS)) { // set event that queue is stalled KeSetEvent(&Queue->StopEvent, IO_NO_INCREMENT, FALSE); KeReleaseSpinLock(&Queue->QueueLock, oldIrql); #ifdef RAISE_IRQL_ALL KeLowerIrql(firstOldIrql); #endif return; } // get a new IRP from the queue for (entry = Queue->IrpQueue.Flink; entry != &Queue->IrpQueue; entry = entry->Flink) { // get the IRP from the list entry Queue->CurrentIrp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); Queue->StatusChangeRoutine(Queue->DeviceObject, CURRENT_IRP_STATUS_QUEUED); ASSERT(Queue->CurrentIrp->Type == IO_TYPE_IRP); // if we found an IRP, pull the queue cancel routine out of it // See if the IRP is canceled if (IoSetCancelRoutine(Queue->CurrentIrp, NULL) == NULL) { // cancel routine already has a hold on this IRP, // just go on to the next one Queue->CurrentIrp = NULL; } else { // Found a usable IRP, pull the entry out of the list RemoveEntryList(entry); break; } } if (Queue->CurrentIrp == NULL) { // set event that queue is stalled KeSetEvent(&Queue->StopEvent, IO_NO_INCREMENT, FALSE); KeReleaseSpinLock(&Queue->QueueLock, oldIrql); #ifdef RAISE_IRQL_ALL KeLowerIrql(firstOldIrql); #endif return; } // found an IRP call StartIo // Determine if we need to queue a DPC or not. // We only use the DPC if the user specified to protect // against StartIo recursion (bUseCprStartIoDpc), and the // queue has multiple entries. If there are not multiple // entries in the queue we won't recurse anyway. if (Queue->bUseCprStartIoDpc && !IsListEmpty(&Queue->IrpQueue)) { // drop the queue protection KeReleaseSpinLock(&Queue->QueueLock, oldIrql); // queue our StartIo DPC to prevent StartIo recursion KeInsertQueueDpc(&Queue->CprStartIoDpc, NULL, NULL); } else { // drop the queue protection // raise our IRQL before calling StartIo // KeReleaseSpinLockFromDpcLevel(&Queue->QueueLock); if ((g_Data.OsMajorVersion == 5) && (g_Data.OsMinorVersion == 0)) { // Windows 2000 // drop the queue protection #if defined(RELEASE_AT_DPC) && !defined(RAISE_IRQL) KeReleaseSpinLockFromDpcLevel(&Queue->QueueLock); #else KeReleaseSpinLock(&Queue->QueueLock, oldIrql); #ifdef RAISE_IRQL KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); #endif #endif } else { KeReleaseSpinLockFromDpcLevel(&Queue->QueueLock); } // call the user's StartIo routine Queue->StartIoRoutine(Queue->DeviceObject, Queue->CurrentIrp); if ((g_Data.OsMajorVersion == 5) && (g_Data.OsMinorVersion == 0)) { #if defined(RAISE_IRQL) || defined(RELEASE_AT_DPC) CprDebugPrint1(NULL, DBG_QUEUE, DBG_INFO, __FUNCTION__" Calling KeLowerIrql(%d)", oldIrql); // drop our IRQL back if (oldIrql > 32) { ASSERT(FALSE); DbgPrint(__FUNCTION__" oldIrql seems corrupt, is %d\n", oldIrql); KeLowerIrql(0); } else { KeLowerIrql(oldIrql); } #endif } else { KeLowerIrql(oldIrql); } } #ifdef RAISE_IRQL_ALL KeLowerIrql(firstOldIrql); #endif return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprFlushQueue // Cancels all IRPs in the queue, or all IRPs in the queue related to a // particular open handle. // // Arguments: // IN Queue // An instance of our queue structure // // IN FileObject // If NULL all IRPs in queue are cancelled, if non-NULL then all // IRPs related to this file object are cancelled. // // Return Value: // none // VOID CprFlushQueue( IN PCPR_QUEUE Queue, IN PFILE_OBJECT FileObject ) { PLIST_ENTRY entry; PLIST_ENTRY nextEntry; PIRP irp; PIO_STACK_LOCATION irpStack; KIRQL oldIrql; LIST_ENTRY cancelList; // initialize our cancel list InitializeListHead(&cancelList); // grab the queue protection KeAcquireSpinLock(&Queue->QueueLock, &oldIrql); // Look at the first entry in the queue entry = Queue->IrpQueue.Flink; while (entry != &Queue->IrpQueue) { // get the next list entry nextEntry = entry->Flink; // Get the IRP from the entry irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); ASSERT(irp->Type == IO_TYPE_IRP); // get the current IRP stack location from the IRP irpStack = IoGetCurrentIrpStackLocation(irp); // Determine if we need to pull out of queue if (FileObject != NULL) { if (irpStack->FileObject != FileObject) { // go to the next entry entry = nextEntry; // We are not flushing this IRP continue; } } // If it is a flush buffers IRP, do not cancel if (irpStack && (irpStack->MajorFunction != IRP_MJ_FLUSH_BUFFERS)) { // Otherwise... Attempt to cancel the IRP if (IoSetCancelRoutine(irp, NULL) == NULL) { // go to the next entry entry = nextEntry; // cancel routine already has this IRP, // just go on continue; } } // pull the IRP from the queue RemoveEntryList(entry); InsertTailList(&cancelList, entry); // go to the next entry entry = nextEntry; } // drop the queue protection KeReleaseSpinLock(&Queue->QueueLock, oldIrql); // Now clear out our cancel list while (!IsListEmpty(&cancelList)) { // Get the first entry on the list entry = RemoveHeadList(&cancelList); // Get the IRP for that entry irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); // Cancel the IRP irpStack = IoGetCurrentIrpStackLocation(irp); if (irpStack->MajorFunction == IRP_MJ_FLUSH_BUFFERS) irp->IoStatus.Status = STATUS_SUCCESS; else irp->IoStatus.Status = STATUS_CANCELLED; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); } return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprInvalidateQueue // Stops queue from receiving anymore IRPs, all IRPs are completed upon // receipt // // Arguments: // IN Queue // An instance of our queue structure // // Return Value: // none // VOID CprInvalidateQueue( IN PCPR_QUEUE Queue, IN NTSTATUS ErrorStatus ) { // indicate the queue is shutdown Queue->ErrorStatus = TRUE; // flush all requests from the queue CprFlushQueue(Queue, NULL); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprPauseQueue // Stops queue from sending anymore IRPs to StartIo to be processed // // Arguments: // IN Queue // An instance of our queue structure // // Return Value: // none // VOID CprPauseQueue( IN PCPR_QUEUE Queue ) { KIRQL oldIrql; BOOLEAN bBusy; KeAcquireSpinLock(&Queue->QueueLock, &oldIrql); // indicate the queue is paused InterlockedIncrement(&Queue->StallCount); bBusy = Queue->CurrentIrp != NULL; if (bBusy) { // reset stop event KeClearEvent(&Queue->StopEvent); } KeReleaseSpinLock(&Queue->QueueLock, oldIrql); if (bBusy) { KeWaitForSingleObject(&Queue->StopEvent, Executive, KernelMode, FALSE, NULL); } return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprRestartQueue // Restarts IRP processing for a queue that was paused // using CprPauseQueue() // or invalidated using CprInvalidateQueue() // // Arguments: // IN Queue // An instance of our queue structure // // Return Value: // none // VOID CprRestartQueue( IN PCPR_QUEUE Queue ) { // if the queue is stalled or invalid, restart it if (InterlockedDecrement(&Queue->StallCount) == 0) { CprStartNext(Queue); } return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprStartIoDpc // DPC routine used to call StartIo, if bUseCprStartIoDpc is specified and // StartIo recursion is possible. // // Arguments: // IN Dpc // DPC object // // IN Context // An instance of our queue structure // // IN Unused1 // Not used // // IN Unused2 // Not used // // Return Value: // none // VOID CprStartIoDpc( IN PKDPC Dpc, IN PVOID Context, IN PVOID Unused1, IN PVOID Unused2 ) { PCPR_QUEUE queue; queue = (PCPR_QUEUE)Context; // call the user's StartIo routine queue->StartIoRoutine(queue->DeviceObject, queue->CurrentIrp); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprQueueCancelRoutine // Cancel routine used for queue IRPs while in the queue // // Arguments: // IN DeviceObject // Device object for our device // // IN Irp // IRP to be cancelled // // Return Value: // none // VOID CprQueueCancelRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { KIRQL oldIrql; PCPR_QUEUE queue; // release the system cancel spinlock //oldIrql = Irp->CancelIrql; IoReleaseCancelSpinLock(Irp->CancelIrql); // get our queue from the IRP queue = (PCPR_QUEUE)Irp->Tail.Overlay.DriverContext[0]; ASSERT(queue != NULL); // grab the queue protection //ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); KeAcquireSpinLock(&queue->QueueLock, &oldIrql); // remove our IRP from the queue RemoveEntryList(&Irp->Tail.Overlay.ListEntry); // drop the queue protection KeReleaseSpinLock(&queue->QueueLock, oldIrql); // cancel the IRP Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // Cancel Safe IRP List /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // CprInitializeList // Sets up a cancel safe IRP list. // // Arguments: // IN List // An instance of our list structure // // IN DeviceObject // Device object for our driver // // Return Value: // none // VOID CprInitializeList( IN PCPR_LIST List, IN PDEVICE_OBJECT DeviceObject ) { // save off the user info List->DeviceObject = DeviceObject; // initialize our queue lock KeInitializeSpinLock(&List->ListLock); // initialize our IRP list InitializeListHead(&List->IrpList); List->ErrorStatus = STATUS_SUCCESS; return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprInsertHead // Puts Irp entry at head of list // // Arguments: // IN List // An instance of our list structure // // IN Irp // IRP to be put in list // // Return Value: // Status // NTSTATUS CprInsertHead( IN PCPR_LIST List, IN PIRP Irp ) { NTSTATUS status; KIRQL oldIrql; PCPR_DEVICE_EXTENSION deviceExtension; deviceExtension = (PCPR_DEVICE_EXTENSION)List->DeviceObject->DeviceExtension; // Grab the list protection KeAcquireSpinLock(&List->ListLock, &oldIrql); if (List->ErrorStatus != STATUS_SUCCESS) { status = List->ErrorStatus; // drop the queue protection KeReleaseSpinLock(&List->ListLock, oldIrql); Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } // put our list pointer into the IRP Irp->Tail.Overlay.DriverContext[0] = List; // put the entry at the head of the list InsertHeadList(&List->IrpList, &Irp->Tail.Overlay.ListEntry); // set cancel routine IoSetCancelRoutine(Irp, CprListCancelRoutine); // Make sure the IRP was not cancelled before // we inserted our cancel routine if (Irp->Cancel) { // If the IRP was cancelled after we put in our cancel routine we // will get back NULL here and we will let the cancel routine handle // the IRP. If NULL is not returned here then we know the IRP was // cancelled before we inserted the cancel routine, and we // need to cancel the IRP if (IoSetCancelRoutine(Irp, NULL) != NULL) { RemoveEntryList(&Irp->Tail.Overlay.ListEntry); // drop the list protection KeReleaseSpinLock(&List->ListLock, oldIrql); // cancel the IRP status = STATUS_CANCELLED; Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } } // mark irp pending since STATUS_PENDING is returned IoMarkIrpPending(Irp); status = STATUS_PENDING; // drop the list protection KeReleaseSpinLock(&List->ListLock, oldIrql); // Make sure that device is powered up if (deviceExtension->DevicePowerState > PowerDeviceD0) { POWER_STATE powerState; powerState.DeviceState = PowerDeviceD0; status = PoRequestPowerIrp( deviceExtension->PhysicalDeviceObject, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL ); } return status; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprInsertTail // Puts Irp entry at end of list // // Arguments: // IN List // An instance of our list structure // // IN Irp // IRP to be put in list // // Return Value: // Status // NTSTATUS CprInsertTail( IN PCPR_LIST List, IN PIRP Irp ) { NTSTATUS status; KIRQL oldIrql; PCPR_DEVICE_EXTENSION deviceExtension; deviceExtension = (PCPR_DEVICE_EXTENSION)List->DeviceObject->DeviceExtension; // Grab the list protection KeAcquireSpinLock(&List->ListLock, &oldIrql); if (List->ErrorStatus != STATUS_SUCCESS) { status = List->ErrorStatus; // drop the queue protection KeReleaseSpinLock(&List->ListLock, oldIrql); Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } // put our list pointer into the IRP Irp->Tail.Overlay.DriverContext[0] = (PVOID)List; // put the entry at the head of the list InsertHeadList(&List->IrpList, &Irp->Tail.Overlay.ListEntry); // set cancel routine IoSetCancelRoutine(Irp, CprListCancelRoutine); // Make sure the IRP was not cancelled before // we inserted our cancel routine if (Irp->Cancel) { // If the IRP was cancelled after we put in our cancel routine we // will get back NULL here and we will let the cancel routine handle // the IRP. If NULL is not returned here then we know the IRP was // cancelled before we inserted the cancel routine, and we // need to cancel the IRP if (IoSetCancelRoutine(Irp, NULL) != NULL) { RemoveEntryList(&Irp->Tail.Overlay.ListEntry); // drop the list protection KeReleaseSpinLock(&List->ListLock, oldIrql); // cancel the IRP status = STATUS_CANCELLED; Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } } // mark irp pending since STATUS_PENDING is returned IoMarkIrpPending(Irp); status = STATUS_PENDING; // drop the list protection KeReleaseSpinLock(&List->ListLock, oldIrql); // Make sure that device is powered up if (deviceExtension->DevicePowerState > PowerDeviceD0) { POWER_STATE powerState; powerState.DeviceState = PowerDeviceD0; status = PoRequestPowerIrp( deviceExtension->PhysicalDeviceObject, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL ); } return status; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprRemoveHead // Removes an Irp entry from the head of list // // Arguments: // IN List // An instance of our list structure // // Return Value: // IRP removed from head of list // PIRP CprRemoveHead( IN PCPR_LIST List ) { PLIST_ENTRY entry; PIRP irp; KIRQL oldIrql; // Grab the list protection KeAcquireSpinLock(&List->ListLock, &oldIrql); // Make sure there are entries in the list if (IsListEmpty(&List->IrpList)) { // drop the list protection KeReleaseSpinLock(&List->ListLock, oldIrql); return NULL; } // get a new IRP from the queue for (entry = List->IrpList.Flink; entry != &List->IrpList; entry = entry->Flink) { // get our IRP from the entry irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); ASSERT(irp->Type == IO_TYPE_IRP); // if we found an IRP, pull the queue cancel routine out of it // See if the IRP is canceled if (IoSetCancelRoutine(irp, NULL) == NULL) { // cancel routine already has a hold on this IRP, // just go on to the next one irp = NULL; } else { // Found a usable IRP, pull the entry out of the list RemoveEntryList(entry); break; } } // drop the list protection KeReleaseSpinLock(&List->ListLock, oldIrql); return irp; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprRemoveTail // Removes an Irp entry from the tail of list // // Arguments: // IN List // An instance of our list structure // // Return Value: // IRP removed from end of list // PIRP CprRemoveTail( IN PCPR_LIST List ) { PLIST_ENTRY entry; PIRP irp; KIRQL oldIrql; // Grab the list protection KeAcquireSpinLock(&List->ListLock, &oldIrql); // Make sure there are entries in the list if (IsListEmpty(&List->IrpList)) { // drop the list protection KeReleaseSpinLock(&List->ListLock, oldIrql); return NULL; } // get a new IRP from the queue for (entry = List->IrpList.Blink; entry != &List->IrpList; entry = entry->Blink) { // get our IRP from the entry irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); ASSERT(irp->Type == IO_TYPE_IRP); // if we found an IRP, pull the queue cancel routine out of it // See if the IRP is canceled if (IoSetCancelRoutine(irp, NULL) == NULL) { // cancel routine already has a hold on this IRP, // just go on to the next one irp = NULL; } else { // Found a usable IRP, pull the entry out of the list RemoveEntryList(entry); break; } } // drop the list protection KeReleaseSpinLock(&List->ListLock, oldIrql); return irp; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprFlushList // Cancels all IRPs in the list, or all IRPs in the list related to a // particular open handle. // // Arguments: // IN List // An instance of our list structure // // IN FileObject // If NULL all IRPs in list are cancelled, if non-NULL then all // IRPs related to this file object are cancelled. // // Return Value: // none // VOID CprFlushList( IN PCPR_LIST List, IN PFILE_OBJECT FileObject ) { PLIST_ENTRY entry; PLIST_ENTRY nextEntry; PIRP irp; PIO_STACK_LOCATION irpStack; KIRQL oldIrql; LIST_ENTRY cancelList; // initialize our cancel list InitializeListHead(&cancelList); // grab the list protection KeAcquireSpinLock(&List->ListLock, &oldIrql); // Look at the first entry in the list entry = List->IrpList.Flink; while (entry != &List->IrpList) { // get the next list entry nextEntry = entry->Flink; // Get the IRP from the entry irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); ASSERT(irp->Type == IO_TYPE_IRP); // Determine if we need to pull out of list if (FileObject != NULL) { // get the current IRP stack location from the IRP irpStack = IoGetCurrentIrpStackLocation(irp); if (irpStack->FileObject != FileObject) { // go to the next entry entry = nextEntry; // We are not flushing this IRP continue; } } // Attempt to cancel the IRP if (IoSetCancelRoutine(irp, NULL) == NULL) { // go to the next entry entry = nextEntry; // cancel routine already has this IRP, // just go on continue; } // pull the IRP from the list RemoveEntryList(entry); InsertTailList(&cancelList, entry); // go to the next entry entry = nextEntry; } // drop the list protection KeReleaseSpinLock(&List->ListLock, oldIrql); // Now clear out our cancel list while (!IsListEmpty(&cancelList)) { // Get the first entry on the list entry = RemoveHeadList(&cancelList); // Get the IRP for that entry irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); // Cancel the IRP irp->IoStatus.Status = STATUS_CANCELLED; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); } return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprInvalidateList // Stops list from receiving anymore IRPs, all IRPs are completed upon // receipt // // Arguments: // IN List // An instance of our list structure // // Return Value: // none // VOID CprInvalidateList( IN PCPR_LIST List, IN NTSTATUS ErrorStatus ) { // indicate the list is shutdown List->ErrorStatus = ErrorStatus; // flush all requests from the list CprFlushList(List, NULL); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprListCancelRoutine // Cancel routine used for our cancel safe IRP list // // Arguments: // IN DeviceObject // Device object for our device // // IN Irp // IRP to be cancelled // // Return Value: // none // VOID CprListCancelRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { KIRQL oldIrql; PCPR_LIST list; //oldIrql = Irp->CancelIrql; // release the system cancel spinlock IoReleaseCancelSpinLock(Irp->CancelIrql); // get our list context from the IRP list = (PCPR_LIST)Irp->Tail.Overlay.DriverContext[0]; // grab the list protection //ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); KeAcquireSpinLock(&list->ListLock, &oldIrql); // remove our IRP from the list RemoveEntryList(&Irp->Tail.Overlay.ListEntry); // drop the list protection KeReleaseSpinLock(&list->ListLock, oldIrql); // cancel the IRP Irp->IoStatus.Status = STATUS_CANCELLED; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CPR_IO_LOCK /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // CprInitializeIoLock // Initializes io lock structure. // // Arguments: // IN IoLock // io lock to initialize // // IN DeviceObject // device object // // Return Value: // none // VOID CprInitializeIoLock( IN PCPR_IO_LOCK IoLock, IN PDEVICE_OBJECT DeviceObject ) { IoLock->DeviceObject = DeviceObject; KeInitializeEvent(&IoLock->StallCompleteEvent, NotificationEvent, FALSE); InitializeListHead(&IoLock->StallIrpList); KeInitializeSpinLock(&IoLock->IoLock); IoLock->StallCount = 1; IoLock->ActiveIrpCount = 0; IoLock->ErrorStatus = STATUS_SUCCESS; IoLock->CurrentIrp = NULL; return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprCheckIoLock // checks if IRP is allowed to proceed. // // Arguments: // IN IoLock // io lock for our device // // IN Irp // new IRP // // Return Value: // Status // NTSTATUS CprCheckIoLock( IN PCPR_IO_LOCK IoLock, IN PIRP Irp ) { NTSTATUS status; KIRQL oldIrql; PCPR_DEVICE_EXTENSION deviceExtension; deviceExtension = (PCPR_DEVICE_EXTENSION)IoLock->DeviceObject->DeviceExtension; KeAcquireSpinLock(&IoLock->IoLock, &oldIrql); // check if device has been removed if (IoLock->ErrorStatus != STATUS_SUCCESS) { status = IoLock->ErrorStatus; KeReleaseSpinLock(&IoLock->IoLock, oldIrql); Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } // check if io is stalled if (IoLock->StallCount > 0) { // check if Irp is not the one sent by CprUnlockIo if (IoLock->CurrentIrp != Irp) { // save device extension into the IRP Irp->Tail.Overlay.DriverContext[0] = IoLock; // stall the IRP InsertTailList(&IoLock->StallIrpList, &Irp->Tail.Overlay.ListEntry); // insert our queue cancel routine into the IRP IoSetCancelRoutine(Irp, CprPendingIoCancelRoutine); // see if IRP was canceled if (Irp->Cancel && (IoSetCancelRoutine(Irp, NULL) != NULL)) { // IRP was canceled RemoveEntryList(&Irp->Tail.Overlay.ListEntry); // drop the lock KeReleaseSpinLock(&IoLock->IoLock, oldIrql); // cancel the IRP status = STATUS_CANCELLED; Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return status; } // mark irp pending since STATUS_PENDING is returned IoMarkIrpPending(Irp); status = STATUS_PENDING; // drop the lock KeReleaseSpinLock(&IoLock->IoLock, oldIrql); // io is stalled. Make sure it is not because of // device is powered down. if (deviceExtension->DevicePowerState > PowerDeviceD0) { POWER_STATE powerState; powerState.DeviceState = PowerDeviceD0; status = PoRequestPowerIrp( deviceExtension->PhysicalDeviceObject, IRP_MN_SET_POWER, powerState, NULL, NULL, NULL ); } return status; } else { IoLock->CurrentIrp = NULL; } } // increment active io count ++IoLock->ActiveIrpCount; // drop the lock KeReleaseSpinLock(&IoLock->IoLock, oldIrql); status = STATUS_SUCCESS; return status; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprPendingIoCancelRoutine // Cancel routine for stalled IRPs. // // Arguments: // IN DeviceObject // our device object // // IN Irp // IRP to be canceled // // Return Value: // None // VOID CprPendingIoCancelRoutine( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { KIRQL oldIrql; PCPR_IO_LOCK ioLock; // release the system cancel spinlock //oldIrql = Irp->CancelIrql; IoReleaseCancelSpinLock(Irp->CancelIrql); // get our queue from the IRP ioLock = (PCPR_IO_LOCK)Irp->Tail.Overlay.DriverContext[0]; // grab the queue protection //ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); KeAcquireSpinLock(&ioLock->IoLock, &oldIrql); // remove our IRP from the queue RemoveEntryList(&Irp->Tail.Overlay.ListEntry); // drop the queue protection KeReleaseSpinLock(&ioLock->IoLock, oldIrql); // cancel the IRP Irp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest(Irp, IO_NO_INCREMENT); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprIncrementIoCount // increment active io count. // // Arguments: // IN IoLock // io lock for our device // // Return Value: // Status // NTSTATUS CprIncrementIoCount( IN PCPR_IO_LOCK IoLock ) { KIRQL oldIrql; NTSTATUS status; KeAcquireSpinLock(&IoLock->IoLock, &oldIrql); if (IoLock->ErrorStatus != STATUS_SUCCESS) { status = IoLock->ErrorStatus; } else if (IoLock->StallCount > 0) { status = STATUS_DEVICE_BUSY; } else { ++IoLock->ActiveIrpCount; status = STATUS_SUCCESS; } KeReleaseSpinLock(&IoLock->IoLock, oldIrql); return status; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprDecrementIoCount // decrements active io count. // // Arguments: // IN IoLock // io lock for our device // // Return Value: // None // VOID CprDecrementIoCount( IN PCPR_IO_LOCK IoLock ) { KIRQL oldIrql; KeAcquireSpinLock(&IoLock->IoLock, &oldIrql); if (--IoLock->ActiveIrpCount == 0) { KeSetEvent(&IoLock->StallCompleteEvent, IO_NO_INCREMENT, FALSE); } KeReleaseSpinLock(&IoLock->IoLock, oldIrql); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprLockIo // Locks new IRP processing. // // Arguments: // IN IoLock // io lock for our device // // Return Value: // None // VOID CprLockIo( IN PCPR_IO_LOCK IoLock ) { KIRQL oldIrql; KeAcquireSpinLock(&IoLock->IoLock, &oldIrql); // increment stall count ++IoLock->StallCount; KeReleaseSpinLock(&IoLock->IoLock, oldIrql); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprWaitForStopIo // Waits for all active io to complete. // // Arguments: // IN IoLock // io lock for our device // // Return Value: // None // VOID CprWaitForStopIo( IN PCPR_IO_LOCK IoLock ) { KIRQL oldIrql; BOOLEAN bWait; KeAcquireSpinLock(&IoLock->IoLock, &oldIrql); // clear stall completed event KeClearEvent(&IoLock->StallCompleteEvent); // check if we need to wait for some IRPs to finish bWait = (IoLock->ActiveIrpCount != 0); KeReleaseSpinLock(&IoLock->IoLock, oldIrql); if (bWait) { // wait for outstanding IRPs to complete KeWaitForSingleObject(&IoLock->StallCompleteEvent, Executive, KernelMode, FALSE, NULL); } return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprUnlockIo // Locks new IRP processing. // // Arguments: // IN IoLock // io lock for our device // // Return Value: // None // VOID CprUnlockIo( IN PCPR_IO_LOCK IoLock ) { PLIST_ENTRY entry; PIRP irp; KIRQL oldIrql; PIO_STACK_LOCATION irpStack; // Grab the list protection KeAcquireSpinLock(&IoLock->IoLock, &oldIrql); // if StallCount is 1 then we need to flush all pending IRPs while ((IoLock->StallCount == 1) && (!IsListEmpty(&IoLock->StallIrpList))) { entry = RemoveHeadList(&IoLock->StallIrpList); irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); if (IoSetCancelRoutine(irp, NULL) == NULL) { // irp was canceled // let cancel routine deal with it. // we need to initialize IRP's list entry, since // our cancel routine expects the IRP to be in a list InitializeListHead(&irp->Tail.Overlay.ListEntry); } else { IoLock->CurrentIrp = irp; KeReleaseSpinLock(&IoLock->IoLock, oldIrql); // call DriverDispatch irpStack = IoGetCurrentIrpStackLocation(irp); IoLock->DeviceObject->DriverObject->MajorFunction[irpStack->MajorFunction]( IoLock->DeviceObject, irp ); KeAcquireSpinLock(&IoLock->IoLock, &oldIrql); } } --IoLock->StallCount; KeReleaseSpinLock(&IoLock->IoLock, oldIrql); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprFlushPendingIo // Cancels stalled IRPs for a particular file object. // // Arguments: // IN IoLock // io lock for our device // // IN FileObject // file object for about to be closed handle // // Return Value: // None // VOID CprFlushPendingIo( IN PCPR_IO_LOCK IoLock, IN PFILE_OBJECT FileObject ) { PLIST_ENTRY entry; PLIST_ENTRY nextEntry; PIRP irp; PIO_STACK_LOCATION irpStack; KIRQL oldIrql; LIST_ENTRY cancelList; CprDebugPrint(NULL, DBG_CREATECLOSE | DBG_IO, DBG_TRACE, __FUNCTION__"++"); // initialize our cancel list InitializeListHead(&cancelList); // grab the list protection KeAcquireSpinLock(&IoLock->IoLock, &oldIrql); // Look at the first entry in the list entry = IoLock->StallIrpList.Flink; while (entry != &IoLock->StallIrpList) { // get the next list entry nextEntry = entry->Flink; // Get the IRP from the entry irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); ASSERT(irp->Type == IO_TYPE_IRP); // Determine if we need to pull out of list if (FileObject != NULL) { // get the current IRP stack location from the IRP irpStack = IoGetCurrentIrpStackLocation(irp); if (irpStack->FileObject != FileObject) { // go to the next entry entry = nextEntry; // We are not flushing this IRP continue; } } // Attempt to cancel the IRP if (IoSetCancelRoutine(irp, NULL) == NULL) { // go to the next entry entry = nextEntry; // cancel routine already has this IRP, // just go on continue; } // pull the IRP from the list RemoveEntryList(entry); InsertTailList(&cancelList, entry); // go to the next entry entry = nextEntry; } // drop the list protection KeReleaseSpinLock(&IoLock->IoLock, oldIrql); // Now clear out our cancel list while (!IsListEmpty(&cancelList)) { // Get the first entry on the list entry = RemoveHeadList(&cancelList); // Get the IRP for that entry irp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); // Cancel the IRP irp->IoStatus.Status = STATUS_CANCELLED; irp->IoStatus.Information = 0; IoCompleteRequest(irp, IO_NO_INCREMENT); } CprDebugPrint(NULL, DBG_CREATECLOSE | DBG_IO, DBG_TRACE, __FUNCTION__"--"); return; } VOID CprInvalidateIo( IN PCPR_IO_LOCK IoLock, IN NTSTATUS ErrorStatus ) { // indicate the list is shutdown IoLock->ErrorStatus = ErrorStatus; // flush all requests from the list CprFlushPendingIo(IoLock, NULL); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // Event Log Queue /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// // CprInitializeEventLogQueue // Sets up a driver managed Event Log queue. // // Arguments: // IN EventLogQueue // An instance of our EventLogQueue structure // // Return Value: // none // VOID CprInitializeEventLogQueue( IN PCPR_EVENT_LOG_QUEUE EventLogQueue ) { // initialize the Event Log queue lock KeInitializeSpinLock(&EventLogQueue->QueueLock); // initialize the Event Log list InitializeListHead(&EventLogQueue->EventLogHead); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprQueueEventLog // Inserts an Event Log into the queue. // // Arguments: // IN pSession // the current session // // IN EventLog // The Event Log to add to the queue // // Return Value: // Status // NTSTATUS CprQueueEventLog( IN PCPR_SESSION pSession, IN PCPR_EVENT_LOG_KERNEL pEventLog ) { NTSTATUS status = STATUS_SUCCESS; KIRQL oldIrql; PCPR_EVENT_LOG_QUEUE EventLogQueue = &pSession->EventLogQueue; // grab the queue protection KeAcquireSpinLock(&EventLogQueue->QueueLock, &oldIrql); // queue the EventLog InsertTailList(&EventLogQueue->EventLogHead, &pEventLog->ListEntry); // drop the queue protection // raise our IRQL before calling StartIo KeReleaseSpinLock(&EventLogQueue->QueueLock, oldIrql); // KeReleaseSpinLockFromDpcLevel(&EventLogQueue->QueueLock); // drop our IRQL back // KeLowerIrql(oldIrql); // DAG TODO: may want to create own Spin Lock. //IoAcquireCancelSpinLock (&oldIrql); // Data will be given if possible // GiveEventLogDataToApp should be called at DPC level. // this should ensure that no other processes will // interrupt the running of this function. GiveEventLogDataToApp(pSession); //IoReleaseCancelSpinLock (oldIrql); return status; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprDeQueueEventLog // Removes the first Event Log from the queue // // Arguments: // IN EventLogQueue // An instance of our queue structure // // Return Value: // PCPR_EVENT_LOG_KERNEL // PCPR_EVENT_LOG_KERNEL CprDeQueueEventLog( IN PCPR_EVENT_LOG_QUEUE EventLogQueue ) { PLIST_ENTRY entry; PCPR_EVENT_LOG_KERNEL pEventLog = NULL; KIRQL oldIrql; // grab the queue protection KeAcquireSpinLock(&EventLogQueue->QueueLock, &oldIrql); if (!IsListEmpty(&EventLogQueue->EventLogHead)) { // Remove the oldest Event Log entry = RemoveHeadList(&EventLogQueue->EventLogHead); // Get the IRP from the entry pEventLog = CONTAINING_RECORD(entry, CPR_EVENT_LOG_KERNEL, ListEntry); } // drop the queue protection KeReleaseSpinLock(&EventLogQueue->QueueLock, oldIrql); return pEventLog; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprFindAndRemoveEventLog // Removes the first Event Log from the queue // // Arguments: // IN EventLogQueue // An instance of our queue structure // // Return Value: // PCPR_EVENT_LOG_KERNEL // PCPR_EVENT_LOG_KERNEL CprFindAndRemoveEventLog( IN PCPR_EVENT_LOG_QUEUE EventLogQueue, ULONG sessionId ) { PLIST_ENTRY entry; PCPR_EVENT_LOG_KERNEL pEventLogKernel; PCPR_EVENT_LOG_KERNEL pFoundEventLog = NULL; KIRQL oldIrql; // grab the queue protection KeAcquireSpinLock(&EventLogQueue->QueueLock, &oldIrql); for (entry = EventLogQueue->EventLogHead.Flink; entry != &EventLogQueue->EventLogHead; entry = entry->Flink) { // Get the Event Log from the entry pEventLogKernel = CONTAINING_RECORD(entry, CPR_EVENT_LOG_KERNEL, ListEntry); if (pEventLogKernel->eventLog.sessionId == sessionId) { // We found it. pFoundEventLog = pEventLogKernel; RemoveEntryList(entry); break; } } KeReleaseSpinLock(&EventLogQueue->QueueLock, oldIrql); return pFoundEventLog; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprFlushEventLogQueue // Cancels all IRPs in the queue, or all IRPs in the queue related to a // particular open handle. // // Arguments: // IN EventLogQueue // An instance of our queue structure // // Return Value: // none // VOID CprFlushEventLogQueue( IN PCPR_EVENT_LOG_QUEUE EventLogQueue ) { PLIST_ENTRY entry; PCPR_EVENT_LOG_KERNEL pEventLog; KIRQL oldIrql; // grab the queue protection KeAcquireSpinLock(&EventLogQueue->QueueLock, &oldIrql); // Look at the first entry in the queue while (!IsListEmpty(&EventLogQueue->EventLogHead)) { entry = RemoveHeadList(&EventLogQueue->EventLogHead); // Get the IRP from the entry pEventLog = CONTAINING_RECORD(entry, CPR_EVENT_LOG_KERNEL, ListEntry); // Put the Event Log back on the lookaside list CprReleaseEventLog(pEventLog); } // drop the queue protection KeReleaseSpinLock(&EventLogQueue->QueueLock, oldIrql); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprInvalidateEventLogQueue // Stops queue from receiving anymore IRPs, all IRPs are completed upon // receipt // // Arguments: // IN EventLogQueue // An instance of our queue structure // // Return Value: // none // VOID CprInvalidateEventLogQueue( IN PCPR_EVENT_LOG_QUEUE EventLogQueue ) { // indicate the queue is shutdown EventLogQueue->ErrorStatus = TRUE; // flush all requests from the queue CprFlushEventLogQueue(EventLogQueue); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprQueueEventReadIrp // Inserts an Event Log into the queue. // // Arguments: // IN EventReadIrpQueue // An instance of our queue structure // // IN pIrp // The Irp to add to the queue // // Return Value: // Status // NTSTATUS CprQueueEventReadIrp( IN PCPR_EVENT_READ_IRP_QUEUE EventReadIrpQueue, IN PIRP pIrp ) { NTSTATUS status = STATUS_SUCCESS; KIRQL oldIrql; // grab the queue protection KeAcquireSpinLock(&EventReadIrpQueue->QueueLock, &oldIrql); // queue the EventLog InsertTailList(&EventReadIrpQueue->EventReadIrpHead, &pIrp->Tail.Overlay.ListEntry); // drop the queue protection // raise our IRQL before calling StartIo // KeReleaseSpinLockFromDpcLevel(&EventReadIrpQueue->QueueLock); KeReleaseSpinLock(&EventReadIrpQueue->QueueLock, oldIrql); // drop our IRQL back // KeLowerIrql(oldIrql); return status; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprDeQueueEventReadIrp // Removes the Event Read Irp with the matching sessionId from the queue // // Arguments: // IN EventReadIrpQueue // An instance of our queue structure // // Return Value: // PIRP // PIRP CprDeQueueEventReadIrp( IN PCPR_EVENT_READ_IRP_QUEUE EventReadIrpQueue ) { PLIST_ENTRY entry; PIRP pIrp = NULL; KIRQL oldIrql; // grab the queue protection KeAcquireSpinLock(&EventReadIrpQueue->QueueLock, &oldIrql); if (!IsListEmpty(&EventReadIrpQueue->EventReadIrpHead)) { // Remove the oldest Event Log entry = RemoveHeadList(&EventReadIrpQueue->EventReadIrpHead); // Get the IRP from the entry pIrp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); } // drop the queue protection KeReleaseSpinLock(&EventReadIrpQueue->QueueLock, oldIrql); return pIrp; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprFlushEventReadIrpQueue // Cancels all IRPs in the queue, or all IRPs in the queue related to a // particular open handle. // // Arguments: // IN EventReadIrpQueue // An instance of our queue structure // // Return Value: // none // VOID CprFlushEventReadIrpQueue( IN PCPR_EVENT_READ_IRP_QUEUE EventReadIrpQueue ) { PLIST_ENTRY entry; PIRP pIrp; KIRQL oldIrql; // grab the queue protection KeAcquireSpinLock(&EventReadIrpQueue->QueueLock, &oldIrql); // Look at the first entry in the queue while (!IsListEmpty(&EventReadIrpQueue->EventReadIrpHead)) { entry = RemoveHeadList(&EventReadIrpQueue->EventReadIrpHead); // Get the IRP from the entry pIrp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); } // drop the queue protection KeReleaseSpinLock(&EventReadIrpQueue->QueueLock, oldIrql); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprInvalidateEventReadIrpQueue // Stops queue from receiving anymore IRPs, all IRPs are completed upon // receipt // // Arguments: // IN EventReadIrpQueue // An instance of our queue structure // // Return Value: // none // VOID CprInvalidateEventReadIrpQueue( IN PCPR_EVENT_READ_IRP_QUEUE EventReadIrpQueue ) { // indicate the queue is shutdown EventReadIrpQueue->ErrorStatus = TRUE; // flush all requests from the queue CprFlushEventReadIrpQueue(EventReadIrpQueue); return; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprFindEventReadIrp // Removes the first Event Log from the queue // // Arguments: // IN EventReadIrpQueue // An instance of our queue structure // // Return Value: // PIRP // PIRP CprFindAnRemoveEventReadIrp( IN PCPR_EVENT_READ_IRP_QUEUE EventReadIrpQueue, IN ULONG sessionId ) { PLIST_ENTRY entry; PIRP pIrp; PIRP pFoundIrp = NULL; KIRQL oldIrql; // grab the queue protection KeAcquireSpinLock(&EventReadIrpQueue->QueueLock, &oldIrql); for (entry = EventReadIrpQueue->EventReadIrpHead.Flink; entry != &EventReadIrpQueue->EventReadIrpHead; entry = entry->Flink) { // Get the IRP from the entry pIrp = CONTAINING_RECORD(entry, IRP, Tail.Overlay.ListEntry); if (IrpSessionId(pIrp) == sessionId) { // We found it. pFoundIrp = pIrp; RemoveEntryList(entry); break; } } // drop the queue protection KeReleaseSpinLock(&EventReadIrpQueue->QueueLock, oldIrql); return pFoundIrp; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprQueueSession // Inserts an Event Log into the queue. // // Arguments: // IN EventReadIrpQueue // An instance of our queue structure // // IN pIrp // The Irp to add to the queue // // Return Value: // Status // NTSTATUS CprQueueSession( IN PCPR_SESSION_QUEUE SessionQueue, IN PCPR_SESSION pSession, IN BOOLEAN lock ) { NTSTATUS status = STATUS_SUCCESS; KIRQL oldIrql; if (lock) { // grab the queue protection KeAcquireSpinLock(&SessionQueue->QueueLock, &oldIrql); } // queue the Session InsertTailList(&SessionQueue->ListHead, &pSession->ListEntry); if (lock) { // drop the queue protection // raise our IRQL before calling StartIo //KeReleaseSpinLockFromDpcLevel(&SessionQueue->QueueLock); KeReleaseSpinLock(&SessionQueue->QueueLock, oldIrql); // drop our IRQL back //KeLowerIrql(oldIrql); } return status; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprDeQueueSession // Removes the Session Irp from the queue // // Arguments: // IN SessionQueue // An instance of our queue structure // // Return Value: // PCPR_SESSION // PCPR_SESSION CprDeQueueSession( IN PCPR_SESSION_QUEUE SessionQueue ) { PLIST_ENTRY entry; PCPR_SESSION pSession = NULL; KIRQL oldIrql; // grab the queue protection KeAcquireSpinLock(&SessionQueue->QueueLock, &oldIrql); if (!IsListEmpty(&SessionQueue->ListHead)) { // Remove the oldest Session entry = RemoveHeadList(&SessionQueue->ListHead); // Get the Session from the entry pSession = CONTAINING_RECORD(entry, CPR_SESSION, ListEntry); } // drop the queue protection KeReleaseSpinLock(&SessionQueue->QueueLock, oldIrql); return pSession; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprFindSession // Finds the first Event Log from the queue // // Arguments: // IN SessionQueue // An instance of our queue structure // // Return Value: // PCPR_SESSION // PCPR_SESSION CprFindSession( IN PCPR_SESSION_QUEUE SessionQueue, IN ULONG sessionId, IN BOOLEAN remove, IN BOOLEAN lock ) { PLIST_ENTRY entry; PCPR_SESSION pSession; PCPR_SESSION pFoundSession = NULL; KIRQL oldIrql; if (lock) { // grab the queue protection KeAcquireSpinLock(&SessionQueue->QueueLock, &oldIrql); } for (entry = SessionQueue->ListHead.Flink; entry != &SessionQueue->ListHead; entry = entry->Flink) { // Get the IRP from the entry pSession = CONTAINING_RECORD(entry, CPR_SESSION, ListEntry); if (pSession->SessionId == sessionId) { // We found it. pFoundSession = pSession; if (remove) { RemoveEntryList(entry); } break; } } if (lock) { // drop the queue protection KeReleaseSpinLock(&SessionQueue->QueueLock, oldIrql); } return pFoundSession; } void CprCleanUpSessions() { PCPR_SESSION pSession; KIRQL OldIrql; pSession = CprDeQueueSession(&gSessionQueue); while(pSession) { CprCleanUpSession(pSession); pSession = CprDeQueueSession(&gSessionQueue); } } void CprCleanUpSession(PCPR_SESSION pSession) { if (pSession) { if (pSession->EventLogIrp) { PIRP pIrp = pSession->EventLogIrp; pSession->EventLogIrp = NULL; if (IoSetCancelRoutine (pIrp, NULL)) { PCPR_EVENT_LOG pEvtLogFromApp = (PCPR_EVENT_LOG)pIrp->AssociatedIrp.SystemBuffer; RtlZeroMemory (pEvtLogFromApp, sizeof (CPR_EVENT_LOG)); pEvtLogFromApp->status = STATUS_CANCELLED; pEvtLogFromApp->flags = CPR_EVENT_FLAG_CANCELLED; pIrp->IoStatus.Status = STATUS_SUCCESS; pIrp->IoStatus.Information = sizeof(CPR_EVENT_LOG); IoCompleteRequest (pIrp, IO_NO_INCREMENT); } } CprFlushEventLogQueue(&pSession->EventLogQueue); ExFreePool(pSession); } } #ifdef USE_COM_STATUS_LIST /////////////////////////////////////////////////////////////////////////////////////////////////// // CprQueueComStatusIrp // Inserts a Com Status Irp into the queue. // // Arguments: // IN SessionQueue // An instance of the Session queue structure // // IN ComStatusIrp // The Irp to add to the queue // // Return Value: // Status // NTSTATUS CprQueueComStatusIrp( IN PCPR_SESSION_QUEUE SessionQueue, IN ULONG sessionId, IN PIRP ComStatusIrp; ) { NTSTATUS status = STATUS_SUCCESS; KIRQL oldIrql; PCPR_SESSION pSession = NULL; PCPR_IRP_LIST pComStatusIrpList = NULL; // grab the queue protection KeAcquireSpinLock(&SessionQueue->QueueLock, &oldIrql); for (entry = SessionQueue->ListHead.Flink; entry != &SessionQueue->ListHead; entry = entry->Flink) { // Get the IRP from the entry pSession = CONTAINING_RECORD(entry, CPR_SESSION, ListEntry); if (pSession->SessionId == sessionId) { // We found it. break; } } if (pSession) { pComStatusIrpList = (PCPR_IRP_LIST)ExAllocatePoolWithTag(NonPagedPool, size(CPR_IRP_LIST), CPR_POOL_TAG_COM_STATUS_IRP); if (pComStatusIrpList) { pComStatusIrpList->Handle = PsGetCurrentProcessId(); pComStatusIrpList->ComStatusIrp = ComStatusIrp; // queue the Irp List InsertTailList(&pSession->ComStatusIrpList, &pComStatusIrpList->ListEntry); status = STATUS_SUCCESS; } else { status = STATUS_INSUFFICIENT_RESOURCES; } } else { status = !STATUS_SUCCESS; } // drop the queue protection // raise our IRQL before calling StartIo KeReleaseSpinLockFromDpcLevel(&SessionQueue->QueueLock); // drop our IRQL back KeLowerIrql(oldIrql); return status; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprDeQueueSession // Removes the Com Status Irp from the queue // // Arguments: // IN SessionQueue // An instance of our queue structure // // Return Value: // PCPR_SESSION // PCPR_SESSION CprDeQueueSession( IN PCPR_SESSION_QUEUE SessionQueue ) { PLIST_ENTRY entry; PCPR_SESSION pSession = NULL; KIRQL oldIrql; // grab the queue protection KeAcquireSpinLock(&SessionQueue->QueueLock, &oldIrql); if (!IsListEmpty(&SessionQueue->ListHead)) { // Remove the oldest Session entry = RemoveHeadList(&SessionQueue->ListHead); // Get the Session from the entry pSession = CONTAINING_RECORD(entry, CPR_SESSION, ListEntry); } // drop the queue protection KeReleaseSpinLock(&SessionQueue->QueueLock, oldIrql); return pSession; } /////////////////////////////////////////////////////////////////////////////////////////////////// // CprFindSession // Finds the first Event Log from the queue // // Arguments: // IN SessionQueue // An instance of our queue structure // // Return Value: // PCPR_SESSION // PCPR_SESSION CprFindSession( IN PCPR_SESSION_QUEUE SessionQueue, IN ULONG sessionId, IN BOOLEAN remove ) { PLIST_ENTRY entry; PCPR_SESSION pSession; PCPR_SESSION pFoundSession = NULL; KIRQL oldIrql; // grab the queue protection KeAcquireSpinLock(&SessionQueue->QueueLock, &oldIrql); for (entry = SessionQueue->ListHead.Flink; entry != &SessionQueue->ListHead; entry = entry->Flink) { // Get the IRP from the entry pSession = CONTAINING_RECORD(entry, CPR_SESSION, ListEntry); if (pSession->SessionId == sessionId) { // We found it. pFoundSession = pSession; if (remove) { RemoveEntryList(entry); } break; } } // drop the queue protection KeReleaseSpinLock(&SessionQueue->QueueLock, oldIrql); return pFoundSession; } void CprCleanUpSessions() { PCPR_SESSION pSession; KIRQL OldIrql; pSession = CprDeQueueSession(&gSessionQueue); while(pSession) { CprCleanUpSession(pSession); pSession = CprDeQueueSession(&gSessionQueue); } } void CprCleanUpSession(PCPR_SESSION pSession) { if (pSession) { if (pSession->EventLogIrp) { PIRP pIrp = pSession->EventLogIrp; pSession->EventLogIrp = NULL; if (IoSetCancelRoutine (pIrp, NULL)) { pIrp->IoStatus.Status = STATUS_CANCELLED; pIrp->IoStatus.Information = 0; //DumpICRI("SimpleCancelRoutine.1", Irp); IoCompleteRequest (pIrp, IO_NO_INCREMENT); } } CprFlushEventLogQueue(&pSession->EventLogQueue); ExFreePool(pSession); } } #endif