|
|
/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
Workque.c
Abstract:
This module implements the queue of work from the FSD to the FSP threads (system worker threads) for the NetWare redirector.
Author:
Colin Watson [ColinW] 19-Dec-1992
Revision History:
--*/
#include "Procs.h"
LIST_ENTRY IrpContextList; KSPIN_LOCK IrpContextInterlock; KSPIN_LOCK ContextInterlock;
LONG FreeContextCount = 4; // Allow up to 4 free contexts
LIST_ENTRY MiniIrpContextList; LONG FreeMiniContextCount = 20; // Allow up to 20 free mini contexts
LONG MiniContextCount = 0; // Allow up to 20 free mini contexts
HANDLE WorkerThreadHandle;
//
// The debug trace level
//
#define Dbg (DEBUG_TRACE_WORKQUE)
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, InitializeIrpContext )
#pragma alloc_text( PAGE, UninitializeIrpContext )
#pragma alloc_text( PAGE, NwAppendToQueueAndWait )
#pragma alloc_text( PAGE, WorkerThread )
#ifndef QFE_BUILD
//
//#pragma alloc_text( PAGE1, NwDequeueIrpContext )
//We hold a spinlock coming in or we acquire one inside the function
//So, this should be non-paged.
//
#pragma alloc_text( PAGE1, AllocateMiniIrpContext )
#pragma alloc_text( PAGE1, FreeMiniIrpContext )
#endif
#endif
#if 0 // Not pageable
AllocateIrpContext FreeIrpContext NwCompleteRequest SpawnWorkerThread
// see ifndef QFE_BUILD above
#endif
PIRP_CONTEXT AllocateIrpContext ( PIRP pIrp ) /*++
Routine Description:
Initialize a work queue structure, allocating all structures used for it.
Arguments:
pIrp - Supplies the Irp for the applications request
Return Value:
PIRP_CONTEXT - Newly allocated Irp Context.
--*/ { PIRP_CONTEXT IrpContext;
if ((IrpContext = (PIRP_CONTEXT )ExInterlockedRemoveHeadList(&IrpContextList, &IrpContextInterlock)) == NULL) {
try {
//
// If there are no IRP contexts in the "zone", allocate a new
// Irp context from non paged pool.
//
IrpContext = ALLOCATE_POOL_EX(NonPagedPool, sizeof(IRP_CONTEXT));
RtlFillMemory( IrpContext, sizeof(IRP_CONTEXT), 0 );
IrpContext->TxMdl = NULL; IrpContext->RxMdl = NULL;
KeInitializeEvent( &IrpContext->Event, SynchronizationEvent, FALSE );
IrpContext->NodeTypeCode = NW_NTC_IRP_CONTEXT; IrpContext->NodeByteSize = sizeof(IRP_CONTEXT);
IrpContext->TxMdl = ALLOCATE_MDL( &IrpContext->req, MAX_SEND_DATA, FALSE, FALSE, NULL ); if ( IrpContext->TxMdl == NULL) { InternalError(("Could not allocate TxMdl for IRP context\n")); ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); }
IrpContext->RxMdl = ALLOCATE_MDL( &IrpContext->rsp, MAX_RECV_DATA, FALSE, FALSE, NULL ); if ( IrpContext->RxMdl == NULL) { InternalError(("Could not allocate RxMdl for IRP context\n")); ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); }
} finally {
if ( AbnormalTermination() ) {
if ( IrpContext != NULL ) {
if (IrpContext->TxMdl != NULL ) { FREE_MDL( IrpContext->TxMdl ); }
FREE_POOL( IrpContext ); } else { InternalError(("Could not allocate pool for IRP context\n")); } } }
MmBuildMdlForNonPagedPool(IrpContext->TxMdl); MmBuildMdlForNonPagedPool(IrpContext->RxMdl);
#ifdef NWDBG
// Make it easy to find fields in the context
IrpContext->Signature1 = 0xfeedf00d; IrpContext->Signature2 = 0xfeedf00d; IrpContext->Signature3 = 0xfeedf00d; #endif
// IrpContext is allocated. Finish off initialization.
} else {
// Record that we have removed an entry from the free list
InterlockedIncrement(&FreeContextCount);
ASSERT( IrpContext != NULL );
//
// The free list uses the start of the structure for the list entry
// so restore corrupted fields.
//
IrpContext->NodeTypeCode = NW_NTC_IRP_CONTEXT; IrpContext->NodeByteSize = sizeof(IRP_CONTEXT);
// Ensure mdl's are clean
IrpContext->TxMdl->Next = NULL; IrpContext->RxMdl->Next = NULL; IrpContext->RxMdl->ByteCount = MAX_RECV_DATA;
//
// Clean "used" fields
//
IrpContext->Flags = 0; IrpContext->Icb = NULL; IrpContext->pEx = NULL; IrpContext->TimeoutRoutine = NULL; IrpContext->CompletionSendRoutine = NULL; IrpContext->ReceiveDataRoutine = NULL; IrpContext->pTdiStruct = NULL;
//
// Clean the specific data zone.
//
RtlZeroMemory( &(IrpContext->Specific), sizeof( IrpContext->Specific ) );
// 8/13/96 cjc Fix problem with apps not being able to save
// files to NDS drives. This was never reset so
// ExchangeWithWait ret'd an error to WriteNCP.
IrpContext->ResponseParameters.Error = 0; }
InterlockedIncrement(&ContextCount);
//
// Save away the fields in the Irp that might be tromped by
// building the Irp for the exchange with the server.
//
IrpContext->pOriginalIrp = pIrp;
if ( pIrp != NULL) { IrpContext->pOriginalSystemBuffer = pIrp->AssociatedIrp.SystemBuffer; IrpContext->pOriginalUserBuffer = pIrp->UserBuffer; IrpContext->pOriginalMdlAddress = pIrp->MdlAddress; }
#ifdef NWDBG
IrpContext->pNpScb = NULL; #endif
ASSERT( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) );
return IrpContext; }
VOID FreeIrpContext ( 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:
--*/ {
ASSERT( IrpContext->NodeTypeCode == NW_NTC_IRP_CONTEXT ); ASSERT( !BooleanFlagOn( IrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) ); ASSERT( IrpContext->PostProcessRoutine == NULL );
FreeReceiveIrp( IrpContext );
#ifdef NWDBG
IrpContext->DebugValue = 0; #endif
IrpContext->Flags = 0;
//
// Cleanup the Irp needs to be restored to its original settings.
//
if ( IrpContext->pOriginalIrp != NULL ) {
PIRP pIrp = IrpContext->pOriginalIrp;
pIrp->AssociatedIrp.SystemBuffer = IrpContext->pOriginalSystemBuffer;
pIrp->UserBuffer = IrpContext->pOriginalUserBuffer;
pIrp->MdlAddress = IrpContext->pOriginalMdlAddress;
#ifdef NWDBG
IrpContext->pOriginalIrp = NULL; #endif
}
#ifdef NWDBG
RtlZeroMemory( &IrpContext->WorkQueueItem, sizeof( WORK_QUEUE_ITEM ) ); #endif
InterlockedDecrement(&ContextCount);
if ( InterlockedDecrement(&FreeContextCount) >= 0 ) {
//
// We use the first two longwords of the IRP context as a list entry
// when we free it to the list.
//
ExInterlockedInsertTailList(&IrpContextList, (PLIST_ENTRY )IrpContext, &IrpContextInterlock); } else { //
// We already have as many free context as we allow so destroy
// this context. Restore FreeContextCount to its original value.
//
InterlockedIncrement( &FreeContextCount );
FREE_MDL( IrpContext->TxMdl ); FREE_MDL( IrpContext->RxMdl ); FREE_POOL(IrpContext); #ifdef NWDBG
ContextCount --; #endif
} }
VOID InitializeIrpContext ( VOID ) /*++
Routine Description:
Initialize the Irp Context system
Arguments:
None.
Return Value: None.
--*/ { PAGED_CODE();
KeInitializeSpinLock(&IrpContextInterlock); KeInitializeSpinLock(&ContextInterlock); InitializeListHead(&IrpContextList); InitializeListHead(&MiniIrpContextList); }
VOID UninitializeIrpContext ( VOID ) /*++
Routine Description:
Initialize the Irp Context system
Arguments:
None.
Return Value: None.
--*/ { PIRP_CONTEXT IrpContext; PLIST_ENTRY ListEntry; PMINI_IRP_CONTEXT MiniIrpContext;
PAGED_CODE();
//
// Free all the IRP contexts.
//
while ( !IsListEmpty( &IrpContextList ) ) { IrpContext = (PIRP_CONTEXT)RemoveHeadList( &IrpContextList );
FREE_MDL( IrpContext->TxMdl ); FREE_MDL( IrpContext->RxMdl ); FREE_POOL(IrpContext); }
while ( !IsListEmpty( &MiniIrpContextList ) ) {
ListEntry = RemoveHeadList( &MiniIrpContextList ); MiniIrpContext = CONTAINING_RECORD( ListEntry, MINI_IRP_CONTEXT, Next );
FREE_POOL( MiniIrpContext->Buffer ); FREE_MDL( MiniIrpContext->Mdl2 ); FREE_MDL( MiniIrpContext->Mdl1 ); FREE_IRP( MiniIrpContext->Irp ); FREE_POOL( MiniIrpContext ); } }
VOID NwCompleteRequest ( PIRP_CONTEXT IrpContext, NTSTATUS Status ) /*++
Routine Description:
The following procedure is used by the FSP and FSD routines to complete an IRP.
Arguments:
IrpContext - A pointer to the IRP context information.
Status - The status to use to complete the IRP.
Return Value:
None.
--*/ { PIRP Irp;
if ( IrpContext == NULL ) { return; }
if ( Status == STATUS_PENDING ) { return; }
if ( Status == STATUS_INSUFFICIENT_RESOURCES ) { Error( EVENT_NWRDR_RESOURCE_SHORTAGE, Status, NULL, 0, 0 ); }
Irp = IrpContext->pOriginalIrp;
Irp->IoStatus.Status = Status; DebugTrace(0, Dbg, "Completing Irp with status %X\n", Status );
// Restore the Irp to its original state
if ((Irp->CurrentLocation) > (CCHAR) (Irp->StackCount +1)) {
DbgPrint("Irp is already completed.\n", Irp); DbgBreakPoint(); }
FreeIrpContext( IrpContext );
IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT );
return; }
VOID NwAppendToQueueAndWait( PIRP_CONTEXT IrpContext ) /*++
Routine Description:
This routine appends an IrpContext to the SCB queue, and waits the the queue to be ready to process the Irp.
Arguments:
IrpContext - A pointer to the IRP context information.
Return Value:
None.
--*/ { BOOLEAN AtFront;
PAGED_CODE();
DebugTrace(+1, Dbg, "NwAppendToQueueAndWait\n", 0);
IrpContext->RunRoutine = SetEvent;
#ifdef MSWDBG
ASSERT( IrpContext->Event.Header.SignalState == 0 ); #endif
AtFront = AppendToScbQueue( IrpContext, IrpContext->pNpScb );
if ( AtFront ) { KickQueue( IrpContext->pNpScb ); }
//
// Wait until we get to the front of the queue.
//
KeWaitForSingleObject( &IrpContext->Event, UserRequest, KernelMode, FALSE, NULL );
ASSERT( IrpContext->pNpScb->Requests.Flink == &IrpContext->NextRequest );
DebugTrace(-1, Dbg, "NwAppendToQueueAndWait\n", 0); return; }
VOID NwDequeueIrpContext( IN PIRP_CONTEXT pIrpContext, IN BOOLEAN OwnSpinLock ) /*++
Routine Description:
This routine removes an IRP Context from the front the SCB queue.
Arguments:
IrpContext - A pointer to the IRP context information.
OwnSpinLock - If TRUE, the caller owns the SCB spin lock.
Return Value:
None.
--*/ { PLIST_ENTRY pListEntry; KIRQL OldIrql; PNONPAGED_SCB pNpScb;
DebugTrace(+1, Dbg, "NwDequeueIrpContext\n", 0);
if (!BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE ) ) { DebugTrace(-1, Dbg, "NwDequeueIrpContext\n", 0); return; }
pNpScb = pIrpContext->pNpScb;
if ( !OwnSpinLock ) { KeAcquireSpinLock( &pNpScb->NpScbSpinLock, &OldIrql ); }
//
// Disable timer from looking at this queue.
//
pNpScb->OkToReceive = FALSE;
pListEntry = RemoveHeadList( &pNpScb->Requests );
if ( !OwnSpinLock ) { KeReleaseSpinLock( &pNpScb->NpScbSpinLock, OldIrql ); }
#ifdef NWDBG
ASSERT ( CONTAINING_RECORD( pListEntry, IRP_CONTEXT, NextRequest ) == pIrpContext );
{
PIRP_CONTEXT RemovedContext = CONTAINING_RECORD( pListEntry, IRP_CONTEXT, NextRequest ); if ( RemovedContext != pIrpContext ) { DbgBreakPoint(); }
}
DebugTrace( 0, Dbg, "Dequeued IRP Context %08lx\n", CONTAINING_RECORD( pListEntry, IRP_CONTEXT, NextRequest ) );
#ifdef MSWDBG
pNpScb->RequestDequeued = TRUE; #endif
#endif
ClearFlag( pIrpContext->Flags, IRP_FLAG_ON_SCB_QUEUE );
//
// Give the next IRP context on the SCB queue a chance to run.
//
KickQueue( pNpScb );
DebugTrace(-1, Dbg, "NwDequeueIrpContext\n", 0); return; }
VOID NwCancelIrp ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++
Routine Description:
This routine implements the cancel function for an IRP being processed by the redirector.
Arguments:
DeviceObject - ignored
Irp - Supplies the Irp being cancelled.
Return Value:
None.
--*/
{ PLIST_ENTRY listEntry, nextListEntry; KIRQL OldIrql; PIRP_CONTEXT pTestIrpContext; PIRP pTestIrp;
UNREFERENCED_PARAMETER( DeviceObject );
//
// We now need to void the cancel routine and release the io cancel
// spin-lock.
//
IoSetCancelRoutine( Irp, NULL ); IoReleaseCancelSpinLock( Irp->CancelIrql );
//
// Now we have to search for the IRP to cancel everywhere. So just
// look for cancelled IRPs and process them all.
//
//
// Process the Get Message queue.
//
KeAcquireSpinLock( &NwMessageSpinLock, &OldIrql );
for ( listEntry = NwGetMessageList.Flink; listEntry != &NwGetMessageList; listEntry = nextListEntry ) {
nextListEntry = listEntry->Flink;
//
// If the file object of the queued request, matches the file object
// that is being closed, remove the IRP from the queue, and
// complete it with an error.
//
pTestIrpContext = CONTAINING_RECORD( listEntry, IRP_CONTEXT, NextRequest ); pTestIrp = pTestIrpContext->pOriginalIrp;
if ( pTestIrp->Cancel ) { RemoveEntryList( listEntry ); NwCompleteRequest( pTestIrpContext, STATUS_CANCELLED ); }
}
KeReleaseSpinLock( &NwMessageSpinLock, OldIrql );
//
// Process the set of SCB IRP queues.
//
//
// And return to our caller
//
return; }
PMINI_IRP_CONTEXT AllocateMiniIrpContext ( PIRP_CONTEXT IrpContext ) /*++
Routine Description:
This routine allocates an IRP, a buffer, and an MDL for sending a burst write fragment.
Arguments:
None.
Return Value:
Irp - The allocated and initialized IRP. NULL - The IRP allocation failed.
--*/
{ PMINI_IRP_CONTEXT MiniIrpContext; PIRP Irp = NULL; PMDL Mdl1 = NULL, Mdl2 = NULL; PVOID Buffer = NULL; PLIST_ENTRY ListEntry;
ListEntry = ExInterlockedRemoveHeadList( &MiniIrpContextList, &IrpContextInterlock);
if ( ListEntry == NULL) {
try { MiniIrpContext = ALLOCATE_POOL_EX( NonPagedPool, sizeof( *MiniIrpContext ) );
MiniIrpContext->NodeTypeCode = NW_NTC_MINI_IRP_CONTEXT; MiniIrpContext->NodeByteSize = sizeof( *MiniIrpContext );
Irp = ALLOCATE_IRP( IrpContext->pNpScb->Server.pDeviceObject->StackSize, FALSE );
if ( Irp == NULL ) { ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); }
Buffer = ALLOCATE_POOL_EX( NonPagedPool, sizeof( NCP_BURST_HEADER ) );
Mdl1 = ALLOCATE_MDL( Buffer, sizeof( NCP_BURST_HEADER ), FALSE, FALSE, NULL ); if ( Mdl1 == NULL ) { ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); }
MmBuildMdlForNonPagedPool( Mdl1 );
//
// Since this MDL can be used to send a packet on any server,
// allocate an MDL large enough for any packet size.
//
Mdl2 = ALLOCATE_MDL( 0, 65535 + PAGE_SIZE - 1, FALSE, FALSE, NULL ); if ( Mdl2 == NULL ) { ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES ); }
Mdl1->Next = Mdl2;
MiniIrpContext->Irp = Irp; MiniIrpContext->Buffer = Buffer; MiniIrpContext->Mdl1 = Mdl1; MiniIrpContext->Mdl2 = Mdl2;
InterlockedIncrement( &MiniContextCount );
} except( EXCEPTION_EXECUTE_HANDLER ) {
if ( Buffer != NULL ) { FREE_POOL( Buffer ); }
if ( Irp != NULL ) { FREE_IRP( Irp ); }
if ( Mdl1 != NULL ) { FREE_MDL( Mdl1 ); }
return( NULL ); }
} else {
//
// Record that we have removed an entry from the free list.
//
InterlockedIncrement( &FreeMiniContextCount ); MiniIrpContext = CONTAINING_RECORD( ListEntry, MINI_IRP_CONTEXT, Next );
}
MiniIrpContext->IrpContext = IrpContext;
return( MiniIrpContext ); }
VOID FreeMiniIrpContext ( PMINI_IRP_CONTEXT MiniIrpContext ) /*++
Routine Description:
This routine frees a mini IRP Context.
Arguments:
MiniIrpContext - The mini IRP context to free.
Return Value:
None.
--*/ { InterlockedDecrement( &MiniContextCount );
if ( InterlockedDecrement( &FreeMiniContextCount ) >= 0 ) {
//
// Ok to keep this mini irp context. Just queue it to the free list.
//
MmPrepareMdlForReuse( MiniIrpContext->Mdl2 );
ExInterlockedInsertTailList( &MiniIrpContextList, &MiniIrpContext->Next, &IrpContextInterlock );
} else {
//
// We already have as many free context as we allow so destroy
// this context. Restore FreeContextCount to its original value.
//
InterlockedIncrement( &FreeContextCount );
FREE_POOL( MiniIrpContext->Buffer ); FREE_MDL( MiniIrpContext->Mdl2 ); FREE_MDL( MiniIrpContext->Mdl1 ); FREE_IRP( MiniIrpContext->Irp );
FREE_POOL( MiniIrpContext ); } }
PWORK_CONTEXT AllocateWorkContext ( VOID ) /*++
Routine Description:
Allocates a work queue structure, and initializes it.
Arguments:
None.
Return Value:
PWORK_CONTEXT - Newly allocated Work Context.
--*/ { PWORK_CONTEXT pWorkContext;
try { pWorkContext = ALLOCATE_POOL_EX(NonPagedPool, sizeof(WORK_CONTEXT)); RtlFillMemory( pWorkContext, sizeof(WORK_CONTEXT), 0 ); pWorkContext->NodeTypeCode = NW_NTC_WORK_CONTEXT; pWorkContext->NodeByteSize = sizeof(WORK_CONTEXT);
return pWorkContext; } except( EXCEPTION_EXECUTE_HANDLER ) {
DebugTrace( 0, Dbg, "Failed to allocate work context\n", 0 );
return NULL;
} }
VOID FreeWorkContext ( PWORK_CONTEXT WorkContext ) /*++
Routine Description:
Free the supplied work context.
Arguments:
PWORK_CONTEXT IrpContext - Work Context to free.
Return Value:
None
--*/ {
ASSERT( WorkContext->NodeTypeCode == NW_NTC_WORK_CONTEXT ); FREE_POOL(WorkContext); } VOID SpawnWorkerThread ( VOID ) /*++
Routine Description:
Create our own worker thread which will service reroute and reconnect attempts.
Arguments:
None.
Return Value:
None.
--*/ { NTSTATUS status;
status = PsCreateSystemThread( &WorkerThreadHandle, PROCESS_ALL_ACCESS, // Access mask
NULL, // object attributes
NULL, // Process handle
NULL, // client id
(PKSTART_ROUTINE) WorkerThread, // Start routine
NULL // Startcontext
);
if ( !NT_SUCCESS(status) ) {
//
// If we can't create the worker thread, it means that we
// cannot service reconnect or reroute attempts. It is a
// non-critical error.
//
DebugTrace( 0, Dbg, "SpawnWorkerThread: Can't create worker thread", 0 ); WorkerThreadRunning = FALSE; } else {
DebugTrace( 0, Dbg, "SpawnWorkerThread: created worker thread", 0 ); WorkerThreadRunning = TRUE; } }
VOID WorkerThread ( VOID ) {
PLIST_ENTRY listentry; PWORK_CONTEXT workContext; PIRP_CONTEXT pIrpContext; NODE_WORK_CODE workCode; PNONPAGED_SCB OriginalNpScb = NULL;
PAGED_CODE();
DebugTrace( 0, Dbg, "Worker thread \n", 0 );
IoSetThreadHardErrorMode( FALSE );
while (TRUE) { //
// Check to see if we have any work to do.
//
listentry = KeRemoveQueue ( &KernelQueue, // Kernel queue object
KernelMode, // Processor wait mode
NULL // No timeout
);
ASSERT( listentry != (PVOID) STATUS_TIMEOUT);
//
// We have atleast one reroute attempt to look into. Get the address of the
// work item.
//
workContext = CONTAINING_RECORD ( listentry, WORK_CONTEXT, Next );
pIrpContext = workContext->pIrpC; workCode = workContext->NodeWorkCode;
if (pIrpContext) { OriginalNpScb = pIrpContext->pNpScb; }
//
// We don't need the work context anymore
//
FreeWorkContext( workContext );
//
// The work which this thread does can be one of the following:
//
// - Attempt a reroute
// - Attempt a reconnect
// - Terminate itself
//
switch (workCode) {
case NWC_NWC_REROUTE: { ASSERT(BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_REROUTE_IN_PROGRESS )); DebugTrace( 0, Dbg, "worker got reroute work for scb 0x%x.\n", OriginalNpScb ); if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_BURST_PACKET ) ) { NewRouteBurstRetry( pIrpContext ); } else { NewRouteRetry( pIrpContext ); }
NwDereferenceScb( OriginalNpScb ); ClearFlag( pIrpContext->Flags, IRP_FLAG_REROUTE_IN_PROGRESS ); break; } case NWC_NWC_RECONNECT: { DebugTrace( 0, Dbg, "worker got reconnect work.\n", 0 ); ReconnectRetry( pIrpContext ); break; } case NWC_NWC_TERMINATE: { DebugTrace( 0, Dbg, "Terminated worker thread.\n", 0 ); //
// Flush any remaining work items out of the work queue...
//
while (listentry != NULL) {
listentry = KeRundownQueue( &KernelQueue ); DebugTrace( 0, Dbg, "Residual workitem in q %X.\n",listentry ); }
//
// and terminate yourself.
//
WorkerThreadRunning = FALSE; PsTerminateSystemThread( STATUS_SUCCESS );
break; } default: { //
// There is something wrong here.
//
DebugTrace( 0, Dbg, "Unknown work code...ignoring\n", 0 ); } } } }
VOID TerminateWorkerThread ( VOID ) { PWORK_CONTEXT workContext = NULL; LARGE_INTEGER timeout; NTSTATUS status;
if (WorkerThreadRunning == TRUE) {
//
// set a 5 second timeout for retrying allocation failures
//
timeout.QuadPart = (LONGLONG) ( NwOneSecond * 5 * (-1) ); //
// Prepare the work context
//
workContext = AllocateWorkContext();
while ( workContext == NULL) { KeDelayExecutionThread( KernelMode, FALSE, &timeout ); workContext = AllocateWorkContext(); }
workContext->NodeWorkCode = NWC_NWC_TERMINATE; workContext->pIrpC = NULL; //
// and queue it.
//
KeInsertQueue( &KernelQueue, &workContext->Next );
//
// We now have to wait until the thread terminates itself.
//
DebugTrace( 0, Dbg, "TerminateWorkerThread: Waiting for thread termination.\n", 0 );
do {
status = ZwWaitForSingleObject( WorkerThreadHandle, FALSE, NULL // No timeout
);
} while ( !NT_SUCCESS( status ) );
DebugTrace( 0, Dbg, "TerminateWorkerThread: Wait returned with 0x%x\n", status );
status = ZwClose( WorkerThreadHandle );
}
}
|