|
|
/*++
Copyright (c) 2000-2000 Microsoft Corporation
Module Name:
Connect.c
Abstract:
This module implements Connect handling routines for the PGM Transport
Author:
Mohammad Shabbir Alam (MAlam) 3-30-2000
Revision History:
--*/
#include "precomp.h"
#include <tcpinfo.h> // for AO_OPTION_xxx, TCPSocketOption
//******************* Pageable Routine Declarations ****************
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PgmCreateConnection)
#endif
//******************* Pageable Routine Declarations ****************
//----------------------------------------------------------------------------
NTSTATUS PgmCreateConnection( IN tPGM_DEVICE *pPgmDevice, IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp, IN PFILE_FULL_EA_INFORMATION TargetEA ) /*++
Routine Description:
This routine is called to create a connection context for the client At this time, we do not knwo which address which connection will be associated with, nor do we know whether it will be a sender or a receiver.
Arguments:
IN pPgmDevice -- Pgm's Device object context IN pIrp -- Client's request Irp IN pIrpSp -- current request's stack pointer IN TargetEA -- contains the client's Connect context
Return Value:
NTSTATUS - Final status of the set event operation
--*/ { NTSTATUS status; CONNECTION_CONTEXT ConnectionContext; tCOMMON_SESSION_CONTEXT *pSession = NULL;
PAGED_CODE();
if (TargetEA->EaValueLength < sizeof(CONNECTION_CONTEXT)) { PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmCreateConnection", "(BufferLength=%d < Min=%d)\n", TargetEA->EaValueLength, sizeof(CONNECTION_CONTEXT)); return (STATUS_INVALID_ADDRESS_COMPONENT); }
if (!(pSession = PgmAllocMem (sizeof(tCOMMON_SESSION_CONTEXT), PGM_TAG('0')))) { PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmCreateConnection", "STATUS_INSUFFICIENT_RESOURCES, Requested <%d> bytes\n", sizeof(tCOMMON_SESSION_CONTEXT)); return (STATUS_INSUFFICIENT_RESOURCES); } PgmZeroMemory (pSession, sizeof (tCOMMON_SESSION_CONTEXT));
InitializeListHead (&pSession->Linkage); PgmInitLock (pSession, SESSION_LOCK); pSession->Verify = PGM_VERIFY_SESSION_UNASSOCIATED; PGM_REFERENCE_SESSION_UNASSOCIATED (pSession, REF_SESSION_CREATE, TRUE); pSession->Process = (PEPROCESS) PsGetCurrentProcess();
// the connection context value is stored in the string just after the
// name "connectionContext", and it is most likely unaligned, so just
// copy it out.( 4 bytes of copying ).
PgmCopyMemory (&pSession->ClientSessionContext, (CONNECTION_CONTEXT) &TargetEA->EaName[TargetEA->EaNameLength+1], sizeof (CONNECTION_CONTEXT));
PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmCreateConnection", "pSession=<%x>, ConnectionContext=<%x>\n", pSession, * ((PVOID UNALIGNED *) &TargetEA->EaName[TargetEA->EaNameLength+1]));
// link on to list of open connections for this device so that we
// know how many open connections there are at any time (if we need to know)
// This linkage is only in place until the client does an associate, then
// the connection is unlinked from here and linked to the client ConnectHead.
//
PgmInterlockedInsertTailList (&PgmDynamicConfig.ConnectionsCreated, &pSession->Linkage,&PgmDynamicConfig);
pIrpSp->FileObject->FsContext = pSession; pIrpSp->FileObject->FsContext2 = (PVOID) TDI_CONNECTION_FILE;
return (STATUS_SUCCESS); }
//----------------------------------------------------------------------------
VOID PgmCleanupSession( IN tCOMMON_SESSION_CONTEXT *pSession, IN PVOID Unused1, IN PVOID Unused2 ) /*++
Routine Description:
This routine performs the cleanup operation for a session handle once the RefCount goes to 0. It is called after a cleanup has been requested on this handle This routine has to be called at Passive Irql since we will need to do some file/section handle manipulation here.
Arguments:
IN pSession -- the session object to be cleaned up
Return Value:
NONE
--*/ { PIRP pIrpCleanup; PGMLockHandle OldIrq;
PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmCleanupSession", "Cleanup Session=<%x>\n", pSession);
if ((pSession->SessionFlags & PGM_SESSION_FLAG_SENDER) && (pSession->pSender->SendDataBufferMapping)) { PgmUnmapAndCloseDataFile (pSession); pSession->pSender->SendDataBufferMapping = NULL; }
PgmLock (pSession, OldIrq); pSession->Process = NULL; // To remember that we have cleanedup
if (pIrpCleanup = pSession->pIrpCleanup) { pSession->pIrpCleanup = NULL; PgmUnlock (pSession, OldIrq); PgmIoComplete (pIrpCleanup, STATUS_SUCCESS, 0); } else { PgmUnlock (pSession, OldIrq); } }
//----------------------------------------------------------------------------
VOID PgmDereferenceSessionCommon( IN tCOMMON_SESSION_CONTEXT *pSession, IN ULONG SessionType, IN ULONG RefContext ) /*++
Routine Description:
This routine is called as a result of a dereference on a session object
Arguments:
IN pSession -- the Session object IN SessionType -- basically, whether it is PGM_VERIFY_SESSION_SEND or PGM_VERIFY_SESSION_RECEIVE IN RefContext -- the context for which this session object was referenced earlier
Return Value:
NONE
--*/ { NTSTATUS status; PGMLockHandle OldIrq; PIRP pIrpCleanup; LIST_ENTRY *pEntry; tNAK_FORWARD_DATA *pNak;
PgmLock (pSession, OldIrq);
ASSERT (PGM_VERIFY_HANDLE2 (pSession, SessionType, PGM_VERIFY_SESSION_DOWN)); ASSERT (pSession->RefCount); // Check for too many derefs
ASSERT (pSession->ReferenceContexts[RefContext]--);
if (--pSession->RefCount) { PgmUnlock (pSession, OldIrq); return; }
PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmDereferenceSessionCommon", "pSession=<%x> Derefenced out, pIrpCleanup=<%x>\n", pSession, pSession->pIrpCleanup);
ASSERT (!pSession->pAssociatedAddress);
pIrpCleanup = pSession->pIrpCleanup; //
// Sonce we may have a lot of memory buffered up, we need
// to free it at non-Dpc
//
PgmUnlock (pSession, OldIrq); if (pSession->pReceiver) { CleanupPendingNaks (pSession, (PVOID) FALSE, NULL); }
//
// Remove from the global list
//
PgmLock (&PgmDynamicConfig, OldIrq); RemoveEntryList (&pSession->Linkage); PgmUnlock (&PgmDynamicConfig, OldIrq);
//
// If we are currently at Dpc, we will have to close the handles
// on a Delayed Worker thread!
//
if (PgmGetCurrentIrql()) { status = PgmQueueForDelayedExecution (PgmCleanupSession, pSession, NULL, NULL, FALSE); if (!NT_SUCCESS (status)) { //
// Apparently we ran out of Resources!
// Complete the cleanup Irp for now and hope that the Close
// can complete the rest of the cleanup
//
PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmDereferenceSessionCommon", "OUT_OF_RSRC, cannot queue Worker request\n", pSession);
if (pIrpCleanup) { pSession->pIrpCleanup = NULL; PgmIoComplete (pIrpCleanup, STATUS_SUCCESS, 0); } } } else { PgmCleanupSession (pSession, NULL, NULL); } }
//----------------------------------------------------------------------------
NTSTATUS PgmCleanupConnection( IN tCOMMON_SESSION_CONTEXT *pSession, IN PIRP pIrp ) /*++
Routine Description:
This routine is called as a result of a close on the client's session handle. If we are a sender, our main job here is to send the FIN immediately, otherwise if we are a receiver, we need to remove ourselves from the timer list
Arguments:
IN pSession -- the Session object IN pIrp -- Client's request Irp
Return Value:
NTSTATUS - Final status of the request (STATUS_PENDING)
--*/ { tCLIENT_SEND_REQUEST *pSendContext; PGMLockHandle OldIrq, OldIrq1;
PgmLock (&PgmDynamicConfig, OldIrq); PgmLock (pSession, OldIrq1);
ASSERT (PGM_VERIFY_HANDLE3 (pSession, PGM_VERIFY_SESSION_UNASSOCIATED, PGM_VERIFY_SESSION_SEND, PGM_VERIFY_SESSION_RECEIVE)); pSession->Verify = PGM_VERIFY_SESSION_DOWN;
//
// If Connection is currently associated, we must let the Disassociate handle
// Removing the connection from the list
//
if (pSession->pAssociatedAddress) { PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmCleanupConnection", "WARNING: pSession=<%x : %x> was not disassociated from pAddress=<%x : %x> earlier\n", pSession, pSession->Verify, pSession->pAssociatedAddress, pSession->pAssociatedAddress->Verify);
ASSERT (0);
PgmUnlock (pSession, OldIrq1); PgmUnlock (&PgmDynamicConfig, OldIrq);
PgmDisassociateAddress (pIrp, IoGetCurrentIrpStackLocation(pIrp));
PgmLock (&PgmDynamicConfig, OldIrq); PgmLock (pSession, OldIrq1); }
ASSERT (!pSession->pAssociatedAddress);
//
// Remove connection from either ConnectionsCreated list
// or ConnectionsDisassociated list
//
RemoveEntryList (&pSession->Linkage); InsertTailList (&PgmDynamicConfig.CleanedUpConnections, &pSession->Linkage);
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_CONNECT, "PgmCleanupConnection", "pSession=<%x>\n", pSession);
pSession->pIrpCleanup = pIrp; if (pSession->SessionFlags & PGM_SESSION_ON_TIMER) { pSession->SessionFlags |= PGM_SESSION_TERMINATED_ABORT; }
PgmUnlock (pSession, OldIrq1); PgmUnlock (&PgmDynamicConfig, OldIrq);
PGM_DEREFERENCE_SESSION_UNASSOCIATED (pSession, REF_SESSION_CREATE);
return (STATUS_PENDING); }
//----------------------------------------------------------------------------
NTSTATUS PgmCloseConnection( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) /*++
Routine Description:
This routine is the final dispatch operation to be performed after the cleanup, which should result in the session object being completely destroyed -- our RefCount must have already been set to 0 when we completed the Cleanup request.
Arguments:
IN pIrp -- Client's request Irp IN pIrpSp -- current request's stack pointer
Return Value:
NTSTATUS - Final status of the operation (STATUS_SUCCESS)
--*/ { tCOMMON_SESSION_CONTEXT *pSession = (tCOMMON_SESSION_CONTEXT *) pIrpSp->FileObject->FsContext;
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_CONNECT, "PgmCloseConnection", "pSession=<%x>\n", pSession);
ASSERT (!pSession->pIrpCleanup);
if (pSession->Process) { PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmCloseConnection", "WARNING! pSession=<%x>, Not yet cleaned up -- Calling cleanup again ...\n", pSession);
PgmCleanupSession (pSession, NULL, NULL); }
pIrpSp->FileObject->FsContext = NULL;
if (pSession->FECOptions) { DestroyFECContext (&pSession->FECContext); }
if (pSession->pSender) { ExDeleteResourceLite (&pSession->pSender->Resource); // Delete the resource
if (pSession->pSender->DataFileName.Buffer) { PgmFreeMem (pSession->pSender->DataFileName.Buffer); }
if (pSession->pSender->pProActiveParityContext) { PgmFreeMem (pSession->pSender->pProActiveParityContext); }
if (pSession->pSender->MaxPayloadSize) { ExDeleteNPagedLookasideList (&pSession->pSender->SenderBufferLookaside); ExDeleteNPagedLookasideList (&pSession->pSender->SendContextLookaside); } PgmFreeMem (pSession->pSender); } else if (pSession->pReceiver) { if (pSession->pFECBuffer) { PgmFreeMem (pSession->pFECBuffer); }
if (pSession->FECGroupSize) { ExDeleteNPagedLookasideList (&pSession->pReceiver->NonParityContextLookaside);
if (pSession->FECOptions) { ExDeleteNPagedLookasideList (&pSession->pReceiver->ParityContextLookaside); } }
PgmFreeMem (pSession->pReceiver); }
PgmFreeMem (pSession);
//
// The final Dereference will complete the Irp!
//
return (STATUS_SUCCESS); }
//----------------------------------------------------------------------------
NTSTATUS PgmConnect( IN tPGM_DEVICE *pPgmDevice, IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) /*++
Routine Description:
This routine is called to setup a connection, but since we are doing PGM, there are no packets to be sent out. What we will do here is to create the file + section map for buffering the data packets, and also finalize the settings based on the default + user -specified settings
Arguments:
IN pPgmDevice -- Pgm's Device object context IN pIrp -- Client's request Irp IN pIrpSp -- current request's stack pointer
Return Value:
NTSTATUS - Final status of the connect operation
--*/ { tIPADDRESS IpAddress, OutIfAddress; LIST_ENTRY *pEntry; LIST_ENTRY *pEntry2; tLOCAL_INTERFACE *pLocalInterface = NULL; tADDRESS_ON_INTERFACE *pLocalAddress = NULL; USHORT Port; PGMLockHandle OldIrq; NTSTATUS status; ULONGLONG WindowAdvanceInMSecs; tADDRESS_CONTEXT *pAddress = NULL; tSEND_SESSION *pSend = (tSEND_SESSION *) pIrpSp->FileObject->FsContext; PTDI_REQUEST_KERNEL pRequestKernel = (PTDI_REQUEST_KERNEL) &pIrpSp->Parameters; ULONG Length, MCastTtl;
//
// Verify Minimum Buffer length!
//
if (!GetIpAddress (pRequestKernel->RequestConnectionInformation->RemoteAddress, pRequestKernel->RequestConnectionInformation->RemoteAddressLength, &IpAddress, &Port)) { PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmConnect", "pSend=<%x>, Invalid Dest address!\n", pSend); return (STATUS_INVALID_ADDRESS_COMPONENT); }
PgmLock (&PgmDynamicConfig, OldIrq); //
// Now, verify that the Connection handle is valid + associated!
//
if ((!PGM_VERIFY_HANDLE (pSend, PGM_VERIFY_SESSION_SEND)) || (!(pAddress = pSend->pAssociatedAddress)) || (!PGM_VERIFY_HANDLE (pAddress, PGM_VERIFY_ADDRESS)) || (pAddress->Flags & PGM_ADDRESS_FLAG_INVALID_OUT_IF)) { PgmUnlock (&PgmDynamicConfig, OldIrq); PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmConnect", "BAD Handle(s), pSend=<%x>, pAddress=<%x>\n", pSend, pAddress);
return (STATUS_INVALID_HANDLE); }
PGM_REFERENCE_ADDRESS (pAddress, REF_ADDRESS_CONNECT, FALSE);
//
// If an outgoing interface has not yet been specified by the
// client, select one ourselves
//
if (!pAddress->SenderMCastOutIf) { status = STATUS_ADDRESS_NOT_ASSOCIATED; OutIfAddress = 0;
pEntry = &PgmDynamicConfig.LocalInterfacesList; while ((pEntry = pEntry->Flink) != &PgmDynamicConfig.LocalInterfacesList) { pLocalInterface = CONTAINING_RECORD (pEntry, tLOCAL_INTERFACE, Linkage); pEntry2 = &pLocalInterface->Addresses; while ((pEntry2 = pEntry2->Flink) != &pLocalInterface->Addresses) { pLocalAddress = CONTAINING_RECORD (pEntry2, tADDRESS_ON_INTERFACE, Linkage); OutIfAddress = htonl (pLocalAddress->IpAddress);
PgmUnlock (&PgmDynamicConfig, OldIrq); status = SetSenderMCastOutIf (pAddress, OutIfAddress); PgmLock (&PgmDynamicConfig, OldIrq);
break; }
if (OutIfAddress) { break; } }
if (!NT_SUCCESS (status)) { PgmUnlock (&PgmDynamicConfig, OldIrq); PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_CONNECT);
PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmConnect", "Could not bind sender to <%x>!\n", OutIfAddress);
return (STATUS_UNSUCCESSFUL); } }
//
// So, we found a valid address to send to
//
pSend->pSender->DestMCastIpAddress = ntohl (IpAddress); pSend->pSender->DestMCastPort = pAddress->SenderMCastPort = ntohs (Port); pSend->pSender->SenderMCastOutIf = pAddress->SenderMCastOutIf;
//
// Set the FEC Info (if applicable)
//
pSend->FECBlockSize = pAddress->FECBlockSize; pSend->FECGroupSize = pAddress->FECGroupSize;
if (pAddress->FECOptions) { Length = sizeof(tBUILD_PARITY_CONTEXT) + pSend->FECGroupSize*sizeof(PUCHAR); if (!(pSend->pSender->pProActiveParityContext = (tBUILD_PARITY_CONTEXT *) PgmAllocMem (Length,PGM_TAG('0'))) || (!NT_SUCCESS (status = CreateFECContext (&pSend->FECContext, pSend->FECGroupSize, pSend->FECBlockSize, FALSE)))) { PgmUnlock (&PgmDynamicConfig, OldIrq); PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_CONNECT);
PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmConnect", "CreateFECContext returned <%x>, pSend=<%x>, Dest IpAddress=<%x>, Port=<%x>\n", status, pSend, IpAddress, Port);
return (STATUS_INSUFFICIENT_RESOURCES); }
pSend->FECOptions = pAddress->FECOptions; pSend->FECProActivePackets = pAddress->FECProActivePackets; pSend->pSender->SpmOptions |= PGM_OPTION_FLAG_PARITY_PRM; pSend->pSender->LastVariableTGPacketSequenceNumber = -1;
ASSERT (!(pSend->FECProActivePackets || pSend->FECProActivePackets) || ((pSend->FECGroupSize && pSend->FECBlockSize) && (pSend->FECGroupSize < pSend->FECBlockSize)));
//
// Now determine the MaxPayloadsize and buffer size
// We will also need to adjust the buffer size to avoid alignment issues
//
Length = sizeof (tPACKET_OPTIONS) + pAddress->OutIfMTU + sizeof (tPOST_PACKET_FEC_CONTEXT); pSend->pSender->PacketBufferSize = (Length + sizeof (PVOID) - 1) & ~(sizeof (PVOID) - 1); pSend->pSender->MaxPayloadSize = pAddress->OutIfMTU - (PGM_MAX_FEC_DATA_HEADER_LENGTH + sizeof (USHORT)); } else { Length = sizeof (tPACKET_OPTIONS) + pAddress->OutIfMTU; pSend->pSender->PacketBufferSize = (Length + sizeof (PVOID) - 1) & ~(sizeof (PVOID) - 1); pSend->pSender->MaxPayloadSize = pAddress->OutIfMTU - PGM_MAX_DATA_HEADER_LENGTH; } ASSERT (pSend->pSender->MaxPayloadSize);
pSend->TSIPort = PgmDynamicConfig.SourcePort++; // Set the Global Src Port + Global Src Id
if (pSend->TSIPort == PgmDynamicConfig.SourcePort) { //
// We don't want the local port and remote port to be the same
// (especially for handling TSI settings on loopback case),
// so pick a different port
//
pSend->TSIPort = PgmDynamicConfig.SourcePort++; }
//
// Now, initialize the Sender info
//
InitializeListHead (&pSend->pSender->PendingSends); InitializeListHead (&pSend->pSender->PendingPacketizedSends); InitializeListHead (&pSend->pSender->CompletedSendsInWindow); InitializeListHead (&pSend->pSender->PendingRDataRequests); InitializeListHead (&pSend->pSender->HandledRDataRequests);
MCastTtl = pAddress->MCastPacketTtl; PgmUnlock (&PgmDynamicConfig, OldIrq);
KeInitializeEvent (&pSend->pSender->SendEvent, SynchronizationEvent, FALSE);
//
// Now, set the MCast TTL
//
status = PgmSetTcpInfo (pAddress->FileHandle, AO_OPTION_MCASTTTL, &MCastTtl, sizeof (ULONG)); if (NT_SUCCESS (status)) { //
// Set the MCast TTL for the RouterAlert handle also
//
status = PgmSetTcpInfo (pAddress->RAlertFileHandle, AO_OPTION_MCASTTTL, &MCastTtl, sizeof (ULONG)); }
if (NT_SUCCESS (status)) { status = PgmCreateDataFileAndMapSection (pSend); if (!NT_SUCCESS (status)) { PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmConnect", "PgmCreateDataFileAndMapSection returned <%x>, pSend=<%x>, Dest IpAddress=<%x>, Port=<%x>\n", status, pSend, IpAddress, Port); } } else { PgmLog (PGM_LOG_ERROR, DBG_ADDRESS, "PgmConnect", "AO_OPTION_MCASTTTL returned <%x>\n", status); }
if (!NT_SUCCESS (status)) { pSend->pSender->MaxPayloadSize = 0; PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_CONNECT);
return (status); }
PgmLock (&PgmDynamicConfig, OldIrq);
//
// Set the appropriate data parameters for the timeout routine
// If the Send rate is quite high, we may need to send more than
// 1 packet per timeout, but typically it should be low enough to
// require several timeouts
// Rate of Kb/Sec == Rate of Bytes/MilliSecs
//
ASSERT (pAddress->RateKbitsPerSec && (BASIC_TIMER_GRANULARITY_IN_MSECS > BITS_PER_BYTE)); if (((pAddress->RateKbitsPerSec * BASIC_TIMER_GRANULARITY_IN_MSECS) / BITS_PER_BYTE) >= pSend->pSender->PacketBufferSize) { //
// We have a high Send rate, so we need to increment our window every timeout
// So, Bytes to be sent in (x) Millisecs = Rate of Bytes/Millisecs * (x)
//
pSend->pSender->SendTimeoutCount = 1; } else { //
// We will set our Send timeout count based for a slow timer
// -- enough for pAddress->OutIfMTU
// So, Number of Timeouts of x millisecs before we can send pAddress->OutIfMTU:
// = Rate of Bytes/Millisecs * (x)
//
pSend->pSender->SendTimeoutCount = (pAddress->OutIfMTU +(pAddress->RateKbitsPerSec/BITS_PER_BYTE-1)) / ((pAddress->RateKbitsPerSec * BASIC_TIMER_GRANULARITY_IN_MSECS)/BITS_PER_BYTE); if (!pSend->pSender->SendTimeoutCount) { ASSERT (0); pSend->pSender->SendTimeoutCount = 1; } } pSend->pSender->IncrementBytesOnSendTimeout = (ULONG) (pAddress->RateKbitsPerSec * pSend->pSender->SendTimeoutCount * BASIC_TIMER_GRANULARITY_IN_MSECS) / BITS_PER_BYTE;
//
// Now, set the values for the next timeout!
//
pSend->pSender->CurrentTimeoutCount = pSend->pSender->SendTimeoutCount; pSend->pSender->CurrentBytesSendable = pSend->pSender->IncrementBytesOnSendTimeout;
//
// Set the SPM timeouts
//
pSend->pSender->CurrentSPMTimeout = 0; pSend->pSender->AmbientSPMTimeout = AMBIENT_SPM_TIMEOUT_IN_MSECS / BASIC_TIMER_GRANULARITY_IN_MSECS; pSend->pSender->InitialHeartbeatSPMTimeout = INITIAL_HEARTBEAT_SPM_TIMEOUT_IN_MSECS / BASIC_TIMER_GRANULARITY_IN_MSECS; pSend->pSender->MaxHeartbeatSPMTimeout = MAX_HEARTBEAT_SPM_TIMEOUT_IN_MSECS / BASIC_TIMER_GRANULARITY_IN_MSECS; pSend->pSender->HeartbeatSPMTimeout = pSend->pSender->InitialHeartbeatSPMTimeout;
//
// Set the Increment window settings
//
// TimerTickCount, LastWindowAdvanceTime and LastTrailingEdgeTime should be 0
WindowAdvanceInMSecs = (((ULONGLONG)pAddress->WindowSizeInMSecs) * pAddress->WindowAdvancePercentage)/100; pSend->pSender->WindowSizeTime = pAddress->WindowSizeInMSecs / BASIC_TIMER_GRANULARITY_IN_MSECS; pSend->pSender->WindowAdvanceDeltaTime = WindowAdvanceInMSecs / BASIC_TIMER_GRANULARITY_IN_MSECS; pSend->pSender->NextWindowAdvanceTime = pSend->pSender->WindowSizeTime + pSend->pSender->WindowAdvanceDeltaTime;
// Set the RData linger time!
pSend->pSender->RDataLingerTime = RDATA_LINGER_TIME_MSECS / BASIC_TIMER_GRANULARITY_IN_MSECS;
//
// Set the late Joiner settings
//
if (pAddress->LateJoinerPercentage) { pSend->pSender->LateJoinSequenceNumbers = (SEQ_TYPE) ((pSend->pSender->MaxPacketsInBuffer * pAddress->LateJoinerPercentage) / (2 * 100));
pSend->pSender->DataOptions |= PGM_OPTION_FLAG_JOIN; pSend->pSender->DataOptionsLength += PGM_PACKET_OPT_JOIN_LENGTH;
pSend->pSender->SpmOptions |= PGM_OPTION_FLAG_JOIN; }
// The timer will be started when the first send comes down
pSend->SessionFlags |= (PGM_SESSION_FLAG_FIRST_PACKET | PGM_SESSION_FLAG_SENDER);
#if 0
pSend->pSender->LeadingWindowOffset = pSend->pSender->TrailingWindowOffset = (pSend->pSender->MaxDataFileSize/(pSend->pSender->PacketBufferSize*2))*pSend->pSender->PacketBufferSize; #endif
pSend->pSender->LeadingWindowOffset = pSend->pSender->TrailingWindowOffset = 0;
PgmUnlock (&PgmDynamicConfig, OldIrq); PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_CONNECT);
ExInitializeNPagedLookasideList (&pSend->pSender->SenderBufferLookaside, NULL, NULL, 0, pAddress->OutIfMTU + sizeof (tPOST_PACKET_FEC_CONTEXT), PGM_TAG('2'), SENDER_BUFFER_LOOKASIDE_DEPTH);
ExInitializeNPagedLookasideList (&pSend->pSender->SendContextLookaside, NULL, NULL, 0, sizeof (tCLIENT_SEND_REQUEST), PGM_TAG('2'), SEND_CONTEXT_LOOKASIDE_DEPTH);
PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmConnect", "DestIP=<%x>, Rate=<%d>, WinBytes=<%d>, WinMS=<%d>, SendTC=<%d>, IncBytes=<%d>, CurrentBS=<%d>\n", (ULONG) IpAddress, (ULONG) pAddress->RateKbitsPerSec, (ULONG) pAddress->WindowSizeInBytes, (ULONG) pAddress->WindowSizeInMSecs, (ULONG) pSend->pSender->SendTimeoutCount, (ULONG) pSend->pSender->IncrementBytesOnSendTimeout, (ULONG) pSend->pSender->CurrentBytesSendable);
return (STATUS_SUCCESS); }
//----------------------------------------------------------------------------
VOID PgmCancelDisconnectIrp( IN PDEVICE_OBJECT DeviceContext, IN PIRP pIrp ) /*++
Routine Description:
This routine handles the cancelling of a Disconnect Irp. It must release the cancel spin lock before returning re: IoCancelIrp().
Arguments:
Return Value:
None
--*/ { PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation (pIrp); tCOMMON_SESSION_CONTEXT *pSession = (tCOMMON_SESSION_CONTEXT *) pIrpSp->FileObject->FsContext; PGMLockHandle OldIrq; PIRP pIrpToComplete;
if (!PGM_VERIFY_HANDLE2 (pSession, PGM_VERIFY_SESSION_SEND, PGM_VERIFY_SESSION_RECEIVE)) { IoReleaseCancelSpinLock (pIrp->CancelIrql);
PgmLog (PGM_LOG_ERROR, (DBG_SEND | DBG_ADDRESS | DBG_CONNECT), "PgmCancelDisconnectIrp", "pIrp=<%x> pSession=<%x>, pAddress=<%x>\n", pIrp, pSession, pSession->pAssociatedAddress); return; }
PgmLock (pSession, OldIrq);
ASSERT (pIrp == pSession->pIrpDisconnect); if ((pIrpToComplete = pSession->pIrpDisconnect) && (pIrpToComplete == pIrp)) { pSession->pIrpDisconnect = NULL; } else { pIrpToComplete = NULL; }
if (pSession->pSender) { pSession->pSender->DisconnectTimeInTicks = pSession->pSender->TimerTickCount; }
//
// If we have reached here, then the Irp must already
// be in the process of being completed!
//
PgmUnlock (pSession, OldIrq); IoReleaseCancelSpinLock (pIrp->CancelIrql);
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, (DBG_SEND | DBG_ADDRESS | DBG_CONNECT), "PgmCancelDisconnectIrp", "pIrp=<%x> was CANCELled, pIrpTpComplete=<%x>\n", pIrp, pIrpToComplete);
if (pIrpToComplete) { pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest (pIrp, IO_NETWORK_INCREMENT); } }
//----------------------------------------------------------------------------
NTSTATUS PgmDisconnect( IN tPGM_DEVICE *pPgmDevice, IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) /*++
Routine Description:
This routine is called by the client to disconnect a currently-active session
Arguments:
IN pPgmDevice -- Pgm's Device object context IN pIrp -- Client's request Irp IN pIrpSp -- current request's stack pointer
Return Value:
NTSTATUS - Final status of the disconnect operation
--*/ { LIST_ENTRY PendingIrpsList; PGMLockHandle OldIrq1, OldIrq2, OldIrq3; PIRP pIrpReceive; NTSTATUS Status; LARGE_INTEGER TimeoutInMSecs; LARGE_INTEGER *pTimeoutInMSecs; tADDRESS_CONTEXT *pAddress = NULL; tCOMMON_SESSION_CONTEXT *pSession = (tCOMMON_SESSION_CONTEXT *) pIrpSp->FileObject->FsContext; PTDI_REQUEST_KERNEL_DISCONNECT pDisconnectRequest = (PTDI_REQUEST_KERNEL_CONNECT) &(pIrpSp->Parameters);
PgmLock (&PgmDynamicConfig, OldIrq1); //
// Now, verify that the Connection handle is valid + associated!
//
if ((!PGM_VERIFY_HANDLE2 (pSession, PGM_VERIFY_SESSION_SEND, PGM_VERIFY_SESSION_RECEIVE)) || (!(pAddress = pSession->pAssociatedAddress)) || (!PGM_VERIFY_HANDLE (pAddress, PGM_VERIFY_ADDRESS))) { PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmDisconnect", "BAD Handle(s), pSession=<%x>, pAddress=<%x>\n", pSession, pAddress);
PgmUnlock (&PgmDynamicConfig, OldIrq1); return (STATUS_INVALID_HANDLE); }
Status = STATUS_SUCCESS; InitializeListHead (&PendingIrpsList); TimeoutInMSecs.QuadPart = 0;
IoAcquireCancelSpinLock (&OldIrq2); PgmLock (pSession, OldIrq3);
if (pSession->pReceiver) { //
// If we have any receive Irps pending, cancel them
//
RemovePendingIrps (pSession, &PendingIrpsList); } else if (pSession->pSender) { //
// See if there is an abortive or graceful disconnect, and
// also if there is a timeout specified.
//
if ((pDisconnectRequest->RequestFlags & TDI_DISCONNECT_ABORT) || (pSession->SessionFlags & PGM_SESSION_FLAG_FIRST_PACKET)) // No packets sent yet!
{ pSession->pSender->DisconnectTimeInTicks = pSession->pSender->TimerTickCount; } else if (NT_SUCCESS (PgmCheckSetCancelRoutine (pIrp, PgmCancelDisconnectIrp, TRUE))) { if ((pTimeoutInMSecs = pDisconnectRequest->RequestSpecific) && ((pTimeoutInMSecs->LowPart != -1) || (pTimeoutInMSecs->HighPart != -1))) // Check Infinite
{ //
// NT relative timeouts are negative. Negate first to get a
// positive value to pass to the transport.
//
TimeoutInMSecs.QuadPart = -((*pTimeoutInMSecs).QuadPart); TimeoutInMSecs = PgmConvert100nsToMilliseconds (TimeoutInMSecs);
pSession->pSender->DisconnectTimeInTicks = pSession->pSender->TimerTickCount + TimeoutInMSecs.QuadPart / BASIC_TIMER_GRANULARITY_IN_MSECS; }
pSession->pIrpDisconnect = pIrp; Status = STATUS_PENDING; } else { Status = STATUS_CANCELLED; } }
if (NT_SUCCESS (Status)) { pSession->SessionFlags |= PGM_SESSION_CLIENT_DISCONNECTED; }
PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmDisconnect", "pIrp=<%x>, pSession=<%x>, pAddress=<%x>, Timeout=<%x : %x>, %s\n", pIrp, pSession, pAddress, TimeoutInMSecs.QuadPart, (pDisconnectRequest->RequestFlags & TDI_DISCONNECT_ABORT ? "ABORTive" : "GRACEful"));
PgmUnlock (pSession, OldIrq3); IoReleaseCancelSpinLock (OldIrq2); PgmUnlock (&PgmDynamicConfig, OldIrq1);
while (!IsListEmpty (&PendingIrpsList)) { pIrpReceive = CONTAINING_RECORD (PendingIrpsList.Flink, IRP, Tail.Overlay.ListEntry); RemoveEntryList (&pIrpReceive->Tail.Overlay.ListEntry);
PgmCancelCancelRoutine (pIrpReceive); pIrpReceive->IoStatus.Status = STATUS_CANCELLED; IoCompleteRequest (pIrpReceive, IO_NETWORK_INCREMENT); }
return (Status); }
//----------------------------------------------------------------------------
NTSTATUS PgmSetRcvBufferLength( IN PIRP pIrp, IN PIO_STACK_LOCATION pIrpSp ) /*++
Routine Description:
This routine is called by the client by the client to set the receive buffer length Currently, we do not utilize this option meaningfully.
Arguments:
IN pIrp -- Client's request Irp IN pIrpSp -- current request's stack pointer
Return Value:
NTSTATUS - Final status of the operation
--*/ { NTSTATUS status; tRECEIVE_SESSION *pReceive = (tRECEIVE_SESSION *) pIrpSp->FileObject->FsContext; tPGM_MCAST_REQUEST *pInputBuffer = (tPGM_MCAST_REQUEST *) pIrp->AssociatedIrp.SystemBuffer;
PAGED_CODE();
if (pIrpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof (tPGM_MCAST_REQUEST)) { PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmSetRcvBufferLength", "Invalid BufferLength, <%d> < <%d>\n", pIrpSp->Parameters.DeviceIoControl.InputBufferLength, sizeof (tPGM_MCAST_REQUEST)); return (STATUS_INVALID_PARAMETER); }
if (!PGM_VERIFY_HANDLE (pReceive, PGM_VERIFY_SESSION_RECEIVE)) { PgmLog (PGM_LOG_ERROR, DBG_CONNECT, "PgmSetRcvBufferLength", "Invalid Handle <%x>\n", pReceive); return (STATUS_INVALID_HANDLE); }
pReceive->pReceiver->RcvBufferLength = pInputBuffer->RcvBufferLength;
PgmLog (PGM_LOG_INFORM_STATUS, DBG_CONNECT, "PgmSetRcvBufferLength", "RcvBufferLength=<%d>\n", pReceive->pReceiver->RcvBufferLength);
//
// ISSUE: What else should we do here ?
//
return (STATUS_SUCCESS); }
//----------------------------------------------------------------------------
|