mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1167 lines
25 KiB
1167 lines
25 KiB
/*++
|
|
|
|
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 );
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|