windows-nt/Source/XPSP1/NT/net/qos/psched/sys/send.c

1613 lines
55 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1996-1999 Microsoft Corporation
Module Name:
send.c
Abstract:
routines for sending packets
Author:
Charlie Wickham (charlwi) 07-May-1996
Yoram Bernet (yoramb)
Rajesh Sundaram (rajeshsu) 01-Aug-1998.
Environment:
Kernel Mode
Revision History:
--*/
#include "psched.h"
#pragma hdrstop
/* External */
/* Static */
/* Forwad */
#define SEND_PACKET_VIA_SCHEDULER(_pktcontext, _vc, _adapter, _ourpacket) \
{ \
PsAssert((_pktcontext)->Vc != 0); \
(_vc)->Stats.PacketsScheduled++; \
(_vc)->Stats.BytesScheduled.QuadPart += (_pktcontext)->Info.PacketLength; \
if(!(*(_vc)->PsComponent->SubmitPacket)( \
(_vc)->PsPipeContext, \
(_vc)->PsFlowContext, \
(_pktcontext)->Info.ClassMapContext, \
&(_pktcontext)->Info)) { \
\
DropPacket((_adapter), (_vc), (_ourpacket), NDIS_STATUS_FAILURE); \
} \
return NDIS_STATUS_PENDING; \
}
#define FILL_PKT_FOR_NIC(OPacket, UserC) \
{ \
NDIS_PACKET_8021Q_INFO VlanPriInfo; \
\
VlanPriInfo.Value = NDIS_PER_PACKET_INFO_FROM_PACKET(OPacket, Ieee8021QInfo);\
VlanPriInfo.TagHeader.UserPriority = (UserC); \
NDIS_PER_PACKET_INFO_FROM_PACKET(OPacket, Ieee8021QInfo) = VlanPriInfo.Value;\
}
#define FILL_PKT_FOR_SCHED(Adapter, PktContext, Vc, OPacket, TOSNC, UserC, UserNC, \
_IPHdr) \
{ \
ULONG _PacketLength; \
FILL_PKT_FOR_NIC(OPacket, UserC); \
NdisQueryPacket((OPacket), NULL, NULL, NULL, &(_PacketLength)); \
(PktContext)->Info.PacketLength = (_PacketLength) - (Adapter)->HeaderSize; \
(PktContext)->Info.ConformanceTime.QuadPart = 0; \
(PktContext)->Info.ClassMapContext = 0; \
(PktContext)->Info.UserPriorityNonConforming = (UserNC); \
(PktContext)->Info.TOSNonConforming = (TOSNC); \
(PktContext)->Info.IPHdr = (_IPHdr); \
(PktContext)->Info.IPHeaderOffset = (Adapter)->IPHeaderOffset; \
(PktContext)->Vc = (Vc); \
}
#define SEND_PACKET_OVER_NIC(Adapter, Packet, UserC, Status) \
{ \
PPS_SEND_PACKET_CONTEXT _PktContext; \
PNDIS_PACKET _OurPacket; \
if((Status = PsDupPacketNoContext(Adapter, Packet, &_OurPacket, &_PktContext)) == NDIS_STATUS_SUCCESS) \
{ \
FILL_PKT_FOR_NIC(_OurPacket, UserC); \
NdisSend(&Status, Adapter->LowerMpHandle, _OurPacket); \
if(Status != NDIS_STATUS_PENDING) { \
if(_PktContext) { \
PsAssert((_PktContext)->Vc == 0); \
NdisIMCopySendCompletePerPacketInfo(_PktContext->OriginalPacket, _OurPacket); \
NdisFreePacket(_OurPacket); \
} \
} \
} \
return Status; \
}
NDIS_STATUS
PsAllocateAndCopyPacket(
PADAPTER Adapter,
PNDIS_PACKET Packet,
PPNDIS_PACKET OurPacket,
PPS_SEND_PACKET_CONTEXT *PktContext)
{
PNDIS_PACKET_OOB_DATA OurOOBData;
PNDIS_PACKET_OOB_DATA XportOOBData;
PMEDIA_SPECIFIC_INFORMATION OurMediaArea;
PVOID MediaSpecificInfo = NULL;
UINT MediaSpecificInfoSize = 0;
NDIS_STATUS Status;
//
// At this point, we know that there are no packet stacks remaining in the packet.
// we proceed to allocate an NDIS packet using NdisAllocatePacket. Note that here
// we do not have to allocate our per-packet area, since NdisAllocatePacket already
// did this for us.
//
if(!Adapter->SendPacketPool)
{
PS_LOCK(&Adapter->Lock);
if(!Adapter->SendPacketPool)
{
NDIS_HANDLE PoolHandle = (void *) NDIS_PACKET_POOL_TAG_FOR_PSCHED;
NdisAllocatePacketPoolEx(&Status,
&PoolHandle,
MIN_PACKET_POOL_SIZE,
MAX_PACKET_POOL_SIZE,
Adapter->PacketContextLength);
if(Status != NDIS_STATUS_SUCCESS)
{
Adapter->Stats.OutOfPackets ++;
PS_UNLOCK(&Adapter->Lock);
return Status;
}
//
// We successfully allocated a packet pool. We can now free the Fixed Size Block pool for the packet-stack API
//
Adapter->SendPacketPool = PoolHandle;
}
PS_UNLOCK(&Adapter->Lock);
}
NdisAllocatePacket(&Status,
OurPacket,
Adapter->SendPacketPool);
if(Status != NDIS_STATUS_SUCCESS)
{
//
// mark as out of resources. Ndis will resubmit.
//
Adapter->Stats.OutOfPackets ++;
return(NDIS_STATUS_RESOURCES);
}
#if DBG
PsAssert((*OurPacket)->Private.Head == NULL);
if(Packet->Private.TotalLength){
PsAssert(Packet->Private.Head);
}
#endif // DBG
//
// chain the buffers from the upper layer packet to the newly allocated packet.
//
(*OurPacket)->Private.Head = Packet->Private.Head;
(*OurPacket)->Private.Tail = Packet->Private.Tail;
//
// Copy the Packet Flags from the Packet to OldPacket. Since we handle loopback in the
// QueryInformation handlers, we don't set the NDIS_FLAGS_DONT_LOOPBACK
//
NdisGetPacketFlags(*OurPacket) = NdisGetPacketFlags(Packet);
//
// Copy the OOB Offset from the original packet to the new packet.
//
XportOOBData = NDIS_OOB_DATA_FROM_PACKET(Packet);
OurOOBData = NDIS_OOB_DATA_FROM_PACKET(*OurPacket);
NdisMoveMemory(OurOOBData,
XportOOBData,
sizeof(NDIS_PACKET_OOB_DATA));
//
// Copy the per packet info into the new packet
//
NdisIMCopySendPerPacketInfo(*OurPacket, Packet);
//
// Copy the Media specific information
//
NDIS_GET_PACKET_MEDIA_SPECIFIC_INFO(Packet,
&MediaSpecificInfo,
&MediaSpecificInfoSize);
if(MediaSpecificInfo || MediaSpecificInfoSize){
NDIS_SET_PACKET_MEDIA_SPECIFIC_INFO(*OurPacket,
MediaSpecificInfo,
MediaSpecificInfoSize);
}
//
// Remember the original packet so that we can complete it properly.
//
*PktContext = PS_SEND_PACKET_CONTEXT_FROM_PACKET(*OurPacket);
(*PktContext)->OriginalPacket = Packet;
(*PktContext)->Vc = 0;
(*PktContext)->Info.NdisPacket = *OurPacket;
return Status;
}
NDIS_STATUS
PsDupPacketNoContext(
PADAPTER Adapter,
PNDIS_PACKET Packet,
PPNDIS_PACKET OurPacket,
PPS_SEND_PACKET_CONTEXT *PktContext)
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
BOOLEAN Remaining;
PNDIS_PACKET_STACK PacketStack;
//
// NDIS provides 2 ways for IMs to indicate packets. If the IM can allocate a packet stack, it should use it as
// it is the optimal approach. In this case, we do not have to do any per-packet copying since we don't allocate
// a new packet.
//
PacketStack = NdisIMGetCurrentPacketStack(Packet, &Remaining);
if(Remaining != 0)
{
//
// The packet stack has space only for 2 DWORDs. Since we are using more than 2, we need to allocate our own
// memory for the per-packet block. Note that we *DONT* do this when we use the NdisAllocatePacket APIs, because
// we initialized the packet pool to already include the space for the per-packet region.
//
*OurPacket = Packet;
*PktContext = 0;
PacketStack->IMReserved[0] = 0;
}
else
{
Status = PsAllocateAndCopyPacket(Adapter,
Packet,
OurPacket,
PktContext);
}
return Status;
}
NDIS_STATUS
PsDupPacketContext(
PADAPTER Adapter,
PNDIS_PACKET Packet,
PPNDIS_PACKET OurPacket,
PPS_SEND_PACKET_CONTEXT *PktContext)
{
NDIS_STATUS Status;
BOOLEAN Remaining;
PNDIS_PACKET_STACK PacketStack;
//
// NDIS provides 2 ways for IMs to indicate packets. If the IM can allocate a packet stack, it should use it as
// it is the optimal approach. In this case, we do not have to do any per-packet copying since we don't allocate
// a new packet.
//
PacketStack = NdisIMGetCurrentPacketStack(Packet, &Remaining);
if(Remaining != 0)
{
//
// The packet stack has space only for 2 DWORDs. Since we are using more than 2, we need to allocate our own
// memory for the per-packet block. Note that we *DONT* do this when we use the NdisAllocatePacket APIs, because
// we initialized the packet pool to already include the space for the per-packet region.
//
*OurPacket = Packet;
*PktContext = (PPS_SEND_PACKET_CONTEXT) (ULONG_PTR)NdisAllocateFromBlockPool(Adapter->SendBlockPool);
PacketStack->IMReserved[0] = (ULONG_PTR)*PktContext;
if(!*PktContext)
{
Adapter->Stats.OutOfPackets ++;
return NDIS_STATUS_RESOURCES;
}
else {
(*PktContext)->Info.NdisPacket = Packet;
(*PktContext)->OriginalPacket = 0;
return NDIS_STATUS_SUCCESS;
}
}
else
{
Status = PsAllocateAndCopyPacket(Adapter,
Packet,
OurPacket,
PktContext);
}
return Status;
}
//
// Tries to classify this packet based on the port numbers. If not found, will add it to one of the flows (in Round
// Robin fashion) and returns a pointer to that Vc
//
PGPC_CLIENT_VC
GetVcForPacket( PPS_WAN_LINK WanLink,
USHORT SrcPort,
USHORT DstPort)
{
PGPC_CLIENT_VC pVc, pVc1;
int i, j;
for( j = 0; j < BEVC_LIST_LEN; j++)
{
pVc = &WanLink->BeVcList[j];
// Let's look at the 2 VCs we have now:
for( i = 0; i < PORT_LIST_LEN; i++)
{
if( (pVc->SrcPort[i] == SrcPort) && (pVc->DstPort[i] == DstPort))
return pVc;
}
}
// Did not find in any of the VCs. Need to choose the Next VC for insertion and insert these valuse..
pVc = &WanLink->BeVcList[WanLink->NextVc];
WanLink->NextVc = ((WanLink->NextVc + 1) % BEVC_LIST_LEN);
pVc->SrcPort[pVc->NextSlot] = SrcPort;
pVc->DstPort[pVc->NextSlot] = DstPort;
pVc->NextSlot = ((pVc->NextSlot + 1)% PORT_LIST_LEN );
return pVc;
}
//
// This routine returns the Src and Dst Port numbers
BOOLEAN
GetPortNos(
IN PNDIS_PACKET Packet ,
IN ULONG TransportHeaderOffset,
IN OUT PUSHORT pSrcPort,
IN OUT PUSHORT pDstPort
)
{
PNDIS_BUFFER ArpBuf , IpBuf , TcpBuf, UdpBuf, DataBuf;
ULONG ArpLen , IpLen , IpHdrLen , TcpLen , UdpLen, DataLen , TotalLen , TcpHeaderOffset;
VOID *ArpH;
IPHeader UNALIGNED *IPH;
TCPHeader UNALIGNED *TCPH;
UDPHeader UNALIGNED *UDPH;
IPAddr Src, Dst;
BOOLEAN bFragment;
USHORT SrcPort , DstPort , IPID, FragOffset ,Size;
PVOID GeneralVA , Data;
ULONG i, Ret;
IpBuf = NULL;
// Steps
// Parse the IP Packet.
// Look for the appropriate ports.
// Look for the data portion and put in the Time & length there.
if(1)
{
PVOID pAddr;
PNDIS_BUFFER pNdisBuf1, pNdisBuf2;
UINT Len;
NdisGetFirstBufferFromPacket( Packet,
&ArpBuf,
&ArpH,
&ArpLen,
&TotalLen
);
pNdisBuf1 = Packet->Private.Head;
NdisQueryBuffer(pNdisBuf1, &pAddr, &Len);
while(Len <= TransportHeaderOffset)
{
TransportHeaderOffset -= Len;
NdisGetNextBuffer(pNdisBuf1, &pNdisBuf2);
NdisQueryBuffer(pNdisBuf2, &pAddr, &Len);
pNdisBuf1 = pNdisBuf2;
}
/* Buffer Descriptor corresponding to Ip Packet */
IpBuf = pNdisBuf1;
/* Length of this Buffer (IP buffer) */
IpLen = Len - TransportHeaderOffset;
/* Starting Virtual Address for this buffer */
GeneralVA = pAddr;
/* Virtual Address of the IP Header */
IPH = (IPHeader *)(((PUCHAR)pAddr) + TransportHeaderOffset);
}
if(!IpBuf)
return FALSE;
IpHdrLen = ((IPH->iph_verlen & (uchar)~IP_VER_FLAG) << 2);
FragOffset = IPH->iph_offset & IP_OFFSET_MASK;
FragOffset = net_short(FragOffset) * 8;
bFragment = (IPH->iph_offset & IP_MF_FLAG) || (FragOffset > 0);
// Don't want to deal with Fragmented packets right now..//
if ( bFragment )
return FALSE;
switch (IPH->iph_protocol)
{
case IPPROTO_TCP :
if (IPH && ((USHORT)IpLen > IpHdrLen))
{
// We have more than the IP Header in this MDL //
TCPH = (TCPHeader *) ((PUCHAR)IPH + IpHdrLen);
TcpLen = IpLen - IpHdrLen;
TcpBuf = IpBuf;
}
else
{
// TCP Header is in the next MDL //
NdisGetNextBuffer(IpBuf, &TcpBuf);
if(!TcpBuf)
return FALSE;
GeneralVA = NULL;
NdisQueryBuffer(TcpBuf,
&GeneralVA,
&TcpLen
);
TCPH = (TCPHeader *) GeneralVA;
}
/* At this point, TcpBuf, TCPH and TcpLen contain the proper values */
// Get the port numbers out.
SrcPort = net_short(TCPH->tcp_src);
DstPort = net_short(TCPH->tcp_dest);
*pSrcPort = SrcPort;
*pDstPort = DstPort;
// If the packet is here, it means: The link on which it is being sent is <= MAX_LINK_SPEED_FOR_DRR.
// So, it is OK to adjust the Window size if we are on an ICS box.
if(gEnableWindowAdjustment)
{
USHORT _old, _new;
ULONG _sum;
_old = (TCPH)->tcp_window;
_new = 1460*6;
if( net_short( _old) < _new)
return TRUE;
_new = net_short( _new );
(TCPH)->tcp_window = _new;
_sum = ((~(TCPH)->tcp_xsum) & 0xffff) + ((~_old) & 0xffff) + _new;
_sum = (_sum & 0xffff) + (_sum >> 16);
_sum += (_sum >> 16);
(TCPH)->tcp_xsum = (ushort) ((~_sum) & 0xffff);
}
return TRUE;
case IPPROTO_UDP:
if (IpLen > IpHdrLen)
{
// We have more than the IP Header in this MDL //
UDPH = (UDPHeader *) ((PUCHAR)IPH + IpHdrLen);
UdpLen = IpLen - IpHdrLen;
UdpBuf = IpBuf;
}
else
{
// UDP Header is in the next MDL //
NdisGetNextBuffer(IpBuf, &UdpBuf);
if(!UdpBuf)
return FALSE;
GeneralVA = NULL;
NdisQueryBuffer(UdpBuf,
&GeneralVA,
&UdpLen
);
UDPH = (UDPHeader *) GeneralVA;
}
/* At this point, UdpBuf, UDPH and UdpLen contain the proper values */
SrcPort = net_short(UDPH->uh_src);
DstPort = net_short(UDPH->uh_dest);
*pSrcPort = SrcPort;
*pDstPort = DstPort;
return TRUE;
default:
;
}
return FALSE;
}
//
// This where we get called for each Send
//
NTSTATUS
MpSend(
IN NDIS_HANDLE MiniportAdapterContext,
IN PNDIS_PACKET TheirPacket,
IN UINT Flags
)
/*++
Routine Description:
Received a xmit request from a legacy transport.
Arguments:
See the DDK...
Return Values:
None
--*/
{
PADAPTER Adapter = (PADAPTER)MiniportAdapterContext;
NDIS_STATUS Status;
PNDIS_PACKET OurPacket;
PPS_SEND_PACKET_CONTEXT PktContext;
PGPC_CLIENT_VC BeVc, Vc = NULL;
PETH_HEADER pAddr;
PNDIS_BUFFER pNdisBuf1;
UINT Len;
PUSHORT id;
PPS_WAN_LINK WanLink;
PsStructAssert(Adapter);
//
// If the device is shutting down, we cannot accept any more sends.
//
if(IsDeviceStateOn(Adapter) == FALSE)
{
return NDIS_STATUS_FAILURE;
}
if(Adapter->MediaType == NdisMediumWan)
{
if(Adapter->ProtocolType == ARP_ETYPE_IP)
{
//
// We should not be getting non-ip packets in the NDISWAN-IP binding.
//
PsAssert(NDIS_GET_PACKET_PROTOCOL_TYPE(TheirPacket) == NDIS_PROTOCOL_ID_TCP_IP);
pNdisBuf1 = TheirPacket->Private.Head;
NdisQueryBuffer(pNdisBuf1, &pAddr, &Len);
if(Len < sizeof(ETH_HEADER))
{
//
// Packet is too small. we have to fail this bogus packet.
//
return NDIS_STATUS_FAILURE;
}
//
// Get to the wanlink using the remote address from the packet.
//
id = (PUSHORT) &pAddr->DestAddr[0];
PS_LOCK(&Adapter->Lock);
WanLink = (PPS_WAN_LINK)(g_WanLinkTable[*id]);
if(WanLink == 0)
{
//
// We received a packet for a wanlink that has already gone down.
//
PS_UNLOCK(&Adapter->Lock);
return NDIS_STATUS_FAILURE;
}
if(WanLink->State != WanStateOpen)
{
//
// We received a packet for a wanlink that has already gone down.
//
PS_UNLOCK(&Adapter->Lock);
return NDIS_STATUS_FAILURE;
}
//
// When we get a StatusIndication for a new WAN link, NDISWAN puts context in the remote address
// When psched intercepts the LineUp, it overwrites NDISWAN's context with its own context. Psched
// uses this context to get to the WanLink from the packet. (see above)
//
// But, when it passes the packet down to NDISWAN, it needs to plumb NDISWAN's context into the packet,
// so that NDISWAN can see the context that it sent to us, as opposed to the context that we sent up to
// wanarp.
//
NdisMoveMemory(pAddr,
&WanLink->SendHeader,
FIELD_OFFSET(ETH_HEADER, Type));
//
// We optimize psched to bypass the scheduling components when there are no flows. There are a set of
// scheduling components per WanLink, so to be truly optimal, we need to check the FLowCount on a specific
// WanLink.
//
if( (WanLink->LinkSpeed > MAX_LINK_SPEED_FOR_DRR) && (!WanLink->CfInfosInstalled) )
{
// Bypass scheduling components, since there are no flows created on this
// wanlink. Note that the UserPriority is never used over wanlinks, so we can set it to 0.
PS_UNLOCK(&Adapter->Lock);
SEND_PACKET_OVER_NIC(Adapter,
TheirPacket,
0,
Status);
}
//
// Now, we are going to do either (1) DiffServ Or (2) IntServ. If the packet does not belong to either
// of these categories, we will just hash it into one of the BeVcs we have and do simple DRR.
//
else
{
//
// There is at least one flow. we need to classify this packet. Since the flow is going
// via the scheduling components, we have to allocate memory for the per-packet info
// (if the packet-stack APIs are used) or a new packet descriptor, which will include the
// per-packet info (if the old NDIS APIs are used) The packet that has been passed to us is
// 'TheirPacket'. If the packet-stack APIs are used, then TheirPacket == OurPacket
// if the non packet-stack APIs are used, then OurPacket == Newly Allocated Packet.
//
// In both cases, the code after this point will just use 'OurPacket' and the right thing will happen.
//
if((Status = PsDupPacketContext(Adapter, TheirPacket, &OurPacket, &PktContext)) != NDIS_STATUS_SUCCESS)
{
PS_UNLOCK(&Adapter->Lock);
return Status;
}
// Case 1. DiffServMode //
if(WanLink->AdapterMode == AdapterModeDiffservFlow)
{
//
// If we are in Diffserv mode, we classify the packet based on the DSCP in the IP header.
//
// Is there atleast ONE DiffServ Flow?
if(Adapter->IPHeaderOffset && WanLink->pDiffServMapping)
{
IPHeader *pIpHdr;
UCHAR tos;
pIpHdr = GetIpHeader(Adapter->IPHeaderOffset, OurPacket);
tos = pIpHdr->iph_tos >> 2;
if((Vc = WanLink->pDiffServMapping[tos].Vc))
{
//
// We found a VC for this packet.
//
PsAssert(Vc->Adapter == Adapter);
PsAssert(Vc->WanLink == WanLink);
InterlockedIncrement(&Vc->RefCount);
SET_TOS_XSUM(OurPacket,
pIpHdr,
(WanLink->pDiffServMapping[tos].ConformingOutboundDSField));
FILL_PKT_FOR_SCHED(Adapter,
PktContext,
Vc,
OurPacket,
(WanLink->pDiffServMapping[tos].NonConformingOutboundDSField),
WanLink->pDiffServMapping[tos].ConformingUserPriority,
WanLink->pDiffServMapping[tos].NonConformingUserPriority,
pIpHdr);
}
// Could not classify packets to ANY of the DiffServ Flows. So, let's just do DRR across the BeVcs.
else
{
//
// There are Diffserv flows installed, but not for this DSCP. Lets use the BEVCS.
//
USHORT SrcPort, DstPort;
if( (WanLink->LinkSpeed <= MAX_LINK_SPEED_FOR_DRR) &&
(GetPortNos( TheirPacket, Adapter->IPHeaderOffset, &SrcPort, &DstPort)))
Vc = GetVcForPacket( WanLink, SrcPort, DstPort);
else
Vc = &WanLink->BestEffortVc;
InterlockedIncrement(&Vc->RefCount);
FILL_PKT_FOR_SCHED(Adapter,
PktContext,
Vc,
OurPacket,
Vc->IPPrecedenceNonConforming,
Vc->UserPriorityConforming,
Vc->UserPriorityNonConforming,
pIpHdr);
}
}
// No, there is not One, let's just do DRR across the BeVcs.
else
{
//
// We are in Diffserv mode, but no Diffserv flows are created as yet. Let's use the BEVCS.
//
USHORT SrcPort, DstPort;
if( (WanLink->LinkSpeed <= MAX_LINK_SPEED_FOR_DRR) &&
(GetPortNos( TheirPacket, Adapter->IPHeaderOffset, &SrcPort, &DstPort)))
Vc = GetVcForPacket( WanLink, SrcPort, DstPort);
else
Vc = &WanLink->BestEffortVc;
InterlockedIncrement(&Vc->RefCount);
FILL_PKT_FOR_SCHED(Adapter,
PktContext,
Vc,
OurPacket,
Vc->IPPrecedenceNonConforming,
Vc->UserPriorityConforming,
Vc->UserPriorityNonConforming,
NULL);
}
PS_UNLOCK(&Adapter->Lock);
}
// Case 2. IntServMode
else
{
USHORT SrcPort=0, DstPort=0;
//
// We are in RSVP mode, and we need to go to the GPC to classify the packet.
// We already have a pointer to our WanLink. But, the wanlink could go away
// when we release the lock and try to classify the packet. So, we take
// a ref on the BestEffortVc for the WanLink.
//
if( (WanLink->LinkSpeed <= MAX_LINK_SPEED_FOR_DRR) &&
(GetPortNos( TheirPacket, Adapter->IPHeaderOffset, &SrcPort, &DstPort)))
BeVc = GetVcForPacket( WanLink, SrcPort, DstPort);
else
BeVc = &WanLink->BestEffortVc;
InterlockedIncrement(&BeVc->RefCount);
PS_UNLOCK(&Adapter->Lock);
if( WanLink->CfInfosInstalled )
Vc = GetVcByClassifyingPacket(Adapter, &WanLink->InterfaceID, OurPacket);
if(!Vc)
{
Vc = BeVc;
}
else
{
DerefClVc(BeVc);
}
FILL_PKT_FOR_SCHED(Adapter,
PktContext,
Vc,
OurPacket,
Vc->IPPrecedenceNonConforming,
Vc->UserPriorityConforming,
Vc->UserPriorityNonConforming,
NULL);
}
//
// There is at least one flow - We need to send this packet via the scheduling
// components.
//
if((Vc->ClVcState == CL_CALL_COMPLETE) ||
(Vc->ClVcState == CL_MODIFY_PENDING))
{
SEND_PACKET_VIA_SCHEDULER(PktContext, Vc, Adapter, OurPacket);
}
else
{
//
// Deref the ref that was added by the GPC.
//
DerefClVc(Vc);
PsDbgSend(DBG_FAILURE, DBG_SEND, MP_SEND, NOT_READY, Adapter, Vc, TheirPacket, OurPacket);
if(PktContext->OriginalPacket)
{
NdisFreePacket(OurPacket);
}
else
{
NdisFreeToBlockPool((PUCHAR)PktContext);
}
return(NDIS_STATUS_FAILURE);
}
}
}
//
// Forget about it. It's a Non-IP packet
//
else
{
//
// For non IP adapters, we just send over the NIC. Note that we don't create a best effort
// Vc for such adapters. The only thing that we lose here is the ability to mark 802.1p on
// such packets (we don't have a Vc, so we cannot supply a UserPriority value to the below
// macro. But that is okay, since 802.1p is meaningful only in non LAN adapters.
//
SEND_PACKET_OVER_NIC(Adapter,
TheirPacket,
0,
Status);
}
}
else
{
//
// We have received a send at our non WAN binding.
//
if(!Adapter->CfInfosInstalled &&
Adapter->BestEffortLimit == UNSPECIFIED_RATE &&
TsCount == 0 )
{
// There is no point in trying to classify if there are no flows installed
Vc = &Adapter->BestEffortVc;
PsAssert(Vc->ClVcState == CL_CALL_COMPLETE);
//
// Bypass scheduling components.
//
SEND_PACKET_OVER_NIC(Adapter,
TheirPacket,
Vc->UserPriorityConforming,
Status);
}
else
{
//
// There is at least one flow, or we are in LimitedBestEffort mode. Let's try to classify the Vc.
// In this case, the packet will have to go via the scheduling components.
//
//
// Since the flow is going via the scheduling components, we have to allocate the per-packet info.
// (if the new NDIS APIs are used) or a new packet descriptor, which will include the per-packet info
// (if the old NDIS APIs are used)
//
if(Adapter->AdapterMode == AdapterModeDiffservFlow)
{
//
// We are in the diffserv mode. We don't have to use the GPC to classify packets.
// For all IP packets, classify on the TOS byte.
//
PS_LOCK(&Adapter->Lock);
if(NDIS_GET_PACKET_PROTOCOL_TYPE(TheirPacket) == NDIS_PROTOCOL_ID_TCP_IP &&
Adapter->IPHeaderOffset &&
Adapter->pDiffServMapping)
{
UCHAR tos;
IPHeader *pIpHdr = 0;
pIpHdr = GetIpHeader(Adapter->IPHeaderOffset, TheirPacket);
tos = pIpHdr->iph_tos >> 2;
if((Vc = Adapter->pDiffServMapping[tos].Vc))
{
PsAssert(Vc->Adapter == Adapter);
if((Status = PsDupPacketContext(Adapter,
TheirPacket,
&OurPacket,
&PktContext)) != NDIS_STATUS_SUCCESS)
{
PS_UNLOCK(&Adapter->Lock);
return Status;
}
InterlockedIncrement(&Vc->RefCount);
SET_TOS_XSUM(OurPacket,
pIpHdr,
(Adapter->pDiffServMapping[tos].ConformingOutboundDSField));
FILL_PKT_FOR_SCHED(Adapter,
PktContext,
Vc,
OurPacket,
(Adapter->pDiffServMapping[tos].NonConformingOutboundDSField),
Adapter->pDiffServMapping[tos].ConformingUserPriority,
Adapter->pDiffServMapping[tos].NonConformingUserPriority,
pIpHdr);
}
else
{
if( ( Adapter->MaxOutstandingSends == 0xffffffff) &&
( TsCount == 0))
{
Vc = &Adapter->BestEffortVc;
PsAssert(Vc->ClVcState == CL_CALL_COMPLETE);
//
// Bypass scheduling components.
//
PS_UNLOCK(&Adapter->Lock);
SEND_PACKET_OVER_NIC(Adapter,
TheirPacket,
Vc->UserPriorityConforming,
Status);
}
Vc = &Adapter->BestEffortVc;
if((Status = PsDupPacketContext(Adapter,
TheirPacket,
&OurPacket,
&PktContext)) != NDIS_STATUS_SUCCESS)
{
PS_UNLOCK(&Adapter->Lock);
return Status;
}
InterlockedIncrement(&Vc->RefCount);
FILL_PKT_FOR_SCHED(Adapter,
PktContext,
Vc,
OurPacket,
Vc->IPPrecedenceNonConforming,
Vc->UserPriorityConforming,
Vc->UserPriorityNonConforming,
NULL);
}
}
else
{
//
// We are in Diffserv mode, but there are no DiffServ Vcs, or it's not an IP
// packet. Send over the Adapter's BE Vc.
//
Vc = &Adapter->BestEffortVc;
if((Status = PsDupPacketContext( Adapter,
TheirPacket,
&OurPacket,
&PktContext)) != NDIS_STATUS_SUCCESS)
{
PS_UNLOCK(&Adapter->Lock);
return Status;
}
InterlockedIncrement(&Vc->RefCount);
FILL_PKT_FOR_SCHED(Adapter,
PktContext,
Vc,
OurPacket,
Vc->IPPrecedenceNonConforming,
Vc->UserPriorityConforming,
Vc->UserPriorityNonConforming,
NULL);
}
PS_UNLOCK(&Adapter->Lock);
}
else
{
// We are in RSVP mode. Let's classify with the GPC.
Vc = GetVcByClassifyingPacket(Adapter, &Adapter->InterfaceID, TheirPacket);
if( !Vc)
{
if( (Adapter->MaxOutstandingSends == 0xffffffff) &&
(TsCount == 0))
{
Vc = &Adapter->BestEffortVc;
PsAssert(Vc->ClVcState == CL_CALL_COMPLETE);
//
// Bypass scheduling components.
//
SEND_PACKET_OVER_NIC(Adapter,
TheirPacket,
Vc->UserPriorityConforming,
Status);
}
// We will be doing DRR on this adapter; so send pkt on BeVc
Vc = &Adapter->BestEffortVc;
InterlockedIncrement(&Vc->RefCount);
}
if((Status = PsDupPacketContext(Adapter, TheirPacket, &OurPacket, &PktContext)) != NDIS_STATUS_SUCCESS)
{
return Status;
}
FILL_PKT_FOR_SCHED(Adapter,
PktContext,
Vc,
OurPacket,
Vc->IPPrecedenceNonConforming,
Vc->UserPriorityConforming,
Vc->UserPriorityNonConforming,
NULL);
}
if((Vc->ClVcState == CL_CALL_COMPLETE) ||
(Vc->ClVcState == CL_MODIFY_PENDING) ||
(Vc->ClVcState == CL_INTERNAL_CALL_COMPLETE))
{
SEND_PACKET_VIA_SCHEDULER(PktContext, Vc, Adapter, OurPacket);
}
else
{
//
// Deref the ref that was added by the GPC.
//
DerefClVc(Vc);
PsDbgSend(DBG_FAILURE, DBG_SEND, MP_SEND, NOT_READY, Adapter, Vc, TheirPacket, OurPacket);
if(PktContext->OriginalPacket)
{
NdisFreePacket(OurPacket);
}
else
{
NdisFreeToBlockPool((PUCHAR)PktContext);
}
return(NDIS_STATUS_FAILURE);
}
}
}
}
VOID
ClSendComplete(
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET Packet,
IN NDIS_STATUS Status
)
/*++
Routine Description:
Completion routine for NdisSendPackets.
Does most of the work for cleaning up after a send.
If necessary, call the PSA's send packet complete function
Arguments:
See the DDK...
Return Values:
None
--*/
{
PGPC_CLIENT_VC Vc;
PADAPTER Adapter = (PADAPTER)ProtocolBindingContext;
PPS_SEND_PACKET_CONTEXT PktContext;
PNDIS_PACKET XportPacket;
HANDLE PoolHandle;
//
// Determine if the packet we are completing is the one we allocated. If so, get
// the original packet from the reserved area and free the allocated packet. If this
// is the packet that was sent down to us then just complete the packet.
//
PoolHandle = NdisGetPoolFromPacket(Packet);
if(PoolHandle != Adapter->SendPacketPool)
{
PNDIS_PACKET_STACK PacketStack;
BOOLEAN Remaining;
PacketStack = NdisIMGetCurrentPacketStack(Packet, &Remaining);
PsAssert(Remaining != 0);
PktContext = (PPS_SEND_PACKET_CONTEXT) PacketStack->IMReserved[0];
if(PktContext != 0)
{
//
// This packet went via the scheduling components.
//
PsAssert(PktContext->Vc);
Vc = PktContext->Vc;
PsDbgSend(DBG_INFO, DBG_SEND, CL_SEND_COMPLETE, ENTER, Adapter, Vc, Packet, 0);
PsAssert(Vc->Adapter == Adapter);
if(Vc->SendComplete)
(*Vc->SendComplete)(Vc->SendCompletePipeContext, Packet);
DerefClVc(Vc);
NdisFreeToBlockPool((PUCHAR)PktContext);
}
NdisMSendComplete(Adapter->PsNdisHandle,
Packet,
Status);
}
else
{
//
// get the pointer to the upper layer's packet. Reinit the packet struct and
// push it back on the adapter's packet SList. Remove the reference incurred
// when the packet was handled by MpSend
//
PktContext = PS_SEND_PACKET_CONTEXT_FROM_PACKET(Packet);
//
// Call the scheduler if necessary
//
if(PktContext->Vc)
{
//
// Some packets never went through the scheduler.
//
Vc = PktContext->Vc;
PsDbgSend(DBG_INFO, DBG_SEND, CL_SEND_COMPLETE, ENTER, Adapter, Vc, Packet, 0);
PsAssert(Vc->Adapter == Adapter);
if(Vc->SendComplete)
{
(*Vc->SendComplete)(Vc->SendCompletePipeContext, Packet);
}
//
// We have taken a ref on the VCs when we sent the packets
// through the scheduling components. Now is the time to
// Deref them
//
DerefClVc(Vc);
}
else
{
PsDbgSend(DBG_INFO, DBG_SEND, CL_SEND_COMPLETE, ENTER, Adapter, 0, Packet, 0);
}
XportPacket = PktContext->OriginalPacket;
NdisIMCopySendCompletePerPacketInfo(XportPacket, Packet);
NdisFreePacket(Packet);
NdisMSendComplete(Adapter->PsNdisHandle,
XportPacket,
Status);
}
} // ClSendComplete
VOID
DropPacket(
IN HANDLE PipeContext,
IN HANDLE FlowContext,
IN PNDIS_PACKET Packet,
IN NDIS_STATUS Status
)
/*++
Routine Description:
Drop a packet after it was queued by the scheduler.
Arguments:
PipeContext - Pipe context (adapter)
FlowContext - Flow context (adapter VC)
Packet - Packet to drop
Status - Return code to return to NDIS
Return Values:
None
--*/
{
PGPC_CLIENT_VC Vc = (PGPC_CLIENT_VC)FlowContext;
PADAPTER Adapter = (PADAPTER)PipeContext;
PPS_SEND_PACKET_CONTEXT PktContext;
PNDIS_PACKET XportPacket;
HANDLE PoolHandle;
//
// Determine if the packet we are completing is the one we allocated. If so, get
// the original packet from the reserved area and free the allocated packet. If this
// is the packet that was sent down to us then just complete the packet.
//
PoolHandle = NdisGetPoolFromPacket(Packet);
if(PoolHandle != Adapter->SendPacketPool)
{
PNDIS_PACKET_STACK PacketStack;
BOOLEAN Remaining;
PacketStack = NdisIMGetCurrentPacketStack(Packet, &Remaining);
PsAssert(Remaining != 0);
PktContext = (PPS_SEND_PACKET_CONTEXT) PacketStack->IMReserved[0];
PsAssert(PktContext != 0);
PsAssert(Vc == PktContext->Vc);
PsAssert(Adapter == Vc->Adapter);
NdisFreeToBlockPool((PUCHAR)PktContext);
NdisMSendComplete(Adapter->PsNdisHandle,
Packet,
Status);
}
else
{
PktContext = PS_SEND_PACKET_CONTEXT_FROM_PACKET(Packet);
PsAssert(PktContext != 0);
PsAssert(Vc == PktContext->Vc);
PsAssert(Adapter == Vc->Adapter);
XportPacket = PktContext->OriginalPacket;
NdisFreePacket(Packet);
NdisMSendComplete(Adapter->PsNdisHandle,
XportPacket,
Status);
}
Vc->Stats.DroppedPackets ++;
PsDbgSend(DBG_INFO, DBG_SEND, DROP_PACKET, ENTER, Adapter, Vc, Packet, 0);
DerefClVc(Vc);
} // DropPacket
char*
ReturnByteAtOffset( PNDIS_PACKET pNdisPacket, ULONG Offset)
{
PVOID VA;
PNDIS_BUFFER pNdisBuf1, pNdisBuf2;
UINT Len;
pNdisBuf1 = pNdisPacket->Private.Head;
NdisQueryBuffer(pNdisBuf1, &VA, &Len);
while(Len <= Offset)
{
Offset -= Len;
NdisGetNextBuffer(pNdisBuf1, &pNdisBuf2);
NdisQueryBuffer(pNdisBuf2, &VA, &Len);
pNdisBuf1 = pNdisBuf2;
}
return (char*)(((char*)VA) + Offset);
}
PGPC_CLIENT_VC FASTCALL
GetVcByClassifyingPacket(
PADAPTER Adapter,
PTC_INTERFACE_ID pInterfaceID,
PNDIS_PACKET OurPacket
)
/*+++
---*/
{
CLASSIFICATION_HANDLE ClassificationHandle;
PGPC_CLIENT_VC Vc = NULL;
#if CBQ
PCLASS_MAP_CONTEXT_BLK pClBlk = NULL;
#endif
NDIS_STATUS Status;
ULONG ProtocolType;
//
// We are in RSVP mode - Use the GPC to classify the packet. If the GPC wants to return a Vc, it will
// return with a ref.
//
ClassificationHandle = (CLASSIFICATION_HANDLE)
PtrToUlong(NDIS_PER_PACKET_INFO_FROM_PACKET(OurPacket, ClassificationHandlePacketInfo));
if (ClassificationHandle)
{
PsAssert(GpcEntries.GpcGetCfInfoClientContextHandler);
Vc = GpcEntries.GpcGetCfInfoClientContextWithRefHandler(GpcQosClientHandle,
ClassificationHandle,
FIELD_OFFSET(GPC_CLIENT_VC, RefCount));
#if CBQ
pClBlk = NULL;
Status = GpcEntries.GpcGetCfInfoClientContextHandler(GpcClassMapClientHandle,
ClassificationHandle,
&pClBlk);
if(pClBlk)
{
PktContext->Info.ClassMapContext = pClBlk->ComponentContext;
}
#endif
//
// If we got a Vc that was not destined for this adapter, we have to reject it.
//
if(Vc)
{
if(Vc->Adapter != Adapter)
{
DerefClVc(Vc);
}
else
return Vc;
}
}
//
// Let's classify this packet since we did not get a Classification ID or a proper Vc.
//
PsAssert(GpcEntries.GpcClassifyPacketHandler);
switch(NDIS_GET_PACKET_PROTOCOL_TYPE(OurPacket))
{
case NDIS_PROTOCOL_ID_TCP_IP:
ProtocolType = GPC_PROTOCOL_TEMPLATE_IP;
break;
case NDIS_PROTOCOL_ID_IPX:
ProtocolType = GPC_PROTOCOL_TEMPLATE_IPX;
break;
default:
ProtocolType = GPC_PROTOCOL_TEMPLATE_NOT_SPECIFIED;
break;
}
//
// If the adapter type is 802.5 (Token Ring), then the MAC header can be of variable size.
// The format of the MAC header is as follows:
// +---------------------+-------------+----------+-----------
// | 2 + 6 (DA) + 6 (SA) | Optional RI | 8 (SNAP) | IP
// +---------------------+-------------+----------+-----------
// Optional RI is present if and only if RI bit as part of SA is set.
// When RI is present, its length is give by the lower 5 bits of the 15th byte.
// 1. Get the VA for the 9th and the 15th bytes.
// 2. If RI if not present, Offset = 14 + 6.
// 3. If present, Offset = 14 + 6 + RI-Size.
if(Adapter->MediaType == NdisMedium802_5)
{
PNDIS_BUFFER pTempNdisBuffer;
PUCHAR pHeaderBuffer;
ULONG BufferLength;
ULONG TotalLength;
ULONG IpOffset;
NdisGetFirstBufferFromPacket( OurPacket,
&pTempNdisBuffer,
&pHeaderBuffer,
&BufferLength,
&TotalLength);
ASSERT( BufferLength >= 15);
if( (*(ReturnByteAtOffset(OurPacket, 8)) & 0x80) == 0)
IpOffset = 14 + 8;
else
IpOffset = 14 + 8 + (*(ReturnByteAtOffset(OurPacket, 14)) & 0x1f);
Status = GpcEntries.GpcClassifyPacketHandler(
GpcQosClientHandle,
ProtocolType,
OurPacket,
IpOffset,
pInterfaceID,
(PGPC_CLIENT_HANDLE)&Vc,
&ClassificationHandle);
}
else
{
Status = GpcEntries.GpcClassifyPacketHandler(
GpcQosClientHandle,
ProtocolType,
OurPacket,
// This is basically to cover up a bug in wandrv.sys, which gives bogus frame header sizes. We look at the
// Ip Header offset supplied by the protocol above for IP packets only, as we are saving only those for the
// time being.
((NDIS_GET_PACKET_PROTOCOL_TYPE(OurPacket) == NDIS_PROTOCOL_ID_TCP_IP) && (Adapter->IPHeaderOffset))
? (Adapter->IPHeaderOffset)
: Adapter->HeaderSize,
pInterfaceID,
(PGPC_CLIENT_HANDLE)&Vc,
&ClassificationHandle);
}
if(Status == GPC_STATUS_SUCCESS)
{
//
// If we have succeeded, we must get a Classification Handle
//
PsAssert(ClassificationHandle != 0);
//
// The Classification succeeded. If we found a ClassificationHandle
// then we must write it in the packet so that anyone below us can use
// it. The very fact that we are here indicates that we did not start
// with a Classification handle or we got a bad one. So, we need not
// worry about over writing the classification handle in the packet.
//
NDIS_PER_PACKET_INFO_FROM_PACKET(OurPacket, ClassificationHandlePacketInfo) = UlongToPtr(ClassificationHandle);
Vc = GpcEntries.GpcGetCfInfoClientContextWithRefHandler(GpcQosClientHandle,
ClassificationHandle,
FIELD_OFFSET(GPC_CLIENT_VC, RefCount));
#if CBQ
//
// Get the CBQ class map context & store it in the
// packet. No point doing this if the first classification
// failed.
//
pClBlk = NULL;
Status = GpcEntries.GpcGetCfInfoClientContextHandler(GpcClassMapClientHandle,
ClassificationHandle,
&pClBlk);
if(pClBlk)
{
PktContext->Info.ClassMapContext = pClBlk->ComponentContext;
}
#endif
}
if(Vc && Vc->Adapter != Adapter)
{
//
// We have used the GPC APIs that return a Vc with a ref. We have to deref here, because we got a wrong Vc
// for this adapter.
//
DerefClVc(Vc);
return NULL;
}
return Vc;
}
VOID
ClCoSendComplete(
IN NDIS_STATUS Status,
IN NDIS_HANDLE ProtocolVcContext,
IN PNDIS_PACKET Packet
)
{
PGPC_CLIENT_VC Vc = (PGPC_CLIENT_VC) ProtocolVcContext;
ClSendComplete(Vc->Adapter,
Packet,
Status);
} // ClCoSendComplete
/* end send.c */