summaryrefslogtreecommitdiffstats
path: root/private/nw/rdr/workque.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/rdr/workque.c')
-rw-r--r--private/nw/rdr/workque.c829
1 files changed, 829 insertions, 0 deletions
diff --git a/private/nw/rdr/workque.c b/private/nw/rdr/workque.c
new file mode 100644
index 000000000..aba699e32
--- /dev/null
+++ b/private/nw/rdr/workque.c
@@ -0,0 +1,829 @@
+/*++
+
+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
+
+//
+// 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 )
+
+#ifndef QFE_BUILD
+#pragma alloc_text( PAGE1, NwDequeueIrpContext )
+#pragma alloc_text( PAGE1, AllocateMiniIrpContext )
+#pragma alloc_text( PAGE1, FreeMiniIrpContext )
+#endif
+
+#endif
+
+#if 0 // Not pageable
+AllocateIrpContext
+FreeIrpContext
+NwCompleteRequest
+
+// 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
+ ExInterlockedIncrementLong(&FreeContextCount,&ContextInterlock);
+
+ 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 ) );
+ }
+
+ ExInterlockedIncrementLong(&ContextCount,&ContextInterlock);
+
+ //
+ // 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
+
+ ExInterlockedDecrementLong(&ContextCount, &ContextInterlock);
+
+ if ( ExInterlockedDecrementLong(&FreeContextCount, &ContextInterlock) !=
+ ResultNegative ) {
+
+ //
+ // 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.
+ //
+
+ ExInterlockedIncrementLong( &FreeContextCount, &ContextInterlock );
+
+ 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
+
+ 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.
+ //
+
+ // BUGBUG. Not done yet. Needed?
+
+ //
+ // 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;
+
+ ExInterlockedIncrementLong( &MiniContextCount, &ContextInterlock );
+
+ } 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.
+ //
+
+ ExInterlockedIncrementLong( &FreeMiniContextCount, &ContextInterlock );
+ 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.
+
+--*/
+{
+ ExInterlockedDecrementLong( &MiniContextCount, &ContextInterlock );
+
+ if ( ExInterlockedDecrementLong( &FreeMiniContextCount, &ContextInterlock) !=
+ ResultNegative ) {
+
+ //
+ // 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.
+ //
+
+ ExInterlockedIncrementLong( &FreeContextCount, &ContextInterlock );
+
+ FREE_POOL( MiniIrpContext->Buffer );
+ FREE_MDL( MiniIrpContext->Mdl2 );
+ FREE_MDL( MiniIrpContext->Mdl1 );
+ FREE_IRP( MiniIrpContext->Irp );
+
+ FREE_POOL( MiniIrpContext );
+ }
+}
+