|
|
/*++
Copyright (c) 1996-1999 Microsoft Corporation
Module Name:
DRRSeq.c
Abstract:
Priority/DRR Sequencer. This module is a scheduling component that determines the order in which submitted packets should be sent.
Author:
Environment:
Kernel Mode
Revision History:
--*/
#include "psched.h"
#pragma hdrstop
// The sequencer classifies each flow into an internal "priority group" based
// on the flow's service type and conformance status. Within each priority
// group, there may be one or more priority levels or offsets. The total
// number of internal priority levels is the sum of the priority levels for
// each priority group. The internal priority assigned to each flow is
// calculated from the priority group and the relative priority within the
// group, which is obtained from the QOS Priority object. The 802.1 priority,
// is set by the wrapper. The non conforming values are obtained from the
// packet.
//
// The flows of the following servicetypes have no internal priority.
// SERVICETYPE_BESTEFFORT
// SERVICETYPE_NONCONFORMING
// SERVICETYPE_QUALITATIVE.
//
// SERVICETYPE_BESTEFFORT is treated as SERVICETYPE_QUALITATIVE in the sequencer, so the no of priority
// groups is 1 less than the no. of servicetypes.
#define RELATIVE_PRIORITIES 8
#define PRIORITY_GROUPS (NUM_TC_SERVICETYPES - 1)
#define INTERNAL_PRIORITIES (((PRIORITY_GROUPS - 2) * RELATIVE_PRIORITIES) + 2)
#define DEFAULT_PRIORITY_OFFSET 3
#define DEFAULT_MIN_QUANTUM 1500
#define PRIORITY_GROUP_NON_CONFORMING 0
#define PRIORITY_GROUP_BEST_EFFORT 1
#define PRIORITY_GROUP_CONTROLLED_LOAD 2
#define PRIORITY_GROUP_GUARANTEED 3
#define PRIORITY_GROUP_NETWORK_CONTROL 4
//
// For maintaining stats
//
#define SEQUENCER_AVERAGING_ARRAY_SIZE 256
#define NETCARD_AVERAGING_ARRAY_SIZE 256
#define SEQUENCER_FLOW_AVERAGING_ARRAY_SIZE 256
// The DRR Sequencer's pipe information
typedef struct _DSEQ_PIPE {
// ContextInfo - Generic context info
// Lock - Protects pipe and flow data
// Flags - See below
// Flows - List of all installed flows
// ActiveFlows - Lists of flows that are waiting to send packets
// PriorityLevels - Number of priority offsets for each priority group
// StartPriority - Lowest internal priority value for each priority group
// ActiveFlowCount - Number of active flows for each service type
// MaxOutstandingSends - Maximum number of outstanding sends
// OutstandingSends - Number of outstanding sends
// PacketsInNetcardAveragingArray
// PacketsInSequencer - Current number packets in sequencer
// PacketsInSequencerAveragingArray
// Bandwidth - Link speed
// MinimumQuantum - Minimum quantum size for DRR
// MinimumRate - Smallest rate currently assigned to a flow
// TimerResolution - Timer resolution in OS time units
// PsFlags - Flags from pipe parameters
// PsPipeContext - PS's pipe context value
PS_PIPE_CONTEXT ContextInfo;
PS_DRRSEQ_STATS Stats; PRUNNING_AVERAGE PacketsInNetcardAveragingArray; ULONG PacketsInSequencer; PRUNNING_AVERAGE PacketsInSequencerAveragingArray;
NDIS_SPIN_LOCK Lock; ULONG Flags; LIST_ENTRY Flows; LIST_ENTRY ActiveFlows[INTERNAL_PRIORITIES]; ULONG PriorityLevels[PRIORITY_GROUPS]; ULONG StartPriority[PRIORITY_GROUPS]; ULONG ActiveFlowCount[PRIORITY_GROUPS]; ULONG TotalActiveFlows; ULONG MaxOutstandingSends;
ULONG ConfiguredMaxOutstandingSends; // This is added to keep track of what the Registry/User-asked value of MOS is, while we might
// have changed MOS to be able to do DRR on this Pipe/WanLink. When we switch back from DRR mode
// with MOS=1, we'll use this going forward.
ULONG IsslowFlowCount; // This is added to keep track of the number of active/current ISSLOW flows. We will do DRR on this
// WanLink (if it is a WanLink) only if this count is 0.
ULONG OutstandingSends; ULONG Bandwidth; ULONG MinimumQuantum; ULONG MinimumRate; ULONG TimerResolution; ULONG PsFlags; HANDLE PsPipeContext; PPS_PROCS PsProcs; PSU_SEND_COMPLETE PreviousUpcallsSendComplete; PPS_PIPE_CONTEXT PreviousUpcallsSendCompletePipeContext; } DSEQ_PIPE, *PDSEQ_PIPE;
// Pipe flag values
#define DSEQ_DEQUEUE 1
#define DSEQ_PASSTHRU 2
typedef enum _FLOW_STATE { DRRSEQ_FLOW_CREATED = 1, DRRSEQ_FLOW_DELETED } FLOW_STATE;
// The DRR Sequencer's flow information
typedef struct _DSEQ_FLOW {
// ContextInfo - Generic context info
// ActiveLinks - Links in active flow list
// Links - Links in installed flow list
// PacketQueue - Self-explanatory
// PacketSendTime - Send time for current packet
// LastConformanceTime - Absolute conformance time of last packet
// TokenRate - TokenRate from GQOS
// UserPriority - Priority offset assigned by user
// Priority - Internal priority
// PriorityGroup - Priority group for flow
// Quantum - Quantum assigned to flow for DRR
// DeficitCounter - Current value of DRR deficit counter
// Flags - See below
// PsFlowContext - PS's flow context value
// BucketSize - TokenBucketSize from GQOS
// NumPacketsInSeq - Number of packets from this flow in the sequencer
// PacketsInSeqAveragingArray-Data for computing average packets in seq from this flow
PS_FLOW_CONTEXT ContextInfo; LIST_ENTRY ActiveLinks; LIST_ENTRY Links; LIST_ENTRY PacketQueue; LARGE_INTEGER PacketSendTime; LARGE_INTEGER LastConformanceTime; ULONG TokenRate; ULONG UserPriority; ULONG Priority; ULONG PriorityGroup; ULONG Quantum; ULONG DeficitCounter; ULONG Flags; HANDLE PsFlowContext; ULONG BucketSize;
ULONG PacketsInSequencer; PS_DRRSEQ_STATS Stats; PRUNNING_AVERAGE PacketsInSeqAveragingArray;
FLOW_STATE State; } DSEQ_FLOW, *PDSEQ_FLOW;
#define MAX_DEQUEUED_PACKETS 8
//
// Values for Drr Seq Flow flags: [ Don't know why 1 was not used here]
#define FLOW_USER_PRIORITY 0x00000002
// GPC_ISSLOW_FLOW 0x00000040 Indicates that this is an ISSLOW flow.
// Make sure not to use the same flag for something else.
// The following macro checks a packet for conformance based on the flow's
// LastPacketTime, the current time, and the timer resolution.
#define PacketIsConforming(_flow, _packetinfo, _curtime, _r) \
( (_flow)->PacketSendTime.QuadPart <= ((_curtime).QuadPart + (_r)) && \ (_packetinfo)->PacketLength <= (_flow)->BucketSize \ )
#define AdjustLastPacketTime(_flow, _curtime, _r) \
if ((_curtime).QuadPart > ((_flow)->PacketSendTime.QuadPart + (_r))) \ if ((_curtime).QuadPart > ((_flow)->LastConformanceTime.QuadPart - (_r))) \ (_flow)->PacketSendTime = (_flow)->LastConformanceTime; \ else \ (_flow)->PacketSendTime = (_curtime);
#define LOCK_PIPE(_p) NdisAcquireSpinLock(&(_p)->Lock)
#define UNLOCK_PIPE(_p) NdisReleaseSpinLock(&(_p)->Lock)
/* External */
/* Static */
/* Forward */
NDIS_STATUS DrrSeqInitializePipe ( IN HANDLE PsPipeContext, IN PPS_PIPE_PARAMETERS PipeParameters, IN PPS_PIPE_CONTEXT ComponentPipeContext, IN PPS_PROCS PsProcs, IN PPS_UPCALLS Upcalls );
NDIS_STATUS DrrSeqModifyPipe ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_PIPE_PARAMETERS PipeParameters );
VOID DrrSeqDeletePipe ( IN PPS_PIPE_CONTEXT PipeContext );
NDIS_STATUS DrrSeqCreateFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN HANDLE PsFlowContext, IN PCO_CALL_PARAMETERS CallParameters, IN PPS_FLOW_CONTEXT ComponentFlowContext );
NDIS_STATUS DrrSeqModifyFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext, IN PCO_CALL_PARAMETERS CallParameters );
VOID DrrSeqDeleteFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext );
VOID DrrSeqEmptyFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext );
static NDIS_STATUS DrrSeqCreateClassMap ( IN PPS_PIPE_CONTEXT PipeContext, IN HANDLE PsClassMapContext, IN PTC_CLASS_MAP_FLOW ClassMap, IN PPS_CLASS_MAP_CONTEXT ComponentClassMapContext );
static NDIS_STATUS DrrSeqDeleteClassMap ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_CLASS_MAP_CONTEXT ComponentClassMapContext );
BOOLEAN DrrSeqSubmitPacket ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext, IN PPS_CLASS_MAP_CONTEXT ClassMapContext, IN PPACKET_INFO_BLOCK PacketInfo );
VOID DrrSeqSendComplete ( IN PPS_PIPE_CONTEXT PipeContext, IN PNDIS_PACKET Packet );
VOID DrrSetInformation( IN PPS_PIPE_CONTEXT ComponentPipeContext, IN PPS_FLOW_CONTEXT ComponentFlowContext, IN NDIS_OID Oid, IN ULONG Len, IN PVOID Data);
VOID DrrQueryInformation ( IN PPS_PIPE_CONTEXT ComponentPipeContext, IN PPS_FLOW_CONTEXT ComponentFlowContext, IN NDIS_OID Oid, IN ULONG Len, IN PVOID Data, IN OUT PULONG BytesWritten, IN OUT PULONG BytesNeeded, IN OUT PNDIS_STATUS Status); VOID DrrSeqSendComplete ( IN PPS_PIPE_CONTEXT PipeContext, IN PNDIS_PACKET Packet );
/* End Forward */
VOID InitializeDrrSequencer( PPSI_INFO Info)
/*++
Routine Description:
Initialization routine for the DRR sequencer. This routine just fills in the PSI_INFO struct and returns.
Arguments:
Info - Pointer to component interface info struct
Return Values:
NDIS_STATUS_SUCCESS
--*/ { Info->PipeContextLength = ((sizeof(DSEQ_PIPE) + 7) & ~7); Info->FlowContextLength = ((sizeof(DSEQ_FLOW) + 7) & ~7); Info->ClassMapContextLength = sizeof(PS_CLASS_MAP_CONTEXT); Info->InitializePipe = DrrSeqInitializePipe; Info->ModifyPipe = DrrSeqModifyPipe; Info->DeletePipe = DrrSeqDeletePipe; Info->CreateFlow = DrrSeqCreateFlow; Info->ModifyFlow = DrrSeqModifyFlow; Info->DeleteFlow = DrrSeqDeleteFlow; Info->EmptyFlow = DrrSeqEmptyFlow; Info->CreateClassMap = DrrSeqCreateClassMap; Info->DeleteClassMap = DrrSeqDeleteClassMap; Info->SubmitPacket = DrrSeqSubmitPacket; Info->ReceivePacket = NULL; Info->ReceiveIndication = NULL; Info->SetInformation = DrrSetInformation; Info->QueryInformation = DrrQueryInformation;
} // InitializeDrrSequencer
VOID CleanupDrrSequencer( VOID)
/*++
Routine Description:
Cleanup routine for the DRR sequencer.
Arguments:
Return Values:
NDIS_STATUS_SUCCESS
--*/ { } // CleanupDrrSequencer
VOID AdjustFlowQuanta( PDSEQ_PIPE Pipe, ULONG MinRate)
/*++
Routine Description:
Adjust the quantum value for all flows based on the new minimum value. If MinRate is unspecified then a search for the new minimum rate will be performed.
Arguments:
Pipe - Pointer to pipe context information MinRate - New value for minimum rate, or GQPS_UNSPECIFIED to force a search
Return Values:
--*/ { PDSEQ_FLOW Flow; PLIST_ENTRY Entry;
if (MinRate == QOS_NOT_SPECIFIED) { if (Pipe->Bandwidth != 0) { MinRate = Pipe->Bandwidth; } for (Entry = Pipe->Flows.Flink; Entry != &Pipe->Flows; Entry = Entry->Flink) { Flow = CONTAINING_RECORD(Entry, DSEQ_FLOW, Links);
if ((Flow->TokenRate < MinRate) && (Flow->PriorityGroup > PRIORITY_GROUP_BEST_EFFORT) && (Flow->PriorityGroup != PRIORITY_GROUP_NETWORK_CONTROL)) { MinRate = Flow->TokenRate; } } }
for (Entry = Pipe->Flows.Flink; Entry != &Pipe->Flows; Entry = Entry->Flink) { Flow = CONTAINING_RECORD(Entry, DSEQ_FLOW, Links);
if ((Flow->TokenRate == QOS_NOT_SPECIFIED) || (Flow->PriorityGroup == PRIORITY_GROUP_NETWORK_CONTROL) || (Flow->PriorityGroup <= PRIORITY_GROUP_BEST_EFFORT)) {
Flow->Quantum = Pipe->MinimumQuantum; } else { Flow->Quantum = (ULONG)((ULONGLONG)(Flow->TokenRate * Pipe->MinimumQuantum) / MinRate); }
PsAssert((LONG)Flow->Quantum > 0); }
Pipe->MinimumRate = MinRate; PsAssert(Pipe->MinimumRate != 0); } // AdjustFlowQuanta
VOID DequeuePackets( PDSEQ_PIPE Pipe)
/*++
Routine Description:
Select the next packet(s) to send. The lock must be held upon entry to this routine.
Arguments:
Pipe - Pointer to pipe context information
Return Values:
--*/ { PDSEQ_FLOW Flow; LARGE_INTEGER CurrentTime; PLIST_ENTRY LowPriorityList = &Pipe->ActiveFlows[0]; PLIST_ENTRY CurrentLink; PPACKET_INFO_BLOCK PacketInfo; PNDIS_PACKET Packet; ULONG Priority; ULONG PriorityGroup; PPACKET_INFO_BLOCK PacketsToSend[MAX_DEQUEUED_PACKETS]; ULONG SendingPriority[MAX_DEQUEUED_PACKETS]; ULONG PacketSendCount = 0; ULONG MaxDequeuedPackets = Pipe->MaxOutstandingSends - Pipe->OutstandingSends; ULONG i;
// Need to call this to disable the user APCs after this point.
// Note that the DDK says it should be called at PASSIVE. But it can very well be
// called at DISPATCH.
KeEnterCriticalRegion();
Pipe->Flags |= DSEQ_DEQUEUE;
PsGetCurrentTime(&CurrentTime);
PsAssert(Pipe->MaxOutstandingSends >= Pipe->OutstandingSends);
if (MaxDequeuedPackets > MAX_DEQUEUED_PACKETS) { MaxDequeuedPackets = MAX_DEQUEUED_PACKETS; }
// First update the conformance status of the flows in the lowest priority list
CurrentLink = LowPriorityList->Flink; while (CurrentLink != LowPriorityList) { // Get the flow pointer from the linkage and set new value for CurrentLink
Flow = CONTAINING_RECORD(CurrentLink, DSEQ_FLOW, ActiveLinks); CurrentLink = CurrentLink->Flink;
// If this flow's priority is higher than the DRR priority, then
// it is a candidate for a status change.
if (Flow->Priority > 0) { PacketInfo = (PPACKET_INFO_BLOCK)Flow->PacketQueue.Flink;
if (PacketIsConforming(Flow, PacketInfo, CurrentTime, Pipe->TimerResolution)) {
// Move flow to higher priority list
Flow->DeficitCounter = Flow->Quantum; RemoveEntryList(&Flow->ActiveLinks); InsertTailList(&Pipe->ActiveFlows[Flow->Priority], &Flow->ActiveLinks); } } }
// Now select the next packet(s) to send
for (PriorityGroup = PRIORITY_GROUPS - 1; ((PriorityGroup > 0) && (Pipe->ActiveFlowCount[PriorityGroup] == 0)); PriorityGroup--) ;
Priority = Pipe->StartPriority[PriorityGroup] + Pipe->PriorityLevels[PriorityGroup] - 1;
while ((PacketSendCount < MaxDequeuedPackets) && (Pipe->TotalActiveFlows > 0) && Priority < INTERNAL_PRIORITIES) {
if (!IsListEmpty(&Pipe->ActiveFlows[Priority])) {
// Get first flow in the current list, and get a pointer to the info
// about the first packet
CurrentLink = Pipe->ActiveFlows[Priority].Flink; Flow = CONTAINING_RECORD(CurrentLink, DSEQ_FLOW, ActiveLinks); PacketInfo = (PPACKET_INFO_BLOCK)Flow->PacketQueue.Flink;
if (Pipe->PsFlags & PS_DISABLE_DRR) {
// DRR is disabled. Remove the first packet from the queue
// and send it.
RemoveEntryList(&PacketInfo->SchedulerLinks);
Flow->LastConformanceTime.QuadPart = PacketInfo->ConformanceTime.QuadPart;
if (Priority > 0) { AdjustLastPacketTime(Flow, CurrentTime, Pipe->TimerResolution); } else { Flow->PacketSendTime = CurrentTime; }
InterlockedIncrement( &Pipe->OutstandingSends );
if(Pipe->OutstandingSends > Pipe->Stats.MaxPacketsInNetcard){ Pipe->Stats.MaxPacketsInNetcard = Pipe->OutstandingSends; }
if(gEnableAvgStats) { //
// Track max packets outstanding. This is a measure
// of how congested the media gets. Of course, it
// will be clipped by the MaxOutstandingSends parameter.
// So - for a valid reading, need to set MOS very large.
//
Pipe->Stats.AveragePacketsInNetcard = RunningAverage(Pipe->PacketsInNetcardAveragingArray, Pipe->OutstandingSends); } SendingPriority[PacketSendCount] = Priority; PacketsToSend[PacketSendCount++] = PacketInfo;
// For logging purposes...
PacketInfo->ConformanceTime = Flow->PacketSendTime;
// If the flow has no more packets to send, remove it from the list.
// Otherwise move it to the end of the appropriate list, depending on
// the conformance time of the next packet.
RemoveEntryList(&Flow->ActiveLinks);
if (IsListEmpty(&Flow->PacketQueue)) { Pipe->TotalActiveFlows--; Pipe->ActiveFlowCount[Flow->PriorityGroup]--; } else { PacketInfo = (PPACKET_INFO_BLOCK)Flow->PacketQueue.Flink; Flow->PacketSendTime.QuadPart += (PacketInfo->ConformanceTime.QuadPart - Flow->LastConformanceTime.QuadPart); if (!PacketIsConforming(Flow, PacketInfo, CurrentTime, Pipe->TimerResolution)) { InsertTailList(LowPriorityList, &Flow->ActiveLinks); } else { InsertTailList(&Pipe->ActiveFlows[Priority], &Flow->ActiveLinks); } } } else if (PacketInfo->PacketLength <= Flow->DeficitCounter) {
// DRR is being used and the flow has a large enough deficit counter
// to send the packet. Remove the packet from the queue and send it.
RemoveEntryList(&PacketInfo->SchedulerLinks);
Flow->LastConformanceTime.QuadPart = PacketInfo->ConformanceTime.QuadPart;
if (Priority > 0) { AdjustLastPacketTime(Flow, CurrentTime, Pipe->TimerResolution); } else { Flow->PacketSendTime = CurrentTime; } Flow->DeficitCounter -= PacketInfo->PacketLength; InterlockedIncrement( &Pipe->OutstandingSends );
if(Pipe->OutstandingSends > Pipe->Stats.MaxPacketsInNetcard){ Pipe->Stats.MaxPacketsInNetcard = Pipe->OutstandingSends; }
if(gEnableAvgStats) { //
// Track max packets outstanding. This is a measure
// of how congested the media gets. Of course, it
// will be clipped by the MaxOutstandingSends parameter.
// So - for a valid reading, need to set MOS very large.
//
Pipe->Stats.AveragePacketsInNetcard = RunningAverage(Pipe->PacketsInNetcardAveragingArray, Pipe->OutstandingSends); }
SendingPriority[PacketSendCount] = Priority; PacketsToSend[PacketSendCount++] = PacketInfo;
// For logging purposes...
PacketInfo->ConformanceTime = Flow->PacketSendTime;
// If the flow has no more packets to send, remove it from the list.
// If the flow still has conforming packets to send, leave it at the head
// of the list. If the flow has non-conforming packets to send, move it
// to the lowest priority list. If we are servicing the zero priority
// level, then no conformance checking is necessary.
if (IsListEmpty(&Flow->PacketQueue)) { RemoveEntryList(&Flow->ActiveLinks); Pipe->TotalActiveFlows--; Pipe->ActiveFlowCount[Flow->PriorityGroup]--; } else { PacketInfo = (PPACKET_INFO_BLOCK)Flow->PacketQueue.Flink; Flow->PacketSendTime.QuadPart += (PacketInfo->ConformanceTime.QuadPart - Flow->LastConformanceTime.QuadPart); if ((Priority > 0) && !PacketIsConforming(Flow, PacketInfo, CurrentTime, Pipe->TimerResolution)) {
Flow->DeficitCounter = Flow->Quantum; RemoveEntryList(&Flow->ActiveLinks); InsertTailList(LowPriorityList, &Flow->ActiveLinks); } } } else {
// The packet cannot be sent because the flow's deficit counter
// is too small. Place the flow at the end of the same priority
// queue and increment the flow's deficit counter by its quantum.
Flow->DeficitCounter += Flow->Quantum; RemoveEntryList(&Flow->ActiveLinks); InsertTailList(&Pipe->ActiveFlows[Priority], &Flow->ActiveLinks); } } else { Priority--; } }
//
// We're gonna send these now, which means they're leaving the
// sequencer. Update the stats.
//
Pipe->PacketsInSequencer -= PacketSendCount; Flow->PacketsInSequencer -= PacketSendCount;
if(gEnableAvgStats) { Flow->Stats.AveragePacketsInSequencer = RunningAverage(Flow->PacketsInSeqAveragingArray, Flow->PacketsInSequencer); }
// Send the next group of packets
UNLOCK_PIPE(Pipe); if (PacketSendCount == 0) { PsDbgOut(DBG_CRITICAL_ERROR, DBG_SCHED_DRR, ("PSCHED: No packets selected\n")); } for (i = 0; i < PacketSendCount; i++) { PacketInfo = PacketsToSend[i]; Flow = (PDSEQ_FLOW)PacketInfo->FlowContext;
Packet = PacketInfo->NdisPacket;
//
// The 802.1 priority is already set by the wrapper. But, if the packet
// is non-conforming, then we want to reset it. We also want to clear
// the IP Precedence Bits.
//
if ((SendingPriority[i] == 0)) {
//
// Non conforming packet!
//
NDIS_PACKET_8021Q_INFO VlanPriInfo;
VlanPriInfo.Value = NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, Ieee8021QInfo); VlanPriInfo.TagHeader.UserPriority = PacketInfo->UserPriorityNonConforming; NDIS_PER_PACKET_INFO_FROM_PACKET(Packet, Ieee8021QInfo) = VlanPriInfo.Value;
Flow->Stats.NonconformingPacketsTransmitted ++; Pipe->Stats.NonconformingPacketsTransmitted ++;
//
// Reset the TOS byte for IP Packets.
//
if(NDIS_GET_PACKET_PROTOCOL_TYPE(Packet) == NDIS_PROTOCOL_ID_TCP_IP) {
if(!PacketInfo->IPHdr) {
PacketInfo->IPHdr = GetIpHeader(PacketInfo->IPHeaderOffset, Packet); } SET_TOS_XSUM(Packet, PacketInfo->IPHdr, PacketInfo->TOSNonConforming); } }
PsDbgSched(DBG_INFO, DBG_SCHED_DRR, DRR_SEQUENCER, PKT_DEQUEUE, Flow->PsFlowContext, Packet, PacketInfo->PacketLength, SendingPriority[i], CurrentTime.QuadPart, PacketInfo->ConformanceTime.QuadPart, Pipe->PacketsInSequencer, 0);
if (!(*Pipe->ContextInfo.NextComponent->SubmitPacket)( Pipe->ContextInfo.NextComponentContext, Flow->ContextInfo.NextComponentContext, (PacketInfo->ClassMapContext != NULL) ? ((PPS_CLASS_MAP_CONTEXT)PacketInfo->ClassMapContext)->NextComponentContext : NULL, PacketInfo)) {
(*Pipe->PsProcs->DropPacket)(Pipe->PsPipeContext, Flow->PsFlowContext, Packet, NDIS_STATUS_FAILURE);
} } LOCK_PIPE(Pipe);
Pipe->Flags &= ~DSEQ_DEQUEUE;
// Re-enable the APCs again.
KeLeaveCriticalRegion();
} // DequeuePackets
NDIS_STATUS DrrSeqInitializePipe ( IN HANDLE PsPipeContext, IN PPS_PIPE_PARAMETERS PipeParameters, IN PPS_PIPE_CONTEXT ComponentPipeContext, IN PPS_PROCS PsProcs, IN PPS_UPCALLS Upcalls )
/*++
Routine Description:
Pipe initialization routine for the DRR sequencer.
Arguments:
PsPipeContext - PS pipe context value PipeParameters - Pointer to pipe parameters ComponentPipeContext - Pointer to this component's context area PsProcs - PS's support routines Upcalls - Previous component's upcall table
Return Values:
Status value from next component
--*/ { PDSEQ_PIPE Pipe = (PDSEQ_PIPE)ComponentPipeContext; HANDLE NdisHandle; ULONG i; ULONG PriorityLevel = 0; PS_UPCALLS DrrSeqUpcalls; NDIS_STATUS Status;
NdisAllocateSpinLock(&Pipe->Lock); Pipe->Flags = 0;
//
// Relative Priorities allow us to further subdivide each priority group
// into sub priorities. This does not exist for NonConforming, BestEffort,
// and Qualitative.
//
Pipe->PriorityLevels[PRIORITY_GROUP_NON_CONFORMING] = 1; Pipe->PriorityLevels[PRIORITY_GROUP_BEST_EFFORT] = 1; Pipe->PriorityLevels[PRIORITY_GROUP_CONTROLLED_LOAD] = RELATIVE_PRIORITIES; Pipe->PriorityLevels[PRIORITY_GROUP_GUARANTEED] = RELATIVE_PRIORITIES; Pipe->PriorityLevels[PRIORITY_GROUP_NETWORK_CONTROL] = RELATIVE_PRIORITIES;
InitializeListHead(&Pipe->Flows); for (i = 0; i < INTERNAL_PRIORITIES; i++) { InitializeListHead(&Pipe->ActiveFlows[i]); } for (i = 0; i < PRIORITY_GROUPS; i++) { Pipe->ActiveFlowCount[i] = 0; Pipe->StartPriority[i] = PriorityLevel; PriorityLevel += Pipe->PriorityLevels[i]; }
Pipe->TotalActiveFlows = 0; Pipe->OutstandingSends = 0; NdisZeroMemory(&Pipe->Stats, sizeof(PS_DRRSEQ_STATS)); Pipe->PacketsInSequencer = 0; Pipe->PacketsInSequencerAveragingArray = NULL; Pipe->PacketsInNetcardAveragingArray = NULL; Status = CreateAveragingArray(&Pipe->PacketsInSequencerAveragingArray, SEQUENCER_AVERAGING_ARRAY_SIZE);
if(Status != NDIS_STATUS_SUCCESS) { return(Status); }
Status = CreateAveragingArray(&Pipe->PacketsInNetcardAveragingArray, NETCARD_AVERAGING_ARRAY_SIZE);
if(Status != NDIS_STATUS_SUCCESS) { DeleteAveragingArray(Pipe->PacketsInSequencerAveragingArray);
return(Status); }
Pipe->MinimumQuantum = PipeParameters->MTUSize - PipeParameters->HeaderSize; if (Pipe->MinimumQuantum == 0) { Pipe->MinimumQuantum = DEFAULT_MIN_QUANTUM; } Pipe->Bandwidth = PipeParameters->Bandwidth;
// This will be set to something more realistic when the first flow is created.
Pipe->MinimumRate = (PipeParameters->Bandwidth > 0) ? PipeParameters->Bandwidth : QOS_NOT_SPECIFIED; PsAssert(Pipe->MinimumRate != 0); Pipe->PsFlags = PipeParameters->Flags; Pipe->IsslowFlowCount = 0; Pipe->ConfiguredMaxOutstandingSends = Pipe->MaxOutstandingSends = PipeParameters->MaxOutstandingSends;
// Change the MOS if necessary..
if( ( PipeParameters->MediaType == NdisMediumWan) && ( Pipe->Bandwidth <= MAX_LINK_SPEED_FOR_DRR) ) { Pipe->MaxOutstandingSends = 1; }
(*PsProcs->GetTimerInfo)(&Pipe->TimerResolution); Pipe->TimerResolution /= 2; Pipe->PsPipeContext = PsPipeContext; Pipe->PsProcs = PsProcs;
if(Upcalls) { Pipe->PreviousUpcallsSendComplete = Upcalls->SendComplete; Pipe->PreviousUpcallsSendCompletePipeContext = Upcalls->PipeContext; } else { Pipe->PreviousUpcallsSendComplete = 0; Pipe->PreviousUpcallsSendCompletePipeContext = 0; }
DrrSeqUpcalls.SendComplete = DrrSeqSendComplete; DrrSeqUpcalls.PipeContext = ComponentPipeContext;
/* This put the DrrSeq in the pass-thru mode when the MaxOutStandingSends == MAX */ if( Pipe->MaxOutstandingSends == 0xffffffff ) Pipe->Flags |= DSEQ_PASSTHRU; else Pipe->Flags &= ~ DSEQ_PASSTHRU;
PsDbgOut(DBG_INFO, DBG_SCHED_DRR, ("PSCHED: DrrSeq pipe initialized at %x.\n", &Pipe));
Status = (*Pipe->ContextInfo.NextComponent->InitializePipe)( PsPipeContext, PipeParameters, Pipe->ContextInfo.NextComponentContext, PsProcs, &DrrSeqUpcalls); if (Status != NDIS_STATUS_SUCCESS) { NdisFreeSpinLock(&Pipe->Lock); DeleteAveragingArray(Pipe->PacketsInSequencerAveragingArray); DeleteAveragingArray(Pipe->PacketsInNetcardAveragingArray); }
return Status; } // DrrSeqInitializePipe
//
// Unload routine: currently do nothing
//
void UnloadSequencer() {
}
NDIS_STATUS DrrSeqModifyPipe ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_PIPE_PARAMETERS PipeParameters )
/*++
Routine Description:
Pipe parameter modification routine for the DRR sequencer.
Arguments:
PipeContext - Pointer to this component's pipe context area PipeParameters - Pointer to pipe parameters
Return Values:
Status value from next component
--*/ { PDSEQ_PIPE Pipe = (PDSEQ_PIPE)PipeContext; ULONG MinQuantum = PipeParameters->MTUSize - PipeParameters->HeaderSize; BOOLEAN AdjustQuanta = FALSE; ULONG MinRate = Pipe->MinimumRate;
LOCK_PIPE(Pipe);
(*Pipe->PsProcs->GetTimerInfo)(&Pipe->TimerResolution); Pipe->TimerResolution /= 2;
if ((MinQuantum > 0) && (MinQuantum != Pipe->MinimumQuantum)) { Pipe->MinimumQuantum = MinQuantum; AdjustQuanta = TRUE; } Pipe->Bandwidth = PipeParameters->Bandwidth; Pipe->ConfiguredMaxOutstandingSends = Pipe->MaxOutstandingSends = PipeParameters->MaxOutstandingSends;
// Change the MOS if necessary..
if( ( PipeParameters->MediaType == NdisMediumWan) && ( Pipe->Bandwidth <= MAX_LINK_SPEED_FOR_DRR) ) { Pipe->MaxOutstandingSends = 1; }
// This put the DrrSeq in the pass-thru mode when the MaxOutStandingSends == MAX
if( Pipe->MaxOutstandingSends == 0xffffffff ) { // Make sure not to do this. It could lead to packets queued up in the sequencer being never sent
// [ Pipe->Flags |= DSEQ_PASSTHRU; ]
} else { Pipe->Flags &= ~ DSEQ_PASSTHRU; } if (Pipe->MinimumRate > Pipe->Bandwidth) { MinRate = QOS_NOT_SPECIFIED; AdjustQuanta = TRUE; }
if (AdjustQuanta) { AdjustFlowQuanta(Pipe, MinRate); } UNLOCK_PIPE(Pipe);
return (*PipeContext->NextComponent->ModifyPipe)( PipeContext->NextComponentContext, PipeParameters);
} // DrrSeqModifyPipe
VOID DrrSeqDeletePipe ( IN PPS_PIPE_CONTEXT PipeContext )
/*++
Routine Description:
Pipe removal routine for token bucket conformer.
Arguments:
PipeContext - Pointer to this component's pipe context area
Return Values:
--*/ { PDSEQ_PIPE Pipe = (PDSEQ_PIPE)PipeContext;
DeleteAveragingArray(Pipe->PacketsInSequencerAveragingArray); DeleteAveragingArray(Pipe->PacketsInNetcardAveragingArray);
PsDbgOut(DBG_INFO, DBG_SCHED_DRR, ("PSCHED: DrrSeq pipe deleted\n"));
PsAssert(Pipe->OutstandingSends == 0); NdisFreeSpinLock(&Pipe->Lock);
(*Pipe->ContextInfo.NextComponent->DeletePipe)(Pipe->ContextInfo.NextComponentContext);
} // DrrSeqDeletePipe
NDIS_STATUS DrrSeqCreateFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN HANDLE PsFlowContext, IN PCO_CALL_PARAMETERS CallParameters, IN PPS_FLOW_CONTEXT ComponentFlowContext )
/*++
Routine Description:
Flow creation routine for the DRR sequencer.
Arguments:
PipeContext - Pointer to this component's pipe context area PsFlowContext - PS flow context value CallParameters - Pointer to call parameters for flow ComponentFlowContext - Pointer to this component's flow context area
Return Values:
Status value from next component
--*/ { PDSEQ_PIPE Pipe = (PDSEQ_PIPE)PipeContext; PDSEQ_FLOW Flow = (PDSEQ_FLOW)ComponentFlowContext; SERVICETYPE ServiceType; ULONG ParamsLength; LPQOS_OBJECT_HDR QoSObject; ULONG UserPriority; ULONG i; NDIS_STATUS Status;
ServiceType = CallParameters->CallMgrParameters->Transmit.ServiceType; if ((ServiceType < SERVICETYPE_BESTEFFORT) || (ServiceType > SERVICETYPE_QUALITATIVE)) { return NDIS_STATUS_FAILURE; } Flow->TokenRate = CallParameters->CallMgrParameters->Transmit.TokenRate; Flow->BucketSize = CallParameters->CallMgrParameters->Transmit.TokenBucketSize; InitializeListHead(&Flow->PacketQueue); PsGetCurrentTime(&Flow->PacketSendTime); Flow->LastConformanceTime = Flow->PacketSendTime; Flow->PsFlowContext = PsFlowContext; Flow->State = DRRSEQ_FLOW_CREATED;
// Set the flow's priority group based on service type.
switch (ServiceType) { case SERVICETYPE_CONTROLLEDLOAD: Flow->PriorityGroup = PRIORITY_GROUP_CONTROLLED_LOAD; break; case SERVICETYPE_GUARANTEED: Flow->PriorityGroup = PRIORITY_GROUP_GUARANTEED; break; case SERVICETYPE_NETWORK_CONTROL: Flow->PriorityGroup = PRIORITY_GROUP_NETWORK_CONTROL; break; case SERVICETYPE_QUALITATIVE: default: Flow->PriorityGroup = PRIORITY_GROUP_BEST_EFFORT; }
Flow->Flags = 0;
// Save the flow in a list so that quantum values can be adjusted if
// a new flow is added with a smaller rate than the existing flows.
LOCK_PIPE(Pipe);
InsertTailList(&Pipe->Flows, &Flow->Links);
// If this flow's rate is smaller than the rate assigned to any existing
// flow, adjust the other flow's quantum values accordingly.
if (ServiceType == SERVICETYPE_BESTEFFORT || ServiceType == SERVICETYPE_NETWORK_CONTROL || ServiceType == SERVICETYPE_QUALITATIVE) { Flow->Quantum = Pipe->MinimumQuantum; } else if (Flow->TokenRate < Pipe->MinimumRate) { AdjustFlowQuanta(Pipe, Flow->TokenRate); } else { Flow->Quantum = (ULONG)((ULONGLONG)(Flow->TokenRate * Pipe->MinimumQuantum) / Pipe->MinimumRate); PsAssert((LONG)Flow->Quantum > 0); } Flow->DeficitCounter = 0;
// If this is a RAS-ISSLOW flow, need to set the MOS back to whatever requested by the user..
if( ((PGPC_CLIENT_VC)(PsFlowContext))->Flags & GPC_ISSLOW_FLOW) { Pipe->MaxOutstandingSends = Pipe->ConfiguredMaxOutstandingSends; Pipe->IsslowFlowCount++; Flow->Flags |= GPC_ISSLOW_FLOW; }
UNLOCK_PIPE(Pipe);
// Now set default values for UserPriority
UserPriority = (Pipe->PriorityLevels[Flow->PriorityGroup] - 1) / 2;
// Look for the priority object and traffic class in the call manager specific parameters
ParamsLength = CallParameters->CallMgrParameters->CallMgrSpecific.Length; if (CallParameters->CallMgrParameters->CallMgrSpecific.ParamType == PARAM_TYPE_GQOS_INFO) {
QoSObject = (LPQOS_OBJECT_HDR)CallParameters->CallMgrParameters->CallMgrSpecific.Parameters; while ((ParamsLength > 0) && (QoSObject->ObjectType != QOS_OBJECT_END_OF_LIST)) { if (QoSObject->ObjectType == QOS_OBJECT_PRIORITY) { UserPriority = ((LPQOS_PRIORITY)QoSObject)->SendPriority; Flow->Flags |= FLOW_USER_PRIORITY; } ParamsLength -= QoSObject->ObjectLength; QoSObject = (LPQOS_OBJECT_HDR)((UINT_PTR)QoSObject + QoSObject->ObjectLength); } }
Flow->UserPriority = UserPriority; if (UserPriority < Pipe->PriorityLevels[Flow->PriorityGroup]) { Flow->Priority = Pipe->StartPriority[Flow->PriorityGroup] + UserPriority; } else { Flow->Priority = Pipe->StartPriority[Flow->PriorityGroup] + Pipe->PriorityLevels[Flow->PriorityGroup] - 1; }
Flow->PacketsInSequencer = 0; NdisZeroMemory(&Flow->Stats, sizeof(PS_DRRSEQ_STATS));
Status = CreateAveragingArray(&Flow->PacketsInSeqAveragingArray, SEQUENCER_FLOW_AVERAGING_ARRAY_SIZE);
if(Status != NDIS_STATUS_SUCCESS){ LOCK_PIPE(Pipe); RemoveEntryList(&Flow->Links); if(Flow->TokenRate == Pipe->MinimumRate) { AdjustFlowQuanta(Pipe, QOS_NOT_SPECIFIED); }
UNLOCK_PIPE(Pipe); return(Status); }
PsDbgOut(DBG_INFO, DBG_SCHED_DRR, ("PSCHED: DrrSeq flow created. Quantum = %u, Priority = %u\n", Flow->Quantum, Flow->Priority));
Status = (*Pipe->ContextInfo.NextComponent->CreateFlow)( Pipe->ContextInfo.NextComponentContext, PsFlowContext, CallParameters, Flow->ContextInfo.NextComponentContext);
if(Status != NDIS_STATUS_SUCCESS) { DeleteAveragingArray(Flow->PacketsInSeqAveragingArray); LOCK_PIPE(Pipe); RemoveEntryList(&Flow->Links); if(Flow->TokenRate == Pipe->MinimumRate) { AdjustFlowQuanta(Pipe, QOS_NOT_SPECIFIED); }
UNLOCK_PIPE(Pipe); }
return Status;
} // DrrSeqCreateFlow
NDIS_STATUS DrrSeqModifyFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext, IN PCO_CALL_PARAMETERS CallParameters )
/*++
Routine Description:
Flow modification routine for the DRR sequencer.
Arguments:
PipeContext - Pointer to this component's pipe context area FlowContext - Pointer to this component's flow context area CallParameters - Pointer to call parameters for flow
Return Values:
Status value from next component
--*/ { PDSEQ_PIPE Pipe = (PDSEQ_PIPE)PipeContext; PDSEQ_FLOW Flow = (PDSEQ_FLOW)FlowContext; SERVICETYPE ServiceType; ULONG TokenRate; ULONG ParamsLength; LPQOS_OBJECT_HDR QoSObject; LPQOS_PRIORITY PriorityObject = NULL; ULONG i; ULONG OldPriorityGroup; ULONG OldRate; ULONG OldPriority; PPACKET_INFO_BLOCK PacketInfo; LARGE_INTEGER CurrentTime;
ServiceType = CallParameters->CallMgrParameters->Transmit.ServiceType; if ((ServiceType != SERVICETYPE_NOCHANGE) && ((ServiceType < SERVICETYPE_BESTEFFORT) || (ServiceType > SERVICETYPE_QUALITATIVE))) { return NDIS_STATUS_FAILURE; }
// Look for the priority and traffic class objects in the call manager
// specific parameters, and save the pointers if found.
ParamsLength = CallParameters->CallMgrParameters->CallMgrSpecific.Length; if (CallParameters->CallMgrParameters->CallMgrSpecific.ParamType == PARAM_TYPE_GQOS_INFO) {
QoSObject = (LPQOS_OBJECT_HDR)CallParameters->CallMgrParameters->CallMgrSpecific.Parameters; while ((ParamsLength > 0) && (QoSObject->ObjectType != QOS_OBJECT_END_OF_LIST)) { if (QoSObject->ObjectType == QOS_OBJECT_PRIORITY) { PriorityObject = (LPQOS_PRIORITY)QoSObject; } ParamsLength -= QoSObject->ObjectLength; QoSObject = (LPQOS_OBJECT_HDR)((UINT_PTR)QoSObject + QoSObject->ObjectLength); } }
LOCK_PIPE(Pipe);
OldPriorityGroup = Flow->PriorityGroup; OldPriority = Flow->Priority;
if (ServiceType != SERVICETYPE_NOCHANGE) { // Set the flow's priority group based on service type.
switch (ServiceType) { case SERVICETYPE_CONTROLLEDLOAD: Flow->PriorityGroup = PRIORITY_GROUP_CONTROLLED_LOAD; break; case SERVICETYPE_GUARANTEED: Flow->PriorityGroup = PRIORITY_GROUP_GUARANTEED; break; case SERVICETYPE_NETWORK_CONTROL: Flow->PriorityGroup = PRIORITY_GROUP_NETWORK_CONTROL; break; case SERVICETYPE_QUALITATIVE: default: Flow->PriorityGroup = PRIORITY_GROUP_BEST_EFFORT; }
TokenRate = CallParameters->CallMgrParameters->Transmit.TokenRate;
OldRate = Flow->TokenRate; if ((TokenRate != OldRate) || (OldPriorityGroup != Flow->PriorityGroup)) {
// If this flow's rate is smaller than the rate assigned to any existing
// flow, adjust the other flows' quantum values accordingly. If this flow's
// old rate was equal to the minimum rate, then locate the new minimum rate and
// adjust the other flows' quantum values accordingly.
Flow->TokenRate = TokenRate; if ((OldRate == Pipe->MinimumRate) && (OldPriorityGroup > PRIORITY_GROUP_BEST_EFFORT) && (OldPriorityGroup != PRIORITY_GROUP_NETWORK_CONTROL)) { AdjustFlowQuanta(Pipe, QOS_NOT_SPECIFIED); } else if (Flow->PriorityGroup <= PRIORITY_GROUP_BEST_EFFORT || Flow->PriorityGroup == PRIORITY_GROUP_NETWORK_CONTROL) { Flow->Quantum = Pipe->MinimumQuantum; } else if (TokenRate < Pipe->MinimumRate) { AdjustFlowQuanta(Pipe, TokenRate); } else { PsAssert(Pipe->MinimumRate != 0); Flow->Quantum = (ULONG)((ULONGLONG)(TokenRate * Pipe->MinimumQuantum) / Pipe->MinimumRate); PsAssert((LONG)Flow->Quantum > 0); }
}
Flow->BucketSize = CallParameters->CallMgrParameters->Transmit.TokenBucketSize; }
// Now set the new values for UserPriority and Priority
if (PriorityObject != NULL) { Flow->UserPriority = PriorityObject->SendPriority; Flow->Flags |= FLOW_USER_PRIORITY; } else if ((Flow->Flags & FLOW_USER_PRIORITY) == 0) { Flow->UserPriority = (Pipe->PriorityLevels[Flow->PriorityGroup] - 1) / 2; }
if (Flow->UserPriority < Pipe->PriorityLevels[Flow->PriorityGroup]) { Flow->Priority = Pipe->StartPriority[Flow->PriorityGroup] + Flow->UserPriority; } else { Flow->Priority = Pipe->StartPriority[Flow->PriorityGroup] + Pipe->PriorityLevels[Flow->PriorityGroup] - 1; }
// Move the flow to the proper priority list if necessary
if ((Flow->Priority != OldPriority) && !IsListEmpty(&Flow->PacketQueue)) { Pipe->ActiveFlowCount[OldPriorityGroup]--; RemoveEntryList(&Flow->ActiveLinks); PacketInfo = (PPACKET_INFO_BLOCK)Flow->PacketQueue.Flink; PsGetCurrentTime(&CurrentTime); Flow->DeficitCounter = Flow->Quantum; Pipe->ActiveFlowCount[Flow->PriorityGroup]++; if (!PacketIsConforming(Flow, PacketInfo, CurrentTime, Pipe->TimerResolution)) { InsertTailList(&Pipe->ActiveFlows[0], &Flow->ActiveLinks); } else { InsertTailList(&Pipe->ActiveFlows[Flow->Priority], &Flow->ActiveLinks); } }
UNLOCK_PIPE(Pipe);
PsDbgOut(DBG_INFO, DBG_SCHED_DRR, ("PSCHED: DrrSeq flow modified. Quantum = %u, Priority = %u\n", Flow->Quantum, Flow->Priority));
return (*Pipe->ContextInfo.NextComponent->ModifyFlow)( Pipe->ContextInfo.NextComponentContext, Flow->ContextInfo.NextComponentContext, CallParameters);
} // DrrSeqModifyFlow
VOID DrrSeqDeleteFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext )
/*++
Routine Description:
Flow removal routine for the DRR sequencer.
Arguments:
PipeContext - Pointer to this component's pipe context area FlowContext - Pointer to this component's flow context area
Return Values:
--*/ { PDSEQ_PIPE Pipe = (PDSEQ_PIPE)PipeContext; PDSEQ_FLOW Flow = (PDSEQ_FLOW)FlowContext; PPACKET_INFO_BLOCK PacketInfo; PNDIS_PACKET Packet; LIST_ENTRY DropList;
InitializeListHead(&DropList);
LOCK_PIPE(Pipe);
if( (Flow->State & DRRSEQ_FLOW_DELETED) != 0) { UNLOCK_PIPE(Pipe); goto DELETE_SEQ_FLOW; } Flow->State = DRRSEQ_FLOW_DELETED;
RemoveEntryList(&Flow->Links);
if (!IsListEmpty(&Flow->PacketQueue)) { // Remove flow from active list
RemoveEntryList(&Flow->ActiveLinks); Pipe->ActiveFlowCount[Flow->PriorityGroup]--; Pipe->TotalActiveFlows--;
while (!IsListEmpty(&Flow->PacketQueue)) {
// Drop any packets that remain queued for this flow.
PacketInfo = (PPACKET_INFO_BLOCK)RemoveHeadList(&Flow->PacketQueue); InsertTailList(&DropList, &PacketInfo->SchedulerLinks); } }
if (Flow->TokenRate == Pipe->MinimumRate) { AdjustFlowQuanta(Pipe, QOS_NOT_SPECIFIED); }
if( Flow->Flags & GPC_ISSLOW_FLOW) { // If this is an ISSLOW flow, we have one less now.
Pipe->IsslowFlowCount--;
if(Pipe->IsslowFlowCount == 0) { // If there are no more ISSLOW flows, turn DRR back on.
Pipe->MaxOutstandingSends = 1; } }
UNLOCK_PIPE(Pipe);
while (!IsListEmpty(&DropList)) { PacketInfo = (PPACKET_INFO_BLOCK)RemoveHeadList(&DropList); Packet = PacketInfo->NdisPacket;
(*Pipe->PsProcs->DropPacket)(Pipe->PsPipeContext, Flow->PsFlowContext, Packet, NDIS_STATUS_FAILURE); }
DELETE_SEQ_FLOW:
DeleteAveragingArray(Flow->PacketsInSeqAveragingArray);
PsDbgOut(DBG_INFO, DBG_SCHED_DRR, ("PSCHED: DrrSeq flow deleted\n"));
(*Pipe->ContextInfo.NextComponent->DeleteFlow)( Pipe->ContextInfo.NextComponentContext, Flow->ContextInfo.NextComponentContext); }
VOID DrrSeqEmptyFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext )
/*++
Routine Description:
Flow removal routine for the DRR sequencer.
Arguments:
PipeContext - Pointer to this component's pipe context area FlowContext - Pointer to this component's flow context area
Return Values:
--*/ { PDSEQ_PIPE Pipe = (PDSEQ_PIPE)PipeContext; PDSEQ_FLOW Flow = (PDSEQ_FLOW)FlowContext; PPACKET_INFO_BLOCK PacketInfo; PNDIS_PACKET Packet; LIST_ENTRY DropList;
InitializeListHead(&DropList);
LOCK_PIPE(Pipe);
Flow->State = DRRSEQ_FLOW_DELETED;
RemoveEntryList(&Flow->Links);
if (!IsListEmpty(&Flow->PacketQueue)) { // Remove flow from active list
RemoveEntryList(&Flow->ActiveLinks); Pipe->ActiveFlowCount[Flow->PriorityGroup]--; Pipe->TotalActiveFlows--;
while (!IsListEmpty(&Flow->PacketQueue)) {
// Drop any packets that remain queued for this flow.
PacketInfo = (PPACKET_INFO_BLOCK)RemoveHeadList(&Flow->PacketQueue); InsertTailList(&DropList, &PacketInfo->SchedulerLinks); } }
if (Flow->TokenRate == Pipe->MinimumRate) { AdjustFlowQuanta(Pipe, QOS_NOT_SPECIFIED); }
if( Flow->Flags & GPC_ISSLOW_FLOW) { // If this is an ISSLOW flow, we have one less now.
Pipe->IsslowFlowCount--;
if(Pipe->IsslowFlowCount == 0) { // If there are no more ISSLOW flows, turn DRR back on.
Pipe->MaxOutstandingSends = 1; } }
UNLOCK_PIPE(Pipe);
while (!IsListEmpty(&DropList)) { PacketInfo = (PPACKET_INFO_BLOCK)RemoveHeadList(&DropList); Packet = PacketInfo->NdisPacket;
(*Pipe->PsProcs->DropPacket)(Pipe->PsPipeContext, Flow->PsFlowContext, Packet, NDIS_STATUS_FAILURE); }
PsDbgOut(DBG_INFO, DBG_SCHED_DRR, ("PSCHED: DrrSeq flow emptied\n"));
(*Pipe->ContextInfo.NextComponent->EmptyFlow)( Pipe->ContextInfo.NextComponentContext, Flow->ContextInfo.NextComponentContext); }
static NDIS_STATUS DrrSeqCreateClassMap ( IN PPS_PIPE_CONTEXT PipeContext, IN HANDLE PsClassMapContext, IN PTC_CLASS_MAP_FLOW ClassMap, IN PPS_CLASS_MAP_CONTEXT ComponentClassMapContext) { PDSEQ_PIPE Pipe = (PDSEQ_PIPE)PipeContext; return (*Pipe->ContextInfo.NextComponent->CreateClassMap)( Pipe->ContextInfo.NextComponentContext, PsClassMapContext, ClassMap, ComponentClassMapContext->NextComponentContext); }
static NDIS_STATUS DrrSeqDeleteClassMap ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_CLASS_MAP_CONTEXT ComponentClassMapContext) { PDSEQ_PIPE Pipe = (PDSEQ_PIPE)PipeContext; return (*Pipe->ContextInfo.NextComponent->DeleteClassMap)( Pipe->ContextInfo.NextComponentContext, ComponentClassMapContext->NextComponentContext); }
BOOLEAN DrrSeqSubmitPacket ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext, IN PPS_CLASS_MAP_CONTEXT ClassMapContext, IN PPACKET_INFO_BLOCK PacketInfo )
/*++
Routine Description:
Packet submission routine for the DRR sequencer.
Arguments:
PipeContext - Pointer to this component's pipe context area FlowContext - Pointer to this component's flow context area Packet - Pointer to packet
Return Values:
Always returns TRUE
--*/ { PDSEQ_PIPE Pipe = (PDSEQ_PIPE)PipeContext; PDSEQ_FLOW Flow = (PDSEQ_FLOW)FlowContext; LARGE_INTEGER CurrentTime; PNDIS_PACKET Packet = PacketInfo->NdisPacket; BOOLEAN FlowInactive; PGPC_CLIENT_VC Vc = Flow->PsFlowContext;
if(Pipe->Flags & DSEQ_PASSTHRU) { InterlockedIncrement( &Pipe->OutstandingSends );
if(Pipe->OutstandingSends > Pipe->Stats.MaxPacketsInNetcard){ Pipe->Stats.MaxPacketsInNetcard = Pipe->OutstandingSends; }
if(gEnableAvgStats) { //
// Track max packets outstanding. This is a measure
// of how congested the media gets. Of course, it
// will be clipped by the MaxOutstandingSends parameter.
// So - for a valid reading, need to set MOS very large.
//
Pipe->Stats.AveragePacketsInNetcard = RunningAverage(Pipe->PacketsInNetcardAveragingArray, Pipe->OutstandingSends); }
//
// Note: The 802.1p is already set by the wrapper
//
if (!(*Pipe->ContextInfo.NextComponent->SubmitPacket)( Pipe->ContextInfo.NextComponentContext, Flow->ContextInfo.NextComponentContext, (ClassMapContext != NULL) ? ClassMapContext->NextComponentContext : NULL, PacketInfo)) {
(*Pipe->PsProcs->DropPacket)(Pipe->PsPipeContext, Flow->PsFlowContext, Packet, NDIS_STATUS_FAILURE);
}
return TRUE; }
LOCK_PIPE(Pipe);
if (Flow->State == DRRSEQ_FLOW_DELETED) { UNLOCK_PIPE(Pipe); return FALSE; }
//
// On WanLinks, when we are doing DRR, we need to put a maximum on the queue-limit.
// NDISWAN has a queue limit of 132KBytes on a modem link; So, we'll limit it to 120
// packets by default.
//
if( ( Pipe->Bandwidth <= MAX_LINK_SPEED_FOR_DRR) && ( Pipe->MaxOutstandingSends == 1) && ( Pipe->PacketsInSequencer >= 120) ) { UNLOCK_PIPE( Pipe); return FALSE; }
//
// There is one case where the PIPE might go away because the send-complete happened
// on a VC belonging to it before the send returned. So, to prevent that, we add a
// Ref on that VC and take it out just before the send returns.
//
// Add a Ref.
InterlockedIncrement(&Vc->RefCount);
PacketInfo->FlowContext = FlowContext; PacketInfo->ClassMapContext = ClassMapContext;
Pipe->PacketsInSequencer++;
if(Pipe->PacketsInSequencer > Pipe->Stats.MaxPacketsInSequencer){ Pipe->Stats.MaxPacketsInSequencer = Pipe->PacketsInSequencer; }
Flow->PacketsInSequencer++; if (Flow->PacketsInSequencer > Flow->Stats.MaxPacketsInSequencer){ Flow->Stats.MaxPacketsInSequencer = Flow->PacketsInSequencer; }
if(gEnableAvgStats) { //
// Track packets in the sequencer at any time.
//
Pipe->Stats.AveragePacketsInSequencer = RunningAverage(Pipe->PacketsInSequencerAveragingArray, Pipe->PacketsInSequencer);
Flow->Stats.AveragePacketsInSequencer = RunningAverage(Flow->PacketsInSeqAveragingArray, Flow->PacketsInSequencer);
}
FlowInactive = IsListEmpty(&Flow->PacketQueue); InsertTailList(&Flow->PacketQueue, &PacketInfo->SchedulerLinks);
PsGetCurrentTime(&CurrentTime);
PsDbgSched(DBG_INFO, DBG_SCHED_DRR, DRR_SEQUENCER, PKT_ENQUEUE, Flow->PsFlowContext, Packet, PacketInfo->PacketLength, Flow->Priority, CurrentTime.QuadPart, PacketInfo->ConformanceTime.QuadPart, Pipe->PacketsInSequencer, 0);
if (FlowInactive) { Flow->PacketSendTime.QuadPart += (PacketInfo->ConformanceTime.QuadPart - Flow->LastConformanceTime.QuadPart);
Flow->DeficitCounter = Flow->Quantum; Pipe->TotalActiveFlows++; Pipe->ActiveFlowCount[Flow->PriorityGroup]++; if (!PacketIsConforming(Flow, PacketInfo, CurrentTime, Pipe->TimerResolution)) { InsertTailList(&Pipe->ActiveFlows[0], &Flow->ActiveLinks); } else { InsertTailList(&Pipe->ActiveFlows[Flow->Priority], &Flow->ActiveLinks); } }
while ((Pipe->TotalActiveFlows > 0) && (Pipe->OutstandingSends < Pipe->MaxOutstandingSends) && ((Pipe->Flags & DSEQ_DEQUEUE) == 0)) {
DequeuePackets(Pipe); }
UNLOCK_PIPE(Pipe);
// Take out the ref.
DerefClVc(Vc);
return TRUE;
} // DrrSeqSubmitPacket
VOID DrrSeqSendComplete ( IN PPS_PIPE_CONTEXT PipeContext, IN PNDIS_PACKET Packet )
/*++
Routine Description:
Send complete handler for the DRR sequencer.
Arguments:
PipeContext - Pointer to this component's pipe context area FlowContext - Pointer to this component's flow context area Packet - Packet that has completed sending
Return Values:
--*/ { PDSEQ_PIPE Pipe = (PDSEQ_PIPE)PipeContext;
InterlockedDecrement( &Pipe->OutstandingSends);
// Need to do this only if the sequencer is not in the bypass mode //
if( (Pipe->Flags & DSEQ_PASSTHRU) == 0) { LOCK_PIPE(Pipe); PsAssert((LONG)Pipe->OutstandingSends >= 0);
while ((Pipe->TotalActiveFlows > 0) && (Pipe->OutstandingSends < Pipe->MaxOutstandingSends) && ((Pipe->Flags & DSEQ_DEQUEUE) == 0)) {
DequeuePackets(Pipe); }
UNLOCK_PIPE(Pipe); }
//
// Call the previous upcalls (if any)
//
if(Pipe->PreviousUpcallsSendComplete) { (*Pipe->PreviousUpcallsSendComplete)(Pipe->PreviousUpcallsSendCompletePipeContext, Packet); }
} // DrrSeqSendComplete
VOID DrrSetInformation ( IN PPS_PIPE_CONTEXT ComponentPipeContext, IN PPS_FLOW_CONTEXT ComponentFlowContext, IN NDIS_OID Oid, IN ULONG Len, IN PVOID Data) { PDSEQ_PIPE Pipe = (PDSEQ_PIPE)ComponentPipeContext; PDSEQ_FLOW Flow = (PDSEQ_FLOW)ComponentFlowContext;
switch(Oid) { case OID_QOS_STATISTICS_BUFFER: if(Flow) { NdisZeroMemory(&Flow->Stats, sizeof(PS_DRRSEQ_STATS)); } else { NdisZeroMemory(&Pipe->Stats, sizeof(PS_DRRSEQ_STATS)); } break; default: break; } (*Pipe->ContextInfo.NextComponent->SetInformation)( Pipe->ContextInfo.NextComponentContext, (Flow)?Flow->ContextInfo.NextComponentContext:0, Oid, Len, Data); }
VOID DrrQueryInformation ( IN PPS_PIPE_CONTEXT ComponentPipeContext, IN PPS_FLOW_CONTEXT ComponentFlowContext, IN NDIS_OID Oid, IN ULONG Len, IN PVOID Data, IN OUT PULONG BytesWritten, IN OUT PULONG BytesNeeded, IN OUT PNDIS_STATUS Status) { PDSEQ_PIPE Pipe = (PDSEQ_PIPE)ComponentPipeContext; PDSEQ_FLOW Flow = (PDSEQ_FLOW)ComponentFlowContext; PS_COMPONENT_STATS Stats; ULONG Size; ULONG RemainingLength;
switch(Oid) { case OID_QOS_STATISTICS_BUFFER:
Size = sizeof(PS_DRRSEQ_STATS) + FIELD_OFFSET(PS_COMPONENT_STATS, Stats);
if(*Status == NDIS_STATUS_SUCCESS) { //
// The previous component has succeeded - Let us
// see if we can write the data
//
RemainingLength = Len - *BytesWritten; if(RemainingLength < Size) {
*Status = NDIS_STATUS_BUFFER_TOO_SHORT;
*BytesNeeded = Size + *BytesWritten;
*BytesWritten = 0;
} else {
PPS_COMPONENT_STATS Cstats = (PPS_COMPONENT_STATS) Data;
*BytesWritten += Size; *BytesNeeded = 0;
if(Flow) {
Cstats->Type = PS_COMPONENT_DRRSEQ; Cstats->Length = sizeof(PS_DRRSEQ_STATS); NdisMoveMemory(&Cstats->Stats, &Flow->Stats, sizeof(PS_DRRSEQ_STATS)); } else {
Cstats->Type = PS_COMPONENT_DRRSEQ; Cstats->Length = sizeof(PS_DRRSEQ_STATS); NdisMoveMemory(&Cstats->Stats, &Pipe->Stats, sizeof(PS_DRRSEQ_STATS));
}
//
// Advance Data so that the next component can update its stats
//
Data = (PVOID) ((PUCHAR)Data + Size); } } else {
*BytesNeeded += Size; *BytesWritten = 0; }
break; default:
break; }
(*Pipe->ContextInfo.NextComponent->QueryInformation)( Pipe->ContextInfo.NextComponentContext, (Flow)?Flow->ContextInfo.NextComponentContext : 0, Oid, Len, Data, BytesWritten, BytesNeeded, Status); }
|