1108 lines
25 KiB
C
1108 lines
25 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
workque.c
|
||
|
||
Abstract:
|
||
|
||
This module handles the communication between the NT redirector
|
||
FSP and the NT redirector FSD.
|
||
|
||
It defines routines that queue requests to the FSD, and routines
|
||
that remove requests from the FSD work queue.
|
||
|
||
|
||
Author:
|
||
|
||
Larry Osterman (LarryO) 30-May-1990
|
||
|
||
Revision History:
|
||
|
||
30-May-1990 LarryO
|
||
|
||
Created
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#pragma hdrstop
|
||
|
||
VOID
|
||
BowserCriticalThreadWorker(
|
||
IN PVOID Ctx
|
||
);
|
||
|
||
VOID
|
||
BowserDelayedThreadWorker(
|
||
IN PVOID Ctx
|
||
);
|
||
|
||
KSPIN_LOCK
|
||
BowserIrpContextInterlock = {0};
|
||
|
||
LIST_ENTRY
|
||
BowserIrpContextList = {0};
|
||
|
||
KSPIN_LOCK
|
||
BowserIrpQueueSpinLock = {0};
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(PAGE, BowserAllocateIrpContext)
|
||
#pragma alloc_text(PAGE, BowserFreeIrpContext)
|
||
#pragma alloc_text(PAGE, BowserInitializeIrpContext)
|
||
#pragma alloc_text(PAGE, BowserpUninitializeIrpContext)
|
||
#pragma alloc_text(PAGE, BowserInitializeIrpQueue)
|
||
#pragma alloc_text(PAGE, BowserQueueNonBufferRequest)
|
||
#pragma alloc_text(INIT, BowserpInitializeIrpQueue)
|
||
#pragma alloc_text(PAGE4BROW, BowserUninitializeIrpQueue)
|
||
#pragma alloc_text(PAGE4BROW, BowserQueueNonBufferRequestReferenced)
|
||
#pragma alloc_text(PAGE4BROW, BowserCancelQueuedIoForFile)
|
||
#pragma alloc_text(PAGE4BROW, BowserTimeoutQueuedIrp)
|
||
#endif
|
||
|
||
//
|
||
// Variables describing browsers use of a Critical system thread.
|
||
//
|
||
|
||
BOOLEAN BowserCriticalThreadRunning = FALSE;
|
||
|
||
LIST_ENTRY BowserCriticalThreadQueue;
|
||
|
||
WORK_QUEUE_ITEM BowserCriticalThreadWorkItem;
|
||
|
||
|
||
|
||
VOID
|
||
BowserQueueCriticalWorkItem (
|
||
IN PWORK_QUEUE_ITEM WorkItem
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine queues an item onto the critical work queue.
|
||
|
||
This routine ensures that at most one critical system thread is consumed
|
||
by the browser by actually queing this item onto a browser specific queue
|
||
then enqueing a critical work queue item that processes that queue.
|
||
|
||
Arguments:
|
||
|
||
WorkItem -- Work item to be processed on the critical work queue.
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
KIRQL OldIrql;
|
||
|
||
//
|
||
// Insert the queue entry into the browser specific queue.
|
||
//
|
||
ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
|
||
InsertTailList( &BowserCriticalThreadQueue, &WorkItem->List );
|
||
|
||
//
|
||
// If the browser doesn't have a critical system thread running,
|
||
// start one now.
|
||
//
|
||
|
||
if ( !BowserCriticalThreadRunning ) {
|
||
|
||
//
|
||
// Mark that the thread is running now
|
||
//
|
||
BowserCriticalThreadRunning = TRUE;
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
|
||
ExInitializeWorkItem( &BowserCriticalThreadWorkItem,
|
||
BowserCriticalThreadWorker,
|
||
NULL );
|
||
|
||
ExQueueWorkItem(&BowserCriticalThreadWorkItem, CriticalWorkQueue );
|
||
|
||
} else {
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
}
|
||
|
||
}
|
||
|
||
VOID
|
||
BowserCriticalThreadWorker(
|
||
IN PVOID Ctx
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes critical browser workitems.
|
||
|
||
This routine runs in a critical system thread. It is the only critical
|
||
system thread used by the browser.
|
||
|
||
Arguments:
|
||
|
||
Ctx - Not used
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL OldIrql;
|
||
PLIST_ENTRY Entry;
|
||
PWORK_QUEUE_ITEM WorkItem;
|
||
|
||
UNREFERENCED_PARAMETER( Ctx );
|
||
|
||
//
|
||
// Loop processing work items
|
||
//
|
||
|
||
while( TRUE ) {
|
||
|
||
//
|
||
// If the queue is empty,
|
||
// indicate that this thread is no longer running.
|
||
// return.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
|
||
|
||
if ( IsListEmpty( &BowserCriticalThreadQueue ) ) {
|
||
BowserCriticalThreadRunning = FALSE;
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Remove an entry from the queue.
|
||
//
|
||
|
||
Entry = RemoveHeadList( &BowserCriticalThreadQueue );
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
|
||
WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List);
|
||
|
||
//
|
||
// Call the queued routine
|
||
//
|
||
|
||
(*WorkItem->WorkerRoutine)(WorkItem->Parameter);
|
||
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// Variables describing browsers use of a Delayed system thread.
|
||
//
|
||
|
||
BOOLEAN BowserDelayedThreadRunning = FALSE;
|
||
|
||
LIST_ENTRY BowserDelayedThreadQueue;
|
||
|
||
WORK_QUEUE_ITEM BowserDelayedThreadWorkItem;
|
||
|
||
|
||
|
||
VOID
|
||
BowserQueueDelayedWorkItem (
|
||
IN PWORK_QUEUE_ITEM WorkItem
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine queues an item onto the Delayed work queue.
|
||
|
||
This routine ensures that at most one Delayed system thread is consumed
|
||
by the browser by actually queing this item onto a browser specific queue
|
||
then enqueing a Delayed work queue item that processes that queue.
|
||
|
||
Arguments:
|
||
|
||
WorkItem -- Work item to be processed on the Delayed work queue.
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
|
||
{
|
||
KIRQL OldIrql;
|
||
|
||
//
|
||
// Insert the queue entry into the browser specific queue.
|
||
//
|
||
ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
|
||
InsertTailList( &BowserDelayedThreadQueue, &WorkItem->List );
|
||
|
||
//
|
||
// If the browser doesn't have a Delayed system thread running,
|
||
// start one now.
|
||
//
|
||
|
||
if ( !BowserDelayedThreadRunning ) {
|
||
|
||
//
|
||
// Mark that the thread is running now
|
||
//
|
||
BowserDelayedThreadRunning = TRUE;
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
|
||
ExInitializeWorkItem( &BowserDelayedThreadWorkItem,
|
||
BowserDelayedThreadWorker,
|
||
NULL );
|
||
|
||
ExQueueWorkItem(&BowserDelayedThreadWorkItem, DelayedWorkQueue );
|
||
|
||
} else {
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
}
|
||
|
||
}
|
||
|
||
VOID
|
||
BowserDelayedThreadWorker(
|
||
IN PVOID Ctx
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes Delayed browser workitems.
|
||
|
||
This routine runs in a Delayed system thread. It is the only Delayed
|
||
system thread used by the browser.
|
||
|
||
Arguments:
|
||
|
||
Ctx - Not used
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
|
||
{
|
||
KIRQL OldIrql;
|
||
PLIST_ENTRY Entry;
|
||
PWORK_QUEUE_ITEM WorkItem;
|
||
|
||
UNREFERENCED_PARAMETER( Ctx );
|
||
|
||
//
|
||
// Loop processing work items
|
||
//
|
||
|
||
while( TRUE ) {
|
||
|
||
//
|
||
// If the queue is empty,
|
||
// indicate that this thread is no longer running.
|
||
// return.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
|
||
|
||
if ( IsListEmpty( &BowserDelayedThreadQueue ) ) {
|
||
BowserDelayedThreadRunning = FALSE;
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Remove an entry from the queue.
|
||
//
|
||
|
||
Entry = RemoveHeadList( &BowserDelayedThreadQueue );
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
|
||
WorkItem = CONTAINING_RECORD(Entry, WORK_QUEUE_ITEM, List);
|
||
|
||
//
|
||
// Call the queued routine
|
||
//
|
||
|
||
(*WorkItem->WorkerRoutine)(WorkItem->Parameter);
|
||
|
||
}
|
||
}
|
||
|
||
|
||
|
||
PIRP_CONTEXT
|
||
BowserAllocateIrpContext (
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialize a work queue structure, allocating all structures used for it.
|
||
|
||
Arguments:
|
||
|
||
None
|
||
|
||
|
||
Return Value:
|
||
|
||
PIRP_CONTEXT - Newly allocated Irp Context.
|
||
|
||
--*/
|
||
{
|
||
PIRP_CONTEXT IrpContext;
|
||
PAGED_CODE();
|
||
|
||
if ((IrpContext = (PIRP_CONTEXT )ExInterlockedRemoveHeadList(&BowserIrpContextList, &BowserIrpContextInterlock)) == NULL) {
|
||
|
||
//
|
||
// If there are no IRP contexts in the "zone", allocate a new
|
||
// Irp context from non paged pool.
|
||
//
|
||
|
||
IrpContext = ALLOCATE_POOL(NonPagedPool, sizeof(IRP_CONTEXT), POOL_IRPCONTEXT);
|
||
|
||
if (IrpContext == NULL) {
|
||
InternalError(("Could not allocate pool for IRP context\n"));
|
||
}
|
||
|
||
return IrpContext;
|
||
}
|
||
|
||
return IrpContext;
|
||
}
|
||
|
||
VOID
|
||
BowserFreeIrpContext (
|
||
PIRP_CONTEXT IrpContext
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialize a work queue structure, allocating all structures used for it.
|
||
|
||
Arguments:
|
||
|
||
PIRP_CONTEXT IrpContext - Irp Context to free.
|
||
None
|
||
|
||
|
||
Return Value:
|
||
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// We use the first two longwords of the IRP context as a list entry
|
||
// when we free it to the zone.
|
||
//
|
||
|
||
ExInterlockedInsertTailList(&BowserIrpContextList, (PLIST_ENTRY )IrpContext,
|
||
&BowserIrpContextInterlock);
|
||
}
|
||
|
||
|
||
VOID
|
||
BowserInitializeIrpContext (
|
||
VOID
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initialize the Irp Context system
|
||
|
||
Arguments:
|
||
|
||
None.
|
||
|
||
|
||
Return Value:
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
KeInitializeSpinLock(&BowserIrpContextInterlock);
|
||
InitializeListHead(&BowserIrpContextList);
|
||
}
|
||
|
||
VOID
|
||
BowserpUninitializeIrpContext(
|
||
VOID
|
||
)
|
||
{
|
||
PAGED_CODE();
|
||
|
||
while (!IsListEmpty(&BowserIrpContextList)) {
|
||
PIRP_CONTEXT IrpContext = (PIRP_CONTEXT)RemoveHeadList(&BowserIrpContextList);
|
||
|
||
FREE_POOL(IrpContext);
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
BowserInitializeIrpQueue(
|
||
PIRP_QUEUE Queue
|
||
)
|
||
{
|
||
PAGED_CODE();
|
||
|
||
InitializeListHead(&Queue->Queue);
|
||
|
||
}
|
||
|
||
VOID
|
||
BowserUninitializeIrpQueue(
|
||
PIRP_QUEUE Queue
|
||
)
|
||
{
|
||
KIRQL OldIrql, CancelIrql;
|
||
PDRIVER_CANCEL pDriverCancel;
|
||
PLIST_ENTRY Entry;
|
||
PIRP Request;
|
||
|
||
BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
|
||
|
||
DISCARDABLE_CODE( BowserDiscardableCodeSection );
|
||
|
||
//
|
||
// Now remove this IRP from the request chain.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
|
||
|
||
while (!IsListEmpty(&Queue->Queue)) {
|
||
|
||
Entry = RemoveHeadList(&Queue->Queue);
|
||
|
||
Request = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
|
||
|
||
// clear cancel routine
|
||
Request->IoStatus.Information = 0;
|
||
Request->Cancel = FALSE;
|
||
pDriverCancel = IoSetCancelRoutine(Request, NULL);
|
||
|
||
// Set to NULL in the cancel routine under BowserIrpQueueSpinLock protection.
|
||
if ( pDriverCancel ) {
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
BowserCompleteRequest(Request, STATUS_CANCELLED);
|
||
ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
|
||
}
|
||
// otherwise the cancel routine is running at the moment.
|
||
}
|
||
|
||
ASSERT (IsListEmpty(&Queue->Queue));
|
||
|
||
//
|
||
// Make sure no more entries are inserted on this queue.
|
||
//
|
||
|
||
Queue->Queue.Flink = NULL;
|
||
Queue->Queue.Blink = NULL;
|
||
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
|
||
BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
|
||
|
||
}
|
||
|
||
VOID
|
||
BowserCancelQueuedRequest(
|
||
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
|
||
IN PIRP Irp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
This routine will cancel a queued IRP.
|
||
|
||
Arguments:
|
||
IN PIRP Irp - Supplies the IRP to cancel.
|
||
|
||
IN PKSPIN_LOCK SpinLock - Supplies a pointer to the spin lock protecting the
|
||
queue
|
||
|
||
IN PLIST_ENTRY Queue - Supplies a pointer to the head of the queue.
|
||
|
||
Note: See bug history for more: 294055, 306281, 124178, 124180, 131773...
|
||
--*/
|
||
|
||
{
|
||
KIRQL OldIrql;
|
||
PLIST_ENTRY Entry, NextEntry;
|
||
PIRP Request;
|
||
PIRP_QUEUE Queue;
|
||
PIO_STACK_LOCATION NextStack = IoGetNextIrpStackLocation(Irp);
|
||
LIST_ENTRY CancelList;
|
||
|
||
ASSERT ( Irp->CancelRoutine == NULL );
|
||
|
||
InitializeListHead(&CancelList);
|
||
|
||
//
|
||
// Release IOmgr set cancel IRP spinlock & acquire the local
|
||
// queue protection spinlock.
|
||
//
|
||
|
||
IoReleaseCancelSpinLock( Irp->CancelIrql );
|
||
ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
|
||
|
||
//
|
||
// Now remove this IRP from the request chain.
|
||
//
|
||
|
||
|
||
//
|
||
// A pointer to the queue is stored in the next stack location.
|
||
//
|
||
|
||
Queue = (PIRP_QUEUE)NextStack->Parameters.Others.Argument4;
|
||
|
||
if (Queue != NULL && Queue->Queue.Flink != NULL) {
|
||
|
||
for (Entry = Queue->Queue.Flink ;
|
||
Entry != &Queue->Queue ;
|
||
Entry = NextEntry) {
|
||
|
||
Request = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
|
||
|
||
if (Request->Cancel) {
|
||
// we're in a cancel routine so the global cancel spinlock is locked
|
||
|
||
NextEntry = Entry->Flink;
|
||
RemoveEntryList(Entry);
|
||
|
||
Request->IoStatus.Information = 0;
|
||
Request->IoStatus.Status = STATUS_CANCELLED;
|
||
IoSetCancelRoutine(Request, NULL);
|
||
|
||
InsertTailList(&CancelList,Entry);
|
||
|
||
} else {
|
||
NextEntry = Entry->Flink;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
|
||
while (!IsListEmpty(&CancelList)) {
|
||
Entry = RemoveHeadList(&CancelList);
|
||
Request = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
|
||
BowserCompleteRequest(Request, Request->IoStatus.Status);
|
||
}
|
||
|
||
UNREFERENCED_PARAMETER(DeviceObject);
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
BowserQueueNonBufferRequest(
|
||
IN PIRP Irp,
|
||
IN PIRP_QUEUE Queue,
|
||
IN PDRIVER_CANCEL CancelRoutine
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Queue an IRP in the specified queue.
|
||
|
||
This routine cannot be called at an IRQ level above APC_LEVEL.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the IRP to queue.
|
||
|
||
Queue - Supplies a pointer to the head of the queue.
|
||
|
||
CancelRoutine - Address of routine to call if the IRP is cancelled.
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
//
|
||
// This routine itself is paged code which calls the discardable code
|
||
// in BowserQueueNonBufferRequestReferenced().
|
||
//
|
||
PAGED_CODE();
|
||
|
||
BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
|
||
DISCARDABLE_CODE( BowserDiscardableCodeSection );
|
||
|
||
Status = BowserQueueNonBufferRequestReferenced( Irp,
|
||
Queue,
|
||
CancelRoutine );
|
||
|
||
BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS
|
||
BowserQueueNonBufferRequestReferenced(
|
||
IN PIRP Irp,
|
||
IN PIRP_QUEUE Queue,
|
||
IN PDRIVER_CANCEL CancelRoutine
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Queue an IRP in the specified queue.
|
||
|
||
This routine can only be called if the BowserDiscardableCodeSection
|
||
is already referenced. It can be called at any IRQ level.
|
||
|
||
Arguments:
|
||
|
||
Irp - Supplies the IRP to queue.
|
||
|
||
Queue - Supplies a pointer to the head of the queue.
|
||
|
||
CancelRoutine - Address of routine to call if the IRP is cancelled.
|
||
--*/
|
||
|
||
{
|
||
KIRQL OldIrql, CancelIrql;
|
||
LARGE_INTEGER CurrentTickCount;
|
||
PIO_STACK_LOCATION NextStackLocation;
|
||
BOOL bReleaseSpinlocks;
|
||
|
||
DISCARDABLE_CODE( BowserDiscardableCodeSection );
|
||
|
||
|
||
// DbgPrint("Queue IRP %lx to queue %lx\n", Irp, Queue);
|
||
|
||
//
|
||
// Insert the request into the request announcement list.
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
|
||
|
||
if (Queue->Queue.Flink == NULL) {
|
||
|
||
ASSERT (Queue->Queue.Blink == NULL);
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
|
||
return(STATUS_CANCELLED);
|
||
}
|
||
|
||
//
|
||
// Flag that this request is going to be pending.
|
||
//
|
||
|
||
IoMarkIrpPending(Irp);
|
||
|
||
InsertTailList(&Queue->Queue, &Irp->Tail.Overlay.ListEntry);
|
||
|
||
//
|
||
// Make sure there's room enough in the stack location for this.
|
||
//
|
||
|
||
ASSERT (Irp->CurrentLocation <= Irp->StackCount);
|
||
|
||
NextStackLocation = IoGetNextIrpStackLocation(Irp);
|
||
|
||
//
|
||
// Stick the current tick count into the next IRP stack location
|
||
// for this IRP. This allows us to figure out if these IRP's have been
|
||
// around for "too long".
|
||
//
|
||
// Beware:the IRP stack location is unaligned.
|
||
//
|
||
|
||
KeQueryTickCount( &CurrentTickCount );
|
||
*((LARGE_INTEGER UNALIGNED *)&NextStackLocation->Parameters.Others.Argument1) =
|
||
CurrentTickCount;
|
||
|
||
|
||
//
|
||
// Link the queue into the IRP.
|
||
//
|
||
|
||
NextStackLocation->Parameters.Others.Argument4 = (PVOID)Queue;
|
||
|
||
// WARNING: double spinlock condition
|
||
IoAcquireCancelSpinLock(&CancelIrql);
|
||
bReleaseSpinlocks = TRUE;
|
||
|
||
if (Irp->Cancel) {
|
||
|
||
//
|
||
// The Irp is in cancellable state:
|
||
// if CancelRoutine == NULL, the routine is currently running
|
||
// Otherwise, we need to cancel it ourselves
|
||
//
|
||
if ( Irp->CancelRoutine ) {
|
||
// cacelable:
|
||
// - rm is valid since we're still holding BowserIrpQueueSpinLock
|
||
RemoveEntryList( &Irp->Tail.Overlay.ListEntry );
|
||
|
||
// release spinlocks before completing the request
|
||
IoReleaseCancelSpinLock(CancelIrql);
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
bReleaseSpinlocks = FALSE;
|
||
|
||
// complete.
|
||
BowserCompleteRequest ( Irp, STATUS_CANCELLED );
|
||
}
|
||
// else CancelRoutine is running
|
||
} else {
|
||
|
||
IoSetCancelRoutine(Irp, CancelRoutine);
|
||
}
|
||
|
||
if ( bReleaseSpinlocks ) {
|
||
// release spinlocks
|
||
IoReleaseCancelSpinLock(CancelIrql);
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
}
|
||
|
||
return STATUS_PENDING;
|
||
|
||
}
|
||
|
||
VOID
|
||
BowserTimeoutQueuedIrp(
|
||
IN PIRP_QUEUE Queue,
|
||
IN ULONG NumberOfSecondsToTimeOut
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
This routine will scan an IRP queue and time out any requests that have
|
||
been on the queue for "too long"
|
||
|
||
Arguments:
|
||
IN PIRP_QUEUE Queue - Supplies the Queue to scan.
|
||
IN ULONG NumberOfSecondsToTimeOut - Supplies the number of seconds a request
|
||
should remain on the queue.
|
||
|
||
Return Value:
|
||
None
|
||
|
||
This routine will also complete any canceled queued requests it finds (on
|
||
general principles).
|
||
|
||
--*/
|
||
|
||
{
|
||
PIRP Irp;
|
||
KIRQL OldIrql, CancelIrql;
|
||
PDRIVER_CANCEL pDriverCancel;
|
||
PLIST_ENTRY Entry, NextEntry;
|
||
LARGE_INTEGER Timeout;
|
||
LIST_ENTRY CancelList;
|
||
|
||
BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
|
||
|
||
DISCARDABLE_CODE( BowserDiscardableCodeSection );
|
||
|
||
InitializeListHead(&CancelList);
|
||
|
||
//
|
||
// Compute the timeout time into 100ns units.
|
||
//
|
||
|
||
Timeout.QuadPart = (LONGLONG)NumberOfSecondsToTimeOut * (LONGLONG)(10000*1000);
|
||
|
||
//
|
||
// Now convert the timeout into a number of ticks.
|
||
//
|
||
|
||
Timeout.QuadPart = Timeout.QuadPart / (LONGLONG)KeQueryTimeIncrement();
|
||
|
||
ASSERT (Timeout.HighPart == 0);
|
||
|
||
// DbgPrint("Dequeue irp from queue %lx...", Queue);
|
||
|
||
ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
|
||
|
||
|
||
for (Entry = Queue->Queue.Flink ;
|
||
Entry != &Queue->Queue ;
|
||
Entry = NextEntry) {
|
||
|
||
Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
|
||
|
||
//
|
||
// If the request was canceled, this is a convenient time to cancel
|
||
// it.
|
||
//
|
||
|
||
if (Irp->Cancel) {
|
||
|
||
NextEntry = Entry->Flink;
|
||
|
||
pDriverCancel = IoSetCancelRoutine(Irp, NULL);
|
||
|
||
// Set to NULL in the cancel routine under BowserIrpQueueSpinLock protection.
|
||
if ( pDriverCancel ) {
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_CANCELLED;
|
||
|
||
RemoveEntryList(Entry);
|
||
|
||
InsertTailList(&CancelList,Entry);
|
||
}
|
||
// otherwise the cancel routine is running at the moment.
|
||
|
||
|
||
|
||
//
|
||
// Now check to see if this request is "too old". If it is, complete
|
||
// it with an error.
|
||
//
|
||
|
||
} else {
|
||
PIO_STACK_LOCATION NextIrpStackLocation;
|
||
LARGE_INTEGER CurrentTickCount;
|
||
LARGE_INTEGER RequestTime;
|
||
LARGE_INTEGER Temp;
|
||
|
||
NextIrpStackLocation = IoGetNextIrpStackLocation(Irp);
|
||
|
||
//
|
||
// Snapshot the current tickcount.
|
||
//
|
||
|
||
KeQueryTickCount(&CurrentTickCount);
|
||
|
||
//
|
||
// Figure out how many seconds this request has been active for
|
||
//
|
||
|
||
Temp.LowPart = (*((LARGE_INTEGER UNALIGNED *)&NextIrpStackLocation->Parameters.Others.Argument1)).LowPart;
|
||
Temp.HighPart= (*((LARGE_INTEGER UNALIGNED *)&NextIrpStackLocation->Parameters.Others.Argument1)).HighPart;
|
||
RequestTime.QuadPart = CurrentTickCount.QuadPart - Temp.QuadPart;
|
||
|
||
ASSERT (RequestTime.HighPart == 0);
|
||
|
||
//
|
||
// If this request has lasted "too long", then time it
|
||
// out.
|
||
//
|
||
|
||
if (RequestTime.LowPart > Timeout.LowPart) {
|
||
|
||
|
||
NextEntry = Entry->Flink;
|
||
|
||
pDriverCancel = IoSetCancelRoutine(Irp, NULL);
|
||
|
||
// Set to NULL in the cancel routine under BowserIrpQueueSpinLock protection.
|
||
if ( pDriverCancel ) {
|
||
|
||
Irp->IoStatus.Information = 0;
|
||
Irp->IoStatus.Status = STATUS_IO_TIMEOUT;
|
||
|
||
RemoveEntryList(Entry);
|
||
|
||
InsertTailList(&CancelList,Entry);
|
||
}
|
||
// otherwise it the cancel routine is running
|
||
|
||
|
||
} else {
|
||
NextEntry = Entry->Flink;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
|
||
while (!IsListEmpty(&CancelList)) {
|
||
Entry = RemoveHeadList(&CancelList);
|
||
Irp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
|
||
BowserCompleteRequest(Irp, Irp->IoStatus.Status);
|
||
}
|
||
|
||
BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
|
||
|
||
// DbgPrint("%lx.\n", Irp);
|
||
|
||
|
||
}
|
||
|
||
PIRP
|
||
BowserDequeueQueuedIrp(
|
||
IN PIRP_QUEUE Queue
|
||
)
|
||
{
|
||
PIRP Irp;
|
||
KIRQL OldIrql;
|
||
PLIST_ENTRY IrpEntry;
|
||
|
||
// DbgPrint("Dequeue irp from queue %lx...", Queue);
|
||
|
||
ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
|
||
|
||
if (IsListEmpty(&Queue->Queue)) {
|
||
//
|
||
// There are no waiting request announcement FsControls, so
|
||
// return success.
|
||
//
|
||
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
|
||
// DbgPrint("No entry found.\n");
|
||
return NULL;
|
||
}
|
||
|
||
IrpEntry = RemoveHeadList(&Queue->Queue);
|
||
|
||
Irp = CONTAINING_RECORD(IrpEntry, IRP, Tail.Overlay.ListEntry);
|
||
|
||
IoAcquireCancelSpinLock(&Irp->CancelIrql);
|
||
|
||
//
|
||
// Remove the cancel request for this IRP.
|
||
//
|
||
|
||
Irp->Cancel = FALSE;
|
||
|
||
IoSetCancelRoutine(Irp, NULL);
|
||
|
||
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
||
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
|
||
// DbgPrint("%lx.\n", Irp);
|
||
return Irp;
|
||
}
|
||
|
||
|
||
VOID
|
||
BowserCancelQueuedIoForFile(
|
||
IN PIRP_QUEUE Queue,
|
||
IN PFILE_OBJECT FileObject
|
||
)
|
||
{
|
||
KIRQL OldIrql;
|
||
PLIST_ENTRY Entry, NextEntry;
|
||
PDRIVER_CANCEL pDriverCancel;
|
||
PIRP Request;
|
||
LIST_ENTRY CancelList;
|
||
|
||
BowserReferenceDiscardableCode( BowserDiscardableCodeSection );
|
||
|
||
DISCARDABLE_CODE( BowserDiscardableCodeSection );
|
||
|
||
InitializeListHead(&CancelList);
|
||
|
||
//
|
||
// Walk the outstanding IRP list for this
|
||
//
|
||
|
||
ACQUIRE_SPIN_LOCK(&BowserIrpQueueSpinLock, &OldIrql);
|
||
|
||
for (Entry = Queue->Queue.Flink ;
|
||
Entry != &Queue->Queue ;
|
||
Entry = NextEntry) {
|
||
|
||
Request = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
|
||
|
||
//
|
||
// If the request was canceled, blow it away.
|
||
//
|
||
|
||
if (Request->Cancel) {
|
||
|
||
NextEntry = Entry->Flink;
|
||
|
||
// This is the cancel routine setting of cancel routine ptr to NULL.
|
||
pDriverCancel = IoSetCancelRoutine(Request, NULL);
|
||
|
||
// Set to NULL in the cancel routine under BowserIrpQueueSpinLock protection.
|
||
if ( pDriverCancel ) {
|
||
|
||
RemoveEntryList(Entry);
|
||
Request->IoStatus.Information = 0;
|
||
Request->IoStatus.Status = STATUS_CANCELLED;
|
||
|
||
InsertTailList(&CancelList,Entry);
|
||
}
|
||
// otherwise the cancel routine is running currently.
|
||
|
||
//
|
||
// If the request was for this file object, blow it away.
|
||
//
|
||
|
||
} else if (Request->Tail.Overlay.OriginalFileObject == FileObject) {
|
||
|
||
NextEntry = Entry->Flink;
|
||
|
||
// This is the cancel routine setting of cancel routine ptr to NULL.
|
||
pDriverCancel = IoSetCancelRoutine(Request, NULL);
|
||
|
||
// Set to NULL in the cancel routine under BowserIrpQueueSpinLock protection.
|
||
if ( pDriverCancel ) {
|
||
|
||
RemoveEntryList(Entry);
|
||
|
||
Request->IoStatus.Information = 0;
|
||
Request->IoStatus.Status = STATUS_FILE_CLOSED;
|
||
|
||
InsertTailList(&CancelList,Entry);
|
||
}
|
||
// otherwise the cancel routine is running currently.
|
||
|
||
} else {
|
||
NextEntry = Entry->Flink;
|
||
}
|
||
|
||
}
|
||
|
||
RELEASE_SPIN_LOCK(&BowserIrpQueueSpinLock, OldIrql);
|
||
|
||
while (!IsListEmpty(&CancelList)) {
|
||
Entry = RemoveHeadList(&CancelList);
|
||
Request = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.ListEntry);
|
||
BowserCompleteRequest(Request, Request->IoStatus.Status);
|
||
}
|
||
|
||
BowserDereferenceDiscardableCode( BowserDiscardableCodeSection );
|
||
}
|
||
|
||
|
||
VOID
|
||
BowserpInitializeIrpQueue(
|
||
VOID
|
||
)
|
||
{
|
||
KeInitializeSpinLock(&BowserIrpQueueSpinLock);
|
||
InitializeListHead( &BowserCriticalThreadQueue );
|
||
InitializeListHead( &BowserDelayedThreadQueue );
|
||
|
||
}
|