6171 lines
229 KiB
C
6171 lines
229 KiB
C
/*++
|
||
|
||
Copyright (c) 2000-2000 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
Receive.c
|
||
|
||
Abstract:
|
||
|
||
This module implements Receive handlers and other routines
|
||
the PGM Transport and other routines that are specific to the
|
||
NT implementation of a driver.
|
||
|
||
Author:
|
||
|
||
Mohammad Shabbir Alam (MAlam) 3-30-2000
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
|
||
#include "precomp.h"
|
||
|
||
|
||
typedef struct in_pktinfo {
|
||
tIPADDRESS ipi_addr; // destination IPv4 address
|
||
UINT ipi_ifindex; // received interface index
|
||
} IP_PKTINFO;
|
||
|
||
//******************* Pageable Routine Declarations ****************
|
||
#ifdef ALLOC_PRAGMA
|
||
#endif
|
||
//******************* Pageable Routine Declarations ****************
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
VOID
|
||
RemovePendingIrps(
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN LIST_ENTRY *pIrpsList
|
||
)
|
||
{
|
||
PIRP pIrp;
|
||
|
||
if (pIrp = pReceive->pReceiver->pIrpReceive)
|
||
{
|
||
pReceive->pReceiver->pIrpReceive = NULL;
|
||
|
||
pIrp->IoStatus.Information = pReceive->pReceiver->BytesInMdl;
|
||
InsertTailList (pIrpsList, &pIrp->Tail.Overlay.ListEntry);
|
||
}
|
||
|
||
while (!IsListEmpty (&pReceive->pReceiver->ReceiveIrpsList))
|
||
{
|
||
pIrp = CONTAINING_RECORD (pReceive->pReceiver->ReceiveIrpsList.Flink, IRP, Tail.Overlay.ListEntry);
|
||
|
||
RemoveEntryList (&pIrp->Tail.Overlay.ListEntry);
|
||
InsertTailList (pIrpsList, &pIrp->Tail.Overlay.ListEntry);
|
||
pIrp->IoStatus.Information = 0;
|
||
}
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
VOID
|
||
FreeNakContext(
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN tNAK_FORWARD_DATA *pNak
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine free's the context used for tracking missing sequences
|
||
|
||
Arguments:
|
||
|
||
IN pReceive -- Receive context
|
||
IN pNak -- Nak Context to be free'ed
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
{
|
||
UCHAR i, j, k, NumPackets;
|
||
|
||
//
|
||
// Free any memory for non-parity data
|
||
//
|
||
j = k = 0;
|
||
NumPackets = pNak->NumDataPackets + pNak->NumParityPackets;
|
||
for (i=0; i<NumPackets; i++)
|
||
{
|
||
ASSERT (pNak->pPendingData[i].pDataPacket);
|
||
if (pNak->pPendingData[i].PacketIndex < pReceive->FECGroupSize)
|
||
{
|
||
j++;
|
||
}
|
||
else
|
||
{
|
||
k++;
|
||
}
|
||
PgmFreeMem (pNak->pPendingData[i].pDataPacket);
|
||
}
|
||
ASSERT (j == pNak->NumDataPackets);
|
||
ASSERT (k == pNak->NumParityPackets);
|
||
|
||
//
|
||
// Return the pNak memory based on whether it was allocated
|
||
// from the parity or non-parity lookaside list
|
||
//
|
||
if (pNak->OriginalGroupSize > 1)
|
||
{
|
||
ExFreeToNPagedLookasideList (&pReceive->pReceiver->ParityContextLookaside, pNak);
|
||
}
|
||
else
|
||
{
|
||
ExFreeToNPagedLookasideList (&pReceive->pReceiver->NonParityContextLookaside, pNak);
|
||
}
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
VOID
|
||
CleanupPendingNaks(
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN PVOID fDerefReceive,
|
||
IN PVOID UnUsed
|
||
)
|
||
{
|
||
LIST_ENTRY NaksList, DataList;
|
||
tNAK_FORWARD_DATA *pNak;
|
||
LIST_ENTRY *pEntry;
|
||
ULONG NumBufferedData = 0;
|
||
ULONG NumNaks = 0;
|
||
PGMLockHandle OldIrq;
|
||
|
||
ASSERT (pReceive->pReceiver);
|
||
|
||
PgmLock (pReceive, OldIrq);
|
||
|
||
DataList.Flink = pReceive->pReceiver->BufferedDataList.Flink;
|
||
DataList.Blink = pReceive->pReceiver->BufferedDataList.Blink;
|
||
pReceive->pReceiver->BufferedDataList.Flink->Blink = &DataList;
|
||
pReceive->pReceiver->BufferedDataList.Blink->Flink = &DataList;
|
||
InitializeListHead (&pReceive->pReceiver->BufferedDataList);
|
||
|
||
NaksList.Flink = pReceive->pReceiver->NaksForwardDataList.Flink;
|
||
NaksList.Blink = pReceive->pReceiver->NaksForwardDataList.Blink;
|
||
pReceive->pReceiver->NaksForwardDataList.Flink->Blink = &NaksList;
|
||
pReceive->pReceiver->NaksForwardDataList.Blink->Flink = &NaksList;
|
||
InitializeListHead (&pReceive->pReceiver->NaksForwardDataList);
|
||
|
||
while (!IsListEmpty (&pReceive->pReceiver->PendingNaksList))
|
||
{
|
||
pEntry = RemoveHeadList (&pReceive->pReceiver->PendingNaksList);
|
||
InitializeListHead (pEntry);
|
||
}
|
||
|
||
PgmUnlock (pReceive, OldIrq);
|
||
|
||
//
|
||
// Cleanup any pending Nak entries
|
||
//
|
||
while (!IsListEmpty (&DataList))
|
||
{
|
||
pEntry = RemoveHeadList (&DataList);
|
||
pNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, Linkage);
|
||
FreeNakContext (pReceive, pNak);
|
||
NumBufferedData++;
|
||
}
|
||
|
||
while (!IsListEmpty (&NaksList))
|
||
{
|
||
pEntry = RemoveHeadList (&NaksList);
|
||
pNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, Linkage);
|
||
FreeNakContext (pReceive, pNak);
|
||
NumNaks++;
|
||
}
|
||
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_SEND, "CleanupPendingNaks",
|
||
"pReceive=<%p>, NumBufferedData=<%d=%d>, TotalDataPackets=<%d>, NumNaks=<%d * %d>\n",
|
||
pReceive,
|
||
(ULONG) pReceive->pReceiver->NumPacketGroupsPendingClient, NumBufferedData,
|
||
(ULONG) pReceive->pReceiver->TotalDataPacketsBuffered, NumNaks, (ULONG) pReceive->FECGroupSize);
|
||
|
||
// ASSERT (NumBufferedData == pReceive->pReceiver->NumPacketGroupsPendingClient);
|
||
pReceive->pReceiver->NumPacketGroupsPendingClient = 0;
|
||
|
||
if (fDerefReceive)
|
||
{
|
||
PGM_DEREFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_CLEANUP_NAKS);
|
||
}
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
BOOLEAN
|
||
CheckIndicateDisconnect(
|
||
IN tADDRESS_CONTEXT *pAddress,
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN PGMLockHandle *pOldIrqAddress,
|
||
IN PGMLockHandle *pOldIrqReceive,
|
||
IN BOOLEAN fAddressLockHeld
|
||
)
|
||
{
|
||
ULONG DisconnectFlag;
|
||
NTSTATUS status;
|
||
BOOLEAN fDisconnectIndicated = FALSE;
|
||
LIST_ENTRY PendingIrpsList;
|
||
PIRP pIrp;
|
||
|
||
//
|
||
// Don't abort if we are currently indicating, or if we have
|
||
// already aborted!
|
||
//
|
||
if (pReceive->SessionFlags & (PGM_SESSION_FLAG_IN_INDICATE | PGM_SESSION_DISCONNECT_INDICATED))
|
||
{
|
||
return (TRUE);
|
||
}
|
||
|
||
if ((pReceive->SessionFlags & PGM_SESSION_TERMINATED_ABORT) ||
|
||
((pReceive->SessionFlags & PGM_SESSION_TERMINATED_GRACEFULLY) &&
|
||
(IsListEmpty (&pReceive->pReceiver->BufferedDataList)) &&
|
||
SEQ_GEQ (pReceive->pReceiver->FirstNakSequenceNumber, (pReceive->pReceiver->FinDataSequenceNumber+1))))
|
||
{
|
||
//
|
||
// The session has terminated, so let the client know
|
||
//
|
||
if (pReceive->SessionFlags & PGM_SESSION_TERMINATED_ABORT)
|
||
{
|
||
DisconnectFlag = TDI_DISCONNECT_ABORT;
|
||
}
|
||
else
|
||
{
|
||
DisconnectFlag = TDI_DISCONNECT_RELEASE;
|
||
}
|
||
|
||
pReceive->SessionFlags |= PGM_SESSION_DISCONNECT_INDICATED;
|
||
|
||
InitializeListHead (&PendingIrpsList);
|
||
RemovePendingIrps (pReceive, &PendingIrpsList);
|
||
|
||
PGM_REFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_CLEANUP_NAKS, TRUE);
|
||
|
||
PgmUnlock (pReceive, *pOldIrqReceive);
|
||
if (fAddressLockHeld)
|
||
{
|
||
PgmUnlock (pAddress, *pOldIrqAddress);
|
||
}
|
||
|
||
while (!IsListEmpty (&PendingIrpsList))
|
||
{
|
||
pIrp = CONTAINING_RECORD (PendingIrpsList.Flink, IRP, Tail.Overlay.ListEntry);
|
||
PgmCancelCancelRoutine (pIrp);
|
||
RemoveEntryList (&pIrp->Tail.Overlay.ListEntry);
|
||
|
||
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
||
IoCompleteRequest (pIrp, IO_NETWORK_INCREMENT);
|
||
}
|
||
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "CheckIndicateDisconnect",
|
||
"Disconnecting pReceive=<%p:%p>, with %s\n",
|
||
pReceive, pReceive->ClientSessionContext,
|
||
(DisconnectFlag == TDI_DISCONNECT_RELEASE ? "TDI_DISCONNECT_RELEASE":"TDI_DISCONNECT_ABORT"));
|
||
|
||
status = (*pAddress->evDisconnect) (pAddress->DiscEvContext,
|
||
pReceive->ClientSessionContext,
|
||
0,
|
||
NULL,
|
||
0,
|
||
NULL,
|
||
DisconnectFlag);
|
||
|
||
|
||
fDisconnectIndicated = TRUE;
|
||
|
||
//
|
||
// See if we can Enqueue the Nak cleanup request to a Worker thread
|
||
//
|
||
if (STATUS_SUCCESS != PgmQueueForDelayedExecution (CleanupPendingNaks,
|
||
pReceive,
|
||
(PVOID) TRUE,
|
||
NULL,
|
||
FALSE))
|
||
{
|
||
CleanupPendingNaks (pReceive, (PVOID) TRUE, NULL);
|
||
}
|
||
|
||
if (fAddressLockHeld)
|
||
{
|
||
PgmLock (pAddress, *pOldIrqAddress);
|
||
}
|
||
PgmLock (pReceive, *pOldIrqReceive);
|
||
}
|
||
|
||
return (fDisconnectIndicated);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
VOID
|
||
ProcessNakOption(
|
||
IN tPACKET_OPTION_GENERIC UNALIGNED *pOptionHeader,
|
||
OUT tNAKS_LIST *pNaksList
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes the Nak list option in the Pgm packet
|
||
|
||
Arguments:
|
||
|
||
IN pOptionHeader -- The Nak List option ptr
|
||
OUT pNaksList -- The parameters extracted (i.e. list of Nak sequences)
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
{
|
||
UCHAR i, NumNaks;
|
||
ULONG pPacketNaks[MAX_SEQUENCES_PER_NAK_OPTION];
|
||
|
||
NumNaks = (pOptionHeader->OptionLength - 4) / 4;
|
||
ASSERT (NumNaks <= MAX_SEQUENCES_PER_NAK_OPTION);
|
||
|
||
PgmCopyMemory (pPacketNaks, (pOptionHeader + 1), (pOptionHeader->OptionLength - 4));
|
||
for (i=0; i < NumNaks; i++)
|
||
{
|
||
//
|
||
// Do not fill in the 0th entry, since that is from the packet header itself
|
||
//
|
||
pNaksList->pNakSequences[i+1] = (SEQ_TYPE) ntohl (pPacketNaks[i]);
|
||
}
|
||
pNaksList->NumSequences = (USHORT) i;
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
ProcessOptions(
|
||
IN tPACKET_OPTION_LENGTH UNALIGNED *pPacketExtension,
|
||
IN ULONG BytesAvailable,
|
||
IN ULONG PacketType,
|
||
OUT tPACKET_OPTIONS *pPacketOptions,
|
||
OUT tNAKS_LIST *pNaksList
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes the options fields on an incoming Pgm packet
|
||
and returns the options information extracted in the OUT parameters
|
||
|
||
Arguments:
|
||
|
||
IN pPacketExtension -- Options section of the packet
|
||
IN BytesAvailable -- from the start of the options
|
||
IN PacketType -- Whether Data or Spm packet, etc
|
||
OUT pPacketOptions -- Structure containing the parameters from the options
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Final status of the operation
|
||
|
||
--*/
|
||
{
|
||
tPACKET_OPTION_GENERIC UNALIGNED *pOptionHeader;
|
||
ULONG BytesLeft = BytesAvailable;
|
||
UCHAR i;
|
||
ULONG MessageFirstSequence, MessageLength, MessageOffset;
|
||
ULONG pOptionsData[3];
|
||
ULONG OptionsFlags = 0;
|
||
ULONG NumOptionsProcessed = 0;
|
||
USHORT TotalOptionsLength = 0;
|
||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||
|
||
pPacketOptions->OptionsLength = 0; // Init
|
||
pPacketOptions->OptionsFlags = 0; // Init
|
||
|
||
if (BytesLeft > sizeof(tPACKET_OPTION_LENGTH))
|
||
{
|
||
PgmCopyMemory (&TotalOptionsLength, &pPacketExtension->TotalOptionsLength, sizeof (USHORT));
|
||
TotalOptionsLength = ntohs (TotalOptionsLength);
|
||
}
|
||
|
||
//
|
||
// First process the Option extension
|
||
//
|
||
if ((BytesLeft < ((sizeof(tPACKET_OPTION_LENGTH) + sizeof(tPACKET_OPTION_GENERIC)))) || // Ext+opt
|
||
(pPacketExtension->Type != PACKET_OPTION_LENGTH) ||
|
||
(pPacketExtension->Length != 4) ||
|
||
(BytesLeft < TotalOptionsLength)) // Verify length
|
||
{
|
||
//
|
||
// Need to get at least our header from transport!
|
||
//
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions",
|
||
"BytesLeft=<%d> < Min=<%d>, TotalOptionsLength=<%d>, ExtLength=<%d>, ExtType=<%x>\n",
|
||
BytesLeft, ((sizeof(tPACKET_OPTION_LENGTH) + sizeof(tPACKET_OPTION_GENERIC))),
|
||
(ULONG) TotalOptionsLength, pPacketExtension->Length, pPacketExtension->Type);
|
||
|
||
return (status);
|
||
}
|
||
|
||
//
|
||
// Now, process each option
|
||
//
|
||
pOptionHeader = (tPACKET_OPTION_GENERIC UNALIGNED *) (pPacketExtension + 1);
|
||
BytesLeft -= sizeof(tPACKET_OPTION_LENGTH);
|
||
NumOptionsProcessed = 0;
|
||
|
||
do
|
||
{
|
||
if (pOptionHeader->OptionLength > BytesLeft)
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions",
|
||
"Incorrectly formatted Options: OptionLength=<%d> > BytesLeft=<%d>, NumProcessed=<%d>\n",
|
||
pOptionHeader->OptionLength, BytesLeft, NumOptionsProcessed);
|
||
|
||
status = STATUS_UNSUCCESSFUL;
|
||
break;
|
||
}
|
||
|
||
status = STATUS_SUCCESS; // By default
|
||
|
||
switch (pOptionHeader->E_OptionType & ~PACKET_OPTION_TYPE_END_BIT)
|
||
{
|
||
case (PACKET_OPTION_NAK_LIST):
|
||
{
|
||
if (((PacketType == PACKET_TYPE_NAK) ||
|
||
(PacketType == PACKET_TYPE_NCF) ||
|
||
(PacketType == PACKET_TYPE_NNAK)) &&
|
||
((pOptionHeader->OptionLength >= PGM_PACKET_OPT_MIN_NAK_LIST_LENGTH) &&
|
||
(pOptionHeader->OptionLength <= PGM_PACKET_OPT_MAX_NAK_LIST_LENGTH)))
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessOptions",
|
||
"NAK_LIST: Num Naks=<%d>\n", (pOptionHeader->OptionLength-4)/4);
|
||
|
||
if (pNaksList)
|
||
{
|
||
ProcessNakOption (pOptionHeader, pNaksList);
|
||
}
|
||
else
|
||
{
|
||
ASSERT (0);
|
||
}
|
||
OptionsFlags |= PGM_OPTION_FLAG_NAK_LIST;
|
||
}
|
||
else
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions",
|
||
"NAK_LIST: PacketType=<%x>, Length=<0x%x>, pPacketOptions=<%x>\n",
|
||
PacketType, pOptionHeader->OptionLength, pPacketOptions);
|
||
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
/*
|
||
// Not supported for now!
|
||
case (PACKET_OPTION_REDIRECT):
|
||
{
|
||
ASSERT (pOptionHeader->OptionLength > 4); // 4 + sizeof(NLA)
|
||
break;
|
||
}
|
||
*/
|
||
|
||
case (PACKET_OPTION_FRAGMENT):
|
||
{
|
||
status = STATUS_UNSUCCESSFUL;
|
||
if (pOptionHeader->OptionLength == PGM_PACKET_OPT_FRAGMENT_LENGTH)
|
||
{
|
||
PgmCopyMemory (pOptionsData, (pOptionHeader + 1), (3 * sizeof(ULONG)));
|
||
if (pOptionHeader->Reserved_F_Opx & PACKET_OPTION_RES_F_OPX_ENCODED_BIT)
|
||
{
|
||
pPacketOptions->MessageFirstSequence = pOptionsData[0];
|
||
pPacketOptions->MessageOffset = pOptionsData[1];
|
||
pPacketOptions->MessageLength = pOptionsData[2];
|
||
pPacketOptions->FECContext.FragmentOptSpecific = pOptionHeader->U_OptSpecific;
|
||
|
||
status = STATUS_SUCCESS;
|
||
OptionsFlags |= PGM_OPTION_FLAG_FRAGMENT;
|
||
}
|
||
else
|
||
{
|
||
MessageFirstSequence = ntohl (pOptionsData[0]);
|
||
MessageOffset = ntohl (pOptionsData[1]);
|
||
MessageLength = ntohl (pOptionsData[2]);
|
||
if ((MessageLength) && (MessageOffset <= MessageLength))
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessOptions",
|
||
"FRAGMENT: MsgOffset/Length=<%d/%d>\n", MessageOffset, MessageLength);
|
||
|
||
if (pPacketOptions)
|
||
{
|
||
pPacketOptions->MessageFirstSequence = MessageFirstSequence;
|
||
pPacketOptions->MessageOffset = MessageOffset;
|
||
pPacketOptions->MessageLength = MessageLength;
|
||
// pPacketOptions->FECContext.FragmentOptSpecific = PACKET_OPTION_SPECIFIC_ENCODED_NULL_BIT;
|
||
}
|
||
|
||
status = STATUS_SUCCESS;
|
||
OptionsFlags |= PGM_OPTION_FLAG_FRAGMENT;
|
||
}
|
||
else
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions",
|
||
"FRAGMENT: MsgOffset/Length=<%d/%d>\n", MessageOffset, MessageLength);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions",
|
||
"FRAGMENT: OptionLength=<%d> != PGM_PACKET_OPT_FRAGMENT_LENGTH=<%d>\n",
|
||
pOptionHeader->OptionLength, PGM_PACKET_OPT_FRAGMENT_LENGTH);
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case (PACKET_OPTION_JOIN):
|
||
{
|
||
if (pOptionHeader->OptionLength == PGM_PACKET_OPT_JOIN_LENGTH)
|
||
{
|
||
PgmCopyMemory (pOptionsData, (pOptionHeader + 1), sizeof(ULONG));
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessOptions",
|
||
"JOIN: LateJoinerSeq=<%d>\n", ntohl (pOptionsData[0]));
|
||
|
||
if (pPacketOptions)
|
||
{
|
||
pPacketOptions->LateJoinerSequence = ntohl (pOptionsData[0]);
|
||
}
|
||
|
||
OptionsFlags |= PGM_OPTION_FLAG_JOIN;
|
||
}
|
||
else
|
||
{
|
||
status = STATUS_UNSUCCESSFUL;
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions",
|
||
"JOIN: OptionLength=<%d> != PGM_PACKET_OPT_JOIN_LENGTH=<%d>\n",
|
||
pOptionHeader->OptionLength, PGM_PACKET_OPT_JOIN_LENGTH);
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case (PACKET_OPTION_SYN):
|
||
{
|
||
if (pOptionHeader->OptionLength == PGM_PACKET_OPT_SYN_LENGTH)
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessOptions",
|
||
"SYN\n");
|
||
|
||
OptionsFlags |= PGM_OPTION_FLAG_SYN;
|
||
}
|
||
else
|
||
{
|
||
status = STATUS_UNSUCCESSFUL;
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions",
|
||
"SYN: OptionLength=<%d> != PGM_PACKET_OPT_SYN_LENGTH=<%d>\n",
|
||
pOptionHeader->OptionLength, PGM_PACKET_OPT_SYN_LENGTH);
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case (PACKET_OPTION_FIN):
|
||
{
|
||
if (pOptionHeader->OptionLength == PGM_PACKET_OPT_FIN_LENGTH)
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessOptions",
|
||
"FIN\n");
|
||
|
||
OptionsFlags |= PGM_OPTION_FLAG_FIN;
|
||
}
|
||
else
|
||
{
|
||
status = STATUS_UNSUCCESSFUL;
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions",
|
||
"FIN: OptionLength=<%d> != PGM_PACKET_OPT_FIN_LENGTH=<%d>\n",
|
||
pOptionHeader->OptionLength, PGM_PACKET_OPT_FIN_LENGTH);
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case (PACKET_OPTION_RST):
|
||
{
|
||
if (pOptionHeader->OptionLength == PGM_PACKET_OPT_RST_LENGTH)
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessOptions",
|
||
"RST\n");
|
||
|
||
OptionsFlags |= PGM_OPTION_FLAG_RST;
|
||
}
|
||
else
|
||
{
|
||
status = STATUS_UNSUCCESSFUL;
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions",
|
||
"RST: OptionLength=<%d> != PGM_PACKET_OPT_RST_LENGTH=<%d>\n",
|
||
pOptionHeader->OptionLength, PGM_PACKET_OPT_RST_LENGTH);
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
//
|
||
// FEC options
|
||
//
|
||
case (PACKET_OPTION_PARITY_PRM):
|
||
{
|
||
if (pOptionHeader->OptionLength == PGM_PACKET_OPT_PARITY_PRM_LENGTH)
|
||
{
|
||
PgmCopyMemory (pOptionsData, (pOptionHeader + 1), sizeof(ULONG));
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessOptions",
|
||
"PARITY_PRM: OptionsSpecific=<%x>, FECGroupInfo=<%d>\n",
|
||
pOptionHeader->U_OptSpecific, ntohl (pOptionsData[0]));
|
||
|
||
if (pPacketOptions)
|
||
{
|
||
pOptionsData[0] = ntohl (pOptionsData[0]);
|
||
ASSERT (((UCHAR) pOptionsData[0]) == pOptionsData[0]);
|
||
pPacketOptions->FECContext.ReceiverFECOptions = pOptionHeader->U_OptSpecific;
|
||
pPacketOptions->FECContext.FECGroupInfo = (UCHAR) pOptionsData[0];
|
||
}
|
||
|
||
OptionsFlags |= PGM_OPTION_FLAG_PARITY_PRM;
|
||
}
|
||
else
|
||
{
|
||
status = STATUS_UNSUCCESSFUL;
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions",
|
||
"PARITY_PRM: OptionLength=<%d> != PGM_PACKET_OPT_PARITY_PRM_LENGTH=<%d>\n",
|
||
pOptionHeader->OptionLength, PGM_PACKET_OPT_PARITY_PRM_LENGTH);
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case (PACKET_OPTION_PARITY_GRP):
|
||
{
|
||
if (pOptionHeader->OptionLength == PGM_PACKET_OPT_PARITY_GRP_LENGTH)
|
||
{
|
||
PgmCopyMemory (pOptionsData, (pOptionHeader + 1), sizeof(ULONG));
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessOptions",
|
||
"PARITY_GRP: FECGroupInfo=<%d>\n",
|
||
ntohl (pOptionsData[0]));
|
||
|
||
if (pPacketOptions)
|
||
{
|
||
pOptionsData[0] = ntohl (pOptionsData[0]);
|
||
ASSERT (((UCHAR) pOptionsData[0]) == pOptionsData[0]);
|
||
pPacketOptions->FECContext.FECGroupInfo = (UCHAR) pOptionsData[0];
|
||
}
|
||
|
||
OptionsFlags |= PGM_OPTION_FLAG_PARITY_GRP;
|
||
}
|
||
else
|
||
{
|
||
status = STATUS_UNSUCCESSFUL;
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions",
|
||
"PARITY_GRP: OptionLength=<%d> != PGM_PACKET_OPT_PARITY_GRP_LENGTH=<%d>\n",
|
||
pOptionHeader->OptionLength, PGM_PACKET_OPT_PARITY_GRP_LENGTH);
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case (PACKET_OPTION_CURR_TGSIZE):
|
||
{
|
||
if (pOptionHeader->OptionLength == PGM_PACKET_OPT_PARITY_CUR_TGSIZE_LENGTH)
|
||
{
|
||
PgmCopyMemory (pOptionsData, (pOptionHeader + 1), sizeof(ULONG));
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessOptions",
|
||
"CURR_TGSIZE: NumPacketsInThisGroup=<%d>\n",
|
||
ntohl (pOptionsData[0]));
|
||
|
||
if (pPacketOptions)
|
||
{
|
||
pPacketOptions->FECContext.NumPacketsInThisGroup = (UCHAR) (ntohl (pOptionsData[0]));
|
||
}
|
||
|
||
OptionsFlags |= PGM_OPTION_FLAG_PARITY_CUR_TGSIZE;
|
||
}
|
||
else
|
||
{
|
||
status = STATUS_UNSUCCESSFUL;
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions",
|
||
"PARITY_GRP: OptionLength=<%d> != PGM_PACKET_OPT_PARITY_CUR_TGSIZE_LENGTH=<%d>\n",
|
||
pOptionHeader->OptionLength, PGM_PACKET_OPT_PARITY_CUR_TGSIZE_LENGTH);
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case (PACKET_OPTION_REDIRECT):
|
||
case (PACKET_OPTION_CR):
|
||
case (PACKET_OPTION_CRQST):
|
||
case (PACKET_OPTION_NAK_BO_IVL):
|
||
case (PACKET_OPTION_NAK_BO_RNG):
|
||
case (PACKET_OPTION_NBR_UNREACH):
|
||
case (PACKET_OPTION_PATH_NLA):
|
||
case (PACKET_OPTION_INVALID):
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "ProcessOptions",
|
||
"WARNING: PacketType=<%x>: Unhandled Option=<%x>, OptionLength=<%d>\n",
|
||
PacketType, (pOptionHeader->E_OptionType & ~PACKET_OPTION_TYPE_END_BIT), pOptionHeader->OptionLength);
|
||
|
||
OptionsFlags |= PGM_OPTION_FLAG_UNRECOGNIZED;
|
||
break;
|
||
}
|
||
|
||
default:
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions",
|
||
"PacketType=<%x>: Unrecognized Option=<%x>, OptionLength=<%d>\n",
|
||
PacketType, (pOptionHeader->E_OptionType & ~PACKET_OPTION_TYPE_END_BIT), pOptionHeader->OptionLength);
|
||
ASSERT (0); // We do not recognize this option, but we will continue anyway!
|
||
|
||
OptionsFlags |= PGM_OPTION_FLAG_UNRECOGNIZED;
|
||
status = STATUS_UNSUCCESSFUL;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
break;
|
||
}
|
||
|
||
NumOptionsProcessed++;
|
||
BytesLeft -= pOptionHeader->OptionLength;
|
||
|
||
if (pOptionHeader->E_OptionType & PACKET_OPTION_TYPE_END_BIT)
|
||
{
|
||
break;
|
||
}
|
||
|
||
pOptionHeader = (tPACKET_OPTION_GENERIC UNALIGNED *)
|
||
(((UCHAR *) pOptionHeader) + pOptionHeader->OptionLength);
|
||
|
||
status = STATUS_UNSUCCESSFUL; // Init for next option!
|
||
} while (BytesLeft >= sizeof(tPACKET_OPTION_GENERIC));
|
||
|
||
ASSERT (NT_SUCCESS (status));
|
||
if (NT_SUCCESS (status))
|
||
{
|
||
if ((BytesLeft + TotalOptionsLength) == BytesAvailable)
|
||
{
|
||
pPacketOptions->OptionsLength = TotalOptionsLength;
|
||
pPacketOptions->OptionsFlags = OptionsFlags;
|
||
}
|
||
else
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessOptions",
|
||
"BytesLeft=<%d> + TotalOptionsLength=<%d> != BytesAvailable=<%d>\n",
|
||
BytesLeft, TotalOptionsLength, BytesAvailable);
|
||
|
||
status = STATUS_INVALID_BUFFER_SIZE;
|
||
}
|
||
}
|
||
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "ProcessOptions",
|
||
"Processed <%d> options, TotalOptionsLength=<%d>\n", NumOptionsProcessed, TotalOptionsLength);
|
||
|
||
return (status);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
ULONG
|
||
AdjustReceiveBufferLists(
|
||
IN tRECEIVE_SESSION *pReceive
|
||
)
|
||
{
|
||
tNAK_FORWARD_DATA *pNak;
|
||
UCHAR TotalPackets, i;
|
||
ULONG NumMoved = 0;
|
||
ULONG DataPacketsMoved = 0;
|
||
|
||
//
|
||
// Assume we have no Naks pending
|
||
//
|
||
pReceive->pReceiver->FirstNakSequenceNumber = pReceive->pReceiver->FurthestKnownGroupSequenceNumber
|
||
+ pReceive->FECGroupSize;
|
||
while (!IsListEmpty (&pReceive->pReceiver->NaksForwardDataList))
|
||
{
|
||
//
|
||
// Move any Naks contexts for which the group is complete
|
||
// to the BufferedDataList
|
||
//
|
||
pNak = CONTAINING_RECORD (pReceive->pReceiver->NaksForwardDataList.Flink, tNAK_FORWARD_DATA, Linkage);
|
||
if (((pNak->NumDataPackets + pNak->NumParityPackets) < pNak->PacketsInGroup) &&
|
||
((pNak->NextIndexToIndicate + pNak->NumDataPackets) < pNak->PacketsInGroup))
|
||
{
|
||
pReceive->pReceiver->FirstNakSequenceNumber = pNak->SequenceNumber;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If this is a partial group with extraneous parity packets,
|
||
// remove the parity packets
|
||
//
|
||
if ((pNak->NextIndexToIndicate) &&
|
||
(pNak->NumParityPackets) &&
|
||
((pNak->NextIndexToIndicate + pNak->NumDataPackets) >= pNak->PacketsInGroup))
|
||
{
|
||
//
|
||
// Start from the end and go backwards
|
||
//
|
||
i = TotalPackets = pNak->NumDataPackets + pNak->NumParityPackets;
|
||
while (i && pNak->NumParityPackets)
|
||
{
|
||
i--; // Convert from packet # to index
|
||
if (pNak->pPendingData[i].PacketIndex >= pNak->OriginalGroupSize)
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "AdjustReceiveBufferLists",
|
||
"Extraneous parity [%d] -- NextIndex=<%d>, Data=<%d>, Parity=<%d>, PktsInGrp=<%d>\n",
|
||
i, (ULONG) pNak->NextIndexToIndicate, (ULONG) pNak->NumDataPackets,
|
||
(ULONG) pNak->NumParityPackets, (ULONG) pNak->PacketsInGroup);
|
||
|
||
PgmFreeMem (pNak->pPendingData[i].pDataPacket);
|
||
if (i != (TotalPackets - 1))
|
||
{
|
||
PgmCopyMemory (&pNak->pPendingData[i], &pNak->pPendingData[TotalPackets-1], sizeof (tPENDING_DATA));
|
||
}
|
||
PgmZeroMemory (&pNak->pPendingData[TotalPackets-1], sizeof (tPENDING_DATA));
|
||
pNak->NumParityPackets--;
|
||
|
||
TotalPackets--;
|
||
|
||
pReceive->pReceiver->DataPacketsPendingNaks--;
|
||
pReceive->pReceiver->TotalDataPacketsBuffered--;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Re-Init all the indices
|
||
//
|
||
for (i=0; i<pNak->OriginalGroupSize; i++)
|
||
{
|
||
pNak->pPendingData[i].ActualIndexOfDataPacket = pNak->OriginalGroupSize;
|
||
}
|
||
|
||
//
|
||
// Set the indices only for the data packets
|
||
//
|
||
for (i=0; i<TotalPackets; i++)
|
||
{
|
||
if (pNak->pPendingData[i].PacketIndex < pNak->OriginalGroupSize)
|
||
{
|
||
pNak->pPendingData[pNak->pPendingData[i].PacketIndex].ActualIndexOfDataPacket = i;
|
||
}
|
||
}
|
||
}
|
||
|
||
RemoveEntryList (&pNak->Linkage);
|
||
InsertTailList (&pReceive->pReceiver->BufferedDataList, &pNak->Linkage);
|
||
NumMoved++;
|
||
DataPacketsMoved += (pNak->NumDataPackets + pNak->NumParityPackets);
|
||
}
|
||
|
||
pReceive->pReceiver->NumPacketGroupsPendingClient += NumMoved;
|
||
pReceive->pReceiver->DataPacketsPendingIndicate += DataPacketsMoved;
|
||
pReceive->pReceiver->DataPacketsPendingNaks -= DataPacketsMoved;
|
||
|
||
ASSERT (pReceive->pReceiver->TotalDataPacketsBuffered == (pReceive->pReceiver->DataPacketsPendingIndicate +
|
||
pReceive->pReceiver->DataPacketsPendingNaks));
|
||
|
||
return (NumMoved);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
VOID
|
||
AdjustNcfRDataResponseTimes(
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN PNAK_FORWARD_DATA pLastNak
|
||
)
|
||
{
|
||
ULONGLONG NcfRDataTickCounts;
|
||
|
||
NcfRDataTickCounts = PgmDynamicConfig.ReceiversTimerTickCount - pLastNak->FirstNcfTickCount;
|
||
pReceive->pReceiver->StatSumOfNcfRDataTicks += NcfRDataTickCounts;
|
||
pReceive->pReceiver->NumNcfRDataTicksSamples++;
|
||
|
||
if (!pReceive->pReceiver->NumNcfRDataTicksSamples)
|
||
{
|
||
//
|
||
// This will be the divisor below, so it has to be non-zero!
|
||
//
|
||
ASSERT (0);
|
||
return;
|
||
}
|
||
|
||
if ((NcfRDataTickCounts > pReceive->pReceiver->MaxOutstandingNakTimeout) &&
|
||
(pReceive->pReceiver->MaxOutstandingNakTimeout !=
|
||
pReceive->pReceiver->MaxRDataResponseTCFromWindow))
|
||
{
|
||
if (pReceive->pReceiver->MaxRDataResponseTCFromWindow &&
|
||
NcfRDataTickCounts > pReceive->pReceiver->MaxRDataResponseTCFromWindow)
|
||
{
|
||
pReceive->pReceiver->MaxOutstandingNakTimeout = pReceive->pReceiver->MaxRDataResponseTCFromWindow;
|
||
}
|
||
else
|
||
{
|
||
pReceive->pReceiver->MaxOutstandingNakTimeout = NcfRDataTickCounts;
|
||
}
|
||
|
||
//
|
||
// Since we just updated the Max value, we should also
|
||
// recalculate the default timeout
|
||
//
|
||
pReceive->pReceiver->AverageNcfRDataResponseTC = pReceive->pReceiver->StatSumOfNcfRDataTicks /
|
||
pReceive->pReceiver->NumNcfRDataTicksSamples;
|
||
NcfRDataTickCounts = (pReceive->pReceiver->AverageNcfRDataResponseTC +
|
||
pReceive->pReceiver->MaxOutstandingNakTimeout) >> 1;
|
||
if (NcfRDataTickCounts > (pReceive->pReceiver->AverageNcfRDataResponseTC << 1))
|
||
{
|
||
NcfRDataTickCounts = pReceive->pReceiver->AverageNcfRDataResponseTC << 1;
|
||
}
|
||
|
||
if (NcfRDataTickCounts > pReceive->pReceiver->OutstandingNakTimeout)
|
||
{
|
||
pReceive->pReceiver->OutstandingNakTimeout = NcfRDataTickCounts;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
VOID
|
||
UpdateSpmIntervalInformation(
|
||
IN tRECEIVE_SESSION *pReceive
|
||
)
|
||
{
|
||
ULONG LastIntervalTickCount = (ULONG) (PgmDynamicConfig.ReceiversTimerTickCount -
|
||
pReceive->pReceiver->LastSpmTickCount);
|
||
|
||
if (!LastIntervalTickCount)
|
||
{
|
||
return;
|
||
}
|
||
|
||
pReceive->pReceiver->LastSpmTickCount = PgmDynamicConfig.ReceiversTimerTickCount;
|
||
if (LastIntervalTickCount > pReceive->pReceiver->MaxSpmInterval)
|
||
{
|
||
pReceive->pReceiver->MaxSpmInterval = LastIntervalTickCount;
|
||
}
|
||
|
||
/*
|
||
if (pReceive->pReceiver->NumSpmIntervalSamples)
|
||
{
|
||
pReceive->pReceiver->StatSumOfSpmIntervals += pReceive->pReceiver->LastSpmTickCount;
|
||
pReceive->pReceiver->NumSpmIntervalSamples++;
|
||
pReceive->pReceiver->AverageSpmInterval = pReceive->pReceiver->StatSumOfSpmIntervals /
|
||
pReceive->pReceiver->NumSpmIntervalSamples;
|
||
}
|
||
*/
|
||
}
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
|
||
VOID
|
||
UpdateRealTimeWindowInformation(
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN SEQ_TYPE LeadingEdgeSeqNumber,
|
||
IN SEQ_TYPE TrailingEdgeSeqNumber
|
||
)
|
||
{
|
||
SEQ_TYPE SequencesInWindow = 1 + LeadingEdgeSeqNumber - TrailingEdgeSeqNumber;
|
||
|
||
if (SEQ_GT (SequencesInWindow, pReceive->pReceiver->MaxSequencesInWindow))
|
||
{
|
||
pReceive->pReceiver->MaxSequencesInWindow = SequencesInWindow;
|
||
}
|
||
|
||
if (TrailingEdgeSeqNumber)
|
||
{
|
||
if ((!pReceive->pReceiver->MinSequencesInWindow) ||
|
||
SEQ_LT (SequencesInWindow, pReceive->pReceiver->MinSequencesInWindow))
|
||
{
|
||
pReceive->pReceiver->MinSequencesInWindow = SequencesInWindow;
|
||
}
|
||
|
||
pReceive->pReceiver->StatSumOfWindowSeqs += SequencesInWindow;
|
||
pReceive->pReceiver->NumWindowSamples++;
|
||
}
|
||
}
|
||
|
||
VOID
|
||
UpdateSampleTimeWindowInformation(
|
||
IN tRECEIVE_SESSION *pReceive
|
||
)
|
||
{
|
||
ULONGLONG NcfRDataTimeout;
|
||
|
||
//
|
||
// No need to update if there is no data
|
||
//
|
||
if (!pReceive->RateKBitsPerSecLast ||
|
||
!pReceive->pReceiver->NumWindowSamples || // Avoid divide by 0 error
|
||
!pReceive->TotalPacketsInLastInterval) // Avoid divide by 0 error
|
||
{
|
||
return;
|
||
}
|
||
ASSERT (INITIAL_NAK_OUTSTANDING_TIMEOUT_MSECS >= BASIC_TIMER_GRANULARITY_IN_MSECS);
|
||
|
||
//
|
||
// Now, update the window information
|
||
//
|
||
if (pReceive->pReceiver->StatSumOfWindowSeqs)
|
||
{
|
||
pReceive->pReceiver->AverageSequencesInWindow = pReceive->pReceiver->StatSumOfWindowSeqs /
|
||
pReceive->pReceiver->NumWindowSamples;
|
||
}
|
||
|
||
if (pReceive->pReceiver->AverageSequencesInWindow)
|
||
{
|
||
pReceive->pReceiver->WindowSizeLastInMSecs = ((pReceive->pReceiver->AverageSequencesInWindow *
|
||
pReceive->TotalBytes) << LOG2_BITS_PER_BYTE) /
|
||
(pReceive->TotalPacketsInLastInterval *
|
||
pReceive->RateKBitsPerSecLast);
|
||
}
|
||
else
|
||
{
|
||
pReceive->pReceiver->WindowSizeLastInMSecs = ((pReceive->pReceiver->MaxSequencesInWindow *
|
||
pReceive->TotalBytes) << LOG2_BITS_PER_BYTE) /
|
||
(pReceive->TotalPacketsInLastInterval *
|
||
pReceive->RateKBitsPerSecLast);
|
||
}
|
||
pReceive->pReceiver->MaxRDataResponseTCFromWindow = pReceive->pReceiver->WindowSizeLastInMSecs /
|
||
(NCF_WAITING_RDATA_MAX_RETRIES * BASIC_TIMER_GRANULARITY_IN_MSECS);
|
||
|
||
//
|
||
// Now, update the NcfRData timeout information
|
||
//
|
||
if (pReceive->pReceiver->StatSumOfNcfRDataTicks &&
|
||
pReceive->pReceiver->NumNcfRDataTicksSamples)
|
||
{
|
||
pReceive->pReceiver->AverageNcfRDataResponseTC = pReceive->pReceiver->StatSumOfNcfRDataTicks /
|
||
pReceive->pReceiver->NumNcfRDataTicksSamples;
|
||
}
|
||
|
||
if (pReceive->pReceiver->AverageNcfRDataResponseTC)
|
||
{
|
||
NcfRDataTimeout = (pReceive->pReceiver->AverageNcfRDataResponseTC +
|
||
pReceive->pReceiver->MaxOutstandingNakTimeout) >> 1;
|
||
if (NcfRDataTimeout > (pReceive->pReceiver->AverageNcfRDataResponseTC << 1))
|
||
{
|
||
NcfRDataTimeout = pReceive->pReceiver->AverageNcfRDataResponseTC << 1;
|
||
}
|
||
if (NcfRDataTimeout >
|
||
INITIAL_NAK_OUTSTANDING_TIMEOUT_MSECS/BASIC_TIMER_GRANULARITY_IN_MSECS)
|
||
{
|
||
pReceive->pReceiver->OutstandingNakTimeout = NcfRDataTimeout;
|
||
}
|
||
else
|
||
{
|
||
pReceive->pReceiver->OutstandingNakTimeout = INITIAL_NAK_OUTSTANDING_TIMEOUT_MSECS /
|
||
BASIC_TIMER_GRANULARITY_IN_MSECS;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
VOID
|
||
RemoveRedundantNaks(
|
||
IN tNAK_FORWARD_DATA *pNak,
|
||
IN BOOLEAN fEliminateExtraParityPackets
|
||
)
|
||
{
|
||
UCHAR i, TotalPackets;
|
||
|
||
ASSERT (fEliminateExtraParityPackets || !pNak->NumParityPackets);
|
||
TotalPackets = pNak->NumDataPackets + pNak->NumParityPackets;
|
||
|
||
//
|
||
// First, eliminate the NULL Packets
|
||
//
|
||
if (pNak->PacketsInGroup < pNak->OriginalGroupSize)
|
||
{
|
||
i = 0;
|
||
while (i < pNak->OriginalGroupSize)
|
||
{
|
||
if ((pNak->pPendingData[i].PacketIndex < pNak->PacketsInGroup) || // Non-NULL Data packet
|
||
(pNak->pPendingData[i].PacketIndex >= pNak->OriginalGroupSize)) // Parity packet
|
||
{
|
||
//
|
||
// Ignore for now!
|
||
//
|
||
i++;
|
||
continue;
|
||
}
|
||
|
||
PgmFreeMem (pNak->pPendingData[i].pDataPacket);
|
||
if (i != (TotalPackets-1))
|
||
{
|
||
PgmCopyMemory (&pNak->pPendingData[i], &pNak->pPendingData[TotalPackets-1], sizeof (tPENDING_DATA));
|
||
}
|
||
PgmZeroMemory (&pNak->pPendingData[TotalPackets-1], sizeof (tPENDING_DATA));
|
||
pNak->NumDataPackets--;
|
||
TotalPackets--;
|
||
}
|
||
ASSERT (pNak->NumDataPackets <= TotalPackets);
|
||
|
||
if (fEliminateExtraParityPackets)
|
||
{
|
||
//
|
||
// If we still have extra parity packets, free those also
|
||
//
|
||
i = 0;
|
||
while ((i < TotalPackets) &&
|
||
(TotalPackets > pNak->PacketsInGroup))
|
||
{
|
||
ASSERT (pNak->NumParityPackets);
|
||
if (pNak->pPendingData[i].PacketIndex < pNak->OriginalGroupSize)
|
||
{
|
||
//
|
||
// Ignore data packets
|
||
//
|
||
i++;
|
||
continue;
|
||
}
|
||
|
||
PgmFreeMem (pNak->pPendingData[i].pDataPacket);
|
||
if (i != (TotalPackets-1))
|
||
{
|
||
PgmCopyMemory (&pNak->pPendingData[i], &pNak->pPendingData[TotalPackets-1], sizeof (tPENDING_DATA));
|
||
}
|
||
PgmZeroMemory (&pNak->pPendingData[TotalPackets-1], sizeof (tPENDING_DATA));
|
||
pNak->NumParityPackets--;
|
||
TotalPackets--;
|
||
}
|
||
|
||
ASSERT (TotalPackets <= pNak->PacketsInGroup);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Re-Init all the indices
|
||
//
|
||
for (i=0; i<pNak->OriginalGroupSize; i++)
|
||
{
|
||
pNak->pPendingData[i].ActualIndexOfDataPacket = pNak->OriginalGroupSize;
|
||
}
|
||
|
||
//
|
||
// Set the indices only for the data packets
|
||
//
|
||
for (i=0; i<TotalPackets; i++)
|
||
{
|
||
if (pNak->pPendingData[i].PacketIndex < pNak->OriginalGroupSize)
|
||
{
|
||
pNak->pPendingData[pNak->pPendingData[i].PacketIndex].ActualIndexOfDataPacket = i;
|
||
}
|
||
}
|
||
|
||
if (((pNak->NumDataPackets + pNak->NumParityPackets) >= pNak->PacketsInGroup) ||
|
||
((pNak->NextIndexToIndicate + pNak->NumDataPackets) >= pNak->PacketsInGroup))
|
||
{
|
||
ASSERT ((!fEliminateExtraParityPackets) ||
|
||
(!IsListEmpty (&pNak->PendingLinkage)));
|
||
|
||
RemoveEntryList (&pNak->PendingLinkage);
|
||
InitializeListHead (&pNak->PendingLinkage);
|
||
}
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
VOID
|
||
PgmSendNakCompletion(
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN tNAK_CONTEXT *pNakContext,
|
||
IN NTSTATUS status
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the Completion routine called by IP on completing a NakSend
|
||
|
||
Arguments:
|
||
|
||
IN pReceive -- Receive context
|
||
IN pNakContext -- Nak Context to be free'ed
|
||
IN status -- status of send from tansport
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
{
|
||
PGMLockHandle OldIrq;
|
||
|
||
PgmLock (pReceive, OldIrq);
|
||
if (NT_SUCCESS (status))
|
||
{
|
||
//
|
||
// Set the Receiver Nak statistics
|
||
//
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_SEND, "PgmSendNakCompletion",
|
||
"SUCCEEDED\n");
|
||
}
|
||
else
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_SEND, "PgmSendNakCompletion",
|
||
"status=<%x>\n", status);
|
||
}
|
||
|
||
if (!(--pNakContext->RefCount))
|
||
{
|
||
PgmUnlock (pReceive, OldIrq);
|
||
|
||
//
|
||
// Free the Memory and deref the Session context for this Nak
|
||
//
|
||
PgmFreeMem (pNakContext);
|
||
PGM_DEREFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_SEND_NAK);
|
||
}
|
||
else
|
||
{
|
||
PgmUnlock (pReceive, OldIrq);
|
||
}
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
PgmSendNak(
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN tNAKS_CONTEXT *pNakSequences
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sends a Nak packet with the number of sequences specified
|
||
|
||
Arguments:
|
||
|
||
IN pReceive -- Receive context
|
||
IN pNakSequences -- List of Sequence #s
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Final status of the operation
|
||
|
||
--*/
|
||
{
|
||
tBASIC_NAK_NCF_PACKET_HEADER *pNakPacket;
|
||
tNAK_CONTEXT *pNakContext;
|
||
tPACKET_OPTION_LENGTH *pPacketExtension;
|
||
tPACKET_OPTION_GENERIC *pOptionHeader;
|
||
ULONG i;
|
||
ULONG XSum;
|
||
USHORT OptionsLength = 0;
|
||
NTSTATUS status;
|
||
|
||
if ((!pNakSequences->NumSequences) ||
|
||
(pNakSequences->NumSequences > (MAX_SEQUENCES_PER_NAK_OPTION+1)) ||
|
||
(!(pNakContext = PgmAllocMem ((2*sizeof(ULONG)+PGM_MAX_NAK_NCF_HEADER_LENGTH), PGM_TAG('2')))))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_SEND, "PgmSendNak",
|
||
"STATUS_INSUFFICIENT_RESOURCES allocating pNakContext\n");
|
||
return (STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
PgmZeroMemory (pNakContext, (2*sizeof(ULONG)+PGM_MAX_NAK_NCF_HEADER_LENGTH));
|
||
|
||
pNakContext->RefCount = 2; // 1 for the unicast, and the other for the MCast Nak
|
||
pNakPacket = &pNakContext->NakPacket;
|
||
pNakPacket->CommonHeader.SrcPort = htons (pReceive->pReceiver->ListenMCastPort);
|
||
pNakPacket->CommonHeader.DestPort = htons (pReceive->TSIPort);
|
||
pNakPacket->CommonHeader.Type = PACKET_TYPE_NAK;
|
||
|
||
if (pNakSequences->NakType == NAK_TYPE_PARITY)
|
||
{
|
||
pNakPacket->CommonHeader.Options = PACKET_HEADER_OPTIONS_PARITY;
|
||
pReceive->pReceiver->TotalParityNaksSent += pNakSequences->NumSequences;
|
||
}
|
||
else
|
||
{
|
||
pNakPacket->CommonHeader.Options = 0;
|
||
pReceive->pReceiver->TotalSelectiveNaksSent += pNakSequences->NumSequences;
|
||
}
|
||
PgmCopyMemory (&pNakPacket->CommonHeader.gSourceId, &pReceive->GSI, SOURCE_ID_LENGTH);
|
||
|
||
pNakPacket->RequestedSequenceNumber = htonl ((ULONG) pNakSequences->Sequences[0]);
|
||
pNakPacket->SourceNLA.NLA_AFI = htons (IPV4_NLA_AFI);
|
||
pNakPacket->SourceNLA.IpAddress = htonl (pReceive->pReceiver->SenderIpAddress);
|
||
pNakPacket->MCastGroupNLA.NLA_AFI = htons (IPV4_NLA_AFI);
|
||
pNakPacket->MCastGroupNLA.IpAddress = htonl (pReceive->pReceiver->ListenMCastIpAddress);
|
||
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_SEND, "PgmSendNak",
|
||
"Sending Naks for:\n\t[%d]\n", (ULONG) pNakSequences->Sequences[0]);
|
||
|
||
if (pNakSequences->NumSequences > 1)
|
||
{
|
||
pPacketExtension = (tPACKET_OPTION_LENGTH *) (pNakPacket + 1);
|
||
pPacketExtension->Type = PACKET_OPTION_LENGTH;
|
||
pPacketExtension->Length = PGM_PACKET_EXTENSION_LENGTH;
|
||
OptionsLength += PGM_PACKET_EXTENSION_LENGTH;
|
||
|
||
pOptionHeader = (tPACKET_OPTION_GENERIC *) (pPacketExtension + 1);
|
||
pOptionHeader->E_OptionType = PACKET_OPTION_NAK_LIST;
|
||
pOptionHeader->OptionLength = 4 + (UCHAR) ((pNakSequences->NumSequences-1) * sizeof(ULONG));
|
||
for (i=1; i<pNakSequences->NumSequences; i++)
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_SEND, "PgmSendNak",
|
||
"\t[%d]\n", (ULONG) pNakSequences->Sequences[i]);
|
||
|
||
((PULONG) (pOptionHeader))[i] = htonl ((ULONG) pNakSequences->Sequences[i]);
|
||
}
|
||
|
||
pOptionHeader->E_OptionType |= PACKET_OPTION_TYPE_END_BIT; // One and only (last) opt
|
||
pNakPacket->CommonHeader.Options |=(PACKET_HEADER_OPTIONS_PRESENT |
|
||
PACKET_HEADER_OPTIONS_NETWORK_SIGNIFICANT);
|
||
OptionsLength = PGM_PACKET_EXTENSION_LENGTH + pOptionHeader->OptionLength;
|
||
pPacketExtension->TotalOptionsLength = htons (OptionsLength);
|
||
}
|
||
|
||
OptionsLength += sizeof(tBASIC_NAK_NCF_PACKET_HEADER); // Now is whole pkt
|
||
pNakPacket->CommonHeader.Checksum = 0;
|
||
XSum = 0;
|
||
XSum = tcpxsum (XSum, (CHAR *) pNakPacket, OptionsLength);
|
||
pNakPacket->CommonHeader.Checksum = (USHORT) (~XSum);
|
||
|
||
PGM_REFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_SEND_NAK, FALSE);
|
||
|
||
//
|
||
// First multicast the Nak
|
||
//
|
||
status = TdiSendDatagram (pReceive->pReceiver->pAddress->pFileObject,
|
||
pReceive->pReceiver->pAddress->pDeviceObject,
|
||
pNakPacket,
|
||
OptionsLength,
|
||
PgmSendNakCompletion, // Completion
|
||
pReceive, // Context1
|
||
pNakContext, // Context2
|
||
pReceive->pReceiver->ListenMCastIpAddress,
|
||
pReceive->pReceiver->ListenMCastPort);
|
||
|
||
ASSERT (NT_SUCCESS (status));
|
||
|
||
//
|
||
// Now, Unicast the Nak
|
||
//
|
||
status = TdiSendDatagram (pReceive->pReceiver->pAddress->pFileObject,
|
||
pReceive->pReceiver->pAddress->pDeviceObject,
|
||
pNakPacket,
|
||
OptionsLength,
|
||
PgmSendNakCompletion, // Completion
|
||
pReceive, // Context1
|
||
pNakContext, // Context2
|
||
pReceive->pReceiver->LastSpmSource,
|
||
IPPROTO_RM);
|
||
|
||
ASSERT (NT_SUCCESS (status));
|
||
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "PgmSendNak",
|
||
"Sent %s Nak for <%d> Sequences [%d--%d] to <%x:%d>\n",
|
||
(pNakSequences->NakType == NAK_TYPE_PARITY ? "PARITY" : "SELECTIVE"),
|
||
pNakSequences->NumSequences, (ULONG) pNakSequences->Sequences[0],
|
||
(ULONG) pNakSequences->Sequences[pNakSequences->NumSequences-1],
|
||
pReceive->pReceiver->SenderIpAddress, IPPROTO_RM);
|
||
|
||
return (status);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
VOID
|
||
CheckSendPendingNaks(
|
||
IN tADDRESS_CONTEXT *pAddress,
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN PGMLockHandle *pOldIrq
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine checks if any Naks need to be sent and sends them
|
||
as required
|
||
|
||
The PgmDynamicConfig lock is held on entry and exit from
|
||
this routine
|
||
|
||
Arguments:
|
||
|
||
IN pAddress -- Address object context
|
||
IN pReceive -- Receive context
|
||
IN pOldIrq -- Irq for PgmDynamicConfig
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
{
|
||
tNAKS_CONTEXT *pNakContext, *pSelectiveNaks = NULL;
|
||
tNAKS_CONTEXT *pParityNaks = NULL;
|
||
LIST_ENTRY NaksList;
|
||
LIST_ENTRY *pEntry;
|
||
tNAK_FORWARD_DATA *pNak;
|
||
SEQ_TYPE LastSequenceNumber;
|
||
PGMLockHandle OldIrq, OldIrq1;
|
||
ULONG NumMissingPackets, TotalSeqsNacked = 0;
|
||
BOOLEAN fSendSelectiveNak, fSendParityNak;
|
||
UCHAR i, j;
|
||
ULONG NumPendingNaks = 0;
|
||
ULONG NumOutstandingNaks = 0;
|
||
|
||
if ((!pReceive->pReceiver->LastSpmSource) ||
|
||
((pReceive->pReceiver->DataPacketsPendingNaks <= OUT_OF_ORDER_PACKETS_BEFORE_NAK) &&
|
||
((pReceive->pReceiver->LastNakSendTime + (NAK_MAX_WAIT_TIMEOUT_MSECS/BASIC_TIMER_GRANULARITY_IN_MSECS)) >
|
||
PgmDynamicConfig.ReceiversTimerTickCount)))
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "CheckSendPendingNaks",
|
||
"No Naks to send for pReceive=<%p>, LastSpmSource=<%x>, NumDataPackets=<%d>, LastSendTime=<%d:%d>, Current=<%d:%d>\n",
|
||
pReceive, pReceive->pReceiver->LastSpmSource,
|
||
pReceive->pReceiver->DataPacketsPendingNaks,
|
||
pReceive->pReceiver->LastNakSendTime+(NAK_MAX_WAIT_TIMEOUT_MSECS/BASIC_TIMER_GRANULARITY_IN_MSECS),
|
||
PgmDynamicConfig.ReceiversTimerTickCount);
|
||
|
||
return;
|
||
}
|
||
|
||
InitializeListHead (&NaksList);
|
||
if (!(pSelectiveNaks = PgmAllocMem (sizeof (tNAKS_CONTEXT), PGM_TAG('5'))) ||
|
||
!(pParityNaks = PgmAllocMem (sizeof (tNAKS_CONTEXT), PGM_TAG('6'))))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_SEND, "CheckSendPendingNaks",
|
||
"STATUS_INSUFFICIENT_RESOURCES allocating pNakContext\n");
|
||
|
||
if (pSelectiveNaks)
|
||
{
|
||
PgmFreeMem (pSelectiveNaks);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
PgmZeroMemory (pSelectiveNaks, sizeof (tNAKS_CONTEXT));
|
||
PgmZeroMemory (pParityNaks, sizeof (tNAKS_CONTEXT));
|
||
pParityNaks->NakType = NAK_TYPE_PARITY;
|
||
pSelectiveNaks->NakType = NAK_TYPE_SELECTIVE;
|
||
InsertTailList (&NaksList, &pParityNaks->Linkage);
|
||
InsertTailList (&NaksList, &pSelectiveNaks->Linkage);
|
||
|
||
PgmLock (pAddress, OldIrq);
|
||
PgmLock (pReceive, OldIrq1);
|
||
|
||
AdjustReceiveBufferLists (pReceive);
|
||
|
||
fSendSelectiveNak = fSendParityNak = FALSE;
|
||
pEntry = &pReceive->pReceiver->PendingNaksList;
|
||
while ((pEntry = pEntry->Flink) != &pReceive->pReceiver->PendingNaksList)
|
||
{
|
||
pNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, PendingLinkage);
|
||
|
||
NumMissingPackets = pNak->PacketsInGroup - (pNak->NumDataPackets + pNak->NumParityPackets);
|
||
ASSERT (NumMissingPackets);
|
||
|
||
//
|
||
// if this Nak is outside the trailing window, then we are hosed!
|
||
//
|
||
if (SEQ_GT (pReceive->pReceiver->LastTrailingEdgeSeqNum, pNak->SequenceNumber))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "CheckSendPendingNaks",
|
||
"Sequence # [%d] out of trailing edge <%d>, NumNcfs received=<%d>\n",
|
||
(ULONG) pNak->SequenceNumber,
|
||
(ULONG) pReceive->pReceiver->LastTrailingEdgeSeqNum,
|
||
pNak->WaitingRDataRetries);
|
||
pReceive->SessionFlags |= PGM_SESSION_FLAG_NAK_TIMED_OUT;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// See if we are currently in NAK pending mode
|
||
//
|
||
if (pNak->PendingNakTimeout)
|
||
{
|
||
NumPendingNaks += NumMissingPackets;
|
||
if (PgmDynamicConfig.ReceiversTimerTickCount > pNak->PendingNakTimeout)
|
||
{
|
||
//
|
||
// Time out Naks only after we have received a FIN!
|
||
//
|
||
if (pNak->WaitingNcfRetries++ >= NAK_WAITING_NCF_MAX_RETRIES)
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "CheckSendPendingNaks",
|
||
"Pending Nak for Sequence # [%d] Timed out! Num Ncfs received=<%d>, Window=<%d--%d> ( %d seqs)\n",
|
||
(ULONG) pNak->SequenceNumber, pNak->WaitingNcfRetries,
|
||
(ULONG) pReceive->pReceiver->LastTrailingEdgeSeqNum,
|
||
(ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber,
|
||
(ULONG) (1+pReceive->pReceiver->FurthestKnownGroupSequenceNumber-
|
||
pReceive->pReceiver->LastTrailingEdgeSeqNum));
|
||
pReceive->SessionFlags |= PGM_SESSION_FLAG_NAK_TIMED_OUT;
|
||
break;
|
||
}
|
||
|
||
if ((pNak->PacketsInGroup > 1) &&
|
||
(pReceive->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT))
|
||
{
|
||
ASSERT (NumMissingPackets <= pReceive->FECGroupSize);
|
||
pParityNaks->Sequences[pParityNaks->NumSequences] = (SEQ_TYPE) (pNak->SequenceNumber + NumMissingPackets - 1);
|
||
|
||
if (++pParityNaks->NumSequences == (MAX_SEQUENCES_PER_NAK_OPTION+1))
|
||
{
|
||
fSendParityNak = TRUE;
|
||
}
|
||
pNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
((NAK_REPEAT_INTERVAL_MSECS + (NAK_RANDOM_BACKOFF_MSECS/NumMissingPackets)) /
|
||
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
||
TotalSeqsNacked += NumMissingPackets;
|
||
NumMissingPackets = 0;
|
||
}
|
||
else
|
||
{
|
||
for (i=pNak->NextIndexToIndicate; i<pNak->PacketsInGroup; i++)
|
||
{
|
||
if ((pNak->pPendingData[i].ActualIndexOfDataPacket >= pNak->OriginalGroupSize) &&
|
||
(!pNak->pPendingData[i].NcfsReceivedForActualIndex))
|
||
{
|
||
pSelectiveNaks->Sequences[pSelectiveNaks->NumSequences++] = pNak->SequenceNumber+i;
|
||
TotalSeqsNacked++;
|
||
if ((!--NumMissingPackets) ||
|
||
(pSelectiveNaks->NumSequences == (MAX_SEQUENCES_PER_NAK_OPTION+1)))
|
||
{
|
||
LastSequenceNumber = pNak->SequenceNumber+i;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!NumMissingPackets)
|
||
{
|
||
pNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
((NAK_REPEAT_INTERVAL_MSECS + NAK_RANDOM_BACKOFF_MSECS) /
|
||
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
||
}
|
||
|
||
if (pSelectiveNaks->NumSequences == (MAX_SEQUENCES_PER_NAK_OPTION+1))
|
||
{
|
||
fSendSelectiveNak = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (pNak->OutstandingNakTimeout)
|
||
{
|
||
NumOutstandingNaks += NumMissingPackets;
|
||
if (PgmDynamicConfig.ReceiversTimerTickCount > pNak->OutstandingNakTimeout)
|
||
{
|
||
//
|
||
// We have timed-out waiting for RData -- Reset the Timeout to send
|
||
// a Nak after the Random Backoff (if we have not exceeded the Data retries)
|
||
//
|
||
if (pNak->WaitingRDataRetries++ == NCF_WAITING_RDATA_MAX_RETRIES)
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "CheckSendPendingNaks",
|
||
"Outstanding Nak for Sequence # [%d] Timed out!, Window=<%d--%d> ( %d seqs), Ncfs=<%d>, FirstNak=<%d>\n",
|
||
(ULONG) pNak->SequenceNumber, (ULONG) pReceive->pReceiver->LastTrailingEdgeSeqNum,
|
||
(ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber,
|
||
(ULONG) (1+pReceive->pReceiver->FurthestKnownGroupSequenceNumber-pReceive->pReceiver->LastTrailingEdgeSeqNum),
|
||
pNak->WaitingRDataRetries, (ULONG) pReceive->pReceiver->FirstNakSequenceNumber);
|
||
|
||
pReceive->SessionFlags |= PGM_SESSION_FLAG_NAK_TIMED_OUT;
|
||
break;
|
||
}
|
||
|
||
pNak->WaitingNcfRetries = 0;
|
||
pNak->OutstandingNakTimeout = 0;
|
||
pNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
((NAK_RANDOM_BACKOFF_MSECS/NumMissingPackets) /
|
||
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
||
|
||
for (i=0; i<pNak->PacketsInGroup; i++)
|
||
{
|
||
pNak->pPendingData[i].NcfsReceivedForActualIndex = 0;
|
||
}
|
||
|
||
NumMissingPackets = 0;
|
||
}
|
||
}
|
||
|
||
while (fSendSelectiveNak || fSendParityNak)
|
||
{
|
||
if (fSendSelectiveNak)
|
||
{
|
||
if (!(pSelectiveNaks = PgmAllocMem (sizeof (tNAKS_CONTEXT), PGM_TAG('5'))))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_SEND, "CheckSendPendingNaks",
|
||
"STATUS_INSUFFICIENT_RESOURCES allocating pSelectiveNaks\n");
|
||
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
break;
|
||
}
|
||
|
||
PgmZeroMemory (pSelectiveNaks, sizeof (tNAKS_CONTEXT));
|
||
pSelectiveNaks->NakType = NAK_TYPE_SELECTIVE;
|
||
InsertTailList (&NaksList, &pSelectiveNaks->Linkage);
|
||
fSendSelectiveNak = FALSE;
|
||
}
|
||
|
||
if (fSendParityNak)
|
||
{
|
||
if (!(pParityNaks = PgmAllocMem (sizeof (tNAKS_CONTEXT), PGM_TAG('6'))))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_SEND, "CheckSendPendingNaks",
|
||
"STATUS_INSUFFICIENT_RESOURCES allocating pParityNaks\n");
|
||
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
break;
|
||
}
|
||
|
||
PgmZeroMemory (pParityNaks, sizeof (tNAKS_CONTEXT));
|
||
pParityNaks->NakType = NAK_TYPE_PARITY;
|
||
InsertTailList (&NaksList, &pParityNaks->Linkage);
|
||
fSendParityNak = FALSE;
|
||
}
|
||
|
||
//
|
||
// If we had some packets left to be sent from the
|
||
// last Nak, include those sequences now
|
||
//
|
||
if (NumMissingPackets)
|
||
{
|
||
for (i=(UCHAR) (1+LastSequenceNumber-pNak->SequenceNumber); i<pNak->PacketsInGroup; i++)
|
||
{
|
||
if (pNak->pPendingData[i].ActualIndexOfDataPacket >= pNak->OriginalGroupSize)
|
||
{
|
||
pSelectiveNaks->Sequences[pSelectiveNaks->NumSequences++] = pNak->SequenceNumber+i;
|
||
TotalSeqsNacked++;
|
||
if ((!--NumMissingPackets) ||
|
||
(pSelectiveNaks->NumSequences == (MAX_SEQUENCES_PER_NAK_OPTION+1)))
|
||
{
|
||
LastSequenceNumber = pNak->SequenceNumber+i;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// We could encounter a situation where we could have received
|
||
// a packet while sending the Nak, so we should reset our MissingPacket
|
||
// count accordingly
|
||
//
|
||
if (i >= pNak->PacketsInGroup)
|
||
{
|
||
NumMissingPackets = 0;
|
||
}
|
||
|
||
if (!NumMissingPackets)
|
||
{
|
||
pNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
((NAK_REPEAT_INTERVAL_MSECS + NAK_RANDOM_BACKOFF_MSECS) /
|
||
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
||
}
|
||
|
||
if (pSelectiveNaks->NumSequences == (MAX_SEQUENCES_PER_NAK_OPTION+1))
|
||
{
|
||
fSendSelectiveNak = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (pReceive->SessionFlags & PGM_SESSION_TERMINATED_ABORT)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
pReceive->pReceiver->NumPendingNaks = NumPendingNaks;
|
||
pReceive->pReceiver->NumOutstandingNaks = NumOutstandingNaks;
|
||
|
||
if (!IsListEmpty (&NaksList))
|
||
{
|
||
pReceive->pReceiver->LastNakSendTime = PgmDynamicConfig.ReceiversTimerTickCount;
|
||
}
|
||
|
||
PgmUnlock (pReceive, OldIrq1);
|
||
PgmUnlock (pAddress, OldIrq);
|
||
PgmUnlock (&PgmDynamicConfig, *pOldIrq);
|
||
|
||
while (!IsListEmpty (&NaksList))
|
||
{
|
||
pNakContext = CONTAINING_RECORD (NaksList.Flink, tNAKS_CONTEXT, Linkage);
|
||
|
||
if (pNakContext->NumSequences &&
|
||
!(pReceive->SessionFlags & (PGM_SESSION_FLAG_NAK_TIMED_OUT | PGM_SESSION_TERMINATED_ABORT)))
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "CheckSendPendingNaks",
|
||
"Sending %s Nak for <%d> sequences, [%d -- %d]!\n",
|
||
(pNakContext->NakType == NAK_TYPE_PARITY ? "Parity" : "Selective"),
|
||
pNakContext->NumSequences, (ULONG) pNakContext->Sequences[0],
|
||
(ULONG) pNakContext->Sequences[MAX_SEQUENCES_PER_NAK_OPTION]);
|
||
|
||
PgmSendNak (pReceive, pNakContext);
|
||
}
|
||
|
||
RemoveEntryList (&pNakContext->Linkage);
|
||
PgmFreeMem (pNakContext);
|
||
}
|
||
|
||
PgmLock (&PgmDynamicConfig, *pOldIrq);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
VOID
|
||
ReceiveTimerTimeout(
|
||
IN PKDPC Dpc,
|
||
IN PVOID DeferredContext,
|
||
IN PVOID SystemArg1,
|
||
IN PVOID SystemArg2
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This timeout routine is called periodically to cycle through the
|
||
list of active receivers and send any Naks if required
|
||
|
||
Arguments:
|
||
|
||
IN Dpc
|
||
IN DeferredContext -- Our context for this timer
|
||
IN SystemArg1
|
||
IN SystemArg2
|
||
|
||
Return Value:
|
||
|
||
NONE
|
||
|
||
--*/
|
||
{
|
||
LIST_ENTRY *pEntry;
|
||
PGMLockHandle OldIrq, OldIrq1;
|
||
tRECEIVE_CONTEXT *pReceiver;
|
||
tRECEIVE_SESSION *pReceive;
|
||
NTSTATUS status;
|
||
LARGE_INTEGER Now, Frequency;
|
||
LARGE_INTEGER DeltaTime, GranularTimeElapsed;
|
||
ULONG NumTimeouts;
|
||
ULONG LastSessionInterval;
|
||
|
||
PgmLock (&PgmDynamicConfig, OldIrq);
|
||
|
||
if (IsListEmpty (&PgmDynamicConfig.CurrentReceivers))
|
||
{
|
||
//
|
||
// Stop the timer if we don't have any receivers currently
|
||
//
|
||
PgmDynamicConfig.GlobalFlags &= ~PGM_CONFIG_FLAG_RECEIVE_TIMER_RUNNING;
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq);
|
||
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "ReceiveTimerTimeout",
|
||
"Not restarting Timer since no Receivers currently active!\n");
|
||
|
||
return;
|
||
}
|
||
|
||
Now = KeQueryPerformanceCounter (&Frequency);
|
||
DeltaTime.QuadPart = Now.QuadPart - PgmDynamicConfig.LastReceiverTimeout.QuadPart;
|
||
for (GranularTimeElapsed.QuadPart = 0, NumTimeouts = 0;
|
||
DeltaTime.QuadPart > PgmDynamicConfig.TimeoutGranularity.QuadPart;
|
||
NumTimeouts++)
|
||
{
|
||
GranularTimeElapsed.QuadPart += PgmDynamicConfig.TimeoutGranularity.QuadPart;
|
||
DeltaTime.QuadPart -= PgmDynamicConfig.TimeoutGranularity.QuadPart;
|
||
}
|
||
|
||
if (!NumTimeouts)
|
||
{
|
||
PgmInitTimer (&PgmDynamicConfig.SessionTimer);
|
||
PgmStartTimer (&PgmDynamicConfig.SessionTimer, BASIC_TIMER_GRANULARITY_IN_MSECS, ReceiveTimerTimeout, NULL);
|
||
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq);
|
||
return;
|
||
}
|
||
|
||
PgmDynamicConfig.ReceiversTimerTickCount += NumTimeouts;
|
||
PgmDynamicConfig.LastReceiverTimeout.QuadPart += GranularTimeElapsed.QuadPart;
|
||
|
||
pEntry = &PgmDynamicConfig.CurrentReceivers;
|
||
while ((pEntry = pEntry->Flink) != &PgmDynamicConfig.CurrentReceivers)
|
||
{
|
||
pReceiver = CONTAINING_RECORD (pEntry, tRECEIVE_CONTEXT, Linkage);
|
||
pReceive = pReceiver->pReceive;
|
||
|
||
PgmLock (pReceive, OldIrq1);
|
||
|
||
LastSessionInterval = (ULONG) (PgmDynamicConfig.ReceiversTimerTickCount -
|
||
pReceiver->LastSessionTickCount);
|
||
if ((LastSessionInterval > MAX_SPM_INTERVAL_MSECS/BASIC_TIMER_GRANULARITY_IN_MSECS) &&
|
||
(LastSessionInterval > (pReceiver->MaxSpmInterval << 5))) // (32 * MaxSpmInterval)
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ReceiveTimerTimeout",
|
||
"Disconnecting session because no SPMs received for <%x:%x> Msecs\n",
|
||
LastSessionInterval);
|
||
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
}
|
||
|
||
if (pReceive->SessionFlags & (PGM_SESSION_FLAG_NAK_TIMED_OUT | PGM_SESSION_TERMINATED_ABORT))
|
||
{
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
pReceive->SessionFlags &= ~PGM_SESSION_ON_TIMER;
|
||
}
|
||
|
||
if (pReceive->SessionFlags & PGM_SESSION_ON_TIMER)
|
||
{
|
||
pReceive->RateCalcTimeout += NumTimeouts;
|
||
|
||
if ((pReceive->RateCalcTimeout >=
|
||
(INTERNAL_RATE_CALCULATION_FREQUENCY/BASIC_TIMER_GRANULARITY_IN_MSECS)) &&
|
||
(pReceiver->StartTickCount != PgmDynamicConfig.ReceiversTimerTickCount)) // Avoid Div by 0
|
||
{
|
||
pReceive->RateKBitsPerSecOverall = (pReceive->TotalBytes << LOG2_BITS_PER_BYTE) /
|
||
((PgmDynamicConfig.ReceiversTimerTickCount-pReceiver->StartTickCount) * BASIC_TIMER_GRANULARITY_IN_MSECS);
|
||
|
||
pReceive->RateKBitsPerSecLast = (pReceive->TotalBytes - pReceive->TotalBytesAtLastInterval) >>
|
||
(LOG2_INTERNAL_RATE_CALCULATION_FREQUENCY-LOG2_BITS_PER_BYTE);
|
||
|
||
//
|
||
// Now, Reset for next calculations
|
||
//
|
||
pReceive->DataBytesAtLastInterval = pReceive->DataBytes;
|
||
pReceive->TotalBytesAtLastInterval = pReceive->TotalBytes;
|
||
pReceive->RateCalcTimeout = 0;
|
||
|
||
//
|
||
// Now, update the window information, if applicable
|
||
//
|
||
if (pReceive->RateKBitsPerSecLast)
|
||
{
|
||
UpdateSampleTimeWindowInformation (pReceive);
|
||
}
|
||
pReceive->pReceiver->StatSumOfWindowSeqs = pReceive->pReceiver->NumWindowSamples = 0;
|
||
// pReceive->pReceiver->StatSumOfNcfRDataTicks = pReceive->pReceiver->NumNcfRDataTicksSamples = 0;
|
||
}
|
||
|
||
PgmUnlock (pReceive, OldIrq1);
|
||
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "ReceiveTimerTimeout",
|
||
"Checking for pending Naks for pReceive=<%p>, Addr=<%x>\n",
|
||
pReceive, pReceiver->ListenMCastIpAddress);
|
||
|
||
CheckSendPendingNaks (pReceiver->pAddress, pReceive, &OldIrq);
|
||
}
|
||
else
|
||
{
|
||
pEntry = pEntry->Blink;
|
||
RemoveEntryList (&pReceiver->Linkage);
|
||
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq1);
|
||
|
||
CheckIndicateDisconnect (pReceiver->pAddress, pReceive, NULL, &OldIrq1, FALSE);
|
||
|
||
PgmUnlock (pReceive, OldIrq);
|
||
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "ReceiveTimerTimeout",
|
||
"PGM_SESSION_ON_TIMER flag cleared for pReceive=<%p>, Addr=<%x>\n",
|
||
pReceive, pReceiver->ListenMCastIpAddress);
|
||
|
||
PGM_DEREFERENCE_ADDRESS (pReceiver->pAddress, REF_ADDRESS_RECEIVE_ACTIVE);
|
||
PGM_DEREFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_TIMER_RUNNING);
|
||
|
||
PgmLock (&PgmDynamicConfig, OldIrq);
|
||
}
|
||
}
|
||
|
||
PgmInitTimer (&PgmDynamicConfig.SessionTimer);
|
||
PgmStartTimer (&PgmDynamicConfig.SessionTimer, BASIC_TIMER_GRANULARITY_IN_MSECS, ReceiveTimerTimeout, NULL);
|
||
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
ExtractNakNcfSequences(
|
||
IN tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED *pNakNcfPacket,
|
||
IN ULONG BytesAvailable,
|
||
OUT tNAKS_LIST *pNakNcfList,
|
||
IN UCHAR FECGroupSize
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called to process a Nak/Ncf packet and extract all
|
||
the Sequences specified therein into a list.
|
||
It also verifies that the sequences are unique and sorted
|
||
|
||
Arguments:
|
||
|
||
IN pNakNcfPacket -- Nak/Ncf packet
|
||
IN BytesAvailable -- PacketLength
|
||
OUT pNakNcfList -- List of sequences returned on success
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Final status of the operation
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
ULONG i;
|
||
tPACKET_OPTIONS PacketOptions;
|
||
SEQ_TYPE LastSequenceNumber;
|
||
SEQ_TYPE FECSequenceMask = FECGroupSize - 1;
|
||
SEQ_TYPE FECGroupMask = ~FECSequenceMask;
|
||
|
||
// Must be called with the Session lock held!
|
||
|
||
PgmZeroMemory (pNakNcfList, sizeof (tNAKS_LIST));
|
||
if (pNakNcfPacket->CommonHeader.Options & PACKET_HEADER_OPTIONS_PARITY)
|
||
{
|
||
pNakNcfList->NakType = NAK_TYPE_PARITY;
|
||
}
|
||
else
|
||
{
|
||
pNakNcfList->NakType = NAK_TYPE_SELECTIVE;
|
||
}
|
||
|
||
PgmZeroMemory (&PacketOptions, sizeof (tPACKET_OPTIONS));
|
||
if (pNakNcfPacket->CommonHeader.Options & PACKET_HEADER_OPTIONS_PRESENT)
|
||
{
|
||
status = ProcessOptions ((tPACKET_OPTION_LENGTH *) (pNakNcfPacket + 1),
|
||
BytesAvailable,
|
||
(pNakNcfPacket->CommonHeader.Type & 0x0f),
|
||
&PacketOptions,
|
||
pNakNcfList);
|
||
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ExtractNakNcfSequences",
|
||
"ProcessOptions returned <%x>\n", status);
|
||
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
ASSERT (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_NAK_LIST);
|
||
}
|
||
|
||
pNakNcfList->pNakSequences[0] = (SEQ_TYPE) ntohl (pNakNcfPacket->RequestedSequenceNumber);
|
||
pNakNcfList->NumSequences += 1;
|
||
|
||
//
|
||
// Now, adjust the sequences according to our local relative sequence number
|
||
// (This is to account for wrap-arounds)
|
||
//
|
||
LastSequenceNumber = pNakNcfList->pNakSequences[0] - FECGroupSize;
|
||
for (i=0; i < pNakNcfList->NumSequences; i++)
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ExtractNakNcfSequences",
|
||
"[%d] Sequence# = <%d>\n", i, (ULONG) pNakNcfList->pNakSequences[i]);
|
||
|
||
//
|
||
// If this is a parity Nak, then we need to separate the TG_SQN from the PKT_SQN
|
||
//
|
||
if (pNakNcfList->NakType == NAK_TYPE_PARITY)
|
||
{
|
||
pNakNcfList->NumNaks[i] = (USHORT) (pNakNcfList->pNakSequences[i] & FECSequenceMask) + 1;
|
||
ASSERT (pNakNcfList->NumNaks[i] <= FECGroupSize);
|
||
pNakNcfList->pNakSequences[i] &= FECGroupMask;
|
||
}
|
||
else
|
||
{
|
||
pNakNcfList->NumNaks[i] = 1;
|
||
}
|
||
|
||
if (SEQ_LEQ (pNakNcfList->pNakSequences[i], LastSequenceNumber))
|
||
{
|
||
//
|
||
// This list is not ordered, so just bail!
|
||
//
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ExtractNakNcfSequences",
|
||
"[%d] Unordered list! Sequence#<%d> before <%d>\n",
|
||
i, (ULONG) LastSequenceNumber, (ULONG) pNakNcfList->pNakSequences[i]);
|
||
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
LastSequenceNumber = pNakNcfList->pNakSequences[i];
|
||
}
|
||
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
CheckAndAddNakRequests(
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN SEQ_TYPE *pLatestSequenceNumber,
|
||
OUT tNAK_FORWARD_DATA **ppThisNak,
|
||
IN enum eNAK_TIMEOUT NakTimeoutType
|
||
)
|
||
{
|
||
tNAK_FORWARD_DATA *pOldNak;
|
||
tNAK_FORWARD_DATA *pLastNak;
|
||
SEQ_TYPE MidSequenceNumber;
|
||
SEQ_TYPE FECGroupMask = pReceive->FECGroupSize-1;
|
||
SEQ_TYPE ThisSequenceNumber = *pLatestSequenceNumber;
|
||
SEQ_TYPE ThisGroupSequenceNumber = ThisSequenceNumber & ~FECGroupMask;
|
||
SEQ_TYPE FurthestGroupSequenceNumber = pReceive->pReceiver->FurthestKnownGroupSequenceNumber;
|
||
ULONG NakRequestSize = sizeof(tNAK_FORWARD_DATA) +
|
||
((pReceive->FECGroupSize-1) * sizeof(tPENDING_DATA));
|
||
ULONGLONG Pending0NakTimeout = PgmDynamicConfig.ReceiversTimerTickCount + 2;
|
||
LIST_ENTRY *pEntry;
|
||
UCHAR i;
|
||
|
||
//
|
||
// Verify that the FurthestKnownGroupSequenceNumber is on a Group boundary
|
||
//
|
||
ASSERT (!(FurthestGroupSequenceNumber & FECGroupMask));
|
||
|
||
if (SEQ_LT (ThisSequenceNumber, pReceive->pReceiver->FirstNakSequenceNumber))
|
||
{
|
||
if (ppThisNak)
|
||
{
|
||
ASSERT (0);
|
||
*ppThisNak = NULL;
|
||
}
|
||
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
if (SEQ_GT (ThisGroupSequenceNumber, (FurthestGroupSequenceNumber + 1000)) &&
|
||
!(pReceive->SessionFlags & PGM_SESSION_FLAG_FIRST_PACKET))
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "CheckAndAddNakRequests",
|
||
"WARNING!!! Too many successive packets lost =<%d>!!! Expecting Next=<%d>, FurthestKnown=<%d>, This=<%d>\n",
|
||
(ULONG) (ThisGroupSequenceNumber - FurthestGroupSequenceNumber),
|
||
(ULONG) pReceive->pReceiver->FirstNakSequenceNumber,
|
||
(ULONG) FurthestGroupSequenceNumber,
|
||
(ULONG) ThisGroupSequenceNumber);
|
||
}
|
||
|
||
//
|
||
// Add any Nak requests if necessary!
|
||
// FurthestGroupSequenceNumber must be a multiple of the FECGroupSize (if applicable)
|
||
//
|
||
pLastNak = NULL;
|
||
while (SEQ_LT (FurthestGroupSequenceNumber, ThisGroupSequenceNumber))
|
||
{
|
||
if (pReceive->FECOptions)
|
||
{
|
||
pLastNak = ExAllocateFromNPagedLookasideList (&pReceive->pReceiver->ParityContextLookaside);
|
||
}
|
||
else
|
||
{
|
||
pLastNak = ExAllocateFromNPagedLookasideList (&pReceive->pReceiver->NonParityContextLookaside);
|
||
}
|
||
|
||
if (!pLastNak)
|
||
{
|
||
pReceive->pReceiver->FurthestKnownGroupSequenceNumber = FurthestGroupSequenceNumber;
|
||
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "CheckAndAddNakRequests",
|
||
"STATUS_INSUFFICIENT_RESOURCES allocating tNAK_FORWARD_DATA, Size=<%d>, Seq=<%d>\n",
|
||
NakRequestSize, (ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber);
|
||
|
||
return (STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
PgmZeroMemory (pLastNak, NakRequestSize);
|
||
|
||
if (pReceive->FECOptions)
|
||
{
|
||
pLastNak->OriginalGroupSize = pLastNak->PacketsInGroup = pReceive->FECGroupSize;
|
||
}
|
||
else
|
||
{
|
||
pLastNak->OriginalGroupSize = pLastNak->PacketsInGroup = 1;
|
||
}
|
||
|
||
for (i=0; i<pLastNak->OriginalGroupSize; i++)
|
||
{
|
||
pLastNak->pPendingData[i].ActualIndexOfDataPacket = pLastNak->OriginalGroupSize;
|
||
}
|
||
|
||
FurthestGroupSequenceNumber += pReceive->FECGroupSize;
|
||
pLastNak->SequenceNumber = FurthestGroupSequenceNumber;
|
||
pLastNak->MinPacketLength = pReceive->MaxFECPacketLength;
|
||
|
||
if (NakTimeoutType == NAK_OUTSTANDING)
|
||
{
|
||
pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
pReceive->pReceiver->OutstandingNakTimeout;
|
||
pLastNak->PendingNakTimeout = 0;
|
||
pLastNak->WaitingNcfRetries = 0;
|
||
}
|
||
else
|
||
{
|
||
switch (NakTimeoutType)
|
||
{
|
||
case (NAK_PENDING_0):
|
||
{
|
||
pLastNak->PendingNakTimeout = Pending0NakTimeout;
|
||
pLastNak->OutstandingNakTimeout = 0;
|
||
|
||
break;
|
||
}
|
||
|
||
case (NAK_PENDING_RB):
|
||
{
|
||
pLastNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
((NAK_RANDOM_BACKOFF_MSECS/pReceive->FECGroupSize) /
|
||
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
||
pLastNak->OutstandingNakTimeout = 0;
|
||
|
||
break;
|
||
}
|
||
|
||
case (NAK_PENDING_RPT_RB):
|
||
{
|
||
pLastNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
((NAK_REPEAT_INTERVAL_MSECS +(NAK_RANDOM_BACKOFF_MSECS/pReceive->FECGroupSize))/
|
||
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
||
pLastNak->OutstandingNakTimeout = 0;
|
||
|
||
break;
|
||
}
|
||
|
||
default:
|
||
{
|
||
ASSERT (0);
|
||
}
|
||
}
|
||
}
|
||
|
||
InsertTailList (&pReceive->pReceiver->NaksForwardDataList, &pLastNak->Linkage);
|
||
InsertTailList (&pReceive->pReceiver->PendingNaksList, &pLastNak->PendingLinkage);
|
||
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "CheckAndAddNakRequests",
|
||
"ADDing NAK request for SeqNum=<%d>, Furthest=<%d>\n",
|
||
(ULONG) pLastNak->SequenceNumber, (ULONG) FurthestGroupSequenceNumber);
|
||
}
|
||
|
||
pReceive->pReceiver->FurthestKnownGroupSequenceNumber = FurthestGroupSequenceNumber;
|
||
|
||
if (pLastNak)
|
||
{
|
||
pLastNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
NAK_REPEAT_INTERVAL_MSECS / BASIC_TIMER_GRANULARITY_IN_MSECS;
|
||
}
|
||
else if ((ppThisNak) && (!IsListEmpty (&pReceive->pReceiver->NaksForwardDataList)))
|
||
{
|
||
//
|
||
// We need to extract the Nak entry for this packet
|
||
// If this sequence is nearer to the tail end, we will search
|
||
// from the tail end, otherwise we will search from the head end
|
||
//
|
||
MidSequenceNumber = pReceive->pReceiver->FirstNakSequenceNumber +
|
||
((pReceive->pReceiver->FurthestKnownGroupSequenceNumber -
|
||
pReceive->pReceiver->FirstNakSequenceNumber) >> 1);
|
||
if (SEQ_GT (ThisSequenceNumber, MidSequenceNumber))
|
||
{
|
||
//
|
||
// Search backwards starting from the tail end
|
||
//
|
||
pEntry = &pReceive->pReceiver->PendingNaksList;
|
||
while ((pEntry = pEntry->Blink) != &pReceive->pReceiver->PendingNaksList)
|
||
{
|
||
pLastNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, PendingLinkage);
|
||
if (SEQ_LEQ (pLastNak->SequenceNumber, ThisGroupSequenceNumber))
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Search from the head
|
||
//
|
||
pEntry = &pReceive->pReceiver->PendingNaksList;
|
||
while ((pEntry = pEntry->Flink) != &pReceive->pReceiver->PendingNaksList)
|
||
{
|
||
pLastNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, PendingLinkage);
|
||
if (SEQ_GEQ (pLastNak->SequenceNumber, ThisGroupSequenceNumber))
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
ASSERT (pLastNak);
|
||
if (pLastNak->SequenceNumber != ThisGroupSequenceNumber)
|
||
{
|
||
pLastNak = NULL;
|
||
}
|
||
}
|
||
|
||
if (ppThisNak)
|
||
{
|
||
*ppThisNak = pLastNak;
|
||
}
|
||
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
ReceiverProcessNakNcfPacket(
|
||
IN tADDRESS_CONTEXT *pAddress,
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN ULONG PacketLength,
|
||
IN tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED *pNakNcfPacket,
|
||
IN UCHAR PacketType
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This is the common routine for processing Nak or Ncf packets
|
||
|
||
Arguments:
|
||
|
||
IN pAddress -- Address object context
|
||
IN pReceive -- Receive context
|
||
IN PacketLength -- Length of packet received from the wire
|
||
IN pNakNcfPacket -- Nak/Ncf packet
|
||
IN PacketType -- whether Nak or Ncf
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Final status of the call
|
||
|
||
--*/
|
||
{
|
||
PGMLockHandle OldIrq;
|
||
ULONG i, j, PacketIndex;
|
||
tNAKS_LIST NakNcfList;
|
||
SEQ_TYPE LastSequenceNumber, FECGroupMask;
|
||
NTSTATUS status;
|
||
LIST_ENTRY *pEntry;
|
||
tNAK_FORWARD_DATA *pLastNak;
|
||
ULONG NumMissingPackets;
|
||
BOOLEAN fFECWithNoParityNak = FALSE;
|
||
|
||
if (PacketLength < sizeof(tBASIC_NAK_NCF_PACKET_HEADER))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ReceiverProcessNakNcfPacket",
|
||
"PacketLength=<%d>, Min=<%d>, ...\n",
|
||
PacketLength, sizeof(tBASIC_NAK_NCF_PACKET_HEADER));
|
||
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
|
||
ASSERT (!pNakNcfPacket->CommonHeader.TSDULength);
|
||
|
||
PgmZeroMemory (&NakNcfList, sizeof (tNAKS_LIST));
|
||
PgmLock (pReceive, OldIrq);
|
||
|
||
status = ExtractNakNcfSequences (pNakNcfPacket,
|
||
(PacketLength - sizeof(tBASIC_NAK_NCF_PACKET_HEADER)),
|
||
&NakNcfList,
|
||
pReceive->FECGroupSize);
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
PgmUnlock (pReceive, OldIrq);
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ReceiverProcessNakNcfPacket",
|
||
"ExtractNakNcfSequences returned <%x>\n", status);
|
||
|
||
return (status);
|
||
}
|
||
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "ReceiverProcessNakNcfPacket",
|
||
"NumSequences=[%d] Range=<%d--%d>, Furthest=<%d>\n",
|
||
NakNcfList.NumSequences,
|
||
(ULONG) NakNcfList.pNakSequences[0], (ULONG) NakNcfList.pNakSequences[NakNcfList.NumSequences-1],
|
||
(ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber);
|
||
|
||
//
|
||
// Compares apples to apples and oranges to oranges
|
||
// i.e. Process parity Naks only if we are parity-aware, and vice-versa
|
||
//
|
||
if (pReceive->pReceiver->SessionNakType != NakNcfList.NakType)
|
||
{
|
||
PgmUnlock (pReceive, OldIrq);
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ReceiverProcessNakNcfPacket",
|
||
"Received a %s Nak! Not processing ... \n",
|
||
((pReceive->FECGroupSize > 1) ? "Non-parity" : "Parity"));
|
||
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
//
|
||
// Special case: If we have FEC enabled, but not with OnDemand parity,
|
||
// then we will process Ncf requests only
|
||
//
|
||
pEntry = &pReceive->pReceiver->PendingNaksList;
|
||
|
||
fFECWithNoParityNak = pReceive->FECOptions &&
|
||
!(pReceive->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT);
|
||
if (fFECWithNoParityNak && (PacketType == PACKET_TYPE_NAK))
|
||
{
|
||
pEntry = pEntry->Blink;
|
||
}
|
||
|
||
i = 0;
|
||
FECGroupMask = pReceive->FECGroupSize - 1;
|
||
while ((pEntry = pEntry->Flink) != &pReceive->pReceiver->PendingNaksList)
|
||
{
|
||
pLastNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, PendingLinkage);
|
||
while (SEQ_LT (NakNcfList.pNakSequences[i], pLastNak->SequenceNumber))
|
||
{
|
||
if (++i == NakNcfList.NumSequences)
|
||
{
|
||
PgmUnlock (pReceive, OldIrq);
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ReceiverProcessNakNcfPacket",
|
||
"Received Ncf for <%d> Sequences -- none in our range\n", i);
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
}
|
||
|
||
LastSequenceNumber = NakNcfList.pNakSequences[i] & ~FECGroupMask;
|
||
if (SEQ_GT (LastSequenceNumber, pLastNak->SequenceNumber))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
NumMissingPackets = pLastNak->PacketsInGroup - (pLastNak->NumDataPackets + pLastNak->NumParityPackets);
|
||
ASSERT (pLastNak->SequenceNumber == LastSequenceNumber);
|
||
ASSERT (NumMissingPackets);
|
||
PacketIndex = (ULONG) (NakNcfList.pNakSequences[i] & FECGroupMask);
|
||
|
||
if (PacketType == PACKET_TYPE_NAK)
|
||
{
|
||
//
|
||
// If we are currently waiting for a Nak or Ncf, we need to
|
||
// reset the timeout for either of the 2 scenarios
|
||
//
|
||
if (pLastNak->PendingNakTimeout) // We are waiting for a Nak
|
||
{
|
||
pLastNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
((NAK_REPEAT_INTERVAL_MSECS + (NAK_RANDOM_BACKOFF_MSECS/NumMissingPackets))/
|
||
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
||
}
|
||
else
|
||
{
|
||
ASSERT (pLastNak->OutstandingNakTimeout);
|
||
|
||
pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
(pReceive->pReceiver->OutstandingNakTimeout <<
|
||
pLastNak->WaitingRDataRetries);
|
||
|
||
if ((pLastNak->WaitingRDataRetries >= (NCF_WAITING_RDATA_MAX_RETRIES >> 1)) &&
|
||
((pReceive->pReceiver->OutstandingNakTimeout << pLastNak->WaitingRDataRetries) <
|
||
pReceive->pReceiver->MaxRDataResponseTCFromWindow))
|
||
{
|
||
pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
(pReceive->pReceiver->MaxRDataResponseTCFromWindow<<1);
|
||
}
|
||
}
|
||
}
|
||
// NCF case -- check if we have this data packet!
|
||
else if ((fFECWithNoParityNak &&
|
||
(pLastNak->pPendingData[PacketIndex].ActualIndexOfDataPacket >= pLastNak->OriginalGroupSize)) ||
|
||
(!fFECWithNoParityNak &&
|
||
(NakNcfList.NumNaks[i] >= NumMissingPackets)))
|
||
{
|
||
if (!pLastNak->FirstNcfTickCount)
|
||
{
|
||
pLastNak->FirstNcfTickCount = PgmDynamicConfig.ReceiversTimerTickCount;
|
||
}
|
||
|
||
if (fFECWithNoParityNak)
|
||
{
|
||
pLastNak->pPendingData[PacketIndex].NcfsReceivedForActualIndex++;
|
||
for (j=0; j<pLastNak->PacketsInGroup; j++)
|
||
{
|
||
if ((pLastNak->pPendingData[j].ActualIndexOfDataPacket >= pLastNak->OriginalGroupSize) &&
|
||
(!pLastNak->pPendingData[j].NcfsReceivedForActualIndex))
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!fFECWithNoParityNak ||
|
||
(j >= pLastNak->PacketsInGroup))
|
||
{
|
||
pLastNak->PendingNakTimeout = 0;
|
||
pLastNak->WaitingNcfRetries = 0;
|
||
|
||
pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
(pReceive->pReceiver->OutstandingNakTimeout <<
|
||
pLastNak->WaitingRDataRetries);
|
||
|
||
if ((pLastNak->WaitingRDataRetries >= (NCF_WAITING_RDATA_MAX_RETRIES >> 1)) &&
|
||
((pReceive->pReceiver->OutstandingNakTimeout << pLastNak->WaitingRDataRetries) <
|
||
pReceive->pReceiver->MaxRDataResponseTCFromWindow))
|
||
{
|
||
pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
(pReceive->pReceiver->MaxRDataResponseTCFromWindow<<1);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (fFECWithNoParityNak)
|
||
{
|
||
pEntry = pEntry->Blink; // There may be more Ncfs for the same group!
|
||
}
|
||
|
||
if (++i == NakNcfList.NumSequences)
|
||
{
|
||
PgmUnlock (pReceive, OldIrq);
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ReceiverProcessNakNcfPacket",
|
||
"Received Ncf for <%d> Sequences, some in our list\n", i);
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
}
|
||
|
||
//
|
||
// So, we need to create new Nak contexts for the remaining Sequences
|
||
// Since the Sequences are ordered, just pick the highest one, and
|
||
// create Naks for all up to that
|
||
//
|
||
if (PacketType == PACKET_TYPE_NAK)
|
||
{
|
||
status = CheckAndAddNakRequests (pReceive,&NakNcfList.pNakSequences[NakNcfList.NumSequences-1], NULL, NAK_PENDING_RPT_RB);
|
||
}
|
||
else // PacketType == PACKET_TYPE_NCF
|
||
{
|
||
status = CheckAndAddNakRequests (pReceive, &NakNcfList.pNakSequences[NakNcfList.NumSequences-1], NULL, NAK_OUTSTANDING);
|
||
}
|
||
|
||
PgmUnlock (pReceive, OldIrq);
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
CoalesceSelectiveNaksIntoGroups(
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN UCHAR GroupSize
|
||
)
|
||
{
|
||
PNAK_FORWARD_DATA pOldNak, pNewNak;
|
||
LIST_ENTRY NewNaksList;
|
||
LIST_ENTRY OldNaksList;
|
||
LIST_ENTRY *pEntry;
|
||
SEQ_TYPE FirstGroupSequenceNumber, LastGroupSequenceNumber, LastSequenceNumber;
|
||
SEQ_TYPE GroupMask = GroupSize - 1;
|
||
ULONG NakRequestSize = sizeof(tNAK_FORWARD_DATA) + ((GroupSize-1) * sizeof(tPENDING_DATA));
|
||
USHORT MinPacketLength;
|
||
UCHAR i;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
ASSERT (pReceive->FECGroupSize == 1);
|
||
ASSERT (GroupSize > 1);
|
||
|
||
//
|
||
// First, call AdjustReceiveBufferLists to ensure that FirstNakSequenceNumber is current
|
||
//
|
||
AdjustReceiveBufferLists (pReceive);
|
||
|
||
FirstGroupSequenceNumber = pReceive->pReceiver->FirstNakSequenceNumber & ~GroupMask;
|
||
LastGroupSequenceNumber = pReceive->pReceiver->FurthestKnownGroupSequenceNumber & ~GroupMask;
|
||
|
||
//
|
||
// If the next packet seq we are expecting is > the furthest known sequence #,
|
||
// then we don't need to do anything
|
||
//
|
||
LastSequenceNumber = LastGroupSequenceNumber + (GroupSize-1);
|
||
//
|
||
// First, add Nak requests for the missing packets in furthest group!
|
||
//
|
||
status = CheckAndAddNakRequests (pReceive, &LastSequenceNumber, NULL, NAK_PENDING_RB);
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "CoalesceSelectiveNaksIntoGroups",
|
||
"CheckAndAddNakRequests returned <%x>\n", status);
|
||
|
||
return (status);
|
||
}
|
||
|
||
ASSERT (LastSequenceNumber == pReceive->pReceiver->FurthestKnownGroupSequenceNumber);
|
||
ExInitializeNPagedLookasideList (&pReceive->pReceiver->ParityContextLookaside,
|
||
NULL,
|
||
NULL,
|
||
0,
|
||
NakRequestSize,
|
||
PGM_TAG('2'),
|
||
PARITY_CONTEXT_LOOKASIDE_DEPTH);
|
||
|
||
if (SEQ_GT (pReceive->pReceiver->FirstNakSequenceNumber, LastSequenceNumber))
|
||
{
|
||
pReceive->pReceiver->FurthestKnownGroupSequenceNumber = LastGroupSequenceNumber;
|
||
|
||
ASSERT (IsListEmpty (&pReceive->pReceiver->NaksForwardDataList));
|
||
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "CoalesceSelectiveNaksIntoGroups",
|
||
"[1] NextOData=<%d>, FirstNak=<%d>, FirstGroup=<%d>, LastGroup=<%d>, no Naks pending!\n",
|
||
(ULONG) pReceive->pReceiver->NextODataSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->FirstNakSequenceNumber,
|
||
(ULONG) FirstGroupSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber);
|
||
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
//
|
||
// We will start coalescing from the end of the list in case we run
|
||
// into failures
|
||
// Also, we will ignore the first Group since it may be a partial group,
|
||
// or we may have indicated some of the data already, so we may not know
|
||
// the exact data length
|
||
//
|
||
pOldNak = pNewNak = NULL;
|
||
InitializeListHead (&NewNaksList);
|
||
InitializeListHead (&OldNaksList);
|
||
while (SEQ_GEQ (LastGroupSequenceNumber, FirstGroupSequenceNumber))
|
||
{
|
||
if (!(pNewNak = ExAllocateFromNPagedLookasideList (&pReceive->pReceiver->ParityContextLookaside)))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "CoalesceSelectiveNaksIntoGroups",
|
||
"STATUS_INSUFFICIENT_RESOURCES allocating tNAK_FORWARD_DATA\n");
|
||
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
break;
|
||
}
|
||
PgmZeroMemory (pNewNak, NakRequestSize);
|
||
InitializeListHead (&pNewNak->PendingLinkage);
|
||
|
||
pNewNak->OriginalGroupSize = pNewNak->PacketsInGroup = GroupSize;
|
||
pNewNak->SequenceNumber = LastGroupSequenceNumber;
|
||
MinPacketLength = pReceive->MaxFECPacketLength;
|
||
|
||
for (i=0; i<pNewNak->OriginalGroupSize; i++)
|
||
{
|
||
pNewNak->pPendingData[i].ActualIndexOfDataPacket = pNewNak->OriginalGroupSize;
|
||
}
|
||
|
||
i = 0;
|
||
while (SEQ_GEQ (LastSequenceNumber, LastGroupSequenceNumber) &&
|
||
(!IsListEmpty (&pReceive->pReceiver->NaksForwardDataList)))
|
||
{
|
||
pEntry = RemoveTailList (&pReceive->pReceiver->NaksForwardDataList);
|
||
pOldNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, Linkage);
|
||
|
||
if (!pOldNak->NumDataPackets)
|
||
{
|
||
ASSERT (!IsListEmpty (&pOldNak->PendingLinkage));
|
||
RemoveEntryList (&pOldNak->PendingLinkage);
|
||
InitializeListHead (&pOldNak->PendingLinkage);
|
||
}
|
||
else
|
||
{
|
||
ASSERT (pOldNak->NumDataPackets == 1);
|
||
ASSERT (IsListEmpty (&pOldNak->PendingLinkage));
|
||
}
|
||
|
||
ASSERT (pOldNak->SequenceNumber == LastSequenceNumber);
|
||
ASSERT (pOldNak->OriginalGroupSize == 1);
|
||
|
||
if (pOldNak->pPendingData[0].pDataPacket)
|
||
{
|
||
ASSERT (pOldNak->NumDataPackets == 1);
|
||
|
||
pNewNak->NumDataPackets++;
|
||
PgmCopyMemory (&pNewNak->pPendingData[i], &pOldNak->pPendingData[0], sizeof (tPENDING_DATA));
|
||
pNewNak->pPendingData[i].PacketIndex = (UCHAR) (LastSequenceNumber - LastGroupSequenceNumber);
|
||
pNewNak->pPendingData[LastSequenceNumber-LastGroupSequenceNumber].ActualIndexOfDataPacket = i;
|
||
i++;
|
||
|
||
pOldNak->pPendingData[0].pDataPacket = NULL;
|
||
pOldNak->NumDataPackets--;
|
||
|
||
if (pOldNak->MinPacketLength < MinPacketLength)
|
||
{
|
||
MinPacketLength = pOldNak->MinPacketLength;
|
||
}
|
||
|
||
if ((pOldNak->ThisGroupSize) &&
|
||
(pOldNak->ThisGroupSize < GroupSize))
|
||
{
|
||
if (pNewNak->PacketsInGroup == GroupSize)
|
||
{
|
||
pNewNak->PacketsInGroup = pOldNak->ThisGroupSize;
|
||
}
|
||
else
|
||
{
|
||
ASSERT (pNewNak->PacketsInGroup == pOldNak->ThisGroupSize);
|
||
}
|
||
}
|
||
}
|
||
|
||
InsertHeadList (&OldNaksList, &pOldNak->Linkage);
|
||
LastSequenceNumber--;
|
||
}
|
||
|
||
pNewNak->MinPacketLength = MinPacketLength;
|
||
|
||
//
|
||
// See if we need to get rid of any excess (NULL) data packets
|
||
//
|
||
RemoveRedundantNaks (pNewNak, FALSE);
|
||
|
||
ASSERT (!pNewNak->NumParityPackets);
|
||
if (pNewNak->NumDataPackets < pNewNak->PacketsInGroup) // No parity packets yet!
|
||
{
|
||
pNewNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
((NAK_RANDOM_BACKOFF_MSECS/(pNewNak->PacketsInGroup-pNewNak->NumDataPackets))/
|
||
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
||
}
|
||
|
||
InsertHeadList (&NewNaksList, &pNewNak->Linkage);
|
||
LastGroupSequenceNumber -= GroupSize;
|
||
}
|
||
|
||
//
|
||
// If we succeeded in allocating all NewNaks above, set the
|
||
// NextIndexToIndicate for the first group.
|
||
// We may also need to adjust FirstNakSequenceNumber and NextODataSequenceNumber
|
||
//
|
||
if ((pNewNak) &&
|
||
(pNewNak->SequenceNumber == FirstGroupSequenceNumber))
|
||
{
|
||
if (SEQ_GT (pReceive->pReceiver->FirstNakSequenceNumber, pNewNak->SequenceNumber))
|
||
{
|
||
pNewNak->NextIndexToIndicate = (UCHAR) (pReceive->pReceiver->FirstNakSequenceNumber -
|
||
pNewNak->SequenceNumber);
|
||
pReceive->pReceiver->FirstNakSequenceNumber = pNewNak->SequenceNumber;
|
||
ASSERT (pNewNak->NextIndexToIndicate < GroupSize);
|
||
ASSERT ((pNewNak->NextIndexToIndicate + pNewNak->NumDataPackets) <= pNewNak->PacketsInGroup);
|
||
}
|
||
ASSERT (pReceive->pReceiver->FirstNakSequenceNumber == pNewNak->SequenceNumber);
|
||
|
||
//
|
||
// We may have data available for this group already in the buffered
|
||
// list (if it has not been indicated already) -- we should move it here
|
||
//
|
||
while ((pNewNak->NextIndexToIndicate) &&
|
||
(!IsListEmpty (&pReceive->pReceiver->BufferedDataList)))
|
||
{
|
||
ASSERT (pNewNak->NumDataPackets < pNewNak->OriginalGroupSize);
|
||
|
||
pEntry = RemoveTailList (&pReceive->pReceiver->BufferedDataList);
|
||
pOldNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, Linkage);
|
||
|
||
pReceive->pReceiver->NumPacketGroupsPendingClient--;
|
||
pReceive->pReceiver->DataPacketsPendingIndicate--;
|
||
pReceive->pReceiver->DataPacketsPendingNaks++;
|
||
pNewNak->NextIndexToIndicate--;
|
||
|
||
ASSERT (pOldNak->pPendingData[0].pDataPacket);
|
||
ASSERT ((pOldNak->NumDataPackets == 1) && (pOldNak->OriginalGroupSize == 1));
|
||
ASSERT (pOldNak->SequenceNumber == (pNewNak->SequenceNumber + pNewNak->NextIndexToIndicate));
|
||
|
||
PgmCopyMemory (&pNewNak->pPendingData[pNewNak->NumDataPackets], &pOldNak->pPendingData[0], sizeof (tPENDING_DATA));
|
||
pNewNak->pPendingData[pNewNak->NumDataPackets].PacketIndex = pNewNak->NextIndexToIndicate;
|
||
pNewNak->pPendingData[pNewNak->NextIndexToIndicate].ActualIndexOfDataPacket = pNewNak->NumDataPackets;
|
||
pNewNak->NumDataPackets++;
|
||
|
||
if (pOldNak->MinPacketLength < pNewNak->MinPacketLength)
|
||
{
|
||
pNewNak->MinPacketLength = pOldNak->MinPacketLength;
|
||
}
|
||
|
||
if ((pOldNak->ThisGroupSize) &&
|
||
(pOldNak->ThisGroupSize < GroupSize))
|
||
{
|
||
if (pNewNak->PacketsInGroup == GroupSize)
|
||
{
|
||
pNewNak->PacketsInGroup = pOldNak->ThisGroupSize;
|
||
}
|
||
else
|
||
{
|
||
ASSERT (pNewNak->PacketsInGroup == pOldNak->ThisGroupSize);
|
||
}
|
||
}
|
||
|
||
pOldNak->pPendingData[0].pDataPacket = NULL;
|
||
pOldNak->NumDataPackets--;
|
||
InsertHeadList (&OldNaksList, &pOldNak->Linkage);
|
||
}
|
||
|
||
if (SEQ_GEQ (pReceive->pReceiver->NextODataSequenceNumber, pNewNak->SequenceNumber))
|
||
{
|
||
ASSERT (pReceive->pReceiver->NextODataSequenceNumber ==
|
||
(pReceive->pReceiver->FirstNakSequenceNumber + pNewNak->NextIndexToIndicate));
|
||
ASSERT (IsListEmpty (&pReceive->pReceiver->BufferedDataList));
|
||
|
||
pReceive->pReceiver->NextODataSequenceNumber = pNewNak->SequenceNumber;
|
||
}
|
||
else
|
||
{
|
||
ASSERT ((0 == pNewNak->NextIndexToIndicate) &&
|
||
!(IsListEmpty (&pReceive->pReceiver->BufferedDataList)));
|
||
}
|
||
|
||
if (SEQ_GT (pReceive->pReceiver->LastTrailingEdgeSeqNum, pReceive->pReceiver->FirstNakSequenceNumber))
|
||
{
|
||
pReceive->pReceiver->LastTrailingEdgeSeqNum = pReceive->pReceiver->FirstNakSequenceNumber;
|
||
}
|
||
|
||
RemoveRedundantNaks (pNewNak, FALSE);
|
||
|
||
if ((pNewNak->NextIndexToIndicate + pNewNak->NumDataPackets) >= pNewNak->PacketsInGroup)
|
||
{
|
||
// This entry will be moved automatically to the buffered data list
|
||
// when we call AdjustReceiveBufferLists below
|
||
pNewNak->PendingNakTimeout = 0;
|
||
}
|
||
else
|
||
{
|
||
pNewNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
((NAK_RANDOM_BACKOFF_MSECS/(pNewNak->PacketsInGroup-(pNewNak->NextIndexToIndicate+pNewNak->NumDataPackets)))/
|
||
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
||
}
|
||
}
|
||
|
||
ASSERT (IsListEmpty (&pReceive->pReceiver->NaksForwardDataList));
|
||
ASSERT (IsListEmpty (&pReceive->pReceiver->PendingNaksList));
|
||
|
||
if (!IsListEmpty (&NewNaksList))
|
||
{
|
||
//
|
||
// Now, move the new list to the end of the current list
|
||
//
|
||
NewNaksList.Flink->Blink = pReceive->pReceiver->NaksForwardDataList.Blink;
|
||
NewNaksList.Blink->Flink = &pReceive->pReceiver->NaksForwardDataList;
|
||
pReceive->pReceiver->NaksForwardDataList.Blink->Flink = NewNaksList.Flink;
|
||
pReceive->pReceiver->NaksForwardDataList.Blink = NewNaksList.Blink;
|
||
}
|
||
|
||
while (!IsListEmpty (&OldNaksList))
|
||
{
|
||
pEntry = RemoveHeadList (&OldNaksList);
|
||
pOldNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, Linkage);
|
||
|
||
FreeNakContext (pReceive, pOldNak);
|
||
}
|
||
|
||
//
|
||
// Put the pending Naks in the PendingNaks list
|
||
//
|
||
pEntry = &pReceive->pReceiver->NaksForwardDataList;
|
||
while ((pEntry = pEntry->Flink) != &pReceive->pReceiver->NaksForwardDataList)
|
||
{
|
||
pNewNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, Linkage);
|
||
if (((pNewNak->NumDataPackets + pNewNak->NumParityPackets) < pNewNak->PacketsInGroup) &&
|
||
((pNewNak->NextIndexToIndicate + pNewNak->NumDataPackets) < pNewNak->PacketsInGroup))
|
||
{
|
||
InsertTailList (&pReceive->pReceiver->PendingNaksList, &pNewNak->PendingLinkage);
|
||
}
|
||
}
|
||
|
||
AdjustReceiveBufferLists (pReceive);
|
||
|
||
//
|
||
// Now, set the FirstKnownGroupSequenceNumber
|
||
//
|
||
pNewNak = NULL;
|
||
if (!(IsListEmpty (&pReceive->pReceiver->NaksForwardDataList)))
|
||
{
|
||
//
|
||
// For the last context, set the Nak timeout appropriately
|
||
//
|
||
pNewNak = CONTAINING_RECORD (pReceive->pReceiver->NaksForwardDataList.Blink, tNAK_FORWARD_DATA, Linkage);
|
||
if (pNewNak->NumDataPackets < pNewNak->PacketsInGroup)
|
||
{
|
||
pNewNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
||
((NAK_REPEAT_INTERVAL_MSECS +
|
||
(NAK_RANDOM_BACKOFF_MSECS /
|
||
(pNewNak->PacketsInGroup-pNewNak->NumDataPackets))) /
|
||
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
||
}
|
||
}
|
||
else if (!(IsListEmpty (&pReceive->pReceiver->BufferedDataList)))
|
||
{
|
||
pNewNak = CONTAINING_RECORD (pReceive->pReceiver->BufferedDataList.Blink, tNAK_FORWARD_DATA, Linkage);
|
||
}
|
||
|
||
if (pNewNak)
|
||
{
|
||
pReceive->pReceiver->FurthestKnownGroupSequenceNumber = pNewNak->SequenceNumber;
|
||
}
|
||
else
|
||
{
|
||
pReceive->pReceiver->FurthestKnownGroupSequenceNumber &= ~GroupMask;
|
||
}
|
||
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "CoalesceSelectiveNaksIntoGroups",
|
||
"[2] NextOData=<%d>, FirstNak=<%d->%d>, FirstGroup=<%d>, LastGroup=<%d>\n",
|
||
(ULONG) pReceive->pReceiver->NextODataSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->FirstNakSequenceNumber,
|
||
(pNewNak ? (ULONG) pNewNak->NextIndexToIndicate : (ULONG) 0),
|
||
(ULONG) FirstGroupSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber);
|
||
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
PgmIndicateToClient(
|
||
IN tADDRESS_CONTEXT *pAddress,
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN ULONG BytesAvailable,
|
||
IN PUCHAR pDataBuffer,
|
||
IN ULONG MessageOffset,
|
||
IN ULONG MessageLength,
|
||
OUT ULONG *pBytesTaken,
|
||
IN PGMLockHandle *pOldIrqAddress,
|
||
IN PGMLockHandle *pOldIrqReceive
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine tries to indicate the Data packet provided to the client
|
||
It is called with the pAddress and pReceive locks held
|
||
|
||
Arguments:
|
||
|
||
IN pAddress -- Address object context
|
||
IN pReceive -- Receive context
|
||
IN BytesAvailableToIndicate -- Length of packet received from the wire
|
||
IN pPgmDataHeader -- Data packet
|
||
IN pOldIrqAddress -- OldIrq for the Address lock
|
||
IN pOldIrqReceive -- OldIrq for the Receive lock
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Final status of the call
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
ULONG ReceiveFlags;
|
||
ULONG BytesLeftInMessage, ClientBytesTaken;
|
||
PIO_STACK_LOCATION pIrpSp;
|
||
PTDI_REQUEST_KERNEL_RECEIVE pClientParams;
|
||
PTDI_IND_RECEIVE evReceive = NULL;
|
||
PVOID RcvEvContext = NULL;
|
||
CONNECTION_CONTEXT ClientSessionContext;
|
||
PIRP pIrpReceive;
|
||
ULONG BytesAvailableToIndicate = BytesAvailable;
|
||
ULONG BytesToCopy;
|
||
|
||
ASSERT ((!pReceive->pReceiver->CurrentMessageLength) || (pReceive->pReceiver->CurrentMessageLength == MessageLength));
|
||
ASSERT (pReceive->pReceiver->CurrentMessageProcessed == MessageOffset);
|
||
|
||
pReceive->pReceiver->CurrentMessageLength = MessageLength;
|
||
pReceive->pReceiver->CurrentMessageProcessed = MessageOffset;
|
||
|
||
BytesLeftInMessage = MessageLength - MessageOffset;
|
||
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "PgmIndicateToClient",
|
||
"MessageLen=<%d/%d>, MessageOff=<%d>, CurrentML=<%d>, CurrentMP=<%d>\n",
|
||
BytesAvailableToIndicate, MessageLength, MessageOffset,
|
||
pReceive->pReceiver->CurrentMessageLength, pReceive->pReceiver->CurrentMessageProcessed);
|
||
|
||
//
|
||
// We may have a receive Irp pending from a previous indication,
|
||
// so see if need to fill that first!
|
||
//
|
||
while ((BytesAvailableToIndicate) &&
|
||
((pIrpReceive = pReceive->pReceiver->pIrpReceive) ||
|
||
(!IsListEmpty (&pReceive->pReceiver->ReceiveIrpsList))))
|
||
{
|
||
if (!pIrpReceive)
|
||
{
|
||
//
|
||
// The client had posted a receive Irp, so use it now!
|
||
//
|
||
pIrpReceive = CONTAINING_RECORD (pReceive->pReceiver->ReceiveIrpsList.Flink,
|
||
IRP, Tail.Overlay.ListEntry);
|
||
RemoveEntryList (&pIrpReceive->Tail.Overlay.ListEntry);
|
||
|
||
pIrpSp = IoGetCurrentIrpStackLocation (pIrpReceive);
|
||
pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE) &pIrpSp->Parameters;
|
||
|
||
pReceive->pReceiver->pIrpReceive = pIrpReceive;
|
||
pReceive->pReceiver->TotalBytesInMdl = pClientParams->ReceiveLength;
|
||
pReceive->pReceiver->BytesInMdl = 0;
|
||
}
|
||
|
||
//
|
||
// Copy whatever bytes we can into it
|
||
//
|
||
if (BytesAvailableToIndicate >
|
||
(pReceive->pReceiver->TotalBytesInMdl - pReceive->pReceiver->BytesInMdl))
|
||
{
|
||
BytesToCopy = pReceive->pReceiver->TotalBytesInMdl - pReceive->pReceiver->BytesInMdl;
|
||
}
|
||
else
|
||
{
|
||
BytesToCopy = BytesAvailableToIndicate;
|
||
}
|
||
|
||
ClientBytesTaken = 0;
|
||
status = TdiCopyBufferToMdl (pDataBuffer,
|
||
0,
|
||
BytesToCopy,
|
||
pReceive->pReceiver->pIrpReceive->MdlAddress,
|
||
pReceive->pReceiver->BytesInMdl,
|
||
&ClientBytesTaken);
|
||
|
||
pReceive->pReceiver->BytesInMdl += ClientBytesTaken;
|
||
pReceive->pReceiver->CurrentMessageProcessed += ClientBytesTaken;
|
||
|
||
BytesLeftInMessage -= ClientBytesTaken;
|
||
BytesAvailableToIndicate -= ClientBytesTaken;
|
||
pDataBuffer += ClientBytesTaken;
|
||
|
||
if ((!ClientBytesTaken) ||
|
||
(pReceive->pReceiver->BytesInMdl >= pReceive->pReceiver->TotalBytesInMdl) ||
|
||
(!BytesLeftInMessage))
|
||
{
|
||
//
|
||
// The Irp is full, so complete the Irp!
|
||
//
|
||
pIrpReceive = pReceive->pReceiver->pIrpReceive;
|
||
pIrpReceive->IoStatus.Information = pReceive->pReceiver->BytesInMdl;
|
||
if (BytesLeftInMessage)
|
||
{
|
||
pIrpReceive->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
else
|
||
{
|
||
ASSERT (pReceive->pReceiver->CurrentMessageLength == pReceive->pReceiver->CurrentMessageProcessed);
|
||
pIrpReceive->IoStatus.Status = STATUS_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// Before releasing the lock, set the parameters for the next receive
|
||
//
|
||
pReceive->pReceiver->pIrpReceive = NULL;
|
||
pReceive->pReceiver->TotalBytesInMdl = pReceive->pReceiver->BytesInMdl = 0;
|
||
|
||
PgmUnlock (pReceive, *pOldIrqReceive);
|
||
PgmUnlock (pAddress, *pOldIrqAddress);
|
||
|
||
PgmCancelCancelRoutine (pIrpReceive);
|
||
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "PgmIndicateToClient",
|
||
"Completing prior pIrp=<%p>, Bytes=<%d>, BytesLeft=<%d>\n",
|
||
pIrpReceive, pIrpReceive->IoStatus.Information, BytesAvailableToIndicate);
|
||
|
||
IoCompleteRequest (pIrpReceive, IO_NETWORK_INCREMENT);
|
||
|
||
PgmLock (pAddress, *pOldIrqAddress);
|
||
PgmLock (pReceive, *pOldIrqReceive);
|
||
}
|
||
}
|
||
|
||
//
|
||
// If there are no more bytes left to indicate, return
|
||
//
|
||
if (BytesAvailableToIndicate == 0)
|
||
{
|
||
if (!BytesLeftInMessage)
|
||
{
|
||
ASSERT (pReceive->pReceiver->CurrentMessageLength == pReceive->pReceiver->CurrentMessageProcessed);
|
||
pReceive->pReceiver->CurrentMessageLength = pReceive->pReceiver->CurrentMessageProcessed = 0;
|
||
}
|
||
|
||
*pBytesTaken = BytesAvailable - BytesAvailableToIndicate;
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
// call the Client Event Handler
|
||
pIrpReceive = NULL;
|
||
ClientBytesTaken = 0;
|
||
evReceive = pAddress->evReceive;
|
||
ClientSessionContext = pReceive->ClientSessionContext;
|
||
RcvEvContext = pAddress->RcvEvContext;
|
||
ASSERT (RcvEvContext);
|
||
|
||
PgmUnlock (pReceive, *pOldIrqReceive);
|
||
PgmUnlock (pAddress, *pOldIrqAddress);
|
||
|
||
ReceiveFlags = TDI_RECEIVE_NORMAL;
|
||
|
||
if (PgmGetCurrentIrql())
|
||
{
|
||
ReceiveFlags |= TDI_RECEIVE_AT_DISPATCH_LEVEL;
|
||
}
|
||
|
||
#if 0
|
||
if (BytesLeftInMessage == BytesAvailableToIndicate)
|
||
{
|
||
ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE;
|
||
}
|
||
|
||
status = (*evReceive) (RcvEvContext,
|
||
ClientSessionContext,
|
||
ReceiveFlags,
|
||
BytesAvailableToIndicate,
|
||
BytesAvailableToIndicate,
|
||
&ClientBytesTaken,
|
||
pDataBuffer,
|
||
&pIrpReceive);
|
||
#else
|
||
ReceiveFlags |= TDI_RECEIVE_ENTIRE_MESSAGE;
|
||
|
||
status = (*evReceive) (RcvEvContext,
|
||
ClientSessionContext,
|
||
ReceiveFlags,
|
||
BytesAvailableToIndicate,
|
||
BytesLeftInMessage,
|
||
&ClientBytesTaken,
|
||
pDataBuffer,
|
||
&pIrpReceive);
|
||
#endif // 0
|
||
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "PgmIndicateToClient",
|
||
"Client's evReceive returned status=<%x>, ReceiveFlags=<%x>, Client took <%d/%d|%d>, pIrp=<%p>\n",
|
||
status, ReceiveFlags, ClientBytesTaken, BytesAvailableToIndicate, BytesLeftInMessage, pIrpReceive);
|
||
|
||
if (ClientBytesTaken > BytesAvailableToIndicate)
|
||
{
|
||
ClientBytesTaken = BytesAvailableToIndicate;
|
||
}
|
||
|
||
ASSERT (ClientBytesTaken <= BytesAvailableToIndicate);
|
||
BytesAvailableToIndicate -= ClientBytesTaken;
|
||
BytesLeftInMessage -= ClientBytesTaken;
|
||
pDataBuffer = pDataBuffer + ClientBytesTaken;
|
||
|
||
if ((status == STATUS_MORE_PROCESSING_REQUIRED) &&
|
||
(pIrpReceive) &&
|
||
(!NT_SUCCESS (PgmCheckSetCancelRoutine (pIrpReceive, PgmCancelReceiveIrp, FALSE))))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmIndicateToClient",
|
||
"pReceive=<%p>, pIrp=<%p> Cancelled during Receive!\n", pReceive, pIrpReceive);
|
||
|
||
PgmIoComplete (pIrpReceive, STATUS_CANCELLED, 0);
|
||
|
||
PgmLock (pAddress, *pOldIrqAddress);
|
||
PgmLock (pReceive, *pOldIrqReceive);
|
||
|
||
pReceive->pReceiver->CurrentMessageProcessed += ClientBytesTaken;
|
||
|
||
*pBytesTaken = BytesAvailable - BytesAvailableToIndicate;
|
||
return (STATUS_UNSUCCESSFUL);
|
||
}
|
||
|
||
PgmLock (pAddress, *pOldIrqAddress);
|
||
PgmLock (pReceive, *pOldIrqReceive);
|
||
|
||
pReceive->pReceiver->CurrentMessageProcessed += ClientBytesTaken;
|
||
|
||
if (!pReceive->pReceiver->pAddress)
|
||
{
|
||
// the connection was disassociated in the interim so do nothing.
|
||
if (status == STATUS_MORE_PROCESSING_REQUIRED)
|
||
{
|
||
PgmUnlock (pReceive, *pOldIrqReceive);
|
||
PgmUnlock (pAddress, *pOldIrqAddress);
|
||
|
||
PgmIoComplete (pIrpReceive, STATUS_CANCELLED, 0);
|
||
|
||
PgmLock (pAddress, *pOldIrqAddress);
|
||
PgmLock (pReceive, *pOldIrqReceive);
|
||
}
|
||
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmIndicateToClient",
|
||
"pReceive=<%p> disassociated during Receive!\n", pReceive);
|
||
|
||
*pBytesTaken = BytesAvailable - BytesAvailableToIndicate;
|
||
return (STATUS_UNSUCCESSFUL);
|
||
}
|
||
|
||
if (status == STATUS_MORE_PROCESSING_REQUIRED)
|
||
{
|
||
ASSERT (pIrpReceive);
|
||
ASSERT (pIrpReceive->MdlAddress);
|
||
|
||
pIrpSp = IoGetCurrentIrpStackLocation (pIrpReceive);
|
||
pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE) &pIrpSp->Parameters;
|
||
ASSERT (pClientParams->ReceiveLength);
|
||
ClientBytesTaken = 0;
|
||
|
||
if (pClientParams->ReceiveLength < BytesAvailableToIndicate)
|
||
{
|
||
BytesToCopy = pClientParams->ReceiveLength;
|
||
}
|
||
else
|
||
{
|
||
BytesToCopy = BytesAvailableToIndicate;
|
||
}
|
||
|
||
status = TdiCopyBufferToMdl (pDataBuffer,
|
||
0,
|
||
BytesToCopy,
|
||
pIrpReceive->MdlAddress,
|
||
pReceive->pReceiver->BytesInMdl,
|
||
&ClientBytesTaken);
|
||
|
||
BytesLeftInMessage -= ClientBytesTaken;
|
||
BytesAvailableToIndicate -= ClientBytesTaken;
|
||
pDataBuffer = pDataBuffer + ClientBytesTaken;
|
||
pReceive->pReceiver->CurrentMessageProcessed += ClientBytesTaken;
|
||
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "PgmIndicateToClient",
|
||
"Client's evReceive returned pIrp=<%p>, BytesInIrp=<%d>, Copied <%d> bytes\n",
|
||
pIrpReceive, pClientParams->ReceiveLength, ClientBytesTaken);
|
||
|
||
if ((!ClientBytesTaken) ||
|
||
(ClientBytesTaken >= pClientParams->ReceiveLength) ||
|
||
(pReceive->pReceiver->CurrentMessageLength == pReceive->pReceiver->CurrentMessageProcessed))
|
||
{
|
||
//
|
||
// The Irp is full, so complete the Irp!
|
||
//
|
||
pIrpReceive->IoStatus.Information = ClientBytesTaken;
|
||
if (pReceive->pReceiver->CurrentMessageLength == pReceive->pReceiver->CurrentMessageProcessed)
|
||
{
|
||
pIrpReceive->IoStatus.Status = STATUS_SUCCESS;
|
||
}
|
||
else
|
||
{
|
||
pIrpReceive->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
|
||
}
|
||
|
||
//
|
||
// Before releasing the lock, set the parameters for the next receive
|
||
//
|
||
pReceive->pReceiver->TotalBytesInMdl = pReceive->pReceiver->BytesInMdl = 0;
|
||
|
||
PgmUnlock (pReceive, *pOldIrqReceive);
|
||
PgmUnlock (pAddress, *pOldIrqAddress);
|
||
|
||
PgmCancelCancelRoutine (pIrpReceive);
|
||
IoCompleteRequest (pIrpReceive, IO_NETWORK_INCREMENT);
|
||
|
||
PgmLock (pAddress, *pOldIrqAddress);
|
||
PgmLock (pReceive, *pOldIrqReceive);
|
||
}
|
||
else
|
||
{
|
||
pReceive->pReceiver->TotalBytesInMdl = pClientParams->ReceiveLength;
|
||
pReceive->pReceiver->BytesInMdl = ClientBytesTaken;
|
||
pReceive->pReceiver->pIrpReceive = pIrpReceive;
|
||
}
|
||
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
else if (status == STATUS_DATA_NOT_ACCEPTED)
|
||
{
|
||
//
|
||
// An Irp could have been posted in the interval
|
||
// between the indicate and acquiring the SpinLocks,
|
||
// so check for that here
|
||
//
|
||
if ((pReceive->pReceiver->pIrpReceive) ||
|
||
(!IsListEmpty (&pReceive->pReceiver->ReceiveIrpsList)))
|
||
{
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
else
|
||
{
|
||
pReceive->SessionFlags |= PGM_SESSION_WAIT_FOR_RECEIVE_IRP;
|
||
}
|
||
}
|
||
|
||
if (pReceive->pReceiver->CurrentMessageLength == pReceive->pReceiver->CurrentMessageProcessed)
|
||
{
|
||
pReceive->pReceiver->CurrentMessageLength = pReceive->pReceiver->CurrentMessageProcessed = 0;
|
||
}
|
||
|
||
if ((NT_SUCCESS (status)) ||
|
||
(status == STATUS_DATA_NOT_ACCEPTED))
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "PgmIndicateToClient",
|
||
"status=<%x>, pReceive=<%p>, Taken=<%d>, Available=<%d>\n",
|
||
status, pReceive, ClientBytesTaken, BytesLeftInMessage);
|
||
//
|
||
// since some bytes were taken (i.e. the session hdr) so
|
||
// return status success. (otherwise the status is
|
||
// statusNotAccpeted).
|
||
//
|
||
}
|
||
else
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmIndicateToClient",
|
||
"Unexpected status=<%x>\n", status);
|
||
|
||
ASSERT (0);
|
||
}
|
||
|
||
*pBytesTaken = BytesAvailable - BytesAvailableToIndicate;
|
||
return (status);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
PgmIndicateGroup(
|
||
IN tADDRESS_CONTEXT *pAddress,
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN PGMLockHandle *pOldIrqAddress,
|
||
IN PGMLockHandle *pOldIrqReceive,
|
||
IN tNAK_FORWARD_DATA *pNak
|
||
)
|
||
{
|
||
UCHAR i, j;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
ULONG BytesTaken, DataBytes, MessageLength;
|
||
|
||
ASSERT (pNak->SequenceNumber == pReceive->pReceiver->NextODataSequenceNumber);
|
||
|
||
j = pNak->NextIndexToIndicate;
|
||
while ((j < pNak->PacketsInGroup) &&
|
||
!(pReceive->SessionFlags & PGM_SESSION_DISCONNECT_INDICATED))
|
||
{
|
||
i = pNak->pPendingData[j].ActualIndexOfDataPacket;
|
||
ASSERT (i < pNak->OriginalGroupSize);
|
||
|
||
if (pReceive->SessionFlags & PGM_SESSION_FLAG_FIRST_PACKET)
|
||
{
|
||
//
|
||
// pReceive->pReceiver->CurrentMessageProcessed would have been set
|
||
// if we were receiving a fragmented message
|
||
// or if we had only accounted for a partial message earlier
|
||
//
|
||
ASSERT (!(pReceive->pReceiver->CurrentMessageProcessed) &&
|
||
!(pReceive->pReceiver->CurrentMessageLength));
|
||
|
||
if (pNak->pPendingData[i].MessageOffset)
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "PgmIndicateGroup",
|
||
"Dropping SeqNum=[%d] since it's a PARTIAL message [%d / %d]!\n",
|
||
(ULONG) (pReceive->pReceiver->NextODataSequenceNumber + j),
|
||
pNak->pPendingData[i].MessageOffset, pNak->pPendingData[i].MessageLength);
|
||
|
||
j++;
|
||
pNak->NextIndexToIndicate++;
|
||
continue;
|
||
}
|
||
|
||
pReceive->SessionFlags &= ~PGM_SESSION_FLAG_FIRST_PACKET;
|
||
}
|
||
else if ((pReceive->pReceiver->CurrentMessageProcessed !=
|
||
pNak->pPendingData[i].MessageOffset) || // Check Offsets
|
||
((pReceive->pReceiver->CurrentMessageProcessed) && // in the midst of a Message, and
|
||
(pReceive->pReceiver->CurrentMessageLength !=
|
||
pNak->pPendingData[i].MessageLength))) // Check MessageLength
|
||
{
|
||
//
|
||
// Our state expects us to be in the middle of a message, but
|
||
// the current packets do not show this
|
||
//
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmIndicateGroup",
|
||
"SeqNum=[%d] Expecting MsgLen=<%d>, MsgOff=<%d>, have MsgLen=<%d>, MsgOff=<%d>\n",
|
||
(ULONG) (pReceive->pReceiver->NextODataSequenceNumber + j),
|
||
pReceive->pReceiver->CurrentMessageLength, pReceive->pReceiver->CurrentMessageProcessed,
|
||
pNak->pPendingData[i].MessageLength,
|
||
pNak->pPendingData[i].MessageOffset);
|
||
|
||
ASSERT (0);
|
||
return (STATUS_UNSUCCESSFUL);
|
||
}
|
||
|
||
DataBytes = pNak->pPendingData[i].PacketLength - pNak->pPendingData[i].DataOffset;
|
||
if (!DataBytes)
|
||
{
|
||
//
|
||
// No need to process empty data packets (can happen if the client
|
||
// picks up partial FEC group)
|
||
//
|
||
j++;
|
||
pNak->NextIndexToIndicate++;
|
||
continue;
|
||
}
|
||
|
||
if (DataBytes > (pNak->pPendingData[i].MessageLength - pNak->pPendingData[i].MessageOffset))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmIndicateGroup",
|
||
"[%d] DataBytes=<%d> > MsgLen=<%d> - MsgOff=<%d> = <%d>\n",
|
||
(ULONG) (pReceive->pReceiver->NextODataSequenceNumber + j),
|
||
DataBytes, pNak->pPendingData[i].MessageLength,
|
||
pNak->pPendingData[i].MessageOffset,
|
||
(pNak->pPendingData[i].MessageLength - pNak->pPendingData[i].MessageOffset));
|
||
|
||
ASSERT (0);
|
||
return (STATUS_UNSUCCESSFUL);
|
||
}
|
||
|
||
BytesTaken = 0;
|
||
status = PgmIndicateToClient (pAddress,
|
||
pReceive,
|
||
DataBytes,
|
||
(pNak->pPendingData[i].pDataPacket + pNak->pPendingData[i].DataOffset),
|
||
pNak->pPendingData[i].MessageOffset,
|
||
pNak->pPendingData[i].MessageLength,
|
||
&BytesTaken,
|
||
pOldIrqAddress,
|
||
pOldIrqReceive);
|
||
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "PgmIndicateGroup",
|
||
"SeqNum=[%d]: PgmIndicate returned<%x>\n",
|
||
(ULONG) pNak->SequenceNumber, status);
|
||
|
||
ASSERT (BytesTaken <= DataBytes);
|
||
|
||
pNak->pPendingData[i].MessageOffset += BytesTaken;
|
||
pNak->pPendingData[i].DataOffset += (USHORT) BytesTaken;
|
||
|
||
if (BytesTaken == DataBytes)
|
||
{
|
||
//
|
||
// Go to the next packet
|
||
//
|
||
j++;
|
||
pNak->NextIndexToIndicate++;
|
||
pReceive->pReceiver->DataPacketsIndicated++;
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
else if (!NT_SUCCESS (status))
|
||
{
|
||
//
|
||
// We failed, and if the status was STATUS_DATA_NOT_ACCEPTED,
|
||
// we also don't have any ReceiveIrps pending either
|
||
//
|
||
break;
|
||
}
|
||
//
|
||
// else retry indicating this data until we get an error
|
||
//
|
||
}
|
||
|
||
//
|
||
// If the status is anything other than STATUS_DATA_NOT_ACCEPTED (whether
|
||
// success or failure), then it means we are done with this data!
|
||
//
|
||
return (status);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
DecodeParityPackets(
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN tNAK_FORWARD_DATA *pNak
|
||
)
|
||
{
|
||
NTSTATUS status;
|
||
USHORT MinBufferSize;
|
||
USHORT DataBytes, FprOffset;
|
||
UCHAR i;
|
||
PUCHAR pDataBuffer;
|
||
tPOST_PACKET_FEC_CONTEXT FECContext;
|
||
|
||
PgmZeroMemory (&FECContext, sizeof (tPOST_PACKET_FEC_CONTEXT));
|
||
|
||
//
|
||
// Verify that the our buffer is large enough to hold the data
|
||
//
|
||
ASSERT (pReceive->MaxMTULength > pNak->ParityDataSize);
|
||
MinBufferSize = pNak->ParityDataSize + sizeof(tPOST_PACKET_FEC_CONTEXT) - sizeof(USHORT);
|
||
|
||
ASSERT (pNak->PacketsInGroup == pNak->NumDataPackets + pNak->NumParityPackets);
|
||
//
|
||
// Now, copy the data into the DecodeBuffers
|
||
//
|
||
FprOffset = pNak->ParityDataSize - sizeof(USHORT) +
|
||
FIELD_OFFSET (tPOST_PACKET_FEC_CONTEXT, FragmentOptSpecific);
|
||
pDataBuffer = pReceive->pFECBuffer;
|
||
for (i=0; i<pReceive->FECGroupSize; i++)
|
||
{
|
||
//
|
||
// See if this is a NULL buffer (for partial groups!)
|
||
//
|
||
if (i >= pNak->PacketsInGroup)
|
||
{
|
||
ASSERT (!pNak->pPendingData[i].PacketIndex);
|
||
ASSERT (!pNak->pPendingData[i].pDataPacket);
|
||
DataBytes = pNak->ParityDataSize - sizeof(USHORT) + sizeof (tPOST_PACKET_FEC_CONTEXT);
|
||
pNak->pPendingData[i].PacketIndex = i;
|
||
pNak->pPendingData[i].PacketLength = DataBytes;
|
||
pNak->pPendingData[i].DataOffset = 0;
|
||
|
||
PgmZeroMemory (pDataBuffer, DataBytes);
|
||
pDataBuffer [FprOffset] = PACKET_OPTION_SPECIFIC_ENCODED_NULL_BIT;
|
||
pNak->pPendingData[i].DecodeBuffer = pDataBuffer;
|
||
pDataBuffer += DataBytes;
|
||
|
||
PgmZeroMemory (pDataBuffer, DataBytes);
|
||
pNak->pPendingData[i].pDataPacket = pDataBuffer;
|
||
pDataBuffer += DataBytes;
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// See if this is a parity packet!
|
||
//
|
||
if (pNak->pPendingData[i].PacketIndex >= pReceive->FECGroupSize)
|
||
{
|
||
DataBytes = pNak->pPendingData[i].PacketLength - pNak->pPendingData[i].DataOffset;
|
||
ASSERT (DataBytes == pNak->ParityDataSize);
|
||
PgmCopyMemory (pDataBuffer,
|
||
pNak->pPendingData[i].pDataPacket + pNak->pPendingData[i].DataOffset,
|
||
DataBytes);
|
||
pNak->pPendingData[i].DecodeBuffer = pDataBuffer;
|
||
|
||
pDataBuffer += (pNak->ParityDataSize - sizeof(USHORT));
|
||
PgmCopyMemory (&FECContext.EncodedTSDULength, pDataBuffer, sizeof (USHORT));
|
||
FECContext.FragmentOptSpecific = pNak->pPendingData[i].FragmentOptSpecific;
|
||
FECContext.EncodedFragmentOptions.MessageFirstSequence = pNak->pPendingData[i].MessageFirstSequence;
|
||
FECContext.EncodedFragmentOptions.MessageOffset = pNak->pPendingData[i].MessageOffset;
|
||
FECContext.EncodedFragmentOptions.MessageLength = pNak->pPendingData[i].MessageLength;
|
||
|
||
PgmCopyMemory (pDataBuffer, &FECContext, sizeof (tPOST_PACKET_FEC_CONTEXT));
|
||
pDataBuffer += sizeof (tPOST_PACKET_FEC_CONTEXT);
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// This is a Data packet
|
||
//
|
||
ASSERT (pNak->pPendingData[i].PacketIndex < pNak->PacketsInGroup);
|
||
|
||
DataBytes = pNak->pPendingData[i].PacketLength - pNak->pPendingData[i].DataOffset;
|
||
ASSERT ((DataBytes+sizeof(USHORT)) <= pNak->ParityDataSize);
|
||
|
||
// Copy the data
|
||
PgmCopyMemory (pDataBuffer,
|
||
pNak->pPendingData[i].pDataPacket + pNak->pPendingData[i].DataOffset,
|
||
DataBytes);
|
||
|
||
//
|
||
// Verify that the Data Buffer length is sufficient for the output data
|
||
//
|
||
if ((pNak->MinPacketLength < MinBufferSize) &&
|
||
(pNak->pPendingData[i].PacketLength < pNak->ParityDataSize))
|
||
{
|
||
if (!(pNak->pPendingData[i].DecodeBuffer = PgmAllocMem (MinBufferSize, PGM_TAG('3'))))
|
||
{
|
||
ASSERT (0);
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "DecodeParityPackets",
|
||
"STATUS_INSUFFICIENT_RESOURCES[2] ...\n");
|
||
|
||
return (STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
PgmFreeMem (pNak->pPendingData[i].pDataPacket);
|
||
pNak->pPendingData[i].pDataPacket = pNak->pPendingData[i].DecodeBuffer;
|
||
}
|
||
pNak->pPendingData[i].DecodeBuffer = pDataBuffer;
|
||
|
||
//
|
||
// Zero the remaining buffer
|
||
//
|
||
PgmZeroMemory ((pDataBuffer + DataBytes), (pNak->ParityDataSize - DataBytes));
|
||
pDataBuffer += (pNak->ParityDataSize - sizeof(USHORT));
|
||
|
||
FECContext.EncodedTSDULength = htons (DataBytes);
|
||
FECContext.FragmentOptSpecific = pNak->pPendingData[i].FragmentOptSpecific;
|
||
if (FECContext.FragmentOptSpecific & PACKET_OPTION_SPECIFIC_ENCODED_NULL_BIT)
|
||
{
|
||
//
|
||
// This bit is set if the option did not exist in the original packet
|
||
//
|
||
FECContext.EncodedFragmentOptions.MessageFirstSequence = 0;
|
||
FECContext.EncodedFragmentOptions.MessageOffset = 0;
|
||
FECContext.EncodedFragmentOptions.MessageLength = 0;
|
||
}
|
||
else
|
||
{
|
||
FECContext.EncodedFragmentOptions.MessageFirstSequence = htonl (pNak->pPendingData[i].MessageFirstSequence);
|
||
FECContext.EncodedFragmentOptions.MessageOffset = htonl (pNak->pPendingData[i].MessageOffset);
|
||
FECContext.EncodedFragmentOptions.MessageLength = htonl (pNak->pPendingData[i].MessageLength);
|
||
}
|
||
|
||
PgmCopyMemory (pDataBuffer, &FECContext, sizeof (tPOST_PACKET_FEC_CONTEXT));
|
||
pDataBuffer += sizeof (tPOST_PACKET_FEC_CONTEXT);
|
||
}
|
||
|
||
DataBytes = pNak->ParityDataSize - sizeof(USHORT) + sizeof (tPOST_PACKET_FEC_CONTEXT);
|
||
status = FECDecode (&pReceive->FECContext,
|
||
&(pNak->pPendingData[0]),
|
||
DataBytes,
|
||
pNak->PacketsInGroup);
|
||
|
||
//
|
||
// Before we do anything else, we should NULL out the dummy DataBuffer
|
||
// ptrs so that they don't get Free'ed accidentally!
|
||
//
|
||
for (i=0; i<pReceive->FECGroupSize; i++)
|
||
{
|
||
pNak->pPendingData[i].DecodeBuffer = NULL;
|
||
if (i >= pNak->PacketsInGroup)
|
||
{
|
||
pNak->pPendingData[i].pDataPacket = NULL;
|
||
}
|
||
pNak->pPendingData[i].ActualIndexOfDataPacket = i;
|
||
}
|
||
|
||
if (NT_SUCCESS (status))
|
||
{
|
||
pNak->NumDataPackets = pNak->PacketsInGroup;
|
||
pNak->NumParityPackets = 0;
|
||
|
||
DataBytes -= sizeof (tPOST_PACKET_FEC_CONTEXT);
|
||
for (i=0; i<pNak->PacketsInGroup; i++)
|
||
{
|
||
PgmCopyMemory (&FECContext,
|
||
&(pNak->pPendingData[i].pDataPacket) [DataBytes],
|
||
sizeof (tPOST_PACKET_FEC_CONTEXT));
|
||
|
||
pNak->pPendingData[i].PacketLength = ntohs (FECContext.EncodedTSDULength);
|
||
if (pNak->pPendingData[i].PacketLength > DataBytes)
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "DecodeParityPackets",
|
||
"[%d] PacketLength=<%d> > MaxDataBytes=<%d>\n",
|
||
(ULONG) i, (ULONG) pNak->pPendingData[i].PacketLength, (ULONG) DataBytes);
|
||
|
||
ASSERT (0);
|
||
return (STATUS_UNSUCCESSFUL);
|
||
}
|
||
pNak->pPendingData[i].DataOffset = 0;
|
||
pNak->pPendingData[i].PacketIndex = i;
|
||
|
||
ASSERT ((pNak->AllOptionsFlags & PGM_OPTION_FLAG_FRAGMENT) ||
|
||
(!FECContext.EncodedFragmentOptions.MessageLength));
|
||
|
||
if (!(pNak->AllOptionsFlags & PGM_OPTION_FLAG_FRAGMENT) ||
|
||
(FECContext.FragmentOptSpecific & PACKET_OPTION_SPECIFIC_ENCODED_NULL_BIT))
|
||
{
|
||
//
|
||
// This is not a packet fragment
|
||
//
|
||
pNak->pPendingData[i].MessageFirstSequence = (ULONG) (SEQ_TYPE) (pNak->SequenceNumber + i);
|
||
pNak->pPendingData[i].MessageOffset = 0;
|
||
pNak->pPendingData[i].MessageLength = pNak->pPendingData[i].PacketLength;
|
||
}
|
||
else
|
||
{
|
||
pNak->pPendingData[i].MessageFirstSequence = ntohl (FECContext.EncodedFragmentOptions.MessageFirstSequence);
|
||
pNak->pPendingData[i].MessageOffset = ntohl (FECContext.EncodedFragmentOptions.MessageOffset);
|
||
pNak->pPendingData[i].MessageLength = ntohl (FECContext.EncodedFragmentOptions.MessageLength);
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "DecodeParityPackets",
|
||
"FECDecode returned <%x>\n", status);
|
||
|
||
ASSERT (0);
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
return (status);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
CheckIndicatePendedData(
|
||
IN tADDRESS_CONTEXT *pAddress,
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN PGMLockHandle *pOldIrqAddress,
|
||
IN PGMLockHandle *pOldIrqReceive
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is typically called if the client signalled an
|
||
inability to handle indicated data -- it will reattempt to
|
||
indicate the data to the client
|
||
|
||
It is called with the pAddress and pReceive locks held
|
||
|
||
Arguments:
|
||
|
||
IN pAddress -- Address object context
|
||
IN pReceive -- Receive context
|
||
IN pOldIrqAddress -- OldIrq for the Address lock
|
||
IN pOldIrqReceive -- OldIrq for the Receive lock
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Final status of the call
|
||
|
||
--*/
|
||
{
|
||
tNAK_FORWARD_DATA *pNextNak;
|
||
tPACKET_OPTIONS PacketOptions;
|
||
ULONG PacketsIndicated;
|
||
tBASIC_DATA_PACKET_HEADER UNALIGNED *pPgmDataHeader;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// If we are already indicating data on another thread, or
|
||
// waiting for the client to post a receive irp, just return
|
||
//
|
||
if ((pReceive->SessionFlags & (PGM_SESSION_FLAG_IN_INDICATE | PGM_SESSION_WAIT_FOR_RECEIVE_IRP)) ||
|
||
(IsListEmpty (&pReceive->pReceiver->BufferedDataList)))
|
||
{
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
pReceive->SessionFlags |= PGM_SESSION_FLAG_IN_INDICATE;
|
||
|
||
pNextNak = CONTAINING_RECORD (pReceive->pReceiver->BufferedDataList.Flink, tNAK_FORWARD_DATA, Linkage);
|
||
ASSERT (pNextNak->SequenceNumber == pReceive->pReceiver->NextODataSequenceNumber);
|
||
|
||
do
|
||
{
|
||
//
|
||
// If we do not have all the data packets, we will need to decode them now
|
||
//
|
||
if (pNextNak->NumParityPackets)
|
||
{
|
||
ASSERT ((pNextNak->NumParityPackets + pNextNak->NumDataPackets) == pNextNak->PacketsInGroup);
|
||
status = DecodeParityPackets (pReceive, pNextNak);
|
||
}
|
||
else
|
||
{
|
||
ASSERT ((pNextNak->NextIndexToIndicate + pNextNak->NumDataPackets) >= pNextNak->PacketsInGroup);
|
||
// The above assertion can be greater if we have only partially indicated a group
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
if (NT_SUCCESS (status))
|
||
{
|
||
status = PgmIndicateGroup (pAddress, pReceive, pOldIrqAddress, pOldIrqReceive, pNextNak);
|
||
}
|
||
else
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "CheckIndicatePendedData",
|
||
"DecodeParityPackets returned <%x>\n", status);
|
||
}
|
||
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
//
|
||
// If the client cannot accept any more data at this time, so
|
||
// we will try again later, otherwise terminate this session!
|
||
//
|
||
if (status != STATUS_DATA_NOT_ACCEPTED)
|
||
{
|
||
ASSERT (0);
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
PacketsIndicated = pNextNak->NumDataPackets + pNextNak->NumParityPackets;
|
||
pReceive->pReceiver->TotalDataPacketsBuffered -= PacketsIndicated;
|
||
pReceive->pReceiver->DataPacketsPendingIndicate -= PacketsIndicated;
|
||
pReceive->pReceiver->NumPacketGroupsPendingClient--;
|
||
ASSERT (pReceive->pReceiver->TotalDataPacketsBuffered >= pReceive->pReceiver->NumPacketGroupsPendingClient);
|
||
|
||
//
|
||
// Advance to the next group boundary
|
||
//
|
||
pReceive->pReceiver->NextODataSequenceNumber += pNextNak->OriginalGroupSize;
|
||
|
||
RemoveEntryList (&pNextNak->Linkage);
|
||
FreeNakContext (pReceive, pNextNak);
|
||
|
||
if (IsListEmpty (&pReceive->pReceiver->BufferedDataList))
|
||
{
|
||
break;
|
||
}
|
||
ASSERT (pReceive->pReceiver->NumPacketGroupsPendingClient);
|
||
|
||
pNextNak = CONTAINING_RECORD (pReceive->pReceiver->BufferedDataList.Flink, tNAK_FORWARD_DATA, Linkage);
|
||
ASSERT (pNextNak->SequenceNumber == pReceive->pReceiver->NextODataSequenceNumber);
|
||
pReceive->pReceiver->NextODataSequenceNumber = pNextNak->SequenceNumber;
|
||
|
||
if (SEQ_LT(pReceive->pReceiver->FirstNakSequenceNumber, pReceive->pReceiver->NextODataSequenceNumber))
|
||
{
|
||
pReceive->pReceiver->FirstNakSequenceNumber = pReceive->pReceiver->NextODataSequenceNumber;
|
||
}
|
||
} while (1);
|
||
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "CheckIndicatePendedData",
|
||
"status=<%x>, pReceive=<%p>, SessionFlags=<%x>\n",
|
||
status, pReceive, pReceive->SessionFlags);
|
||
|
||
pReceive->SessionFlags &= ~PGM_SESSION_FLAG_IN_INDICATE;
|
||
CheckIndicateDisconnect (pAddress, pReceive, pOldIrqAddress, pOldIrqReceive, TRUE);
|
||
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
|
||
#ifdef MAX_BUFF_DBG
|
||
ULONG MaxPacketGroupsPendingClient = 0;
|
||
ULONG MaxPacketsBuffered = 0;
|
||
ULONG MaxPacketsPendingIndicate = 0;
|
||
ULONG MaxPacketsPendingNaks = 0;
|
||
#endif // MAX_BUFF_DBG
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
PgmHandleNewData(
|
||
IN SEQ_TYPE *pThisDataSequenceNumber,
|
||
IN tADDRESS_CONTEXT *pAddress,
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN USHORT PacketLength,
|
||
IN tBASIC_DATA_PACKET_HEADER UNALIGNED *pOData,
|
||
IN UCHAR PacketType,
|
||
IN PGMLockHandle *pOldIrqAddress,
|
||
IN PGMLockHandle *pOldIrqReceive
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine buffers data packets received out-of-order
|
||
|
||
Arguments:
|
||
|
||
IN pThisDataSequenceNumber -- Sequence # of unordered data packet
|
||
IN pAddress -- Address object context
|
||
IN pReceive -- Receive context
|
||
IN PacketLength -- Length of packet received from the wire
|
||
IN pODataBuffer -- Data packet
|
||
IN PacketType -- Type of Pgm packet
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Final status of the call
|
||
|
||
--*/
|
||
{
|
||
SEQ_TYPE ThisDataSequenceNumber = *pThisDataSequenceNumber;
|
||
LIST_ENTRY *pEntry;
|
||
PNAK_FORWARD_DATA pOldNak, pLastNak = NULL;
|
||
ULONG MessageLength, DataOffset, BytesTaken, DataBytes;
|
||
ULONGLONG NcfRDataTickCounts;
|
||
NTSTATUS status;
|
||
USHORT TSDULength;
|
||
tPACKET_OPTIONS PacketOptions;
|
||
UCHAR i, PacketIndex, NakIndex;
|
||
BOOLEAN fIsParityPacket;
|
||
PUCHAR pDataBuffer;
|
||
|
||
fIsParityPacket = pOData->CommonHeader.Options & PACKET_HEADER_OPTIONS_PARITY;
|
||
|
||
ASSERT (PacketLength <= pReceive->MaxMTULength);
|
||
|
||
//
|
||
// Extract all the information that we need from the packet options right now!
|
||
//
|
||
PgmZeroMemory (&PacketOptions, sizeof (tPACKET_OPTIONS));
|
||
if (pOData->CommonHeader.Options & PACKET_HEADER_OPTIONS_PRESENT)
|
||
{
|
||
status = ProcessOptions ((tPACKET_OPTION_LENGTH *) (pOData + 1),
|
||
(PacketLength - sizeof(tBASIC_DATA_PACKET_HEADER)),
|
||
(pOData->CommonHeader.Type & 0x0f),
|
||
&PacketOptions,
|
||
NULL);
|
||
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmHandleNewData",
|
||
"ProcessOptions returned <%x>, SeqNum=[%d]: NumOutOfOrder=<%d> ...\n",
|
||
status, (ULONG) ThisDataSequenceNumber, pReceive->pReceiver->TotalDataPacketsBuffered);
|
||
|
||
ASSERT (0);
|
||
|
||
pReceive->pReceiver->NumDataPacketsDropped++;
|
||
return (status);
|
||
}
|
||
}
|
||
|
||
PgmCopyMemory (&TSDULength, &pOData->CommonHeader.TSDULength, sizeof (USHORT));
|
||
TSDULength = ntohs (TSDULength);
|
||
if (PacketLength != (sizeof(tBASIC_DATA_PACKET_HEADER) + PacketOptions.OptionsLength + TSDULength))
|
||
{
|
||
ASSERT (0);
|
||
pReceive->pReceiver->NumDataPacketsDropped++;
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
|
||
DataOffset = sizeof (tBASIC_DATA_PACKET_HEADER) + PacketOptions.OptionsLength;
|
||
DataBytes = TSDULength;
|
||
|
||
ASSERT ((PacketOptions.OptionsFlags & ~PGM_VALID_DATA_OPTION_FLAGS) == 0);
|
||
pLastNak = NULL;
|
||
BytesTaken = 0;
|
||
|
||
//
|
||
// If we are not parity-enabled, and this is the next expected data packet,
|
||
// we can try to indicate this data over here only
|
||
//
|
||
if ((!pReceive->FECOptions) &&
|
||
((ULONG) ThisDataSequenceNumber == (ULONG) pReceive->pReceiver->NextODataSequenceNumber) &&
|
||
(IsListEmpty (&pReceive->pReceiver->BufferedDataList)) &&
|
||
(!fIsParityPacket) &&
|
||
!(pReceive->SessionFlags & (PGM_SESSION_FLAG_IN_INDICATE |
|
||
PGM_SESSION_WAIT_FOR_RECEIVE_IRP |
|
||
PGM_SESSION_DISCONNECT_INDICATED |
|
||
PGM_SESSION_TERMINATED_ABORT)))
|
||
{
|
||
ASSERT (!pReceive->pReceiver->NumPacketGroupsPendingClient);
|
||
if (!IsListEmpty (&pReceive->pReceiver->NaksForwardDataList))
|
||
{
|
||
pLastNak = CONTAINING_RECORD (pReceive->pReceiver->NaksForwardDataList.Flink, tNAK_FORWARD_DATA, Linkage);
|
||
ASSERT ((pLastNak->SequenceNumber == ThisDataSequenceNumber) &&
|
||
(!pLastNak->pPendingData[0].pDataPacket));
|
||
}
|
||
|
||
if (PacketOptions.MessageLength)
|
||
{
|
||
MessageLength = PacketOptions.MessageLength;
|
||
ASSERT (DataBytes <= MessageLength - PacketOptions.MessageOffset);
|
||
}
|
||
else
|
||
{
|
||
MessageLength = DataBytes;
|
||
ASSERT (!PacketOptions.MessageOffset);
|
||
}
|
||
|
||
//
|
||
// If we have a NULL packet, then skip it
|
||
//
|
||
if ((!DataBytes) ||
|
||
(PacketOptions.MessageOffset == MessageLength))
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "PgmHandleNewData",
|
||
"Dropping SeqNum=[%d] since it's a NULL message [%d / %d]!\n",
|
||
(ULONG) (pReceive->pReceiver->NextODataSequenceNumber),
|
||
PacketOptions.MessageOffset, PacketOptions.MessageLength);
|
||
|
||
BytesTaken = DataBytes;
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
//
|
||
// If we are starting receiving in the midst of a message, we should also ignore
|
||
//
|
||
else if ((pReceive->SessionFlags & PGM_SESSION_FLAG_FIRST_PACKET) &&
|
||
(PacketOptions.MessageOffset))
|
||
{
|
||
//
|
||
// pReceive->pReceiver->CurrentMessageProcessed would have been set
|
||
// if we were receiving a fragmented message
|
||
// or if we had only accounted for a partial message earlier
|
||
//
|
||
ASSERT (!(pReceive->pReceiver->CurrentMessageProcessed) &&
|
||
!(pReceive->pReceiver->CurrentMessageLength));
|
||
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "PgmHandleNewData",
|
||
"Dropping SeqNum=[%d] since it's a PARTIAL message [%d / %d]!\n",
|
||
(ULONG) (pReceive->pReceiver->NextODataSequenceNumber),
|
||
PacketOptions.MessageOffset, PacketOptions.MessageLength);
|
||
|
||
BytesTaken = DataBytes;
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
else if ((pReceive->pReceiver->CurrentMessageProcessed != PacketOptions.MessageOffset) ||
|
||
((pReceive->pReceiver->CurrentMessageProcessed) &&
|
||
(pReceive->pReceiver->CurrentMessageLength != PacketOptions.MessageLength)))
|
||
{
|
||
//
|
||
// Our state expects us to be in the middle of a message, but
|
||
// the current packets do not show this
|
||
//
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmHandleNewData",
|
||
"SeqNum=[%d] Expecting MsgLen=<%d>, MsgOff=<%d>, have MsgLen=<%d>, MsgOff=<%d>\n",
|
||
(ULONG) pReceive->pReceiver->NextODataSequenceNumber,
|
||
pReceive->pReceiver->CurrentMessageLength,
|
||
pReceive->pReceiver->CurrentMessageProcessed,
|
||
PacketOptions.MessageLength, PacketOptions.MessageOffset);
|
||
|
||
ASSERT (0);
|
||
BytesTaken = DataBytes;
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
else
|
||
{
|
||
pReceive->SessionFlags |= PGM_SESSION_FLAG_IN_INDICATE;
|
||
|
||
status = PgmIndicateToClient (pAddress,
|
||
pReceive,
|
||
DataBytes,
|
||
(((PUCHAR) pOData) + DataOffset),
|
||
PacketOptions.MessageOffset,
|
||
MessageLength,
|
||
&BytesTaken,
|
||
pOldIrqAddress,
|
||
pOldIrqReceive);
|
||
|
||
pReceive->SessionFlags &= ~(PGM_SESSION_FLAG_IN_INDICATE | PGM_SESSION_FLAG_FIRST_PACKET);
|
||
pReceive->DataBytes += BytesTaken;
|
||
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "PgmHandleNewData",
|
||
"SeqNum=[%d]: PgmIndicate returned<%x>\n",
|
||
(ULONG) ThisDataSequenceNumber, status);
|
||
|
||
ASSERT (BytesTaken <= DataBytes);
|
||
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
//
|
||
// If the client cannot accept any more data at this time, so
|
||
// we will try again later, otherwise terminate this session!
|
||
//
|
||
if (status != STATUS_DATA_NOT_ACCEPTED)
|
||
{
|
||
ASSERT (0);
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
BytesTaken = DataBytes;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_FIN)
|
||
{
|
||
pReceive->pReceiver->FinDataSequenceNumber = ThisDataSequenceNumber;
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_GRACEFULLY;
|
||
}
|
||
|
||
if (BytesTaken == DataBytes)
|
||
{
|
||
if (pLastNak)
|
||
{
|
||
if ((PacketType == PACKET_TYPE_RDATA) &&
|
||
(pLastNak->FirstNcfTickCount))
|
||
{
|
||
AdjustNcfRDataResponseTimes (pReceive, pLastNak);
|
||
}
|
||
|
||
ASSERT (!IsListEmpty (&pLastNak->PendingLinkage));
|
||
RemoveEntryList (&pLastNak->PendingLinkage);
|
||
InitializeListHead (&pLastNak->PendingLinkage);
|
||
|
||
RemoveEntryList (&pLastNak->Linkage);
|
||
FreeNakContext (pReceive, pLastNak);
|
||
}
|
||
else
|
||
{
|
||
pReceive->pReceiver->FurthestKnownGroupSequenceNumber++;
|
||
}
|
||
|
||
pReceive->pReceiver->NextODataSequenceNumber++;
|
||
pReceive->pReceiver->FirstNakSequenceNumber = pReceive->pReceiver->NextODataSequenceNumber;
|
||
if (pLastNak)
|
||
{
|
||
//
|
||
// Now, move any Naks contexts for which the group is complete
|
||
// to the BufferedDataList
|
||
//
|
||
AdjustReceiveBufferLists (pReceive);
|
||
}
|
||
|
||
return (status);
|
||
}
|
||
}
|
||
|
||
//
|
||
// First, ensure we have a Nak context available for this data
|
||
//
|
||
status = CheckAndAddNakRequests (pReceive, &ThisDataSequenceNumber, &pLastNak, NAK_PENDING_RB);
|
||
if ((!NT_SUCCESS (status)) ||
|
||
(!pLastNak))
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "PgmHandleNewData",
|
||
"CheckAndAddNakRequests for <%d> returned <%x>, pLastNak=<%p>\n",
|
||
ThisDataSequenceNumber, status, pLastNak);
|
||
|
||
if (NT_SUCCESS (status))
|
||
{
|
||
pReceive->pReceiver->NumDupPacketsBuffered++;
|
||
}
|
||
else
|
||
{
|
||
pReceive->pReceiver->NumDataPacketsDropped++;
|
||
}
|
||
return (status);
|
||
}
|
||
|
||
//
|
||
// If this group has a different GroupSize, set that now
|
||
//
|
||
if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_CUR_TGSIZE)
|
||
{
|
||
if (!(PacketOptions.FECContext.NumPacketsInThisGroup) ||
|
||
(pReceive->FECOptions &&
|
||
(PacketOptions.FECContext.NumPacketsInThisGroup >= pReceive->FECGroupSize)))
|
||
{
|
||
//
|
||
// Bad Packet!
|
||
//
|
||
ASSERT (0);
|
||
status = STATUS_DATA_NOT_ACCEPTED;
|
||
}
|
||
else if (pLastNak->OriginalGroupSize == 1)
|
||
{
|
||
//
|
||
// This path will be used if we have not yet received
|
||
// an SPM (so don't know group size, etc), but have a
|
||
// data packet from a partial group
|
||
//
|
||
pLastNak->ThisGroupSize = PacketOptions.FECContext.NumPacketsInThisGroup;
|
||
}
|
||
//
|
||
// If we have already received all the data packets, don't do anything here
|
||
//
|
||
else if (pLastNak->PacketsInGroup == pReceive->FECGroupSize)
|
||
{
|
||
pLastNak->PacketsInGroup = PacketOptions.FECContext.NumPacketsInThisGroup;
|
||
//
|
||
// Get rid of any of the excess (NULL) data packets
|
||
//
|
||
RemoveRedundantNaks (pLastNak, TRUE);
|
||
}
|
||
else if (pLastNak->PacketsInGroup != PacketOptions.FECContext.NumPacketsInThisGroup)
|
||
{
|
||
ASSERT (0);
|
||
status = STATUS_DATA_NOT_ACCEPTED;
|
||
}
|
||
}
|
||
|
||
if (status == STATUS_DATA_NOT_ACCEPTED)
|
||
{
|
||
pReceive->pReceiver->NumDataPacketsDropped++;
|
||
return (status);
|
||
}
|
||
|
||
//
|
||
//
|
||
// See if we even need this packet!
|
||
//
|
||
if (fIsParityPacket)
|
||
{
|
||
//
|
||
// Do not handle parity packets if we are not aware of FEC,
|
||
// or it is a partial group size = 1 packet
|
||
//
|
||
if ((pLastNak->PacketsInGroup == 1) || // Do not handle parity packets if we are not aware of FEC
|
||
((pLastNak->NumDataPackets+pLastNak->NumParityPackets) >= pLastNak->PacketsInGroup) ||
|
||
((pLastNak->NextIndexToIndicate + pLastNak->NumDataPackets) >= pLastNak->PacketsInGroup))
|
||
{
|
||
pReceive->pReceiver->NumDupPacketsBuffered++;
|
||
status = STATUS_DATA_NOT_ACCEPTED;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// Determine the ParityPacket Index
|
||
//
|
||
PacketIndex = (UCHAR) (ThisDataSequenceNumber & (pReceive->FECGroupSize-1));
|
||
if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_GRP)
|
||
{
|
||
ASSERT (((pOData->CommonHeader.Type & 0x0f) == PACKET_TYPE_RDATA) ||
|
||
((pOData->CommonHeader.Type & 0x0f) == PACKET_TYPE_ODATA));
|
||
ASSERT (PacketOptions.FECContext.FECGroupInfo);
|
||
PacketIndex += ((USHORT) PacketOptions.FECContext.FECGroupInfo * pReceive->FECGroupSize);
|
||
}
|
||
}
|
||
}
|
||
else // This is a non-parity packet
|
||
{
|
||
PacketIndex = (UCHAR) (ThisDataSequenceNumber & (pReceive->FECGroupSize-1));
|
||
|
||
if ((PacketIndex >= pLastNak->PacketsInGroup) ||
|
||
(PacketIndex < pLastNak->NextIndexToIndicate))
|
||
{
|
||
//
|
||
// We don't need this Packet!
|
||
//
|
||
pReceive->pReceiver->NumDupPacketsBuffered++;
|
||
status = STATUS_DATA_NOT_ACCEPTED;
|
||
}
|
||
}
|
||
|
||
if (status != STATUS_DATA_NOT_ACCEPTED)
|
||
{
|
||
//
|
||
// Verify that this is not a duplicate of a packet we
|
||
// may have already received
|
||
//
|
||
for (i=0; i < (pLastNak->NumDataPackets+pLastNak->NumParityPackets); i++)
|
||
{
|
||
if (pLastNak->pPendingData[i].PacketIndex == PacketIndex)
|
||
{
|
||
ASSERT (!fIsParityPacket);
|
||
pReceive->pReceiver->NumDupPacketsBuffered++;
|
||
status = STATUS_DATA_NOT_ACCEPTED;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (status == STATUS_DATA_NOT_ACCEPTED)
|
||
{
|
||
AdjustReceiveBufferLists (pReceive); // In case this became a partial group
|
||
return (status);
|
||
}
|
||
|
||
#ifdef MAX_BUFF_DBG
|
||
{
|
||
if (pReceive->pReceiver->NumPacketGroupsPendingClient > MaxPacketGroupsPendingClient)
|
||
{
|
||
MaxPacketGroupsPendingClient = pReceive->pReceiver->NumPacketGroupsPendingClient;
|
||
}
|
||
if (pReceive->pReceiver->TotalDataPacketsBuffered >= MaxPacketsBuffered)
|
||
{
|
||
MaxPacketsBuffered = pReceive->pReceiver->TotalDataPacketsBuffered;
|
||
}
|
||
if (pReceive->pReceiver->DataPacketsPendingIndicate >= MaxPacketsPendingIndicate)
|
||
{
|
||
MaxPacketsPendingIndicate = pReceive->pReceiver->DataPacketsPendingIndicate;
|
||
}
|
||
if (pReceive->pReceiver->DataPacketsPendingNaks >= MaxPacketsPendingNaks)
|
||
{
|
||
MaxPacketsPendingNaks = pReceive->pReceiver->DataPacketsPendingNaks;
|
||
}
|
||
ASSERT (pReceive->pReceiver->TotalDataPacketsBuffered == (pReceive->pReceiver->DataPacketsPendingIndicate +
|
||
pReceive->pReceiver->DataPacketsPendingNaks));
|
||
}
|
||
#endif // MAX_BUFF_DBG
|
||
|
||
if (pReceive->pReceiver->TotalDataPacketsBuffered >= MAX_PACKETS_BUFFERED)
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmHandleNewData",
|
||
"[%d]: Excessive number of packets buffered=<%d> > <%d>, Aborting ...\n",
|
||
(ULONG) ThisDataSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->TotalDataPacketsBuffered,
|
||
(ULONG) MAX_PACKETS_BUFFERED);
|
||
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
return (STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
|
||
if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_FIN)
|
||
{
|
||
pReceive->pReceiver->FinDataSequenceNumber = pLastNak->SequenceNumber + (pLastNak->NumDataPackets - 1);
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_GRACEFULLY;
|
||
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "PgmHandleNewData",
|
||
"SeqNum=[%d]: Got a FIN!!!\n", (ULONG) pReceive->pReceiver->FinDataSequenceNumber);
|
||
}
|
||
|
||
if ((PacketType == PACKET_TYPE_RDATA) &&
|
||
(pLastNak->FirstNcfTickCount) &&
|
||
(((pLastNak->NumDataPackets + pLastNak->NumParityPackets) >= pLastNak->PacketsInGroup) ||
|
||
((pLastNak->NextIndexToIndicate + pLastNak->NumDataPackets) >= pLastNak->PacketsInGroup)))
|
||
{
|
||
AdjustNcfRDataResponseTimes (pReceive, pLastNak);
|
||
}
|
||
|
||
//
|
||
// First, check if we are a data packet
|
||
// (save unique data packets even if we have extra parity packets)
|
||
// This can help save CPU!
|
||
//
|
||
pDataBuffer = NULL;
|
||
NakIndex = pLastNak->NumDataPackets + pLastNak->NumParityPackets;
|
||
if (!fIsParityPacket)
|
||
{
|
||
ASSERT (PacketIndex < pReceive->FECGroupSize);
|
||
ASSERT (pLastNak->pPendingData[PacketIndex].ActualIndexOfDataPacket == pLastNak->OriginalGroupSize);
|
||
|
||
if ((PacketLength + sizeof (tPOST_PACKET_FEC_CONTEXT)) <= pLastNak->MinPacketLength)
|
||
{
|
||
pDataBuffer = PgmAllocMem (pLastNak->MinPacketLength, PGM_TAG('D'));
|
||
}
|
||
else
|
||
{
|
||
pDataBuffer = PgmAllocMem ((PacketLength+sizeof(tPOST_PACKET_FEC_CONTEXT)), PGM_TAG('D'));
|
||
}
|
||
|
||
if (!pDataBuffer)
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmHandleNewData",
|
||
"[%d]: STATUS_INSUFFICIENT_RESOURCES <%d> bytes, NumDataPackets=<%d>, Aborting ...\n",
|
||
(ULONG) ThisDataSequenceNumber, pLastNak->MinPacketLength,
|
||
(ULONG) pReceive->pReceiver->TotalDataPacketsBuffered);
|
||
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
return (STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
PgmCopyMemory (pDataBuffer, pOData, PacketLength);
|
||
|
||
//
|
||
// If we have some un-needed parity packets, we
|
||
// can free that memory now
|
||
//
|
||
if (NakIndex >= pLastNak->PacketsInGroup)
|
||
{
|
||
ASSERT (pLastNak->NumParityPackets);
|
||
for (i=0; i<pLastNak->PacketsInGroup; i++)
|
||
{
|
||
if (pLastNak->pPendingData[i].PacketIndex >= pLastNak->OriginalGroupSize)
|
||
{
|
||
PgmFreeMem (pLastNak->pPendingData[i].pDataPacket);
|
||
pLastNak->pPendingData[i].pDataPacket = NULL;
|
||
pLastNak->pPendingData[i].PacketLength = pLastNak->pPendingData[i].DataOffset = 0;
|
||
|
||
break;
|
||
}
|
||
}
|
||
ASSERT (i < pLastNak->PacketsInGroup);
|
||
pLastNak->NumParityPackets--;
|
||
NakIndex = i;
|
||
}
|
||
ASSERT (!pLastNak->pPendingData[NakIndex].pDataPacket);
|
||
pLastNak->pPendingData[NakIndex].pDataPacket = pDataBuffer;
|
||
|
||
pLastNak->pPendingData[NakIndex].PacketLength = PacketLength;
|
||
pLastNak->pPendingData[NakIndex].DataOffset = (USHORT) (DataOffset + BytesTaken);
|
||
|
||
pLastNak->pPendingData[NakIndex].PacketIndex = PacketIndex;
|
||
pLastNak->pPendingData[PacketIndex].ActualIndexOfDataPacket = NakIndex;
|
||
|
||
pLastNak->NumDataPackets++;
|
||
pReceive->DataBytes += PacketLength - (DataOffset + BytesTaken);
|
||
|
||
ASSERT (!(PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_GRP));
|
||
|
||
//
|
||
// Save some options for future reference
|
||
//
|
||
if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_FRAGMENT)
|
||
{
|
||
pLastNak->pPendingData[NakIndex].FragmentOptSpecific = 0;
|
||
pLastNak->pPendingData[NakIndex].MessageFirstSequence = PacketOptions.MessageFirstSequence;
|
||
pLastNak->pPendingData[NakIndex].MessageLength = PacketOptions.MessageLength;
|
||
pLastNak->pPendingData[NakIndex].MessageOffset = PacketOptions.MessageOffset + BytesTaken;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// This is not a fragment
|
||
//
|
||
pLastNak->pPendingData[NakIndex].FragmentOptSpecific = PACKET_OPTION_SPECIFIC_ENCODED_NULL_BIT;
|
||
|
||
pLastNak->pPendingData[NakIndex].MessageFirstSequence = (ULONG) (SEQ_TYPE) (pLastNak->SequenceNumber + PacketIndex);
|
||
pLastNak->pPendingData[NakIndex].MessageOffset = BytesTaken;
|
||
pLastNak->pPendingData[NakIndex].MessageLength = PacketLength - DataOffset;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
ASSERT (PacketIndex >= pLastNak->OriginalGroupSize);
|
||
ASSERT (NakIndex < pLastNak->PacketsInGroup);
|
||
ASSERT (!pLastNak->pPendingData[NakIndex].pDataPacket);
|
||
|
||
pDataBuffer = PgmAllocMem ((PacketLength+sizeof(tPOST_PACKET_FEC_CONTEXT)-sizeof(USHORT)), PGM_TAG('P'));
|
||
if (!pDataBuffer)
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmHandleNewData",
|
||
"[%d -- Parity]: STATUS_INSUFFICIENT_RESOURCES <%d> bytes, NumDataPackets=<%d>, Aborting ...\n",
|
||
(ULONG) ThisDataSequenceNumber, PacketLength,
|
||
(ULONG) pReceive->pReceiver->TotalDataPacketsBuffered);
|
||
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
return (STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
pLastNak->pPendingData[NakIndex].pDataPacket = pDataBuffer;
|
||
|
||
//
|
||
// This is a new parity packet
|
||
//
|
||
PgmCopyMemory (pDataBuffer, pOData, PacketLength);
|
||
pLastNak->pPendingData[NakIndex].PacketIndex = PacketIndex;
|
||
pLastNak->pPendingData[NakIndex].PacketLength = PacketLength;
|
||
pLastNak->pPendingData[NakIndex].DataOffset = (USHORT) DataOffset;
|
||
|
||
pLastNak->pPendingData[NakIndex].FragmentOptSpecific = PacketOptions.FECContext.FragmentOptSpecific;
|
||
pLastNak->pPendingData[NakIndex].MessageFirstSequence = PacketOptions.MessageFirstSequence;
|
||
pLastNak->pPendingData[NakIndex].MessageLength = PacketOptions.MessageLength;
|
||
pLastNak->pPendingData[NakIndex].MessageOffset = PacketOptions.MessageOffset + BytesTaken;
|
||
|
||
pLastNak->NumParityPackets++;
|
||
pReceive->DataBytes += PacketLength - DataOffset;
|
||
|
||
if (!pLastNak->ParityDataSize)
|
||
{
|
||
pLastNak->ParityDataSize = (USHORT) (PacketLength - DataOffset);
|
||
}
|
||
else
|
||
{
|
||
ASSERT (pLastNak->ParityDataSize == (USHORT) (PacketLength - DataOffset));
|
||
}
|
||
}
|
||
|
||
pLastNak->AllOptionsFlags |= PacketOptions.OptionsFlags;
|
||
|
||
pReceive->pReceiver->TotalDataPacketsBuffered++;
|
||
pReceive->pReceiver->DataPacketsPendingNaks++;
|
||
|
||
//
|
||
// See if this group is complete
|
||
//
|
||
if (((pLastNak->NumDataPackets + pLastNak->NumParityPackets) >= pLastNak->PacketsInGroup) ||
|
||
((pLastNak->NextIndexToIndicate + pLastNak->NumDataPackets) >= pLastNak->PacketsInGroup))
|
||
{
|
||
ASSERT (!IsListEmpty (&pLastNak->PendingLinkage));
|
||
|
||
RemoveEntryList (&pLastNak->PendingLinkage);
|
||
InitializeListHead (&pLastNak->PendingLinkage);
|
||
|
||
AdjustReceiveBufferLists (pReceive);
|
||
}
|
||
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "PgmHandleNewData",
|
||
"SeqNum=[%d]: NumOutOfOrder=<%d> ...\n",
|
||
(ULONG) ThisDataSequenceNumber, pReceive->pReceiver->TotalDataPacketsBuffered);
|
||
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
ProcessDataPacket(
|
||
IN tADDRESS_CONTEXT *pAddress,
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN INT SourceAddressLength,
|
||
IN PVOID pSourceAddress,
|
||
IN ULONG PacketLength,
|
||
IN tBASIC_DATA_PACKET_HEADER UNALIGNED *pODataBuffer,
|
||
IN UCHAR PacketType
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine looks at the data packet received from the wire
|
||
and handles it appropriately depending on whether it is in order
|
||
or not
|
||
|
||
Arguments:
|
||
|
||
IN pAddress -- Address object context
|
||
IN pReceive -- Receive context
|
||
IN SourceAddressLength -- Length of source address
|
||
IN pSourceAddress -- Address of remote host
|
||
IN PacketLength -- Length of packet received from the wire
|
||
IN pODataBuffer -- Data packet
|
||
IN PacketType -- Type of Pgm packet
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Final status of the call
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
SEQ_TYPE ThisPacketSequenceNumber;
|
||
SEQ_TYPE ThisTrailingEdge;
|
||
tNAK_FORWARD_DATA *pNextNak;
|
||
ULONG DisconnectFlag;
|
||
PGMLockHandle OldIrq, OldIrq1;
|
||
ULONG ulData;
|
||
|
||
if (PacketLength < sizeof(tBASIC_DATA_PACKET_HEADER))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessDataPacket",
|
||
"PacketLength=<%d> < tBASIC_DATA_PACKET_HEADER=<%d>\n",
|
||
PacketLength, sizeof(tBASIC_DATA_PACKET_HEADER));
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
|
||
PgmLock (pAddress, OldIrq);
|
||
PgmLock (pReceive, OldIrq1);
|
||
|
||
PgmCopyMemory (&ulData, &pODataBuffer->DataSequenceNumber, sizeof(ULONG));
|
||
ThisPacketSequenceNumber = (SEQ_TYPE) ntohl (ulData);
|
||
|
||
PgmCopyMemory (&ulData, &pODataBuffer->TrailingEdgeSequenceNumber, sizeof(ULONG));
|
||
ThisTrailingEdge = (SEQ_TYPE) ntohl (ulData);
|
||
|
||
ASSERT (ntohl (ulData) == (ULONG) ThisTrailingEdge);
|
||
|
||
//
|
||
// Update our Window information (use offset from Leading edge to account for wrap-around)
|
||
//
|
||
if (SEQ_GT (ThisTrailingEdge, pReceive->pReceiver->LastTrailingEdgeSeqNum))
|
||
{
|
||
pReceive->pReceiver->LastTrailingEdgeSeqNum = ThisTrailingEdge;
|
||
}
|
||
|
||
//
|
||
// If the next packet we are expecting is out-of-range, then we
|
||
// should terminate the session
|
||
//
|
||
if (SEQ_LT (pReceive->pReceiver->FirstNakSequenceNumber, pReceive->pReceiver->LastTrailingEdgeSeqNum))
|
||
{
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
if (SEQ_GT (pReceive->pReceiver->LastTrailingEdgeSeqNum, (1 + pReceive->pReceiver->FurthestKnownGroupSequenceNumber)))
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "ProcessDataPacket",
|
||
"NETWORK problems -- data loss=<%d> packets > window size!\n\tExpecting=<%d>, FurthestKnown=<%d>, Trail=<%d>, Window=[%d--%d] =< %d > seqs\n",
|
||
(ULONG) (1 + ThisPacketSequenceNumber -
|
||
pReceive->pReceiver->FurthestKnownGroupSequenceNumber),
|
||
(ULONG) pReceive->pReceiver->FirstNakSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber,
|
||
(ULONG) ThisTrailingEdge, (ULONG) ThisPacketSequenceNumber,
|
||
(ULONG) (1+ThisPacketSequenceNumber-ThisTrailingEdge));
|
||
}
|
||
else
|
||
{
|
||
ASSERT (!IsListEmpty (&pReceive->pReceiver->NaksForwardDataList));
|
||
pNextNak = CONTAINING_RECORD (pReceive->pReceiver->NaksForwardDataList.Flink, tNAK_FORWARD_DATA, Linkage);
|
||
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "ProcessDataPacket",
|
||
"Session window has past TrailingEdge -- Expecting=<%d==%d>, NumNcfs=<%d>, FurthestKnown=<%d>, Window=[%d--%d] = < %d > seqs\n",
|
||
(ULONG) pReceive->pReceiver->FirstNakSequenceNumber,
|
||
(ULONG) pNextNak->SequenceNumber,
|
||
pNextNak->WaitingRDataRetries,
|
||
(ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->LastTrailingEdgeSeqNum,
|
||
(ULONG) ThisTrailingEdge, (ULONG) ThisPacketSequenceNumber,
|
||
(ULONG) (1+ThisPacketSequenceNumber-ThisTrailingEdge));
|
||
}
|
||
}
|
||
else if (SEQ_GT (pReceive->pReceiver->FirstNakSequenceNumber, ThisPacketSequenceNumber))
|
||
{
|
||
//
|
||
// Drop this packet since it is earlier than our window
|
||
//
|
||
pReceive->pReceiver->NumDupPacketsOlderThanWindow++;
|
||
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessDataPacket",
|
||
"Dropping this packet, SeqNum=[%d] < NextOData=[%d]\n",
|
||
(ULONG) ThisPacketSequenceNumber, (ULONG) pReceive->pReceiver->FirstNakSequenceNumber);
|
||
}
|
||
else
|
||
{
|
||
if (PacketType == PACKET_TYPE_ODATA)
|
||
{
|
||
UpdateRealTimeWindowInformation (pReceive, ThisPacketSequenceNumber, ThisTrailingEdge);
|
||
}
|
||
|
||
status = PgmHandleNewData (&ThisPacketSequenceNumber,
|
||
pAddress,
|
||
pReceive,
|
||
(USHORT) PacketLength,
|
||
pODataBuffer,
|
||
PacketType,
|
||
&OldIrq,
|
||
&OldIrq1);
|
||
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessDataPacket",
|
||
"PgmHandleNewData returned <%x>, SeqNum=[%d] < NextOData=[%d]\n",
|
||
status, (ULONG) ThisPacketSequenceNumber, (ULONG) pReceive->pReceiver->NextODataSequenceNumber);
|
||
|
||
//
|
||
// Now, try to indicate any data which may still be pending
|
||
//
|
||
status = CheckIndicatePendedData (pAddress, pReceive, &OldIrq, &OldIrq1);
|
||
}
|
||
|
||
CheckIndicateDisconnect (pAddress, pReceive, &OldIrq, &OldIrq1, TRUE);
|
||
|
||
PgmUnlock (pReceive, OldIrq1);
|
||
PgmUnlock (pAddress, OldIrq);
|
||
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
ProcessSpmPacket(
|
||
IN tADDRESS_CONTEXT *pAddress,
|
||
IN tRECEIVE_SESSION *pReceive,
|
||
IN ULONG PacketLength,
|
||
IN tBASIC_SPM_PACKET_HEADER UNALIGNED *pSpmPacket
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes Spm packets
|
||
|
||
Arguments:
|
||
|
||
IN pAddress -- Address object context
|
||
IN pReceive -- Receive context
|
||
IN PacketLength -- Length of packet received from the wire
|
||
IN pSpmPacket -- Spm packet
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Final status of the call
|
||
|
||
--*/
|
||
{
|
||
SEQ_TYPE SpmSequenceNumber, LeadingEdgeSeqNumber, TrailingEdgeSeqNumber;
|
||
LIST_ENTRY *pEntry;
|
||
ULONG DisconnectFlag;
|
||
NTSTATUS status;
|
||
PGMLockHandle OldIrq, OldIrq1;
|
||
tPACKET_OPTIONS PacketOptions;
|
||
PNAK_FORWARD_DATA pNak;
|
||
USHORT TSDULength;
|
||
tNLA PathNLA;
|
||
BOOLEAN fFirstSpm;
|
||
ULONG ulData;
|
||
|
||
ASSERT (PacketLength >= sizeof(tBASIC_SPM_PACKET_HEADER));
|
||
|
||
//
|
||
// First process the options
|
||
//
|
||
PgmZeroMemory (&PacketOptions, sizeof (tPACKET_OPTIONS));
|
||
if (pSpmPacket->CommonHeader.Options & PACKET_HEADER_OPTIONS_PRESENT)
|
||
{
|
||
status = ProcessOptions ((tPACKET_OPTION_LENGTH *) (pSpmPacket + 1),
|
||
(PacketLength - sizeof(tBASIC_SPM_PACKET_HEADER)),
|
||
(pSpmPacket->CommonHeader.Type & 0x0f),
|
||
&PacketOptions,
|
||
NULL);
|
||
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"ProcessOptions returned <%x>\n", status);
|
||
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
}
|
||
ASSERT ((PacketOptions.OptionsFlags & ~PGM_VALID_SPM_OPTION_FLAGS) == 0);
|
||
|
||
PgmCopyMemory (&PathNLA, &pSpmPacket->PathNLA, sizeof (tNLA));
|
||
PgmCopyMemory (&TSDULength, &pSpmPacket->CommonHeader.TSDULength, sizeof (USHORT));
|
||
TSDULength = ntohs (TSDULength);
|
||
ASSERT (!TSDULength);
|
||
ASSERT (PathNLA.IpAddress);
|
||
|
||
PgmCopyMemory (&ulData, &pSpmPacket->SpmSequenceNumber, sizeof (ULONG));
|
||
SpmSequenceNumber = (SEQ_TYPE) ntohl (ulData);
|
||
PgmCopyMemory (&ulData, &pSpmPacket->LeadingEdgeSeqNumber, sizeof (ULONG));
|
||
LeadingEdgeSeqNumber = (SEQ_TYPE) ntohl (ulData);
|
||
PgmCopyMemory (&ulData, &pSpmPacket->TrailingEdgeSeqNumber, sizeof (ULONG));
|
||
TrailingEdgeSeqNumber = (SEQ_TYPE) ntohl (ulData);
|
||
|
||
//
|
||
// Verify Packet length
|
||
//
|
||
if ((sizeof(tBASIC_SPM_PACKET_HEADER) + PacketOptions.OptionsLength) != PacketLength)
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"Bad PacketLength=<%d>, OptionsLength=<%d>, TSDULength=<%d>\n",
|
||
PacketLength, PacketOptions.OptionsLength, (ULONG) TSDULength);
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
|
||
PgmLock (pAddress, OldIrq);
|
||
|
||
if (!pReceive)
|
||
{
|
||
//
|
||
// Since we do not have a live connection yet, we will
|
||
// have to store some state in the Address context
|
||
//
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"[%d] Received SPM before OData for session, LastSpmSource=<%x>, FEC %sabled, Window=[%d - %d]\n",
|
||
SpmSequenceNumber, PathNLA.IpAddress,
|
||
(PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_PRM ? "EN" : "DIS"),
|
||
(ULONG) TrailingEdgeSeqNumber, (ULONG) LeadingEdgeSeqNumber);
|
||
|
||
if ((ntohs (PathNLA.NLA_AFI) == IPV4_NLA_AFI) &&
|
||
(PathNLA.IpAddress))
|
||
{
|
||
pAddress->LastSpmSource = ntohl (PathNLA.IpAddress);
|
||
}
|
||
|
||
//
|
||
// Check if the sender is FEC-enabled
|
||
//
|
||
if ((PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_PRM) &&
|
||
(PacketOptions.FECContext.ReceiverFECOptions) &&
|
||
(PacketOptions.FECContext.FECGroupInfo > 1))
|
||
{
|
||
pAddress->FECOptions = PacketOptions.FECContext.ReceiverFECOptions;
|
||
pAddress->FECGroupSize = (UCHAR) PacketOptions.FECContext.FECGroupInfo;
|
||
ASSERT (PacketOptions.FECContext.FECGroupInfo == pAddress->FECGroupSize);
|
||
}
|
||
|
||
PgmUnlock (pAddress, OldIrq);
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
PgmLock (pReceive, OldIrq1);
|
||
UpdateSpmIntervalInformation (pReceive);
|
||
|
||
//
|
||
// If this is not the first SPM packet (LastSpmSource is not NULL), see if it is out-of-sequence,
|
||
// otherwise take this as the first packet
|
||
//
|
||
if ((pReceive->pReceiver->LastSpmSource) &&
|
||
(SEQ_LEQ (SpmSequenceNumber, pReceive->pReceiver->LastSpmSequenceNumber)))
|
||
{
|
||
PgmUnlock (pReceive, OldIrq1);
|
||
PgmUnlock (pAddress, OldIrq);
|
||
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"Out-of-sequence SPM Packet received!\n");
|
||
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
pReceive->pReceiver->LastSpmSequenceNumber = SpmSequenceNumber;
|
||
|
||
//
|
||
// Save the last Sender NLA
|
||
//
|
||
if ((ntohs(PathNLA.NLA_AFI) == IPV4_NLA_AFI) &&
|
||
(PathNLA.IpAddress))
|
||
{
|
||
pReceive->pReceiver->LastSpmSource = ntohl (PathNLA.IpAddress);
|
||
}
|
||
else
|
||
{
|
||
pReceive->pReceiver->LastSpmSource = pReceive->pReceiver->SenderIpAddress;
|
||
}
|
||
|
||
UpdateRealTimeWindowInformation (pReceive, LeadingEdgeSeqNumber, TrailingEdgeSeqNumber);
|
||
|
||
//
|
||
// Update the trailing edge if this is more ahead
|
||
//
|
||
if (SEQ_GT (TrailingEdgeSeqNumber, pReceive->pReceiver->LastTrailingEdgeSeqNum))
|
||
{
|
||
pReceive->pReceiver->LastTrailingEdgeSeqNum = TrailingEdgeSeqNumber;
|
||
}
|
||
|
||
if (SEQ_GT (pReceive->pReceiver->LastTrailingEdgeSeqNum, pReceive->pReceiver->FirstNakSequenceNumber))
|
||
{
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
if (SEQ_GT (pReceive->pReceiver->LastTrailingEdgeSeqNum, (1 + pReceive->pReceiver->FurthestKnownGroupSequenceNumber)))
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"NETWORK problems -- data loss=<%d> packets > window size!\n\tExpecting=<%d>, FurthestKnown=<%d>, Window=[%d--%d] = < %d > seqs\n",
|
||
(ULONG) (1 + LeadingEdgeSeqNumber -
|
||
pReceive->pReceiver->FurthestKnownGroupSequenceNumber),
|
||
(ULONG) pReceive->pReceiver->FirstNakSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->LastTrailingEdgeSeqNum, LeadingEdgeSeqNumber,
|
||
(ULONG) (1+LeadingEdgeSeqNumber-pReceive->pReceiver->LastTrailingEdgeSeqNum));
|
||
}
|
||
else
|
||
{
|
||
ASSERT (!IsListEmpty (&pReceive->pReceiver->NaksForwardDataList));
|
||
pNak = CONTAINING_RECORD (pReceive->pReceiver->NaksForwardDataList.Flink, tNAK_FORWARD_DATA, Linkage);
|
||
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"Session window has past TrailingEdge -- Expecting <%d==%d>, NumNcfs=<%d>, FurthestKnown=<%d>, Window=[%d--%d] = < %d > seqs\n",
|
||
(ULONG) pReceive->pReceiver->FirstNakSequenceNumber,
|
||
(ULONG) pNak->SequenceNumber,
|
||
pNak->WaitingRDataRetries,
|
||
(ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->LastTrailingEdgeSeqNum, LeadingEdgeSeqNumber,
|
||
(ULONG) (1+LeadingEdgeSeqNumber-pReceive->pReceiver->LastTrailingEdgeSeqNum));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now, process all the options
|
||
//
|
||
if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_RST_N)
|
||
{
|
||
pReceive->pReceiver->FinDataSequenceNumber = pReceive->pReceiver->FurthestKnownGroupSequenceNumber;
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"Got an RST_N! FinSeq=<%d>, NextODataSeq=<%d>, FurthestData=<%d>\n",
|
||
(ULONG) pReceive->pReceiver->FinDataSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->NextODataSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber);
|
||
}
|
||
else if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_RST)
|
||
{
|
||
pReceive->pReceiver->FinDataSequenceNumber = LeadingEdgeSeqNumber;
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"Got an RST! FinSeq=<%d>, NextODataSeq=<%d>, FurthestData=<%d>\n",
|
||
(ULONG) pReceive->pReceiver->FinDataSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->NextODataSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber);
|
||
}
|
||
else if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_FIN)
|
||
{
|
||
pReceive->pReceiver->FinDataSequenceNumber = LeadingEdgeSeqNumber;
|
||
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_GRACEFULLY;
|
||
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"Got a FIN! FinSeq=<%d>, NextODataSeq=<%d>, FurthestData=<%d>\n",
|
||
(ULONG) pReceive->pReceiver->FinDataSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->NextODataSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber);
|
||
}
|
||
|
||
//
|
||
// See if we need to abort
|
||
//
|
||
if (CheckIndicateDisconnect (pAddress, pReceive, &OldIrq, &OldIrq1, TRUE))
|
||
{
|
||
PgmUnlock (pReceive, OldIrq1);
|
||
PgmUnlock (pAddress, OldIrq);
|
||
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
//
|
||
// If the Leading edge is > our current leading edge, then
|
||
// we need to send NAKs for the missing data Packets
|
||
//
|
||
status = CheckAndAddNakRequests (pReceive, &LeadingEdgeSeqNumber, NULL, NAK_PENDING_RB);
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
PgmUnlock (pReceive, OldIrq1);
|
||
PgmUnlock (pAddress, OldIrq);
|
||
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"CheckAndAddNakRequests returned <%x>\n", status);
|
||
|
||
return (status);
|
||
}
|
||
|
||
//
|
||
// Check if the sender is FEC-enabled
|
||
//
|
||
if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_PRM)
|
||
{
|
||
if ((pReceive->FECGroupSize == 1) &&
|
||
(PacketOptions.FECContext.ReceiverFECOptions) &&
|
||
(PacketOptions.FECContext.FECGroupInfo > 1))
|
||
{
|
||
ASSERT (!pReceive->pFECBuffer);
|
||
|
||
if (!(pReceive->pFECBuffer = PgmAllocMem ((pReceive->MaxFECPacketLength * PacketOptions.FECContext.FECGroupInfo*2), PGM_TAG('3'))))
|
||
{
|
||
status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"STATUS_INSUFFICIENT_RESOURCES -- MaxFECPacketLength = <%d>, GroupSize=<%d>\n",
|
||
pReceive->MaxFECPacketLength, PacketOptions.FECContext.FECGroupInfo);
|
||
|
||
}
|
||
else if (!NT_SUCCESS (status = CreateFECContext (&pReceive->FECContext, PacketOptions.FECContext.FECGroupInfo, FEC_MAX_BLOCK_SIZE, TRUE)))
|
||
{
|
||
PgmFreeMem (pReceive->pFECBuffer);
|
||
pReceive->pFECBuffer = NULL;
|
||
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"CreateFECContext returned <%x>\n", status);
|
||
}
|
||
else if (!NT_SUCCESS (status = CoalesceSelectiveNaksIntoGroups (pReceive, (UCHAR) PacketOptions.FECContext.FECGroupInfo)))
|
||
{
|
||
DestroyFECContext (&pReceive->FECContext);
|
||
|
||
PgmFreeMem (pReceive->pFECBuffer);
|
||
pReceive->pFECBuffer = NULL;
|
||
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"CoalesceSelectiveNaksIntoGroups returned <%x>\n", status);
|
||
}
|
||
else
|
||
{
|
||
pReceive->FECOptions = PacketOptions.FECContext.ReceiverFECOptions;
|
||
pReceive->FECGroupSize = (UCHAR) PacketOptions.FECContext.FECGroupInfo;
|
||
if (pReceive->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT)
|
||
{
|
||
pReceive->pReceiver->SessionNakType = NAK_TYPE_PARITY;
|
||
}
|
||
ASSERT (PacketOptions.FECContext.FECGroupInfo == pReceive->FECGroupSize);
|
||
}
|
||
|
||
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
PgmUnlock (pReceive, OldIrq1);
|
||
PgmUnlock (pAddress, OldIrq);
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
|
||
fFirstSpm = TRUE;
|
||
}
|
||
else
|
||
{
|
||
fFirstSpm = FALSE;
|
||
}
|
||
|
||
if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_CUR_TGSIZE)
|
||
{
|
||
//
|
||
// The Leading edge Packet belongs to a Variable sized group
|
||
// so set that information appropriately
|
||
// Determine the group to which this leading edge belongs to
|
||
//
|
||
LeadingEdgeSeqNumber &= ~((SEQ_TYPE) (pReceive->FECGroupSize-1));
|
||
|
||
if ((PacketOptions.FECContext.NumPacketsInThisGroup) &&
|
||
(PacketOptions.FECContext.NumPacketsInThisGroup < pReceive->FECGroupSize) &&
|
||
SEQ_GEQ (LeadingEdgeSeqNumber, pReceive->pReceiver->FirstNakSequenceNumber))
|
||
{
|
||
//
|
||
// We will proceed backwards from the end since we have a higher
|
||
// probability of finding the leading edge group near the end!
|
||
//
|
||
pEntry = &pReceive->pReceiver->PendingNaksList;
|
||
while ((pEntry = pEntry->Blink) != &pReceive->pReceiver->PendingNaksList)
|
||
{
|
||
pNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, PendingLinkage);
|
||
if (SEQ_GT (pNak->SequenceNumber, LeadingEdgeSeqNumber))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if ((pNak->SequenceNumber == LeadingEdgeSeqNumber) &&
|
||
(pNak->PacketsInGroup == pReceive->FECGroupSize))
|
||
{
|
||
//
|
||
// We have already coalesced the list, so the packets should
|
||
// be ordered into groups!
|
||
//
|
||
pNak->PacketsInGroup = PacketOptions.FECContext.NumPacketsInThisGroup;
|
||
RemoveRedundantNaks (pNak, TRUE);
|
||
}
|
||
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"WARNING .. PARITY_CUR_TGSIZE ThisGroupSize=<%x>, FECGroupSize=<%x>\n",
|
||
PacketOptions.FECContext.NumPacketsInThisGroup, pReceive->FECGroupSize);
|
||
}
|
||
}
|
||
|
||
if (fFirstSpm)
|
||
{
|
||
status = CheckIndicatePendedData (pAddress, pReceive, &OldIrq, &OldIrq1);
|
||
}
|
||
}
|
||
|
||
PgmUnlock (pReceive, OldIrq1);
|
||
PgmUnlock (pAddress, OldIrq);
|
||
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "ProcessSpmPacket",
|
||
"NextOData=<%d>, FinDataSeq=<%d> \n",
|
||
(ULONG) pReceive->pReceiver->NextODataSequenceNumber,
|
||
(ULONG) pReceive->pReceiver->FinDataSequenceNumber);
|
||
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
PgmProcessIncomingPacket(
|
||
IN tADDRESS_CONTEXT *pAddress,
|
||
IN tCOMMON_SESSION_CONTEXT *pSession,
|
||
IN INT SourceAddressLength,
|
||
IN PVOID pSourceAddress,
|
||
IN ULONG PacketLength,
|
||
IN tCOMMON_HEADER UNALIGNED *pPgmHeader,
|
||
IN UCHAR PacketType
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine process an incoming packet and calls the
|
||
appropriate handler depending on whether is a data packet
|
||
packet, etc.
|
||
|
||
Arguments:
|
||
|
||
IN pAddress -- Address object context
|
||
IN pReceive -- Receive context
|
||
IN SourceAddressLength -- Length of source address
|
||
IN pSourceAddress -- Address of remote host
|
||
IN PacketLength -- Length of packet received from the wire
|
||
IN pPgmHeader -- Pgm packet
|
||
IN PacketType -- Type of Pgm packet
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Final status of the call
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
tIPADDRESS SrcIpAddress;
|
||
TA_IP_ADDRESS *pRemoteAddress = (PTA_IP_ADDRESS) pSourceAddress;
|
||
|
||
//
|
||
// We have an active connection for this TSI, so process the data appropriately
|
||
//
|
||
switch (PacketType)
|
||
{
|
||
case (PACKET_TYPE_SPM):
|
||
{
|
||
if (PGM_VERIFY_HANDLE (pSession, PGM_VERIFY_SESSION_RECEIVE))
|
||
{
|
||
pSession->TotalBytes += PacketLength;
|
||
pSession->TotalPacketsInLastInterval++;
|
||
pSession->pReceiver->LastSessionTickCount = PgmDynamicConfig.ReceiversTimerTickCount;
|
||
|
||
status = ProcessSpmPacket (pAddress,
|
||
pSession,
|
||
PacketLength,
|
||
(tBASIC_SPM_PACKET_HEADER UNALIGNED *) pPgmHeader);
|
||
}
|
||
else
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmProcessIncomingPacket",
|
||
"Received SPM packet, not on Receiver session! pSession=<%p>\n", pSession);
|
||
status = STATUS_DATA_NOT_ACCEPTED;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case (PACKET_TYPE_ODATA):
|
||
case (PACKET_TYPE_RDATA):
|
||
{
|
||
if (PGM_VERIFY_HANDLE (pSession, PGM_VERIFY_SESSION_RECEIVE))
|
||
{
|
||
if (PacketType == PACKET_TYPE_ODATA)
|
||
{
|
||
pSession->pReceiver->NumODataPacketsReceived++;
|
||
pSession->pReceiver->LastSessionTickCount = PgmDynamicConfig.ReceiversTimerTickCount;
|
||
}
|
||
else
|
||
{
|
||
pSession->pReceiver->NumRDataPacketsReceived++;
|
||
}
|
||
pSession->TotalBytes += PacketLength;
|
||
pSession->TotalPacketsInLastInterval++;
|
||
status = ProcessDataPacket (pAddress,
|
||
pSession,
|
||
SourceAddressLength,
|
||
pSourceAddress,
|
||
PacketLength,
|
||
(tBASIC_DATA_PACKET_HEADER UNALIGNED *) pPgmHeader,
|
||
PacketType);
|
||
}
|
||
else
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmProcessIncomingPacket",
|
||
"Received Data packet, not on Receiver session! pSession=<%p>\n", pSession);
|
||
status = STATUS_DATA_NOT_ACCEPTED;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case (PACKET_TYPE_NCF):
|
||
{
|
||
if (PGM_VERIFY_HANDLE (pSession, PGM_VERIFY_SESSION_RECEIVE))
|
||
{
|
||
status = ReceiverProcessNakNcfPacket (pAddress,
|
||
pSession,
|
||
PacketLength,
|
||
(tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED *) pPgmHeader,
|
||
PacketType);
|
||
}
|
||
else
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmProcessIncomingPacket",
|
||
"Received Ncf packet, not on Receiver session! pSession=<%p>\n", pSession);
|
||
status = STATUS_DATA_NOT_ACCEPTED;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
case (PACKET_TYPE_NAK):
|
||
{
|
||
if (pSession->pSender)
|
||
{
|
||
ASSERT (!pSession->pReceiver);
|
||
status = SenderProcessNakPacket (pAddress,
|
||
pSession,
|
||
PacketLength,
|
||
(tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED *) pPgmHeader);
|
||
}
|
||
else
|
||
{
|
||
ASSERT (pSession->pReceiver);
|
||
|
||
//
|
||
// If the Nak was sent by us, then we can ignore it!
|
||
//
|
||
SrcIpAddress = ntohl (((PTDI_ADDRESS_IP) &pRemoteAddress->Address[0].Address)->in_addr);
|
||
if (!SrcIsUs (SrcIpAddress))
|
||
{
|
||
status = ReceiverProcessNakNcfPacket (pAddress,
|
||
pSession,
|
||
PacketLength,
|
||
(tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED*)pPgmHeader,
|
||
PacketType);
|
||
}
|
||
|
||
ASSERT (NT_SUCCESS (status));
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
default:
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmProcessIncomingPacket",
|
||
"Unknown PacketType=<%x>, PacketLength=<%d>\n", PacketType, PacketLength);
|
||
|
||
ASSERT (0);
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
}
|
||
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "PgmProcessIncomingPacket",
|
||
"PacketType=<%x> for pSession=<%p> PacketLength=<%d>, status=<%x>\n",
|
||
PacketType, pSession, PacketLength, status);
|
||
|
||
return (status);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
PgmNewInboundConnection(
|
||
IN tADDRESS_CONTEXT *pAddress,
|
||
IN INT SourceAddressLength,
|
||
IN PVOID pSourceAddress,
|
||
IN ULONG ReceiveDatagramFlags,
|
||
IN tBASIC_DATA_PACKET_HEADER UNALIGNED *pPgmHeader,
|
||
IN ULONG PacketLength,
|
||
OUT tRECEIVE_SESSION **ppReceive
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes a new incoming connection
|
||
|
||
Arguments:
|
||
|
||
IN pAddress -- Address object context
|
||
IN SourceAddressLength -- Length of source address
|
||
IN pSourceAddress -- Address of remote host
|
||
IN ReceiveDatagramFlags-- Flags set by the transport for this packet
|
||
IN pPgmHeader -- Pgm packet
|
||
IN PacketLength -- Length of packet received from the wire
|
||
OUT ppReceive -- pReceive context for this session returned by the client (if successful)
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Final status of the call
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
tRECEIVE_SESSION *pReceive;
|
||
CONNECTION_CONTEXT ConnectId;
|
||
PIO_STACK_LOCATION pIrpSp;
|
||
TA_IP_ADDRESS RemoteAddress;
|
||
INT RemoteAddressSize;
|
||
PTDI_IND_CONNECT evConnect = NULL;
|
||
PVOID ConEvContext = NULL;
|
||
PGMLockHandle OldIrq, OldIrq1, OldIrq2;
|
||
PIRP pIrp = NULL;
|
||
ULONG ulData;
|
||
USHORT PortNum;
|
||
SEQ_TYPE FirstODataSequenceNumber;
|
||
tPACKET_OPTIONS PacketOptions;
|
||
LARGE_INTEGER Frequency;
|
||
|
||
//
|
||
// We need to set the Next expected sequence number, so first see if
|
||
// there is a late joiner option
|
||
//
|
||
PgmZeroMemory (&PacketOptions, sizeof (tPACKET_OPTIONS));
|
||
if (pPgmHeader->CommonHeader.Options & PACKET_HEADER_OPTIONS_PRESENT)
|
||
{
|
||
status = ProcessOptions ((tPACKET_OPTION_LENGTH *) (pPgmHeader + 1),
|
||
(PacketLength - sizeof(tBASIC_DATA_PACKET_HEADER)),
|
||
(pPgmHeader->CommonHeader.Type & 0x0f),
|
||
&PacketOptions,
|
||
NULL);
|
||
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmNewInboundConnection",
|
||
"ProcessOptions returned <%x>\n", status);
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
ASSERT ((PacketOptions.OptionsFlags & ~PGM_VALID_DATA_OPTION_FLAGS) == 0);
|
||
}
|
||
|
||
PgmCopyMemory (&ulData, &pPgmHeader->DataSequenceNumber, sizeof (ULONG));
|
||
FirstODataSequenceNumber = (SEQ_TYPE) ntohl (ulData);
|
||
PgmLock (pAddress, OldIrq1);
|
||
//
|
||
// The Address is already referenced in the calling routine,
|
||
// so we don not need to reference it here again!
|
||
//
|
||
#if 0
|
||
if (!IsListEmpty(&pAddress->ListenHead))
|
||
{
|
||
//
|
||
// Ignore this for now since we have not encountered posted listens! (Is this an ISSUE ?)
|
||
}
|
||
#endif // 0
|
||
|
||
if (!(ConEvContext = pAddress->ConEvContext))
|
||
{
|
||
//
|
||
// Client has not yet posted a Listen!
|
||
// take all of the data so that a disconnect will not be held up
|
||
// by data still in the transport.
|
||
//
|
||
PgmUnlock (pAddress, OldIrq1);
|
||
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmNewInboundConnection",
|
||
"No Connect handler, pAddress=<%p>\n", pAddress);
|
||
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
|
||
RemoteAddressSize = offsetof (TA_IP_ADDRESS, Address[0].Address) + sizeof(TDI_ADDRESS_IP);
|
||
ASSERT (SourceAddressLength <= RemoteAddressSize);
|
||
PgmCopyMemory (&RemoteAddress, pSourceAddress, RemoteAddressSize);
|
||
PgmCopyMemory (&((PTDI_ADDRESS_IP) &RemoteAddress.Address[0].Address)->sin_port,
|
||
&pPgmHeader->CommonHeader.SrcPort, sizeof (USHORT));
|
||
RemoteAddress.TAAddressCount = 1;
|
||
evConnect = pAddress->evConnect;
|
||
|
||
PgmUnlock (pAddress, OldIrq1);
|
||
|
||
status = (*evConnect) (ConEvContext,
|
||
RemoteAddressSize,
|
||
&RemoteAddress,
|
||
0,
|
||
NULL,
|
||
0, // options length
|
||
NULL, // Options
|
||
&ConnectId,
|
||
&pIrp);
|
||
|
||
if ((status != STATUS_MORE_PROCESSING_REQUIRED) ||
|
||
(pIrp == NULL))
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "PgmNewInboundConnection",
|
||
"Client REJECTed incoming session: status=<%x>, pAddress=<%p>, evConn=<%p>\n",
|
||
status, pAddress, pAddress->evConnect);
|
||
|
||
*ppReceive = NULL;
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
|
||
PgmLock (&PgmDynamicConfig, OldIrq);
|
||
PgmLock (pAddress, OldIrq1);
|
||
|
||
//
|
||
// the pReceive ptr was stored in the FsContext value when the connection
|
||
// was initially created.
|
||
//
|
||
pIrpSp = IoGetCurrentIrpStackLocation (pIrp);
|
||
pReceive = (tRECEIVE_SESSION *) pIrpSp->FileObject->FsContext;
|
||
if ((!PGM_VERIFY_HANDLE (pReceive, PGM_VERIFY_SESSION_RECEIVE)) ||
|
||
(pReceive->pAssociatedAddress != pAddress))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmNewInboundConnection",
|
||
"Invalid Connection Handle=<%p>\n", pReceive);
|
||
|
||
PgmUnlock (pAddress, OldIrq1);
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq);
|
||
*ppReceive = NULL;
|
||
return (STATUS_INTERNAL_ERROR);
|
||
}
|
||
ASSERT (ConnectId == pReceive->ClientSessionContext);
|
||
|
||
PgmLock (pReceive, OldIrq2);
|
||
|
||
pReceive->pReceiver->SenderIpAddress = ntohl (((PTDI_ADDRESS_IP)&RemoteAddress.Address[0].Address)->in_addr);
|
||
pReceive->MaxMTULength = (USHORT) PgmDynamicConfig.MaxMTU;
|
||
pReceive->MaxFECPacketLength = pReceive->MaxMTULength +
|
||
sizeof (tPOST_PACKET_FEC_CONTEXT) - sizeof (USHORT);
|
||
ASSERT (!pReceive->pFECBuffer);
|
||
|
||
//
|
||
// If we had received an Spm earlier, then we may need to set
|
||
// some of the Spm-specific options
|
||
//
|
||
pReceive->FECGroupSize = 1; // Default to non-parity mode
|
||
pReceive->pReceiver->SessionNakType = NAK_TYPE_SELECTIVE;
|
||
if ((pAddress->LastSpmSource) ||
|
||
(pAddress->FECOptions))
|
||
{
|
||
if (pAddress->LastSpmSource)
|
||
{
|
||
pReceive->pReceiver->LastSpmSource = pAddress->LastSpmSource;
|
||
}
|
||
else
|
||
{
|
||
pReceive->pReceiver->LastSpmSource = pReceive->pReceiver->SenderIpAddress;
|
||
}
|
||
|
||
if (pAddress->FECOptions)
|
||
{
|
||
if (!(pReceive->pFECBuffer = PgmAllocMem ((pReceive->MaxFECPacketLength * pAddress->FECGroupSize * 2), PGM_TAG('3'))))
|
||
{
|
||
PgmUnlock (pReceive, OldIrq2);
|
||
PgmUnlock (pAddress, OldIrq1);
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq);
|
||
*ppReceive = NULL;
|
||
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmNewInboundConnection",
|
||
"STATUS_INSUFFICIENT_RESOURCES allocating pFECBuffer, %d bytes\n",
|
||
(pReceive->MaxFECPacketLength * pAddress->FECGroupSize * 2));
|
||
|
||
return (STATUS_INSUFFICIENT_RESOURCES);
|
||
}
|
||
else if (!NT_SUCCESS (status = CreateFECContext (&pReceive->FECContext, pAddress->FECGroupSize, FEC_MAX_BLOCK_SIZE, TRUE)))
|
||
{
|
||
PgmFreeMem (pReceive->pFECBuffer);
|
||
pReceive->pFECBuffer = NULL;
|
||
|
||
PgmUnlock (pReceive, OldIrq2);
|
||
PgmUnlock (pAddress, OldIrq1);
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq);
|
||
*ppReceive = NULL;
|
||
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "PgmNewInboundConnection",
|
||
"CreateFECContext returned <%x>\n", status);
|
||
|
||
return (status);
|
||
}
|
||
|
||
ASSERT (pAddress->FECGroupSize > 1);
|
||
pReceive->FECGroupSize = pAddress->FECGroupSize;
|
||
pReceive->FECOptions = pAddress->FECOptions;
|
||
if (pReceive->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT)
|
||
{
|
||
pReceive->pReceiver->SessionNakType = NAK_TYPE_PARITY;
|
||
}
|
||
|
||
ExInitializeNPagedLookasideList (&pReceive->pReceiver->ParityContextLookaside,
|
||
NULL,
|
||
NULL,
|
||
0,
|
||
(sizeof(tNAK_FORWARD_DATA) +
|
||
((pReceive->FECGroupSize-1) * sizeof(tPENDING_DATA))),
|
||
PGM_TAG('2'),
|
||
PARITY_CONTEXT_LOOKASIDE_DEPTH);
|
||
}
|
||
|
||
pAddress->LastSpmSource = pAddress->FECOptions = pAddress->FECGroupSize = 0;
|
||
}
|
||
|
||
//
|
||
// Initialize our Connect info
|
||
// Save the SourceId and Src port for this connection
|
||
//
|
||
PgmCopyMemory (pReceive->GSI, pPgmHeader->CommonHeader.gSourceId, SOURCE_ID_LENGTH);
|
||
PgmCopyMemory (&PortNum, &pPgmHeader->CommonHeader.SrcPort, sizeof (USHORT));
|
||
pReceive->TSIPort = ntohs (PortNum);
|
||
|
||
PGM_REFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_TDI_RCV_HANDLER, TRUE);
|
||
PGM_REFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_TIMER_RUNNING, TRUE);
|
||
|
||
PGM_REFERENCE_ADDRESS (pAddress, REF_ADDRESS_RECEIVE_ACTIVE, TRUE);
|
||
pReceive->SessionFlags |= (PGM_SESSION_ON_TIMER | PGM_SESSION_FLAG_FIRST_PACKET);
|
||
pReceive->pReceiver->pAddress = pAddress;
|
||
|
||
ExInitializeNPagedLookasideList (&pReceive->pReceiver->NonParityContextLookaside,
|
||
NULL,
|
||
NULL,
|
||
0,
|
||
sizeof (tNAK_FORWARD_DATA),
|
||
PGM_TAG ('2'),
|
||
NON_PARITY_CONTEXT_LOOKASIDE_DEPTH);
|
||
//
|
||
// Set the NextODataSequenceNumber and FurthestKnownGroupSequenceNumber based
|
||
// on this packet's Sequence # and the lateJoin option (if present)
|
||
// Make sure all of the Sequence numbers are on group boundaries (if not,
|
||
// set them at the start of the next group)
|
||
//
|
||
FirstODataSequenceNumber &= ~((SEQ_TYPE) pReceive->FECGroupSize - 1);
|
||
if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_JOIN)
|
||
{
|
||
PacketOptions.LateJoinerSequence += (pReceive->FECGroupSize - 1);
|
||
PacketOptions.LateJoinerSequence &= ~((SEQ_TYPE) pReceive->FECGroupSize - 1);
|
||
|
||
pReceive->pReceiver->NextODataSequenceNumber = (SEQ_TYPE) PacketOptions.LateJoinerSequence;
|
||
}
|
||
else
|
||
{
|
||
//
|
||
// There is no late joiner option
|
||
//
|
||
pReceive->pReceiver->NextODataSequenceNumber = FirstODataSequenceNumber;
|
||
}
|
||
pReceive->pReceiver->LastTrailingEdgeSeqNum = pReceive->pReceiver->FirstNakSequenceNumber =
|
||
pReceive->pReceiver->NextODataSequenceNumber;
|
||
pReceive->pReceiver->OutstandingNakTimeout = INITIAL_NAK_OUTSTANDING_TIMEOUT_MSECS/BASIC_TIMER_GRANULARITY_IN_MSECS;
|
||
pReceive->pReceiver->MaxOutstandingNakTimeout = pReceive->pReceiver->OutstandingNakTimeout;
|
||
|
||
//
|
||
// Set the FurthestKnown Sequence # and Allocate Nak contexts
|
||
//
|
||
pReceive->pReceiver->FurthestKnownGroupSequenceNumber = (pReceive->pReceiver->NextODataSequenceNumber-
|
||
pReceive->FECGroupSize) &
|
||
~((SEQ_TYPE) pReceive->FECGroupSize - 1);
|
||
|
||
//
|
||
// Since this is the first receive for this session, see if we need to
|
||
// start the receive timer
|
||
//
|
||
KeQueryPerformanceCounter (&Frequency);
|
||
PgmDynamicConfig.TimeoutGranularity.QuadPart = (Frequency.QuadPart * BASIC_TIMER_GRANULARITY_IN_MSECS) / 1000;
|
||
InsertTailList (&PgmDynamicConfig.CurrentReceivers, &pReceive->pReceiver->Linkage);
|
||
if (!(PgmDynamicConfig.GlobalFlags & PGM_CONFIG_FLAG_RECEIVE_TIMER_RUNNING))
|
||
{
|
||
PgmDynamicConfig.GlobalFlags |= PGM_CONFIG_FLAG_RECEIVE_TIMER_RUNNING;
|
||
PgmDynamicConfig.LastReceiverTimeout = KeQueryPerformanceCounter (NULL);
|
||
pReceive->pReceiver->StartTickCount = PgmDynamicConfig.ReceiversTimerTickCount = 1;
|
||
|
||
PgmInitTimer (&PgmDynamicConfig.SessionTimer);
|
||
PgmStartTimer (&PgmDynamicConfig.SessionTimer, BASIC_TIMER_GRANULARITY_IN_MSECS, ReceiveTimerTimeout, NULL);
|
||
}
|
||
else
|
||
{
|
||
pReceive->pReceiver->StartTickCount = PgmDynamicConfig.ReceiversTimerTickCount;
|
||
}
|
||
CheckAndAddNakRequests (pReceive, &FirstODataSequenceNumber, NULL, NAK_PENDING_0);
|
||
|
||
PgmLog (PGM_LOG_INFORM_STATUS, DBG_RECEIVE, "PgmNewInboundConnection",
|
||
"New incoming connection, pAddress=<%p>, pReceive=<%p>, ThisSeq=<%d==>%d> (%sparity), StartSeq=<%d>\n",
|
||
pAddress, pReceive, ntohl(ulData), (ULONG) FirstODataSequenceNumber,
|
||
(pPgmHeader->CommonHeader.Options & PACKET_HEADER_OPTIONS_PARITY ? "" : "non-"),
|
||
(ULONG) pReceive->pReceiver->NextODataSequenceNumber);
|
||
|
||
PgmUnlock (pReceive, OldIrq2);
|
||
PgmUnlock (pAddress, OldIrq1);
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq);
|
||
|
||
//
|
||
// We are ready to proceed! So, complete the client's Accept Irp
|
||
//
|
||
PgmIoComplete (pIrp, STATUS_SUCCESS, 0);
|
||
|
||
//
|
||
// If we had failed, we would already have returned before now!
|
||
//
|
||
*ppReceive = pReceive;
|
||
return (STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
ProcessReceiveCompletionRoutine(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP pIrp,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles the case when a datagram is too
|
||
short and and Irp has to be passed back to the transport to get the
|
||
rest of the datagram. The irp completes through here when full.
|
||
|
||
Arguments:
|
||
|
||
IN DeviceObject - unused.
|
||
IN Irp - Supplies Irp that the transport has finished processing.
|
||
IN Context - Supplies the pReceive - the connection data structure
|
||
|
||
Return Value:
|
||
|
||
The final status from the operation (success or an exception).
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PIRP pIoRequestPacket;
|
||
ULONG BytesTaken;
|
||
tRCV_COMPLETE_CONTEXT *pRcvContext = (tRCV_COMPLETE_CONTEXT *) Context;
|
||
ULONG Offset = pRcvContext->BytesAvailable;
|
||
PVOID pBuffer;
|
||
ULONG SrcAddressLength;
|
||
PVOID pSrcAddress;
|
||
|
||
if (pBuffer = MmGetSystemAddressForMdlSafe (pIrp->MdlAddress, HighPagePriority))
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "ProcessReceiveCompletionRoutine",
|
||
"pIrp=<%p>, pRcvBuffer=<%p>, Status=<%x> Length=<%d>\n",
|
||
pIrp, Context, pIrp->IoStatus.Status, pIrp->IoStatus.Information);
|
||
|
||
SrcAddressLength = pRcvContext->SrcAddressLength;
|
||
pSrcAddress = pRcvContext->pSrcAddress;
|
||
|
||
//
|
||
// just call the regular indication routine as if UDP had done it.
|
||
//
|
||
TdiRcvDatagramHandler (pRcvContext->pAddress,
|
||
SrcAddressLength,
|
||
pSrcAddress,
|
||
0,
|
||
NULL,
|
||
TDI_RECEIVE_NORMAL,
|
||
(ULONG) pIrp->IoStatus.Information,
|
||
(ULONG) pIrp->IoStatus.Information,
|
||
&BytesTaken,
|
||
pBuffer,
|
||
&pIoRequestPacket);
|
||
}
|
||
else
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "ProcessReceiveCompletionRoutine",
|
||
"MmGetSystemA... FAILed, pIrp=<%p>, pLocalBuffer=<%p>\n", pIrp, pRcvContext);
|
||
}
|
||
|
||
//
|
||
// Free the Irp and Mdl and Buffer
|
||
//
|
||
IoFreeMdl (pIrp->MdlAddress);
|
||
pIrp->MdlAddress = NULL;
|
||
IoFreeIrp (pIrp);
|
||
PgmFreeMem (pRcvContext);
|
||
|
||
return (STATUS_MORE_PROCESSING_REQUIRED);
|
||
}
|
||
|
||
|
||
#ifdef DROP_DBG
|
||
|
||
ULONG MinDropInterval = 10;
|
||
ULONG MaxDropInterval = 10;
|
||
// ULONG DropCount = 10;
|
||
ULONG DropCount = -1;
|
||
#endif // DROP_DBG
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
TdiRcvDatagramHandler(
|
||
IN PVOID pDgramEventContext,
|
||
IN INT SourceAddressLength,
|
||
IN PVOID pSourceAddress,
|
||
IN INT OptionsLength,
|
||
IN TDI_CMSGHDR *pControlData,
|
||
IN ULONG ReceiveDatagramFlags,
|
||
IN ULONG BytesIndicated,
|
||
IN ULONG BytesAvailable,
|
||
OUT ULONG *pBytesTaken,
|
||
IN PVOID pTsdu,
|
||
OUT PIRP *ppIrp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is the handler for receiving all Pgm packets from
|
||
the transport (protocol == IPPROTO_RM)
|
||
|
||
Arguments:
|
||
|
||
IN pDgramEventContext -- Our context (pAddress)
|
||
IN SourceAddressLength -- Length of source address
|
||
IN pSourceAddress -- Address of remote host
|
||
IN OptionsLength
|
||
IN pControlData -- ControlData from transport
|
||
IN ReceiveDatagramFlags -- Flags set by the transport for this packet
|
||
IN BytesIndicated -- Bytes in this indicate
|
||
IN BytesAvailable -- total bytes available with the transport
|
||
OUT pBytesTaken -- bytes taken by us
|
||
IN pTsdu -- data packet ptr
|
||
OUT ppIrp -- pIrp if more processing required
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - Final status of the call
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
tCOMMON_HEADER UNALIGNED *pPgmHeader;
|
||
tBASIC_SPM_PACKET_HEADER UNALIGNED *pSpmPacket;
|
||
tCOMMON_SESSION_CONTEXT *pSession;
|
||
PLIST_ENTRY pEntry;
|
||
PGMLockHandle OldIrq, OldIrq1;
|
||
USHORT TSDULength, TSIPort, LocalSessionPort, PacketSessionPort;
|
||
PVOID pFECBuffer;
|
||
UCHAR PacketType;
|
||
IP_PKTINFO *pPktInfo;
|
||
PIRP pLocalIrp = NULL;
|
||
PMDL pLocalMdl = NULL;
|
||
tRCV_COMPLETE_CONTEXT *pRcvBuffer = NULL;
|
||
ULONG XSum, BufferLength = 0;
|
||
IPV4Header *pIp = (IPV4Header *) pTsdu;
|
||
PTA_IP_ADDRESS pIpAddress = (PTA_IP_ADDRESS) pSourceAddress;
|
||
tADDRESS_CONTEXT *pAddress = (tADDRESS_CONTEXT *) pDgramEventContext;
|
||
|
||
*pBytesTaken = 0; // Initialize the Bytes Taken!
|
||
*ppIrp = NULL;
|
||
|
||
#ifdef DROP_DBG
|
||
//
|
||
// Drop OData packets only for now!
|
||
//
|
||
pPgmHeader = (tCOMMON_HEADER UNALIGNED *) (((PUCHAR)pIp) + (pIp->HeaderLength * 4));
|
||
PacketType = pPgmHeader->Type & 0x0f;
|
||
if ((PacketType == PACKET_TYPE_ODATA) &&
|
||
!(((tBASIC_DATA_PACKET_HEADER *) pPgmHeader)->CommonHeader.Options & PACKET_HEADER_OPTIONS_PARITY) &&
|
||
!(--DropCount))
|
||
{
|
||
ULONG SequenceNumber;
|
||
|
||
DropCount = GetRandomInteger (MinDropInterval, MaxDropInterval);
|
||
|
||
/*
|
||
PgmCopyMemory (&SequenceNumber, &((tBASIC_DATA_PACKET_HEADER *) pPgmHeader)->DataSequenceNumber, sizeof (ULONG));
|
||
DbgPrint("TdiRcvDatagramHandler: Dropping packet, %s SeqNum = %d!\n",
|
||
(((tBASIC_DATA_PACKET_HEADER *) pPgmHeader)->CommonHeader.Options & PACKET_HEADER_OPTIONS_PARITY ? "PARITY" : "DATA"),
|
||
ntohl (SequenceNumber));
|
||
*/
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
#endif // DROP_DBG
|
||
ASSERT (BytesAvailable < MAX_RECEIVE_SIZE);
|
||
|
||
PgmLock (&PgmDynamicConfig, OldIrq);
|
||
if (BytesIndicated > PgmDynamicConfig.MaxMTU)
|
||
{
|
||
PgmDynamicConfig.MaxMTU = BytesIndicated;
|
||
}
|
||
|
||
if (!PGM_VERIFY_HANDLE (pAddress, PGM_VERIFY_ADDRESS))
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "TdiRcvDatagramHandler",
|
||
"Invalid Address handle=<%p>\n", pAddress);
|
||
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq);
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
|
||
//
|
||
// Now, Reference the Address so that it cannot go away
|
||
// while we are processing it!
|
||
//
|
||
PGM_REFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER, FALSE);
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq);
|
||
|
||
//
|
||
// If we do not have the complete datagram, then pass an Irp down to retrieve it
|
||
//
|
||
ASSERT (BytesIndicated <= BytesAvailable);
|
||
if (!(ReceiveDatagramFlags & TDI_RECEIVE_ENTIRE_MESSAGE) &&
|
||
(BytesAvailable != BytesIndicated))
|
||
{
|
||
//
|
||
//
|
||
// Build an irp to do the receive with and attach a buffer to it.
|
||
//
|
||
BufferLength = sizeof (tRCV_COMPLETE_CONTEXT) + BytesAvailable + SourceAddressLength;
|
||
BufferLength = ((BufferLength + 3)/sizeof(ULONG)) * sizeof(ULONG);
|
||
|
||
if ((pLocalIrp = IoAllocateIrp (pgPgmDevice->pPgmDeviceObject->StackSize, FALSE)) &&
|
||
(pRcvBuffer = PgmAllocMem (BufferLength, PGM_TAG('3'))) &&
|
||
(pLocalMdl = IoAllocateMdl (&pRcvBuffer->BufferData, BytesAvailable, FALSE, FALSE, NULL)))
|
||
{
|
||
pLocalIrp->MdlAddress = pLocalMdl;
|
||
MmBuildMdlForNonPagedPool (pLocalMdl); // Map the pages in memory...
|
||
|
||
TdiBuildReceiveDatagram (pLocalIrp,
|
||
pAddress->pDeviceObject,
|
||
pAddress->pFileObject,
|
||
ProcessReceiveCompletionRoutine,
|
||
pRcvBuffer,
|
||
pLocalMdl,
|
||
BytesAvailable,
|
||
NULL,
|
||
NULL,
|
||
0); // (ULONG) TDI_RECEIVE_NORMAL) ?
|
||
|
||
// make the next stack location the current one. Normally IoCallDriver
|
||
// would do this but we are not going through IoCallDriver here, since the
|
||
// Irp is just passed back with RcvIndication.
|
||
//
|
||
ASSERT (pLocalIrp->CurrentLocation > 1);
|
||
IoSetNextIrpStackLocation (pLocalIrp);
|
||
|
||
//
|
||
// save the source address and length in the buffer for later
|
||
// indication back to this routine.
|
||
//
|
||
pRcvBuffer->pAddress = pAddress;
|
||
pRcvBuffer->SrcAddressLength = SourceAddressLength;
|
||
pRcvBuffer->pSrcAddress = (PVOID) ((PUCHAR)&pRcvBuffer->BufferData + BytesAvailable);
|
||
PgmCopyMemory (pRcvBuffer->pSrcAddress, pSourceAddress, SourceAddressLength);
|
||
|
||
*pBytesTaken = 0;
|
||
*ppIrp = pLocalIrp;
|
||
|
||
status = STATUS_MORE_PROCESSING_REQUIRED;
|
||
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "TdiRcvDatagramHandler",
|
||
"BytesI=<%d>, BytesA=<%d>, Flags=<%x>, pIrp=<%p>\n",
|
||
BytesIndicated, BytesAvailable, ReceiveDatagramFlags, pLocalIrp);
|
||
}
|
||
else
|
||
{
|
||
// Cleanup on failure:
|
||
if (pLocalIrp)
|
||
{
|
||
IoFreeIrp (pLocalIrp);
|
||
}
|
||
if (pRcvBuffer)
|
||
{
|
||
PgmFreeMem (pRcvBuffer);
|
||
}
|
||
|
||
status = STATUS_DATA_NOT_ACCEPTED;
|
||
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "TdiRcvDatagramHandler",
|
||
"INSUFFICIENT_RESOURCES, BuffLen=<%d>, pIrp=<%p>, pBuff=<%p>\n",
|
||
BufferLength, pLocalIrp, pRcvBuffer);
|
||
}
|
||
|
||
PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER);
|
||
return (status);
|
||
}
|
||
|
||
//
|
||
// Now that we have the complete datagram, verify that it is valid
|
||
// First line of defense against bad packets.
|
||
//
|
||
if ((BytesIndicated < (sizeof(IPV4Header) + sizeof(tCOMMON_HEADER))) ||
|
||
(pIp->Version != 4) ||
|
||
(BytesIndicated < (pIp->HeaderLength*4 + sizeof(tCOMMON_HEADER))) ||
|
||
(pIpAddress->TAAddressCount != 1) ||
|
||
(pIpAddress->Address[0].AddressLength != TDI_ADDRESS_LENGTH_IP) ||
|
||
(pIpAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_IP))
|
||
{
|
||
//
|
||
// Need to get at least our header from transport!
|
||
//
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "TdiRcvDatagramHandler",
|
||
"IPver=<%d>, BytesI=<%d>, Min=<%d>, AddrType=<%d>\n",
|
||
pIp->Version, BytesIndicated, (sizeof(IPV4Header) + sizeof(tBASIC_DATA_PACKET_HEADER)),
|
||
pIpAddress->Address[0].AddressType);
|
||
|
||
ASSERT (0);
|
||
|
||
PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER);
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
|
||
pPgmHeader = (tCOMMON_HEADER UNALIGNED *) (((PUCHAR)pIp) + (pIp->HeaderLength * 4));
|
||
PgmCopyMemory (&TSDULength, &pPgmHeader->TSDULength, sizeof (USHORT));
|
||
TSDULength = ntohs (TSDULength);
|
||
|
||
BytesIndicated -= (pIp->HeaderLength * 4);
|
||
BytesAvailable -= (pIp->HeaderLength * 4);
|
||
|
||
ASSERT (BytesIndicated == BytesAvailable);
|
||
|
||
//
|
||
// Now, Verify Checksum
|
||
//
|
||
if ((XSum = tcpxsum (0, (CHAR *) pPgmHeader, BytesIndicated)) != 0xffff)
|
||
{
|
||
//
|
||
// Need to get at least our header from transport!
|
||
//
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "TdiRcvDatagramHandler",
|
||
"Bad Checksum on Pgm Packet (type=<%x>)! XSum=<%x> -- Rejecting packet\n",
|
||
pPgmHeader->Type, XSum);
|
||
|
||
// ASSERT (0);
|
||
|
||
PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER);
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
|
||
//
|
||
// Now, determine the TSI, i.e. GSI (from packet) + TSIPort (below)
|
||
//
|
||
PacketType = pPgmHeader->Type & 0x0f;
|
||
if ((PacketType == PACKET_TYPE_NAK) ||
|
||
(PacketType == PACKET_TYPE_NNAK) ||
|
||
(PacketType == PACKET_TYPE_SPMR) ||
|
||
(PacketType == PACKET_TYPE_POLR))
|
||
{
|
||
PgmCopyMemory (&TSIPort, &pPgmHeader->DestPort, sizeof (USHORT));
|
||
PgmCopyMemory (&PacketSessionPort, &pPgmHeader->SrcPort, sizeof (USHORT));
|
||
}
|
||
else
|
||
{
|
||
PgmCopyMemory (&TSIPort, &pPgmHeader->SrcPort, sizeof (USHORT));
|
||
PgmCopyMemory (&PacketSessionPort, &pPgmHeader->DestPort, sizeof (USHORT));
|
||
}
|
||
TSIPort = ntohs (TSIPort);
|
||
PacketSessionPort = ntohs (PacketSessionPort);
|
||
|
||
//
|
||
// If this packet is for a different session port, drop it
|
||
//
|
||
if (pAddress->ReceiverMCastAddr)
|
||
{
|
||
LocalSessionPort = pAddress->ReceiverMCastPort;
|
||
}
|
||
else
|
||
{
|
||
LocalSessionPort = pAddress->SenderMCastPort;
|
||
}
|
||
|
||
if (LocalSessionPort != PacketSessionPort)
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_PATH, DBG_RECEIVE, "TdiRcvDatagramHandler",
|
||
"Dropping packet for different Session port, <%x>!=<%x>!\n", LocalSessionPort, PacketSessionPort);
|
||
|
||
PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER);
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
|
||
//
|
||
// Now check if this receive is for an active connection
|
||
//
|
||
pSession = NULL;
|
||
PgmLock (pAddress, OldIrq); // So that the list cannot change!
|
||
pEntry = &pAddress->AssociatedConnections;
|
||
while ((pEntry = pEntry->Flink) != &pAddress->AssociatedConnections)
|
||
{
|
||
pSession = CONTAINING_RECORD (pEntry, tCOMMON_SESSION_CONTEXT, Linkage);
|
||
|
||
PgmLock (pSession, OldIrq1);
|
||
|
||
if ((PGM_VERIFY_HANDLE2 (pSession, PGM_VERIFY_SESSION_RECEIVE, PGM_VERIFY_SESSION_SEND)) &&
|
||
(0 == strncmp (pSession->GSI, pPgmHeader->gSourceId, SOURCE_ID_LENGTH)) &&
|
||
(TSIPort == pSession->TSIPort) &&
|
||
!(pSession->SessionFlags & (PGM_SESSION_DISCONNECT_INDICATED | PGM_SESSION_TERMINATED_ABORT)))
|
||
{
|
||
if (pSession->pSender)
|
||
{
|
||
PGM_REFERENCE_SESSION_SEND (pSession, REF_SESSION_TDI_RCV_HANDLER, TRUE);
|
||
PgmUnlock (pSession, OldIrq1);
|
||
break;
|
||
}
|
||
|
||
ASSERT (pSession->pReceiver);
|
||
PGM_REFERENCE_SESSION_RECEIVE (pSession, REF_SESSION_TDI_RCV_HANDLER, TRUE);
|
||
|
||
if ((pSession->FECOptions) &&
|
||
(BytesIndicated > pSession->MaxMTULength))
|
||
{
|
||
if (pFECBuffer = PgmAllocMem (((BytesIndicated+sizeof(tPOST_PACKET_FEC_CONTEXT)-sizeof(USHORT))
|
||
*pSession->FECGroupSize*2), PGM_TAG('3')))
|
||
{
|
||
ASSERT (pSession->pFECBuffer);
|
||
PgmFreeMem (pSession->pFECBuffer);
|
||
pSession->pFECBuffer = pFECBuffer;
|
||
pSession->MaxMTULength = (USHORT) BytesIndicated;
|
||
pSession->MaxFECPacketLength = pSession->MaxMTULength +
|
||
sizeof (tPOST_PACKET_FEC_CONTEXT) - sizeof (USHORT);
|
||
}
|
||
else
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, DBG_RECEIVE, "TdiRcvDatagramHandler",
|
||
"STATUS_INSUFFICIENT_RESOURCES -- pFECBuffer=<%d> bytes\n",
|
||
(BytesIndicated+sizeof(tPOST_PACKET_FEC_CONTEXT)-sizeof(USHORT)));
|
||
|
||
pSession = NULL;
|
||
pSession->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
||
}
|
||
}
|
||
|
||
PgmUnlock (pSession, OldIrq1);
|
||
break;
|
||
}
|
||
|
||
PgmUnlock (pSession, OldIrq1);
|
||
pSession = NULL;
|
||
}
|
||
|
||
PgmUnlock (pAddress, OldIrq);
|
||
|
||
if (!pSession)
|
||
{
|
||
// We should drop this packet because we received this either because
|
||
// we may have a loopback session, or we have a listen but this
|
||
// is not an OData packet
|
||
status = STATUS_DATA_NOT_ACCEPTED;
|
||
|
||
//
|
||
// New sessions will be accepted only if we are a receiver
|
||
// Also, new sessions will always be initiated only with an OData packet
|
||
// Also, verify that the client has posted a connect handler!
|
||
//
|
||
if ((pAddress->ReceiverMCastAddr) &&
|
||
(pAddress->ConEvContext))
|
||
{
|
||
if ((PacketType == PACKET_TYPE_ODATA) &&
|
||
(!(pPgmHeader->Options & PACKET_HEADER_OPTIONS_PARITY)))
|
||
{
|
||
//
|
||
// This is a new incoming connection, so see if the
|
||
// client accepts it.
|
||
//
|
||
status = PgmNewInboundConnection (pAddress,
|
||
SourceAddressLength,
|
||
pSourceAddress,
|
||
ReceiveDatagramFlags,
|
||
(tBASIC_DATA_PACKET_HEADER UNALIGNED *) pPgmHeader,
|
||
BytesIndicated,
|
||
&pSession);
|
||
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "TdiRcvDatagramHandler",
|
||
"pAddress=<%p> FAILed to accept new connection, PacketType=<%x>, status=<%x>\n",
|
||
pAddress, PacketType, status);
|
||
}
|
||
}
|
||
else if (PacketType == PACKET_TYPE_SPM)
|
||
{
|
||
ProcessSpmPacket (pAddress,
|
||
NULL, // This will signify that we do not have a connection yet
|
||
BytesIndicated,
|
||
(tBASIC_SPM_PACKET_HEADER UNALIGNED *) pPgmHeader);
|
||
}
|
||
}
|
||
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER);
|
||
return (STATUS_DATA_NOT_ACCEPTED);
|
||
}
|
||
}
|
||
|
||
if ((pAddress->Flags & PGM_ADDRESS_WAITING_FOR_NEW_INTERFACE) &&
|
||
(pAddress->Flags & PGM_ADDRESS_LISTEN_ON_ALL_INTERFACES) &&
|
||
(ReceiveDatagramFlags & TDI_RECEIVE_CONTROL_INFO))
|
||
{
|
||
//
|
||
// See if we can Enqueue the stop listening request
|
||
//
|
||
PgmLock (&PgmDynamicConfig, OldIrq);
|
||
PgmLock (pAddress, OldIrq1);
|
||
|
||
pPktInfo = (IP_PKTINFO*) TDI_CMSG_DATA (pControlData);
|
||
PGM_REFERENCE_ADDRESS (pAddress, REF_ADDRESS_STOP_LISTENING, TRUE);
|
||
|
||
if (STATUS_SUCCESS == PgmQueueForDelayedExecution (StopListeningOnAllInterfacesExcept,
|
||
pAddress,
|
||
ULongToPtr (pPktInfo->ipi_ifindex),
|
||
NULL,
|
||
TRUE))
|
||
{
|
||
pAddress->Flags &= ~PGM_ADDRESS_WAITING_FOR_NEW_INTERFACE;
|
||
|
||
PgmUnlock (pAddress, OldIrq1);
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq);
|
||
}
|
||
else
|
||
{
|
||
PgmUnlock (pAddress, OldIrq1);
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq);
|
||
|
||
PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_STOP_LISTENING);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Now, handle the packet appropriately
|
||
//
|
||
status = PgmProcessIncomingPacket (pAddress,
|
||
pSession,
|
||
SourceAddressLength,
|
||
pSourceAddress,
|
||
BytesIndicated,
|
||
pPgmHeader,
|
||
PacketType);
|
||
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, DBG_RECEIVE, "TdiRcvDatagramHandler",
|
||
"PacketType=<%x> for pSession=<%p> BytesI=<%d>, BytesA=<%d>, status=<%x>\n",
|
||
PacketType, pSession, BytesIndicated, BytesAvailable, status);
|
||
|
||
if (pSession->pSender)
|
||
{
|
||
PGM_DEREFERENCE_SESSION_SEND (pSession, REF_SESSION_TDI_RCV_HANDLER);
|
||
}
|
||
else
|
||
{
|
||
ASSERT (pSession->pReceiver);
|
||
PGM_DEREFERENCE_SESSION_RECEIVE (pSession, REF_SESSION_TDI_RCV_HANDLER);
|
||
}
|
||
|
||
PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER);
|
||
|
||
//
|
||
// Only acceptable return codes are STATUS_SUCCESS and STATUS_DATA_NOT_ACCPETED
|
||
// (STATUS_MORE_PROCESSING_REQUIRED is not valid here because we have no Irp).
|
||
//
|
||
if (STATUS_SUCCESS != status)
|
||
{
|
||
status = STATUS_DATA_NOT_ACCEPTED;
|
||
}
|
||
|
||
return (status);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
VOID
|
||
PgmCancelReceiveIrp(
|
||
IN PDEVICE_OBJECT DeviceContext,
|
||
IN PIRP pIrp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles the cancelling of a Receive Irp. It must release the
|
||
cancel spin lock before returning re: IoCancelIrp().
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
None
|
||
|
||
--*/
|
||
{
|
||
PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation (pIrp);
|
||
tRECEIVE_SESSION *pReceive = (tRECEIVE_SESSION *) pIrpSp->FileObject->FsContext;
|
||
PGMLockHandle OldIrq;
|
||
PLIST_ENTRY pEntry;
|
||
|
||
if (!PGM_VERIFY_HANDLE (pReceive, PGM_VERIFY_SESSION_RECEIVE))
|
||
{
|
||
IoReleaseCancelSpinLock (pIrp->CancelIrql);
|
||
|
||
PgmLog (PGM_LOG_ERROR, (DBG_RECEIVE | DBG_ADDRESS | DBG_CONNECT), "PgmCancelReceiveIrp",
|
||
"pIrp=<%p> pReceive=<%p>, pAddress=<%p>\n", pIrp, pReceive, pReceive->pReceiver->pAddress);
|
||
return;
|
||
}
|
||
|
||
PgmLock (pReceive, OldIrq);
|
||
|
||
//
|
||
// See if we are actively receiving
|
||
//
|
||
if (pIrp == pReceive->pReceiver->pIrpReceive)
|
||
{
|
||
pIrp->IoStatus.Information = pReceive->pReceiver->BytesInMdl;
|
||
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
||
|
||
pReceive->pReceiver->BytesInMdl = pReceive->pReceiver->TotalBytesInMdl = 0;
|
||
pReceive->pReceiver->pIrpReceive = NULL;
|
||
|
||
PgmUnlock (pReceive, OldIrq);
|
||
IoReleaseCancelSpinLock (pIrp->CancelIrql);
|
||
|
||
IoCompleteRequest (pIrp,IO_NETWORK_INCREMENT);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// We are not actively receiving, so see if this Irp is
|
||
// in our Irps list
|
||
//
|
||
pEntry = &pReceive->pReceiver->ReceiveIrpsList;
|
||
while ((pEntry = pEntry->Flink) != &pReceive->pReceiver->ReceiveIrpsList)
|
||
{
|
||
if (pEntry == &pIrp->Tail.Overlay.ListEntry)
|
||
{
|
||
RemoveEntryList (pEntry);
|
||
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
||
pIrp->IoStatus.Information = 0;
|
||
|
||
PgmUnlock (pReceive, OldIrq);
|
||
IoReleaseCancelSpinLock (pIrp->CancelIrql);
|
||
|
||
IoCompleteRequest(pIrp,IO_NETWORK_INCREMENT);
|
||
return;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we have reached here, then the Irp must already
|
||
// be in the process of being completed!
|
||
//
|
||
PgmUnlock (pReceive, OldIrq);
|
||
IoReleaseCancelSpinLock (pIrp->CancelIrql);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|
||
|
||
NTSTATUS
|
||
PgmReceive(
|
||
IN tPGM_DEVICE *pPgmDevice,
|
||
IN PIRP pIrp,
|
||
IN PIO_STACK_LOCATION pIrpSp
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is called via dispatch by the client to post a Receive pIrp
|
||
|
||
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 request
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PGMLockHandle OldIrq, OldIrq1, OldIrq2, OldIrq3;
|
||
tADDRESS_CONTEXT *pAddress = NULL;
|
||
tRECEIVE_SESSION *pReceive = (tRECEIVE_SESSION *) pIrpSp->FileObject->FsContext;
|
||
PTDI_REQUEST_KERNEL_RECEIVE pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE) &pIrpSp->Parameters;
|
||
|
||
PgmLock (&PgmDynamicConfig, OldIrq);
|
||
IoAcquireCancelSpinLock (&OldIrq1);
|
||
|
||
//
|
||
// Verify that the connection is valid and is associated with an address
|
||
//
|
||
if ((!PGM_VERIFY_HANDLE (pReceive, PGM_VERIFY_SESSION_RECEIVE)) ||
|
||
(!(pAddress = pReceive->pAssociatedAddress)) ||
|
||
(!PGM_VERIFY_HANDLE (pAddress, PGM_VERIFY_ADDRESS)))
|
||
{
|
||
PgmLog (PGM_LOG_ERROR, (DBG_RECEIVE | DBG_ADDRESS | DBG_CONNECT), "PgmReceive",
|
||
"Invalid Handles pReceive=<%p>, pAddress=<%p>\n", pReceive, pAddress);
|
||
|
||
status = STATUS_INVALID_HANDLE;
|
||
}
|
||
else if (pReceive->SessionFlags & PGM_SESSION_DISCONNECT_INDICATED)
|
||
{
|
||
PgmLog (PGM_LOG_INFORM_PATH, (DBG_RECEIVE | DBG_ADDRESS | DBG_CONNECT), "PgmReceive",
|
||
"Receive Irp=<%p> was posted after session has been Disconnected, pReceive=<%p>, pAddress=<%p>\n",
|
||
pIrp, pReceive, pAddress);
|
||
|
||
status = STATUS_CANCELLED;
|
||
}
|
||
else if (!pClientParams->ReceiveLength)
|
||
{
|
||
ASSERT (0);
|
||
PgmLog (PGM_LOG_ERROR, (DBG_RECEIVE | DBG_ADDRESS | DBG_CONNECT), "PgmReceive",
|
||
"Invalid Handles pReceive=<%p>, pAddress=<%p>\n", pReceive, pAddress);
|
||
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
else
|
||
{
|
||
status = STATUS_SUCCESS;
|
||
}
|
||
|
||
if (!NT_SUCCESS (status))
|
||
{
|
||
IoReleaseCancelSpinLock (OldIrq1);
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq);
|
||
|
||
pIrp->IoStatus.Information = 0;
|
||
return (status);
|
||
}
|
||
|
||
PgmLock (pAddress, OldIrq2);
|
||
PgmLock (pReceive, OldIrq3);
|
||
|
||
if (!NT_SUCCESS (PgmCheckSetCancelRoutine (pIrp, PgmCancelReceiveIrp, TRUE)))
|
||
{
|
||
PgmUnlock (pReceive, OldIrq3);
|
||
PgmUnlock (pAddress, OldIrq2);
|
||
IoReleaseCancelSpinLock (OldIrq1);
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq);
|
||
|
||
PgmLog (PGM_LOG_ERROR, (DBG_RECEIVE | DBG_ADDRESS | DBG_CONNECT), "PgmReceive",
|
||
"Could not set Cancel routine on receive Irp=<%p>, pReceive=<%p>, pAddress=<%p>\n",
|
||
pIrp, pReceive, pAddress);
|
||
|
||
return (STATUS_CANCELLED);
|
||
}
|
||
IoReleaseCancelSpinLock (OldIrq3);
|
||
|
||
PGM_REFERENCE_ADDRESS (pAddress, REF_ADDRESS_CLIENT_RECEIVE, TRUE);
|
||
PGM_REFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_CLIENT_RECEIVE, TRUE);
|
||
|
||
PgmUnlock (&PgmDynamicConfig, OldIrq2);
|
||
|
||
PgmLog (PGM_LOG_INFORM_ALL_FUNCS, (DBG_RECEIVE | DBG_ADDRESS | DBG_CONNECT), "PgmReceive",
|
||
"Client posted ReceiveIrp = <%p> for pReceive=<%p>\n", pIrp, pReceive);
|
||
|
||
InsertTailList (&pReceive->pReceiver->ReceiveIrpsList, &pIrp->Tail.Overlay.ListEntry);
|
||
pReceive->SessionFlags &= ~PGM_SESSION_WAIT_FOR_RECEIVE_IRP;
|
||
|
||
//
|
||
// Now, try to indicate any data which may still be pending
|
||
//
|
||
status = CheckIndicatePendedData (pAddress, pReceive, &OldIrq, &OldIrq1);
|
||
|
||
PgmUnlock (pReceive, OldIrq1);
|
||
PgmUnlock (pAddress, OldIrq);
|
||
|
||
PGM_DEREFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_CLIENT_RECEIVE);
|
||
PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_CLIENT_RECEIVE);
|
||
|
||
return (STATUS_PENDING);
|
||
}
|
||
|
||
|
||
//----------------------------------------------------------------------------
|