345 lines
7.5 KiB
C
345 lines
7.5 KiB
C
|
||
/*++
|
||
|
||
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 );
|
||
}
|