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.
4199 lines
158 KiB
4199 lines
158 KiB
/*++
|
|
|
|
Copyright (c) 2000-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Send.c
|
|
|
|
Abstract:
|
|
|
|
This module implements Send routines
|
|
the PGM Transport
|
|
|
|
Author:
|
|
|
|
Mohammad Shabbir Alam (MAlam) 3-30-2000
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
#ifdef FILE_LOGGING
|
|
#include "send.tmh"
|
|
#endif // FILE_LOGGING
|
|
|
|
//******************* Pageable Routine Declarations ****************
|
|
#ifdef ALLOC_PRAGMA
|
|
#endif
|
|
//******************* Pageable Routine Declarations ****************
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
InitDataSpmOptions(
|
|
IN tCOMMON_SESSION_CONTEXT *pSession,
|
|
IN tCLIENT_SEND_REQUEST *pSendContext,
|
|
IN PUCHAR pOptions,
|
|
IN OUT USHORT *pBufferSize,
|
|
IN ULONG PgmOptionsFlag,
|
|
IN tPACKET_OPTIONS *pPacketOptions
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the header options for Data and Spm packets
|
|
|
|
Arguments:
|
|
|
|
IN pOptions -- Options buffer
|
|
IN OUT pBufferSize -- IN Maximum packet size, OUT Options length
|
|
IN PgmOptionsFlag -- Options requested to be set by caller
|
|
IN pPacketOptions -- Data for specific options
|
|
IN pSendContext -- Context for this send
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Final status of the call
|
|
|
|
--*/
|
|
{
|
|
ULONG pOptionsData[3];
|
|
USHORT OptionsLength = 0;
|
|
USHORT MaxBufferSize = *pBufferSize;
|
|
tPACKET_OPTION_GENERIC UNALIGNED *pOptionHeader;
|
|
tPACKET_OPTION_LENGTH UNALIGNED *pLengthOption = (tPACKET_OPTION_LENGTH UNALIGNED *) pOptions;
|
|
|
|
//
|
|
// Set the Packet Extension information
|
|
//
|
|
OptionsLength += PGM_PACKET_EXTENSION_LENGTH;
|
|
if (OptionsLength > MaxBufferSize)
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmOptions: ERROR -- " \
|
|
"Not enough space for HeaderExtension! <%d> > <%d>\n", OptionsLength, MaxBufferSize));
|
|
return (STATUS_INVALID_BLOCK_LENGTH);
|
|
}
|
|
pLengthOption->Type = PACKET_OPTION_LENGTH;
|
|
pLengthOption->Length = PGM_PACKET_EXTENSION_LENGTH;
|
|
|
|
//
|
|
// First fill in the Network-Element-specific options:
|
|
//
|
|
if (PgmOptionsFlag & (PGM_OPTION_FLAG_CRQST | PGM_OPTION_FLAG_NBR_UNREACH))
|
|
{
|
|
// Not supporting these options for now
|
|
ASSERT (0);
|
|
return (STATUS_NOT_SUPPORTED);
|
|
}
|
|
|
|
if (PgmOptionsFlag & PGM_OPTION_FLAG_PARITY_PRM)
|
|
{
|
|
//
|
|
// Applies to SPMs only
|
|
//
|
|
pOptionHeader = (tPACKET_OPTION_GENERIC UNALIGNED *) &pOptions[OptionsLength];
|
|
OptionsLength += PGM_PACKET_OPT_PARITY_PRM_LENGTH;
|
|
if (OptionsLength > MaxBufferSize)
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmOptions: ERROR -- " \
|
|
"Not enough space for PARITY_PRM Option! <%d> > <%d>\n", OptionsLength, MaxBufferSize));
|
|
return (STATUS_INVALID_BLOCK_LENGTH);
|
|
}
|
|
pOptionHeader->E_OptionType = PACKET_OPTION_PARITY_PRM;
|
|
pOptionHeader->OptionLength = PGM_PACKET_OPT_PARITY_PRM_LENGTH;
|
|
|
|
pOptionHeader->U_OptSpecific = pSession->FECOptions;
|
|
pOptionsData[0] = htonl (pPacketOptions->FECContext.FECGroupInfo);
|
|
PgmCopyMemory ((pOptionHeader + 1), pOptionsData, (sizeof(ULONG)));
|
|
}
|
|
|
|
if (PgmOptionsFlag & PGM_OPTION_FLAG_PARITY_CUR_TGSIZE)
|
|
{
|
|
pOptionHeader = (tPACKET_OPTION_GENERIC UNALIGNED *) &pOptions[OptionsLength];
|
|
OptionsLength += PGM_PACKET_OPT_PARITY_CUR_TGSIZE_LENGTH;
|
|
if (OptionsLength > MaxBufferSize)
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmOptions: ERROR -- " \
|
|
"Not enough space for PARITY_CUR_TGSIZE Option! <%d> > <%d>\n", OptionsLength, MaxBufferSize));
|
|
return (STATUS_INVALID_BLOCK_LENGTH);
|
|
}
|
|
pOptionHeader->E_OptionType = PACKET_OPTION_CURR_TGSIZE;
|
|
pOptionHeader->OptionLength = PGM_PACKET_OPT_PARITY_CUR_TGSIZE_LENGTH;
|
|
pOptionsData[0] = htonl (pPacketOptions->FECContext.NumPacketsInThisGroup);
|
|
PgmCopyMemory ((pOptionHeader + 1), pOptionsData, (sizeof(ULONG)));
|
|
}
|
|
|
|
//
|
|
// Now, fill in the non-Network significant options
|
|
//
|
|
if (PgmOptionsFlag & PGM_OPTION_FLAG_SYN)
|
|
{
|
|
pOptionHeader = (tPACKET_OPTION_GENERIC UNALIGNED *) &pOptions[OptionsLength];
|
|
OptionsLength += PGM_PACKET_OPT_SYN_LENGTH;
|
|
if (OptionsLength > MaxBufferSize)
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmOptions: ERROR -- " \
|
|
"Not enough space for SYN Option! <%d> > <%d>\n", OptionsLength, MaxBufferSize));
|
|
return (STATUS_INVALID_BLOCK_LENGTH);
|
|
}
|
|
|
|
pOptionHeader->E_OptionType = PACKET_OPTION_SYN;
|
|
pOptionHeader->OptionLength = PGM_PACKET_OPT_SYN_LENGTH;
|
|
|
|
if ((pSendContext) &&
|
|
(pSendContext->DataOptions & PGM_OPTION_FLAG_SYN))
|
|
{
|
|
//
|
|
// Remove this option once it has been used!
|
|
//
|
|
pSendContext->DataOptions &= ~PGM_OPTION_FLAG_SYN;
|
|
pSendContext->DataOptionsLength -= PGM_PACKET_OPT_SYN_LENGTH;
|
|
if (!pSendContext->DataOptions)
|
|
{
|
|
// No other options, so set the length to 0
|
|
pSendContext->DataOptionsLength = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (PgmOptionsFlag & PGM_OPTION_FLAG_FIN)
|
|
{
|
|
pOptionHeader = (tPACKET_OPTION_GENERIC UNALIGNED *) &pOptions[OptionsLength];
|
|
OptionsLength += PGM_PACKET_OPT_FIN_LENGTH;
|
|
if (OptionsLength > MaxBufferSize)
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmOptions: ERROR -- " \
|
|
"Not enough space for FIN Option! <%d> > <%d>\n", OptionsLength, MaxBufferSize));
|
|
return (STATUS_INVALID_BLOCK_LENGTH);
|
|
}
|
|
pOptionHeader->E_OptionType = PACKET_OPTION_FIN;
|
|
pOptionHeader->OptionLength = PGM_PACKET_OPT_FIN_LENGTH;
|
|
}
|
|
|
|
if (PgmOptionsFlag & (PGM_OPTION_FLAG_RST | PGM_OPTION_FLAG_RST_N))
|
|
{
|
|
pOptionHeader = (tPACKET_OPTION_GENERIC UNALIGNED *) &pOptions[OptionsLength];
|
|
OptionsLength += PGM_PACKET_OPT_RST_LENGTH;
|
|
if (OptionsLength > MaxBufferSize)
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmOptions: ERROR -- " \
|
|
"Not enough space for RST Option! <%d> > <%d>\n", OptionsLength, MaxBufferSize));
|
|
return (STATUS_INVALID_BLOCK_LENGTH);
|
|
}
|
|
pOptionHeader->E_OptionType = PACKET_OPTION_RST;
|
|
pOptionHeader->OptionLength = PGM_PACKET_OPT_RST_LENGTH;
|
|
if (PgmOptionsFlag & PGM_OPTION_FLAG_RST_N)
|
|
{
|
|
pOptionHeader->U_OptSpecific = PACKET_OPTION_SPECIFIC_RST_N_BIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// now, set the FEC-specific options
|
|
//
|
|
if (PgmOptionsFlag & PGM_OPTION_FLAG_PARITY_GRP)
|
|
{
|
|
//
|
|
// Applies to Parity packets only
|
|
//
|
|
pOptionHeader = (tPACKET_OPTION_GENERIC UNALIGNED *) &pOptions[OptionsLength];
|
|
OptionsLength += PGM_PACKET_OPT_PARITY_GRP_LENGTH;
|
|
if (OptionsLength > MaxBufferSize)
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmOptions: ERROR -- " \
|
|
"Not enough space for PARITY_GRP Option! <%d> > <%d>\n", OptionsLength, MaxBufferSize));
|
|
return (STATUS_INVALID_BLOCK_LENGTH);
|
|
}
|
|
pOptionHeader->E_OptionType = PACKET_OPTION_PARITY_GRP;
|
|
pOptionHeader->OptionLength = PGM_PACKET_OPT_PARITY_GRP_LENGTH;
|
|
|
|
pOptionsData[0] = htonl (pPacketOptions->FECContext.FECGroupInfo);
|
|
PgmCopyMemory ((pOptionHeader + 1), pOptionsData, (sizeof(ULONG)));
|
|
}
|
|
|
|
//
|
|
// The following options should always be at the end, since they
|
|
// are never net-sig.
|
|
//
|
|
if (PgmOptionsFlag & PGM_OPTION_FLAG_FRAGMENT)
|
|
{
|
|
pPacketOptions->FragmentOptionOffset = OptionsLength;
|
|
|
|
pOptionHeader = (tPACKET_OPTION_GENERIC UNALIGNED *) &pOptions[OptionsLength];
|
|
OptionsLength += PGM_PACKET_OPT_FRAGMENT_LENGTH;
|
|
if (OptionsLength > MaxBufferSize)
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmOptions: ERROR -- " \
|
|
"Not enough space for FragmentExtension! <%d> > <%d>\n", OptionsLength, MaxBufferSize));
|
|
return (STATUS_INVALID_BLOCK_LENGTH);
|
|
}
|
|
pOptionHeader->E_OptionType = PACKET_OPTION_FRAGMENT;
|
|
pOptionHeader->OptionLength = PGM_PACKET_OPT_FRAGMENT_LENGTH;
|
|
|
|
//
|
|
// The PACKET_OPTION_RES_F_OPX_ENCODED_BIT will be set if necessary
|
|
// later since the OptionSpecific component is computed at the same
|
|
// time the entire data is encoded
|
|
//
|
|
pOptionsData[0] = htonl ((ULONG) pPacketOptions->MessageFirstSequence);
|
|
pOptionsData[1] = htonl (pPacketOptions->MessageOffset);
|
|
pOptionsData[2] = htonl (pPacketOptions->MessageLength);
|
|
PgmCopyMemory ((pOptionHeader + 1), pOptionsData, (3 * sizeof(ULONG)));
|
|
}
|
|
|
|
if (PgmOptionsFlag & PGM_OPTION_FLAG_JOIN)
|
|
{
|
|
pPacketOptions->LateJoinerOptionOffset = OptionsLength;
|
|
|
|
pOptionHeader = (tPACKET_OPTION_GENERIC UNALIGNED *) &pOptions[OptionsLength];
|
|
OptionsLength += PGM_PACKET_OPT_JOIN_LENGTH;
|
|
if (OptionsLength > MaxBufferSize)
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmOptions: ERROR -- " \
|
|
"Not enough space for JOIN Option! <%d> > <%d>\n", OptionsLength, MaxBufferSize));
|
|
return (STATUS_INVALID_BLOCK_LENGTH);
|
|
}
|
|
pOptionHeader->E_OptionType = PACKET_OPTION_JOIN;
|
|
pOptionHeader->OptionLength = PGM_PACKET_OPT_JOIN_LENGTH;
|
|
pOptionsData[0] = htonl ((ULONG) (SEQ_TYPE) pPacketOptions->LateJoinerSequence);
|
|
PgmCopyMemory ((pOptionHeader + 1), pOptionsData, (sizeof(ULONG)));
|
|
}
|
|
|
|
//
|
|
// So far, so good -- so set the rest of the option-specific info
|
|
//
|
|
if (OptionsLength)
|
|
{
|
|
pLengthOption->TotalOptionsLength = htons (OptionsLength); // Total length of all options
|
|
pOptionHeader->E_OptionType |= PACKET_OPTION_TYPE_END_BIT; // Indicates the last option
|
|
}
|
|
|
|
*pBufferSize = OptionsLength;
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
InitDataSpmHeader(
|
|
IN tCOMMON_SESSION_CONTEXT *pSession,
|
|
IN tCLIENT_SEND_REQUEST *pSendContext,
|
|
IN PUCHAR pHeader,
|
|
IN OUT USHORT *pHeaderLength,
|
|
IN ULONG PgmOptionsFlag,
|
|
IN tPACKET_OPTIONS *pPacketOptions,
|
|
IN UCHAR PacketType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes most of the header for Data and Spm packets
|
|
and fills in all of the optional fields
|
|
|
|
Arguments:
|
|
|
|
IN pSession -- Pgm session (sender) context
|
|
IN pHeader -- Packet buffer
|
|
IN pHeaderLength -- Maximum packet size
|
|
IN PgmOptionsFlag -- Options requested to be set by caller
|
|
IN pPacketOptions -- Data for specific options
|
|
IN PacketType -- whether Data or Spm packet
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Final status of the call
|
|
|
|
--*/
|
|
{
|
|
tCOMMON_HEADER *pCommonHeader = (tCOMMON_HEADER *) pHeader;
|
|
USHORT HeaderLength;
|
|
USHORT OptionsLength;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
// NOTE: Session Lock must be held on Entry and Exit!
|
|
|
|
if (!(PGM_VERIFY_HANDLE2 (pSession, PGM_VERIFY_SESSION_SEND, PGM_VERIFY_SESSION_DOWN)))
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmHeader: ERROR -- " \
|
|
"Bad Session ptr = <%p>\n", pSession));
|
|
return (STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
//
|
|
// Memory for the Header must have been pre-allocated by the caller
|
|
//
|
|
if (*pHeaderLength < sizeof (tCOMMON_HEADER))
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmHeader: ERROR -- " \
|
|
"InBufferLength = <%x> < Min = <%d>\n", *pHeaderLength, sizeof (tCOMMON_HEADER)));
|
|
return (STATUS_INVALID_BUFFER_SIZE);
|
|
}
|
|
|
|
pCommonHeader->SrcPort = htons (pSession->TSI.hPort);
|
|
PgmCopyMemory (&pCommonHeader->gSourceId, pSession->TSI.GSI, SOURCE_ID_LENGTH);
|
|
pCommonHeader->Type = PacketType;
|
|
pCommonHeader->Options = 0;
|
|
pCommonHeader->DestPort = htons (pSession->pSender->DestMCastPort);
|
|
|
|
//
|
|
// Now, set the initial header size and verify that we have a
|
|
// valid set of options based on the Packet type
|
|
//
|
|
switch (PacketType)
|
|
{
|
|
case (PACKET_TYPE_SPM):
|
|
{
|
|
HeaderLength = sizeof (tBASIC_SPM_PACKET_HEADER);
|
|
if (PgmOptionsFlag != (PGM_VALID_SPM_OPTION_FLAGS & PgmOptionsFlag))
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmHeader: ERROR -- " \
|
|
"Unsupported Options flags=<%x> for SPM packets\n", PgmOptionsFlag));
|
|
|
|
return (STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (PgmOptionsFlag & NETWORK_SIG_SPM_OPTIONS_FLAGS)
|
|
{
|
|
pCommonHeader->Options |= PACKET_HEADER_OPTIONS_NETWORK_SIGNIFICANT;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case (PACKET_TYPE_ODATA):
|
|
{
|
|
HeaderLength = sizeof (tBASIC_DATA_PACKET_HEADER);
|
|
if (PgmOptionsFlag != (PGM_VALID_DATA_OPTION_FLAGS & PgmOptionsFlag))
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmHeader: ERROR -- " \
|
|
"Unsupported Options flags=<%x> for ODATA packets\n", PgmOptionsFlag));
|
|
|
|
return (STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (PgmOptionsFlag & NETWORK_SIG_ODATA_OPTIONS_FLAGS)
|
|
{
|
|
pCommonHeader->Options |= PACKET_HEADER_OPTIONS_NETWORK_SIGNIFICANT;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case (PACKET_TYPE_RDATA):
|
|
{
|
|
HeaderLength = sizeof (tBASIC_DATA_PACKET_HEADER);
|
|
if (PgmOptionsFlag != (PGM_VALID_DATA_OPTION_FLAGS & PgmOptionsFlag))
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmHeader: ERROR -- " \
|
|
"Unsupported Options flags=<%x> for RDATA packets\n", PgmOptionsFlag));
|
|
|
|
return (STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
if (PgmOptionsFlag & NETWORK_SIG_RDATA_OPTIONS_FLAGS)
|
|
{
|
|
pCommonHeader->Options |= PACKET_HEADER_OPTIONS_NETWORK_SIGNIFICANT;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmHeader: ERROR -- " \
|
|
"Unsupported packet type = <%x>\n", PacketType));
|
|
|
|
return (STATUS_INVALID_PARAMETER); // Unrecognized Packet type!
|
|
}
|
|
}
|
|
|
|
if (*pHeaderLength < HeaderLength)
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmHeader: ERROR -- " \
|
|
"InBufferLength=<%x> < HeaderLength=<%d> based on PacketType=<%x>\n",
|
|
*pHeaderLength, HeaderLength, PacketType));
|
|
|
|
return (STATUS_INVALID_BLOCK_LENGTH);
|
|
}
|
|
|
|
//
|
|
// Add any options if specified
|
|
//
|
|
OptionsLength = 0;
|
|
if (PgmOptionsFlag)
|
|
{
|
|
OptionsLength = *pHeaderLength - HeaderLength;
|
|
status = InitDataSpmOptions (pSession,
|
|
pSendContext,
|
|
&pHeader[HeaderLength],
|
|
&OptionsLength,
|
|
PgmOptionsFlag,
|
|
pPacketOptions);
|
|
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmTrace (LogError, ("InitDataSpmHeader: ERROR -- " \
|
|
"InitDataSpmOptions returned <%x>\n", status));
|
|
|
|
return (status);
|
|
}
|
|
|
|
//
|
|
// So far, so good -- so set the rest of the option-specific info
|
|
//
|
|
pCommonHeader->Options |= PACKET_HEADER_OPTIONS_PRESENT; // Set the options bit
|
|
}
|
|
|
|
//
|
|
// The caller must now set the Checksum and other header information
|
|
//
|
|
PgmTrace (LogAllFuncs, ("InitDataSpmHeader: " \
|
|
"pHeader=<%p>, HeaderLength=<%d>, OptionsLength=<%d>\n",
|
|
pHeader, (ULONG) HeaderLength, (ULONG) OptionsLength));
|
|
|
|
*pHeaderLength = HeaderLength + OptionsLength;
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
PgmSendSpmCompletion(
|
|
IN tSEND_SESSION *pSend,
|
|
IN tBASIC_SPM_PACKET_HEADER *pSpmPacket,
|
|
IN NTSTATUS status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the transport when the Spm send has been completed
|
|
|
|
Arguments:
|
|
|
|
IN pSend -- Pgm session (sender) context
|
|
IN pSpmPacket -- Spm packet buffer
|
|
IN status --
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
{
|
|
PGMLockHandle OldIrq;
|
|
|
|
PgmLock (pSend, OldIrq);
|
|
if (NT_SUCCESS (status))
|
|
{
|
|
//
|
|
// Set the Spm statistics
|
|
//
|
|
PgmTrace (LogAllFuncs, ("PgmSendSpmCompletion: " \
|
|
"SUCCEEDED\n"));
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogError, ("PgmSendSpmCompletion: ERROR -- " \
|
|
"status=<%x>\n", status));
|
|
}
|
|
PgmUnlock (pSend, OldIrq);
|
|
|
|
PGM_DEREFERENCE_SESSION_SEND (pSend, REF_SESSION_SEND_SPM);
|
|
|
|
//
|
|
// Free the Memory that was allocated for this
|
|
//
|
|
PgmFreeMem (pSpmPacket);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
PgmSendSpm(
|
|
IN tSEND_SESSION *pSend,
|
|
IN PGMLockHandle *pOldIrq,
|
|
OUT ULONG *pBytesSent
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to send an Spm packet
|
|
The pSend lock is held before calling this routine
|
|
|
|
Arguments:
|
|
|
|
IN pSend -- Pgm session (sender) context
|
|
IN pOldIrq -- pSend's OldIrq
|
|
OUT pBytesSent -- Set if send succeeded (used for calculating throughput)
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Final status of the send
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
ULONG XSum, OptionsFlags;
|
|
tBASIC_SPM_PACKET_HEADER *pSpmPacket = NULL;
|
|
tPACKET_OPTIONS PacketOptions;
|
|
USHORT PacketLength = (USHORT) pSend->pSender->pAddress->OutIfMTU; // Init to max
|
|
|
|
*pBytesSent = 0;
|
|
|
|
if (!(pSpmPacket = PgmAllocMem (PacketLength, PGM_TAG('2'))))
|
|
{
|
|
PgmTrace (LogError, ("PgmSendSpm: ERROR -- " \
|
|
"STATUS_INSUFFICIENT_RESOURCES\n"));
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
PgmZeroMemory (pSpmPacket, PacketLength);
|
|
PgmZeroMemory (&PacketOptions, sizeof(tPACKET_OPTIONS));
|
|
|
|
OptionsFlags = pSend->pSender->SpmOptions;
|
|
if (OptionsFlags & PGM_OPTION_FLAG_JOIN)
|
|
{
|
|
//
|
|
// See if we have enough packets for the LateJoiner sequence numbers
|
|
//
|
|
if (SEQ_GEQ (pSend->pSender->NextODataSequenceNumber, (pSend->pSender->TrailingGroupSequenceNumber +
|
|
pSend->pSender->LateJoinSequenceNumbers)))
|
|
{
|
|
PacketOptions.LateJoinerSequence = (ULONG) (SEQ_TYPE) (pSend->pSender->NextODataSequenceNumber -
|
|
pSend->pSender->LateJoinSequenceNumbers);
|
|
}
|
|
else
|
|
{
|
|
PacketOptions.LateJoinerSequence = (ULONG) (SEQ_TYPE) pSend->pSender->TrailingGroupSequenceNumber;
|
|
}
|
|
}
|
|
|
|
if (OptionsFlags & PGM_OPTION_FLAG_PARITY_PRM) // Check if this is FEC-enabled
|
|
{
|
|
PacketOptions.FECContext.FECGroupInfo = pSend->FECGroupSize;
|
|
|
|
//
|
|
// See if we need to set the CURR_TGSIZE option for variable Group length
|
|
//
|
|
if ((pSend->pSender->EmptySequencesForLastSend) &&
|
|
(pSend->pSender->LastVariableTGPacketSequenceNumber ==
|
|
(pSend->pSender->NextODataSequenceNumber - (1 + pSend->pSender->EmptySequencesForLastSend))))
|
|
{
|
|
PacketOptions.FECContext.NumPacketsInThisGroup = pSend->FECGroupSize -
|
|
(UCHAR)pSend->pSender->EmptySequencesForLastSend;
|
|
OptionsFlags |= PGM_OPTION_FLAG_PARITY_CUR_TGSIZE;
|
|
ASSERT (PacketOptions.FECContext.NumPacketsInThisGroup);
|
|
}
|
|
}
|
|
|
|
status = InitDataSpmHeader (pSend,
|
|
NULL,
|
|
(PUCHAR) pSpmPacket,
|
|
&PacketLength,
|
|
OptionsFlags,
|
|
&PacketOptions,
|
|
PACKET_TYPE_SPM);
|
|
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmTrace (LogError, ("PgmSendSpm: ERROR -- " \
|
|
"InitDataSpmHeader returned <%x>\n", status));
|
|
|
|
PgmFreeMem (pSpmPacket);
|
|
return (status);
|
|
}
|
|
|
|
ASSERT (PacketLength);
|
|
|
|
pSpmPacket->SpmSequenceNumber = htonl ((ULONG) pSend->pSender->NextSpmSequenceNumber++);
|
|
pSpmPacket->TrailingEdgeSeqNumber = htonl ((ULONG) pSend->pSender->TrailingGroupSequenceNumber);
|
|
pSpmPacket->LeadingEdgeSeqNumber = htonl ((ULONG)((SEQ_TYPE)(pSend->pSender->NextODataSequenceNumber - 1)));
|
|
pSpmPacket->PathNLA.NLA_AFI = htons (IPV4_NLA_AFI);
|
|
pSpmPacket->PathNLA.IpAddress = htonl (pSend->pSender->SenderMCastOutIf);
|
|
|
|
pSpmPacket->CommonHeader.Checksum = 0;
|
|
XSum = 0;
|
|
XSum = tcpxsum (XSum, (CHAR *) pSpmPacket, PacketLength); // Compute the Checksum
|
|
pSpmPacket->CommonHeader.Checksum = (USHORT) (~XSum);
|
|
|
|
PGM_REFERENCE_SESSION_SEND (pSend, REF_SESSION_SEND_SPM, TRUE);
|
|
PgmUnlock (pSend, *pOldIrq);
|
|
|
|
status = TdiSendDatagram (pSend->pSender->pAddress->pRAlertFileObject,
|
|
pSend->pSender->pAddress->pRAlertDeviceObject,
|
|
pSpmPacket,
|
|
PacketLength,
|
|
PgmSendSpmCompletion, // Completion
|
|
pSend, // Context1
|
|
pSpmPacket, // Context2
|
|
pSend->pSender->DestMCastIpAddress,
|
|
pSend->pSender->DestMCastPort,
|
|
FALSE);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
PgmTrace (LogAllFuncs, ("PgmSendSpm: " \
|
|
"Sent <%d> bytes to <%x:%d>, Options=<%x>, Window=[%d--%d]\n",
|
|
(ULONG) PacketLength, pSend->pSender->DestMCastIpAddress, pSend->pSender->DestMCastPort,
|
|
OptionsFlags, (ULONG) pSend->pSender->TrailingGroupSequenceNumber,
|
|
(ULONG) (pSend->pSender->NextODataSequenceNumber-1)));
|
|
|
|
PgmLock (pSend, *pOldIrq);
|
|
|
|
*pBytesSent = PacketLength;
|
|
return (status);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
PgmSendRDataCompletion(
|
|
IN tSEND_RDATA_CONTEXT *pRDataContext,
|
|
IN PVOID pRDataBuffer,
|
|
IN NTSTATUS status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the transport when the RData send has been completed
|
|
|
|
Arguments:
|
|
|
|
IN pRDataContext -- RData context
|
|
IN pContext2 -- not used
|
|
IN status --
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
{
|
|
tSEND_SESSION *pSend = pRDataContext->pSend;
|
|
PGMLockHandle OldIrq;
|
|
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmTrace (LogError, ("PgmSendRDataCompletion: ERROR -- " \
|
|
"status=<%x>\n", status));
|
|
}
|
|
|
|
//
|
|
// Set the RData statistics
|
|
//
|
|
PgmLock (pSend, OldIrq);
|
|
if ((!--pRDataContext->NumPacketsInTransport) &&
|
|
(!AnyMoreNaks(pRDataContext)))
|
|
{
|
|
ASSERT (pSend->pSender->NumRDataRequestsPending <= pSend->pSender->pRDataInfo->NumAllocated);
|
|
if (pRDataContext->PostRDataHoldTime)
|
|
{
|
|
if (!pRDataContext->CleanupTime)
|
|
{
|
|
pSend->pSender->NumRDataRequestsPending--;
|
|
}
|
|
pRDataContext->CleanupTime = pSend->pSender->TimerTickCount + pRDataContext->PostRDataHoldTime;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We have already removed the entry, so just destroy it!
|
|
//
|
|
DestroyEntry (pSend->pSender->pRDataInfo, pRDataContext);
|
|
}
|
|
}
|
|
PgmUnlock (pSend, OldIrq);
|
|
|
|
if (pRDataBuffer)
|
|
{
|
|
ExFreeToNPagedLookasideList (&pSend->pSender->SenderBufferLookaside, pRDataBuffer);
|
|
}
|
|
|
|
PgmTrace (LogAllFuncs, ("PgmSendRDataCompletion: " \
|
|
"status=<%x>, pRDataBuffer=<%p>\n", status, pRDataBuffer));
|
|
|
|
PGM_DEREFERENCE_SESSION_SEND (pSend, REF_SESSION_SEND_RDATA);
|
|
return;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
PgmBuildParityPacket(
|
|
IN tSEND_SESSION *pSend,
|
|
IN tPACKET_BUFFER *pPacketBuffer,
|
|
IN tBUILD_PARITY_CONTEXT *pParityContext,
|
|
IN PUCHAR pFECPacket,
|
|
IN OUT USHORT *pPacketLength,
|
|
IN UCHAR PacketType
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
tPACKET_OPTIONS PacketOptions;
|
|
tPOST_PACKET_FEC_CONTEXT UNALIGNED *pFECContext;
|
|
tPOST_PACKET_FEC_CONTEXT FECContext;
|
|
ULONG SequenceNumber;
|
|
ULONG FECGroupMask;
|
|
tPACKET_OPTION_GENERIC UNALIGNED *pOptionHeader;
|
|
USHORT PacketLength = *pPacketLength; // Init to max buffer length
|
|
tBASIC_DATA_PACKET_HEADER UNALIGNED *pRData = (tBASIC_DATA_PACKET_HEADER UNALIGNED *)
|
|
&pPacketBuffer->DataPacket;
|
|
|
|
*pPacketLength = 0; // Init, in case of error
|
|
|
|
//
|
|
// First, get the options encoded in this RData packet to see
|
|
// if we need to use them!
|
|
//
|
|
FECGroupMask = pSend->FECGroupSize - 1;
|
|
pParityContext->NextFECPacketIndex = pPacketBuffer->PacketOptions.FECContext.SenderNextFECPacketIndex;
|
|
SequenceNumber = (ntohl(pRData->DataSequenceNumber)) | (pParityContext->NextFECPacketIndex & FECGroupMask);
|
|
ASSERT (!(pParityContext->OptionsFlags & ~(PGM_OPTION_FLAG_SYN |
|
|
PGM_OPTION_FLAG_FIN |
|
|
PGM_OPTION_FLAG_FRAGMENT |
|
|
PGM_OPTION_FLAG_PARITY_CUR_TGSIZE |
|
|
PGM_OPTION_FLAG_PARITY_GRP)));
|
|
|
|
PgmZeroMemory (&PacketOptions, sizeof (tPACKET_OPTIONS));
|
|
|
|
//
|
|
// We don't need to set any parameters for the SYN and FIN options
|
|
// We will set the parameters for the FRAGMENT option (if needed) later
|
|
// since will need to have the encoded paramters
|
|
//
|
|
if (pParityContext->OptionsFlags & PGM_OPTION_FLAG_PARITY_CUR_TGSIZE)
|
|
{
|
|
ASSERT (pParityContext->NumPacketsInThisGroup);
|
|
PacketOptions.FECContext.NumPacketsInThisGroup = pParityContext->NumPacketsInThisGroup;
|
|
}
|
|
|
|
if (pParityContext->NextFECPacketIndex >= pSend->FECGroupSize)
|
|
{
|
|
pParityContext->OptionsFlags |= PGM_OPTION_FLAG_PARITY_GRP;
|
|
PacketOptions.FECContext.FECGroupInfo = pParityContext->NextFECPacketIndex / pSend->FECGroupSize;
|
|
}
|
|
|
|
//
|
|
// The Out buffer must be initialized before entering this routine
|
|
//
|
|
// PgmZeroMemory (pFECPacket, PacketLength);
|
|
status = InitDataSpmHeader (pSend,
|
|
NULL,
|
|
(PUCHAR) pFECPacket,
|
|
&PacketLength,
|
|
pParityContext->OptionsFlags,
|
|
&PacketOptions,
|
|
PacketType);
|
|
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmTrace (LogError, ("PgmBuildParityPacket: ERROR -- " \
|
|
"InitDataSpmHeader returned <%x>\n", status));
|
|
return (status);
|
|
}
|
|
|
|
#ifdef FEC_DBG
|
|
if (pParityContext->NextFECPacketIndex == pSend->FECGroupSize)
|
|
{
|
|
UCHAR i;
|
|
PUCHAR *ppData;
|
|
PUCHAR pData;
|
|
tPOST_PACKET_FEC_CONTEXT UNALIGNED *pFECC;
|
|
tPOST_PACKET_FEC_CONTEXT FECC;
|
|
|
|
ppData = &pParityContext->pDataBuffers[0];
|
|
PgmTrace (LogFec, ("\n"));
|
|
for (i=0; i<pSend->FECGroupSize; i++)
|
|
{
|
|
pData = ppData[i];
|
|
pFECC = (tPOST_PACKET_FEC_CONTEXT UNALIGNED *) &pData[pSend->pSender->MaxPayloadSize];
|
|
PgmCopyMemory (&FECC, pFECC, sizeof (tPOST_PACKET_FEC_CONTEXT));
|
|
PgmTrace (LogFec, ("\t[%d] EncTSDULen=<%x>, Fpr=<%x>, [%x -- %x -- %x]\n",
|
|
SequenceNumber+i, FECC.EncodedTSDULength, FECC.FragmentOptSpecific,
|
|
FECC.EncodedFragmentOptions.MessageFirstSequence,
|
|
FECC.EncodedFragmentOptions.MessageOffset,
|
|
FECC.EncodedFragmentOptions.MessageLength));
|
|
}
|
|
}
|
|
#endif // FEC_DBG
|
|
|
|
status = FECEncode (&pSend->FECContext,
|
|
&pParityContext->pDataBuffers[0],
|
|
pParityContext->NumPacketsInThisGroup,
|
|
(pSend->pSender->MaxPayloadSize + sizeof (tPOST_PACKET_FEC_CONTEXT)),
|
|
pParityContext->NextFECPacketIndex,
|
|
&pFECPacket[PacketLength]);
|
|
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmTrace (LogError, ("PgmBuildParityPacket: ERROR -- " \
|
|
"FECEncode returned <%x>\n", status));
|
|
return (status);
|
|
}
|
|
|
|
#ifdef FEC_DBG
|
|
{
|
|
tPOST_PACKET_FEC_CONTEXT UNALIGNED *pFECC;
|
|
tPOST_PACKET_FEC_CONTEXT FECC;
|
|
|
|
pFECC = (tPOST_PACKET_FEC_CONTEXT UNALIGNED *) &pFECPacket[PacketLength+pSend->pSender->MaxPayloadSize];
|
|
PgmCopyMemory (&FECC, pFECC, sizeof (tPOST_PACKET_FEC_CONTEXT));
|
|
|
|
PgmTrace (LogFec, ("[%d:%d] ==> EncTSDULen=<%x>, Fpr=<%x>, [%x -- %x -- %x]\n",
|
|
(ULONG) SequenceNumber, (ULONG) pParityContext->NextFECPacketIndex,
|
|
FECC.EncodedTSDULength, FECC.FragmentOptSpecific,
|
|
FECC.EncodedFragmentOptions.MessageFirstSequence,
|
|
FECC.EncodedFragmentOptions.MessageOffset,
|
|
FECC.EncodedFragmentOptions.MessageLength));
|
|
}
|
|
#endif // FEC_DBG
|
|
|
|
//
|
|
// Now, fill in the remaining fields of the header
|
|
//
|
|
pRData = (tBASIC_DATA_PACKET_HEADER *) pFECPacket;
|
|
|
|
//
|
|
// Set the FEC-specific options
|
|
//
|
|
pRData->CommonHeader.Options |= (PACKET_HEADER_OPTIONS_PARITY |
|
|
PACKET_HEADER_OPTIONS_VAR_PKTLEN);
|
|
|
|
if (pParityContext->OptionsFlags & PGM_OPTION_FLAG_FRAGMENT)
|
|
{
|
|
pFECContext = (tPOST_PACKET_FEC_CONTEXT UNALIGNED *) (pFECPacket +
|
|
PacketLength +
|
|
pSend->pSender->MaxPayloadSize);
|
|
PgmCopyMemory (&FECContext, pFECContext, sizeof (tPOST_PACKET_FEC_CONTEXT));
|
|
|
|
ASSERT (pRData->CommonHeader.Options & PACKET_HEADER_OPTIONS_PRESENT);
|
|
if (PacketOptions.FragmentOptionOffset)
|
|
{
|
|
pOptionHeader = (tPACKET_OPTION_GENERIC UNALIGNED *) &((PUCHAR) (pRData + 1)) [PacketOptions.FragmentOptionOffset];
|
|
|
|
pOptionHeader->Reserved_F_Opx |= PACKET_OPTION_RES_F_OPX_ENCODED_BIT;
|
|
pOptionHeader->U_OptSpecific = FECContext.FragmentOptSpecific;
|
|
|
|
PgmCopyMemory ((pOptionHeader + 1),
|
|
&FECContext.EncodedFragmentOptions,
|
|
(sizeof (tFRAGMENT_OPTIONS)));
|
|
}
|
|
else
|
|
{
|
|
ASSERT (0);
|
|
}
|
|
}
|
|
|
|
pRData->CommonHeader.TSDULength = htons ((USHORT) pSend->pSender->MaxPayloadSize + sizeof (USHORT));
|
|
pRData->DataSequenceNumber = htonl (SequenceNumber);
|
|
|
|
//
|
|
// Set the next FECPacketIndex
|
|
//
|
|
if (++pParityContext->NextFECPacketIndex >= pSend->FECBlockSize) // n
|
|
{
|
|
pParityContext->NextFECPacketIndex = pSend->FECGroupSize; // k
|
|
}
|
|
pPacketBuffer->PacketOptions.FECContext.SenderNextFECPacketIndex = pParityContext->NextFECPacketIndex;
|
|
|
|
PacketLength += (USHORT) (pSend->pSender->MaxPayloadSize + sizeof (USHORT));
|
|
*pPacketLength = PacketLength;
|
|
return (status);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
PgmSendRData(
|
|
IN tSEND_SESSION *pSend,
|
|
IN tSEND_RDATA_CONTEXT *pRDataContext,
|
|
IN PGMLockHandle *pOldIrq,
|
|
OUT ULONG *pBytesSent
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to send a Repair Data (RData) packet
|
|
The pSend lock is held before calling this routine
|
|
|
|
Arguments:
|
|
|
|
IN pSend -- Pgm session (sender) context
|
|
IN pOldIrq -- pSend's OldIrq
|
|
OUT pBytesSent -- Set if send succeeded (used for calculating throughput)
|
|
|
|
Arguments:
|
|
|
|
IN
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Final status of the send request
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
KAPC_STATE ApcState;
|
|
BOOLEAN fAttached, fInserted;
|
|
LIST_ENTRY *pEntry;
|
|
ULONGLONG OffsetBytes;
|
|
ULONG XSum, PacketsBehindLeadingEdge, NumNaksProcessed;
|
|
tBASIC_DATA_PACKET_HEADER *pRData;
|
|
PUCHAR pSendBuffer = NULL;
|
|
USHORT i, PacketLength;
|
|
tPACKET_BUFFER *pPacketBuffer;
|
|
tPACKET_BUFFER *pPacketBufferTemp;
|
|
SEQ_TYPE RDataSequenceNumber;
|
|
tSEND_CONTEXT *pSender = pSend->pSender;
|
|
USHORT NakType;
|
|
UCHAR NakIndex;
|
|
BOOLEAN fMoreRequests;
|
|
|
|
*pBytesSent = 0;
|
|
if (!(pSendBuffer = ExAllocateFromNPagedLookasideList (&pSender->SenderBufferLookaside)))
|
|
{
|
|
PgmTrace (LogError, ("PgmSendRData: ERROR -- " \
|
|
"STATUS_INSUFFICIENT_RESOURCES\n"));
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
PacketLength = PGM_MAX_FEC_DATA_HEADER_LENGTH + (USHORT) pSender->MaxPayloadSize;
|
|
ASSERT (PacketLength <= pSender->PacketBufferSize);
|
|
|
|
NumNaksProcessed = 1;
|
|
RDataSequenceNumber = pRDataContext->RDataSequenceNumber;
|
|
if (pSend->FECOptions)
|
|
{
|
|
if (pRDataContext->NumParityNaks)
|
|
{
|
|
NakType = NAK_TYPE_PARITY;
|
|
pRDataContext->NumParityNaks--;
|
|
}
|
|
else
|
|
{
|
|
NakType = NAK_TYPE_SELECTIVE;
|
|
if (GetNextNakIndex (pRDataContext, &NakIndex))
|
|
{
|
|
ASSERT (NakIndex < pSend->FECGroupSize);
|
|
RDataSequenceNumber += NakIndex;
|
|
}
|
|
else
|
|
{
|
|
ASSERT (0);
|
|
}
|
|
}
|
|
|
|
if (!(fMoreRequests = AnyMoreNaks (pRDataContext)))
|
|
{
|
|
pRDataContext->CleanupTime = pSender->TimerTickCount + pRDataContext->PostRDataHoldTime;
|
|
pSender->NumRDataRequestsPending--;
|
|
if (!pRDataContext->PostRDataHoldTime)
|
|
{
|
|
RemoveEntry (pSender->pRDataInfo, pRDataContext);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NakType = NAK_TYPE_SELECTIVE;
|
|
pRDataContext->SelectiveNaksMask[0] = 0;
|
|
fMoreRequests = FALSE;
|
|
pRDataContext->CleanupTime = pSender->TimerTickCount + pRDataContext->PostRDataHoldTime;
|
|
pSender->NumRDataRequestsPending--;
|
|
if (!pRDataContext->PostRDataHoldTime)
|
|
{
|
|
RemoveEntry (pSender->pRDataInfo, pRDataContext);
|
|
}
|
|
}
|
|
|
|
ASSERT (PGM_MAX_FEC_DATA_HEADER_LENGTH >= PGM_MAX_DATA_HEADER_LENGTH);
|
|
ASSERT ((SEQ_LT (RDataSequenceNumber, pSender->NextODataSequenceNumber)) &&
|
|
(SEQ_GEQ (RDataSequenceNumber, pSender->TrailingGroupSequenceNumber)));
|
|
|
|
//
|
|
// Find the buffer address based on offset from the trailing edge
|
|
// Also, check for wrap-around
|
|
//
|
|
OffsetBytes = (SEQ_TYPE) (RDataSequenceNumber-pSender->TrailingEdgeSequenceNumber) *
|
|
pSender->PacketBufferSize;
|
|
OffsetBytes += pSender->TrailingWindowOffset;
|
|
if (OffsetBytes >= pSender->MaxDataFileSize)
|
|
{
|
|
OffsetBytes -= pSender->MaxDataFileSize; // Wrap -around
|
|
}
|
|
|
|
pPacketBuffer = (tPACKET_BUFFER *) (((PUCHAR) pSender->SendDataBufferMapping) + OffsetBytes);
|
|
|
|
pRDataContext->NumPacketsInTransport++; // Referenced until SendCompletion
|
|
PGM_REFERENCE_SESSION_SEND (pSend, REF_SESSION_SEND_RDATA, TRUE);
|
|
|
|
PgmUnlock (pSend, *pOldIrq);
|
|
PgmAttachToProcessForVMAccess (pSend, &ApcState, &fAttached, REF_PROCESS_ATTACH_SEND_RDATA);
|
|
|
|
switch (NakType)
|
|
{
|
|
case (NAK_TYPE_PARITY):
|
|
{
|
|
//
|
|
// If this is the first parity packet to be sent from this group,
|
|
// then we will need to initialize the buffers
|
|
//
|
|
if (!pRDataContext->OnDemandParityContext.NumPacketsInThisGroup)
|
|
{
|
|
pRDataContext->OnDemandParityContext.OptionsFlags = 0;
|
|
pRDataContext->OnDemandParityContext.NumPacketsInThisGroup = 0;
|
|
|
|
pPacketBufferTemp = pPacketBuffer;
|
|
for (i=0; i<pSend->FECGroupSize; i++)
|
|
{
|
|
pRDataContext->OnDemandParityContext.pDataBuffers[i] = &((PUCHAR) &pPacketBufferTemp->DataPacket)
|
|
[sizeof (tBASIC_DATA_PACKET_HEADER) +
|
|
pPacketBufferTemp->PacketOptions.OptionsLength];
|
|
|
|
pRDataContext->OnDemandParityContext.OptionsFlags |= pPacketBufferTemp->PacketOptions.OptionsFlags &
|
|
(PGM_OPTION_FLAG_SYN |
|
|
PGM_OPTION_FLAG_FIN |
|
|
PGM_OPTION_FLAG_FRAGMENT |
|
|
PGM_OPTION_FLAG_PARITY_CUR_TGSIZE);
|
|
|
|
if (pPacketBufferTemp->PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_CUR_TGSIZE)
|
|
{
|
|
ASSERT (!pRDataContext->OnDemandParityContext.NumPacketsInThisGroup);
|
|
ASSERT (pPacketBufferTemp->PacketOptions.FECContext.NumPacketsInThisGroup);
|
|
pRDataContext->OnDemandParityContext.NumPacketsInThisGroup = pPacketBufferTemp->PacketOptions.FECContext.NumPacketsInThisGroup;
|
|
}
|
|
|
|
pPacketBufferTemp = (tPACKET_BUFFER *) (((PUCHAR) pPacketBufferTemp) +
|
|
pSender->PacketBufferSize);
|
|
}
|
|
|
|
if (!(pRDataContext->OnDemandParityContext.OptionsFlags & PGM_OPTION_FLAG_PARITY_CUR_TGSIZE))
|
|
{
|
|
ASSERT (!pRDataContext->OnDemandParityContext.NumPacketsInThisGroup);
|
|
pRDataContext->OnDemandParityContext.NumPacketsInThisGroup = pSend->FECGroupSize;
|
|
}
|
|
}
|
|
|
|
ASSERT (pRDataContext->OnDemandParityContext.pDataBuffers[0]);
|
|
|
|
//
|
|
// If we have just 1 packet in this group, then we just do
|
|
// a selective Nak
|
|
//
|
|
if (pRDataContext->OnDemandParityContext.NumPacketsInThisGroup != 1)
|
|
{
|
|
PgmZeroMemory (pSendBuffer, PacketLength); // Zero the buffer
|
|
status = PgmBuildParityPacket (pSend,
|
|
pPacketBuffer,
|
|
&pRDataContext->OnDemandParityContext,
|
|
pSendBuffer,
|
|
&PacketLength,
|
|
PACKET_TYPE_RDATA);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmTrace (LogError, ("PgmSendRData: ERROR -- " \
|
|
"PgmBuildParityPacket returned <%x>\n", status));
|
|
|
|
PgmDetachProcess (&ApcState, &fAttached, REF_PROCESS_ATTACH_SEND_RDATA);
|
|
PgmLock (pSend, *pOldIrq);
|
|
|
|
ExFreeToNPagedLookasideList (&pSender->SenderBufferLookaside, pSendBuffer);
|
|
|
|
if ((!fMoreRequests) &&
|
|
(!pRDataContext->PostRDataHoldTime))
|
|
{
|
|
ASSERT (pRDataContext->CleanupTime);
|
|
DestroyEntry (pSend->pSender->pRDataInfo, pRDataContext);
|
|
}
|
|
else
|
|
{
|
|
pRDataContext->NumPacketsInTransport--; // Undoing what we did earlier
|
|
ASSERT (!pRDataContext->CleanupTime);
|
|
pRDataContext->NumParityNaks++;
|
|
pSender->NumRDataRequestsPending++;
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// FALL THROUGH to send a selective Nak!
|
|
// Do not send any more Naks for this group!
|
|
//
|
|
NakType = NAK_TYPE_SELECTIVE;
|
|
if (fMoreRequests)
|
|
{
|
|
NumNaksProcessed += pRDataContext->NumParityNaks;
|
|
pRDataContext->NumParityNaks = 0;
|
|
while (GetNextNakIndex (pRDataContext, &NakIndex))
|
|
{
|
|
NumNaksProcessed++;
|
|
}
|
|
|
|
fMoreRequests = FALSE;
|
|
pRDataContext->CleanupTime = pSender->TimerTickCount + pRDataContext->PostRDataHoldTime;
|
|
pSender->NumRDataRequestsPending--;
|
|
if (!pRDataContext->PostRDataHoldTime)
|
|
{
|
|
RemoveEntry (pSender->pRDataInfo, pRDataContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
case (NAK_TYPE_SELECTIVE):
|
|
{
|
|
//
|
|
// Since the packet was already filled in earlier, we just need to
|
|
// update the Trailing Edge Seq number + PacketType and Checksum!
|
|
//
|
|
pRData = &pPacketBuffer->DataPacket;
|
|
ASSERT ((ULONG) RDataSequenceNumber == (ULONG) ntohl (pRData->DataSequenceNumber));
|
|
|
|
PacketLength = pPacketBuffer->PacketOptions.TotalPacketLength;
|
|
|
|
PgmCopyMemory (pSendBuffer, pRData, PacketLength);
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
ASSERT (0);
|
|
}
|
|
}
|
|
|
|
PgmDetachProcess (&ApcState, &fAttached, REF_PROCESS_ATTACH_SEND_RDATA);
|
|
|
|
pRData = (tBASIC_DATA_PACKET_HEADER *) pSendBuffer;
|
|
pRData->TrailingEdgeSequenceNumber = htonl ((ULONG) pSender->TrailingGroupSequenceNumber);
|
|
pRData->CommonHeader.Type = PACKET_TYPE_RDATA;
|
|
pRData->CommonHeader.Checksum = 0;
|
|
XSum = 0;
|
|
XSum = tcpxsum (XSum, (CHAR *) pRData, (ULONG) PacketLength); // Compute the Checksum
|
|
pRData->CommonHeader.Checksum = (USHORT) (~XSum);
|
|
|
|
status = TdiSendDatagram (pSender->pAddress->pRAlertFileObject,
|
|
pSender->pAddress->pRAlertDeviceObject,
|
|
pRData,
|
|
(ULONG) PacketLength,
|
|
PgmSendRDataCompletion, // Completion
|
|
pRDataContext, // Context1
|
|
pSendBuffer, // Context2
|
|
pSender->DestMCastIpAddress,
|
|
pSender->DestMCastPort,
|
|
FALSE);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
PgmTrace (LogAllFuncs, ("PgmSendRData: " \
|
|
"[%d] Sent <%d> bytes to <%x->%d>\n",
|
|
(ULONG) RDataSequenceNumber, (ULONG) PacketLength,
|
|
pSender->DestMCastIpAddress, pSender->DestMCastPort));
|
|
|
|
PgmLock (pSend, *pOldIrq);
|
|
|
|
ASSERT (pSender->NumRDataRequestsPending <= pSender->pRDataInfo->NumAllocated);
|
|
|
|
pSender->NumOutstandingNaks -= NumNaksProcessed;
|
|
pSender->TotalRDataPacketsSent++;
|
|
pSender->RDataPacketsInLastInterval++;
|
|
|
|
*pBytesSent = PacketLength;
|
|
return (status);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
PgmSendNcfCompletion(
|
|
IN tSEND_SESSION *pSend,
|
|
IN tBASIC_NAK_NCF_PACKET_HEADER *pNcfPacket,
|
|
IN NTSTATUS status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the transport when the Ncf send has been completed
|
|
|
|
Arguments:
|
|
|
|
IN pSend -- Pgm session (sender) context
|
|
IN pNcfPacket -- Ncf packet buffer
|
|
IN status --
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
{
|
|
PGMLockHandle OldIrq;
|
|
|
|
PgmLock (pSend, OldIrq);
|
|
if (NT_SUCCESS (status))
|
|
{
|
|
//
|
|
// Set the Ncf statistics
|
|
//
|
|
PgmTrace (LogAllFuncs, ("PgmSendNcfCompletion: " \
|
|
"SUCCEEDED\n"));
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogError, ("PgmSendNcfCompletion: ERROR -- " \
|
|
"status=<%x>\n", status));
|
|
}
|
|
PgmUnlock (pSend, OldIrq);
|
|
|
|
PGM_DEREFERENCE_SESSION_SEND (pSend, REF_SESSION_SEND_NCF);
|
|
PgmFreeMem (pNcfPacket);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
PgmSendNcf(
|
|
IN tSEND_SESSION *pSend,
|
|
IN tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED *pNakPacket,
|
|
IN tNAKS_LIST *pNcfsList,
|
|
IN ULONG NakPacketLength
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to send an NCF packet
|
|
|
|
Arguments:
|
|
|
|
IN pSend -- Pgm session (sender) context
|
|
IN pNakPacket -- Nak packet which trigerred the Ncf
|
|
IN NakPacketLength -- Length of Nak packet
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Final status of the send
|
|
|
|
--*/
|
|
{
|
|
ULONG i, XSum;
|
|
NTSTATUS status;
|
|
tBASIC_NAK_NCF_PACKET_HEADER *pNcfPacket;
|
|
tPACKET_OPTION_LENGTH *pPacketExtension;
|
|
tPACKET_OPTION_GENERIC *pOptionHeader;
|
|
USHORT OptionsLength = 0;
|
|
|
|
if (!(pNcfPacket = PgmAllocMem (NakPacketLength, PGM_TAG('2'))))
|
|
{
|
|
PgmTrace (LogError, ("PgmSendNcf: ERROR -- " \
|
|
"STATUS_INSUFFICIENT_RESOURCES\n"));
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
PgmZeroMemory (pNcfPacket, NakPacketLength); // Copy the packet in its entirety
|
|
|
|
//
|
|
// Now, set the fields specific to this sender
|
|
//
|
|
pNcfPacket->CommonHeader.DestPort = htons (pSend->pSender->DestMCastPort);
|
|
pNcfPacket->CommonHeader.SrcPort = htons (pSend->TSI.hPort);
|
|
PgmCopyMemory (&pNcfPacket->CommonHeader.gSourceId, pSend->TSI.GSI, SOURCE_ID_LENGTH);
|
|
pNcfPacket->CommonHeader.Type = PACKET_TYPE_NCF;
|
|
if (pNcfsList->NakType == NAK_TYPE_PARITY)
|
|
{
|
|
pNcfPacket->CommonHeader.Options = PACKET_HEADER_OPTIONS_PARITY;
|
|
for (i=0; i<pNcfsList->NumSequences; i++)
|
|
{
|
|
pNcfsList->NumParityNaks[i]--; // Convert from NumParityNaks to NakIndex
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pNcfPacket->CommonHeader.Options = 0;
|
|
}
|
|
|
|
pNcfPacket->SourceNLA.NLA_AFI = pNakPacket->SourceNLA.NLA_AFI;
|
|
pNcfPacket->SourceNLA.IpAddress = pNakPacket->SourceNLA.IpAddress;
|
|
pNcfPacket->MCastGroupNLA.NLA_AFI = pNakPacket->MCastGroupNLA.NLA_AFI;
|
|
pNcfPacket->MCastGroupNLA.IpAddress = pNakPacket->MCastGroupNLA.IpAddress;
|
|
|
|
//
|
|
// Now, fill in the Sequence numbers
|
|
//
|
|
pNcfPacket->RequestedSequenceNumber = htonl ((ULONG) ((SEQ_TYPE) (pNcfsList->pNakSequences[0] +
|
|
pNcfsList->NakIndex[0])));
|
|
if (pNcfsList->NumSequences > 1)
|
|
{
|
|
pPacketExtension = (tPACKET_OPTION_LENGTH *) (pNcfPacket + 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) ((pNcfsList->NumSequences-1) * sizeof(ULONG));
|
|
for (i=1; i<pNcfsList->NumSequences; i++)
|
|
{
|
|
((PULONG) (pOptionHeader))[i] = htonl ((ULONG) ((SEQ_TYPE) (pNcfsList->pNakSequences[i] +
|
|
pNcfsList->NakIndex[i])));
|
|
}
|
|
|
|
pOptionHeader->E_OptionType |= PACKET_OPTION_TYPE_END_BIT; // One and only (last) opt
|
|
pNcfPacket->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
|
|
|
|
pNcfPacket->CommonHeader.Checksum = 0;
|
|
XSum = 0;
|
|
XSum = tcpxsum (XSum, (CHAR *) pNcfPacket, NakPacketLength);
|
|
pNcfPacket->CommonHeader.Checksum = (USHORT) (~XSum);
|
|
|
|
PGM_REFERENCE_SESSION_SEND (pSend, REF_SESSION_SEND_NCF, FALSE);
|
|
|
|
status = TdiSendDatagram (pSend->pSender->pAddress->pRAlertFileObject,
|
|
pSend->pSender->pAddress->pRAlertDeviceObject,
|
|
pNcfPacket,
|
|
OptionsLength,
|
|
PgmSendNcfCompletion, // Completion
|
|
pSend, // Context1
|
|
pNcfPacket, // Context2
|
|
pSend->pSender->DestMCastIpAddress,
|
|
pSend->pSender->DestMCastPort,
|
|
FALSE);
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
PgmTrace (LogAllFuncs, ("PgmSendNcf: " \
|
|
"Sent <%d> bytes to <%x:%d>\n",
|
|
NakPacketLength, pSend->pSender->DestMCastIpAddress, pSend->pSender->DestMCastPort));
|
|
|
|
return (status);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
SenderProcessNakPacket(
|
|
IN tADDRESS_CONTEXT *pAddress,
|
|
IN tSEND_SESSION *pSend,
|
|
IN ULONG PacketLength,
|
|
IN tBASIC_NAK_NCF_PACKET_HEADER UNALIGNED *pNakPacket
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine processes an incoming Nak packet sent to the sender
|
|
|
|
Arguments:
|
|
|
|
IN pAddress -- Pgm's address object
|
|
IN pSend -- Pgm session (sender) context
|
|
IN PacketLength -- Nak packet length
|
|
IN pNakPacket -- Nak packet data
|
|
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Final status of the call
|
|
|
|
--*/
|
|
{
|
|
PGMLockHandle OldIrq;
|
|
tNAKS_LIST NaksList;
|
|
tSEND_RDATA_CONTEXT *pRDataContext;
|
|
tSEND_RDATA_CONTEXT *pRDataNew;
|
|
SEQ_TYPE LastSequenceNumber;
|
|
NTSTATUS status;
|
|
|
|
ASSERT (!pNakPacket->CommonHeader.TSDULength);
|
|
|
|
PgmLock (pSend, OldIrq);
|
|
|
|
//
|
|
// Initialize the last sequence number
|
|
//
|
|
LastSequenceNumber = pSend->pSender->NextODataSequenceNumber;
|
|
status = ExtractNakNcfSequences (pNakPacket,
|
|
(PacketLength - sizeof(tBASIC_NAK_NCF_PACKET_HEADER)),
|
|
&NaksList,
|
|
&LastSequenceNumber,
|
|
pSend->FECGroupSize);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmUnlock (pSend, OldIrq);
|
|
PgmTrace (LogError, ("SenderProcessNakPacket: ERROR -- " \
|
|
"ExtractNakNcfSequences returned <%x>\n", status));
|
|
|
|
return (status);
|
|
}
|
|
|
|
pSend->pSender->NaksReceived += NaksList.NumSequences;
|
|
|
|
//
|
|
// The oldest as well as latest sequence numbers have to be in our window
|
|
//
|
|
if (SEQ_LT (NaksList.pNakSequences[0], pSend->pSender->TrailingGroupSequenceNumber) ||
|
|
SEQ_GEQ (LastSequenceNumber, pSend->pSender->NextODataSequenceNumber))
|
|
{
|
|
pSend->pSender->NaksReceivedTooLate++;
|
|
PgmUnlock (pSend, OldIrq);
|
|
|
|
PgmTrace (LogError, ("SenderProcessNakPacket: ERROR -- " \
|
|
"Invalid %s Naks = [%d-%d] not in window [%d -- [%d]\n",
|
|
(NaksList.NakType == NAK_TYPE_PARITY ? "Parity" : "Selective"),
|
|
(ULONG) NaksList.pNakSequences[0], (ULONG) LastSequenceNumber,
|
|
(ULONG) pSend->pSender->TrailingGroupSequenceNumber, (ULONG) (pSend->pSender->NextODataSequenceNumber-1)));
|
|
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
//
|
|
// Check if this is a parity Nak and we are anabled for Parity Naks
|
|
//
|
|
if ((pNakPacket->CommonHeader.Options & PACKET_HEADER_OPTIONS_PARITY) &&
|
|
!(pSend->FECOptions & PACKET_OPTION_SPECIFIC_FEC_OND_BIT))
|
|
{
|
|
PgmTrace (LogError, ("SenderProcessNakPacket: ERROR -- " \
|
|
"Receiver requested Parity Naks, but we are not enabled for parity!\n"));
|
|
|
|
PgmUnlock (pSend, OldIrq);
|
|
return (STATUS_DATA_NOT_ACCEPTED);
|
|
}
|
|
|
|
status = FilterAndAddNaksToList (pSend, &NaksList);
|
|
|
|
PgmUnlock (pSend, OldIrq);
|
|
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmTrace (LogError, ("SenderProcessNakPacket: ERROR -- " \
|
|
"FilterAndAddNaksToList returned <%x>\n", status));
|
|
|
|
return (status);
|
|
}
|
|
|
|
//
|
|
// If applicable, send the Ncf for this Nak
|
|
//
|
|
if (NaksList.NumSequences)
|
|
{
|
|
PgmTrace (LogAllFuncs, ("SenderProcessNakPacket: " \
|
|
"Now sending Ncf for Nak received for <%d> Sequences, NakType=<%x>\n",
|
|
NaksList.NumSequences, NaksList.NakType));
|
|
|
|
status = PgmSendNcf (pSend, pNakPacket, &NaksList, PacketLength);
|
|
}
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
PgmSendODataCompletion(
|
|
IN tCLIENT_SEND_REQUEST *pSendContext,
|
|
IN tPACKET_BUFFER *pPacketBuffer,
|
|
IN NTSTATUS status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called by the transport when the OData send has been completed
|
|
|
|
Arguments:
|
|
|
|
IN pSendContext -- Pgm's Send context
|
|
IN pUnused -- not used
|
|
IN status --
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
{
|
|
ULONG SendLength;
|
|
PGMLockHandle OldIrq;
|
|
PIRP pIrpCurrentSend = NULL;
|
|
PIRP pIrpToComplete = NULL;
|
|
tSEND_SESSION *pSend = pSendContext->pSend;
|
|
|
|
PgmLock (pSend, OldIrq);
|
|
|
|
if (NT_SUCCESS (status))
|
|
{
|
|
//
|
|
// Set the Ncf statistics
|
|
//
|
|
PgmTrace (LogAllFuncs, ("PgmSendODataCompletion: " \
|
|
"SUCCEEDED\n"));
|
|
|
|
if (!pPacketBuffer)
|
|
{
|
|
pSendContext->NumDataPacketsSentSuccessfully++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogError, ("PgmSendODataCompletion: ERROR -- " \
|
|
"status=<%x>\n", status));
|
|
}
|
|
|
|
//
|
|
// If all the OData has been sent, we may need to complete the Irp
|
|
// Since we don't know whether we are on the CurrentSend or Completed
|
|
// Sends list, we will need to also check the Bytes
|
|
//
|
|
if ((--pSendContext->NumSendsPending == 0) && // No other sends pending
|
|
(pSendContext->NumParityPacketsToSend == 0) && // No parity packets pending
|
|
(!pSendContext->BytesLeftToPacketize) && // All bytes have been packetized
|
|
(pSendContext->NumDataPacketsSent == pSendContext->DataPacketsPacketized)) // Pkts sent == total Pkts
|
|
{
|
|
PgmTrace (LogAllFuncs, ("PgmSendODataCompletion: " \
|
|
"Completing Send#=<%d>, pIrp=<%p> for <%d> packets, Seq=[%d, %d]\n",
|
|
pSendContext->SendNumber, pSendContext->pIrp, pSendContext->DataPacketsPacketized,
|
|
(ULONG) pSendContext->StartSequenceNumber, (ULONG) pSendContext->EndSequenceNumber));
|
|
|
|
pSend->DataBytes += pSendContext->BytesInSend;
|
|
if (pIrpCurrentSend = pSendContext->pIrp)
|
|
{
|
|
if (pSendContext->NumDataPacketsSentSuccessfully == pSendContext->NumDataPacketsSent)
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
SendLength = pSendContext->BytesInSend;
|
|
|
|
PgmTrace (LogPath, ("PgmSendODataCompletion: " \
|
|
"pIrp=<%p -- %p>, pSendContext=<%p>, NumPackets sent successfully = <%d/%d>\n",
|
|
pSendContext->pIrp, pSendContext->pIrpToComplete, pSendContext,
|
|
pSendContext->NumDataPacketsSentSuccessfully, pSendContext->NumDataPacketsSent));
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogError, ("PgmSendODataCompletion: ERROR -- " \
|
|
"pIrp=<%p -- %p>, pSendContext=<%p>, NumPackets sent successfully = <%d/%d>\n",
|
|
pSendContext->pIrp, pSendContext->pIrpToComplete, pSendContext,
|
|
pSendContext->NumDataPacketsSentSuccessfully, pSendContext->NumDataPacketsSent));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
SendLength = 0;
|
|
}
|
|
|
|
pSendContext->pIrp = NULL;
|
|
pIrpToComplete = pSendContext->pIrpToComplete;
|
|
}
|
|
else
|
|
{
|
|
ASSERT (0); // To verify there is no double completion!
|
|
}
|
|
|
|
if (pSendContext->pMessage2Request)
|
|
{
|
|
//
|
|
// We could have a situation where the send was split into 2, and
|
|
// the second send could either be in the PendingSends list or
|
|
// the PendingPacketizedSends list, or the CompletedSendsInWindow list
|
|
//
|
|
// We should have the other send complete the Irp and delink ourselves
|
|
//
|
|
ASSERT (pSendContext == pSendContext->pMessage2Request->pMessage2Request);
|
|
|
|
if (pIrpToComplete)
|
|
{
|
|
ASSERT (!pSendContext->pMessage2Request->pIrpToComplete);
|
|
pSendContext->pMessage2Request->pIrpToComplete = pSendContext->pIrpToComplete;
|
|
pIrpToComplete = pSendContext->pIrpToComplete = NULL;
|
|
}
|
|
|
|
pSendContext->pMessage2Request->pMessage2Request = NULL;
|
|
pSendContext->pMessage2Request = NULL;
|
|
}
|
|
}
|
|
|
|
PgmUnlock (pSend, OldIrq);
|
|
|
|
if (pPacketBuffer)
|
|
{
|
|
ExFreeToNPagedLookasideList (&pSend->pSender->SenderBufferLookaside, pPacketBuffer);
|
|
}
|
|
|
|
if (pIrpCurrentSend)
|
|
{
|
|
if (pIrpToComplete)
|
|
{
|
|
PgmIoComplete (pIrpToComplete, status, SendLength);
|
|
}
|
|
PGM_DEREFERENCE_SESSION_SEND (pSend, REF_SESSION_SEND_IN_WINDOW);
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
GetNextPacketOptionsAndData(
|
|
IN tSEND_SESSION *pSend,
|
|
IN tCLIENT_SEND_REQUEST *pSendContext,
|
|
IN tBASIC_DATA_PACKET_HEADER **ppODataBuffer,
|
|
IN PGMLockHandle *pOldIrq,
|
|
OUT USHORT *pPacketLength
|
|
)
|
|
{
|
|
KAPC_STATE ApcState;
|
|
BOOLEAN fAttached;
|
|
NTSTATUS status;
|
|
SEQ_TYPE NextODataSequenceNumber, FECGroupMask;
|
|
PUCHAR pSendBuffer;
|
|
tPACKET_BUFFER *pFileBuffer;
|
|
tPACKET_OPTIONS EmptyPacketOptions;
|
|
ULONG ulBytes, ulOptionsLength;
|
|
ULONG i, DataBytesInThisPacket;
|
|
USHORT usBytes, HeaderLength, PacketsLeftInGroup;
|
|
tPOST_PACKET_FEC_CONTEXT FECContext;
|
|
tPOST_PACKET_FEC_CONTEXT UNALIGNED *pBufferFECContext;
|
|
tPACKET_OPTIONS *pPacketOptions;
|
|
tBASIC_DATA_PACKET_HEADER *pODataBuffer;
|
|
ULONG PacketBufferSize;
|
|
ULONG MaxPayloadSize;
|
|
tSEND_CONTEXT *pSender = pSend->pSender;
|
|
UCHAR NumPackets, EmptyPackets = 0;
|
|
|
|
//
|
|
// Get the current send packet
|
|
//
|
|
pFileBuffer = (tPACKET_BUFFER *) (pSender->SendDataBufferMapping + pSendContext->NextPacketOffset);
|
|
|
|
pPacketOptions = &pFileBuffer->PacketOptions;
|
|
pODataBuffer = &pFileBuffer->DataPacket;
|
|
*ppODataBuffer = pODataBuffer; // Save this buffer address!
|
|
|
|
NextODataSequenceNumber = pSender->NextODataSequenceNumber;
|
|
PacketBufferSize = pSender->PacketBufferSize;
|
|
MaxPayloadSize = pSender->MaxPayloadSize;
|
|
|
|
//
|
|
// Prepare info for any applicable options
|
|
//
|
|
if (pSendContext->BytesLeftToPacketize > pSender->MaxPayloadSize)
|
|
{
|
|
DataBytesInThisPacket = pSender->MaxPayloadSize;
|
|
}
|
|
else
|
|
{
|
|
DataBytesInThisPacket = (USHORT) pSendContext->BytesLeftToPacketize;
|
|
}
|
|
ASSERT (DataBytesInThisPacket);
|
|
|
|
PgmZeroMemory (&EmptyPacketOptions, sizeof (tPACKET_OPTIONS));
|
|
|
|
//
|
|
// See if we need to set the FIN flag
|
|
//
|
|
if ((pSendContext->bLastSend) &&
|
|
(pSendContext->BytesLeftToPacketize == DataBytesInThisPacket))
|
|
{
|
|
PgmTrace (LogPath, ("GetNextPacketOptionsAndData: " \
|
|
"Setting Fin flag since bLastSend set for last packet!\n"));
|
|
|
|
//
|
|
// We have finished packetizing all the packets, but
|
|
// since this is the last send we also need to set the
|
|
// FIN on the last packet
|
|
//
|
|
pSendContext->bLastSend = FALSE;
|
|
if (!pSendContext->DataOptions)
|
|
{
|
|
ASSERT (!pSendContext->DataOptionsLength);
|
|
pSendContext->DataOptionsLength = PGM_PACKET_EXTENSION_LENGTH;
|
|
}
|
|
|
|
if ((pSend->SessionFlags & PGM_SESSION_SENDS_CANCELLED) ||
|
|
!(pSend->pIrpDisconnect))
|
|
{
|
|
pSendContext->DataOptions |= PGM_OPTION_FLAG_RST;
|
|
pSendContext->DataOptionsLength += PGM_PACKET_OPT_RST_LENGTH;
|
|
}
|
|
else
|
|
{
|
|
pSendContext->DataOptions |= PGM_OPTION_FLAG_FIN;
|
|
pSendContext->DataOptionsLength += PGM_PACKET_OPT_FIN_LENGTH;
|
|
}
|
|
}
|
|
EmptyPacketOptions.OptionsFlags = pSendContext->DataOptions;
|
|
ulOptionsLength = pSendContext->DataOptionsLength; // Save for assert below
|
|
|
|
if (EmptyPacketOptions.OptionsFlags & PGM_OPTION_FLAG_FRAGMENT)
|
|
{
|
|
EmptyPacketOptions.MessageFirstSequence = (ULONG) (SEQ_TYPE) pSendContext->MessageFirstSequenceNumber;
|
|
EmptyPacketOptions.MessageOffset = pSendContext->LastMessageOffset + pSendContext->NextDataOffsetInMdl;
|
|
EmptyPacketOptions.MessageLength = pSendContext->ThisMessageLength;
|
|
}
|
|
|
|
if (EmptyPacketOptions.OptionsFlags & PGM_OPTION_FLAG_JOIN)
|
|
{
|
|
//
|
|
// See if we have enough packets for the LateJoiner sequence numbers
|
|
//
|
|
if (SEQ_GT (NextODataSequenceNumber, (pSender->TrailingGroupSequenceNumber +
|
|
pSender->LateJoinSequenceNumbers)))
|
|
{
|
|
EmptyPacketOptions.LateJoinerSequence = (ULONG) (SEQ_TYPE) (NextODataSequenceNumber -
|
|
pSender->LateJoinSequenceNumbers);
|
|
}
|
|
else
|
|
{
|
|
EmptyPacketOptions.LateJoinerSequence = (ULONG) (SEQ_TYPE) pSender->TrailingGroupSequenceNumber;
|
|
}
|
|
}
|
|
|
|
if (pSend->FECOptions) // Check if this is FEC-enabled
|
|
{
|
|
FECGroupMask = pSend->FECGroupSize-1;
|
|
PacketsLeftInGroup = pSend->FECGroupSize - (UCHAR) (NextODataSequenceNumber & FECGroupMask);
|
|
//
|
|
// Save information if we are at beginning of group boundary
|
|
//
|
|
if (PacketsLeftInGroup == pSend->FECGroupSize)
|
|
{
|
|
EmptyPacketOptions.FECContext.SenderNextFECPacketIndex = pSend->FECGroupSize;
|
|
}
|
|
|
|
//
|
|
// Check if we need to set the variable TG size option
|
|
//
|
|
if ((pSender->NumPacketsRemaining == 1) && // Last packet
|
|
(PacketsLeftInGroup > 1)) // Variable TG size
|
|
{
|
|
//
|
|
// This is a variable Transmission Group Size, i.e. PacketsInGroup < pSend->FECGroupSize
|
|
//
|
|
if (!EmptyPacketOptions.OptionsFlags)
|
|
{
|
|
ulOptionsLength = PGM_PACKET_EXTENSION_LENGTH;
|
|
}
|
|
ulOptionsLength += PGM_PACKET_OPT_PARITY_CUR_TGSIZE_LENGTH;
|
|
EmptyPacketOptions.OptionsFlags |= PGM_OPTION_FLAG_PARITY_CUR_TGSIZE;
|
|
|
|
EmptyPacketOptions.FECContext.NumPacketsInThisGroup = 1 + (UCHAR) (NextODataSequenceNumber & FECGroupMask);
|
|
pSender->LastVariableTGPacketSequenceNumber = NextODataSequenceNumber;
|
|
EmptyPackets = (UCHAR) (PacketsLeftInGroup - 1);
|
|
|
|
pSendContext->NumParityPacketsToSend = pSend->FECProActivePackets;
|
|
}
|
|
|
|
//
|
|
// Otherwise see if the next send needs to be for pro-active parity
|
|
//
|
|
else if ((pSend->FECProActivePackets) && // Need to send FEC pro-active packets
|
|
(1 == PacketsLeftInGroup)) // Last Packet In Group
|
|
{
|
|
pSendContext->NumParityPacketsToSend = pSend->FECProActivePackets;
|
|
}
|
|
|
|
//
|
|
// If this is the GroupLeader packet, and we have pro-active parity enabled,
|
|
// then we need to set the buffer information for computing the FEC packets
|
|
//
|
|
if ((pSend->FECProActivePackets) && // Need to send FEC pro-active packets
|
|
(pSend->FECGroupSize == PacketsLeftInGroup)) // GroupLeader
|
|
{
|
|
pSender->pLastProActiveGroupLeader = pFileBuffer;
|
|
}
|
|
}
|
|
|
|
HeaderLength = (USHORT) pSender->MaxPayloadSize; // Init -- max buffer size available
|
|
|
|
//
|
|
// Now, save the Buffer(s) to the memory-mapped file for repairs
|
|
//
|
|
PgmUnlock (pSend, *pOldIrq);
|
|
PgmAcquireResourceExclusive (&pSend->pSender->Resource, TRUE);
|
|
PgmAttachToProcessForVMAccess (pSend->Process, &ApcState, &fAttached, REF_PROCESS_ATTACH_PACKETIZE);
|
|
|
|
PgmZeroMemory (pODataBuffer, PGM_MAX_FEC_DATA_HEADER_LENGTH); // Zero the buffer
|
|
status = InitDataSpmHeader (pSend,
|
|
pSendContext,
|
|
(PUCHAR) pODataBuffer,
|
|
&HeaderLength,
|
|
EmptyPacketOptions.OptionsFlags,
|
|
&EmptyPacketOptions,
|
|
PACKET_TYPE_ODATA);
|
|
|
|
if (NT_SUCCESS (status))
|
|
{
|
|
ASSERT ((sizeof(tBASIC_DATA_PACKET_HEADER) + ulOptionsLength) == HeaderLength);
|
|
ASSERT ((pSend->FECBlockSize && (HeaderLength+pSendContext->DataPayloadSize) <=
|
|
(PacketBufferSize-sizeof(tPOST_PACKET_FEC_CONTEXT))) ||
|
|
(!pSend->FECBlockSize && ((HeaderLength+pSendContext->DataPayloadSize) <=
|
|
PacketBufferSize)));
|
|
|
|
ulBytes = 0;
|
|
status = TdiCopyMdlToBuffer (pSendContext->pIrp->MdlAddress,
|
|
pSendContext->NextDataOffsetInMdl,
|
|
(((PUCHAR) pODataBuffer) + HeaderLength),
|
|
0, // Destination Offset
|
|
DataBytesInThisPacket,
|
|
&ulBytes);
|
|
|
|
if (((!NT_SUCCESS (status)) && (STATUS_BUFFER_OVERFLOW != status)) || // Overflow acceptable!
|
|
(ulBytes != DataBytesInThisPacket))
|
|
{
|
|
PgmTrace (LogError, ("GetNextPacketOptionsAndData: ERROR -- " \
|
|
"TdiCopyMdlToBuffer returned <%x>, BytesCopied=<%d/%d>\n",
|
|
status, ulBytes, DataBytesInThisPacket));
|
|
|
|
status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
else
|
|
{
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogError, ("GetNextPacketOptionsAndData: ERROR -- " \
|
|
"InitDataSpmHeader returned <%x>\n", status));
|
|
}
|
|
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmDetachProcess (&ApcState, &fAttached, REF_PROCESS_ATTACH_PACKETIZE);
|
|
PgmReleaseResource (&pSend->pSender->Resource);
|
|
PgmLock (pSend, *pOldIrq);
|
|
|
|
if ((pSendContext->DataOptions & PGM_OPTION_FLAG_FIN) &&
|
|
(pSendContext->BytesLeftToPacketize == DataBytesInThisPacket))
|
|
{
|
|
pSendContext->bLastSend = TRUE;
|
|
pSendContext->DataOptions &= ~PGM_OPTION_FLAG_FIN;
|
|
if (pSendContext->DataOptions)
|
|
{
|
|
pSendContext->DataOptionsLength -= PGM_PACKET_OPT_FIN_LENGTH;
|
|
}
|
|
else
|
|
{
|
|
pSendContext->DataOptionsLength = 0;
|
|
}
|
|
}
|
|
pSendContext->NumParityPacketsToSend = 0;
|
|
|
|
return (status);
|
|
}
|
|
|
|
pODataBuffer->CommonHeader.TSDULength = htons ((USHORT) DataBytesInThisPacket);
|
|
pODataBuffer->DataSequenceNumber = htonl ((ULONG) NextODataSequenceNumber);
|
|
|
|
EmptyPacketOptions.OptionsLength = HeaderLength - sizeof (tBASIC_DATA_PACKET_HEADER);
|
|
EmptyPacketOptions.TotalPacketLength = HeaderLength + (USHORT) DataBytesInThisPacket;
|
|
*pPacketLength = EmptyPacketOptions.TotalPacketLength;
|
|
|
|
//
|
|
// Zero out the remaining buffer
|
|
//
|
|
PgmZeroMemory ((((PUCHAR) pODataBuffer)+EmptyPacketOptions.TotalPacketLength),
|
|
(PacketBufferSize-(sizeof(tPACKET_OPTIONS)+EmptyPacketOptions.TotalPacketLength)));
|
|
|
|
//
|
|
// Set the PacketOptions Information for FEC packets
|
|
//
|
|
if (pSend->FECOptions)
|
|
{
|
|
pBufferFECContext = (tPOST_PACKET_FEC_CONTEXT *) (((PUCHAR) pODataBuffer) +
|
|
HeaderLength +
|
|
MaxPayloadSize);
|
|
PgmZeroMemory (&FECContext, sizeof (tPOST_PACKET_FEC_CONTEXT));
|
|
|
|
FECContext.EncodedTSDULength = htons ((USHORT) DataBytesInThisPacket);
|
|
FECContext.EncodedFragmentOptions.MessageFirstSequence = htonl ((ULONG) (SEQ_TYPE) EmptyPacketOptions.MessageFirstSequence);
|
|
FECContext.EncodedFragmentOptions.MessageOffset = htonl (EmptyPacketOptions.MessageOffset);
|
|
FECContext.EncodedFragmentOptions.MessageLength = htonl (EmptyPacketOptions.MessageLength);
|
|
PgmCopyMemory (pBufferFECContext, &FECContext, sizeof (tPOST_PACKET_FEC_CONTEXT));
|
|
|
|
//
|
|
// If this is not a fragment, set the PACKET_OPTION_SPECIFIC_ENCODED_NULL_BIT
|
|
//
|
|
if (!(EmptyPacketOptions.OptionsFlags & PGM_OPTION_FLAG_FRAGMENT))
|
|
{
|
|
((PUCHAR) pBufferFECContext)
|
|
[FIELD_OFFSET (tPOST_PACKET_FEC_CONTEXT, FragmentOptSpecific)] =
|
|
PACKET_OPTION_SPECIFIC_ENCODED_NULL_BIT;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save the PacketOptions
|
|
//
|
|
PgmCopyMemory (pPacketOptions, &EmptyPacketOptions, sizeof (tPACKET_OPTIONS));
|
|
|
|
//
|
|
// From this point onwards, pFileBuffer will not be a valid ptr
|
|
//
|
|
NextODataSequenceNumber++;
|
|
if (EmptyPackets)
|
|
{
|
|
if (EmptyPacketOptions.OptionsFlags & PGM_OPTION_FLAG_FRAGMENT)
|
|
{
|
|
EmptyPacketOptions.OptionsFlags &= ~PGM_OPTION_FLAG_FRAGMENT;
|
|
ulOptionsLength -= PGM_PACKET_OPT_FRAGMENT_LENGTH;
|
|
}
|
|
|
|
if (EmptyPacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_CUR_TGSIZE)
|
|
{
|
|
EmptyPacketOptions.OptionsFlags &= ~PGM_OPTION_FLAG_PARITY_CUR_TGSIZE;
|
|
ulOptionsLength -= PGM_PACKET_OPT_PARITY_CUR_TGSIZE_LENGTH;
|
|
}
|
|
|
|
if (!EmptyPacketOptions.OptionsFlags)
|
|
{
|
|
ulOptionsLength = 0;
|
|
}
|
|
|
|
EmptyPacketOptions.OptionsLength = (USHORT) ulOptionsLength;
|
|
|
|
NumPackets = EmptyPackets;
|
|
while (NumPackets--)
|
|
{
|
|
pFileBuffer = (tPACKET_BUFFER *) (((PUCHAR) pFileBuffer) + PacketBufferSize);
|
|
|
|
pPacketOptions = &pFileBuffer->PacketOptions;
|
|
pODataBuffer = &pFileBuffer->DataPacket;
|
|
PgmCopyMemory (pPacketOptions, &EmptyPacketOptions, sizeof (tPACKET_OPTIONS));
|
|
PgmZeroMemory (pODataBuffer, (PacketBufferSize-sizeof(tPACKET_OPTIONS))); // Zero the buffer
|
|
|
|
HeaderLength = (USHORT) MaxPayloadSize; // Init -- max buffer size available
|
|
status = InitDataSpmHeader (pSend,
|
|
pSendContext,
|
|
(PUCHAR) &pFileBuffer->DataPacket,
|
|
&HeaderLength,
|
|
EmptyPacketOptions.OptionsFlags,
|
|
&EmptyPacketOptions,
|
|
PACKET_TYPE_ODATA);
|
|
|
|
//
|
|
// Since these packets are not expected to be sent, we will ignore the return status!
|
|
//
|
|
ASSERT (NT_SUCCESS (status));
|
|
ASSERT ((sizeof(tBASIC_DATA_PACKET_HEADER) + ulOptionsLength) == HeaderLength);
|
|
ASSERT ((HeaderLength+pSendContext->DataPayloadSize) <=
|
|
(PacketBufferSize-sizeof(tPOST_PACKET_FEC_CONTEXT)));
|
|
|
|
pODataBuffer->CommonHeader.TSDULength = 0;
|
|
pODataBuffer->DataSequenceNumber = htonl ((ULONG) NextODataSequenceNumber++);
|
|
|
|
pPacketOptions->TotalPacketLength = HeaderLength;
|
|
pPacketOptions->OptionsLength = HeaderLength - sizeof (tBASIC_DATA_PACKET_HEADER);
|
|
}
|
|
}
|
|
|
|
if (pSendContext->NumParityPacketsToSend)
|
|
{
|
|
//
|
|
// Start from the Group leader packet
|
|
//
|
|
ASSERT (pSender->pLastProActiveGroupLeader);
|
|
pFileBuffer = pSender->pLastProActiveGroupLeader;
|
|
pSendBuffer = (PUCHAR) pFileBuffer;
|
|
|
|
pSender->pProActiveParityContext->OptionsFlags = 0;
|
|
pSender->pProActiveParityContext->NumPacketsInThisGroup = 0;
|
|
|
|
for (i=0; i<pSend->FECGroupSize; i++)
|
|
{
|
|
pSender->pProActiveParityContext->pDataBuffers[i] =
|
|
&((PUCHAR) &pFileBuffer->DataPacket)[sizeof(tBASIC_DATA_PACKET_HEADER) +
|
|
pFileBuffer->PacketOptions.OptionsLength];
|
|
pSender->pProActiveParityContext->OptionsFlags |= pFileBuffer->PacketOptions.OptionsFlags &
|
|
(PGM_OPTION_FLAG_SYN |
|
|
PGM_OPTION_FLAG_FIN |
|
|
PGM_OPTION_FLAG_FRAGMENT |
|
|
PGM_OPTION_FLAG_PARITY_CUR_TGSIZE);
|
|
|
|
if (pFileBuffer->PacketOptions.OptionsFlags & PGM_OPTION_FLAG_PARITY_CUR_TGSIZE)
|
|
{
|
|
ASSERT (!pSender->pProActiveParityContext->NumPacketsInThisGroup);
|
|
ASSERT (pFileBuffer->PacketOptions.FECContext.NumPacketsInThisGroup);
|
|
pSender->pProActiveParityContext->NumPacketsInThisGroup = pFileBuffer->PacketOptions.FECContext.NumPacketsInThisGroup;
|
|
}
|
|
|
|
pSendBuffer += PacketBufferSize;
|
|
pFileBuffer = (tPACKET_BUFFER *) pSendBuffer;
|
|
}
|
|
|
|
if (!(pSender->pProActiveParityContext->OptionsFlags & PGM_OPTION_FLAG_PARITY_CUR_TGSIZE))
|
|
{
|
|
ASSERT (!pSender->pProActiveParityContext->NumPacketsInThisGroup);
|
|
pSender->pProActiveParityContext->NumPacketsInThisGroup = pSend->FECGroupSize;
|
|
}
|
|
}
|
|
|
|
PgmDetachProcess (&ApcState, &fAttached, REF_PROCESS_ATTACH_PACKETIZE);
|
|
PgmReleaseResource (&pSend->pSender->Resource);
|
|
PgmLock (pSend, *pOldIrq);
|
|
|
|
ASSERT (pSender->BufferPacketsAvailable >= (ULONG) (1 + EmptyPackets));
|
|
ASSERT (NextODataSequenceNumber == (pSender->NextODataSequenceNumber + 1 + EmptyPackets));
|
|
|
|
//
|
|
// Update the Send buffer information
|
|
//
|
|
pSendContext->DataPacketsPacketized++;
|
|
pSendContext->NextPacketOffset += PacketBufferSize;
|
|
pSendContext->DataBytesInLastPacket = DataBytesInThisPacket;
|
|
pSendContext->NextDataOffsetInMdl += DataBytesInThisPacket;
|
|
pSendContext->BytesLeftToPacketize -= DataBytesInThisPacket;
|
|
if (!pSendContext->BytesLeftToPacketize)
|
|
{
|
|
pSendContext->EndSequenceNumber = pSender->NextODataSequenceNumber;
|
|
}
|
|
|
|
|
|
pSender->LastODataSentSequenceNumber++;
|
|
ASSERT (pSender->LastODataSentSequenceNumber == pSender->NextODataSequenceNumber);
|
|
pSender->NextODataSequenceNumber++;
|
|
pSender->NumPacketsRemaining--;
|
|
pSender->BufferPacketsAvailable--;
|
|
pSender->BufferSizeAvailable -= pSender->PacketBufferSize;
|
|
pSender->LeadingWindowOffset += pSender->PacketBufferSize;
|
|
pSender->EmptySequencesForLastSend = EmptyPackets;
|
|
|
|
while (EmptyPackets--)
|
|
{
|
|
pSender->NextODataSequenceNumber++;
|
|
pSender->BufferPacketsAvailable--;
|
|
pSender->BufferSizeAvailable -= pSender->PacketBufferSize;
|
|
pSender->LeadingWindowOffset += pSender->PacketBufferSize;
|
|
pSendContext->NextPacketOffset += pSender->PacketBufferSize;
|
|
|
|
ASSERT (pSender->LeadingWindowOffset <= pSender->MaxDataFileSize);
|
|
}
|
|
|
|
ASSERT (pSender->NextODataSequenceNumber == NextODataSequenceNumber);
|
|
|
|
if (pSender->LeadingWindowOffset >= pSender->MaxDataFileSize)
|
|
{
|
|
ASSERT (pSender->LeadingWindowOffset == pSender->MaxDataFileSize);
|
|
pSender->LeadingWindowOffset = 0;
|
|
}
|
|
|
|
if (pSendContext->NextPacketOffset >= pSender->MaxDataFileSize)
|
|
{
|
|
ASSERT (pSendContext->NextPacketOffset == pSender->MaxDataFileSize);
|
|
pSendContext->NextPacketOffset = 0; // We need to wrap around!
|
|
}
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
PgmSendNextOData(
|
|
IN tSEND_SESSION *pSend,
|
|
IN PGMLockHandle *pOldIrq,
|
|
OUT ULONG *pBytesSent
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to send a Data (OData) packet
|
|
The pSend lock is held before calling this routine
|
|
|
|
Arguments:
|
|
|
|
IN pSend -- Pgm session (sender) context
|
|
IN pOldIrq -- pSend's OldIrq
|
|
OUT pBytesSent -- Set if send succeeded (used for calculating throughput)
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Final status of the send request
|
|
|
|
--*/
|
|
{
|
|
ULONG i, XSum;
|
|
USHORT SendBufferLength;
|
|
KAPC_STATE ApcState;
|
|
BOOLEAN fAttached;
|
|
tCLIENT_SEND_REQUEST *pSendContext;
|
|
tPACKET_OPTIONS PacketOptions;
|
|
ULONG OptionValue;
|
|
tSEND_CONTEXT *pSender = pSend->pSender;
|
|
SEQ_TYPE FECGroupMask = pSend->FECGroupSize-1;
|
|
BOOLEAN fSendingFECPacket = FALSE;
|
|
BOOLEAN fResetOptions = FALSE;
|
|
tBASIC_DATA_PACKET_HEADER *pODataBuffer = NULL;
|
|
tPACKET_BUFFER *pSendBuffer = NULL;
|
|
UCHAR EmptyPackets = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
*pBytesSent = 0; // Initialize
|
|
if (pSend->pSender->BufferPacketsAvailable < pSend->FECGroupSize)
|
|
{
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
if (IsListEmpty (&pSender->PendingPacketizedSends))
|
|
{
|
|
if (IsListEmpty (&pSender->PendingSends))
|
|
{
|
|
ASSERT (0);
|
|
ExFreeToNPagedLookasideList (&pSender->SenderBufferLookaside, pSendBuffer);
|
|
return (STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
pSendContext = CONTAINING_RECORD (pSender->PendingSends.Flink, tCLIENT_SEND_REQUEST, Linkage);
|
|
RemoveEntryList (&pSendContext->Linkage);
|
|
InsertTailList (&pSender->PendingPacketizedSends, &pSendContext->Linkage);
|
|
|
|
pSendContext->NextPacketOffset = pSend->pSender->LeadingWindowOffset; // First packet's offset
|
|
pSendContext->StartSequenceNumber = pSend->pSender->NextODataSequenceNumber;
|
|
pSendContext->EndSequenceNumber = pSend->pSender->NextODataSequenceNumber; // temporary
|
|
|
|
if (pSendContext->LastMessageOffset)
|
|
{
|
|
pSendContext->MessageFirstSequenceNumber = pSend->pSender->LastMessageFirstSequence;
|
|
}
|
|
else
|
|
{
|
|
pSendContext->MessageFirstSequenceNumber = pSendContext->StartSequenceNumber;
|
|
pSend->pSender->LastMessageFirstSequence = pSendContext->StartSequenceNumber;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pSendContext = CONTAINING_RECORD (pSender->PendingPacketizedSends.Flink, tCLIENT_SEND_REQUEST, Linkage);
|
|
}
|
|
|
|
//
|
|
// This routine is called only if we have a packet to send, so
|
|
// set pODataBuffer to the packet to be sent
|
|
// NumDataPacketsSent and DataPacketsPacketized should both be 0 for a fresh send
|
|
// They will be equal if we had run out of Buffer space for the last
|
|
// packetization (i.e. Send length > available buffer space)
|
|
//
|
|
|
|
if (pSendContext->NumParityPacketsToSend)
|
|
{
|
|
SendBufferLength = (USHORT) pSender->PacketBufferSize;
|
|
if (!(pSendBuffer = ExAllocateFromNPagedLookasideList (&pSender->SenderBufferLookaside)))
|
|
{
|
|
PgmTrace (LogError, ("PgmSendNextOData: ERROR -- " \
|
|
"STATUS_INSUFFICIENT_RESOURCES\n"));
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
PgmZeroMemory (pSendBuffer, pSender->PacketBufferSize); // Zero the buffer
|
|
pODataBuffer = &pSendBuffer->DataPacket;
|
|
|
|
//
|
|
// Release the Send lock and attach to the SectionMap process
|
|
// to compute the parity packet
|
|
//
|
|
PgmUnlock (pSend, *pOldIrq);
|
|
PgmAttachToProcessForVMAccess (pSend->Process, &ApcState, &fAttached, REF_PROCESS_ATTACH_PACKETIZE);
|
|
|
|
SendBufferLength -= sizeof (tPACKET_OPTIONS);
|
|
status = PgmBuildParityPacket (pSend,
|
|
pSender->pLastProActiveGroupLeader,
|
|
pSender->pProActiveParityContext,
|
|
(PUCHAR) pODataBuffer,
|
|
&SendBufferLength,
|
|
PACKET_TYPE_ODATA);
|
|
|
|
PgmDetachProcess (&ApcState, &fAttached, REF_PROCESS_ATTACH_PACKETIZE);
|
|
PgmLock (pSend, *pOldIrq);
|
|
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmTrace (LogError, ("PgmSendNextOData: ERROR -- " \
|
|
"PgmBuildParityPacket returned <%x>\n", status));
|
|
|
|
ExFreeToNPagedLookasideList (&pSender->SenderBufferLookaside, pSendBuffer);
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
ASSERT (SendBufferLength <= (PGM_MAX_FEC_DATA_HEADER_LENGTH +
|
|
htons (pODataBuffer->CommonHeader.TSDULength)));
|
|
fSendingFECPacket = TRUE;
|
|
pSendContext->NumParityPacketsToSend--;
|
|
}
|
|
else
|
|
{
|
|
if (!pSendContext->NumDataPacketsSent)
|
|
{
|
|
pSendContext->SendStartTime = pSend->pSender->TimerTickCount;
|
|
if (pSend->FECOptions)
|
|
{
|
|
ASSERT ((SEQ_LT (pSender->LastODataSentSequenceNumber, pSender->NextODataSequenceNumber)) &&
|
|
((pSender->NextODataSequenceNumber - pSender->LastODataSentSequenceNumber) <= pSend->FECGroupSize));
|
|
pSender->LastODataSentSequenceNumber = pSender->NextODataSequenceNumber - 1;
|
|
}
|
|
}
|
|
|
|
status = GetNextPacketOptionsAndData (pSend, pSendContext, &pODataBuffer, pOldIrq, &SendBufferLength);
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmTrace (LogError, ("PgmSendNextOData: ERROR -- " \
|
|
"GetNextPacketOptionsAndDataOffset returned <%x>\n", status));
|
|
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
ASSERT (pSendContext->NumDataPacketsSent < pSendContext->DataPacketsPacketized);
|
|
pSendContext->NumDataPacketsSent++;
|
|
}
|
|
|
|
//
|
|
// If we have sent all the data for this Send (or however many bytes
|
|
// we had packetized from this send), we need to packetize more packets
|
|
//
|
|
if ((pSendContext->NumDataPacketsSent == pSendContext->DataPacketsPacketized) &&
|
|
(!pSendContext->NumParityPacketsToSend) &&
|
|
(!pSendContext->BytesLeftToPacketize))
|
|
{
|
|
//
|
|
// Move it to the CompletedSends list
|
|
// The last send completion will complete the Send Irp
|
|
//
|
|
ASSERT (pSender->NextODataSequenceNumber == (1 + pSendContext->EndSequenceNumber + pSender->EmptySequencesForLastSend));
|
|
|
|
RemoveEntryList (&pSendContext->Linkage);
|
|
InsertTailList (&pSender->CompletedSendsInWindow, &pSendContext->Linkage);
|
|
pSender->NumODataRequestsPending--;
|
|
//
|
|
// If the last packet on this Send had a FIN, we will need to
|
|
// follow this send with an Ambient SPM including the FIN flag
|
|
//
|
|
ASSERT (!pSendContext->bLastSend);
|
|
if (pSendContext->DataOptions & PGM_OPTION_FLAG_FIN)
|
|
{
|
|
PgmTrace (LogPath, ("PgmSendNextOData: " \
|
|
"Setting FIN since client closed session!\n"));
|
|
|
|
pSender->SpmOptions |= PGM_OPTION_FLAG_FIN;
|
|
pSender->CurrentSPMTimeout = pSender->AmbientSPMTimeout;
|
|
pSend->SessionFlags |= PGM_SESSION_FLAG_SEND_AMBIENT_SPM;
|
|
}
|
|
}
|
|
|
|
pSendContext->NumSendsPending++;
|
|
ASSERT (pSendContext->NumSendsPending);
|
|
PgmUnlock (pSend, *pOldIrq);
|
|
|
|
pODataBuffer->TrailingEdgeSequenceNumber = htonl ((ULONG) pSender->TrailingGroupSequenceNumber);
|
|
XSum = 0;
|
|
pODataBuffer->CommonHeader.Checksum = 0;
|
|
XSum = tcpxsum (XSum, (CHAR *) pODataBuffer, SendBufferLength); // Compute the Checksum
|
|
pODataBuffer->CommonHeader.Checksum = (USHORT) (~XSum);
|
|
|
|
status = TdiSendDatagram (pSender->pAddress->pFileObject,
|
|
pSender->pAddress->pDeviceObject,
|
|
pODataBuffer,
|
|
(ULONG) SendBufferLength,
|
|
PgmSendODataCompletion, // Completion
|
|
pSendContext, // Context1
|
|
pSendBuffer, // Context2
|
|
pSender->DestMCastIpAddress,
|
|
pSender->DestMCastPort,
|
|
(BOOLEAN) (pSendBuffer ? FALSE : TRUE));
|
|
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
PgmTrace (LogAllFuncs, ("PgmSendNextOData: " \
|
|
"[%d-%d] -- Sent <%d> bytes to <%x:%d>\n",
|
|
(ULONG) pSender->TrailingGroupSequenceNumber,
|
|
(ULONG) pSender->LastODataSentSequenceNumber,
|
|
SendBufferLength, pSender->DestMCastIpAddress, pSender->DestMCastPort));
|
|
|
|
PgmLock (pSend, *pOldIrq);
|
|
|
|
pSend->pSender->TotalODataPacketsSent++;
|
|
pSend->pSender->ODataPacketsInLastInterval++;
|
|
|
|
*pBytesSent = SendBufferLength;
|
|
return (status);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
PgmCancelAllSends(
|
|
IN tSEND_SESSION *pSend,
|
|
IN LIST_ENTRY *pListEntry,
|
|
IN PIRP pIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles the cancelling of a Send Irp. It must release the
|
|
cancel spin lock before returning re: IoCancelIrp().
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
tCLIENT_SEND_REQUEST *pSendContext;
|
|
PIRP pIrpToComplete = NULL;
|
|
SEQ_TYPE HighestLeadSeq;
|
|
ULONG NumExSequencesInOldWindow, NumRequests = 0;
|
|
ULONGLONG BufferSpaceFreed;
|
|
|
|
//
|
|
// Now cancel all the remaining send requests because the integrity
|
|
// of the data cannot be guaranteed
|
|
// We also have to deal with the fact that some Irps may have
|
|
// data in the transport (i.e. possibly the first send on the Packetized
|
|
// list, or the last send of the completed list)
|
|
//
|
|
// We will start with the unpacketized requests
|
|
//
|
|
while (!IsListEmpty (&pSend->pSender->PendingSends))
|
|
{
|
|
pEntry = RemoveHeadList (&pSend->pSender->PendingSends);
|
|
pSendContext = CONTAINING_RECORD (pEntry, tCLIENT_SEND_REQUEST, Linkage);
|
|
InsertTailList (pListEntry, pEntry);
|
|
NumRequests++;
|
|
|
|
ASSERT (!pSendContext->NumSendsPending);
|
|
|
|
//
|
|
// If this is a partial send, we will mark the Irp for completion
|
|
// initially to the companion send request (to avoid complications
|
|
// of Sends pending in the transport here)
|
|
//
|
|
if (pSendContext->pMessage2Request)
|
|
{
|
|
//
|
|
// pMessage2Request could either be on the PendingPacketizedSends
|
|
// list or on the Completed Sends list (awaiting a send completion)
|
|
//
|
|
ASSERT (pSendContext->pMessage2Request->pIrp);
|
|
if (pSendContext->pIrpToComplete)
|
|
{
|
|
ASSERT (!pSendContext->pMessage2Request->pIrpToComplete);
|
|
pSendContext->pMessage2Request->pIrpToComplete = pSendContext->pIrpToComplete;
|
|
pSendContext->pIrpToComplete = NULL;
|
|
}
|
|
|
|
pSendContext->pMessage2Request->pMessage2Request = NULL;
|
|
pSendContext->pMessage2Request = NULL;
|
|
}
|
|
|
|
ASSERT (pSendContext->BytesLeftToPacketize == pSendContext->BytesInSend);
|
|
pSend->pSender->NumODataRequestsPending--;
|
|
pSend->pSender->NumPacketsRemaining -= pSendContext->NumPacketsRemaining;
|
|
}
|
|
|
|
//
|
|
// Now, go through all the sends which have already been packetized
|
|
// except for the first one which we will handle below
|
|
//
|
|
HighestLeadSeq = pSend->pSender->NextODataSequenceNumber;
|
|
pEntry = pSend->pSender->PendingPacketizedSends.Flink;
|
|
while ((pEntry = pEntry->Flink) != &pSend->pSender->PendingPacketizedSends)
|
|
{
|
|
pSendContext = CONTAINING_RECORD (pEntry, tCLIENT_SEND_REQUEST, Linkage);
|
|
pEntry = pEntry->Blink;
|
|
RemoveEntryList (&pSendContext->Linkage);
|
|
InsertTailList (pListEntry, &pSendContext->Linkage);
|
|
pSend->pSender->NumODataRequestsPending--;
|
|
pSend->pSender->NumPacketsRemaining -= pSendContext->NumPacketsRemaining;
|
|
NumRequests++;
|
|
|
|
if (SEQ_LT (pSendContext->StartSequenceNumber, HighestLeadSeq))
|
|
{
|
|
HighestLeadSeq = pSendContext->StartSequenceNumber;
|
|
}
|
|
|
|
ASSERT ((!pSendContext->NumDataPacketsSent) && (!pSendContext->NumSendsPending));
|
|
if (pSendContext->pMessage2Request)
|
|
{
|
|
//
|
|
// pMessage2Request could either be on the PendingPacketizedSends
|
|
// list or on the Completed Sends list (awaiting a send completion)
|
|
//
|
|
ASSERT (pSendContext->pMessage2Request->pIrp);
|
|
if (pSendContext->pIrpToComplete)
|
|
{
|
|
ASSERT (!pSendContext->pMessage2Request->pIrpToComplete);
|
|
pSendContext->pMessage2Request->pIrpToComplete = pSendContext->pIrpToComplete;
|
|
pSendContext->pIrpToComplete = NULL;
|
|
}
|
|
|
|
pSendContext->pMessage2Request->pMessage2Request = NULL;
|
|
pSendContext->pMessage2Request = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Terminate the first PendingPacketizedSend only if we have not
|
|
// yet started sending it or this Cancel was meant for that request
|
|
// (Try to protect data integrity as much as possible)
|
|
//
|
|
if (!IsListEmpty (&pSend->pSender->PendingPacketizedSends))
|
|
{
|
|
pSendContext = CONTAINING_RECORD (pSend->pSender->PendingPacketizedSends.Flink, tCLIENT_SEND_REQUEST, Linkage);
|
|
if ((!pSendContext->NumDataPacketsSent) ||
|
|
(!pIrp || (pSendContext->pIrp == pIrp)))
|
|
{
|
|
RemoveEntryList (&pSendContext->Linkage);
|
|
ASSERT (IsListEmpty (&pSend->pSender->PendingPacketizedSends));
|
|
NumRequests++;
|
|
|
|
//
|
|
// If we have some data pending in the transport,
|
|
// then we will have to let the SendCompletion handle that
|
|
//
|
|
ASSERT ((pSendContext->BytesLeftToPacketize) ||
|
|
(pSendContext->NumDataPacketsSent < pSendContext->DataPacketsPacketized) ||
|
|
(pSendContext->NumParityPacketsToSend));
|
|
|
|
PgmTrace (LogPath, ("PgmCancelAllSends: " \
|
|
"Partial Send, pIrp=<%p>, BytesLeftToPacketize=<%d/%d>, PacketsSent=<%d/%d>, Pending=<%d>\n",
|
|
pSendContext->pIrp, pSendContext->BytesLeftToPacketize,
|
|
pSendContext->BytesInSend, pSendContext->NumDataPacketsSent,
|
|
pSendContext->DataPacketsPacketized, pSendContext->NumSendsPending));
|
|
|
|
pSendContext->BytesLeftToPacketize = 0;
|
|
pSendContext->DataPacketsPacketized = pSendContext->NumDataPacketsSent;
|
|
pSendContext->NumParityPacketsToSend = 0;
|
|
|
|
pSend->pSender->NumODataRequestsPending--;
|
|
pSend->pSender->NumPacketsRemaining -= pSendContext->NumPacketsRemaining;
|
|
|
|
if (pSendContext->NumSendsPending)
|
|
{
|
|
InsertTailList (&pSend->pSender->CompletedSendsInWindow, &pSendContext->Linkage);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If we have a companion partial, then it must be in the completed list
|
|
// awaiting SendCompletion
|
|
//
|
|
if (pSendContext->pMessage2Request)
|
|
{
|
|
ASSERT (pSendContext->pMessage2Request->pIrp);
|
|
if (pSendContext->pIrpToComplete)
|
|
{
|
|
ASSERT (!pSendContext->pMessage2Request->BytesLeftToPacketize);
|
|
ASSERT (!pSendContext->pMessage2Request->pIrpToComplete);
|
|
pSendContext->pMessage2Request->pIrpToComplete = pSendContext->pIrpToComplete;
|
|
pSendContext->pIrpToComplete = NULL;
|
|
}
|
|
|
|
pSendContext->pMessage2Request->pMessage2Request = NULL;
|
|
pSendContext->pMessage2Request = NULL;
|
|
}
|
|
|
|
InsertTailList (pListEntry, &pSendContext->Linkage);
|
|
}
|
|
|
|
pSendContext->EndSequenceNumber = pSend->pSender->LastODataSentSequenceNumber;
|
|
HighestLeadSeq = pSend->pSender->LastODataSentSequenceNumber + 1;
|
|
}
|
|
}
|
|
|
|
NumExSequencesInOldWindow = (ULONG) (SEQ_TYPE) (pSend->pSender->NextODataSequenceNumber-HighestLeadSeq);
|
|
BufferSpaceFreed = NumExSequencesInOldWindow * pSend->pSender->PacketBufferSize;
|
|
if (NumExSequencesInOldWindow)
|
|
{
|
|
pSend->SessionFlags |= PGM_SESSION_SENDS_CANCELLED;
|
|
|
|
PgmTrace (LogPath, ("PgmCancelAllSends: " \
|
|
"[%d]: NumSeqs=<%d>, NextOData=<%d-->%d>, BuffFreeed=<%d>, LeadingOffset=<%d-->%d>\n",
|
|
NumRequests, NumExSequencesInOldWindow,
|
|
(ULONG) pSend->pSender->NextODataSequenceNumber, (ULONG) HighestLeadSeq,
|
|
(ULONG) BufferSpaceFreed, (ULONG) pSend->pSender->LeadingWindowOffset,
|
|
(ULONG) (pSend->pSender->LeadingWindowOffset - BufferSpaceFreed)));
|
|
}
|
|
|
|
pSend->pSender->NextODataSequenceNumber = HighestLeadSeq;
|
|
|
|
pSend->pSender->BufferPacketsAvailable += NumExSequencesInOldWindow;
|
|
pSend->pSender->BufferSizeAvailable += BufferSpaceFreed;
|
|
ASSERT (pSend->pSender->BufferSizeAvailable <= pSend->pSender->MaxDataFileSize);
|
|
if (pSend->pSender->LeadingWindowOffset >= BufferSpaceFreed)
|
|
{
|
|
pSend->pSender->LeadingWindowOffset -= BufferSpaceFreed;
|
|
}
|
|
else
|
|
{
|
|
pSend->pSender->LeadingWindowOffset = pSend->pSender->MaxDataFileSize - (BufferSpaceFreed - pSend->pSender->LeadingWindowOffset);
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
ULONG
|
|
AdvanceWindow(
|
|
IN tSEND_SESSION *pSend
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to check if we need to advance the
|
|
trailing window, and does so as appropriate
|
|
The pSend lock is held before calling this routine
|
|
|
|
Arguments:
|
|
|
|
IN pSend -- Pgm session (sender) context
|
|
|
|
Return Value:
|
|
|
|
TRUE if the send window buffer is empty, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY *pEntry;
|
|
tCLIENT_SEND_REQUEST *pSendContextAdjust;
|
|
tCLIENT_SEND_REQUEST *pSendContext1;
|
|
tSEND_RDATA_CONTEXT *pRDataContext;
|
|
SEQ_TYPE HighestTrailSeq, MaxSequencesToAdvance, NumSequences, NumExSequencesInOldWindow;
|
|
ULONGLONG NewTrailTime, PreferredTrailTime = 0;
|
|
tSEND_CONTEXT *pSender = pSend->pSender;
|
|
|
|
//
|
|
// See if we need to increment the Trailing edge of our transmit window
|
|
//
|
|
if (pSender->TimerTickCount > pSender->NextWindowAdvanceTime)
|
|
{
|
|
PgmTrace (LogPath, ("AdvanceWindow: " \
|
|
"Advancing NextWindowAdvanceTime -- TimerTC = [%I64d] >= NextWinAdvT [%I64d]\n",
|
|
pSender->TimerTickCount, pSender->NextWindowAdvanceTime));
|
|
|
|
pSender->NextWindowAdvanceTime = pSender->TimerTickCount + pSender->WindowAdvanceDeltaTime;
|
|
}
|
|
|
|
PreferredTrailTime = (pSender->NextWindowAdvanceTime - pSender->WindowAdvanceDeltaTime) -
|
|
pSender->WindowSizeTime;
|
|
if (PreferredTrailTime < pSender->TrailingEdgeTime)
|
|
{
|
|
//
|
|
// Out window is already ahead of the Preferred trail time
|
|
//
|
|
PgmTrace (LogAllFuncs, ("AdvanceWindow: " \
|
|
"Transmit Window=[%d, %d], TimerTC=[%I64d], PrefTrail=<%I64d>, TrailTime=<%I64d>\n",
|
|
(ULONG) pSender->TrailingEdgeSequenceNumber, (ULONG) (pSender->NextODataSequenceNumber-1),
|
|
pSender->TimerTickCount, PreferredTrailTime, pSender->TrailingEdgeTime));
|
|
|
|
return (0);
|
|
}
|
|
|
|
//
|
|
// Determine the maximum sequences we can advance by (initially all seqs in window)
|
|
//
|
|
HighestTrailSeq = pSender->NextODataSequenceNumber & ~((SEQ_TYPE) pSend->FECGroupSize-1); // Init
|
|
NumSequences = HighestTrailSeq - pSender->TrailingEdgeSequenceNumber;
|
|
|
|
//
|
|
// Now, limit that depending on pending RData
|
|
//
|
|
if (pRDataContext = AnyRequestPending (pSender->pRDataInfo)) // Start with pending RData requests
|
|
{
|
|
if (SEQ_LT (pRDataContext->RDataSequenceNumber, HighestTrailSeq))
|
|
{
|
|
HighestTrailSeq = pRDataContext->RDataSequenceNumber;
|
|
}
|
|
}
|
|
MaxSequencesToAdvance = HighestTrailSeq - pSender->TrailingEdgeSequenceNumber;
|
|
|
|
//
|
|
// If we are required to advance the window on-demand, then we
|
|
// will need to limit the Maximum sequences we can advance by
|
|
//
|
|
if ((pSender->pAddress->Flags & PGM_ADDRESS_USE_WINDOW_AS_DATA_CACHE) &&
|
|
!(pSend->SessionFlags & PGM_SESSION_SENDER_DISCONNECTED))
|
|
{
|
|
if (NumSequences <= (pSender->MaxPacketsInBuffer >> 1))
|
|
{
|
|
MaxSequencesToAdvance = 0;
|
|
}
|
|
else if ((NumSequences - MaxSequencesToAdvance) < (pSender->MaxPacketsInBuffer >> 1))
|
|
{
|
|
MaxSequencesToAdvance = (ULONG) (NumSequences - (pSender->MaxPacketsInBuffer >> 1));
|
|
HighestTrailSeq = pSender->TrailingEdgeSequenceNumber + MaxSequencesToAdvance;
|
|
}
|
|
}
|
|
|
|
if (!MaxSequencesToAdvance)
|
|
{
|
|
PgmTrace (LogAllFuncs, ("AdvanceWindow: " \
|
|
"Transmit Window=[%d, %d], TimerTC=[%I64d], MaxSeqs=<%d>, PrefTrail=<%I64d>, TrailTime=<%I64d>\n",
|
|
(ULONG) pSender->TrailingEdgeSequenceNumber, (ULONG) (pSender->NextODataSequenceNumber-1),
|
|
pSender->TimerTickCount, MaxSequencesToAdvance,
|
|
PreferredTrailTime, pSender->TrailingEdgeTime));
|
|
|
|
return (0);
|
|
}
|
|
|
|
PgmTrace (LogPath, ("AdvanceWindow: " \
|
|
"PreferredTrail=[%I64d] > TrailingEdge=[%I64d], WinAdvMSecs=<%I64d>, WinSizeMSecs=<%I64d>\n",
|
|
PreferredTrailTime, pSender->TrailingEdgeTime, pSender->WindowAdvanceDeltaTime,
|
|
pSender->WindowSizeTime));
|
|
|
|
NewTrailTime = PreferredTrailTime; // Init to Preferred Trail time (in case no data)
|
|
|
|
// Now, check the completed sends list
|
|
NumExSequencesInOldWindow = NumSequences = 0;
|
|
pSendContext1 = pSendContextAdjust = NULL;
|
|
pEntry = pSender->CompletedSendsInWindow.Flink;
|
|
while (pEntry != &pSender->CompletedSendsInWindow)
|
|
{
|
|
pSendContext1 = CONTAINING_RECORD (pEntry, tCLIENT_SEND_REQUEST, Linkage);
|
|
ASSERT (NumExSequencesInOldWindow <= MaxSequencesToAdvance);
|
|
ASSERT (SEQ_LEQ (pSendContext1->StartSequenceNumber, HighestTrailSeq));
|
|
ASSERT (SEQ_GEQ (pSendContext1->EndSequenceNumber, pSender->TrailingEdgeSequenceNumber));
|
|
|
|
NewTrailTime = pSendContext1->SendStartTime;
|
|
if ((pSendContext1->NumSendsPending) || // Cannot advance if completions are pending
|
|
(pSendContext1->SendStartTime >= PreferredTrailTime) || // need to keep for window
|
|
(SEQ_GEQ (pSendContext1->StartSequenceNumber, HighestTrailSeq))) // Only == valid
|
|
{
|
|
ASSERT (SEQ_LEQ (pSendContext1->StartSequenceNumber, HighestTrailSeq));
|
|
|
|
//
|
|
// Reset HighestTrailSeq
|
|
//
|
|
if (SEQ_GT (pSender->TrailingEdgeSequenceNumber, pSendContext1->StartSequenceNumber))
|
|
{
|
|
HighestTrailSeq = pSender->TrailingEdgeSequenceNumber;
|
|
ASSERT (!NumExSequencesInOldWindow);
|
|
}
|
|
else
|
|
{
|
|
HighestTrailSeq = pSendContext1->StartSequenceNumber;
|
|
NumExSequencesInOldWindow = HighestTrailSeq - pSender->TrailingEdgeSequenceNumber;
|
|
}
|
|
MaxSequencesToAdvance = NumExSequencesInOldWindow;
|
|
|
|
break;
|
|
}
|
|
else if (SEQ_GEQ (pSendContext1->EndSequenceNumber, HighestTrailSeq)) // Need to keep this Send
|
|
{
|
|
if (SEQ_LEQ (pSender->TrailingEdgeSequenceNumber, pSendContext1->StartSequenceNumber))
|
|
{
|
|
NumExSequencesInOldWindow = pSendContext1->StartSequenceNumber -
|
|
pSender->TrailingEdgeSequenceNumber;
|
|
}
|
|
|
|
pSendContextAdjust = pSendContext1;
|
|
break;
|
|
}
|
|
|
|
// Remove the send that is definitely out of the new window
|
|
pEntry = pEntry->Flink;
|
|
RemoveEntryList (&pSendContext1->Linkage);
|
|
ASSERT ((!pSendContext1->pMessage2Request) && (!pSendContext1->pIrp));
|
|
ExFreeToNPagedLookasideList (&pSender->SendContextLookaside,pSendContext1);
|
|
}
|
|
|
|
ASSERT (NumExSequencesInOldWindow <= MaxSequencesToAdvance);
|
|
|
|
//
|
|
// pSendContext1 will be NULL if there are no completed sends,
|
|
// in which case we may have 1 huge current send that could be hogging
|
|
// our buffer, so check that then!
|
|
//
|
|
if ((!pSendContext1) &&
|
|
(!IsListEmpty (&pSender->PendingPacketizedSends)))
|
|
{
|
|
ASSERT (!pSendContextAdjust);
|
|
pSendContextAdjust = CONTAINING_RECORD (pSender->PendingPacketizedSends.Flink, tCLIENT_SEND_REQUEST, Linkage);
|
|
if ((pSendContextAdjust->NumSendsPending) || // Ensure no sends pending
|
|
(pSendContextAdjust->NumParityPacketsToSend) || // No parity packets left to send
|
|
(!pSendContextAdjust->NumDataPacketsSent) || // No packets sent yet
|
|
(pSendContextAdjust->DataPacketsPacketized != pSendContextAdjust->NumDataPacketsSent) ||
|
|
(pSendContextAdjust->SendStartTime > PreferredTrailTime))
|
|
{
|
|
pSendContextAdjust = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// pSendContextAdjust will be non-NULL if we need to adjust
|
|
// the Trailing edge within this Send request
|
|
//
|
|
if (pSendContextAdjust)
|
|
{
|
|
//
|
|
// Do some sanity checks!
|
|
//
|
|
ASSERT (PreferredTrailTime >= pSendContextAdjust->SendStartTime);
|
|
ASSERT (SEQ_GEQ (HighestTrailSeq, pSender->TrailingEdgeSequenceNumber));
|
|
ASSERT (SEQ_GEQ (HighestTrailSeq, pSendContextAdjust->StartSequenceNumber));
|
|
ASSERT (SEQ_GEQ (pSendContextAdjust->EndSequenceNumber,pSender->TrailingEdgeSequenceNumber));
|
|
|
|
//
|
|
// See if this send is partially in or out of the window now!
|
|
// Calculate the offset of sequences in this Send request for the
|
|
// preferred trail time
|
|
//
|
|
NumSequences = (ULONG) (SEQ_TYPE) (((PreferredTrailTime - pSendContextAdjust->SendStartTime) *
|
|
BASIC_TIMER_GRANULARITY_IN_MSECS *
|
|
pSender->pAddress->RateKbitsPerSec) /
|
|
(pSender->pAddress->OutIfMTU << LOG2_BITS_PER_BYTE));
|
|
|
|
//
|
|
// Limit the NumSequences by the number of packets in this Send
|
|
//
|
|
if (SEQ_GT ((pSendContextAdjust->StartSequenceNumber + NumSequences),
|
|
pSendContextAdjust->EndSequenceNumber))
|
|
{
|
|
NumSequences = pSendContextAdjust->EndSequenceNumber -
|
|
pSendContextAdjust->StartSequenceNumber + 1;
|
|
}
|
|
|
|
//
|
|
// Limit the NumSequences by the HighestTrailSeq (pending RData requests)
|
|
//
|
|
if (SEQ_GT ((pSendContextAdjust->StartSequenceNumber + NumSequences),
|
|
HighestTrailSeq))
|
|
{
|
|
NumSequences = HighestTrailSeq - pSendContextAdjust->StartSequenceNumber;
|
|
}
|
|
|
|
//
|
|
// We may not need to advance here if we are at (or behind) the trailing edge
|
|
//
|
|
if (SEQ_LEQ ((pSendContextAdjust->StartSequenceNumber + NumSequences),
|
|
pSender->TrailingEdgeSequenceNumber))
|
|
{
|
|
NumSequences = 0;
|
|
HighestTrailSeq = pSender->TrailingEdgeSequenceNumber;
|
|
}
|
|
else if (SEQ_LT (pSendContextAdjust->StartSequenceNumber, pSender->TrailingEdgeSequenceNumber))
|
|
{
|
|
ASSERT (SEQ_LEQ (pSender->TrailingEdgeSequenceNumber, pSendContextAdjust->EndSequenceNumber));
|
|
NumSequences = pSendContextAdjust->StartSequenceNumber + NumSequences -
|
|
pSender->TrailingEdgeSequenceNumber;
|
|
HighestTrailSeq = pSender->TrailingEdgeSequenceNumber + NumSequences;
|
|
}
|
|
else
|
|
{
|
|
HighestTrailSeq = pSendContextAdjust->StartSequenceNumber + NumSequences;
|
|
}
|
|
|
|
NumExSequencesInOldWindow += NumSequences;
|
|
ASSERT (NumExSequencesInOldWindow <= MaxSequencesToAdvance);
|
|
|
|
//
|
|
// Now, set the NewTrailTime
|
|
//
|
|
NewTrailTime = (NumSequences * pSender->pAddress->OutIfMTU * BITS_PER_BYTE) /
|
|
(pSender->pAddress->RateKbitsPerSec * BASIC_TIMER_GRANULARITY_IN_MSECS);
|
|
NewTrailTime += pSendContextAdjust->SendStartTime;
|
|
|
|
//
|
|
// See, if we can discard this whole send request
|
|
//
|
|
if ((!pSendContextAdjust->BytesLeftToPacketize) &&
|
|
(SEQ_GT ((pSendContextAdjust->StartSequenceNumber + NumSequences),
|
|
pSendContextAdjust->EndSequenceNumber)))
|
|
{
|
|
//
|
|
// We can drop this whole send since it is outside of our window
|
|
//
|
|
ASSERT (HighestTrailSeq == (pSendContextAdjust->EndSequenceNumber + 1));
|
|
|
|
// Remove this send and free it!
|
|
ASSERT ((!pSendContextAdjust->pMessage2Request) && (!pSendContextAdjust->pIrp));
|
|
RemoveEntryList (&pSendContextAdjust->Linkage);
|
|
ExFreeToNPagedLookasideList (&pSender->SendContextLookaside, pSendContextAdjust);
|
|
}
|
|
}
|
|
|
|
if (!NumExSequencesInOldWindow)
|
|
{
|
|
PgmTrace (LogAllFuncs, ("AdvanceWindow: " \
|
|
"Transmit Window=[%d, %d], TimerTC=[%I64d], MaxSeqs=<%d>, PrefTrail=<%I64d>, TrailTime=<%I64d>\n",
|
|
(ULONG) pSender->TrailingEdgeSequenceNumber, (ULONG) (pSender->NextODataSequenceNumber-1),
|
|
pSender->TimerTickCount, MaxSequencesToAdvance,
|
|
PreferredTrailTime, pSender->TrailingEdgeTime));
|
|
|
|
return (0);
|
|
}
|
|
|
|
ASSERT (SEQ_GT (HighestTrailSeq, pSender->TrailingEdgeSequenceNumber));
|
|
ASSERT (HighestTrailSeq == (pSender->TrailingEdgeSequenceNumber + NumExSequencesInOldWindow));
|
|
|
|
//
|
|
// Now, limit the # sequences to advance with the window size
|
|
//
|
|
if (NumExSequencesInOldWindow > MaxSequencesToAdvance)
|
|
{
|
|
ASSERT (0);
|
|
NumExSequencesInOldWindow = MaxSequencesToAdvance;
|
|
}
|
|
HighestTrailSeq = pSender->TrailingEdgeSequenceNumber + NumExSequencesInOldWindow;
|
|
|
|
PgmTrace (LogPath, ("AdvanceWindow: " \
|
|
"BuffAva=<%d>, NumSeqsAdvanced=<%d>, Max=<%d>, TrailSeqNum=<%d>=><%d>, TrailTime=<%I64d>=><%I64d>\n",
|
|
(ULONG) pSender->BufferSizeAvailable, (ULONG) NumExSequencesInOldWindow,
|
|
(ULONG) MaxSequencesToAdvance, (ULONG) pSender->TrailingEdgeSequenceNumber,
|
|
(ULONG) HighestTrailSeq, pSender->TrailingEdgeTime, NewTrailTime));
|
|
|
|
//
|
|
// Now, adjust the buffer settings
|
|
//
|
|
pSender->BufferPacketsAvailable += NumExSequencesInOldWindow;
|
|
pSender->BufferSizeAvailable += (NumExSequencesInOldWindow * pSender->PacketBufferSize);
|
|
ASSERT (pSender->BufferPacketsAvailable <= pSender->MaxPacketsInBuffer);
|
|
ASSERT (pSender->BufferSizeAvailable <= pSender->MaxDataFileSize);
|
|
pSender->TrailingWindowOffset += (NumExSequencesInOldWindow * pSender->PacketBufferSize);
|
|
if (pSender->TrailingWindowOffset >= pSender->MaxDataFileSize)
|
|
{
|
|
// Wrap around case!
|
|
pSender->TrailingWindowOffset -= pSender->MaxDataFileSize;
|
|
}
|
|
ASSERT (pSender->TrailingWindowOffset < pSender->MaxDataFileSize);
|
|
pSender->TrailingEdgeSequenceNumber = HighestTrailSeq;
|
|
pSender->TrailingGroupSequenceNumber = (HighestTrailSeq+pSend->FECGroupSize-1) &
|
|
~((SEQ_TYPE) pSend->FECGroupSize-1);
|
|
pSender->TrailingEdgeTime = NewTrailTime;
|
|
UpdateRDataTrailingEdge (pSender->pRDataInfo, HighestTrailSeq);
|
|
|
|
PgmTrace (LogAllFuncs, ("AdvanceWindow: " \
|
|
"Transmit Window Range=[%d, %d], TimerTC=[%I64d]\n",
|
|
(ULONG) pSender->TrailingEdgeSequenceNumber,
|
|
(ULONG) (pSender->NextODataSequenceNumber-1),
|
|
pSender->TimerTickCount));
|
|
|
|
return (NumExSequencesInOldWindow);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOLEAN
|
|
CheckForTermination(
|
|
IN tSEND_SESSION *pSend,
|
|
IN PGMLockHandle *pOldIrq
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to check and terminate the session
|
|
if necessary.
|
|
The pSend lock is held before calling this routine
|
|
|
|
Arguments:
|
|
|
|
IN pSend -- Pgm session (sender) context
|
|
|
|
Return Value:
|
|
|
|
TRUE if the send window buffer is empty, FALSE otherwise
|
|
|
|
--*/
|
|
{
|
|
LIST_ENTRY *pEntry;
|
|
LIST_ENTRY ListEntry;
|
|
tCLIENT_SEND_REQUEST *pSendContext;
|
|
tSEND_RDATA_CONTEXT *pRDataContext;
|
|
PIRP pIrp;
|
|
ULONG NumSequences;
|
|
|
|
if (!(PGM_VERIFY_HANDLE (pSend, PGM_VERIFY_SESSION_DOWN)) &&
|
|
!(PGM_VERIFY_HANDLE (pSend->pSender->pAddress, PGM_VERIFY_ADDRESS_DOWN)) &&
|
|
!(pSend->SessionFlags & PGM_SESSION_CLIENT_DISCONNECTED))
|
|
{
|
|
PgmTrace (LogAllFuncs, ("CheckForTermination: " \
|
|
"Session for pSend=<%p> does not need to be terminated\n", pSend));
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// See if we have processed the disconnect for the first time yet
|
|
//
|
|
if (!(pSend->SessionFlags & PGM_SESSION_SENDER_DISCONNECTED))
|
|
{
|
|
PgmTrace (LogStatus, ("CheckForTermination: " \
|
|
"Session is going down!, Packets remaining=<%d>\n", pSend->pSender->NumPacketsRemaining));
|
|
|
|
pSend->SessionFlags |= PGM_SESSION_SENDER_DISCONNECTED;
|
|
|
|
//
|
|
// We have to set the FIN on the Data as well as SPM packets.
|
|
// Thus, there are 2 situations -- either we have finished sending
|
|
// all the Data packets, or we are still in the midst of sending
|
|
//
|
|
// If there are no more sends pending, we will have to
|
|
// modify the last packet ourselves to set the FIN option
|
|
//
|
|
if (!IsListEmpty (&pSend->pSender->PendingSends))
|
|
{
|
|
PgmTrace (LogStatus, ("CheckForTermination: " \
|
|
"Send pending on list -- setting bLastSend for FIN on last Send\n"));
|
|
|
|
pSendContext = CONTAINING_RECORD (pSend->pSender->PendingSends.Blink, tCLIENT_SEND_REQUEST,Linkage);
|
|
|
|
//
|
|
// We will just set a flag here, so that when the last packet
|
|
// is packetized, the FIN flags are set
|
|
//
|
|
pSendContext->bLastSend = TRUE;
|
|
}
|
|
else if (pSend->pSender->NumODataRequestsPending)
|
|
{
|
|
PgmTrace (LogStatus, ("CheckForTermination: " \
|
|
"Last Send in progress -- setting bLastSend for FIN on this Send\n"));
|
|
|
|
//
|
|
// If have already packetized the last send, but have not yet
|
|
// sent it out, then PgmSendNextOData will put the FIN in the data packet
|
|
// otherwise, if we have not yet packetized the packet, then we will set the
|
|
// FIN option while preparing the last packet
|
|
//
|
|
pSendContext = CONTAINING_RECORD (pSend->pSender->PendingPacketizedSends.Blink, tCLIENT_SEND_REQUEST,Linkage);
|
|
pSendContext->bLastSend = TRUE;
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogStatus, ("CheckForTermination: " \
|
|
"No Sends in progress -- setting FIN for next SPM\n"));
|
|
|
|
//
|
|
// We have finished packetizing and sending all the packets,
|
|
// so set the FIN flag on the SPMs and also modify the last
|
|
// RData packet (if still in the window) for the FIN -- this
|
|
// will be done when the next RData packet is sent out
|
|
//
|
|
if ((pSend->SessionFlags & PGM_SESSION_SENDS_CANCELLED) ||
|
|
!(pSend->pIrpDisconnect))
|
|
{
|
|
pSend->pSender->SpmOptions &= ~PGM_OPTION_FLAG_FIN;
|
|
pSend->pSender->SpmOptions |= PGM_OPTION_FLAG_RST;
|
|
}
|
|
else
|
|
{
|
|
pSend->pSender->SpmOptions &= ~PGM_OPTION_FLAG_RST;
|
|
pSend->pSender->SpmOptions |= PGM_OPTION_FLAG_FIN;
|
|
}
|
|
|
|
//
|
|
// We also need to send an SPM immediately
|
|
//
|
|
pSend->pSender->CurrentSPMTimeout = pSend->pSender->AmbientSPMTimeout;
|
|
pSend->SessionFlags |= PGM_SESSION_FLAG_SEND_AMBIENT_SPM;
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// If we have a (graceful) disconnect Irp to complete, we should complete
|
|
// it if we have timed out, or are ready to do so now
|
|
//
|
|
if ((pIrp = pSend->pIrpDisconnect) && // Disconnect Irp pending
|
|
(((pSend->pSender->DisconnectTimeInTicks) && (pSend->pSender->TimerTickCount >
|
|
pSend->pSender->DisconnectTimeInTicks)) ||
|
|
((IsListEmpty (&pSend->pSender->PendingSends)) && // No Unpacketized Sends pending
|
|
(IsListEmpty (&pSend->pSender->PendingPacketizedSends)) && // No Packetized sends pending
|
|
!(FindFirstEntry (pSend, NULL, TRUE)) && // No Pending RData requests
|
|
(IsListEmpty (&pSend->pSender->CompletedSendsInWindow)) && // Window is Empty
|
|
(pSend->pSender->SpmOptions & (PGM_OPTION_FLAG_FIN | // FIN | RST | RST_N set on SPMs
|
|
PGM_OPTION_FLAG_RST |
|
|
PGM_OPTION_FLAG_RST_N)) &&
|
|
!(pSend->SessionFlags & PGM_SESSION_FLAG_SEND_AMBIENT_SPM)))) // No Ambient Spm pending
|
|
{
|
|
pSend->pIrpDisconnect = NULL;
|
|
PgmUnlock (pSend, *pOldIrq);
|
|
|
|
PgmTrace (LogStatus, ("CheckForTermination: " \
|
|
"Completing Graceful disconnect pIrp=<%p>\n", pIrp));
|
|
|
|
PgmIoComplete (pIrp, STATUS_SUCCESS, 0);
|
|
|
|
PgmLock (pSend, *pOldIrq);
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Do the final cleanup only if the handles have been closed
|
|
// or the disconnect has timed out
|
|
//
|
|
if (!(PGM_VERIFY_HANDLE (pSend, PGM_VERIFY_SESSION_DOWN)) &&
|
|
!(PGM_VERIFY_HANDLE (pSend->pSender->pAddress, PGM_VERIFY_ADDRESS_DOWN)) &&
|
|
((!pSend->pSender->DisconnectTimeInTicks) || (pSend->pSender->TimerTickCount <
|
|
pSend->pSender->DisconnectTimeInTicks)))
|
|
{
|
|
PgmTrace (LogAllFuncs, ("CheckForTermination: " \
|
|
"Handles have not yet been closed for pSend=<%p>, TC=<%I64d>, DisconnectTime=<%I64d>\n",
|
|
pSend, pSend->pSender->TimerTickCount, pSend->pSender->DisconnectTimeInTicks));
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
// *****************************************************************
|
|
// We will reach here only if we need to cleanup ASAP
|
|
// *****************************************************************
|
|
|
|
//
|
|
// First, cleanup all handled RData requests (which have completed)
|
|
//
|
|
RemoveAllEntries (pSend, TRUE);
|
|
|
|
//
|
|
// Now, Cancel and Complete all the send requests which are pending
|
|
//
|
|
InitializeListHead (&ListEntry);
|
|
PgmCancelAllSends (pSend, &ListEntry, NULL);
|
|
while (!IsListEmpty (&ListEntry))
|
|
{
|
|
pEntry = RemoveHeadList (&ListEntry);
|
|
pSendContext = CONTAINING_RECORD (pEntry, tCLIENT_SEND_REQUEST, Linkage);
|
|
ASSERT (!pSendContext->pMessage2Request);
|
|
|
|
PgmUnlock (pSend, *pOldIrq);
|
|
if (pSendContext->pIrpToComplete)
|
|
{
|
|
ASSERT (pSendContext->pIrpToComplete == pSendContext->pIrp);
|
|
PgmIoComplete (pSendContext->pIrpToComplete, STATUS_CANCELLED, 0);
|
|
}
|
|
else
|
|
{
|
|
ASSERT (pSendContext->pIrp);
|
|
}
|
|
|
|
PGM_DEREFERENCE_SESSION_SEND (pSend, REF_SESSION_SEND_IN_WINDOW);
|
|
PgmLock (pSend, *pOldIrq);
|
|
|
|
ExFreeToNPagedLookasideList (&pSend->pSender->SendContextLookaside, pSendContext);
|
|
}
|
|
|
|
//
|
|
// Verify that at least 1 SPM with the FIN or RST or RST_N flag
|
|
// has been sent
|
|
//
|
|
if (!(pSend->pSender->SpmOptions & (PGM_OPTION_FLAG_FIN |
|
|
PGM_OPTION_FLAG_RST |
|
|
PGM_OPTION_FLAG_RST_N)))
|
|
{
|
|
if ((pSend->SessionFlags & PGM_SESSION_SENDS_CANCELLED) ||
|
|
!(pSend->pIrpDisconnect))
|
|
{
|
|
pSend->pSender->SpmOptions &= ~PGM_OPTION_FLAG_FIN;
|
|
pSend->pSender->SpmOptions |= PGM_OPTION_FLAG_RST;
|
|
}
|
|
else
|
|
{
|
|
pSend->pSender->SpmOptions &= ~PGM_OPTION_FLAG_RST;
|
|
pSend->pSender->SpmOptions |= PGM_OPTION_FLAG_FIN;
|
|
}
|
|
|
|
pSend->SessionFlags |= PGM_SESSION_FLAG_SEND_AMBIENT_SPM;
|
|
|
|
PgmTrace (LogAllFuncs, ("CheckForTermination: " \
|
|
"SPM with FIN|RST|RST_N has not yet been sent for pSend=<%p>\n", pSend));
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Verify that there are no SPMs pending
|
|
//
|
|
if (pSend->SessionFlags & PGM_SESSION_FLAG_SEND_AMBIENT_SPM)
|
|
{
|
|
PgmTrace (LogAllFuncs, ("CheckForTermination: " \
|
|
"Cannot cleanup pSend=<%p> since we have Ambient SPM pending!\n", pSend));
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
//
|
|
// Verify that we do not have any completions pending also since
|
|
// Ip would need to reference the data buffer otherwise
|
|
//
|
|
while (!IsListEmpty (&pSend->pSender->CompletedSendsInWindow))
|
|
{
|
|
pSendContext = CONTAINING_RECORD (pSend->pSender->CompletedSendsInWindow.Flink, tCLIENT_SEND_REQUEST, Linkage);
|
|
if (pSendContext->NumSendsPending)
|
|
{
|
|
PgmTrace (LogPath, ("CheckForTermination: " \
|
|
"Session has terminated, but cannot continue cleanup since Sends are still pending!\n"));
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Now, set the buffer settings
|
|
//
|
|
ASSERT (SEQ_GEQ (pSend->pSender->TrailingEdgeSequenceNumber,
|
|
(pSendContext->StartSequenceNumber+1-pSend->FECGroupSize)));
|
|
ASSERT (SEQ_LEQ (pSend->pSender->TrailingEdgeSequenceNumber, pSendContext->EndSequenceNumber));
|
|
|
|
NumSequences = (ULONG) (SEQ_TYPE) (pSendContext->EndSequenceNumber-pSend->pSender->TrailingEdgeSequenceNumber) +1;
|
|
pSend->pSender->BufferPacketsAvailable += NumSequences;
|
|
pSend->pSender->BufferSizeAvailable += (NumSequences * pSend->pSender->PacketBufferSize);
|
|
ASSERT (pSend->pSender->BufferPacketsAvailable <= pSend->pSender->MaxPacketsInBuffer);
|
|
ASSERT (pSend->pSender->BufferSizeAvailable <= pSend->pSender->MaxDataFileSize);
|
|
pSend->pSender->TrailingWindowOffset += (NumSequences * pSend->pSender->PacketBufferSize);
|
|
if (pSend->pSender->TrailingWindowOffset >= pSend->pSender->MaxDataFileSize)
|
|
{
|
|
// Wrap around case!
|
|
pSend->pSender->TrailingWindowOffset -= pSend->pSender->MaxDataFileSize;
|
|
}
|
|
pSend->pSender->TrailingEdgeSequenceNumber += (SEQ_TYPE) NumSequences;
|
|
|
|
ASSERT (pSend->pSender->TrailingWindowOffset < pSend->pSender->MaxDataFileSize);
|
|
ASSERT (SEQ_GT (pSend->pSender->TrailingEdgeSequenceNumber, pSendContext->EndSequenceNumber));
|
|
ASSERT ((!pSendContext->pMessage2Request) && (!pSendContext->pIrp));
|
|
|
|
RemoveEntryList (&pSendContext->Linkage);
|
|
ExFreeToNPagedLookasideList (&pSend->pSender->SendContextLookaside, pSendContext);
|
|
}
|
|
|
|
//
|
|
// If any sends are pending, return False
|
|
//
|
|
if ((pSend->pIrpDisconnect) ||
|
|
!(IsListEmpty (&pSend->pSender->CompletedSendsInWindow)) ||
|
|
!(IsListEmpty (&pSend->pSender->PendingSends)) ||
|
|
!(IsListEmpty (&pSend->pSender->PendingPacketizedSends)) ||
|
|
(AnyRequestPending (pSend->pSender->pRDataInfo)))
|
|
{
|
|
PgmTrace (LogPath, ("CheckForTermination: " \
|
|
"Cannot cleanup completely since transmit Window=[%d--%d] still has pending Sends!\n",
|
|
(ULONG) pSend->pSender->TrailingEdgeSequenceNumber,
|
|
(ULONG) (pSend->pSender->NextODataSequenceNumber-1)));
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
PgmTrace (LogAllFuncs, ("CheckForTermination: " \
|
|
"Transmit Window has no pending Sends! TimerTC=[%I64d]\n", pSend->pSender->TimerTickCount));
|
|
|
|
ASSERT (!pSend->pIrpDisconnect);
|
|
return (TRUE);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOLEAN
|
|
SendNextPacket(
|
|
IN tSEND_SESSION *pSend
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is queued by the timer to send Data/Spm packets
|
|
based on available throughput
|
|
|
|
Arguments:
|
|
|
|
IN pSend -- Pgm session (sender) context
|
|
IN Unused1
|
|
IN Unused2
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
{
|
|
ULONG BytesSent;
|
|
ULONG NumSequences;
|
|
PGMLockHandle OldIrq;
|
|
BOOLEAN fTerminateSession = FALSE;
|
|
LIST_ENTRY *pEntry;
|
|
tSEND_RDATA_CONTEXT *pRDataContext, *pRDataToSend;
|
|
tSEND_RDATA_CONTEXT *pRDataLast = NULL;
|
|
tSEND_CONTEXT *pSender = pSend->pSender;
|
|
BOOLEAN fHighSpeedOptimize = (BOOLEAN) (pSender->pAddress->Flags &
|
|
PGM_ADDRESS_HIGH_SPEED_OPTIMIZED);
|
|
|
|
PgmLock (pSend, OldIrq);
|
|
//
|
|
// pSender->CurrentBytesSendable applies to OData, RData and SPMs only
|
|
//
|
|
while (pSender->CurrentBytesSendable >= pSender->pAddress->OutIfMTU)
|
|
{
|
|
BytesSent = 0;
|
|
|
|
//
|
|
// See if we need to send any Ambient SPMs
|
|
//
|
|
if ((pSend->SessionFlags & PGM_SESSION_FLAG_SEND_AMBIENT_SPM) &&
|
|
((pSender->PacketsSentSinceLastSpm > MAX_DATA_PACKETS_BEFORE_SPM) ||
|
|
(pSender->CurrentSPMTimeout >= pSender->AmbientSPMTimeout)))
|
|
{
|
|
PgmTrace (LogPath, ("SendNextPacket: " \
|
|
"Send Ambient SPM, TC=[%I64d], BS=<%d>\n",
|
|
pSender->TimerTickCount, pSender->CurrentBytesSendable));
|
|
//
|
|
// Some data packet was sent recently, so we are in Ambient SPM mode
|
|
//
|
|
PgmSendSpm (pSend, &OldIrq, &BytesSent);
|
|
|
|
pSender->CurrentSPMTimeout = 0; // Reset the SPM timeout
|
|
pSender->HeartbeatSPMTimeout = pSender->InitialHeartbeatSPMTimeout;
|
|
pSend->SessionFlags &= ~PGM_SESSION_FLAG_SEND_AMBIENT_SPM;
|
|
pSender->PacketsSentSinceLastSpm = 0;
|
|
}
|
|
//
|
|
// Otherwise see if we need to send any Heartbeat SPMs
|
|
//
|
|
else if ((!(pSend->SessionFlags & PGM_SESSION_FLAG_SEND_AMBIENT_SPM)) &&
|
|
(pSender->CurrentSPMTimeout >= pSender->HeartbeatSPMTimeout))
|
|
{
|
|
//
|
|
// No data packet was sent recently, so we need to send a Heartbeat SPM
|
|
//
|
|
PgmTrace (LogPath, ("SendNextPacket: " \
|
|
"Send Heartbeat SPM, TC=[%I64d], BS=<%d>\n",
|
|
pSender->TimerTickCount, pSender->CurrentBytesSendable));
|
|
|
|
//
|
|
// (Send Heartbeat SPM Packet)
|
|
//
|
|
PgmSendSpm (pSend, &OldIrq, &BytesSent);
|
|
|
|
pSender->CurrentSPMTimeout = 0; // Reset the SPM timeout
|
|
pSender->HeartbeatSPMTimeout *= 2;
|
|
if (pSender->HeartbeatSPMTimeout > pSender->MaxHeartbeatSPMTimeout)
|
|
{
|
|
pSender->HeartbeatSPMTimeout = pSender->MaxHeartbeatSPMTimeout;
|
|
}
|
|
pSender->PacketsSentSinceLastSpm = 0;
|
|
}
|
|
//
|
|
// Next, see if we need to send any RData
|
|
//
|
|
else if ((pSender->NumRDataRequestsPending) || (pSender->NumODataRequestsPending))
|
|
{
|
|
//
|
|
// See if we need to send an RData packet now
|
|
//
|
|
if (pRDataToSend = FindFirstEntry (pSend, &pRDataLast, fHighSpeedOptimize))
|
|
{
|
|
PgmTrace (LogPath, ("SendNextPacket: " \
|
|
"Send RData[%d] -- TC=[%I64d], BS=<%d>, MTU=<%d>\n",
|
|
pRDataToSend->RDataSequenceNumber, pSender->TimerTickCount,
|
|
pSender->CurrentBytesSendable, pSender->pAddress->OutIfMTU));
|
|
|
|
PgmSendRData (pSend, pRDataToSend, &OldIrq, &BytesSent);
|
|
}
|
|
else if (pSender->NumODataRequestsPending)
|
|
{
|
|
PgmTrace (LogPath, ("SendNextPacket: " \
|
|
"Send OData -- TC=[%I64d], BS=<%d>, MTU=<%d>\n",
|
|
pSender->TimerTickCount, pSender->CurrentBytesSendable,
|
|
pSender->pAddress->OutIfMTU));
|
|
|
|
//
|
|
// Send OData
|
|
//
|
|
PgmSendNextOData (pSend, &OldIrq, &BytesSent);
|
|
}
|
|
|
|
PgmTrace (LogPath, ("SendNextPacket: " \
|
|
"Sent <%d> Data bytes\n", BytesSent));
|
|
|
|
if (BytesSent == 0)
|
|
{
|
|
//
|
|
// We may not have enough buffer space to packetize and send
|
|
// more data, or we have no data to send at this time, so just
|
|
// break out and see if we can advance the trailing window!
|
|
//
|
|
if (pSender->CurrentBytesSendable >
|
|
(NUM_LEAKY_BUCKETS * pSender->IncrementBytesOnSendTimeout))
|
|
{
|
|
pSender->CurrentBytesSendable = NUM_LEAKY_BUCKETS *
|
|
pSender->IncrementBytesOnSendTimeout;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
pSend->SessionFlags |= PGM_SESSION_FLAG_SEND_AMBIENT_SPM;
|
|
pSender->PacketsSentSinceLastSpm++;
|
|
}
|
|
|
|
//
|
|
// We do not have any more packets to send, so reset
|
|
// BytesSendable so that we don't exceed the rate on
|
|
// the next send
|
|
//
|
|
else
|
|
{
|
|
if (pSender->CurrentBytesSendable >
|
|
(NUM_LEAKY_BUCKETS * pSender->IncrementBytesOnSendTimeout))
|
|
{
|
|
pSender->CurrentBytesSendable = NUM_LEAKY_BUCKETS *
|
|
pSender->IncrementBytesOnSendTimeout;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
pSend->TotalBytes += BytesSent;
|
|
pSender->CurrentBytesSendable -= BytesSent;
|
|
} // while (CurrentBytesSendable >= pSender->pAddress->OutIfMTU)
|
|
|
|
//
|
|
// See if we need to scavenge completed RData requests
|
|
//
|
|
if (!fHighSpeedOptimize)
|
|
{
|
|
RemoveAllEntries (pSend, FALSE);
|
|
}
|
|
|
|
//
|
|
// See if we need to increment the Trailing Window -- returns number of Sequences advanced
|
|
//
|
|
NumSequences = AdvanceWindow (pSend);
|
|
|
|
//
|
|
// Now check if we need to terminate this session
|
|
//
|
|
fTerminateSession = CheckForTermination (pSend, &OldIrq);
|
|
|
|
PgmTrace (LogAllFuncs, ("SendNextPacket: " \
|
|
"Sent <%I64d> total bytes, fTerminateSession=<%x>\n", pSend->TotalBytes, fTerminateSession));
|
|
|
|
//
|
|
// Clear the WorkerRunning flag so that the next Worker
|
|
// routine can be queued
|
|
//
|
|
pSend->SessionFlags &= ~PGM_SESSION_FLAG_WORKER_RUNNING;
|
|
PgmUnlock (pSend, OldIrq);
|
|
|
|
return (fTerminateSession);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
SendSessionTimeout(
|
|
IN PKDPC Dpc,
|
|
IN PVOID DeferredContext,
|
|
IN PVOID SystemArg1,
|
|
IN PVOID SystemArg2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the timout that gets called every BASIC_TIMER_GRANULARITY_IN_MSECS
|
|
to schedule the next Send request
|
|
|
|
Arguments:
|
|
|
|
IN Dpc
|
|
IN DeferredContext -- Our context for this timer
|
|
IN SystemArg1
|
|
IN SystemArg2
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
PGMLockHandle OldIrq;
|
|
LARGE_INTEGER Now;
|
|
LARGE_INTEGER DeltaTime, GranularTimeElapsed, TimeoutGranularity;
|
|
ULONG NumTimeouts;
|
|
SEQ_TYPE NumSequencesInWindow;
|
|
ULONGLONG LastRDataPercentage;
|
|
tSEND_SESSION *pSend = (tSEND_SESSION *) DeferredContext;
|
|
tSEND_CONTEXT *pSender = pSend->pSender;
|
|
tADDRESS_CONTEXT *pAddress = pSender->pAddress;
|
|
|
|
Now.QuadPart = KeQueryInterruptTime ();
|
|
|
|
PgmLock (pSend, OldIrq);
|
|
|
|
//
|
|
// First check if we have been told to stop the timer
|
|
//
|
|
if (pSend->SessionFlags & PGM_SESSION_FLAG_STOP_TIMER)
|
|
{
|
|
PgmTrace (LogStatus, ("SendSessionTimeout: " \
|
|
"Session has terminated -- will deref and not restart timer!\n"));
|
|
|
|
//
|
|
// Deref for the timer reference
|
|
//
|
|
pSender->pAddress = NULL;
|
|
PgmUnlock (pSend, OldIrq);
|
|
PGM_DEREFERENCE_SESSION_SEND (pSend, REF_SESSION_TIMER_RUNNING);
|
|
PGM_DEREFERENCE_ADDRESS (pAddress, REF_ADDRESS_SEND_IN_PROGRESS);
|
|
return;
|
|
}
|
|
|
|
DeltaTime.QuadPart = Now.QuadPart - pSender->LastTimeout.QuadPart;
|
|
TimeoutGranularity.QuadPart = pSender->TimeoutGranularity.QuadPart;
|
|
for (GranularTimeElapsed.QuadPart = 0, NumTimeouts = 0;
|
|
DeltaTime.QuadPart > TimeoutGranularity.QuadPart;
|
|
NumTimeouts++)
|
|
{
|
|
GranularTimeElapsed.QuadPart += TimeoutGranularity.QuadPart;
|
|
DeltaTime.QuadPart -= TimeoutGranularity.QuadPart;
|
|
}
|
|
|
|
if (NumTimeouts)
|
|
{
|
|
pSend->RateCalcTimeout += NumTimeouts;
|
|
if (pSend->RateCalcTimeout >=
|
|
(INTERNAL_RATE_CALCULATION_FREQUENCY/BASIC_TIMER_GRANULARITY_IN_MSECS))
|
|
{
|
|
pSend->RateKBitsPerSecOverall = (pSend->TotalBytes << LOG2_BITS_PER_BYTE) /
|
|
(pSender->TimerTickCount * BASIC_TIMER_GRANULARITY_IN_MSECS);
|
|
|
|
pSend->RateKBitsPerSecLast = (pSend->TotalBytes - pSend->TotalBytesAtLastInterval) >>
|
|
(LOG2_INTERNAL_RATE_CALCULATION_FREQUENCY - LOG2_BITS_PER_BYTE);
|
|
|
|
pSend->DataBytesAtLastInterval = pSend->DataBytes;
|
|
pSend->TotalBytesAtLastInterval = pSend->TotalBytes;
|
|
pSend->RateCalcTimeout = 0;
|
|
|
|
LastRDataPercentage = 0;
|
|
if (pSender->RDataPacketsInLastInterval)
|
|
{
|
|
LastRDataPercentage = (100*pSender->RDataPacketsInLastInterval) /
|
|
(pSender->RDataPacketsInLastInterval +
|
|
pSender->ODataPacketsInLastInterval);
|
|
|
|
PgmTrace (LogPath, ("SendSessionTimeout: " \
|
|
"Sent %d RData + %d OData, %% = %d -- Overall RData %% = %d\n",
|
|
(ULONG) pSender->RDataPacketsInLastInterval,
|
|
(ULONG) pSender->ODataPacketsInLastInterval, (ULONG) LastRDataPercentage,
|
|
(ULONG) ((100*pSender->TotalRDataPacketsSent)/
|
|
(pSender->TotalRDataPacketsSent+pSender->TotalODataPacketsSent))));
|
|
}
|
|
else
|
|
{
|
|
PgmTrace (LogPath, ("SendSessionTimeout: " \
|
|
"No RData, %d OData packets in last interval\n",
|
|
(ULONG) pSender->ODataPacketsInLastInterval));
|
|
}
|
|
|
|
if (pAddress->Flags & PGM_ADDRESS_HIGH_SPEED_OPTIMIZED)
|
|
{
|
|
if (LastRDataPercentage > MIN_PREFERRED_REPAIR_PERCENTAGE)
|
|
{
|
|
if (pSender->IncrementBytesOnSendTimeout > pSender->DeltaIncrementBytes)
|
|
{
|
|
PgmTrace (LogStatus, ("SendSessionTimeout: " \
|
|
"\tIncBytes = <%d> - <%d>\n",
|
|
(ULONG) pSender->IncrementBytesOnSendTimeout, (ULONG) pSender->DeltaIncrementBytes));
|
|
pSender->IncrementBytesOnSendTimeout -= pSender->DeltaIncrementBytes;
|
|
}
|
|
}
|
|
else if (pSender->IncrementBytesOnSendTimeout < pSender->OriginalIncrementBytes)
|
|
{
|
|
PgmTrace (LogStatus, ("SendSessionTimeout: " \
|
|
"\tIncBytes = <%d> + <%d>\n",
|
|
(ULONG) pSender->IncrementBytesOnSendTimeout, (ULONG) pSender->DeltaIncrementBytes));
|
|
pSender->IncrementBytesOnSendTimeout += pSender->DeltaIncrementBytes;
|
|
ASSERT (pSender->IncrementBytesOnSendTimeout <=
|
|
pSender->OriginalIncrementBytes);
|
|
}
|
|
}
|
|
|
|
pSender->RDataPacketsInLastInterval = 0;
|
|
pSender->ODataPacketsInLastInterval = 0;
|
|
}
|
|
|
|
pSender->LastTimeout.QuadPart += GranularTimeElapsed.QuadPart;
|
|
|
|
//
|
|
// Increment the absolute timer, and check for overflow
|
|
//
|
|
pSender->TimerTickCount += NumTimeouts;
|
|
|
|
//
|
|
// If the SPMTimeout value is less than the HeartbeatTimeout, increment it
|
|
//
|
|
if (pSender->CurrentSPMTimeout <= pSender->HeartbeatSPMTimeout)
|
|
{
|
|
pSender->CurrentSPMTimeout += NumTimeouts;
|
|
}
|
|
|
|
//
|
|
// See if we can send anything
|
|
//
|
|
ASSERT (pSender->CurrentTimeoutCount);
|
|
ASSERT (pSender->SendTimeoutCount);
|
|
if (pSender->CurrentTimeoutCount > NumTimeouts)
|
|
{
|
|
pSender->CurrentTimeoutCount -= NumTimeouts;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We got here because NumTimeouts >= pSender->CurrentTimeoutCount
|
|
//
|
|
pSender->CurrentBytesSendable += (ULONG) pSender->IncrementBytesOnSendTimeout;
|
|
if (NumTimeouts != pSender->CurrentTimeoutCount)
|
|
{
|
|
if (1 == pSender->SendTimeoutCount)
|
|
{
|
|
pSender->CurrentBytesSendable += (ULONG) ((NumTimeouts - pSender->CurrentTimeoutCount)
|
|
* pSender->IncrementBytesOnSendTimeout);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This path will get taken on a slow receiver when the timer
|
|
// fires at a lower granularity than that requested
|
|
//
|
|
pSender->CurrentBytesSendable += (ULONG) (((NumTimeouts - pSender->CurrentTimeoutCount)
|
|
* pSender->IncrementBytesOnSendTimeout) /
|
|
pSender->SendTimeoutCount);
|
|
}
|
|
}
|
|
pSender->CurrentTimeoutCount = pSender->SendTimeoutCount;
|
|
|
|
//
|
|
// Send a synchronization event to the sender thread to
|
|
// send the next available data
|
|
//
|
|
KeSetEvent (&pSender->SendEvent, 0, FALSE);
|
|
}
|
|
}
|
|
|
|
PgmUnlock (pSend, OldIrq);
|
|
|
|
//
|
|
// Now, restart the timer
|
|
//
|
|
PgmInitTimer (&pSend->SessionTimer);
|
|
PgmStartTimer (&pSend->SessionTimer, BASIC_TIMER_GRANULARITY_IN_MSECS, SendSessionTimeout, pSend);
|
|
|
|
PgmTrace (LogAllFuncs, ("SendSessionTimeout: " \
|
|
"TickCount=<%I64d>, CurrentTimeoutCount=<%I64d>, CurrentSPMTimeout=<%I64d>, Worker %srunning\n",
|
|
pSender->TimerTickCount, pSender->CurrentTimeoutCount,
|
|
pSender->CurrentSPMTimeout,
|
|
((pSend->SessionFlags & PGM_SESSION_FLAG_WORKER_RUNNING) ? "" : "NOT ")));
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
SenderWorkerThread(
|
|
IN tSEND_SESSION *pSend
|
|
)
|
|
{
|
|
BOOLEAN fTerminateSends;
|
|
PGMLockHandle OldIrq;
|
|
NTSTATUS status;
|
|
|
|
do
|
|
{
|
|
status = KeWaitForSingleObject (&pSend->pSender->SendEvent, // Object to wait on.
|
|
Executive, // Reason for waiting
|
|
KernelMode, // Processor mode
|
|
FALSE, // Alertable
|
|
NULL); // Timeout
|
|
ASSERT (NT_SUCCESS (status));
|
|
|
|
fTerminateSends = SendNextPacket (pSend);
|
|
}
|
|
while (!fTerminateSends);
|
|
|
|
PgmLock (pSend, OldIrq);
|
|
pSend->SessionFlags |= PGM_SESSION_FLAG_STOP_TIMER; // To ensure timer does last deref and stops
|
|
PgmUnlock (pSend, OldIrq);
|
|
|
|
// PsTerminateSystemThread (STATUS_SUCCESS);
|
|
return;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
VOID
|
|
PgmCancelSendIrp(
|
|
IN PDEVICE_OBJECT DeviceContext,
|
|
IN PIRP pIrp
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine handles the cancelling of a Send Irp. It must release the
|
|
cancel spin lock before returning re: IoCancelIrp().
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation (pIrp);
|
|
tSEND_SESSION *pSend = (tSEND_SESSION *) pIrpSp->FileObject->FsContext;
|
|
PGMLockHandle OldIrq;
|
|
PLIST_ENTRY pEntry;
|
|
LIST_ENTRY ListEntry;
|
|
tCLIENT_SEND_REQUEST *pSendContext1;
|
|
tCLIENT_SEND_REQUEST *pSendContext2 = NULL;
|
|
ULONG NumRequests;
|
|
|
|
if (!PGM_VERIFY_HANDLE (pSend, PGM_VERIFY_SESSION_SEND))
|
|
{
|
|
IoReleaseCancelSpinLock (pIrp->CancelIrql);
|
|
|
|
PgmTrace (LogError, ("PgmCancelSendIrp: ERROR -- " \
|
|
"pIrp=<%p> pSend=<%p>, pAddress=<%p>\n",
|
|
pIrp, pSend, (pSend ? pSend->pAssociatedAddress : NULL)));
|
|
return;
|
|
}
|
|
|
|
PgmLock (pSend, OldIrq);
|
|
|
|
//
|
|
// First, see if the Irp is on any of our lists
|
|
//
|
|
pEntry = &pSend->pSender->PendingSends;
|
|
while ((pEntry = pEntry->Flink) != &pSend->pSender->PendingSends)
|
|
{
|
|
pSendContext1 = CONTAINING_RECORD (pEntry, tCLIENT_SEND_REQUEST, Linkage);
|
|
if (pSendContext1->pIrp == pIrp)
|
|
{
|
|
pSendContext2 = pSendContext1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!pSendContext2)
|
|
{
|
|
//
|
|
// Now, search the packetized list
|
|
//
|
|
pEntry = &pSend->pSender->PendingPacketizedSends;
|
|
while ((pEntry = pEntry->Flink) != &pSend->pSender->PendingPacketizedSends)
|
|
{
|
|
pSendContext1 = CONTAINING_RECORD (pEntry, tCLIENT_SEND_REQUEST, Linkage);
|
|
if (pSendContext1->pIrp == pIrp)
|
|
{
|
|
pSendContext2 = pSendContext1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!pSendContext2)
|
|
{
|
|
//
|
|
// We did not find the irp -- either it was just being completed
|
|
// (or waiting for a send-complete), or the Irp was bad ?
|
|
//
|
|
PgmUnlock (pSend, OldIrq);
|
|
IoReleaseCancelSpinLock (pIrp->CancelIrql);
|
|
|
|
PgmTrace (LogPath, ("PgmCancelSendIrp: " \
|
|
"Did not find Cancel Irp=<%p>\n", pIrp));
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
InitializeListHead (&ListEntry);
|
|
PgmCancelAllSends (pSend, &ListEntry, pIrp);
|
|
|
|
PgmUnlock (pSend, OldIrq);
|
|
IoReleaseCancelSpinLock (pIrp->CancelIrql);
|
|
|
|
//
|
|
// Now, complete all the sends which we removed
|
|
//
|
|
NumRequests = 0;
|
|
while (!IsListEmpty (&ListEntry))
|
|
{
|
|
pEntry = RemoveHeadList (&ListEntry);
|
|
pSendContext1 = CONTAINING_RECORD (pEntry, tCLIENT_SEND_REQUEST, Linkage);
|
|
ASSERT (!pSendContext1->pMessage2Request);
|
|
|
|
if (pSendContext1->pIrpToComplete)
|
|
{
|
|
NumRequests++;
|
|
ASSERT (pSendContext1->pIrpToComplete == pSendContext1->pIrp);
|
|
PgmIoComplete (pSendContext1->pIrpToComplete, STATUS_CANCELLED, 0);
|
|
}
|
|
else
|
|
{
|
|
ASSERT (pSendContext1->pIrp);
|
|
}
|
|
|
|
PGM_DEREFERENCE_SESSION_SEND (pSend, REF_SESSION_SEND_IN_WINDOW);
|
|
ExFreeToNPagedLookasideList (&pSend->pSender->SendContextLookaside, pSendContext1);
|
|
}
|
|
|
|
PgmTrace (LogPath, ("PgmCancelSendIrp: " \
|
|
"Cancelled <%d> Irps for pIrp=<%p>\n", NumRequests, pIrp));
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
NTSTATUS
|
|
PgmSendRequestFromClient(
|
|
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 Send 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 OldIrq1, OldIrq2, OldIrq3, OldIrq4;
|
|
tADDRESS_CONTEXT *pAddress = NULL;
|
|
tCLIENT_SEND_REQUEST *pSendContext1;
|
|
tCLIENT_SEND_REQUEST *pSendContext2 = NULL;
|
|
ULONG BytesLeftInMessage;
|
|
tSEND_SESSION *pSend = (tSEND_SESSION *) pIrpSp->FileObject->FsContext;
|
|
tSEND_CONTEXT *pSender;
|
|
PTDI_REQUEST_KERNEL_SEND pTdiRequest = (PTDI_REQUEST_KERNEL_SEND) &pIrpSp->Parameters;
|
|
KAPC_STATE ApcState;
|
|
BOOLEAN fFirstSend, fResourceAcquired, fAttached;
|
|
LARGE_INTEGER Frequency;
|
|
LIST_ENTRY ListEntry;
|
|
|
|
PgmLock (&PgmDynamicConfig, OldIrq1);
|
|
|
|
//
|
|
// Verify that the connection is valid and is associated with an address
|
|
//
|
|
if ((!PGM_VERIFY_HANDLE (pSend, PGM_VERIFY_SESSION_SEND)) ||
|
|
(!(pAddress = pSend->pAssociatedAddress)) ||
|
|
(!pSend->pSender->SendTimeoutCount) || // Verify PgmConnect has run!
|
|
(!PGM_VERIFY_HANDLE (pAddress, PGM_VERIFY_ADDRESS)) ||
|
|
(pSend->SessionFlags & (PGM_SESSION_CLIENT_DISCONNECTED | PGM_SESSION_SENDS_CANCELLED)) ||
|
|
(pAddress->Flags & PGM_ADDRESS_FLAG_INVALID_OUT_IF))
|
|
{
|
|
PgmTrace (LogError, ("PgmSendRequestFromClient: ERROR -- " \
|
|
"Invalid Handles pSend=<%p>, pAddress=<%p>\n", pSend, pAddress));
|
|
|
|
PgmUnlock (&PgmDynamicConfig, OldIrq1);
|
|
return (STATUS_INVALID_HANDLE);
|
|
}
|
|
|
|
pSender = pSend->pSender;
|
|
if (!pSender->DestMCastIpAddress)
|
|
{
|
|
PgmTrace (LogError, ("PgmSendRequestFromClient: ERROR -- " \
|
|
"Destination address not specified for pSend=<%p>\n", pSend));
|
|
|
|
PgmUnlock (&PgmDynamicConfig, OldIrq1);
|
|
return (STATUS_INVALID_ADDRESS_COMPONENT);
|
|
}
|
|
|
|
if (!pTdiRequest->SendLength)
|
|
{
|
|
PgmTrace (LogStatus, ("PgmSendRequestFromClient: " \
|
|
"pIrp=<%p> for pSend=<%p> is of length 0!\n", pIrp, pSend));
|
|
|
|
PgmUnlock (&PgmDynamicConfig, OldIrq1);
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
PgmLock (pAddress, OldIrq2);
|
|
PgmLock (pSend, OldIrq3);
|
|
|
|
if (!(pSendContext1 = ExAllocateFromNPagedLookasideList (&pSender->SendContextLookaside)))
|
|
{
|
|
PgmTrace (LogError, ("PgmSendRequestFromClient: ERROR -- " \
|
|
"STATUS_INSUFFICIENT_RESOURCES allocating pSendContext1\n"));
|
|
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
// If we have more that 1 message data in this request,
|
|
// we will need another send context
|
|
//
|
|
if ((pSender->ThisSendMessageLength) && // Client has specified current message length
|
|
(BytesLeftInMessage = pSender->ThisSendMessageLength - pSender->BytesSent) &&
|
|
(BytesLeftInMessage < pTdiRequest->SendLength) && // ==> Have some extra data in this request
|
|
(!(pSendContext2 = ExAllocateFromNPagedLookasideList (&pSender->SendContextLookaside))))
|
|
{
|
|
ExFreeToNPagedLookasideList (&pSender->SendContextLookaside, pSendContext1);
|
|
PgmTrace (LogError, ("PgmSendRequestFromClient: ERROR -- " \
|
|
"STATUS_INSUFFICIENT_RESOURCES allocating pSendContext1\n"));
|
|
|
|
return (STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
//
|
|
//
|
|
// Zero the SendDataContext structure(s)
|
|
//
|
|
PgmZeroMemory (pSendContext1, sizeof (tCLIENT_SEND_REQUEST));
|
|
InitializeListHead (&pSendContext1->Linkage);
|
|
if (pSendContext2)
|
|
{
|
|
PgmZeroMemory (pSendContext2, sizeof (tCLIENT_SEND_REQUEST));
|
|
InitializeListHead (&pSendContext2->Linkage);
|
|
}
|
|
|
|
if (pSend->SessionFlags & PGM_SESSION_FLAG_FIRST_PACKET)
|
|
{
|
|
fFirstSend = TRUE;
|
|
}
|
|
else
|
|
{
|
|
fFirstSend = FALSE;
|
|
}
|
|
|
|
//
|
|
// Reference the Address and Connection so that they cannot go away
|
|
// while we are processing!
|
|
//
|
|
PGM_REFERENCE_SESSION_SEND (pSend, REF_SESSION_SEND_IN_WINDOW, TRUE);
|
|
|
|
PgmUnlock (pSend, OldIrq3);
|
|
PgmUnlock (pAddress, OldIrq2);
|
|
PgmUnlock (&PgmDynamicConfig, OldIrq1);
|
|
|
|
if (PgmGetCurrentIrql())
|
|
{
|
|
fResourceAcquired = FALSE;
|
|
}
|
|
else
|
|
{
|
|
fResourceAcquired = TRUE;
|
|
PgmAcquireResourceExclusive (&pSender->Resource, TRUE);
|
|
}
|
|
|
|
if (fFirstSend)
|
|
{
|
|
//
|
|
// Don't start the timer yet, but start the sender thread
|
|
//
|
|
PgmAttachToProcessForVMAccess (pSend, &ApcState, &fAttached, REF_PROCESS_ATTACH_START_SENDER_THREAD);
|
|
|
|
status = PsCreateSystemThread (&pSender->SendHandle,
|
|
PROCESS_ALL_ACCESS,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
SenderWorkerThread,
|
|
pSend);
|
|
|
|
if (!NT_SUCCESS (status))
|
|
{
|
|
PgmDetachProcess (&ApcState, &fAttached, REF_PROCESS_ATTACH_START_SENDER_THREAD);
|
|
if (fResourceAcquired)
|
|
{
|
|
PgmReleaseResource (&pSender->Resource);
|
|
}
|
|
|
|
ExFreeToNPagedLookasideList (&pSender->SendContextLookaside, pSendContext1);
|
|
if (pSendContext2)
|
|
{
|
|
ExFreeToNPagedLookasideList (&pSender->SendContextLookaside, pSendContext2);
|
|
}
|
|
PGM_DEREFERENCE_SESSION_SEND (pSend, REF_SESSION_SEND_IN_WINDOW);
|
|
|
|
PgmTrace (LogError, ("PgmSendRequestFromClient: ERROR -- " \
|
|
"status=<%x> starting sender thread\n", status));
|
|
|
|
return (status);
|
|
}
|
|
|
|
//
|
|
// Close the handle to the thread so that it goes away when the
|
|
// thread terminates
|
|
//
|
|
ZwClose (pSender->SendHandle);
|
|
PgmDetachProcess (&ApcState, &fAttached, REF_PROCESS_ATTACH_START_SENDER_THREAD);
|
|
|
|
PgmLock (&PgmDynamicConfig, OldIrq1);
|
|
IoAcquireCancelSpinLock (&OldIrq2);
|
|
PgmLock (pAddress, OldIrq3);
|
|
PgmLock (pSend, OldIrq4);
|
|
|
|
pSend->SessionFlags &= ~PGM_SESSION_FLAG_FIRST_PACKET;
|
|
pSender->pAddress = pAddress;
|
|
pSender->LastODataSentSequenceNumber = -1;
|
|
|
|
//
|
|
// Set the SYN flag for the first packet
|
|
//
|
|
pSendContext1->DataOptions |= PGM_OPTION_FLAG_SYN; // First packet only
|
|
pSendContext1->DataOptionsLength += PGM_PACKET_OPT_SYN_LENGTH;
|
|
|
|
PGM_REFERENCE_SESSION_SEND (pSend, REF_SESSION_TIMER_RUNNING, TRUE);
|
|
PGM_REFERENCE_ADDRESS (pAddress, REF_ADDRESS_SEND_IN_PROGRESS, TRUE);
|
|
|
|
//
|
|
// Now, set and start the timer
|
|
//
|
|
pSender->LastTimeout.QuadPart = KeQueryInterruptTime ();
|
|
pSender->TimeoutGranularity.QuadPart = BASIC_TIMER_GRANULARITY_IN_MSECS * 10000; // 100 ns units
|
|
pSender->TimerTickCount = 1;
|
|
PgmInitTimer (&pSend->SessionTimer);
|
|
PgmStartTimer (&pSend->SessionTimer, BASIC_TIMER_GRANULARITY_IN_MSECS, SendSessionTimeout, pSend);
|
|
}
|
|
else
|
|
{
|
|
PgmLock (&PgmDynamicConfig, OldIrq1);
|
|
IoAcquireCancelSpinLock (&OldIrq2);
|
|
PgmLock (pAddress, OldIrq3);
|
|
PgmLock (pSend, OldIrq4);
|
|
}
|
|
|
|
pSendContext1->pSend = pSend;
|
|
pSendContext1->pIrp = pIrp;
|
|
pSendContext1->pIrpToComplete = pIrp;
|
|
pSendContext1->NextDataOffsetInMdl = 0;
|
|
pSendContext1->SendNumber = pSender->NextSendNumber++;
|
|
pSendContext1->DataPayloadSize = pSender->MaxPayloadSize;
|
|
pSendContext1->DataOptions |= pSender->DataOptions; // Attach options common for every send
|
|
pSendContext1->DataOptionsLength += pSender->DataOptionsLength;
|
|
pSendContext1->pLastMessageVariableTGPacket = (PVOID) -1; // FEC-specific
|
|
|
|
if (pSender->ThisSendMessageLength)
|
|
{
|
|
PgmTrace (LogPath, ("PgmSendRequestFromClient: " \
|
|
"Send # [%d]: MessageLength=<%d>, BytesSent=<%d>, BytesInSend=<%d>\n",
|
|
pSendContext1->SendNumber, pSender->ThisSendMessageLength,
|
|
pSender->BytesSent, pTdiRequest->SendLength));
|
|
|
|
pSendContext1->ThisMessageLength = pSender->ThisSendMessageLength;
|
|
pSendContext1->LastMessageOffset = pSender->BytesSent;
|
|
if (pSendContext2)
|
|
{
|
|
//
|
|
// First, set the parameters for SendDataContext1
|
|
//
|
|
pSendContext1->BytesInSend = BytesLeftInMessage;
|
|
pSendContext1->pIrpToComplete = NULL; // This Irp will be completed by the Context2
|
|
|
|
//
|
|
// Now, set the parameters for SendDataContext1
|
|
//
|
|
pSendContext2->pSend = pSend;
|
|
pSendContext2->pIrp = pIrp;
|
|
pSendContext2->pIrpToComplete = pIrp;
|
|
pSendContext2->SendNumber = pSender->NextSendNumber++;
|
|
pSendContext2->DataPayloadSize = pSender->MaxPayloadSize;
|
|
pSendContext2->DataOptions |= pSender->DataOptions; // Attach options common for every send
|
|
pSendContext2->DataOptionsLength += pSender->DataOptionsLength;
|
|
pSendContext2->pLastMessageVariableTGPacket = (PVOID) -1; // FEC-specific
|
|
|
|
pSendContext2->ThisMessageLength = pTdiRequest->SendLength - BytesLeftInMessage;
|
|
pSendContext2->BytesInSend = pSendContext2->ThisMessageLength;
|
|
pSendContext2->NextDataOffsetInMdl = BytesLeftInMessage;
|
|
}
|
|
else
|
|
{
|
|
pSendContext1->BytesInSend = pTdiRequest->SendLength;
|
|
}
|
|
|
|
pSender->BytesSent += pSendContext1->BytesInSend;
|
|
if (pSender->BytesSent == pSender->ThisSendMessageLength)
|
|
{
|
|
pSender->BytesSent = pSender->ThisSendMessageLength = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pSendContext1->ThisMessageLength = pTdiRequest->SendLength;
|
|
pSendContext1->BytesInSend = pTdiRequest->SendLength;
|
|
}
|
|
|
|
// If the total Message length exceeds that of PayloadSize/Packet, then we need to fragment
|
|
if ((pSendContext1->ThisMessageLength > pSendContext1->DataPayloadSize) ||
|
|
(pSendContext1->ThisMessageLength > pSendContext1->BytesInSend))
|
|
{
|
|
pSendContext1->DataOptions |= PGM_OPTION_FLAG_FRAGMENT;
|
|
pSendContext1->DataOptionsLength += PGM_PACKET_OPT_FRAGMENT_LENGTH;
|
|
|
|
pSendContext1->NumPacketsRemaining = (pSendContext1->BytesInSend +
|
|
(pSender->MaxPayloadSize - 1)) /
|
|
pSender->MaxPayloadSize;
|
|
ASSERT (pSendContext1->NumPacketsRemaining >= 1);
|
|
}
|
|
else
|
|
{
|
|
pSendContext1->NumPacketsRemaining = 1;
|
|
}
|
|
pSender->NumPacketsRemaining += pSendContext1->NumPacketsRemaining;
|
|
|
|
// Adjust the OptionsLength for the Packet Extension and determine
|
|
if (pSendContext1->DataOptions)
|
|
{
|
|
pSendContext1->DataOptionsLength += PGM_PACKET_EXTENSION_LENGTH;
|
|
}
|
|
|
|
pSendContext1->BytesLeftToPacketize = pSendContext1->BytesInSend;
|
|
InsertTailList (&pSender->PendingSends, &pSendContext1->Linkage);
|
|
pSender->NumODataRequestsPending++;
|
|
|
|
//
|
|
// Do the same for Context2, if applicable
|
|
if (pSendContext2)
|
|
{
|
|
//
|
|
// Interlink the 2 Send requests
|
|
//
|
|
pSendContext2->pMessage2Request = pSendContext1;
|
|
pSendContext1->pMessage2Request = pSendContext2;
|
|
|
|
PGM_REFERENCE_SESSION_SEND (pSend, REF_SESSION_SEND_IN_WINDOW, TRUE);
|
|
|
|
if (pSendContext2->ThisMessageLength > pSendContext1->DataPayloadSize)
|
|
{
|
|
pSendContext2->DataOptions |= PGM_OPTION_FLAG_FRAGMENT;
|
|
pSendContext2->DataOptionsLength += PGM_PACKET_OPT_FRAGMENT_LENGTH;
|
|
|
|
pSendContext2->NumPacketsRemaining = (pSendContext2->BytesInSend +
|
|
(pSender->MaxPayloadSize - 1)) /
|
|
pSender->MaxPayloadSize;
|
|
ASSERT (pSendContext2->NumPacketsRemaining >= 1);
|
|
}
|
|
else
|
|
{
|
|
pSendContext2->NumPacketsRemaining = 1;
|
|
}
|
|
pSender->NumPacketsRemaining += pSendContext2->NumPacketsRemaining;
|
|
|
|
// Adjust the OptionsLength for the Packet Extension and determine
|
|
if (pSendContext2->DataOptions)
|
|
{
|
|
pSendContext2->DataOptionsLength += PGM_PACKET_EXTENSION_LENGTH;
|
|
}
|
|
|
|
pSendContext2->BytesLeftToPacketize = pSendContext2->BytesInSend;
|
|
InsertTailList (&pSender->PendingSends, &pSendContext2->Linkage);
|
|
pSender->NumODataRequestsPending++;
|
|
}
|
|
|
|
if (!NT_SUCCESS (PgmCheckSetCancelRoutine (pIrp, PgmCancelSendIrp, TRUE)))
|
|
{
|
|
pSend->SessionFlags |= PGM_SESSION_SENDS_CANCELLED;
|
|
|
|
pSender->NumODataRequestsPending--;
|
|
pSender->NumPacketsRemaining -= pSendContext1->NumPacketsRemaining;
|
|
RemoveEntryList (&pSendContext1->Linkage);
|
|
ExFreeToNPagedLookasideList (&pSender->SendContextLookaside, pSendContext1);
|
|
|
|
if (pSendContext2)
|
|
{
|
|
pSender->NumODataRequestsPending--;
|
|
pSender->NumPacketsRemaining -= pSendContext2->NumPacketsRemaining;
|
|
RemoveEntryList (&pSendContext2->Linkage);
|
|
ExFreeToNPagedLookasideList (&pSender->SendContextLookaside, pSendContext2);
|
|
}
|
|
|
|
PgmUnlock (pSend, OldIrq4);
|
|
PgmUnlock (pAddress, OldIrq3);
|
|
IoReleaseCancelSpinLock (OldIrq2);
|
|
PgmUnlock (&PgmDynamicConfig, OldIrq1);
|
|
|
|
PGM_DEREFERENCE_SESSION_SEND (pSend, REF_SESSION_SEND_IN_WINDOW);
|
|
if (pSendContext2)
|
|
{
|
|
PGM_DEREFERENCE_SESSION_SEND (pSend, REF_SESSION_SEND_IN_WINDOW);
|
|
}
|
|
|
|
PgmTrace (LogError, ("PgmSendRequestFromClient: ERROR -- " \
|
|
"Could not set Cancel routine on Send Irp=<%p>, pSend=<%p>, pAddress=<%p>\n",
|
|
pIrp, pSend, pAddress));
|
|
|
|
return (STATUS_CANCELLED);
|
|
}
|
|
|
|
IoReleaseCancelSpinLock (OldIrq4);
|
|
|
|
PgmUnlock (pAddress, OldIrq3);
|
|
PgmUnlock (&PgmDynamicConfig, OldIrq2);
|
|
|
|
if (fResourceAcquired)
|
|
{
|
|
// PgmPrepareNextSend (pSend, &OldIrq1, TRUE, TRUE);
|
|
}
|
|
|
|
if (pSender->CurrentBytesSendable >= pAddress->OutIfMTU)
|
|
{
|
|
//
|
|
// Send a synchronization event to the sender thread to
|
|
// send the next available data
|
|
//
|
|
KeSetEvent (&pSender->SendEvent, 0, FALSE);
|
|
}
|
|
|
|
PgmUnlock (pSend, OldIrq1);
|
|
|
|
if (fResourceAcquired)
|
|
{
|
|
PgmReleaseResource (&pSender->Resource);
|
|
}
|
|
|
|
PgmTrace (LogPath, ("PgmSendRequestFromClient: " \
|
|
"[%d] Send pending for pIrp=<%p>, pSendContext=<%p -- %p>\n",
|
|
pSendContext1->SendNumber, pIrp, pSendContext1, pSendContext2));
|
|
|
|
return (STATUS_PENDING);
|
|
}
|
|
|
|
|