|
|
/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
timer.c
Abstract:
This module contains code which implements the receive and send timeouts for each connection. Netbios timeouts are specified in 0.5 second units.
For each application using Netbios there is a single timer started when the first connection specifies a non-zero rto or sto. This regular 1 second pulse is used for all connections by this application. It is stopped when the application exits (and closes the connection to \Device\Netbios).
If a send timesout the connection is disconnected as per Netbios 3.0. Individual receives can timeout without affecting the session.
Author:
Colin Watson (ColinW) 15-Sep-1991
Environment:
Kernel mode
Revision History:
--*/
#include "nb.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NbStartTimer)
#pragma alloc_text(PAGE, NbTimer)
#endif
VOID RunTimerForLana( PFCB pfcb, PLANA_INFO plana, int index );
VOID NbStartTimer( IN PFCB pfcb ) /*++
Routine Description:
This routine starts the timer ticking for this FCB.
Arguments:
pfcb - Pointer to our FCB.
Return Value:
none.
--*/ { LARGE_INTEGER DueTime;
PAGED_CODE();
DueTime.QuadPart = Int32x32To64( 500, -MILLISECONDS );
// 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.
//
IF_NBDBG (NB_DEBUG_CALL) { NbPrint( ("Start the timer for fcb: %lx\n", pfcb)); }
if ( pfcb->TimerRunning == TRUE ) { return; }
KeInitializeDpc ( &pfcb->Dpc, NbTimerDPC, pfcb);
KeInitializeTimer (&pfcb->Timer);
pfcb->TimerRunning = TRUE;
(VOID)KeSetTimer ( &pfcb->Timer, DueTime, &pfcb->Dpc); }
VOID NbTimerDPC( 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 raised Irql.
Arguments:
Context - Pointer to our FCB.
Return Value:
none.
--*/
{ PFCB pfcb = (PFCB) Context;
IoQueueWorkItem( pfcb->WorkEntry, NbTimer, DelayedWorkQueue, pfcb );
UNREFERENCED_PARAMETER (Dpc); UNREFERENCED_PARAMETER (SystemArgument1); UNREFERENCED_PARAMETER (SystemArgument2); }
VOID NbTimer( PDEVICE_OBJECT DeviceObject, PVOID Context ) { ULONG lana_index; int index; PFCB pfcb = (PFCB) Context;
LARGE_INTEGER DueTime;
PAGED_CODE();
//
// For each network adapter that is allocated, scan each connection.
//
LOCK_RESOURCE(pfcb);
IF_NBDBG (NB_DEBUG_TIMER) { NbPrint((" NbTimeout\n" )); }
if ( pfcb->TimerRunning != TRUE ) {
//
// Driver is being closed. We are trying to cancel the timer
// but the dpc was already fired. Set the timer cancelled event
// to the signalled state and exit.
//
UNLOCK_RESOURCE(pfcb); KeSetEvent( pfcb->TimerCancelled, 0, FALSE); return; }
for ( lana_index = 0; lana_index <= pfcb->MaxLana; lana_index++ ) {
// For each network.
PLANA_INFO plana = pfcb->ppLana[lana_index];
if (( plana != NULL ) && ( plana->Status == NB_INITIALIZED)) {
// For each connection on that network.
for ( index = 1; index <= MAXIMUM_CONNECTION; index++) {
if ( plana->ConnectionBlocks[index] != NULL ) {
RunTimerForLana(pfcb, plana, index); } } }
}
DueTime.QuadPart = Int32x32To64( 500, -MILLISECONDS );
(VOID)KeSetTimer ( &pfcb->Timer, DueTime, &pfcb->Dpc);
UNLOCK_RESOURCE(pfcb); }
VOID RunTimerForLana( PFCB pfcb, PLANA_INFO plana, int index ) {
KIRQL OldIrql; // Used when SpinLock held.
PPCB ppcb; PCB pcb;
ppcb = &plana->ConnectionBlocks[index]; pcb = *ppcb;
if (( pcb->Status != SESSION_ESTABLISHED ) && ( pcb->Status != HANGUP_PENDING )) { // Only examine valid connections.
return; }
LOCK_SPINLOCK( pfcb, OldIrql );
if (( pcb->ReceiveTimeout != 0 ) && ( !IsListEmpty( &pcb->ReceiveList))) { PDNCB pdncb; PLIST_ENTRY ReceiveEntry = pcb->ReceiveList.Flink;
pdncb = CONTAINING_RECORD( ReceiveEntry, DNCB, ncb_next);
if ( pdncb->tick_count <= 1) { PIRP Irp = pdncb->irp;
// Read request timed out.
IF_NBDBG (NB_DEBUG_TIMER) { NbPrint(("Timeout Read pncb: %lx\n", pdncb)); }
NCB_COMPLETE( pdncb, NRC_CMDTMO );
RemoveEntryList( &pdncb->ncb_next );
IoAcquireCancelSpinLock(&Irp->CancelIrql);
//
// Remove the cancel request for this IRP. If its cancelled then its
// ok to just process it because we will be returning it to the caller.
//
Irp->Cancel = FALSE;
IoSetCancelRoutine(Irp, NULL);
IoReleaseCancelSpinLock(Irp->CancelIrql);
// repair the Irp so that the NCB gets copied back.
Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = FIELD_OFFSET( DNCB, ncb_cmd_cplt ); IoCompleteRequest( Irp, IO_NETWORK_INCREMENT);
} else { IF_NBDBG (NB_DEBUG_TIMER) { NbPrint(("Tick Read pdncb: %lx, count: %x\n", pdncb, pdncb->tick_count)); } pdncb->tick_count -= 1; } }
if (( pcb->SendTimeout != 0 ) && (!IsListEmpty( &pcb->SendList))) { PDNCB pdncb; PLIST_ENTRY SendEntry = pcb->SendList.Flink;
pdncb = CONTAINING_RECORD( SendEntry, DNCB, ncb_next); if ( pdncb->tick_count <= 1) { // Send request timed out- hangup connection.
IF_NBDBG (NB_DEBUG_TIMER) { NbPrint(("Timeout send pncb: %lx\n", pdncb)); }
NCB_COMPLETE( pdncb, NRC_CMDTMO );
pcb->Status = SESSION_ABORTED;
UNLOCK_SPINLOCK( pfcb, OldIrql );
CloseConnection( ppcb, 1000 );
//
// No need to worry about looking for a timed out hangup, the session
// will be closed as soon as the transport cancels the send.
//
return;
} else { IF_NBDBG (NB_DEBUG_TIMER) { NbPrint(("Tick Write pdncb: %lx, count: %x\n", pdncb, pdncb->tick_count)); } pdncb->tick_count -= 1; } }
if (( pcb->pdncbHangup != NULL ) && ( pcb->Status == HANGUP_PENDING )) { if ( pcb->pdncbHangup->tick_count <= 1) { IF_NBDBG (NB_DEBUG_TIMER) { NbPrint(("Timeout send pncb: %lx\n", pcb->pdncbHangup)); }
NCB_COMPLETE( pcb->pdncbHangup, NRC_CMDTMO );
UNLOCK_SPINLOCK( pfcb, OldIrql );
AbandonConnection( ppcb );
LOCK_SPINLOCK( pfcb, OldIrql );
} else { IF_NBDBG (NB_DEBUG_TIMER) { NbPrint(("Tick Hangup pdncb: %lx, count: %x\n", pcb->pdncbHangup, pcb->pdncbHangup->tick_count)); } pcb->pdncbHangup->tick_count -= 1; } }
UNLOCK_SPINLOCK( pfcb, OldIrql ); }
|