You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6864 lines
259 KiB
6864 lines
259 KiB
/*++
|
|
|
|
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"
|
|
|
|
#ifdef FILE_LOGGING
|
|
#include "receive.tmh"
|
|
#endif // FILE_LOGGING
|
|
|
|
|
|
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
|
|
FreeDataBuffer(
|
|
IN tRECEIVE_SESSION *pReceive,
|
|
IN tPENDING_DATA *pPendingData
|
|
)
|
|
{
|
|
ASSERT (pPendingData->pDataPacket);
|
|
|
|
if (pPendingData->PendingDataFlags & PENDING_DATA_LOOKASIDE_ALLOCATION_FLAG)
|
|
{
|
|
ExFreeToNPagedLookasideList (&pReceive->pReceiver->DataBufferLookaside, pPendingData->pDataPacket);
|
|
if ((0 == --pReceive->pReceiver->NumDataBuffersFromLookaside) &&
|
|
!(pReceive->SessionFlags & PGM_SESSION_DATA_FROM_LOOKASIDE))
|
|
{
|
|
ASSERT (pReceive->pReceiver->MaxBufferLength > pReceive->pReceiver->DataBufferLookasideLength);
|
|
pReceive->pReceiver->MaxBufferLength += 100;
|
|
pReceive->pReceiver->DataBufferLookasideLength = pReceive->pReceiver->MaxBufferLength;
|
|
pReceive->SessionFlags |= PGM_SESSION_DATA_FROM_LOOKASIDE;
|
|
|
|
ExDeleteNPagedLookasideList (&pReceive->pReceiver->DataBufferLookaside);
|
|
|
|
ASSERT (pReceive->pReceiver->MaxPacketsBufferedInLookaside);
|
|
ExInitializeNPagedLookasideList (&pReceive->pReceiver->DataBufferLookaside,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
pReceive->pReceiver->DataBufferLookasideLength,
|
|
PGM_TAG ('D'),
|
|
pReceive->pReceiver->MaxPacketsBufferedInLookaside);
|
|
}
|
|
|
|
pPendingData->PendingDataFlags &= ~PENDING_DATA_LOOKASIDE_ALLOCATION_FLAG;
|
|
}
|
|
else
|
|
{
|
|
PgmFreeMem (pPendingData->pDataPacket);
|
|
}
|
|
|
|
pPendingData->pDataPacket = NULL;
|
|
pPendingData->PacketLength = pPendingData->DataOffset = 0;
|
|
}
|
|
|
|
PVOID
|
|
AllocateDataBuffer(
|
|
IN tRECEIVE_SESSION *pReceive,
|
|
IN tPENDING_DATA *pPendingData,
|
|
IN ULONG BufferSize
|
|
)
|
|
{
|
|
ASSERT (!pPendingData->pDataPacket);
|
|
ASSERT (!(pPendingData->PendingDataFlags & PENDING_DATA_LOOKASIDE_ALLOCATION_FLAG));
|
|
|
|
if ((pReceive->SessionFlags & PGM_SESSION_DATA_FROM_LOOKASIDE) &&
|
|
(BufferSize <= pReceive->pReceiver->DataBufferLookasideLength))
|
|
{
|
|
if (pPendingData->pDataPacket = ExAllocateFromNPagedLookasideList (&pReceive->pReceiver->DataBufferLookaside))
|
|
{
|
|
pReceive->pReceiver->NumDataBuffersFromLookaside++;
|
|
pPendingData->PendingDataFlags |= PENDING_DATA_LOOKASIDE_ALLOCATION_FLAG;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pReceive->SessionFlags &= ~PGM_SESSION_DATA_FROM_LOOKASIDE; // Ensure no more lookaside!
|
|
pPendingData->pDataPacket = PgmAllocMem (BufferSize, PGM_TAG('D'));
|
|
|
|
if (BufferSize > pReceive->pReceiver->MaxBufferLength)
|
|
{
|
|
pReceive->pReceiver->MaxBufferLength = BufferSize;
|
|
}
|
|
}
|
|
|
|
return (pPendingData->pDataPacket);
|
|
}
|
|
|
|
PVOID
|
|
ReAllocateDataBuffer(
|
|
IN tRECEIVE_SESSION *pReceive,
|
|
IN tPENDING_DATA *pPendingData,
|
|
IN ULONG BufferSize
|
|
)
|
|
{
|
|
ULONG SavedFlags1, SavedFlags2;
|
|
PUCHAR pSavedPacket1, pSavedPacket2;
|
|
|
|
//
|
|
// First, save the context for the current buffer
|
|
//
|
|
SavedFlags1 = pPendingData->PendingDataFlags;
|
|
pSavedPacket1 = pPendingData->pDataPacket;
|
|
pPendingData->PendingDataFlags = 0;
|
|
pPendingData->pDataPacket = NULL;
|
|
|
|
if (AllocateDataBuffer (pReceive, pPendingData, BufferSize))
|
|
{
|
|
ASSERT (pPendingData->pDataPacket);
|
|
|
|
//
|
|
// Now, save the context for the new buffer
|
|
//
|
|
SavedFlags2 = pPendingData->PendingDataFlags;
|
|
pSavedPacket2 = pPendingData->pDataPacket;
|
|
|
|
//
|
|
// Free the original buffer
|
|
//
|
|
pPendingData->PendingDataFlags = SavedFlags1;
|
|
pPendingData->pDataPacket = pSavedPacket1;
|
|
FreeDataBuffer (pReceive, pPendingData);
|
|
|
|
//
|
|
// Reset the information for the new buffer!
|
|
//
|
|
if (SavedFlags2 & PENDING_DATA_LOOKASIDE_ALLOCATION_FLAG)
|
|
{
|
|
pPendingData->PendingDataFlags = SavedFlags1 | PENDING_DATA_LOOKASIDE_ALLOCATION_FLAG;
|
|
}
|
|
else
|
|
{
|
|
pPendingData->PendingDataFlags = SavedFlags1 & ~PENDING_DATA_LOOKASIDE_ALLOCATION_FLAG;
|
|
}
|
|
pPendingData->pDataPacket = pSavedPacket2;
|
|
|
|
return (pPendingData->pDataPacket);
|
|
}
|
|
|
|
//
|
|
// Failure case!
|
|
//
|
|
pPendingData->pDataPacket = pSavedPacket1;
|
|
pPendingData->PendingDataFlags = SavedFlags1;
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
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++)
|
|
{
|
|
if (pNak->pPendingData[i].PacketIndex < pReceive->FECGroupSize)
|
|
{
|
|
j++;
|
|
}
|
|
else
|
|
{
|
|
k++;
|
|
}
|
|
FreeDataBuffer (pReceive, &pNak->pPendingData[i]);
|
|
}
|
|
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 fReceiveLockHeld
|
|
)
|
|
{
|
|
LIST_ENTRY NaksList, DataList;
|
|
tNAK_FORWARD_DATA *pNak;
|
|
LIST_ENTRY *pEntry;
|
|
ULONG NumBufferedData = 0;
|
|
ULONG NumNaks = 0;
|
|
PGMLockHandle OldIrq;
|
|
|
|
ASSERT (pReceive->pReceiver);
|
|
|
|
if (!fReceiveLockHeld)
|
|
{
|
|
PgmLock (pReceive, OldIrq);
|
|
}
|
|
else
|
|
{
|
|
ASSERT (!fDerefReceive);
|
|
}
|
|
|
|
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);
|
|
|
|
RemoveAllPendingReceiverEntries (pReceive->pReceiver);
|
|
|
|
pReceive->pReceiver->NumDataBuffersFromLookaside++; // So that we don't assert
|
|
|
|
if (!fReceiveLockHeld)
|
|
{
|
|
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++;
|
|
}
|
|
|
|
PgmTrace (LogStatus, ("CleanupPendingNaks: " \
|
|
"pReceive=<%p>, FirstNak=<%d>, NumBufferedData=<%d=%d>, TotalDataPackets=<%d>, NumNaks=<%d * %d>\n",
|
|
pReceive, (ULONG) pReceive->pReceiver->FirstNakSequenceNumber,
|
|
(ULONG) pReceive->pReceiver->NumPacketGroupsPendingClient, NumBufferedData,
|
|
(ULONG) pReceive->pReceiver->TotalDataPacketsBuffered, NumNaks, (ULONG) pReceive->FECGroupSize));
|
|
|
|
// ASSERT (NumBufferedData == pReceive->pReceiver->NumPacketGroupsPendingClient);
|
|
pReceive->pReceiver->NumPacketGroupsPendingClient = 0;
|
|
|
|
if (!fReceiveLockHeld)
|
|
{
|
|
PgmLock (pReceive, OldIrq);
|
|
}
|
|
|
|
pReceive->pReceiver->NumDataBuffersFromLookaside--; // Undoing what we did earlier!
|
|
ASSERT (!pReceive->pReceiver->NumDataBuffersFromLookaside);
|
|
if (pReceive->SessionFlags & PGM_SESSION_DATA_FROM_LOOKASIDE)
|
|
{
|
|
pReceive->pReceiver->MaxBufferLength = 0;
|
|
pReceive->SessionFlags &= ~PGM_SESSION_DATA_FROM_LOOKASIDE; // Ensure no more lookaside!
|
|
ExDeleteNPagedLookasideList (&pReceive->pReceiver->DataBufferLookaside);
|
|
}
|
|
|
|
if (pReceive->pReceiver->pReceiveData)
|
|
{
|
|
PgmFreeMem (pReceive->pReceiver->pReceiveData);
|
|
pReceive->pReceiver->pReceiveData = NULL;
|
|
}
|
|
|
|
if (!fReceiveLockHeld)
|
|
{
|
|
PgmUnlock (pReceive, OldIrq);
|
|
|
|
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;
|
|
tNAK_FORWARD_DATA *pNak;
|
|
SEQ_TYPE FirstNakSequenceNumber;
|
|
|
|
//
|
|
// Don't abort if we are currently indicating, or if we have
|
|
// already aborted!
|
|
//
|
|
fDisconnectIndicated = (pReceive->SessionFlags & PGM_SESSION_CLIENT_DISCONNECTED ? TRUE : FALSE);
|
|
if (pReceive->SessionFlags & (PGM_SESSION_FLAG_IN_INDICATE | PGM_SESSION_CLIENT_DISCONNECTED))
|
|
{
|
|
return (fDisconnectIndicated);
|
|
}
|
|
|
|
if (IsListEmpty (&pReceive->pReceiver->NaksForwardDataList))
|
|
{
|
|
FirstNakSequenceNumber = pReceive->pReceiver->FirstNakSequenceNumber;
|
|
}
|
|
else
|
|
{
|
|
pNak = CONTAINING_RECORD (pReceive->pReceiver->NaksForwardDataList.Flink, tNAK_FORWARD_DATA, Linkage);
|
|
FirstNakSequenceNumber = pNak->SequenceNumber + pNak->NextIndexToIndicate;
|
|
}
|
|
|
|
if ((pReceive->SessionFlags & PGM_SESSION_TERMINATED_ABORT) ||
|
|
((pReceive->SessionFlags & PGM_SESSION_TERMINATED_GRACEFULLY) &&
|
|
(IsListEmpty (&pReceive->pReceiver->BufferedDataList)) &&
|
|
SEQ_GEQ (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_FLAG_IN_INDICATE | PGM_SESSION_CLIENT_DISCONNECTED);
|
|
|
|
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);
|
|
}
|
|
|
|
PgmTrace (LogStatus, ("CheckIndicateDisconnect: " \
|
|
"Disconnecting pReceive=<%p:%p>, with %s, FirstNak=<%d>, NextOData=<%d>\n",
|
|
pReceive, pReceive->ClientSessionContext,
|
|
(DisconnectFlag == TDI_DISCONNECT_RELEASE ? "TDI_DISCONNECT_RELEASE":"TDI_DISCONNECT_ABORT"),
|
|
FirstNakSequenceNumber, pReceive->pReceiver->NextODataSequenceNumber));
|
|
|
|
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,
|
|
(PVOID) FALSE,
|
|
FALSE))
|
|
{
|
|
CleanupPendingNaks (pReceive, (PVOID) TRUE, (PVOID) FALSE);
|
|
}
|
|
|
|
if (fAddressLockHeld)
|
|
{
|
|
PgmLock (pAddress, *pOldIrqAddress);
|
|
}
|
|
PgmLock (pReceive, *pOldIrqReceive);
|
|
|
|
pReceive->SessionFlags &= ~PGM_SESSION_FLAG_IN_INDICATE;
|
|
|
|
//
|
|
// We may have received a disassociate while disconnecting, so
|
|
// complete the irp here
|
|
//
|
|
if (pIrp = pReceive->pIrpDisassociate)
|
|
{
|
|
pReceive->pIrpDisassociate = NULL;
|
|
PgmUnlock (pReceive, *pOldIrqReceive);
|
|
if (fAddressLockHeld)
|
|
{
|
|
PgmUnlock (pAddress, *pOldIrqAddress);
|
|
}
|
|
|
|
PgmIoComplete (pIrp, STATUS_SUCCESS, 0);
|
|
|
|
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!
|
|
//
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"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;
|
|
status = STATUS_SUCCESS; // By default
|
|
|
|
do
|
|
{
|
|
if (pOptionHeader->OptionLength > BytesLeft)
|
|
{
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"Incorrectly formatted Options: OptionLength=<%d> > BytesLeft=<%d>, NumProcessed=<%d>\n",
|
|
pOptionHeader->OptionLength, BytesLeft, NumOptionsProcessed));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
|
|
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)))
|
|
{
|
|
PgmTrace (LogPath, ("ProcessOptions: " \
|
|
"NAK_LIST: Num Naks=<%d>\n", (pOptionHeader->OptionLength-4)/4));
|
|
|
|
if (!pNaksList)
|
|
{
|
|
ASSERT (0);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
break;
|
|
}
|
|
|
|
ProcessNakOption (pOptionHeader, pNaksList);
|
|
OptionsFlags |= PGM_OPTION_FLAG_NAK_LIST;
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"NAK_LIST: PacketType=<%x>, Length=<0x%x>, pPacketOptions=<%p>\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):
|
|
{
|
|
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;
|
|
|
|
OptionsFlags |= PGM_OPTION_FLAG_FRAGMENT;
|
|
}
|
|
else
|
|
{
|
|
MessageFirstSequence = ntohl (pOptionsData[0]);
|
|
MessageOffset = ntohl (pOptionsData[1]);
|
|
MessageLength = ntohl (pOptionsData[2]);
|
|
if ((MessageLength) && (MessageOffset <= MessageLength))
|
|
{
|
|
PgmTrace (LogPath, ("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;
|
|
}
|
|
|
|
OptionsFlags |= PGM_OPTION_FLAG_FRAGMENT;
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"FRAGMENT: MsgOffset/Length=<%d/%d>\n", MessageOffset, MessageLength));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"FRAGMENT: OptionLength=<%d> != PGM_PACKET_OPT_FRAGMENT_LENGTH=<%d>\n",
|
|
pOptionHeader->OptionLength, PGM_PACKET_OPT_FRAGMENT_LENGTH));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case (PACKET_OPTION_JOIN):
|
|
{
|
|
if (pOptionHeader->OptionLength == PGM_PACKET_OPT_JOIN_LENGTH)
|
|
{
|
|
PgmCopyMemory (pOptionsData, (pOptionHeader + 1), sizeof(ULONG));
|
|
PgmTrace (LogPath, ("ProcessOptions: " \
|
|
"JOIN: LateJoinerSeq=<%d>\n", ntohl (pOptionsData[0])));
|
|
|
|
if (pPacketOptions)
|
|
{
|
|
pPacketOptions->LateJoinerSequence = ntohl (pOptionsData[0]);
|
|
}
|
|
|
|
OptionsFlags |= PGM_OPTION_FLAG_JOIN;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_UNSUCCESSFUL;
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"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)
|
|
{
|
|
PgmTrace (LogPath, ("ProcessOptions: " \
|
|
"SYN\n"));
|
|
|
|
OptionsFlags |= PGM_OPTION_FLAG_SYN;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_UNSUCCESSFUL;
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"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)
|
|
{
|
|
PgmTrace (LogPath, ("ProcessOptions: " \
|
|
"FIN\n"));
|
|
|
|
OptionsFlags |= PGM_OPTION_FLAG_FIN;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_UNSUCCESSFUL;
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"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)
|
|
{
|
|
PgmTrace (LogPath, ("ProcessOptions: " \
|
|
"RST\n"));
|
|
|
|
OptionsFlags |= PGM_OPTION_FLAG_RST;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_UNSUCCESSFUL;
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"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));
|
|
PgmTrace (LogPath, ("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;
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"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));
|
|
PgmTrace (LogPath, ("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;
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"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));
|
|
if (pOptionsData[0])
|
|
{
|
|
PgmTrace (LogPath, ("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
|
|
{
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"CURR_TGSIZE: NumPacketsInThisGroup=<%d>\n", ntohl (pOptionsData[0])));
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_UNSUCCESSFUL;
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"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):
|
|
{
|
|
PgmTrace (LogStatus, ("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:
|
|
{
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"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);
|
|
|
|
} 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
|
|
{
|
|
PgmTrace (LogError, ("ProcessOptions: ERROR -- " \
|
|
"BytesLeft=<%d> + TotalOptionsLength=<%d> != BytesAvailable=<%d>\n",
|
|
BytesLeft, TotalOptionsLength, BytesAvailable));
|
|
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
}
|
|
}
|
|
|
|
PgmTrace (LogAllFuncs, ("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;
|
|
|
|
//
|
|
// Update the last consumed time if we did not have any data buffered prior
|
|
// to this
|
|
//
|
|
if (IsListEmpty (&pReceive->pReceiver->BufferedDataList))
|
|
{
|
|
pReceive->pReceiver->LastDataConsumedTime = PgmDynamicConfig.ReceiversTimerTickCount;
|
|
}
|
|
|
|
//
|
|
// 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)
|
|
{
|
|
PgmTrace (LogAllFuncs, ("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));
|
|
|
|
FreeDataBuffer (pReceive, &pNak->pPendingData[i]);
|
|
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
|
|
)
|
|
{
|
|
tRECEIVE_CONTEXT *pReceiver = pReceive->pReceiver;
|
|
SEQ_TYPE SequencesInWindow = 1 + LeadingEdgeSeqNumber - TrailingEdgeSeqNumber;
|
|
|
|
if (SEQ_GT (SequencesInWindow, pReceiver->MaxSequencesInWindow))
|
|
{
|
|
pReceiver->MaxSequencesInWindow = SequencesInWindow;
|
|
}
|
|
|
|
if (TrailingEdgeSeqNumber)
|
|
{
|
|
if ((!pReceiver->MinSequencesInWindow) ||
|
|
SEQ_LT (SequencesInWindow, pReceiver->MinSequencesInWindow))
|
|
{
|
|
pReceiver->MinSequencesInWindow = SequencesInWindow;
|
|
}
|
|
|
|
pReceiver->StatSumOfWindowSeqs += SequencesInWindow;
|
|
pReceiver->NumWindowSamples++;
|
|
}
|
|
}
|
|
|
|
VOID
|
|
UpdateSampleTimeWindowInformation(
|
|
IN tRECEIVE_SESSION *pReceive
|
|
)
|
|
{
|
|
ULONGLONG NcfRDataTimeout;
|
|
tRECEIVE_CONTEXT *pReceiver = pReceive->pReceiver;
|
|
|
|
//
|
|
// No need to update if there is no data
|
|
//
|
|
if (!pReceive->MaxRateKBitsPerSec ||
|
|
!pReceive->TotalPacketsReceived) // Avoid divide by 0 error
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Now, update the window information
|
|
//
|
|
if (pReceiver->NumWindowSamples)
|
|
{
|
|
pReceiver->AverageSequencesInWindow = pReceiver->StatSumOfWindowSeqs /
|
|
pReceiver->NumWindowSamples;
|
|
}
|
|
|
|
if (pReceiver->AverageSequencesInWindow)
|
|
{
|
|
pReceiver->WindowSizeLastInMSecs = ((pReceiver->AverageSequencesInWindow *
|
|
pReceive->TotalBytes) << LOG2_BITS_PER_BYTE) /
|
|
(pReceive->TotalPacketsReceived *
|
|
pReceive->MaxRateKBitsPerSec);
|
|
}
|
|
else
|
|
{
|
|
pReceiver->WindowSizeLastInMSecs = ((pReceiver->MaxSequencesInWindow *
|
|
pReceive->TotalBytes) << LOG2_BITS_PER_BYTE) /
|
|
(pReceive->TotalPacketsReceived *
|
|
pReceive->MaxRateKBitsPerSec);
|
|
}
|
|
pReceiver->MaxRDataResponseTCFromWindow = pReceiver->WindowSizeLastInMSecs /
|
|
(NCF_WAITING_RDATA_MAX_RETRIES * BASIC_TIMER_GRANULARITY_IN_MSECS);
|
|
|
|
PgmTrace (LogPath, ("UpdateSampleTimeWindowInformation: " \
|
|
"pReceive=<%p>, MaxRate=<%I64d>, AvgSeqsInWindow=<%I64d>, WinSzinMSecsLast=<%I64d>\n",
|
|
pReceive, pReceive->MaxRateKBitsPerSec, pReceiver->AverageSequencesInWindow, pReceiver->WindowSizeLastInMSecs));
|
|
|
|
//
|
|
// Now, update the NcfRData timeout information
|
|
//
|
|
if (pReceiver->StatSumOfNcfRDataTicks &&
|
|
pReceiver->NumNcfRDataTicksSamples)
|
|
{
|
|
pReceiver->AverageNcfRDataResponseTC = pReceiver->StatSumOfNcfRDataTicks /
|
|
pReceiver->NumNcfRDataTicksSamples;
|
|
}
|
|
|
|
if (pReceiver->AverageNcfRDataResponseTC)
|
|
{
|
|
NcfRDataTimeout = (pReceiver->AverageNcfRDataResponseTC +
|
|
pReceiver->MaxOutstandingNakTimeout) >> 1;
|
|
if (NcfRDataTimeout > (pReceiver->AverageNcfRDataResponseTC << 1))
|
|
{
|
|
NcfRDataTimeout = pReceiver->AverageNcfRDataResponseTC << 1;
|
|
}
|
|
if (NcfRDataTimeout >
|
|
pReceiver->InitialOutstandingNakTimeout/BASIC_TIMER_GRANULARITY_IN_MSECS)
|
|
{
|
|
pReceiver->OutstandingNakTimeout = NcfRDataTimeout;
|
|
}
|
|
else
|
|
{
|
|
pReceiver->OutstandingNakTimeout = pReceiver->InitialOutstandingNakTimeout /
|
|
BASIC_TIMER_GRANULARITY_IN_MSECS;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
VOID
|
|
RemoveRedundantNaks(
|
|
IN tRECEIVE_SESSION *pReceive,
|
|
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;
|
|
}
|
|
|
|
FreeDataBuffer (pReceive, &pNak->pPendingData[i]);
|
|
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;
|
|
}
|
|
|
|
FreeDataBuffer (pReceive, &pNak->pPendingData[i]);
|
|
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 ((fEliminateExtraParityPackets) &&
|
|
(((pNak->NumDataPackets + pNak->NumParityPackets) >= pNak->PacketsInGroup) ||
|
|
((pNak->NextIndexToIndicate + pNak->NumDataPackets) >= pNak->PacketsInGroup)))
|
|
{
|
|
RemovePendingReceiverEntry (pNak);
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
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
|
|
//
|
|
PgmTrace (LogAllFuncs, ("PgmSendNakCompletion: " \
|
|
"SUCCEEDED\n"));
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogError, ("PgmSendNakCompletion: ERROR -- " \
|
|
"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')))))
|
|
{
|
|
PgmTrace (LogError, ("PgmSendNak: ERROR -- " \
|
|
"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->TSI.hPort);
|
|
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->TSI.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);
|
|
|
|
PgmTrace (LogPath, ("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++)
|
|
{
|
|
PgmTrace (LogPath, ("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,
|
|
FALSE);
|
|
|
|
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,
|
|
FALSE);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
PgmTrace (LogAllFuncs, ("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 tRECEIVE_CONTEXT *pReceiver,
|
|
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;
|
|
PGMLockHandle OldIrq, OldIrq1;
|
|
ULONG NumMissingPackets, PacketsInGroup, NumNaks, TotalSeqsNacked = 0;
|
|
BOOLEAN fSendSelectiveNak, fSendParityNak;
|
|
UCHAR i, j;
|
|
ULONG NumPendingNaks = 0;
|
|
ULONG NumOutstandingNaks = 0;
|
|
USHORT Index;
|
|
ULONG NakRandomBackoffMSecs, NakRepeatIntervalMSecs;
|
|
|
|
if ((!pReceiver->LastSpmSource) ||
|
|
((pReceiver->DataPacketsPendingNaks <= OUT_OF_ORDER_PACKETS_BEFORE_NAK) &&
|
|
((pReceiver->LastNakSendTime + (pReceive->pReceiver->InitialOutstandingNakTimeout>>2)) >
|
|
PgmDynamicConfig.ReceiversTimerTickCount)))
|
|
{
|
|
PgmTrace (LogPath, ("CheckSendPendingNaks: " \
|
|
"No Naks to send for pReceive=<%p>, LastSpmSource=<%x>, NumDataPackets=<%d>, LastSendTime=<%I64d>, Current=<%I64d>\n",
|
|
pReceive, pReceiver->LastSpmSource,
|
|
pReceiver->DataPacketsPendingNaks,
|
|
pReceiver->LastNakSendTime+(NAK_MIN_INITIAL_BACKOFF_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'))))
|
|
{
|
|
PgmTrace (LogError, ("CheckSendPendingNaks: ERROR -- " \
|
|
"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);
|
|
|
|
if (pAddress->Flags & PGM_ADDRESS_HIGH_SPEED_OPTIMIZED)
|
|
{
|
|
NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS_OPT;
|
|
NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS_OPT;
|
|
}
|
|
else
|
|
{
|
|
NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS;
|
|
NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS;
|
|
}
|
|
|
|
fSendSelectiveNak = fSendParityNak = FALSE;
|
|
pEntry = &pReceiver->PendingNaksList;
|
|
while ((pEntry = pEntry->Flink) != &pReceive->pReceiver->PendingNaksList)
|
|
{
|
|
pNak = CONTAINING_RECORD (pEntry, tNAK_FORWARD_DATA, SendNakLinkage);
|
|
NumMissingPackets = pNak->PacketsInGroup - (pNak->NumDataPackets + pNak->NumParityPackets);
|
|
|
|
ASSERT (NumMissingPackets);
|
|
//
|
|
// if this Nak is outside the trailing window, then we are hosed!
|
|
//
|
|
if (SEQ_GT (pReceiver->LastTrailingEdgeSeqNum, pNak->SequenceNumber))
|
|
{
|
|
PgmTrace (LogError, ("CheckSendPendingNaks: ERROR -- " \
|
|
"Sequence # [%d] out of trailing edge <%d>, NumNcfs received=<%d>\n",
|
|
(ULONG) pNak->SequenceNumber,
|
|
(ULONG) 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)
|
|
{
|
|
PgmTrace (LogError, ("CheckSendPendingNaks: ERROR -- " \
|
|
"Pending Nak for Sequence # [%d] Timed out! Num Naks sent=<%d>, Window=<%d--%d> ( %d seqs)\n",
|
|
(ULONG) pNak->SequenceNumber, pNak->WaitingNcfRetries,
|
|
(ULONG) pReceiver->LastTrailingEdgeSeqNum,
|
|
(ULONG) pReceiver->FurthestKnownGroupSequenceNumber,
|
|
(ULONG) (1+pReceiver->FurthestKnownGroupSequenceNumber-
|
|
pReceiver->LastTrailingEdgeSeqNum)));
|
|
pReceive->SessionFlags |= PGM_SESSION_FLAG_NAK_TIMED_OUT;
|
|
break;
|
|
}
|
|
|
|
if ((pNak->PacketsInGroup > 1) &&
|
|
(pReceive->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT) &&
|
|
((pNak->SequenceNumber != pReceiver->FurthestKnownGroupSequenceNumber) ||
|
|
(SEQ_GEQ (pReceiver->FurthestKnownSequenceNumber, (pNak->SequenceNumber+pReceive->FECGroupSize-1)))))
|
|
{
|
|
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 +
|
|
((NakRepeatIntervalMSecs + (NakRandomBackoffMSecs/NumMissingPackets)) /
|
|
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
|
TotalSeqsNacked += NumMissingPackets;
|
|
NumMissingPackets = 0;
|
|
}
|
|
else
|
|
{
|
|
NumNaks = 0;
|
|
if (pReceive->FECOptions)
|
|
{
|
|
if (pNak->SequenceNumber == pReceiver->FurthestKnownGroupSequenceNumber)
|
|
{
|
|
ASSERT (SEQ_GEQ (pReceiver->FurthestKnownSequenceNumber, pReceiver->FurthestKnownGroupSequenceNumber));
|
|
PacketsInGroup = pReceiver->FurthestKnownSequenceNumber - pNak->SequenceNumber + 1;
|
|
ASSERT (PacketsInGroup >= (ULONG) (pNak->NumDataPackets + pNak->NumParityPackets));
|
|
}
|
|
else
|
|
{
|
|
PacketsInGroup = pNak->PacketsInGroup;
|
|
}
|
|
NumMissingPackets = PacketsInGroup -
|
|
(pNak->NextIndexToIndicate +pNak->NumDataPackets +pNak->NumParityPackets);
|
|
|
|
ASSERT ((NumMissingPackets) ||
|
|
(pNak->SequenceNumber == pReceiver->FurthestKnownGroupSequenceNumber));
|
|
|
|
for (i=pNak->NextIndexToIndicate; i<PacketsInGroup; i++)
|
|
{
|
|
if (pNak->pPendingData[i].ActualIndexOfDataPacket >= pNak->OriginalGroupSize)
|
|
{
|
|
if (!pNak->pPendingData[i].NcfsReceivedForActualIndex)
|
|
{
|
|
pSelectiveNaks->Sequences[pSelectiveNaks->NumSequences++] = pNak->SequenceNumber+i;
|
|
NumNaks++;
|
|
}
|
|
|
|
if (pSelectiveNaks->NumSequences == (MAX_SEQUENCES_PER_NAK_OPTION+1))
|
|
{
|
|
if (!(pSelectiveNaks = PgmAllocMem (sizeof (tNAKS_CONTEXT), PGM_TAG('5'))))
|
|
{
|
|
PgmTrace (LogError, ("CheckSendPendingNaks: ERROR -- " \
|
|
"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);
|
|
}
|
|
|
|
if (!--NumMissingPackets)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT (!NumMissingPackets);
|
|
if (NumNaks)
|
|
{
|
|
TotalSeqsNacked += NumNaks;
|
|
}
|
|
else
|
|
{
|
|
pNak->WaitingNcfRetries--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pSelectiveNaks->Sequences[pSelectiveNaks->NumSequences++] = pNak->SequenceNumber;
|
|
TotalSeqsNacked++;
|
|
}
|
|
|
|
pNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
|
((NakRepeatIntervalMSecs + NakRandomBackoffMSecs) /
|
|
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
|
|
|
if (!pSelectiveNaks)
|
|
{
|
|
break;
|
|
}
|
|
|
|
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)
|
|
{
|
|
PgmTrace (LogError, ("CheckSendPendingNaks: ERROR -- " \
|
|
"Outstanding Nak for Sequence # [%d] Timed out!, Window=<%d--%d> ( %d seqs), Ncfs=<%d>, FirstNak=<%d>\n",
|
|
(ULONG) pNak->SequenceNumber, (ULONG) pReceiver->LastTrailingEdgeSeqNum,
|
|
(ULONG) pReceiver->FurthestKnownGroupSequenceNumber,
|
|
(ULONG) (1+pReceiver->FurthestKnownGroupSequenceNumber-pReceiver->LastTrailingEdgeSeqNum),
|
|
pNak->WaitingRDataRetries, (ULONG) pReceiver->FirstNakSequenceNumber));
|
|
|
|
pReceive->SessionFlags |= PGM_SESSION_FLAG_NAK_TIMED_OUT;
|
|
break;
|
|
}
|
|
|
|
pNak->WaitingNcfRetries = 0;
|
|
pNak->OutstandingNakTimeout = 0;
|
|
pNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
|
((NakRandomBackoffMSecs/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'))))
|
|
{
|
|
PgmTrace (LogError, ("CheckSendPendingNaks: ERROR -- " \
|
|
"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'))))
|
|
{
|
|
PgmTrace (LogError, ("CheckSendPendingNaks: ERROR -- " \
|
|
"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 (pReceive->SessionFlags & PGM_SESSION_TERMINATED_ABORT)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
pReceiver->NumPendingNaks = NumPendingNaks;
|
|
pReceiver->NumOutstandingNaks = NumOutstandingNaks;
|
|
|
|
if (!IsListEmpty (&NaksList))
|
|
{
|
|
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)))
|
|
{
|
|
PgmTrace (LogAllFuncs, ("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
|
|
CheckForSessionTimeout(
|
|
IN tRECEIVE_SESSION *pReceive,
|
|
IN tRECEIVE_CONTEXT *pReceiver
|
|
)
|
|
{
|
|
ULONG LastInterval;
|
|
|
|
LastInterval = (ULONG) (PgmDynamicConfig.ReceiversTimerTickCount -
|
|
pReceiver->LastSessionTickCount);
|
|
|
|
if ((LastInterval > MAX_SPM_INTERVAL_MSECS/BASIC_TIMER_GRANULARITY_IN_MSECS) &&
|
|
(LastInterval > (pReceiver->MaxSpmInterval << 5))) // (32 * MaxSpmInterval)
|
|
{
|
|
PgmTrace (LogError, ("ReceiveTimerTimeout: ERROR -- " \
|
|
"Disconnecting session because no SPM or Data packets received for <%d> Msecs\n",
|
|
(LastInterval * BASIC_TIMER_GRANULARITY_IN_MSECS)));
|
|
|
|
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
|
return;
|
|
}
|
|
|
|
LastInterval = (ULONG) (PgmDynamicConfig.ReceiversTimerTickCount -
|
|
pReceiver->LastDataConsumedTime);
|
|
if ((!IsListEmpty (&pReceiver->BufferedDataList)) &&
|
|
(LastInterval > MAX_DATA_CONSUMPTION_TIME_MSECS/BASIC_TIMER_GRANULARITY_IN_MSECS))
|
|
{
|
|
PgmTrace (LogError, ("ReceiveTimerTimeout: ERROR -- " \
|
|
"Disconnecting session because Data has not been consumed for <%I64x> Msecs\n",
|
|
(LastInterval * BASIC_TIMER_GRANULARITY_IN_MSECS)));
|
|
|
|
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
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;
|
|
LARGE_INTEGER DeltaTime, GranularTimeElapsed;
|
|
ULONG NumTimeouts;
|
|
BOOLEAN fReStartTimer = TRUE;
|
|
ULONGLONG BytesInLastInterval;
|
|
|
|
PgmLock (&PgmDynamicConfig, OldIrq);
|
|
|
|
ASSERT (!IsListEmpty (&PgmDynamicConfig.CurrentReceivers));
|
|
|
|
Now.QuadPart = KeQueryInterruptTime ();
|
|
DeltaTime.QuadPart = Now.QuadPart - PgmDynamicConfig.LastReceiverTimeout.QuadPart;
|
|
//
|
|
// If more than a certain number of timeouts have elapsed, we should skip the
|
|
// optimization since it could result in a big loop!
|
|
// Let's limit the optimization to 256 times the TimeoutGranularity for now.
|
|
//
|
|
if (DeltaTime.QuadPart > (PgmDynamicConfig.TimeoutGranularity.QuadPart << 8))
|
|
{
|
|
NumTimeouts = (ULONG) (DeltaTime.QuadPart / PgmDynamicConfig.TimeoutGranularity.QuadPart);
|
|
GranularTimeElapsed.QuadPart = NumTimeouts * PgmDynamicConfig.TimeoutGranularity.QuadPart;
|
|
}
|
|
else
|
|
{
|
|
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);
|
|
|
|
CheckForSessionTimeout (pReceive, pReceiver);
|
|
|
|
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
|
|
{
|
|
BytesInLastInterval = pReceive->TotalBytes - pReceive->TotalBytesAtLastInterval;
|
|
pReceive->RateKBitsPerSecOverall = (pReceive->TotalBytes << LOG2_BITS_PER_BYTE) /
|
|
((PgmDynamicConfig.ReceiversTimerTickCount-pReceiver->StartTickCount) * BASIC_TIMER_GRANULARITY_IN_MSECS);
|
|
|
|
pReceive->RateKBitsPerSecLast = BytesInLastInterval >>
|
|
(LOG2_INTERNAL_RATE_CALCULATION_FREQUENCY-LOG2_BITS_PER_BYTE);
|
|
|
|
if (pReceive->RateKBitsPerSecLast > pReceive->MaxRateKBitsPerSec)
|
|
{
|
|
pReceive->MaxRateKBitsPerSec = pReceive->RateKBitsPerSecLast;
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
pReceiver->StatSumOfWindowSeqs = pReceiver->NumWindowSamples = 0;
|
|
// pReceiver->StatSumOfNcfRDataTicks = pReceiver->NumNcfRDataTicksSamples = 0;
|
|
}
|
|
|
|
if (IsListEmpty (&pReceiver->PendingNaksList))
|
|
{
|
|
pReceiver->NumPendingNaks = 0;
|
|
pReceiver->NumOutstandingNaks = 0;
|
|
|
|
PgmUnlock (pReceive, OldIrq1);
|
|
|
|
PgmTrace (LogAllFuncs, ("ReceiveTimerTimeout: " \
|
|
"No pending Naks for pReceive=<%p>, Addr=<%x>\n",
|
|
pReceive, pReceiver->ListenMCastIpAddress));
|
|
}
|
|
else
|
|
{
|
|
PgmUnlock (pReceive, OldIrq1);
|
|
|
|
PgmTrace (LogAllFuncs, ("ReceiveTimerTimeout: " \
|
|
"Checking for pending Naks for pReceive=<%p>, Addr=<%x>\n",
|
|
pReceive, pReceiver->ListenMCastIpAddress));
|
|
|
|
CheckSendPendingNaks (pReceiver->pAddress, pReceive, pReceiver, &OldIrq);
|
|
}
|
|
}
|
|
else if (!(pReceive->SessionFlags & PGM_SESSION_FLAG_IN_INDICATE))
|
|
{
|
|
PgmTrace (LogStatus, ("ReceiveTimerTimeout: " \
|
|
"PGM_SESSION_ON_TIMER flag cleared for pReceive=<%p>, Addr=<%x>\n",
|
|
pReceive, pReceiver->ListenMCastIpAddress));
|
|
|
|
pEntry = pEntry->Blink;
|
|
RemoveEntryList (&pReceiver->Linkage);
|
|
|
|
if (IsListEmpty (&PgmDynamicConfig.CurrentReceivers))
|
|
{
|
|
fReStartTimer = FALSE;
|
|
PgmDynamicConfig.GlobalFlags &= ~PGM_CONFIG_FLAG_RECEIVE_TIMER_RUNNING;
|
|
|
|
PgmTrace (LogStatus, ("ReceiveTimerTimeout: " \
|
|
"Not restarting Timer since no Receivers currently active!\n"));
|
|
}
|
|
|
|
PgmUnlock (&PgmDynamicConfig, OldIrq1);
|
|
|
|
CheckIndicateDisconnect (pReceiver->pAddress, pReceive, NULL, &OldIrq1, FALSE);
|
|
|
|
PgmUnlock (pReceive, OldIrq);
|
|
|
|
PGM_DEREFERENCE_ADDRESS (pReceiver->pAddress, REF_ADDRESS_RECEIVE_ACTIVE);
|
|
PGM_DEREFERENCE_SESSION_RECEIVE (pReceive, REF_SESSION_TIMER_RUNNING);
|
|
|
|
if (!fReStartTimer)
|
|
{
|
|
return;
|
|
}
|
|
|
|
PgmLock (&PgmDynamicConfig, OldIrq);
|
|
}
|
|
else
|
|
{
|
|
PgmUnlock (pReceive, OldIrq1);
|
|
}
|
|
}
|
|
|
|
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,
|
|
OUT SEQ_TYPE *pLastSequenceNumber,
|
|
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;
|
|
USHORT ThisSequenceIndex;
|
|
SEQ_TYPE LastSequenceNumber, ThisSequenceNumber, ThisSequenceGroup;
|
|
SEQ_TYPE NextUnsentSequenceNumber, NextUnsentSequenceGroup;
|
|
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))
|
|
{
|
|
PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \
|
|
"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)
|
|
//
|
|
if (pLastSequenceNumber)
|
|
{
|
|
NextUnsentSequenceNumber = *pLastSequenceNumber;
|
|
}
|
|
else
|
|
{
|
|
NextUnsentSequenceNumber = FECGroupSize + pNakNcfList->pNakSequences[pNakNcfList->NumSequences-1];
|
|
}
|
|
NextUnsentSequenceGroup = NextUnsentSequenceNumber & FECGroupMask;
|
|
|
|
LastSequenceNumber = pNakNcfList->pNakSequences[0] - FECGroupSize;
|
|
for (i=0; i < pNakNcfList->NumSequences; i++)
|
|
{
|
|
ThisSequenceNumber = pNakNcfList->pNakSequences[i];
|
|
|
|
//
|
|
// If this is a parity Nak, then we need to separate the TG_SQN from the PKT_SQN
|
|
//
|
|
ThisSequenceGroup = ThisSequenceNumber & FECGroupMask;
|
|
ThisSequenceIndex = (USHORT) (ThisSequenceNumber & FECSequenceMask);
|
|
pNakNcfList->pNakSequences[i] = ThisSequenceGroup;
|
|
pNakNcfList->NakIndex[i] = ThisSequenceIndex;
|
|
|
|
PgmTrace (LogPath, ("ExtractNakNcfSequences: " \
|
|
"[%d] Sequence# = <%d> ==> [%d:%d]\n",
|
|
i, (ULONG) pNakNcfList->pNakSequences[i], ThisSequenceNumber, ThisSequenceIndex));
|
|
|
|
if (SEQ_LEQ (ThisSequenceNumber, LastSequenceNumber))
|
|
{
|
|
//
|
|
// This list is not ordered, so just bail!
|
|
//
|
|
PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \
|
|
"[%d] Unordered list! Sequence#<%d> before <%d>\n",
|
|
i, (ULONG) LastSequenceNumber, (ULONG) ThisSequenceNumber));
|
|
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
if (pNakNcfList->NakType == NAK_TYPE_SELECTIVE)
|
|
{
|
|
if (SEQ_LEQ (ThisSequenceNumber, LastSequenceNumber))
|
|
{
|
|
//
|
|
// This list is not ordered, so just bail!
|
|
//
|
|
PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \
|
|
"[%d] Unordered Selective list! Sequence#<%d> before <%d>\n",
|
|
i, (ULONG) LastSequenceNumber, (ULONG) ThisSequenceNumber));
|
|
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
if (SEQ_GEQ (ThisSequenceNumber, NextUnsentSequenceNumber))
|
|
{
|
|
pNakNcfList->NumSequences = (USHORT) i; // Don't want to include this sequence!
|
|
|
|
PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \
|
|
"Invalid Selective Nak = [%d] further than leading edge = [%d]\n",
|
|
(ULONG) ThisSequenceNumber, (ULONG) NextUnsentSequenceNumber));
|
|
|
|
break;
|
|
}
|
|
|
|
LastSequenceNumber = ThisSequenceNumber;
|
|
}
|
|
else // pNakNcfList->NakType == NAK_TYPE_PARITY
|
|
{
|
|
if (SEQ_LEQ (ThisSequenceGroup, LastSequenceNumber))
|
|
{
|
|
//
|
|
// This list is not ordered, so just bail!
|
|
//
|
|
PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \
|
|
"[%d] Unordered Parity list! Sequence#<%d> before <%d>\n",
|
|
i, (ULONG) LastSequenceNumber, (ULONG) ThisSequenceNumber));
|
|
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
if (SEQ_GEQ (ThisSequenceGroup, NextUnsentSequenceGroup))
|
|
{
|
|
pNakNcfList->NumSequences = (USHORT) i; // Don't want to include this sequence!
|
|
|
|
PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \
|
|
"Invalid Parity Nak = [%d] further than leading edge = [%d]\n",
|
|
(ULONG) ThisSequenceGroup, (ULONG) NextUnsentSequenceGroup));
|
|
|
|
break;
|
|
}
|
|
|
|
LastSequenceNumber = ThisSequenceGroup;
|
|
pNakNcfList->NumParityNaks[i]++;
|
|
}
|
|
}
|
|
|
|
if (!pNakNcfList->NumSequences)
|
|
{
|
|
PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \
|
|
"No Valid %s Naks in List, First Nak=<%d>!\n",
|
|
(pNakNcfList->NakType == NAK_TYPE_PARITY ? "Parity" : "Selective"),
|
|
(ULONG) ThisSequenceNumber));
|
|
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
if (pLastSequenceNumber)
|
|
{
|
|
*pLastSequenceNumber = LastSequenceNumber;
|
|
}
|
|
|
|
if (pNakNcfList->NumSequences)
|
|
{
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
return (STATUS_UNSUCCESSFUL);
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
CheckAndAddNakRequests(
|
|
IN tRECEIVE_SESSION *pReceive,
|
|
IN SEQ_TYPE *pLatestSequenceNumber,
|
|
OUT tNAK_FORWARD_DATA **ppThisNak,
|
|
IN enum eNAK_TIMEOUT NakTimeoutType,
|
|
IN BOOLEAN fSetFurthestKnown
|
|
)
|
|
{
|
|
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;
|
|
ULONG NakRandomBackoffMSecs, NakRepeatIntervalMSecs;
|
|
tRECEIVE_CONTEXT *pReceiver = pReceive->pReceiver;
|
|
|
|
//
|
|
// Verify that the FurthestKnownGroupSequenceNumber is on a Group boundary
|
|
//
|
|
ASSERT (!(FurthestGroupSequenceNumber & FECGroupMask));
|
|
|
|
if (SEQ_LT (ThisSequenceNumber, pReceiver->FirstNakSequenceNumber))
|
|
{
|
|
if (ppThisNak)
|
|
{
|
|
ASSERT (0);
|
|
*ppThisNak = NULL;
|
|
}
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
if (SEQ_GT (ThisGroupSequenceNumber, (pReceiver->NextODataSequenceNumber + MAX_SEQUENCES_IN_RCV_WINDOW)))
|
|
{
|
|
PgmTrace (LogError, ("CheckAndAddNakRequests: ERROR -- " \
|
|
"STATUS_INSUFFICIENT_RESOURCES -- Too many packets in Window [%d, %d] = %d Sequences\n",
|
|
(ULONG) pReceiver->NextODataSequenceNumber,
|
|
(ULONG) ThisGroupSequenceNumber,
|
|
(ULONG) (ThisGroupSequenceNumber - pReceiver->NextODataSequenceNumber + 1)));
|
|
|
|
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
if (SEQ_GT (ThisGroupSequenceNumber, (FurthestGroupSequenceNumber + 1000)) &&
|
|
!(pReceive->SessionFlags & PGM_SESSION_FLAG_FIRST_PACKET))
|
|
{
|
|
PgmTrace (LogStatus, ("CheckAndAddNakRequests: " \
|
|
"WARNING!!! Too many successive packets lost =<%d>!!! Expecting Next=<%d>, FurthestKnown=<%d>, This=<%d>\n",
|
|
(ULONG) (ThisGroupSequenceNumber - FurthestGroupSequenceNumber),
|
|
(ULONG) pReceiver->FirstNakSequenceNumber,
|
|
(ULONG) FurthestGroupSequenceNumber,
|
|
(ULONG) ThisGroupSequenceNumber));
|
|
}
|
|
|
|
if (pReceiver->pAddress->Flags & PGM_ADDRESS_HIGH_SPEED_OPTIMIZED)
|
|
{
|
|
NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS_OPT;
|
|
NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS_OPT;
|
|
}
|
|
else
|
|
{
|
|
NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS;
|
|
NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS;
|
|
}
|
|
|
|
//
|
|
// 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 (&pReceiver->ParityContextLookaside);
|
|
}
|
|
else
|
|
{
|
|
pLastNak = ExAllocateFromNPagedLookasideList (&pReceiver->NonParityContextLookaside);
|
|
}
|
|
|
|
if (!pLastNak)
|
|
{
|
|
pReceiver->FurthestKnownGroupSequenceNumber = FurthestGroupSequenceNumber;
|
|
pReceiver->FurthestKnownSequenceNumber = pReceiver->FurthestKnownSequenceNumber + FECGroupMask; // End of prev group
|
|
|
|
PgmTrace (LogError, ("ExtractNakNcfSequences: ERROR -- " \
|
|
"STATUS_INSUFFICIENT_RESOURCES allocating tNAK_FORWARD_DATA, Size=<%d>, Seq=<%d>\n",
|
|
NakRequestSize, (ULONG) pReceiver->FurthestKnownGroupSequenceNumber));
|
|
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
PgmZeroMemory (pLastNak, NakRequestSize);
|
|
|
|
if (pReceive->FECOptions)
|
|
{
|
|
pLastNak->OriginalGroupSize = pLastNak->PacketsInGroup = pReceive->FECGroupSize;
|
|
for (i=0; i<pLastNak->OriginalGroupSize; i++)
|
|
{
|
|
pLastNak->pPendingData[i].ActualIndexOfDataPacket = pLastNak->OriginalGroupSize;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pLastNak->OriginalGroupSize = pLastNak->PacketsInGroup = 1;
|
|
pLastNak->pPendingData[0].ActualIndexOfDataPacket = 1;
|
|
}
|
|
|
|
FurthestGroupSequenceNumber += pReceive->FECGroupSize;
|
|
pLastNak->SequenceNumber = FurthestGroupSequenceNumber;
|
|
pLastNak->MinPacketLength = pReceive->MaxFECPacketLength;
|
|
|
|
if (NakTimeoutType == NAK_OUTSTANDING)
|
|
{
|
|
pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
|
pReceiver->OutstandingNakTimeout;
|
|
pLastNak->PendingNakTimeout = 0;
|
|
pLastNak->WaitingNcfRetries = 0;
|
|
}
|
|
else
|
|
{
|
|
pLastNak->OutstandingNakTimeout = 0;
|
|
switch (NakTimeoutType)
|
|
{
|
|
case (NAK_PENDING_0):
|
|
{
|
|
pLastNak->PendingNakTimeout = Pending0NakTimeout;
|
|
break;
|
|
}
|
|
|
|
case (NAK_PENDING_RB):
|
|
{
|
|
pLastNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
|
((NakRandomBackoffMSecs/pReceive->FECGroupSize) /
|
|
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
|
|
|
break;
|
|
}
|
|
|
|
case (NAK_PENDING_RPT_RB):
|
|
{
|
|
pLastNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
|
((NakRepeatIntervalMSecs +(NakRandomBackoffMSecs/pReceive->FECGroupSize))/
|
|
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
ASSERT (0);
|
|
}
|
|
}
|
|
}
|
|
|
|
InsertTailList (&pReceiver->NaksForwardDataList, &pLastNak->Linkage);
|
|
AppendPendingReceiverEntry (pReceiver, pLastNak);
|
|
|
|
PgmTrace (LogPath, ("CheckAndAddNakRequests: " \
|
|
"ADDing NAK request for SeqNum=<%d>, Furthest=<%d>\n",
|
|
(ULONG) pLastNak->SequenceNumber, (ULONG) FurthestGroupSequenceNumber));
|
|
}
|
|
|
|
pReceiver->FurthestKnownGroupSequenceNumber = FurthestGroupSequenceNumber;
|
|
if (SEQ_GT (ThisSequenceNumber, pReceiver->FurthestKnownSequenceNumber))
|
|
{
|
|
if (fSetFurthestKnown)
|
|
{
|
|
pReceiver->FurthestKnownSequenceNumber = ThisSequenceNumber;
|
|
}
|
|
else if (SEQ_GT (FurthestGroupSequenceNumber, pReceiver->FurthestKnownSequenceNumber))
|
|
{
|
|
pReceiver->FurthestKnownSequenceNumber = FurthestGroupSequenceNumber + FECGroupMask;
|
|
}
|
|
}
|
|
|
|
if (pLastNak)
|
|
{
|
|
pLastNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
|
NakRepeatIntervalMSecs / BASIC_TIMER_GRANULARITY_IN_MSECS;
|
|
}
|
|
else if ((ppThisNak) && (!IsListEmpty (&pReceiver->NaksForwardDataList)))
|
|
{
|
|
pLastNak = FindReceiverEntry (pReceiver, ThisGroupSequenceNumber);
|
|
ASSERT (!pLastNak || (pLastNak->SequenceNumber == ThisGroupSequenceNumber));
|
|
}
|
|
|
|
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 PacketsInGroup, NumMissingPackets;
|
|
ULONG NakRandomBackoffMSecs, NakRepeatIntervalMSecs;
|
|
BOOLEAN fValidNcf, fFECWithSelectiveNaksOnly = FALSE;
|
|
tRECEIVE_CONTEXT *pReceiver = pReceive->pReceiver;
|
|
|
|
ASSERT (!pNakNcfPacket->CommonHeader.TSDULength);
|
|
|
|
PgmZeroMemory (&NakNcfList, sizeof (tNAKS_LIST));
|
|
PgmLock (pReceive, OldIrq);
|
|
|
|
status = ExtractNakNcfSequences (pNakNcfPacket,
|
|
(PacketLength - sizeof(tBASIC_NAK_NCF_PACKET_HEADER)),
|
|
&NakNcfList,
|
|
NULL,
|
|
pReceive->FECGroupSize);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmUnlock (pReceive, OldIrq);
|
|
PgmTrace (LogError, ("ReceiverProcessNakNcfPacket: ERROR -- " \
|
|
"ExtractNakNcfSequences returned <%x>\n", status));
|
|
|
|
return (status);
|
|
}
|
|
|
|
PgmTrace (LogAllFuncs, ("ReceiverProcessNakNcfPacket: " \
|
|
"NumSequences=[%d] Range=<%d--%d>, Furthest=<%d>\n",
|
|
NakNcfList.NumSequences,
|
|
(ULONG) NakNcfList.pNakSequences[0], (ULONG) NakNcfList.pNakSequences[NakNcfList.NumSequences-1],
|
|
(ULONG) pReceiver->FurthestKnownGroupSequenceNumber));
|
|
|
|
//
|
|
// Compares apples to apples and oranges to oranges
|
|
// i.e. Process parity Naks only if we are parity-aware, and vice-versa
|
|
// Exception is ofr the case of a selective Ncf received when we have OnDemand parity
|
|
//
|
|
if ((pReceiver->SessionNakType == NakNcfList.NakType) ||
|
|
((PacketType == PACKET_TYPE_NCF) &&
|
|
(NakNcfList.NakType == NAK_TYPE_SELECTIVE)))
|
|
{
|
|
if (pAddress->Flags & PGM_ADDRESS_HIGH_SPEED_OPTIMIZED)
|
|
{
|
|
NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS_OPT;
|
|
NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS_OPT;
|
|
}
|
|
else
|
|
{
|
|
NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS;
|
|
NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PgmUnlock (pReceive, OldIrq);
|
|
PgmTrace (LogPath, ("ReceiverProcessNakNcfPacket: " \
|
|
"Received a %s Nak! Not processing ... \n",
|
|
((pReceive->FECGroupSize > 1) ? "Non-parity" : "Parity")));
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
i = 0;
|
|
FECGroupMask = pReceive->FECGroupSize - 1;
|
|
|
|
//
|
|
// Special case: If we have FEC enabled, but not with OnDemand parity,
|
|
// then we will process Ncf requests only
|
|
//
|
|
fFECWithSelectiveNaksOnly = pReceive->FECOptions &&
|
|
!(pReceive->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT);
|
|
|
|
if ((PacketType == PACKET_TYPE_NAK) &&
|
|
(fFECWithSelectiveNaksOnly))
|
|
{
|
|
i = NakNcfList.NumSequences;
|
|
}
|
|
|
|
for ( ; i <NakNcfList.NumSequences; i++)
|
|
{
|
|
LastSequenceNumber = NakNcfList.pNakSequences[i];
|
|
pLastNak = FindReceiverEntry (pReceiver, LastSequenceNumber);
|
|
|
|
if (pLastNak)
|
|
{
|
|
ASSERT (pLastNak->SequenceNumber == LastSequenceNumber);
|
|
if ((pReceive->FECOptions) &&
|
|
(LastSequenceNumber == pReceiver->FurthestKnownGroupSequenceNumber))
|
|
{
|
|
ASSERT (SEQ_GEQ (pReceiver->FurthestKnownSequenceNumber, pReceiver->FurthestKnownGroupSequenceNumber));
|
|
PacketsInGroup = pReceiver->FurthestKnownSequenceNumber - pLastNak->SequenceNumber + 1;
|
|
ASSERT (PacketsInGroup >= (ULONG) (pLastNak->NumDataPackets + pLastNak->NumParityPackets));
|
|
}
|
|
else
|
|
{
|
|
PacketsInGroup = pLastNak->PacketsInGroup;
|
|
}
|
|
NumMissingPackets = PacketsInGroup - (pLastNak->NextIndexToIndicate + pLastNak->NumDataPackets + pLastNak->NumParityPackets);
|
|
}
|
|
|
|
if ((!pLastNak) ||
|
|
(!NumMissingPackets))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
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 +
|
|
((NakRepeatIntervalMSecs + (NakRandomBackoffMSecs/NumMissingPackets))/
|
|
BASIC_TIMER_GRANULARITY_IN_MSECS);
|
|
}
|
|
else
|
|
{
|
|
ASSERT (pLastNak->OutstandingNakTimeout);
|
|
|
|
pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
|
(pReceiver->OutstandingNakTimeout <<
|
|
pLastNak->WaitingRDataRetries);
|
|
|
|
if ((pLastNak->WaitingRDataRetries >= (NCF_WAITING_RDATA_MAX_RETRIES >> 1)) &&
|
|
((pReceiver->OutstandingNakTimeout << pLastNak->WaitingRDataRetries) <
|
|
pReceiver->MaxRDataResponseTCFromWindow))
|
|
{
|
|
pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
|
(pReceiver->MaxRDataResponseTCFromWindow<<1);
|
|
}
|
|
}
|
|
}
|
|
else // NCF case
|
|
{
|
|
PacketIndex = NakNcfList.NakIndex[i];
|
|
fValidNcf = FALSE;
|
|
|
|
// first check for OnDemand case
|
|
if (pReceive->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT)
|
|
{
|
|
// We may also need to process selective naks in certain cases
|
|
if (NakNcfList.NakType == NAK_TYPE_SELECTIVE)
|
|
{
|
|
fValidNcf = TRUE;
|
|
|
|
pLastNak->pPendingData[PacketIndex].NcfsReceivedForActualIndex++;
|
|
for (j=0; j<PacketsInGroup; j++)
|
|
{
|
|
if ((pLastNak->pPendingData[j].ActualIndexOfDataPacket >= pLastNak->OriginalGroupSize) &&
|
|
(!pLastNak->pPendingData[j].NcfsReceivedForActualIndex))
|
|
{
|
|
fValidNcf = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!pLastNak->FirstNcfTickCount)
|
|
{
|
|
pLastNak->FirstNcfTickCount = PgmDynamicConfig.ReceiversTimerTickCount;
|
|
}
|
|
}
|
|
else if (NakNcfList.NumParityNaks[i] >= NumMissingPackets) // Parity Nak
|
|
{
|
|
fValidNcf = TRUE;
|
|
|
|
if (!pLastNak->FirstNcfTickCount)
|
|
{
|
|
pLastNak->FirstNcfTickCount = PgmDynamicConfig.ReceiversTimerTickCount;
|
|
}
|
|
}
|
|
}
|
|
// Selective Naks only -- with or without FEC
|
|
else if (pLastNak->pPendingData[PacketIndex].ActualIndexOfDataPacket >= pLastNak->OriginalGroupSize)
|
|
{
|
|
fValidNcf = TRUE;
|
|
if (fFECWithSelectiveNaksOnly)
|
|
{
|
|
pLastNak->pPendingData[PacketIndex].NcfsReceivedForActualIndex++;
|
|
for (j=0; j<pLastNak->PacketsInGroup; j++)
|
|
{
|
|
if ((pLastNak->pPendingData[j].ActualIndexOfDataPacket >= pLastNak->OriginalGroupSize) &&
|
|
(!pLastNak->pPendingData[j].NcfsReceivedForActualIndex))
|
|
{
|
|
fValidNcf = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!pLastNak->FirstNcfTickCount)
|
|
{
|
|
pLastNak->FirstNcfTickCount = PgmDynamicConfig.ReceiversTimerTickCount;
|
|
}
|
|
}
|
|
|
|
if (fValidNcf)
|
|
{
|
|
pLastNak->PendingNakTimeout = 0;
|
|
pLastNak->WaitingNcfRetries = 0;
|
|
|
|
pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
|
(pReceiver->OutstandingNakTimeout <<
|
|
pLastNak->WaitingRDataRetries);
|
|
|
|
if ((pLastNak->WaitingRDataRetries >= (NCF_WAITING_RDATA_MAX_RETRIES >> 1)) &&
|
|
((pReceiver->OutstandingNakTimeout << pLastNak->WaitingRDataRetries) <
|
|
pReceiver->MaxRDataResponseTCFromWindow))
|
|
{
|
|
pLastNak->OutstandingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
|
(pReceiver->MaxRDataResponseTCFromWindow<<1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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
|
|
//
|
|
LastSequenceNumber = NakNcfList.pNakSequences[NakNcfList.NumSequences-1] + NakNcfList.NakIndex[NakNcfList.NumSequences-1];
|
|
if (NakNcfList.NakType == NAK_TYPE_PARITY)
|
|
{
|
|
LastSequenceNumber--;
|
|
}
|
|
|
|
if (PacketType == PACKET_TYPE_NAK)
|
|
{
|
|
status = CheckAndAddNakRequests (pReceive, &LastSequenceNumber, NULL, NAK_PENDING_RPT_RB, TRUE);
|
|
}
|
|
else // PacketType == PACKET_TYPE_NCF
|
|
{
|
|
status = CheckAndAddNakRequests (pReceive, &LastSequenceNumber, NULL, NAK_OUTSTANDING, TRUE);
|
|
}
|
|
|
|
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 FurthestKnownSequenceNumber;
|
|
SEQ_TYPE GroupMask = GroupSize - 1;
|
|
ULONG NakRequestSize = sizeof(tNAK_FORWARD_DATA) + ((GroupSize-1) * sizeof(tPENDING_DATA));
|
|
USHORT MinPacketLength;
|
|
UCHAR i;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG NakRandomBackoffMSecs, NakRepeatIntervalMSecs;
|
|
|
|
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;
|
|
FurthestKnownSequenceNumber = pReceive->pReceiver->FurthestKnownSequenceNumber; // Save
|
|
|
|
//
|
|
// 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, FALSE);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmTrace (LogError, ("CoalesceSelectiveNaksIntoGroups: ERROR -- " \
|
|
"CheckAndAddNakRequests returned <%x>\n", status));
|
|
|
|
return (status);
|
|
}
|
|
pReceive->pReceiver->FurthestKnownSequenceNumber = FurthestKnownSequenceNumber; // Reset
|
|
|
|
ASSERT (LastSequenceNumber == pReceive->pReceiver->FurthestKnownGroupSequenceNumber);
|
|
ASSERT (pReceive->pReceiver->MaxPacketsBufferedInLookaside);
|
|
ExInitializeNPagedLookasideList (&pReceive->pReceiver->ParityContextLookaside,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
NakRequestSize,
|
|
PGM_TAG('2'),
|
|
(USHORT) (pReceive->pReceiver->MaxPacketsBufferedInLookaside/GroupSize));
|
|
|
|
if (SEQ_GT (pReceive->pReceiver->FirstNakSequenceNumber, LastSequenceNumber))
|
|
{
|
|
pReceive->pReceiver->FurthestKnownGroupSequenceNumber = LastGroupSequenceNumber;
|
|
|
|
ASSERT (IsListEmpty (&pReceive->pReceiver->NaksForwardDataList));
|
|
|
|
PgmTrace (LogStatus, ("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);
|
|
}
|
|
|
|
if (pReceive->pReceiver->pAddress->Flags & PGM_ADDRESS_HIGH_SPEED_OPTIMIZED)
|
|
{
|
|
NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS_OPT;
|
|
NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS_OPT;
|
|
}
|
|
else
|
|
{
|
|
NakRandomBackoffMSecs = NAK_RANDOM_BACKOFF_MSECS;
|
|
NakRepeatIntervalMSecs = NAK_REPEAT_INTERVAL_MSECS;
|
|
}
|
|
|
|
//
|
|
// 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)))
|
|
{
|
|
PgmTrace (LogError, ("CoalesceSelectiveNaksIntoGroups: ERROR -- " \
|
|
"STATUS_INSUFFICIENT_RESOURCES allocating tNAK_FORWARD_DATA\n"));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
break;
|
|
}
|
|
PgmZeroMemory (pNewNak, NakRequestSize);
|
|
InitializeListHead (&pNewNak->SendNakLinkage);
|
|
InitializeListHead (&pNewNak->LookupLinkage);
|
|
|
|
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)
|
|
{
|
|
RemovePendingReceiverEntry (pOldNak);
|
|
}
|
|
else
|
|
{
|
|
ASSERT (pOldNak->NumDataPackets == 1);
|
|
ASSERT (IsListEmpty (&pOldNak->SendNakLinkage));
|
|
ASSERT (IsListEmpty (&pOldNak->LookupLinkage));
|
|
}
|
|
|
|
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->pPendingData[0].PendingDataFlags = 0;
|
|
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 (pReceive, pNewNak, FALSE);
|
|
|
|
ASSERT (!pNewNak->NumParityPackets);
|
|
if (pNewNak->NumDataPackets < pNewNak->PacketsInGroup) // No parity packets yet!
|
|
{
|
|
pNewNak->PendingNakTimeout = PgmDynamicConfig.ReceiversTimerTickCount +
|
|
((NakRandomBackoffMSecs/(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->pPendingData[0].PendingDataFlags = 0;
|
|
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 (pReceive, 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 +
|
|
((NakRandomBackoffMSecs/(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
|
|
//
|
|
pReceive->pReceiver->ReceiveDataIndexShift = gFECLog2[GroupSize];
|
|
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))
|
|
{
|
|
AppendPendingReceiverEntry (pReceive->pReceiver, pNewNak);
|
|
}
|
|
}
|
|
|
|
AdjustReceiveBufferLists (pReceive);
|
|
|
|
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 +
|
|
((NakRepeatIntervalMSecs +
|
|
(NakRandomBackoffMSecs /
|
|
(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);
|
|
}
|
|
|
|
//
|
|
// Now, set the FirstKnownGroupSequenceNumber
|
|
//
|
|
if (pNewNak)
|
|
{
|
|
pReceive->pReceiver->FurthestKnownGroupSequenceNumber = pNewNak->SequenceNumber;
|
|
}
|
|
else
|
|
{
|
|
pReceive->pReceiver->FurthestKnownGroupSequenceNumber &= ~GroupMask;
|
|
}
|
|
|
|
PgmTrace (LogStatus, ("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); // If we had failed earlier, we should still fail!
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
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
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION pIrpSp;
|
|
PTDI_REQUEST_KERNEL_RECEIVE pClientParams;
|
|
CONNECTION_CONTEXT ClientSessionContext;
|
|
PIRP pIrpReceive;
|
|
ULONG ReceiveFlags, BytesTaken, BytesToCopy, BytesLeftInMessage, ClientBytesTaken;
|
|
tRECEIVE_CONTEXT *pReceiver = pReceive->pReceiver;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PTDI_IND_RECEIVE evReceive = NULL;
|
|
PVOID RcvEvContext = NULL;
|
|
ULONG BytesAvailableToIndicate = BytesAvailable;
|
|
|
|
if (pReceive->SessionFlags & (PGM_SESSION_CLIENT_DISCONNECTED |
|
|
PGM_SESSION_TERMINATED_ABORT))
|
|
{
|
|
PgmTrace (LogError, ("PgmIndicateToClient: ERROR -- " \
|
|
"pReceive=<%p> disassociated during Receive!\n", pReceive));
|
|
|
|
*pBytesTaken = 0;
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
ASSERT ((!pReceiver->CurrentMessageLength) || (pReceiver->CurrentMessageLength == MessageLength));
|
|
ASSERT (pReceiver->CurrentMessageProcessed == MessageOffset);
|
|
|
|
pReceiver->CurrentMessageLength = MessageLength;
|
|
pReceiver->CurrentMessageProcessed = MessageOffset;
|
|
|
|
BytesLeftInMessage = MessageLength - MessageOffset;
|
|
|
|
PgmTrace (LogAllFuncs, ("PgmIndicateToClient: " \
|
|
"MessageLen=<%d/%d>, MessageOff=<%d>, CurrentML=<%d>, CurrentMP=<%d>\n",
|
|
BytesAvailableToIndicate, MessageLength, MessageOffset,
|
|
pReceiver->CurrentMessageLength, pReceiver->CurrentMessageProcessed));
|
|
|
|
//
|
|
// We may have a receive Irp pending from a previous indication,
|
|
// so see if need to fill that first!
|
|
//
|
|
while ((BytesAvailableToIndicate) &&
|
|
((pIrpReceive = pReceiver->pIrpReceive) ||
|
|
(!IsListEmpty (&pReceiver->ReceiveIrpsList))))
|
|
{
|
|
if (!pIrpReceive)
|
|
{
|
|
//
|
|
// The client had posted a receive Irp, so use it now!
|
|
//
|
|
pIrpReceive = CONTAINING_RECORD (pReceiver->ReceiveIrpsList.Flink,
|
|
IRP, Tail.Overlay.ListEntry);
|
|
RemoveEntryList (&pIrpReceive->Tail.Overlay.ListEntry);
|
|
|
|
pIrpSp = IoGetCurrentIrpStackLocation (pIrpReceive);
|
|
pClientParams = (PTDI_REQUEST_KERNEL_RECEIVE) &pIrpSp->Parameters;
|
|
|
|
pReceiver->pIrpReceive = pIrpReceive;
|
|
pReceiver->TotalBytesInMdl = pClientParams->ReceiveLength;
|
|
pReceiver->BytesInMdl = 0;
|
|
}
|
|
|
|
//
|
|
// Copy whatever bytes we can into it
|
|
//
|
|
if (BytesAvailableToIndicate >
|
|
(pReceiver->TotalBytesInMdl - pReceiver->BytesInMdl))
|
|
{
|
|
BytesToCopy = pReceiver->TotalBytesInMdl - pReceiver->BytesInMdl;
|
|
}
|
|
else
|
|
{
|
|
BytesToCopy = BytesAvailableToIndicate;
|
|
}
|
|
|
|
ClientBytesTaken = 0;
|
|
status = TdiCopyBufferToMdl (pDataBuffer,
|
|
0,
|
|
BytesToCopy,
|
|
pReceiver->pIrpReceive->MdlAddress,
|
|
pReceiver->BytesInMdl,
|
|
&ClientBytesTaken);
|
|
|
|
pReceiver->BytesInMdl += ClientBytesTaken;
|
|
pReceiver->CurrentMessageProcessed += ClientBytesTaken;
|
|
|
|
BytesLeftInMessage -= ClientBytesTaken;
|
|
BytesAvailableToIndicate -= ClientBytesTaken;
|
|
pDataBuffer += ClientBytesTaken;
|
|
|
|
if ((!ClientBytesTaken) ||
|
|
(pReceiver->BytesInMdl >= pReceiver->TotalBytesInMdl) ||
|
|
(!BytesLeftInMessage))
|
|
{
|
|
//
|
|
// The Irp is full, so complete the Irp!
|
|
//
|
|
pIrpReceive = pReceiver->pIrpReceive;
|
|
pIrpReceive->IoStatus.Information = pReceiver->BytesInMdl;
|
|
if (BytesLeftInMessage)
|
|
{
|
|
pIrpReceive->IoStatus.Status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
else
|
|
{
|
|
ASSERT (pReceiver->CurrentMessageLength == pReceiver->CurrentMessageProcessed);
|
|
pIrpReceive->IoStatus.Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Before releasing the lock, set the parameters for the next receive
|
|
//
|
|
pReceiver->pIrpReceive = NULL;
|
|
pReceiver->TotalBytesInMdl = pReceiver->BytesInMdl = 0;
|
|
|
|
PgmUnlock (pReceive, *pOldIrqReceive);
|
|
PgmUnlock (pAddress, *pOldIrqAddress);
|
|
|
|
PgmCancelCancelRoutine (pIrpReceive);
|
|
|
|
PgmTrace (LogPath, ("PgmIndicateToClient: " \
|
|
"Completing prior pIrp=<%p>, Bytes=<%d>, BytesLeft=<%d>\n",
|
|
pIrpReceive, (ULONG) 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 (pReceiver->CurrentMessageLength == pReceiver->CurrentMessageProcessed);
|
|
pReceiver->CurrentMessageLength = pReceiver->CurrentMessageProcessed = 0;
|
|
}
|
|
|
|
if (BytesTaken = (BytesAvailable - BytesAvailableToIndicate))
|
|
{
|
|
*pBytesTaken = BytesTaken;
|
|
pReceiver->LastDataConsumedTime = PgmDynamicConfig.ReceiversTimerTickCount;
|
|
}
|
|
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
|
|
|
|
PgmTrace (LogPath, ("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))))
|
|
{
|
|
PgmTrace (LogError, ("PgmIndicateToClient: ERROR -- " \
|
|
"pReceive=<%p>, pIrp=<%p> Cancelled during Receive!\n", pReceive, pIrpReceive));
|
|
|
|
PgmIoComplete (pIrpReceive, STATUS_CANCELLED, 0);
|
|
|
|
PgmLock (pAddress, *pOldIrqAddress);
|
|
PgmLock (pReceive, *pOldIrqReceive);
|
|
|
|
pReceiver->CurrentMessageProcessed += ClientBytesTaken;
|
|
|
|
if (BytesTaken = (BytesAvailable - BytesAvailableToIndicate))
|
|
{
|
|
*pBytesTaken = BytesTaken;
|
|
pReceiver->LastDataConsumedTime = PgmDynamicConfig.ReceiversTimerTickCount;
|
|
}
|
|
return (STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
PgmLock (pAddress, *pOldIrqAddress);
|
|
PgmLock (pReceive, *pOldIrqReceive);
|
|
|
|
pReceiver->CurrentMessageProcessed += ClientBytesTaken;
|
|
|
|
if (!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);
|
|
}
|
|
|
|
PgmTrace (LogError, ("PgmIndicateToClient: ERROR -- " \
|
|
"pReceive=<%p> disassociated during Receive!\n", pReceive));
|
|
|
|
if (BytesTaken = (BytesAvailable - BytesAvailableToIndicate))
|
|
{
|
|
*pBytesTaken = BytesTaken;
|
|
pReceiver->LastDataConsumedTime = PgmDynamicConfig.ReceiversTimerTickCount;
|
|
}
|
|
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,
|
|
pReceiver->BytesInMdl,
|
|
&ClientBytesTaken);
|
|
|
|
BytesLeftInMessage -= ClientBytesTaken;
|
|
BytesAvailableToIndicate -= ClientBytesTaken;
|
|
pDataBuffer = pDataBuffer + ClientBytesTaken;
|
|
pReceiver->CurrentMessageProcessed += ClientBytesTaken;
|
|
|
|
PgmTrace (LogPath, ("PgmIndicateToClient: " \
|
|
"Client's evReceive returned pIrp=<%p>, BytesInIrp=<%d>, Copied <%d> bytes\n",
|
|
pIrpReceive, pClientParams->ReceiveLength, ClientBytesTaken));
|
|
|
|
if ((!ClientBytesTaken) ||
|
|
(ClientBytesTaken >= pClientParams->ReceiveLength) ||
|
|
(pReceiver->CurrentMessageLength == pReceiver->CurrentMessageProcessed))
|
|
{
|
|
//
|
|
// The Irp is full, so complete the Irp!
|
|
//
|
|
pIrpReceive->IoStatus.Information = ClientBytesTaken;
|
|
if (pReceiver->CurrentMessageLength == 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
|
|
//
|
|
pReceiver->TotalBytesInMdl = pReceiver->BytesInMdl = 0;
|
|
|
|
PgmUnlock (pReceive, *pOldIrqReceive);
|
|
PgmUnlock (pAddress, *pOldIrqAddress);
|
|
|
|
PgmCancelCancelRoutine (pIrpReceive);
|
|
IoCompleteRequest (pIrpReceive, IO_NETWORK_INCREMENT);
|
|
|
|
PgmLock (pAddress, *pOldIrqAddress);
|
|
PgmLock (pReceive, *pOldIrqReceive);
|
|
}
|
|
else
|
|
{
|
|
pReceiver->TotalBytesInMdl = pClientParams->ReceiveLength;
|
|
pReceiver->BytesInMdl = ClientBytesTaken;
|
|
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 ((pReceiver->pIrpReceive) ||
|
|
(!IsListEmpty (&pReceiver->ReceiveIrpsList)))
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
pReceive->SessionFlags |= PGM_SESSION_WAIT_FOR_RECEIVE_IRP;
|
|
}
|
|
}
|
|
|
|
if (pReceiver->CurrentMessageLength == pReceiver->CurrentMessageProcessed)
|
|
{
|
|
pReceiver->CurrentMessageLength = pReceiver->CurrentMessageProcessed = 0;
|
|
}
|
|
|
|
if ((NT_SUCCESS (status)) ||
|
|
(status == STATUS_DATA_NOT_ACCEPTED))
|
|
{
|
|
PgmTrace (LogAllFuncs, ("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
|
|
{
|
|
PgmTrace (LogError, ("PgmIndicateToClient: ERROR -- " \
|
|
"Unexpected status=<%x>\n", status));
|
|
|
|
ASSERT (0);
|
|
}
|
|
|
|
if (BytesTaken = (BytesAvailable - BytesAvailableToIndicate))
|
|
{
|
|
*pBytesTaken = BytesTaken;
|
|
pReceiver->LastDataConsumedTime = PgmDynamicConfig.ReceiversTimerTickCount;
|
|
}
|
|
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)
|
|
{
|
|
if (pReceive->SessionFlags & PGM_SESSION_CLIENT_DISCONNECTED)
|
|
{
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
break;
|
|
}
|
|
|
|
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)
|
|
{
|
|
PgmTrace (LogPath, ("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++;
|
|
status = STATUS_SUCCESS;
|
|
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
|
|
//
|
|
PgmTrace (LogError, ("PgmIndicateGroup: ERROR -- " \
|
|
"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++;
|
|
status = STATUS_SUCCESS;
|
|
continue;
|
|
}
|
|
|
|
if (DataBytes > (pNak->pPendingData[i].MessageLength - pNak->pPendingData[i].MessageOffset))
|
|
{
|
|
PgmTrace (LogError, ("PgmIndicateGroup: ERROR -- " \
|
|
"[%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);
|
|
|
|
PgmTrace (LogPath, ("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 (!ReAllocateDataBuffer (pReceive, &pNak->pPendingData[i], MinBufferSize))
|
|
{
|
|
ASSERT (0);
|
|
PgmTrace (LogError, ("DecodeParityPackets: ERROR -- " \
|
|
"STATUS_INSUFFICIENT_RESOURCES[2] ...\n"));
|
|
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
#ifdef FEC_DBG
|
|
{
|
|
UCHAR i;
|
|
tPOST_PACKET_FEC_CONTEXT UNALIGNED *pFECC;
|
|
tPOST_PACKET_FEC_CONTEXT FECC;
|
|
|
|
for (i=0; i<pReceive->FECGroupSize; i++)
|
|
{
|
|
pFECC = (tPOST_PACKET_FEC_CONTEXT UNALIGNED *)
|
|
&pNak->pPendingData[i].DecodeBuffer[pNak->ParityDataSize-sizeof(USHORT)];
|
|
PgmCopyMemory (&FECC, pFECC, sizeof (tPOST_PACKET_FEC_CONTEXT));
|
|
PgmTrace (LogFec, ("\t-- [%d:%d:%d] EncTSDULen=<%x>, Fpr=<%x>, [%x -- %x -- %x]\n",
|
|
(ULONG) pNak->SequenceNumber, (ULONG) pNak->pPendingData[i].PacketIndex,
|
|
(ULONG) pNak->pPendingData[i].ActualIndexOfDataPacket,
|
|
FECC.EncodedTSDULength, FECC.FragmentOptSpecific,
|
|
FECC.EncodedFragmentOptions.MessageFirstSequence,
|
|
FECC.EncodedFragmentOptions.MessageOffset,
|
|
FECC.EncodedFragmentOptions.MessageLength)));
|
|
}
|
|
}
|
|
#endif // FEC_DBG
|
|
|
|
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)
|
|
{
|
|
ASSERT (!pNak->pPendingData[i].PendingDataFlags);
|
|
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)
|
|
{
|
|
PgmTrace (LogError, ("DecodeParityPackets: ERROR -- " \
|
|
"[%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
|
|
{
|
|
PgmTrace (LogError, ("DecodeParityPackets: ERROR -- " \
|
|
"FECDecode returned <%x>\n", status));
|
|
|
|
ASSERT (0);
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
#ifdef FEC_DBG
|
|
if (NT_SUCCESS (status))
|
|
{
|
|
UCHAR i;
|
|
tPOST_PACKET_FEC_CONTEXT UNALIGNED *pFECC;
|
|
tPOST_PACKET_FEC_CONTEXT FECC;
|
|
|
|
DataBytes = pNak->ParityDataSize - sizeof(USHORT);
|
|
for (i=0; i<pNak->PacketsInGroup; i++)
|
|
{
|
|
pFECC = (tPOST_PACKET_FEC_CONTEXT UNALIGNED *) &pNak->pPendingData[i].pDataPacket[DataBytes];
|
|
PgmCopyMemory (&FECC, pFECC, sizeof (tPOST_PACKET_FEC_CONTEXT));
|
|
PgmTrace (LogFec, ("\t++ [%d] EncTSDULen=<%x>, Fpr=<%x>, [%x -- %x -- %x], ==> [%x -- %x -- %x]\n",
|
|
(ULONG) (pNak->SequenceNumber+i), FECC.EncodedTSDULength, FECC.FragmentOptSpecific,
|
|
FECC.EncodedFragmentOptions.MessageFirstSequence,
|
|
FECC.EncodedFragmentOptions.MessageOffset,
|
|
FECC.EncodedFragmentOptions.MessageLength,
|
|
pNak->pPendingData[i].MessageFirstSequence,
|
|
pNak->pPendingData[i].MessageOffset,
|
|
pNak->pPendingData[i].MessageLength));
|
|
}
|
|
PgmTrace (LogFec, ("\n"));
|
|
}
|
|
#endif // FEC_DBG
|
|
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;
|
|
tRECEIVE_CONTEXT *pReceiver = pReceive->pReceiver;
|
|
|
|
//
|
|
// 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))
|
|
{
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
ASSERT (!(pReceive->SessionFlags & PGM_SESSION_CLIENT_DISCONNECTED));
|
|
pReceive->SessionFlags |= PGM_SESSION_FLAG_IN_INDICATE;
|
|
while (!IsListEmpty (&pReceiver->BufferedDataList))
|
|
{
|
|
pNextNak = CONTAINING_RECORD (pReceiver->BufferedDataList.Flink, tNAK_FORWARD_DATA, Linkage);
|
|
|
|
ASSERT ((pReceiver->NumPacketGroupsPendingClient) &&
|
|
(pNextNak->SequenceNumber == pReceiver->NextODataSequenceNumber) &&
|
|
(SEQ_GT(pReceiver->FirstNakSequenceNumber, pReceiver->NextODataSequenceNumber)));
|
|
|
|
//
|
|
// 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);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmTrace (LogError, ("CheckIndicatePendedData: ERROR -- " \
|
|
"DecodeParityPackets returned <%x>\n", status));
|
|
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The below assertion can be greater if we have only partially indicated a group
|
|
ASSERT ((pNextNak->NextIndexToIndicate + pNextNak->NumDataPackets) >= pNextNak->PacketsInGroup);
|
|
}
|
|
|
|
status = PgmIndicateGroup (pAddress, pReceive, pOldIrqAddress, pOldIrqReceive, pNextNak);
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Advance to the next group boundary
|
|
//
|
|
pReceiver->NextODataSequenceNumber += pNextNak->OriginalGroupSize;
|
|
|
|
PacketsIndicated = pNextNak->NumDataPackets + pNextNak->NumParityPackets;
|
|
pReceiver->TotalDataPacketsBuffered -= PacketsIndicated;
|
|
pReceiver->DataPacketsPendingIndicate -= PacketsIndicated;
|
|
pReceiver->NumPacketGroupsPendingClient--;
|
|
ASSERT (pReceiver->TotalDataPacketsBuffered >= pReceiver->NumPacketGroupsPendingClient);
|
|
|
|
RemoveEntryList (&pNextNak->Linkage);
|
|
FreeNakContext (pReceive, pNextNak);
|
|
}
|
|
pReceive->SessionFlags &= ~PGM_SESSION_FLAG_IN_INDICATE;
|
|
|
|
PgmTrace (LogAllFuncs, ("CheckIndicatePendedData: " \
|
|
"status=<%x>, pReceive=<%p>, SessionFlags=<%x>\n",
|
|
status, pReceive, pReceive->SessionFlags));
|
|
|
|
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;
|
|
ULONG MessageLength, DataOffset, BytesTaken, DataBytes, BufferLength;
|
|
ULONGLONG NcfRDataTickCounts;
|
|
NTSTATUS status;
|
|
USHORT TSDULength;
|
|
tPACKET_OPTIONS PacketOptions;
|
|
UCHAR i, PacketIndex, NakIndex;
|
|
BOOLEAN fIsParityPacket, fPartiallyIndicated;
|
|
PUCHAR pDataBuffer;
|
|
tRECEIVE_CONTEXT *pReceiver = pReceive->pReceiver;
|
|
|
|
ASSERT (PacketLength <= pReceive->MaxMTULength);
|
|
fIsParityPacket = pOData->CommonHeader.Options & PACKET_HEADER_OPTIONS_PARITY;
|
|
|
|
//
|
|
// First, ensure we have a Nak context available for this data
|
|
//
|
|
pLastNak = NULL;
|
|
status = CheckAndAddNakRequests (pReceive, &ThisDataSequenceNumber, &pLastNak, NAK_PENDING_RB, (BOOLEAN) !fIsParityPacket);
|
|
if ((!NT_SUCCESS (status)) ||
|
|
(!pLastNak))
|
|
{
|
|
PgmTrace (LogAllFuncs, ("PgmHandleNewData: " \
|
|
"CheckAndAddNakRequests for <%d> returned <%x>, pLastNak=<%p>\n",
|
|
ThisDataSequenceNumber, status, pLastNak));
|
|
|
|
if (NT_SUCCESS (status))
|
|
{
|
|
pReceiver->NumDupPacketsBuffered++;
|
|
}
|
|
else
|
|
{
|
|
pReceiver->NumDataPacketsDropped++;
|
|
}
|
|
return (status);
|
|
}
|
|
|
|
//
|
|
// Now, extract all the information that we need from the packet options
|
|
//
|
|
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))
|
|
{
|
|
PgmTrace (LogError, ("PgmHandleNewData: ERROR -- " \
|
|
"ProcessOptions returned <%x>, SeqNum=[%d]: NumOutOfOrder=<%d> ...\n",
|
|
status, (ULONG) ThisDataSequenceNumber, pReceiver->TotalDataPacketsBuffered));
|
|
|
|
ASSERT (0);
|
|
|
|
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);
|
|
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);
|
|
BytesTaken = 0;
|
|
|
|
//
|
|
// If this group has a different GroupSize, set that now
|
|
//
|
|
if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_CUR_TGSIZE)
|
|
{
|
|
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;
|
|
}
|
|
else if (PacketOptions.FECContext.NumPacketsInThisGroup >= pReceive->FECGroupSize)
|
|
{
|
|
//
|
|
// Bad Packet!
|
|
//
|
|
ASSERT (0);
|
|
pReceiver->NumDataPacketsDropped++;
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
//
|
|
// 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;
|
|
if (pLastNak->SequenceNumber == pReceiver->FurthestKnownGroupSequenceNumber)
|
|
{
|
|
pReceiver->FurthestKnownSequenceNumber = pLastNak->SequenceNumber + pLastNak->PacketsInGroup - 1;
|
|
}
|
|
|
|
//
|
|
// Get rid of any of the excess (NULL) data packets
|
|
//
|
|
RemoveRedundantNaks (pReceive, pLastNak, TRUE);
|
|
}
|
|
else if (pLastNak->PacketsInGroup != PacketOptions.FECContext.NumPacketsInThisGroup)
|
|
{
|
|
ASSERT (0);
|
|
pReceiver->NumDataPacketsDropped++;
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
}
|
|
|
|
if (PacketOptions.OptionsFlags & PGM_OPTION_FLAG_FIN)
|
|
{
|
|
if (fIsParityPacket)
|
|
{
|
|
pReceiver->FinDataSequenceNumber = pLastNak->SequenceNumber + (pLastNak->PacketsInGroup - 1);
|
|
}
|
|
else
|
|
{
|
|
pReceiver->FinDataSequenceNumber = ThisDataSequenceNumber;
|
|
}
|
|
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_GRACEFULLY;
|
|
|
|
PgmTrace (LogStatus, ("PgmHandleNewData: " \
|
|
"SeqNum=[%d]: Got a FIN!!!\n", (ULONG) pReceiver->FinDataSequenceNumber));
|
|
|
|
if (pLastNak)
|
|
{
|
|
ASSERT (pLastNak->SequenceNumber == (pReceiver->FinDataSequenceNumber & ~(pReceive->FECGroupSize-1)));
|
|
pLastNak->PacketsInGroup = (UCHAR) (pReceiver->FinDataSequenceNumber + 1 - pLastNak->SequenceNumber);
|
|
ASSERT (pLastNak->PacketsInGroup <= pReceive->FECGroupSize);
|
|
|
|
RemoveRedundantNaks (pReceive, pLastNak, TRUE);
|
|
AdjustReceiveBufferLists (pReceive);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine the Packet Index
|
|
//
|
|
fPartiallyIndicated = FALSE;
|
|
if (pReceive->FECOptions)
|
|
{
|
|
PacketIndex = (UCHAR) (ThisDataSequenceNumber & (pReceive->FECGroupSize-1));
|
|
|
|
//
|
|
// See if we even need this packet!
|
|
//
|
|
if (!fIsParityPacket)
|
|
{
|
|
//
|
|
// This is a data packet!
|
|
//
|
|
if ((PacketIndex >= pLastNak->PacketsInGroup) ||
|
|
(PacketIndex < pLastNak->NextIndexToIndicate))
|
|
{
|
|
//
|
|
// We don't need this Packet!
|
|
//
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
}
|
|
//
|
|
// Parity packet
|
|
//
|
|
else if (((pLastNak->NumDataPackets+pLastNak->NumParityPackets) >= pLastNak->PacketsInGroup) ||
|
|
((pLastNak->NextIndexToIndicate + pLastNak->NumDataPackets) >= pLastNak->PacketsInGroup))
|
|
{
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
else
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AdjustReceiveBufferLists (pReceive); // In case this became a partial group
|
|
}
|
|
|
|
if (status == STATUS_DATA_NOT_ACCEPTED)
|
|
{
|
|
pReceiver->NumDupPacketsBuffered++;
|
|
return (status);
|
|
}
|
|
}
|
|
else // We are not aware of FEC
|
|
{
|
|
//
|
|
// If we are not aware of options, drop this packet if it is a parity packet!
|
|
//
|
|
if (fIsParityPacket)
|
|
{
|
|
pReceiver->NumDataPacketsDropped++;
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
PacketIndex = 0;
|
|
|
|
ASSERT (!pLastNak->pPendingData[0].pDataPacket);
|
|
|
|
//
|
|
// If this is the next expected data packet, let's see if we can try
|
|
// to indicate this data over here only (avoid a packet copy)
|
|
// Note: This should be the regular indicate path in the case of non-FEC,
|
|
// low-loss and low-CPU sessions
|
|
//
|
|
if ((ThisDataSequenceNumber == pReceiver->NextODataSequenceNumber) &&
|
|
!(pReceive->SessionFlags & (PGM_SESSION_FLAG_IN_INDICATE |
|
|
PGM_SESSION_WAIT_FOR_RECEIVE_IRP)) &&
|
|
(IsListEmpty (&pReceiver->BufferedDataList)))
|
|
{
|
|
ASSERT (!pReceiver->NumPacketGroupsPendingClient);
|
|
|
|
//
|
|
// If we are starting receiving in the midst of a message, we should ignore them
|
|
//
|
|
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));
|
|
|
|
PgmTrace (LogPath, ("PgmHandleNewData: " \
|
|
"Dropping SeqNum=[%d] since it's a PARTIAL message [%d / %d]!\n",
|
|
(ULONG) (pReceive->pReceiver->NextODataSequenceNumber),
|
|
PacketOptions.MessageOffset, PacketOptions.MessageLength));
|
|
|
|
DataBytes = 0;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else if ((pReceiver->CurrentMessageProcessed != PacketOptions.MessageOffset) ||
|
|
((pReceiver->CurrentMessageProcessed) &&
|
|
(pReceiver->CurrentMessageLength != PacketOptions.MessageLength)))
|
|
{
|
|
//
|
|
// Our state expects us to be in the middle of a message, but
|
|
// the current packets do not show this
|
|
//
|
|
PgmTrace (LogError, ("PgmHandleNewData: ERROR -- " \
|
|
"SeqNum=[%d] Expecting MsgLen=<%d>, MsgOff=<%d>, have MsgLen=<%d>, MsgOff=<%d>\n",
|
|
(ULONG) pReceiver->NextODataSequenceNumber,
|
|
pReceiver->CurrentMessageLength,
|
|
pReceiver->CurrentMessageProcessed,
|
|
PacketOptions.MessageLength, PacketOptions.MessageOffset));
|
|
|
|
ASSERT (0);
|
|
BytesTaken = DataBytes;
|
|
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
|
return (STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
RemoveEntryList (&pLastNak->Linkage);
|
|
RemovePendingReceiverEntry (pLastNak);
|
|
|
|
pReceiver->NextODataSequenceNumber++;
|
|
pReceiver->FirstNakSequenceNumber = pReceiver->NextODataSequenceNumber;
|
|
|
|
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))
|
|
{
|
|
PgmTrace (LogPath, ("PgmHandleNewData: " \
|
|
"Dropping SeqNum=[%d] since it's a NULL message [%d / %d]!\n",
|
|
(ULONG) (pReceiver->NextODataSequenceNumber),
|
|
PacketOptions.MessageOffset, PacketOptions.MessageLength));
|
|
|
|
BytesTaken = DataBytes;
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
ASSERT (!(pReceive->SessionFlags & PGM_SESSION_CLIENT_DISCONNECTED));
|
|
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;
|
|
|
|
PgmTrace (LogPath, ("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 (BytesTaken == DataBytes)
|
|
{
|
|
if ((PacketType == PACKET_TYPE_RDATA) &&
|
|
(pLastNak->FirstNcfTickCount))
|
|
{
|
|
AdjustNcfRDataResponseTimes (pReceive, pLastNak);
|
|
}
|
|
|
|
FreeNakContext (pReceive, pLastNak);
|
|
AdjustReceiveBufferLists (pReceive); // move any additional complete groups to the BufferedDataList
|
|
|
|
return (status);
|
|
}
|
|
|
|
fPartiallyIndicated = TRUE;
|
|
InsertHeadList (&pReceiver->BufferedDataList, &pLastNak->Linkage);
|
|
}
|
|
}
|
|
|
|
#ifdef MAX_BUFF_DBG
|
|
{
|
|
if (pReceiver->NumPacketGroupsPendingClient > MaxPacketGroupsPendingClient)
|
|
{
|
|
MaxPacketGroupsPendingClient = pReceiver->NumPacketGroupsPendingClient;
|
|
}
|
|
if (pReceiver->TotalDataPacketsBuffered >= MaxPacketsBuffered)
|
|
{
|
|
MaxPacketsBuffered = pReceiver->TotalDataPacketsBuffered;
|
|
}
|
|
if (pReceiver->DataPacketsPendingIndicate >= MaxPacketsPendingIndicate)
|
|
{
|
|
MaxPacketsPendingIndicate = pReceiver->DataPacketsPendingIndicate;
|
|
}
|
|
if (pReceiver->DataPacketsPendingNaks >= MaxPacketsPendingNaks)
|
|
{
|
|
MaxPacketsPendingNaks = pReceiver->DataPacketsPendingNaks;
|
|
}
|
|
ASSERT (pReceiver->TotalDataPacketsBuffered == (pReceiver->DataPacketsPendingIndicate +
|
|
pReceiver->DataPacketsPendingNaks));
|
|
}
|
|
#endif // MAX_BUFF_DBG
|
|
|
|
if (pReceiver->TotalDataPacketsBuffered >= pReceiver->MaxPacketsBufferedInLookaside)
|
|
{
|
|
PgmTrace (LogError, ("PgmHandleNewData: ERROR -- " \
|
|
"[%d -- %d]: Excessive number of packets buffered=<%d> > <%d>, Aborting ...\n",
|
|
(ULONG) pReceiver->FirstNakSequenceNumber, (ULONG) ThisDataSequenceNumber,
|
|
(ULONG) pReceiver->TotalDataPacketsBuffered,
|
|
(ULONG) pReceiver->MaxPacketsBufferedInLookaside));
|
|
|
|
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// 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)
|
|
{
|
|
BufferLength = PacketLength + sizeof(tPOST_PACKET_FEC_CONTEXT) - sizeof(USHORT);
|
|
}
|
|
else if ((PacketLength + sizeof (tPOST_PACKET_FEC_CONTEXT)) <= pLastNak->MinPacketLength)
|
|
{
|
|
BufferLength = pLastNak->MinPacketLength;
|
|
}
|
|
else
|
|
{
|
|
BufferLength = PacketLength + sizeof(tPOST_PACKET_FEC_CONTEXT);
|
|
}
|
|
pDataBuffer = NULL;
|
|
|
|
if (!fIsParityPacket)
|
|
{
|
|
ASSERT (PacketIndex < pReceive->FECGroupSize);
|
|
ASSERT (pLastNak->pPendingData[PacketIndex].ActualIndexOfDataPacket == pLastNak->OriginalGroupSize);
|
|
|
|
//
|
|
// 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)
|
|
{
|
|
pDataBuffer = ReAllocateDataBuffer (pReceive, &pLastNak->pPendingData[i], BufferLength);
|
|
BufferLength = 0;
|
|
|
|
break;
|
|
}
|
|
}
|
|
ASSERT (i < pLastNak->PacketsInGroup);
|
|
pLastNak->NumParityPackets--;
|
|
NakIndex = i;
|
|
}
|
|
else
|
|
{
|
|
ASSERT (!pLastNak->pPendingData[NakIndex].pDataPacket);
|
|
}
|
|
|
|
if (BufferLength)
|
|
{
|
|
pDataBuffer = AllocateDataBuffer (pReceive, &pLastNak->pPendingData[NakIndex], BufferLength);
|
|
}
|
|
|
|
if (!pDataBuffer)
|
|
{
|
|
PgmTrace (LogError, ("PgmHandleNewData: ERROR -- " \
|
|
"[%d]: STATUS_INSUFFICIENT_RESOURCES <%d> bytes, NumDataPackets=<%d>, Aborting ...\n",
|
|
(ULONG) ThisDataSequenceNumber, pLastNak->MinPacketLength,
|
|
(ULONG) pReceiver->TotalDataPacketsBuffered));
|
|
|
|
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
ASSERT (pLastNak->pPendingData[NakIndex].pDataPacket == pDataBuffer);
|
|
|
|
PgmCopyMemory (pDataBuffer, pOData, PacketLength);
|
|
|
|
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 = AllocateDataBuffer (pReceive, &pLastNak->pPendingData[NakIndex], BufferLength);
|
|
if (!pDataBuffer)
|
|
{
|
|
PgmTrace (LogError, ("PgmHandleNewData: ERROR -- " \
|
|
"[%d -- Parity]: STATUS_INSUFFICIENT_RESOURCES <%d> bytes, NumDataPackets=<%d>, Aborting ...\n",
|
|
(ULONG) ThisDataSequenceNumber, PacketLength,
|
|
(ULONG) pReceiver->TotalDataPacketsBuffered));
|
|
|
|
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// This is a new parity packet
|
|
//
|
|
ASSERT (pLastNak->pPendingData[NakIndex].pDataPacket == pDataBuffer);
|
|
|
|
PgmCopyMemory (pDataBuffer, pOData, PacketLength);
|
|
pLastNak->pPendingData[NakIndex].PacketLength = PacketLength;
|
|
pLastNak->pPendingData[NakIndex].DataOffset = (USHORT) DataOffset;
|
|
pLastNak->pPendingData[NakIndex].PacketIndex = PacketIndex;
|
|
|
|
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));
|
|
}
|
|
}
|
|
|
|
if ((PacketType == PACKET_TYPE_RDATA) &&
|
|
(pLastNak->FirstNcfTickCount) &&
|
|
(((pLastNak->NumDataPackets + pLastNak->NumParityPackets) >= pLastNak->PacketsInGroup) ||
|
|
((pLastNak->NextIndexToIndicate + pLastNak->NumDataPackets) >= pLastNak->PacketsInGroup)))
|
|
{
|
|
AdjustNcfRDataResponseTimes (pReceive, pLastNak);
|
|
}
|
|
|
|
pLastNak->AllOptionsFlags |= PacketOptions.OptionsFlags;
|
|
|
|
pReceiver->TotalDataPacketsBuffered++;
|
|
if (fPartiallyIndicated)
|
|
{
|
|
pReceiver->NumPacketGroupsPendingClient++;
|
|
pReceiver->DataPacketsPendingIndicate++;
|
|
pReceiver->NextODataSequenceNumber = ThisDataSequenceNumber;
|
|
}
|
|
else
|
|
{
|
|
pReceiver->DataPacketsPendingNaks++;
|
|
|
|
//
|
|
// See if this group is complete
|
|
//
|
|
if (((pLastNak->NumDataPackets + pLastNak->NumParityPackets) >= pLastNak->PacketsInGroup) ||
|
|
((pLastNak->NextIndexToIndicate + pLastNak->NumDataPackets) >= pLastNak->PacketsInGroup))
|
|
{
|
|
RemovePendingReceiverEntry (pLastNak);
|
|
AdjustReceiveBufferLists (pReceive);
|
|
}
|
|
}
|
|
|
|
PgmTrace (LogAllFuncs, ("PgmHandleNewData: " \
|
|
"SeqNum=[%d]: NumOutOfOrder=<%d> ...\n",
|
|
(ULONG) ThisDataSequenceNumber, pReceiver->TotalDataPacketsBuffered));
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
ProcessDataPacket(
|
|
IN tADDRESS_CONTEXT *pAddress,
|
|
IN tRECEIVE_SESSION *pReceive,
|
|
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 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))
|
|
{
|
|
PgmTrace (LogError, ("ProcessDataPacket: ERROR -- " \
|
|
"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);
|
|
|
|
if (pReceive->SessionFlags & (PGM_SESSION_CLIENT_DISCONNECTED |
|
|
PGM_SESSION_TERMINATED_ABORT))
|
|
{
|
|
PgmTrace (LogPath, ("ProcessDataPacket: " \
|
|
"Dropping packet because session is terminating!\n"));
|
|
pReceive->pReceiver->NumDataPacketsDropped++;
|
|
|
|
PgmUnlock (pReceive, OldIrq1);
|
|
PgmUnlock (pAddress, OldIrq);
|
|
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
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) == (LONG) 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)))
|
|
{
|
|
PgmTrace (LogStatus, ("ProcessDataPacket: " \
|
|
"NETWORK problems -- data loss=<%d> packets > window size!\n\tExpecting=<%d>, FurthestKnown=<%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);
|
|
|
|
PgmTrace (LogStatus, ("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,
|
|
(ULONG) pNextNak->WaitingRDataRetries,
|
|
(ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber,
|
|
(ULONG) pReceive->pReceiver->LastTrailingEdgeSeqNum,
|
|
(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++;
|
|
|
|
PgmTrace (LogPath, ("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);
|
|
|
|
PgmTrace (LogPath, ("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;
|
|
|
|
//
|
|
// 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))
|
|
{
|
|
PgmTrace (LogError, ("ProcessSpmPacket: ERROR -- " \
|
|
"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);
|
|
|
|
PathNLA.NLA_AFI = ntohs (PathNLA.NLA_AFI);
|
|
|
|
if ((TSDULength) ||
|
|
(IPV4_NLA_AFI != PathNLA.NLA_AFI) ||
|
|
(!PathNLA.IpAddress))
|
|
{
|
|
PgmTrace (LogError, ("ProcessSpmPacket: ERROR -- " \
|
|
"TSDULength=<%d>, PathNLA.IpAddress=<%x>\n",
|
|
(ULONG) TSDULength, PathNLA.IpAddress));
|
|
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
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)
|
|
{
|
|
PgmTrace (LogError, ("ProcessSpmPacket: ERROR -- " \
|
|
"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
|
|
//
|
|
PgmTrace (LogPath, ("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);
|
|
|
|
PgmTrace (LogAllFuncs, ("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)))
|
|
{
|
|
PgmTrace (LogStatus, ("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);
|
|
|
|
PgmTrace (LogStatus, ("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)));
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the Leading edge is > our current leading edge, then
|
|
// we need to send NAKs for the missing data Packets
|
|
//
|
|
pNak = NULL;
|
|
if (SEQ_GEQ (LeadingEdgeSeqNumber, pReceive->pReceiver->FirstNakSequenceNumber))
|
|
{
|
|
status = CheckAndAddNakRequests (pReceive, &LeadingEdgeSeqNumber, &pNak, NAK_PENDING_RB, TRUE);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmUnlock (pReceive, OldIrq1);
|
|
PgmUnlock (pAddress, OldIrq);
|
|
|
|
PgmTrace (LogError, ("ProcessSpmPacket: ERROR -- " \
|
|
"CheckAndAddNakRequests returned <%x>\n", status));
|
|
|
|
return (status);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
|
|
PgmTrace (LogStatus, ("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;
|
|
|
|
PgmTrace (LogStatus, ("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;
|
|
|
|
PgmTrace (LogStatus, ("ProcessSpmPacket: " \
|
|
"Got a FIN! FinSeq=<%d>, NextODataSeq=<%d>, FirstNak=<%d>, FurthestKnown=<%d>, FurthestGroup=<%d>\n",
|
|
(ULONG) pReceive->pReceiver->FinDataSequenceNumber,
|
|
(ULONG) pReceive->pReceiver->NextODataSequenceNumber,
|
|
(ULONG) pReceive->pReceiver->FirstNakSequenceNumber,
|
|
(ULONG) pReceive->pReceiver->FurthestKnownSequenceNumber,
|
|
(ULONG) pReceive->pReceiver->FurthestKnownGroupSequenceNumber));
|
|
|
|
//
|
|
// See if we need to set the Fin Sequence #
|
|
//
|
|
if (pNak)
|
|
{
|
|
ASSERT (pNak->SequenceNumber == (LeadingEdgeSeqNumber & ~(pReceive->FECGroupSize-1)));
|
|
pNak->PacketsInGroup = (UCHAR) (LeadingEdgeSeqNumber + 1 - pNak->SequenceNumber);
|
|
ASSERT (pNak->PacketsInGroup <= pReceive->FECGroupSize);
|
|
|
|
RemoveRedundantNaks (pReceive, pNak, TRUE);
|
|
AdjustReceiveBufferLists (pReceive);
|
|
}
|
|
}
|
|
|
|
//
|
|
// See if we need to abort
|
|
//
|
|
if (CheckIndicateDisconnect (pAddress, pReceive, &OldIrq, &OldIrq1, TRUE))
|
|
{
|
|
PgmUnlock (pReceive, OldIrq1);
|
|
PgmUnlock (pAddress, OldIrq);
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
|
|
PgmTrace (LogError, ("ProcessSpmPacket: ERROR -- " \
|
|
"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;
|
|
|
|
PgmTrace (LogError, ("ProcessSpmPacket: ERROR -- " \
|
|
"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;
|
|
|
|
PgmTrace (LogError, ("ProcessSpmPacket: ERROR -- " \
|
|
"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) &&
|
|
(pNak = FindReceiverEntry (pReceive->pReceiver, LeadingEdgeSeqNumber)) &&
|
|
(pNak->PacketsInGroup == pReceive->FECGroupSize))
|
|
{
|
|
//
|
|
// We have already coalesced the list, so the packets should
|
|
// be ordered into groups!
|
|
//
|
|
pNak->PacketsInGroup = PacketOptions.FECContext.NumPacketsInThisGroup;
|
|
if (pNak->SequenceNumber == pReceive->pReceiver->FurthestKnownGroupSequenceNumber)
|
|
{
|
|
pReceive->pReceiver->FurthestKnownSequenceNumber = pNak->SequenceNumber + pNak->PacketsInGroup - 1;
|
|
}
|
|
RemoveRedundantNaks (pReceive, pNak, TRUE);
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogPath, ("ProcessSpmPacket: " \
|
|
"WARNING .. PARITY_CUR_TGSIZE ThisGroupSize=<%x>, FECGroupSize=<%x>\n",
|
|
PacketOptions.FECContext.NumPacketsInThisGroup, pReceive->FECGroupSize));
|
|
}
|
|
}
|
|
}
|
|
|
|
status = CheckIndicatePendedData (pAddress, pReceive, &OldIrq, &OldIrq1);
|
|
|
|
PgmUnlock (pReceive, OldIrq1);
|
|
PgmUnlock (pAddress, OldIrq);
|
|
|
|
PgmTrace (LogAllFuncs, ("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 PTA_IP_ADDRESS pRemoteAddress,
|
|
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 pRemoteAddress -- 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
|
|
|
|
--*/
|
|
{
|
|
tIPADDRESS SrcIpAddress;
|
|
tNLA SourceNLA, MCastGroupNLA;
|
|
tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED *pNakNcfPacket;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// We have an active connection for this TSI, so process the data appropriately
|
|
//
|
|
|
|
//
|
|
// First check for SPM packets
|
|
//
|
|
if (PACKET_TYPE_SPM == PacketType)
|
|
{
|
|
if (PacketLength < sizeof(tBASIC_SPM_PACKET_HEADER))
|
|
{
|
|
PgmTrace (LogError, ("PgmProcessIncomingPacket: ERROR -- " \
|
|
"Bad SPM Packet length: PacketLength=<%d> < sizeof(tBASIC_SPM_PACKET_HEADER)=<%d>\n",
|
|
PacketLength, sizeof(tBASIC_SPM_PACKET_HEADER)));
|
|
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
if (PGM_VERIFY_HANDLE (pSession, PGM_VERIFY_SESSION_RECEIVE))
|
|
{
|
|
pSession->TotalBytes += PacketLength;
|
|
pSession->TotalPacketsReceived++;
|
|
pSession->pReceiver->LastSessionTickCount = PgmDynamicConfig.ReceiversTimerTickCount;
|
|
|
|
status = ProcessSpmPacket (pAddress,
|
|
pSession,
|
|
PacketLength,
|
|
(tBASIC_SPM_PACKET_HEADER UNALIGNED *) pPgmHeader);
|
|
|
|
PgmTrace (LogAllFuncs, ("PgmProcessIncomingPacket: " \
|
|
"SPM PacketType=<%x> for pSession=<%p> PacketLength=<%d>, status=<%x>\n",
|
|
PacketType, pSession, PacketLength, status));
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogError, ("PgmProcessIncomingPacket: ERROR -- " \
|
|
"Received SPM packet, not on Receiver session! pSession=<%p>\n", pSession));
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
//
|
|
// The only other packets we process are Nak and Ncf packets, so ignore the rest!
|
|
//
|
|
if ((PACKET_TYPE_NCF != PacketType) &&
|
|
(PACKET_TYPE_NAK != PacketType))
|
|
{
|
|
PgmTrace (LogError, ("PgmProcessIncomingPacket: ERROR -- " \
|
|
"Unknown PacketType=<%x>, PacketLength=<%d>\n", PacketType, PacketLength));
|
|
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
//
|
|
// Now, verify packet info for Nak and Ncf packets
|
|
//
|
|
if (PacketLength < sizeof(tBASIC_NAK_NCF_PACKET_HEADER))
|
|
{
|
|
PgmTrace (LogError, ("PgmProcessIncomingPacket: ERROR -- " \
|
|
"NakNcf packet! PacketLength=<%d>, Min=<%d>, ...\n",
|
|
PacketLength, sizeof(tBASIC_NAK_NCF_PACKET_HEADER)));
|
|
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
pNakNcfPacket = (tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED *) pPgmHeader;
|
|
PgmCopyMemory (&SourceNLA, &pNakNcfPacket->SourceNLA, sizeof (tNLA));
|
|
PgmCopyMemory (&MCastGroupNLA, &pNakNcfPacket->MCastGroupNLA, sizeof (tNLA));
|
|
if (((htons(IPV4_NLA_AFI) != SourceNLA.NLA_AFI) || (!SourceNLA.IpAddress)) ||
|
|
((htons(IPV4_NLA_AFI) != MCastGroupNLA.NLA_AFI) || (!MCastGroupNLA.IpAddress)))
|
|
{
|
|
PgmTrace (LogError, ("PgmProcessIncomingPacket: ERROR -- " \
|
|
"NakNcf packet! PacketLength=<%d>, Min=<%d>, ...\n",
|
|
PacketLength, sizeof(tBASIC_NAK_NCF_PACKET_HEADER)));
|
|
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
|
|
if (PACKET_TYPE_NCF == PacketType)
|
|
{
|
|
if (PGM_VERIFY_HANDLE (pSession, PGM_VERIFY_SESSION_RECEIVE))
|
|
{
|
|
status = ReceiverProcessNakNcfPacket (pAddress,
|
|
pSession,
|
|
PacketLength,
|
|
pNakNcfPacket,
|
|
PacketType);
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogError, ("PgmProcessIncomingPacket: ERROR -- " \
|
|
"Received Ncf packet, not on Receiver session! pSession=<%p>\n", pSession));
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
}
|
|
// Now process NAK packet
|
|
else if (pSession->pSender)
|
|
{
|
|
ASSERT (!pSession->pReceiver);
|
|
status = SenderProcessNakPacket (pAddress,
|
|
pSession,
|
|
PacketLength,
|
|
pNakNcfPacket);
|
|
}
|
|
else
|
|
{
|
|
ASSERT (pSession->pReceiver);
|
|
|
|
//
|
|
// Check for Remote guy's address
|
|
// If the Nak was sent by us, then we can ignore it!
|
|
//
|
|
if ((pRemoteAddress->TAAddressCount == 1) &&
|
|
(pRemoteAddress->Address[0].AddressLength == TDI_ADDRESS_LENGTH_IP) &&
|
|
(pRemoteAddress->Address[0].AddressType == TDI_ADDRESS_TYPE_IP) &&
|
|
(SrcIpAddress = ntohl(((PTDI_ADDRESS_IP)&pRemoteAddress->Address[0].Address)->in_addr)) &&
|
|
(!SrcIsUs (SrcIpAddress)) &&
|
|
(SrcIsOnLocalSubnet (SrcIpAddress)))
|
|
{
|
|
status = ReceiverProcessNakNcfPacket (pAddress,
|
|
pSession,
|
|
PacketLength,
|
|
pNakNcfPacket,
|
|
PacketType);
|
|
}
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
}
|
|
|
|
PgmTrace (LogAllFuncs, ("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;
|
|
|
|
//
|
|
// 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))
|
|
{
|
|
PgmTrace (LogError, ("PgmNewInboundConnection: ERROR -- " \
|
|
"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);
|
|
|
|
PgmTrace (LogError, ("PgmNewInboundConnection: ERROR -- " \
|
|
"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))
|
|
{
|
|
PgmTrace (LogPath, ("PgmNewInboundConnection: " \
|
|
"Client REJECTed incoming session: status=<%x>, pAddress=<%p>, evConn=<%p>\n",
|
|
status, pAddress, pAddress->evConnect));
|
|
|
|
if (pIrp)
|
|
{
|
|
PgmIoComplete (pIrp, STATUS_INTERNAL_ERROR, 0);
|
|
}
|
|
|
|
*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))
|
|
{
|
|
PgmTrace (LogError, ("PgmNewInboundConnection: ERROR -- " \
|
|
"pReceive=<%p>, pAddress=<%p : %p>\n",
|
|
pReceive, (pReceive ? pReceive->pAssociatedAddress : NULL), pAddress));
|
|
|
|
PgmUnlock (pAddress, OldIrq1);
|
|
PgmUnlock (&PgmDynamicConfig, OldIrq);
|
|
|
|
PgmIoComplete (pIrp, STATUS_INTERNAL_ERROR, 0);
|
|
*ppReceive = NULL;
|
|
return (STATUS_INTERNAL_ERROR);
|
|
}
|
|
ASSERT (ConnectId == pReceive->ClientSessionContext);
|
|
|
|
PgmLock (pReceive, OldIrq2);
|
|
|
|
if (!(pReceive->pReceiver->pReceiveData = InitReceiverData (pReceive)))
|
|
{
|
|
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
|
|
|
PgmUnlock (pAddress, OldIrq2);
|
|
PgmUnlock (&PgmDynamicConfig, OldIrq1);
|
|
PgmUnlock (pReceive, OldIrq);
|
|
|
|
PgmIoComplete (pIrp, STATUS_INSUFFICIENT_RESOURCES, 0);
|
|
*ppReceive = NULL;
|
|
|
|
PgmTrace (LogError, ("PgmNewInboundConnection: ERROR -- " \
|
|
"STATUS_INSUFFICIENT_RESOURCES allocating pReceiveData -- pReceive=<%p>, pAddress=<%p>\n",
|
|
pReceive, pAddress));
|
|
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
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 (pAddress->Flags & PGM_ADDRESS_HIGH_SPEED_OPTIMIZED)
|
|
{
|
|
pReceive->pReceiver->MaxPacketsBufferedInLookaside = MAX_PACKETS_BUFFERED * 3; // 9000, about 15 Megs!
|
|
pReceive->pReceiver->InitialOutstandingNakTimeout = INITIAL_NAK_OUTSTANDING_TIMEOUT_MS_OPT /
|
|
BASIC_TIMER_GRANULARITY_IN_MSECS;
|
|
}
|
|
else
|
|
{
|
|
pReceive->pReceiver->MaxPacketsBufferedInLookaside = MAX_PACKETS_BUFFERED;
|
|
pReceive->pReceiver->InitialOutstandingNakTimeout = INITIAL_NAK_OUTSTANDING_TIMEOUT_MSECS /
|
|
BASIC_TIMER_GRANULARITY_IN_MSECS;
|
|
}
|
|
ASSERT (pReceive->pReceiver->MaxPacketsBufferedInLookaside);
|
|
ASSERT (pReceive->pReceiver->InitialOutstandingNakTimeout);
|
|
pReceive->pReceiver->OutstandingNakTimeout = pReceive->pReceiver->InitialOutstandingNakTimeout;
|
|
|
|
//
|
|
// 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)
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
if (!(pReceive->pFECBuffer = PgmAllocMem ((pReceive->MaxFECPacketLength * pAddress->FECGroupSize * 2), PGM_TAG('3'))))
|
|
{
|
|
PgmTrace (LogError, ("PgmNewInboundConnection: ERROR -- " \
|
|
"STATUS_INSUFFICIENT_RESOURCES allocating pFECBuffer, %d bytes\n",
|
|
(pReceive->MaxFECPacketLength * pAddress->FECGroupSize * 2)));
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
else if (!NT_SUCCESS (status = CreateFECContext (&pReceive->FECContext, pAddress->FECGroupSize, FEC_MAX_BLOCK_SIZE, TRUE)))
|
|
{
|
|
PgmFreeMem (pReceive->pFECBuffer);
|
|
pReceive->pFECBuffer = NULL;
|
|
|
|
PgmTrace (LogError, ("PgmNewInboundConnection: ERROR -- " \
|
|
"CreateFECContext returned <%x>\n", status));
|
|
}
|
|
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
if (pReceive->pReceiver->pReceiveData)
|
|
{
|
|
PgmFreeMem (pReceive->pReceiver->pReceiveData);
|
|
pReceive->pReceiver->pReceiveData = NULL;
|
|
}
|
|
|
|
pReceive->SessionFlags |= PGM_SESSION_TERMINATED_ABORT;
|
|
|
|
PgmUnlock (pAddress, OldIrq2);
|
|
PgmUnlock (&PgmDynamicConfig, OldIrq1);
|
|
PgmUnlock (pReceive, OldIrq);
|
|
|
|
PgmIoComplete (pIrp, status, 0);
|
|
*ppReceive = NULL;
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
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'),
|
|
(USHORT) (pReceive->pReceiver->MaxPacketsBufferedInLookaside/pReceive->FECGroupSize));
|
|
}
|
|
|
|
pAddress->LastSpmSource = pAddress->FECOptions = pAddress->FECGroupSize = 0;
|
|
}
|
|
|
|
//
|
|
// Initialize our Connect info
|
|
// Save the SourceId and Src port for this connection
|
|
//
|
|
PgmCopyMemory (&PortNum, &pPgmHeader->CommonHeader.SrcPort, sizeof (USHORT));
|
|
PgmCopyMemory (pReceive->TSI.GSI, pPgmHeader->CommonHeader.gSourceId, SOURCE_ID_LENGTH);
|
|
pReceive->TSI.hPort = 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;
|
|
|
|
pReceive->pReceiver->MaxBufferLength = pReceive->MaxMTULength + sizeof(tPOST_PACKET_FEC_CONTEXT);
|
|
pReceive->pReceiver->DataBufferLookasideLength = pReceive->pReceiver->MaxBufferLength;
|
|
pReceive->SessionFlags |= PGM_SESSION_DATA_FROM_LOOKASIDE;
|
|
ExInitializeNPagedLookasideList (&pReceive->pReceiver->DataBufferLookaside,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
pReceive->pReceiver->DataBufferLookasideLength,
|
|
PGM_TAG ('D'),
|
|
pReceive->pReceiver->MaxPacketsBufferedInLookaside);
|
|
|
|
ExInitializeNPagedLookasideList (&pReceive->pReceiver->NonParityContextLookaside,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
sizeof (tNAK_FORWARD_DATA),
|
|
PGM_TAG ('2'),
|
|
pReceive->pReceiver->MaxPacketsBufferedInLookaside);
|
|
|
|
//
|
|
// 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->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);
|
|
pReceive->pReceiver->FurthestKnownSequenceNumber = pReceive->pReceiver->NextODataSequenceNumber-1;
|
|
|
|
//
|
|
// Since this is the first receive for this session, see if we need to
|
|
// start the receive timer
|
|
//
|
|
InsertTailList (&PgmDynamicConfig.CurrentReceivers, &pReceive->pReceiver->Linkage);
|
|
if (!(PgmDynamicConfig.GlobalFlags & PGM_CONFIG_FLAG_RECEIVE_TIMER_RUNNING))
|
|
{
|
|
pReceive->pReceiver->StartTickCount = PgmDynamicConfig.ReceiversTimerTickCount = 1;
|
|
PgmDynamicConfig.LastReceiverTimeout.QuadPart = KeQueryInterruptTime ();
|
|
PgmDynamicConfig.TimeoutGranularity.QuadPart = BASIC_TIMER_GRANULARITY_IN_MSECS * 10000; // 100ns
|
|
if (!PgmDynamicConfig.TimeoutGranularity.QuadPart)
|
|
{
|
|
ASSERT (0);
|
|
PgmDynamicConfig.TimeoutGranularity.QuadPart = 1;
|
|
}
|
|
|
|
PgmDynamicConfig.GlobalFlags |= PGM_CONFIG_FLAG_RECEIVE_TIMER_RUNNING;
|
|
|
|
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, TRUE);
|
|
|
|
PgmTrace (LogStatus, ("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))
|
|
{
|
|
PgmTrace (LogAllFuncs, ("ProcessReceiveCompletionRoutine: " \
|
|
"pIrp=<%p>, pRcvBuffer=<%p>, Status=<%x> Length=<%d>\n",
|
|
pIrp, Context, pIrp->IoStatus.Status, (ULONG) 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
|
|
{
|
|
PgmTrace (LogError, ("ProcessReceiveCompletionRoutine: ERROR -- " \
|
|
"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,
|
|
#if(WINVER > 0x0500)
|
|
IN TDI_CMSGHDR *pControlData,
|
|
#else
|
|
IN PVOID *pControlData,
|
|
#endif // WINVER
|
|
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 LocalSessionPort, PacketSessionPort;
|
|
tTSI TSI;
|
|
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;
|
|
LARGE_INTEGER TimeValue;
|
|
|
|
KeQuerySystemTime (&TimeValue);
|
|
DropCount = MinDropInterval + ((TimeValue.LowTime >> 8) % (MaxDropInterval - MinDropInterval + 1));
|
|
|
|
/*
|
|
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
|
|
PgmLock (&PgmDynamicConfig, OldIrq);
|
|
if (BytesIndicated > PgmDynamicConfig.MaxMTU)
|
|
{
|
|
PgmDynamicConfig.MaxMTU = BytesIndicated;
|
|
}
|
|
|
|
if (!PGM_VERIFY_HANDLE (pAddress, PGM_VERIFY_ADDRESS))
|
|
{
|
|
PgmTrace (LogPath, ("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
|
|
//
|
|
if ((BytesAvailable != BytesIndicated) &&
|
|
!(ReceiveDatagramFlags & TDI_RECEIVE_ENTIRE_MESSAGE))
|
|
{
|
|
ASSERT (BytesIndicated <= BytesAvailable);
|
|
|
|
//
|
|
// 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;
|
|
|
|
PgmTrace (LogAllFuncs, ("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;
|
|
|
|
PgmTrace (LogError, ("TdiRcvDatagramHandler: ERROR -- " \
|
|
"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<<2) + sizeof(tCOMMON_HEADER))))
|
|
{
|
|
//
|
|
// Need to get at least our header from transport!
|
|
//
|
|
PgmTrace (LogError, ("TdiRcvDatagramHandler: ERROR -- " \
|
|
"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));
|
|
|
|
BytesIndicated -= (pIp->HeaderLength * 4);
|
|
BytesAvailable -= (pIp->HeaderLength * 4);
|
|
|
|
//
|
|
// Now, Verify Checksum
|
|
//
|
|
if ((XSum = tcpxsum (0, (CHAR *) pPgmHeader, BytesIndicated)) != 0xffff)
|
|
{
|
|
//
|
|
// Need to get at least our header from transport!
|
|
//
|
|
PgmTrace (LogError, ("TdiRcvDatagramHandler: ERROR -- " \
|
|
"Bad Checksum on Pgm Packet (type=<%x>)! XSum=<%x> -- Rejecting packet\n",
|
|
pPgmHeader->Type, XSum));
|
|
|
|
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 (&PacketSessionPort, &pPgmHeader->SrcPort, sizeof (USHORT));
|
|
PgmCopyMemory (&TSI.hPort, &pPgmHeader->DestPort, sizeof (USHORT));
|
|
}
|
|
else
|
|
{
|
|
PgmCopyMemory (&PacketSessionPort, &pPgmHeader->DestPort, sizeof (USHORT));
|
|
PgmCopyMemory (&TSI.hPort, &pPgmHeader->SrcPort, sizeof (USHORT));
|
|
}
|
|
PacketSessionPort = ntohs (PacketSessionPort);
|
|
TSI.hPort = ntohs (TSI.hPort);
|
|
PgmCopyMemory (&TSI.GSI, &pPgmHeader->gSourceId, SOURCE_ID_LENGTH);
|
|
|
|
//
|
|
// If this packet is for a different session port, drop it
|
|
//
|
|
if (pAddress->ReceiverMCastAddr)
|
|
{
|
|
LocalSessionPort = pAddress->ReceiverMCastPort;
|
|
}
|
|
else
|
|
{
|
|
LocalSessionPort = pAddress->SenderMCastPort;
|
|
}
|
|
|
|
if (LocalSessionPort != PacketSessionPort)
|
|
{
|
|
PgmTrace (LogPath, ("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)) &&
|
|
(pSession->TSI.ULLTSI == TSI.ULLTSI) &&
|
|
!(pSession->SessionFlags & (PGM_SESSION_CLIENT_DISCONNECTED | PGM_SESSION_TERMINATED_ABORT)))
|
|
{
|
|
if (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
|
|
{
|
|
PgmTrace (LogError, ("TdiRcvDatagramHandler: ERROR -- " \
|
|
"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;
|
|
}
|
|
|
|
ASSERT (pSession->pSender);
|
|
PGM_REFERENCE_SESSION_SEND (pSession, REF_SESSION_TDI_RCV_HANDLER, TRUE);
|
|
|
|
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
|
|
if ((pIpAddress->TAAddressCount != 1) ||
|
|
(pIpAddress->Address[0].AddressLength != TDI_ADDRESS_LENGTH_IP) ||
|
|
(pIpAddress->Address[0].AddressType != TDI_ADDRESS_TYPE_IP))
|
|
{
|
|
PgmTrace (LogError, ("TdiRcvDatagramHandler: ERROR -- " \
|
|
"[1] Bad AddrType=<%d>\n", pIpAddress->Address[0].AddressType));
|
|
|
|
PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_TDI_RCV_HANDLER);
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
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,
|
|
pIpAddress,
|
|
ReceiveDatagramFlags,
|
|
(tBASIC_DATA_PACKET_HEADER UNALIGNED *) pPgmHeader,
|
|
BytesIndicated,
|
|
&pSession);
|
|
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmTrace (LogAllFuncs, ("TdiRcvDatagramHandler: " \
|
|
"pAddress=<%p> FAILed to accept new connection, PacketType=<%x>, status=<%x>\n",
|
|
pAddress, PacketType, status));
|
|
}
|
|
}
|
|
else if ((PacketType == PACKET_TYPE_SPM) &&
|
|
(BytesIndicated >= sizeof(tBASIC_SPM_PACKET_HEADER)))
|
|
{
|
|
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(WINVER > 0x0500)
|
|
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);
|
|
}
|
|
}
|
|
#endif // WINVER
|
|
|
|
//
|
|
// Now, handle the packet appropriately
|
|
//
|
|
// Use fast Path for Data packets!
|
|
//
|
|
if ((PacketType == PACKET_TYPE_ODATA) ||
|
|
(PacketType == PACKET_TYPE_RDATA))
|
|
{
|
|
if (pAddress->ReceiverMCastAddr)
|
|
{
|
|
if (PacketType == PACKET_TYPE_ODATA)
|
|
{
|
|
pSession->pReceiver->NumODataPacketsReceived++;
|
|
}
|
|
else
|
|
{
|
|
pSession->pReceiver->NumRDataPacketsReceived++;
|
|
}
|
|
pSession->pReceiver->LastSessionTickCount = PgmDynamicConfig.ReceiversTimerTickCount;
|
|
pSession->TotalBytes += BytesIndicated;
|
|
pSession->TotalPacketsReceived++;
|
|
status = ProcessDataPacket (pAddress,
|
|
pSession,
|
|
BytesIndicated,
|
|
(tBASIC_DATA_PACKET_HEADER UNALIGNED *) pPgmHeader,
|
|
PacketType);
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogError, ("TdiRcvDatagramHandler: ERROR -- " \
|
|
"Received Data packet, not on Receiver session! pSession=<%p>\n", pSession));
|
|
status = STATUS_DATA_NOT_ACCEPTED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = PgmProcessIncomingPacket (pAddress,
|
|
pSession,
|
|
SourceAddressLength,
|
|
pIpAddress,
|
|
BytesIndicated,
|
|
pPgmHeader,
|
|
PacketType);
|
|
}
|
|
|
|
PgmTrace (LogAllFuncs, ("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);
|
|
|
|
PgmTrace (LogError, ("PgmCancelReceiveIrp: ERROR -- " \
|
|
"pIrp=<%p> pReceive=<%p>, pAddress=<%p>\n",
|
|
pIrp, pReceive, (pReceive ? pReceive->pReceiver->pAddress : NULL)));
|
|
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)))
|
|
{
|
|
PgmTrace (LogError, ("PgmReceive: ERROR -- " \
|
|
"Invalid Handles pReceive=<%p>, pAddress=<%p>\n", pReceive, pAddress));
|
|
|
|
status = STATUS_INVALID_HANDLE;
|
|
}
|
|
else if (pReceive->SessionFlags & PGM_SESSION_CLIENT_DISCONNECTED)
|
|
{
|
|
PgmTrace (LogPath, ("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);
|
|
PgmTrace (LogError, ("PgmReceive: ERROR -- " \
|
|
"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);
|
|
|
|
PgmTrace (LogError, ("PgmReceive: ERROR -- " \
|
|
"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);
|
|
|
|
PgmTrace (LogAllFuncs, ("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
|
|
//
|
|
// if (!(pAddress->Flags & PGM_ADDRESS_HIGH_SPEED_OPTIMIZED))
|
|
{
|
|
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);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|