summaryrefslogtreecommitdiffstats
path: root/private/nw/rdr/timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'private/nw/rdr/timer.c')
-rw-r--r--private/nw/rdr/timer.c537
1 files changed, 537 insertions, 0 deletions
diff --git a/private/nw/rdr/timer.c b/private/nw/rdr/timer.c
new file mode 100644
index 000000000..e5ca1204c
--- /dev/null
+++ b/private/nw/rdr/timer.c
@@ -0,0 +1,537 @@
+/*++
+
+Copyright (c) 1993 Microsoft Corporation
+
+Module Name:
+
+ timer.c
+
+Abstract:
+
+ This module contains code which implements the receive and send timeouts
+ for each connection.
+
+Author:
+
+ Colin Watson (ColinW) 21-Feb-1993
+
+Environment:
+
+ Kernel mode
+
+Revision History:
+
+--*/
+
+#include "procs.h"
+
+//
+// The debug trace level
+//
+
+#define Dbg (DEBUG_TRACE_TIMER)
+
+LARGE_INTEGER DueTime;
+KDPC NwDpc; // DPC object for timeouts.
+KTIMER Timer; // kernel timer for this request.
+ULONG ScavengerTickCount;
+
+BOOLEAN WorkerRunning = FALSE;
+WORK_QUEUE_ITEM WorkItem;
+
+#ifdef NWDBG
+BOOLEAN DisableTimer = FALSE;
+#endif
+
+//
+// When we want to stop the timer, set TimerStop to TRUE. When the timer
+// is stopped TimerStopped will be set to the signalled state.
+//
+
+BOOLEAN TimerStop;
+KEVENT TimerStopped;
+
+VOID
+TimerDPC(
+ IN PKDPC Dpc,
+ IN PVOID Context,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2
+ );
+
+#ifdef ALLOC_PRAGMA
+#pragma alloc_text( PAGE, StartTimer )
+#pragma alloc_text( PAGE, StopTimer )
+
+#endif
+
+
+VOID
+StartTimer(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine starts the timer ticking.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ //
+ // We need 18.21 ticks per second
+ //
+
+ DueTime.QuadPart = (( 100000 * MILLISECONDS ) / 1821) * -1;
+
+ //
+ // This is the first connection with timeouts specified.
+ // Set up the timer so that every 500 milliseconds we scan all the
+ // connections for timed out receive and sends.
+ //
+
+ TimerStop = FALSE;
+
+ KeInitializeEvent( &TimerStopped, SynchronizationEvent, FALSE );
+ KeInitializeDpc( &NwDpc, TimerDPC, NULL );
+ KeInitializeTimer( &Timer );
+
+ (VOID)KeSetTimer(&Timer, DueTime, &NwDpc);
+
+ DebugTrace(+0, Dbg, "StartTimer\n", 0);
+}
+
+
+VOID
+StopTimer(
+ VOID
+ )
+/*++
+
+Routine Description:
+
+ This routine stops the timer. It blocks until the timer has stopped.
+
+Arguments:
+
+ None.
+
+Return Value:
+
+ None.
+
+--*/
+{
+ PAGED_CODE();
+
+ if (TimerStop == FALSE) {
+ TimerStop = TRUE;
+
+ DebugTrace(+0, Dbg, "StopTimer\n", 0);
+ KeWaitForSingleObject (&TimerStopped, Executive, KernelMode, FALSE, NULL);
+ }
+}
+
+
+VOID
+TimerDPC(
+ IN PKDPC Dpc,
+ IN PVOID Context,
+ IN PVOID SystemArgument1,
+ IN PVOID SystemArgument2
+ )
+/*++
+
+Routine Description:
+
+ This routine is called to search for timed out send and receive
+ requests. This routine is called at DPC level.
+
+Arguments:
+
+ Dpc - Unused.
+ Context - Unused.
+ SystemArgument1 - Unused.
+ SystemArgument2 - Unused.
+
+Return Value:
+
+ None.
+
+--*/
+
+{
+ PLIST_ENTRY ScbQueueEntry;
+ PLIST_ENTRY NextScbQueueEntry;
+ PLIST_ENTRY IrpContextEntry;
+ PLIST_ENTRY NextIrpContextEntry;
+ SHORT RetryCount;
+ PIRP_CONTEXT pIrpContext;
+ LARGE_INTEGER CurrentTime = {0, 0};
+ WCHAR AnonymousName[] = L"UNKNOWN";
+ PWCHAR ServerLogName;
+
+ if ( TimerStop ) {
+ KeSetEvent( &TimerStopped, 0, FALSE );
+ return;
+ }
+
+ //
+ // For each Server see if there is a timeout to process.
+ //
+
+#ifdef NWDBG
+ if ( DisableTimer ) {
+ //
+ // Reset the timer to run for another tick.
+ //
+
+ (VOID)KeSetTimer ( &Timer, DueTime, &NwDpc);
+
+ return;
+ }
+#endif
+
+ //DebugTrace(+1, Dbg, "TimerDpc....\n", 0);
+
+ //
+ // Scan through the Scb's looking timed out requests.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &ScbSpinLock );
+
+ ScbQueueEntry = ScbQueue.Flink;
+
+ if (ScbQueueEntry != &ScbQueue) {
+ PNONPAGED_SCB pNpScb = CONTAINING_RECORD(ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks);
+ NwQuietReferenceScb( pNpScb );
+ }
+
+ for (;
+ ScbQueueEntry != &ScbQueue ;
+ ScbQueueEntry = NextScbQueueEntry ) {
+
+ PNONPAGED_SCB pNpScb = CONTAINING_RECORD(ScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks);
+
+ // Obtain a pointer to the next SCB in the SCB list before
+ // dereferencing the current one.
+ //
+
+ NextScbQueueEntry = pNpScb->ScbLinks.Flink;
+
+ if (NextScbQueueEntry != &ScbQueue) {
+ PNONPAGED_SCB pNextNpScb = CONTAINING_RECORD(NextScbQueueEntry,
+ NONPAGED_SCB,
+ ScbLinks);
+ //
+ // Reference the next entry in the list to ensure the scavenger
+ // doesn't put it on another list or destroy it.
+ //
+
+ NwQuietReferenceScb( pNextNpScb );
+ }
+
+ KeReleaseSpinLockFromDpcLevel( &ScbSpinLock );
+
+ //
+ // Acquire the Scb specific spin lock to protect access
+ // the the Scb fields.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &pNpScb->NpScbSpinLock );
+
+ //
+ // Look at the first request on the queue only (since it is
+ // the only active request).
+ //
+
+ if ( ( !IsListEmpty( &pNpScb->Requests )) &&
+ ( !pNpScb->Sending ) &&
+ ( pNpScb->OkToReceive ) &&
+ ( --pNpScb->TimeOut <= 0 ) ) {
+
+ PIRP_CONTEXT pIrpContext;
+
+ //
+ // This request has timed out. Try to retransmit the request.
+ //
+
+ pIrpContext = CONTAINING_RECORD(
+ pNpScb->Requests.Flink,
+ IRP_CONTEXT,
+ NextRequest);
+
+ pNpScb->TimeOut = pNpScb->MaxTimeOut;
+
+ //
+ // Check the retry count while we own the spin lock.
+ //
+
+ RetryCount = --pNpScb->RetryCount;
+ NwQuietDereferenceScb( pNpScb );
+
+ //
+ // Set OkToReceive to FALSE, so that if we receive a response
+ // right now, our receive handler won't handle the response
+ // and cause IRP context to be freed.
+ //
+
+ pNpScb->OkToReceive = FALSE;
+ KeReleaseSpinLockFromDpcLevel( &pNpScb->NpScbSpinLock );
+
+ if ( pIrpContext->pOriginalIrp->Cancel ) {
+
+ //
+ // This IRP has been cancelled. Call the callback routine.
+ //
+ // BUGBUG - This will cause a timeout error.
+ //
+
+ DebugTrace(+0, Dbg, "Timer cancel IRP %X\n", pIrpContext->pOriginalIrp );
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+
+ } else if ( RetryCount >= 0) {
+
+ //
+ // We're not out of retries. Resend the request packet.
+ //
+ // First adjust the send timeout up. Adjust the timeout
+ // more slowly on a close by server.
+ //
+
+ if ( pNpScb->SendTimeout < pNpScb->MaxTimeOut ) {
+ if ( pNpScb->TickCount <= 4 ) {
+ pNpScb->SendTimeout++;
+ } else {
+ pNpScb->SendTimeout = pNpScb->SendTimeout * 3 / 2;
+ if ( pNpScb->SendTimeout > pNpScb->MaxTimeOut ) {
+ pNpScb->SendTimeout = pNpScb->MaxTimeOut;
+ }
+ }
+ }
+
+ pNpScb->TimeOut = pNpScb->SendTimeout;
+ DebugTrace(+0, Dbg, "Adjusting send timeout: %x\n", pIrpContext );
+ DebugTrace(+0, Dbg, "Adjusting send timeout to: %d\n", pNpScb->TimeOut );
+
+ if ( pIrpContext->TimeoutRoutine != NULL ) {
+
+ DebugTrace(+0, Dbg, "Timeout Routine, retry %x\n", RetryCount+1);
+ DebugTrace(+0, Dbg, "Calling TimeoutRoutine, %x\n", pIrpContext->TimeoutRoutine);
+ pIrpContext->TimeoutRoutine( pIrpContext );
+
+ } else {
+
+ DebugTrace(+0, Dbg, "Resending Packet, retry %x\n", RetryCount+1);
+ PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_RETRY_SEND );
+ SendNow( pIrpContext );
+ }
+
+ Stats.FailedSessions++;
+
+ } else {
+
+ ASSERT( pIrpContext->pEx != NULL );
+
+ //
+ // We are out of retries.
+ //
+
+ if ( BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED ) ||
+ ( NwAbsoluteTotalWaitTime != 0 &&
+ pNpScb->TotalWaitTime >= NwAbsoluteTotalWaitTime ) ) {
+
+
+ ClearFlag( pIrpContext->Flags, IRP_FLAG_RETRY_SEND );
+
+ //
+ // He have already attempted to reroute the request.
+ // Give up.
+ //
+
+ DebugTrace(+0, Dbg, "Abandon Exchange\n", 0 );
+
+ if ( pIrpContext->pNpScb != &NwPermanentNpScb ) {
+
+ //
+ // Reset to the attaching state. If the server
+ // is dead, the next attempt to open a handle will
+ // fail with a better error than unexpected network
+ // error.
+ //
+
+ pIrpContext->pNpScb->State = SCB_STATE_ATTACHING;
+
+ //
+ // Determine the CurrentTime. We need to know if
+ // TimeOutEventInterval minutes have passed before
+ // we log the next time-out event.
+ //
+
+ KeQuerySystemTime( &CurrentTime );
+
+ if ( CanLogTimeOutEvent( pNpScb->NwNextEventTime,
+ CurrentTime
+ )) {
+
+ if ( pNpScb->ServerName.Buffer != NULL ) {
+ ServerLogName = pNpScb->ServerName.Buffer;
+ } else {
+ ServerLogName = &AnonymousName[0];
+ }
+
+ Error(
+ EVENT_NWRDR_TIMEOUT,
+ STATUS_UNEXPECTED_NETWORK_ERROR,
+ NULL,
+ 0,
+ 1,
+ ServerLogName );
+
+ //
+ // Set the LastEventTime to the CurrentTime
+ //
+
+ UpdateNextEventTime(
+ pNpScb->NwNextEventTime,
+ CurrentTime,
+ TimeOutEventInterval
+ );
+ }
+
+ }
+
+ pIrpContext->ResponseParameters.Error = ERROR_UNEXP_NET_ERR;
+ pIrpContext->pEx( pIrpContext, 0, NULL );
+
+ } else {
+
+ //
+ // Attempt to reroute the request.
+ //
+
+ SetFlag( pIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
+
+ if ( BooleanFlagOn(
+ pIrpContext->Flags,
+ IRP_FLAG_BURST_PACKET ) ) {
+ pIrpContext->PostProcessRoutine = NewRouteBurstRetry;
+ } else {
+ pIrpContext->PostProcessRoutine = NewRouteRetry;
+ }
+
+ NwPostToFsp( pIrpContext, FALSE );
+ }
+ }
+
+ } else {
+
+ if ( ( !IsListEmpty( &pNpScb->Requests )) &&
+ ( !pNpScb->Sending ) &&
+ ( pNpScb->OkToReceive ) ) {
+
+ DebugTrace( 0, Dbg, "TimeOut %d\n", pNpScb->TimeOut );
+ }
+
+ //
+ // Nothing to do for this SCB. Dereference this SCB and
+ // release the spin lock.
+ //
+
+ KeReleaseSpinLockFromDpcLevel( &pNpScb->NpScbSpinLock );
+ NwQuietDereferenceScb( pNpScb );
+ }
+
+ KeAcquireSpinLockAtDpcLevel( &ScbSpinLock );
+
+ }
+
+ KeReleaseSpinLockFromDpcLevel( &ScbSpinLock );
+
+ //
+ // Now see if the scavenger routine needs to be run.
+ // Only ever queue one workitem.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &NwScavengerSpinLock );
+
+ NwScavengerTickCount++;
+ if (( !WorkerRunning ) &&
+ ( NwScavengerTickCount > NwScavengerTickRunCount )) {
+
+ ExInitializeWorkItem( &WorkItem, NwScavengerRoutine, &WorkItem );
+ ExQueueWorkItem( &WorkItem, DelayedWorkQueue );
+ NwScavengerTickCount = 0;
+ WorkerRunning = TRUE;
+ }
+
+ KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
+
+ //
+ // Scan the list of pending locks, looking for locks to retry.
+ //
+
+ KeAcquireSpinLockAtDpcLevel( &NwPendingLockSpinLock );
+
+ for (IrpContextEntry = NwPendingLockList.Flink ;
+ IrpContextEntry != &NwPendingLockList ;
+ IrpContextEntry = NextIrpContextEntry ) {
+
+ NextIrpContextEntry = IrpContextEntry->Flink;
+ pIrpContext = CONTAINING_RECORD( IrpContextEntry, IRP_CONTEXT, NextRequest );
+
+ //
+ // BUGBUG surely we can't use the key like this to control the number
+ // of retries.
+ //
+
+ if ( --pIrpContext->Specific.Lock.Key <= 0 ) {
+
+ //
+ // Remove the IRP Context from the queue and reattempt the lock.
+ // Set the SEQUENCE_NO_REQUIRED flag so that the packet gets
+ // renumbered.
+ //
+
+ RemoveEntryList( &pIrpContext->NextRequest );
+ SetFlag( pIrpContext->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
+ PrepareAndSendPacket( pIrpContext );
+ }
+
+ }
+
+ KeReleaseSpinLockFromDpcLevel( &NwPendingLockSpinLock );
+
+ //
+ // Reset the timer to run for another tick.
+ //
+
+ (VOID)KeSetTimer ( &Timer, DueTime, &NwDpc);
+
+ //DebugTrace(-1, Dbg, "TimerDpc\n", 0);
+ return;
+
+ UNREFERENCED_PARAMETER (Dpc);
+ UNREFERENCED_PARAMETER (Context);
+ UNREFERENCED_PARAMETER (SystemArgument1);
+ UNREFERENCED_PARAMETER (SystemArgument2);
+
+}
+
+