|
|
/*++
Copyright (c) 1996-1999 Microsoft Corporation
Module Name:
Conformr.c
Abstract:
Token Bucket Conformer. This module is a scheduling component that assigns conformance times to packets, based on the token bucket algorithm.
Author: Intel->YoramB->RajeshSu->SanjayKa.
Environment:
Kernel Mode
Revision History:
--*/
#include "psched.h"
#pragma hdrstop
#ifdef QUEUE_LIMIT
ULONG gPhysMemSize; // size of physical memory (in MB), used for shaper queue limit default
#endif // QUEUE_LIMIT
//
// For maintaining shaper Pipe & Flow Stats.
//
#define SHAPER_AVERAGING_ARRAY_SIZE 256
#define SHAPER_FLOW_AVERAGING_ARRAY_SIZE 256
// The conformer's pipe information
typedef struct _TBC_PIPE {
// ContextInfo - Generic context info
// MaxPacket - Maximum packet size for pipe
// PsPipeContext - PS's pipe context value
// DropPacket - PS's drop packet routine
// HeaderLength - Length of MAC header for this pipe
// ControlledLoadMode - Default mode for non-conforming traffic from
// controlled load flows
// GuaranteedMode - Default mode for non-conforming traffic from
// guaranteed service flows
// IntermediateSystem - TRUE if "IS" mode should be used for implementing discard semantics
// Stats - Per Pipe stats.
PS_PIPE_CONTEXT ContextInfo;
PS_CONFORMER_STATS cStats; PS_SHAPER_STATS sStats; PRUNNING_AVERAGE PacketsInShaperAveragingArray; ULONG PacketsInShaper;
ULONG MaxPacket; LIST_ENTRY ActiveFlows; ULONG TimerStatus; ULONG TimerResolution; HANDLE PsPipeContext; PPS_PROCS PsProcs; ULONG HeaderLength; ULONG ControlledLoadMode; ULONG GuaranteedMode; ULONG NetworkControlMode; ULONG Qualitative; ULONG IntermediateSystem;
ULONG TimerUnloadFlag; NDIS_EVENT TimerUnloadEvent;
// Need this to figure out the timer-wheel size //
NDIS_MEDIUM MediaType;
// Timer wheel parameters //
PVOID pTimerWheel; ULONG TimerWheelShift; NDIS_MINIPORT_TIMER Timer; NDIS_SPIN_LOCK Lock;
ULONG SetSlotValue; LARGE_INTEGER SetTimerValue; LARGE_INTEGER ExecTimerValue; ULONG ExecSlot;
} TBC_PIPE, *PTBC_PIPE;
#define TIMER_UNINITIALIZED 0
#define TIMER_INACTIVE 1
#define TIMER_SET 2
#define TIMER_PROC_EXECUTING 3
typedef enum _FLOW_STATE { TS_FLOW_CREATED = 1, TS_FLOW_DELETED } FLOW_STATE;
// The conformer's flow information
typedef struct _TBC_FLOW {
// ContextInfo - Generic context info
// Lock - Protects flow data
// TokenRate - TokenRate from generic QoS
// Capacity - TokenBucketSize from generic QoS
// PeakRate - PeakBandwidth from generic QoS
// MinPolicedUnit - MinimumPolicedUnit from generic QoS
// Mode - Flow S/D mode
// NoConformance - Indicates whether flow is exempt from conformance algorithm
// LastConformanceTime - Absolute tb conformance time of last non-discarded packet
// PeakConformanceTime - Earliest time next packet can be sent, based on peak rate
// LastConformanceCredits - Number of credits at LastConformanceTime
// PsFlowContext - PS's flow context value
// Stats - Per flow stats.
PS_FLOW_CONTEXT ContextInfo; NDIS_SPIN_LOCK Lock; ULONG Flags; LIST_ENTRY Links; ULONG Mode; ULONG Shape; LIST_ENTRY PacketQueue; LARGE_INTEGER FlowEligibilityTime; ULONG LoopCount; ULONG TokenRate; ULONG Capacity; ULONG PeakRate; ULONG MinPolicedUnit; ULONG NoConformance; LARGE_INTEGER LastConformanceTime; LARGE_INTEGER PeakConformanceTime; ULONG LastConformanceCredits; HANDLE PsFlowContext; #ifdef QUEUE_LIMIT
ULONG QueueSize; ULONG QueueSizeLimit; ULONG DropOverLimitPacketsFromHead; ULONG UseDefaultQueueLimit; #endif // QUEUE_LIMIT
PS_CONFORMER_STATS cStats; PS_SHAPER_STATS sStats; ULONG PacketsInShaper; PRUNNING_AVERAGE PacketsInShaperAveragingArray;
FLOW_STATE State; } TBC_FLOW, *PTBC_FLOW;
// Macros used during token bucket conformance calculation
#define EARNED_CREDITS(_t,_r) ((ULONG)(( (_t) * (_r) ) / OS_TIME_SCALE))
#define TIME_TO_EARN_CREDITS(_c,_r) (((LONGLONG)(_c) * OS_TIME_SCALE) / (_r) )
#define TIME_TO_SEND(_c,_r) (((LONGLONG)(_c) * OS_TIME_SCALE) / (_r) )
#define PACKET_IS_CONFORMING(_ttime, _curtime, _r) \
( ((_ttime).QuadPart - (_curtime).QuadPart) <= (_r) )
#define LOCK_FLOW(_f) NdisAcquireSpinLock(&(_f)->Lock)
#define UNLOCK_FLOW(_f) NdisReleaseSpinLock(&(_f)->Lock)
#define PacketIsEligible(_pktinfo, _flow, _curtime, _r) \
( (_pktinfo)->DelayTime.QuadPart <= ((_curtime).QuadPart + (_r)) )
#define FlowIsEligible(_flow, _curtime, _r) \
( (_flow)->FlowEligibilityTime.QuadPart <= ((_curtime).QuadPart + (_r)) )
#define LOCK_PIPE(_p) NdisAcquireSpinLock(&(_p)->Lock)
#define UNLOCK_PIPE(_p) NdisReleaseSpinLock(&(_p)->Lock)
//
// Define the maximum number of time for which a packet can live in the shaper. If a packet becomes conformant at
// a time that is > this value, it gets discarded. This is to prevent apps from queueing up packets in the shaper
// for a very long time (and exiting immediately causing a bugcheck when the app terminates after 5 min.). Note that
// this applies only to shape mode flows.
//
#define MAX_TIME_FOR_PACKETS_IN_SHAPER 250000
#define TIMER_WHEEL_QTY 8 // in ms //
#define TIMER_WHEEL_SHIFT 3
#define MSIN100NS 10000 // these many ticks are there in 1 ms //
#define WAN_TIMER_WHEEL_SHIFT 8 // how many TIMER_WHEEL_QTY will it have? //
#define LAN_TIMER_WHEEL_SHIFT 11 // how many TIMER_WHEEL_QTY will it have? //
#define DUMMY_SLOT (0xffffffff)
#define DUMMY_TIME (0)
/* External */
/* Static */
/* Forward */
NDIS_STATUS TbcInitializePipe ( IN HANDLE PsPipeContext, IN PPS_PIPE_PARAMETERS PipeParameters, IN PPS_PIPE_CONTEXT ComponentPipeContext, IN PPS_PROCS PsProcs, IN PPS_UPCALLS Upcalls );
NDIS_STATUS TbcModifyPipe ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_PIPE_PARAMETERS PipeParameters );
VOID TbcDeletePipe ( IN PPS_PIPE_CONTEXT PipeContext );
NDIS_STATUS TbcCreateFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN HANDLE PsFlowContext, IN PCO_CALL_PARAMETERS CallParameters, IN PPS_FLOW_CONTEXT ComponentFlowContext );
NDIS_STATUS TbcModifyFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext, IN PCO_CALL_PARAMETERS CallParameters );
VOID TbcDeleteFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext );
VOID TbcEmptyFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext );
NDIS_STATUS TbcCreateClassMap ( IN PPS_PIPE_CONTEXT PipeContext, IN HANDLE PsClassMapContext, IN PTC_CLASS_MAP_FLOW ClassMap, IN PPS_CLASS_MAP_CONTEXT ComponentClassMapContext);
NDIS_STATUS TbcDeleteClassMap ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_CLASS_MAP_CONTEXT ComponentClassMapContext);
BOOLEAN TbcSubmitPacket ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext, IN PPS_CLASS_MAP_CONTEXT ClassMapContext, IN PPACKET_INFO_BLOCK PacketInfo );
VOID TbcSetInformation ( IN PPS_PIPE_CONTEXT ComponentPipeContext, IN PPS_FLOW_CONTEXT ComponentFlowContext, IN NDIS_OID Oid, IN ULONG Len, IN PVOID Data);
VOID TbcQueryInformation ( 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);
/* End Forward */
extern VOID ServiceActiveFlows( PVOID SysArg1, PVOID Context, PVOID SysArg2, PVOID SysArg3);
VOID InitializeTbConformer( PPSI_INFO Info)
/*++
Routine Description:
Initialization routine for token bucket conformer. This routine just fills in the PSI_INFO struct and returns.
Arguments:
Info - Pointer to component interface info struct
Return Values:
NDIS_STATUS_SUCCESS
--*/ { #ifdef QUEUE_LIMIT
ULONG bytesWritten; SYSTEM_BASIC_INFORMATION sbi; #endif // QUEUE_LIMIT
Info->PipeContextLength = ((sizeof(TBC_PIPE)+7) & ~7); Info->FlowContextLength = ((sizeof(TBC_FLOW)+7) & ~7); Info->ClassMapContextLength = sizeof(PS_CLASS_MAP_CONTEXT); Info->InitializePipe = TbcInitializePipe; Info->ModifyPipe = TbcModifyPipe; Info->DeletePipe = TbcDeletePipe; Info->CreateFlow = TbcCreateFlow; Info->ModifyFlow = TbcModifyFlow; Info->DeleteFlow = TbcDeleteFlow; Info->EmptyFlow = TbcEmptyFlow; Info->CreateClassMap = TbcCreateClassMap; Info->DeleteClassMap = TbcDeleteClassMap; Info->SubmitPacket = TbcSubmitPacket; Info->ReceivePacket = NULL; Info->ReceiveIndication = NULL; Info->SetInformation = TbcSetInformation; Info->QueryInformation = TbcQueryInformation;
#ifdef QUEUE_LIMIT
NtQuerySystemInformation(SystemBasicInformation, &sbi, sizeof(SYSTEM_BASIC_INFORMATION), &bytesWritten); gPhysMemSize = sbi.NumberOfPhysicalPages * sbi.PageSize; // convert to MB
gPhysMemSize >>= 20; #endif // QUEUE_LIMIT
} // InitializeTbConformer
//
// Unload routine: currently does nothing
//
void UnloadConformr() {
}
NDIS_STATUS TbcInitializePipe ( 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 token bucket conformer.
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
--*/ { PTBC_PIPE Pipe = (PTBC_PIPE)ComponentPipeContext; NDIS_STATUS Status; HANDLE NdisHandle; int i = 0; PLIST_ENTRY pList = NULL;
PsDbgOut(DBG_INFO, DBG_SCHED_TBC, ("PSCHED: Conformer pipe initialized. Bandwidth = %u\n", PipeParameters->Bandwidth));
Pipe->MaxPacket = PipeParameters->MTUSize - PipeParameters->HeaderSize; Pipe->PsPipeContext = PsPipeContext; (*PsProcs->GetTimerInfo)(&Pipe->TimerResolution); Pipe->TimerResolution /= 2; Pipe->PsProcs = PsProcs; Pipe->HeaderLength = PipeParameters->HeaderSize; Pipe->ControlledLoadMode = PipeParameters->SDModeControlledLoad; Pipe->GuaranteedMode = PipeParameters->SDModeGuaranteed; Pipe->NetworkControlMode = PipeParameters->SDModeNetworkControl; Pipe->Qualitative = PipeParameters->SDModeQualitative; Pipe->IntermediateSystem = (PipeParameters->Flags & PS_INTERMEDIATE_SYS) ? TRUE : FALSE; Pipe->MediaType = PipeParameters->MediaType;
InitializeListHead(&Pipe->ActiveFlows); NdisHandle = (*PsProcs->NdisPipeHandle)(PsPipeContext);
// 1. Initialize the spin lock that protects the timer wheel //
NdisAllocateSpinLock(&Pipe->Lock);
// 2. Initialize the timer for the timer wheel //
if (NdisHandle != NULL) { NdisMInitializeTimer( &Pipe->Timer, NdisHandle, ServiceActiveFlows, Pipe); Pipe->TimerStatus = TIMER_INACTIVE; } else { // Why would it come here.... ? //
Pipe->TimerStatus = TIMER_UNINITIALIZED; }
// Remember what kind of pipe are we installing now.. //
if( Pipe->MediaType == NdisMediumWan ) Pipe->TimerWheelShift = WAN_TIMER_WHEEL_SHIFT; else Pipe->TimerWheelShift = LAN_TIMER_WHEEL_SHIFT;
// These values should always be initialized //
Pipe->pTimerWheel = NULL; Pipe->SetSlotValue = DUMMY_SLOT; Pipe->SetTimerValue.QuadPart = DUMMY_TIME;
Pipe->cStats.NonconformingPacketsScheduled = 0; Pipe->PacketsInShaper = 0; Pipe->PacketsInShaperAveragingArray = NULL;
NdisZeroMemory(&Pipe->sStats, sizeof(PS_SHAPER_STATS));
Status = CreateAveragingArray(&Pipe->PacketsInShaperAveragingArray, SHAPER_AVERAGING_ARRAY_SIZE);
if(Status != NDIS_STATUS_SUCCESS) { NdisFreeSpinLock( &Pipe->Lock ); return(Status); }
NdisInitializeEvent(&Pipe->TimerUnloadEvent); Pipe->TimerUnloadFlag = 0;
Status = (*Pipe->ContextInfo.NextComponent->InitializePipe)( PsPipeContext, PipeParameters, Pipe->ContextInfo.NextComponentContext, PsProcs, Upcalls);
if (Status != NDIS_STATUS_SUCCESS) { DeleteAveragingArray(Pipe->PacketsInShaperAveragingArray); NdisFreeSpinLock( &Pipe->Lock ); }
return Status;
} // TbcInitializePipe
NDIS_STATUS TbcModifyPipe ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_PIPE_PARAMETERS PipeParameters )
/*++
Routine Description:
Pipe parameter modification routine for token bucket conformer.
Arguments:
PipeContext - Pointer to this component's pipe context area PipeParameters - Pointer to pipe parameters
Return Values:
Status value from next component
--*/ { PTBC_PIPE Pipe = (PTBC_PIPE)PipeContext; PTBC_FLOW Flow; PLIST_ENTRY Entry;
PsDbgOut(DBG_INFO, DBG_SCHED_TBC, ("PSCHED: Conformer pipe modified. Bandwidth = %u\n", PipeParameters->Bandwidth));
LOCK_PIPE(Pipe);
(*Pipe->PsProcs->GetTimerInfo)(&Pipe->TimerResolution); Pipe->TimerResolution /= 2;
UNLOCK_PIPE(Pipe);
return (*Pipe->ContextInfo.NextComponent->ModifyPipe)( Pipe->ContextInfo.NextComponentContext, PipeParameters);
} // TbcModifyPipe
VOID TbcDeletePipe ( 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:
--*/ { PTBC_PIPE Pipe = (PTBC_PIPE)PipeContext; BOOLEAN Cancelled;
if (Pipe->TimerStatus == TIMER_SET) { BOOLEAN TimerCancelled; NdisMCancelTimer(&Pipe->Timer, &TimerCancelled );
if( !TimerCancelled ) { // Need to handle the case where the Timer could not be cancelled. In this case, the DPC could be running,
// and we will have to wait here before going further
} else { Pipe->TimerStatus = TIMER_INACTIVE; } }
DeleteAveragingArray(Pipe->PacketsInShaperAveragingArray);
// Every pipe does not necessarily have a Timer-wheel now //
if( Pipe->pTimerWheel ) PsFreePool( Pipe->pTimerWheel); NdisFreeSpinLock(&Pipe->Lock);
PsDbgOut(DBG_INFO, DBG_SCHED_TBC, ("PSCHED: Conformer pipe deleted\n"));
(*Pipe->ContextInfo.NextComponent->DeletePipe)(Pipe->ContextInfo.NextComponentContext);
} // TbcDeletePipe
#ifdef QUEUE_LIMIT
/*
SetDefaultFlowQueueLimit() - Sets the queue size limit on a flow using a formula based on the amount of physical memory in the system and the overall bandwidth of the flow.
OUT PTS_FLOW Flow - Pointer to the flow to set the limit on IN PCO_CALL_PARAMETERS CallParameters - Call parameters containing the flow's bandwidth requirements */ static void SetDefaultFlowQueueLimit ( OUT PTS_FLOW Flow, IN PCO_CALL_PARAMETERS CallParameters ) { ULONG FlowBandwidth; // = either PeakRate or TokenRate+BucketSize
// determine the "flow bandwidth"
// if the peak rate is specified, use it as flow b/w
if (CallParameters->CallMgrParameters->Transmit.PeakBandwidth != QOS_NOT_SPECIFIED) FlowBandwidth = CallParameters->CallMgrParameters->Transmit.PeakBandwidth; // otherwise use tokenrate + bucket size
else if (QOS_NOT_SPECIFIED == CallParameters->CallMgrParameters->Transmit.TokenBucketSize) FlowBandwidth = CallParameters->CallMgrParameters->Transmit.TokenRate; else FlowBandwidth = CallParameters->CallMgrParameters->Transmit.TokenRate + CallParameters->CallMgrParameters->Transmit.TokenBucketSize; // then use it to compute the queue limit (first in time units)
Flow->QueueSizeLimit = (ULONG)(40.0 * log10(0.2 * gPhysMemSize) / log10(FlowBandwidth)); // convert time limit to size limit
Flow->QueueSizeLimit *= FlowBandwidth; } #endif // QUEUE_LIMIT
NDIS_STATUS TbcCreateFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN HANDLE PsFlowContext, IN PCO_CALL_PARAMETERS CallParameters, IN PPS_FLOW_CONTEXT ComponentFlowContext )
/*++
Routine Description:
Flow creation routine for token bucket conformer.
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
--*/ { PTBC_PIPE Pipe = (PTBC_PIPE)PipeContext; PTBC_FLOW Flow = (PTBC_FLOW)ComponentFlowContext; HANDLE NdisHandle; NDIS_STATUS Status; ULONG ParamsLength; LPQOS_OBJECT_HDR QoSObject; LPQOS_SD_MODE ShapeDiscardObject = NULL; ULONG Mode; ULONG PeakRate;
ULONG Slot= 0; LARGE_INTEGER Ms; LARGE_INTEGER TenMs; LARGE_INTEGER CurrentTimeInMs; LONGLONG DeltaTimeInMs; PLIST_ENTRY pList = NULL; LARGE_INTEGER CurrentTime;
#ifdef QUEUE_LIMIT
LPQOS_SHAPER_QUEUE_LIMIT_DROP_MODE ShaperOverLimitDropModeObject = NULL; LPQOS_SHAPER_QUEUE_LIMIT ShaperQueueLimitObject = NULL; #endif // QUEUELIMIT
if (Pipe->TimerStatus == TIMER_UNINITIALIZED) { NdisHandle = (*Pipe->PsProcs->NdisPipeHandle)(Pipe->PsPipeContext);
if (NdisHandle != NULL) { NdisMInitializeTimer( &Pipe->Timer, NdisHandle, ServiceActiveFlows, Pipe); Pipe->TimerStatus = TIMER_INACTIVE; } else { return NDIS_STATUS_FAILURE; } }
NdisAllocateSpinLock(&Flow->Lock);
// Get the required values from the flowspec. We assume here that the PS wrapper
// has performed the required validity checks:
// TokenRate <= PeakRate
// TokenRate > 0
Flow->State = TS_FLOW_CREATED; Flow->TokenRate = CallParameters->CallMgrParameters->Transmit.TokenRate; Flow->Capacity = CallParameters->CallMgrParameters->Transmit.TokenBucketSize; Flow->PeakRate = CallParameters->CallMgrParameters->Transmit.PeakBandwidth; Flow->MinPolicedUnit = (CallParameters->CallMgrParameters->Transmit.MinimumPolicedSize == QOS_NOT_SPECIFIED) ? 0 : CallParameters->CallMgrParameters->Transmit.MinimumPolicedSize;
if (Flow->Capacity == QOS_NOT_SPECIFIED) { if( Pipe->MaxPacket > (CallParameters->CallMgrParameters->Transmit.TokenRate / 100) ) Flow->Capacity = Pipe->MaxPacket; else Flow->Capacity = CallParameters->CallMgrParameters->Transmit.TokenRate / 100; }
// Look for the Shape/Discard object in the call manager specific parameters.
// If it is found, save the pointer.
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_SD_MODE) { ShapeDiscardObject = (LPQOS_SD_MODE)QoSObject; #ifdef QUEUE_LIMIT
else if (QoSObject->ObjectType == QOS_OBJECT_SHAPER_QUEUE_DROP_MODE) { ShaperOverLimitDropModeObject = (LPQOS_SHAPER_QUEUE_LIMIT_DROP_MODE)QoSObject; } else if (QoSObject->ObjectType == QOS_OBJECT_SHAPER_QUEUE_LIMIT) { ShaperQueueLimitObject = (LPQOS_SHAPER_QUEUE_LIMIT)QoSObject; } #endif // QUEUE_LIMIT
} ParamsLength -= QoSObject->ObjectLength; QoSObject = (LPQOS_OBJECT_HDR)((UINT_PTR)QoSObject + QoSObject->ObjectLength); } }
// If no Shape/Discard object was found, set the default value for the
// "Discard" parameter. Otherwise set it to the value specified by the
// object.
if (ShapeDiscardObject == NULL) { switch (CallParameters->CallMgrParameters->Transmit.ServiceType) { case SERVICETYPE_CONTROLLEDLOAD: Mode = Pipe->ControlledLoadMode; break; case SERVICETYPE_GUARANTEED: Mode = Pipe->GuaranteedMode; break; case SERVICETYPE_NETWORK_CONTROL: Mode = Pipe->NetworkControlMode; break; case SERVICETYPE_QUALITATIVE: Mode = Pipe->Qualitative; break; default: Mode = TC_NONCONF_BORROW; } } else { Mode = ShapeDiscardObject->ShapeDiscardMode; }
Flow->Mode = Mode; Flow->NoConformance = ((Mode == TC_NONCONF_BORROW_PLUS) || (Flow->TokenRate == QOS_NOT_SPECIFIED));
PsGetCurrentTime(&Flow->LastConformanceTime);
Flow->PeakConformanceTime = Flow->LastConformanceTime; Flow->LastConformanceCredits = Flow->Capacity; Flow->PsFlowContext = PsFlowContext;
PeakRate = CallParameters->CallMgrParameters->Transmit.PeakBandwidth; if (Flow->Mode == TC_NONCONF_SHAPE) { Flow->Shape = TRUE; } else if ((PeakRate != QOS_NOT_SPECIFIED) && (Flow->Mode != TC_NONCONF_BORROW_PLUS) && !Pipe->IntermediateSystem) { Flow->Shape = TRUE; } else { Flow->Shape = FALSE; }
#ifdef QUEUE_LIMIT
Flow->QueueSize = 0; // If the flow is shaped, set the queue limiting params. If not specified, use defaults
if (Flow->Shape) { // set the drop mode
if (NULL != ShaperOverLimitDropModeObject) { Flow->DropOverLimitPacketsFromHead = (BOOLEAN) ShaperOverLimitDropModeObject->DropMode; } else { // default to this behavior
Flow->DropOverLimitPacketsFromHead = TRUE; }
// set the queue limit
if (NULL != ShaperQueueLimitObject) { Flow->UseDefaultQueueLimit = FALSE; Flow->QueueSizeLimit = ShaperQueueLimitObject->QueueSizeLimit; } else { Flow->UseDefaultQueueLimit = TRUE; // default to a size based on the flow's bandwidth and physical memory
SetDefaultFlowQueueLimit(Flow, CallParameters); } } #endif // QUEUE_LIMIT
InitializeListHead(&Flow->PacketQueue); PsGetCurrentTime(&Flow->FlowEligibilityTime);
Flow->cStats.NonconformingPacketsScheduled = 0; Flow->PacketsInShaper = 0; Flow->PacketsInShaperAveragingArray = NULL; NdisZeroMemory(&Flow->sStats, sizeof(PS_SHAPER_STATS));
Status = CreateAveragingArray(&Flow->PacketsInShaperAveragingArray, SHAPER_FLOW_AVERAGING_ARRAY_SIZE); if(Status != NDIS_STATUS_SUCCESS){ return(Status); }
PsDbgOut(DBG_INFO, DBG_SCHED_TBC, ("PSCHED: Conformer flow %08X (PsFlowContext = %08X) created. Rate = %u\n", Flow, Flow->PsFlowContext, Flow->TokenRate));
Status = (*Pipe->ContextInfo.NextComponent->CreateFlow)( Pipe->ContextInfo.NextComponentContext, PsFlowContext, CallParameters, Flow->ContextInfo.NextComponentContext);
LOCK_PIPE( Pipe ); if (Status != NDIS_STATUS_SUCCESS) { NdisFreeSpinLock(&Flow->Lock); DeleteAveragingArray(Flow->PacketsInShaperAveragingArray); }
UNLOCK_PIPE( Pipe );
return Status;
} // TbcCreateFlow
NDIS_STATUS TbcModifyFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext, IN PCO_CALL_PARAMETERS CallParameters )
/*++
Routine Description:
Flow modification routine for token bucket conformer.
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
--*/ { PTBC_PIPE Pipe = (PTBC_PIPE)PipeContext; PTBC_FLOW Flow = (PTBC_FLOW)FlowContext; ULONG ParamsLength; LPQOS_OBJECT_HDR QoSObject; LPQOS_SD_MODE ShapeDiscardObject = NULL; ULONG Mode; ULONG PeakRate; LARGE_INTEGER CurrentTime;
#ifdef QUEUE_LIMIT
LPQOS_SHAPER_QUEUE_LIMIT_DROP_MODE ShaperOverLimitDropModeObject = NULL; LPQOS_SHAPER_QUEUE_LIMIT ShaperQueueLimitObject = NULL; #endif // QUEUE_LIMIT
// Look for the Shape/Discard object in the call manager specific parameters.
// If it is found, save the pointer.
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_SD_MODE) { ShapeDiscardObject = (LPQOS_SD_MODE)QoSObject; #ifdef QUEUE_LIMIT
else if (QoSObject->ObjectType == QOS_OBJECT_SHAPER_QUEUE_DROP_MODE) { ShaperOverLimitDropModeObject = (LPQOS_SHAPER_QUEUE_LIMIT_DROP_MODE)QoSObject; } else if (QoSObject->ObjectType == QOS_OBJECT_SHAPER_QUEUE_LIMIT) { ShaperQueueLimitObject = (LPQOS_SHAPER_QUEUE_LIMIT)QoSObject; } #endif // QUEUE_LIMIT
} ParamsLength -= QoSObject->ObjectLength; QoSObject = (LPQOS_OBJECT_HDR)((UINT_PTR)QoSObject + QoSObject->ObjectLength); } }
PeakRate = CallParameters->CallMgrParameters->Transmit.PeakBandwidth;
LOCK_FLOW(Flow);
//
// There are basically 2 parameters that have to be corrected in this function:
// They are (a) LastConformanceTime (b) LastConformanceCredits.
// (1) If LastConformanceTime is in the future: Goto step(4).
// (2) (a) Figure out how many bytes were accumulated between LastConformanceTime and CurrentTime.
// (b) If Accumulated Credits is greater than Bucket size, Accumulated Credits = Bucket size.
// (c) Set LastConformanceTime to CurrentTime.
// (3) PeakConformanceTime will not be changed.
// (4) Change the Flow parameters, as specified in the Modify-call.
PsGetCurrentTime(&CurrentTime);
if( Flow->LastConformanceTime.QuadPart < CurrentTime.QuadPart) { ULONG Credits;
Credits = Flow->LastConformanceCredits + EARNED_CREDITS( CurrentTime.QuadPart - Flow->LastConformanceTime.QuadPart, Flow->TokenRate);
if( Credits > Flow->Capacity) Flow->LastConformanceCredits = Flow->Capacity; else Flow->LastConformanceCredits = Credits;
Flow->LastConformanceTime.QuadPart = CurrentTime.QuadPart; }
if (CallParameters->CallMgrParameters->Transmit.ServiceType != SERVICETYPE_NOCHANGE) {
// Get the new flowspec values. Again we assume the PS wrapper has done
// the required validity checks.
Flow->TokenRate = CallParameters->CallMgrParameters->Transmit.TokenRate; Flow->Capacity = CallParameters->CallMgrParameters->Transmit.TokenBucketSize; Flow->PeakRate = CallParameters->CallMgrParameters->Transmit.PeakBandwidth; Flow->MinPolicedUnit = (CallParameters->CallMgrParameters->Transmit.MinimumPolicedSize == QOS_NOT_SPECIFIED) ? 0 : CallParameters->CallMgrParameters->Transmit.MinimumPolicedSize;
if (Flow->Capacity == QOS_NOT_SPECIFIED) { if( Pipe->MaxPacket > (CallParameters->CallMgrParameters->Transmit.TokenRate / 100) ) Flow->Capacity = Pipe->MaxPacket; else Flow->Capacity = CallParameters->CallMgrParameters->Transmit.TokenRate / 100; }
if (ShapeDiscardObject == NULL) {
// Re-calculate the Shape parameter if the user has never specified
// a Shape/Discard object.
switch (CallParameters->CallMgrParameters->Transmit.ServiceType) { case SERVICETYPE_CONTROLLEDLOAD: Mode = Pipe->ControlledLoadMode; break; case SERVICETYPE_GUARANTEED: Mode = Pipe->GuaranteedMode; break; case SERVICETYPE_NETWORK_CONTROL: Mode = Pipe->NetworkControlMode; break; case SERVICETYPE_QUALITATIVE: Mode = Pipe->Qualitative; break; default: Mode = TC_NONCONF_BORROW; } } } else { // The ServiceType has not changed. We can use the existing mode.
Mode = Flow->Mode; }
if (ShapeDiscardObject != NULL) { Mode = ShapeDiscardObject->ShapeDiscardMode; }
Flow->Mode = Mode; Flow->NoConformance = ((Mode == TC_NONCONF_BORROW_PLUS) || (Flow->TokenRate == QOS_NOT_SPECIFIED));
if (Flow->Mode == TC_NONCONF_SHAPE) { Flow->Shape = TRUE; } else if ((PeakRate != QOS_NOT_SPECIFIED) && (Flow->Mode != TC_NONCONF_BORROW_PLUS) && !Pipe->IntermediateSystem) { Flow->Shape = TRUE; } else { Flow->Shape = FALSE; }
#ifdef QUEUE_LIMIT
// If the flow is shaped, check the queue limiting params. If specified, use
if (Flow->Shape) { // modify drop mode
if (NULL != ShaperOverLimitDropModeObject) { Flow->DropOverLimitPacketsFromHead = (BOOLEAN) ShaperOverLimitDropModeObject->DropMode; }
// modify queue limit
if (NULL != ShaperQueueLimitObject) { Flow->UseDefaultQueueLimit = FALSE; Flow->QueueSizeLimit = ShaperQueueLimitObject->QueueSizeLimit; } // if they haven't overridden the limit, recompute it in case bandwidth req's changed
else if (Flow->UseDefaultQueueLimit) { SetDefaultFlowQueueLimit(Flow, CallParameters); } } #endif // QUEUE_LIMIT
UNLOCK_FLOW(Flow);
PsDbgOut(DBG_INFO, DBG_SCHED_TBC, ("PSCHED: Conformer flow %08x (PsFlowContext %08X) modified. Rate = %u\n", Flow, Flow->PsFlowContext, Flow->TokenRate));
return (*Pipe->ContextInfo.NextComponent->ModifyFlow)( Pipe->ContextInfo.NextComponentContext, Flow->ContextInfo.NextComponentContext, CallParameters);
} // TbcModifyFlow
VOID TbcDeleteFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext )
/*++
Routine Description:
Flow removal routine for token bucket conformer.
Arguments:
PipeContext - Pointer to this component's pipe context area FlowContext - Pointer to this component's flow context area
Return Values:
--*/ { PTBC_PIPE Pipe = (PTBC_PIPE)PipeContext; PTBC_FLOW Flow = (PTBC_FLOW)FlowContext; PPACKET_INFO_BLOCK PacketInfo; PNDIS_PACKET Packet; LIST_ENTRY DropList;
PsDbgOut(DBG_INFO, DBG_SCHED_TBC, ("PSCHED: Conformer flow %08X (PS context %08X) deleted\n", Flow, Flow->PsFlowContext));
NdisFreeSpinLock(&Flow->Lock);
InitializeListHead(&DropList);
LOCK_PIPE(Pipe);
if (!IsListEmpty(&Flow->PacketQueue)) {
// Remove flow from active list
RemoveEntryList(&Flow->Links);
while (!IsListEmpty(&Flow->PacketQueue)) {
// Drop any packets that remain queued for this flow.
PacketInfo = (PPACKET_INFO_BLOCK)RemoveHeadList(&Flow->PacketQueue); InsertTailList(&DropList, &PacketInfo->SchedulerLinks);
} }
DeleteAveragingArray(Flow->PacketsInShaperAveragingArray);
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); }
(*Pipe->ContextInfo.NextComponent->DeleteFlow)( Pipe->ContextInfo.NextComponentContext, Flow->ContextInfo.NextComponentContext);
} // TbcDeleteFlow
VOID TbcEmptyFlow ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_FLOW_CONTEXT FlowContext )
/*++
Routine Description:
Flow removal routine for token bucket conformer.
Arguments:
PipeContext - Pointer to this component's pipe context area FlowContext - Pointer to this component's flow context area
Return Values:
--*/ { PTBC_PIPE Pipe = (PTBC_PIPE)PipeContext; PTBC_FLOW Flow = (PTBC_FLOW)FlowContext; PPACKET_INFO_BLOCK PacketInfo; PNDIS_PACKET Packet; LIST_ENTRY DropList;
PsDbgOut(DBG_INFO, DBG_SCHED_TBC, ("PSCHED: Conformer flow %08X (PS context %08X) emptied\n", Flow, Flow->PsFlowContext));
InitializeListHead(&DropList);
LOCK_PIPE(Pipe);
if (!IsListEmpty(&Flow->PacketQueue)) { // Remove flow from active list
RemoveEntryList(&Flow->Links);
while (!IsListEmpty(&Flow->PacketQueue)) { // Drop any packets that remain queued for this flow.
PacketInfo = (PPACKET_INFO_BLOCK)RemoveHeadList(&Flow->PacketQueue); InsertTailList(&DropList, &PacketInfo->SchedulerLinks); } }
Flow->State = TS_FLOW_DELETED;
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); }
(*Pipe->ContextInfo.NextComponent->EmptyFlow)( Pipe->ContextInfo.NextComponentContext, Flow->ContextInfo.NextComponentContext);
} // TbcModifyFlow
static NDIS_STATUS TbcCreateClassMap ( IN PPS_PIPE_CONTEXT PipeContext, IN HANDLE PsClassMapContext, IN PTC_CLASS_MAP_FLOW ClassMap, IN PPS_CLASS_MAP_CONTEXT ComponentClassMapContext) { PTBC_PIPE Pipe = (PTBC_PIPE)PipeContext; return (*Pipe->ContextInfo.NextComponent->CreateClassMap)( Pipe->ContextInfo.NextComponentContext, PsClassMapContext, ClassMap, ComponentClassMapContext->NextComponentContext); }
static NDIS_STATUS TbcDeleteClassMap ( IN PPS_PIPE_CONTEXT PipeContext, IN PPS_CLASS_MAP_CONTEXT ComponentClassMapContext) { PTBC_PIPE Pipe = (PTBC_PIPE)PipeContext; return (*Pipe->ContextInfo.NextComponent->DeleteClassMap)( Pipe->ContextInfo.NextComponentContext, ComponentClassMapContext->NextComponentContext); }
void InsertFlow( PTBC_PIPE Pipe, PTBC_FLOW Flow, LARGE_INTEGER CurrentTime, PPACKET_INFO_BLOCK PacketInfo, PNDIS_PACKET Packet, ULONG ExecSlot, LARGE_INTEGER ExecTimeInTenMs) { /* So, the packet is not eligible to be sent out right now, and the pkt-queue is empty.. */
ULONG Slot= 0; LARGE_INTEGER Ms; LARGE_INTEGER TenMs; LARGE_INTEGER CurrentTimeInMs; LARGE_INTEGER DeltaTimeInTenMs, CurrentTimeInTenMs; PLIST_ENTRY pList = NULL; BOOLEAN TimerCancelled;
PsDbgSched(DBG_INFO, DBG_SCHED_SHAPER, SHAPER, PKT_ENQUEUE, Flow->PsFlowContext, Packet, PacketInfo->PacketLength, 0, CurrentTime.QuadPart, PacketInfo->DelayTime.QuadPart, Pipe->PacketsInShaper, 0);
/* Conf time in ms and 10ms */ Ms.QuadPart = OS_TIME_TO_MILLISECS( Flow->FlowEligibilityTime.QuadPart ); TenMs.QuadPart = Ms.QuadPart >> TIMER_WHEEL_SHIFT;
/* Diff in 10 MS */ DeltaTimeInTenMs.QuadPart = TenMs.QuadPart - ExecTimeInTenMs.QuadPart;
/* Figure out the Slot for this time.. */ Slot = (ULONG)( (TenMs.QuadPart) & (( 1 << Pipe->TimerWheelShift) - 1) );
/* Update the loop count too */ Flow->LoopCount = (ULONG)( DeltaTimeInTenMs.QuadPart >> Pipe->TimerWheelShift );
if( Slot == ExecSlot) Slot = ( (Slot + 1) & ((1 << Pipe->TimerWheelShift) - 1) );
pList = (PLIST_ENTRY)( (char*)Pipe->pTimerWheel + ( sizeof(LIST_ENTRY) * Slot) ); /* Need to insert the flow to the timer-wheel in slot's position*/ InsertTailList(pList, &Flow->Links); }
VOID ServiceActiveFlows( PVOID SysArg1, PVOID Context, PVOID SysArg2, PVOID SysArg3)
/*++
Routine Description:
Service the active flow list after a timer expiration.
Arguments:
Context - Pointer to pipe context information
Return Values:
--*/ { PTBC_PIPE Pipe = (PTBC_PIPE)Context; PTBC_FLOW Flow; LARGE_INTEGER CurrentTime; LONGLONG RelTimeInMillisecs; PPACKET_INFO_BLOCK PacketInfo; PNDIS_PACKET Packet; BOOLEAN DoneWithFlow;
PLIST_ENTRY CurrentLink; PLIST_ENTRY ListHead; PLIST_ENTRY ListEnd;
ULONG i = 0; ULONG SetSlot= 0; ULONG CurrentSlot = 0; LARGE_INTEGER Ms; LARGE_INTEGER TenMs; LARGE_INTEGER CurrentTimeInMs; LONGLONG DeltaTimeInMs;
LIST_ENTRY SendList; LIST_ENTRY FlowList;
InitializeListHead(&SendList); InitializeListHead(&FlowList);
LOCK_PIPE(Pipe); PsGetCurrentTime(&CurrentTime);
/* start from here.. */ i = SetSlot = Pipe->SetSlotValue;
Ms.QuadPart = OS_TIME_TO_MILLISECS( CurrentTime.QuadPart); TenMs.QuadPart = Ms.QuadPart >> TIMER_WHEEL_SHIFT;
// Need to make sure that SetTimerValue is lesser than TenMs //
if( Pipe->SetTimerValue.QuadPart > TenMs.QuadPart) { // Why is the timer firing earlier than when it is slated to?
TenMs.QuadPart = 1; NdisMSetTimer(&Pipe->Timer, (UINT)(TenMs.QuadPart << TIMER_WHEEL_SHIFT)); UNLOCK_PIPE(Pipe); return; }
/* run till here.. */ CurrentSlot = (ULONG)( (TenMs.QuadPart) & ((1 << Pipe->TimerWheelShift) - 1) );
/* Indicate that the timer is running */ Pipe->TimerStatus = TIMER_PROC_EXECUTING; Pipe->ExecTimerValue.QuadPart = Pipe->SetTimerValue.QuadPart; Pipe->ExecSlot = Pipe->SetSlotValue;
ListHead = (PLIST_ENTRY)((char*)Pipe->pTimerWheel + (sizeof(LIST_ENTRY)* SetSlot )); ListEnd = (PLIST_ENTRY)((char*)Pipe->pTimerWheel + (sizeof(LIST_ENTRY)* CurrentSlot ));
while(1) { while( !IsListEmpty( ListHead) ) { CurrentLink = ListHead->Flink; Flow = CONTAINING_RECORD(CurrentLink, TBC_FLOW, Links); RemoveEntryList(&Flow->Links);
PsAssert(!IsListEmpty(&Flow->PacketQueue)); DoneWithFlow = FALSE;
InitializeListHead( &SendList );
PacketInfo = (PPACKET_INFO_BLOCK)Flow->PacketQueue.Flink;
if( Flow->LoopCount > 0 ) { Flow->LoopCount--; InsertTailList( &FlowList, &Flow->Links ); continue; }
while( FlowIsEligible(Flow, CurrentTime, ((TIMER_WHEEL_QTY/2) * MSIN100NS))) { RemoveEntryList(&PacketInfo->SchedulerLinks);
Packet = PacketInfo->NdisPacket;
DoneWithFlow = IsListEmpty(&Flow->PacketQueue);
Pipe->PacketsInShaper--; Flow->PacketsInShaper--;
if(gEnableAvgStats) { Pipe->sStats.AveragePacketsInShaper = RunningAverage(Pipe->PacketsInShaperAveragingArray, Pipe->PacketsInShaper);
Flow->sStats.AveragePacketsInShaper = RunningAverage(Flow->PacketsInShaperAveragingArray, Flow->PacketsInShaper);
} PsDbgSched(DBG_INFO, DBG_SCHED_SHAPER, SHAPER, PKT_DEQUEUE, Flow->PsFlowContext, Packet, PacketInfo->PacketLength, 0, CurrentTime.QuadPart, PacketInfo->DelayTime.QuadPart, Pipe->PacketsInShaper, 0);
InsertTailList( &SendList, &PacketInfo->SchedulerLinks);
if( !DoneWithFlow) { PacketInfo = (PPACKET_INFO_BLOCK)Flow->PacketQueue.Flink; Flow->FlowEligibilityTime.QuadPart = PacketInfo->DelayTime.QuadPart; } else { break; } }
if( !DoneWithFlow) { /* Need to insert in the right place.. */ InsertFlow( Pipe, Flow, CurrentTime, PacketInfo, Packet, i, Pipe->ExecTimerValue); }
/* send the packet corresponding to this flow here */ UNLOCK_PIPE(Pipe); while( !IsListEmpty( &SendList )) { PPACKET_INFO_BLOCK PacketInfo;
PacketInfo = (PPACKET_INFO_BLOCK)RemoveHeadList(&SendList);
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, PacketInfo->NdisPacket, NDIS_STATUS_FAILURE); } }
LOCK_PIPE(Pipe); }
/* Now, we need to re-insert back all the non-zero loop counts into the same buckets (before we move on ) */ while( !IsListEmpty( &FlowList) ) { CurrentLink = RemoveHeadList( &FlowList ); InsertTailList(ListHead, CurrentLink); } /* We have traversed the whole length.. */ if(ListHead == ListEnd) break;
/* Need to move ListHead to next slot.. */ i = ( (i+1) & ((1 << Pipe->TimerWheelShift) - 1) ); ListHead = (PLIST_ENTRY)((char*)Pipe->pTimerWheel + (sizeof(LIST_ENTRY)* i));
Pipe->ExecSlot = i; Pipe->ExecTimerValue.QuadPart ++; }
//
// Need to find the "next non-empty slot" and set the timer.
// If no such slot is found, do not set the timer.
//
i = ( CurrentSlot + 1) & ((1 << Pipe->TimerWheelShift) - 1) ; TenMs.QuadPart = 1;
while(1) { ListHead = (PLIST_ENTRY)((char*)Pipe->pTimerWheel + (sizeof(LIST_ENTRY)* i));
if( !IsListEmpty( ListHead) ) { // found a non-empty slot //
Pipe->SetSlotValue = i; Pipe->SetTimerValue.QuadPart = (Ms.QuadPart >> TIMER_WHEEL_SHIFT) + TenMs.QuadPart;
Pipe->TimerStatus = TIMER_SET; NdisMSetTimer(&Pipe->Timer, (UINT)(TenMs.QuadPart << TIMER_WHEEL_SHIFT));
UNLOCK_PIPE(Pipe); return; }
if( i == CurrentSlot) break;
i = ((i +1) & ((1 << Pipe->TimerWheelShift) - 1) ); TenMs.QuadPart = TenMs.QuadPart + 1; }
Pipe->TimerStatus = TIMER_INACTIVE; UNLOCK_PIPE(Pipe); return;
} // ServiceActiveFlows
BOOLEAN TbcSubmitPacket ( 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 token bucket conformer.
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:
Status value from next component
--*/ { PTBC_PIPE Pipe = (PTBC_PIPE)PipeContext; PTBC_FLOW Flow = (PTBC_FLOW)FlowContext; PNDIS_PACKET Packet = PacketInfo->NdisPacket; LARGE_INTEGER CurrentTime; LARGE_INTEGER ConformanceTime; LARGE_INTEGER TransmitTime; LARGE_INTEGER PeakConformanceTime; ULONG Credits; ULONG PacketLength; BOOLEAN TimerCancelled; LONGLONG RelTimeInMillisecs; BOOLEAN Status; #ifdef QUEUE_LIMIT
PPACKET_INFO_BLOCK PacketToBeDroppedInfo; #endif // QUEUE_LIMIT
PsGetCurrentTime(&CurrentTime);
if (Flow->NoConformance) {
// The conformance time calculation is not performed for certain types of
// flows. If the flow does not have a specified rate, we cannot really do
// token bucket. Flows that use the "borrow+" shape/discard mode only use
// their rate as a relative weight. For either of these types of flows
// there is no distinction between conforming and non-conforming traffic.
// So, we just set the "conformance" time to the current time to insure
// that all packets will be handled as conforming in subsequent components.
PacketInfo->ConformanceTime.QuadPart = CurrentTime.QuadPart;
} else {
// We decided to not use the MinPolicedSize as per WMT request. This makes the overhead
// calculation complicated and incorrect.
PacketLength = //(PacketInfo->PacketLength < Flow->MinPolicedUnit) ? Flow->MinPolicedUnit :
PacketInfo->PacketLength;
LOCK_FLOW(Flow);
// Set ConformanceTime to the earliest time at which the packet may
// possibly go out, based on the token bucket parameters, and Credits
// to the number of credits available at that time.
if (CurrentTime.QuadPart > Flow->LastConformanceTime.QuadPart) {
ConformanceTime = CurrentTime; Credits = Flow->LastConformanceCredits + EARNED_CREDITS( CurrentTime.QuadPart - Flow->LastConformanceTime.QuadPart, Flow->TokenRate); } else { ConformanceTime = Flow->LastConformanceTime; Credits = Flow->LastConformanceCredits; }
if (Credits > Flow->Capacity) { Credits = Flow->Capacity; }
// Now check whether there are enough credits to send the packet at ConformanceTime
if (Credits < PacketLength) {
// If there aren't enough credits, update ConformanceTime to the time at which
// there will be enough credits
ConformanceTime.QuadPart += (LONGLONG)TIME_TO_EARN_CREDITS(PacketLength - Credits, Flow->TokenRate);
// Now update Credits to be the number of credits available at ConformanceTime,
// taking this packet into account. In this case, the number of credits
// at ConformanceTime will be zero.
Credits = 0;
// If it has to wait to earn credits, it's non-conforming
Flow->cStats.NonconformingPacketsScheduled ++; Pipe->cStats.NonconformingPacketsScheduled ++; } else { // There are enough credits, so the packet can be sent at ConformanceTime. Update
// Credits to be the number of credits available at ConformanceTime, taking this
// packet into account.
Credits -= PacketLength; }
// Calculate the adjusted conformance time, which is the maximum of the
// token bucket conformance time and the peak conformance time.
if (Flow->PeakRate != QOS_NOT_SPECIFIED) { PeakConformanceTime = (Flow->PeakConformanceTime.QuadPart < CurrentTime.QuadPart) ? CurrentTime : Flow->PeakConformanceTime;
TransmitTime = (PeakConformanceTime.QuadPart < ConformanceTime.QuadPart) ? ConformanceTime : PeakConformanceTime;
} else {
PeakConformanceTime = Flow->LastConformanceTime; TransmitTime = ConformanceTime; }
// Perform mode-specific operations. For discard mode flows, check whether
// the packet should be dropped. For all flows, set the packet conformance
// times based on the pipe/flow mode. The packet's conformance time is the
// time at which the packet should be considered conforming. The delay time
// is the earliest time at which a packet is eligible for sending.
// When deciding whether to drop a packet, we consider a packet conforming if
// its conformance time is within half a clock tick of the current time.
if (Flow->Mode == TC_NONCONF_DISCARD) {
if (Pipe->IntermediateSystem) { if (!PACKET_IS_CONFORMING(TransmitTime, CurrentTime, Pipe->TimerResolution)) { UNLOCK_FLOW(Flow);
PsDbgSched(DBG_TRACE, DBG_SCHED_TBC, TBC_CONFORMER, PKT_DISCARD, Flow->PsFlowContext, Packet, PacketInfo->PacketLength, 0, CurrentTime.QuadPart, TransmitTime.QuadPart, 0, 0);
return FALSE; } } else { if (!PACKET_IS_CONFORMING(ConformanceTime, CurrentTime, Pipe->TimerResolution)) { UNLOCK_FLOW(Flow);
PsDbgSched(DBG_TRACE, DBG_SCHED_TBC, TBC_CONFORMER, PKT_DISCARD, Flow->PsFlowContext, Packet, PacketInfo->PacketLength, 0, CurrentTime.QuadPart, ConformanceTime.QuadPart, 0, 0);
return FALSE; } } }
// Set the packet conformance times
if (Pipe->IntermediateSystem) {
if (Flow->Mode == TC_NONCONF_SHAPE) {
// Both conformance times are the adjusted conformance time.
PacketInfo->ConformanceTime.QuadPart = PacketInfo->DelayTime.QuadPart = TransmitTime.QuadPart;
//
// If the packet is going to remain for > 5 min, discard it.
//
if(TransmitTime.QuadPart > CurrentTime.QuadPart && OS_TIME_TO_MILLISECS((TransmitTime.QuadPart - CurrentTime.QuadPart)) > MAX_TIME_FOR_PACKETS_IN_SHAPER) { UNLOCK_FLOW(Flow); return FALSE; }
} else {
// Packet's conformance time is the adjusted conformance time,
// and the delay time is the current time.
PacketInfo->ConformanceTime.QuadPart = TransmitTime.QuadPart; PacketInfo->DelayTime.QuadPart = CurrentTime.QuadPart; } } else {
if (Flow->Mode == TC_NONCONF_SHAPE) {
// Packet's conformance time is the token bucket conformance time,
// and the delay time is the adjusted conformance time.
PacketInfo->ConformanceTime.QuadPart = ConformanceTime.QuadPart; PacketInfo->DelayTime.QuadPart = TransmitTime.QuadPart;
//
// If the packet is going to remain for > 5 min, discard it.
//
if(TransmitTime.QuadPart > CurrentTime.QuadPart && OS_TIME_TO_MILLISECS((TransmitTime.QuadPart - CurrentTime.QuadPart)) > MAX_TIME_FOR_PACKETS_IN_SHAPER) { UNLOCK_FLOW(Flow); return FALSE; }
} else {
// Packet's conformance time is the token bucket conformance time, and
// the delay time is the peak conformance time.
PacketInfo->ConformanceTime.QuadPart = ConformanceTime.QuadPart; PacketInfo->DelayTime.QuadPart = PeakConformanceTime.QuadPart; } }
// Update the flow's variables
if (Flow->PeakRate != QOS_NOT_SPECIFIED) { Flow->PeakConformanceTime.QuadPart = PeakConformanceTime.QuadPart + (LONGLONG)TIME_TO_SEND(PacketLength, Flow->PeakRate); } Flow->LastConformanceTime = ConformanceTime; Flow->LastConformanceCredits = Credits; UNLOCK_FLOW(Flow);
}
// Pass the packet on
PsDbgSched(DBG_INFO, DBG_SCHED_TBC, TBC_CONFORMER, PKT_CONFORMANCE, Flow->PsFlowContext, Packet, PacketInfo->PacketLength, 0, CurrentTime.QuadPart, (Pipe->IntermediateSystem) ? TransmitTime.QuadPart : ConformanceTime.QuadPart, 0, 0);
if (!Flow->Shape) { // No shaping in effect. Pass the packet on.
/* Since the packet is not being shaped, it could be non-conformant. So, need to reset it's 802.1p and
IP-Precedence values. */
if( (!Flow->NoConformance) && !PACKET_IS_CONFORMING(PacketInfo->ConformanceTime, CurrentTime, Pipe->TimerResolution)) { 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; // 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); } } return (*Pipe->ContextInfo.NextComponent->SubmitPacket)( Pipe->ContextInfo.NextComponentContext, Flow->ContextInfo.NextComponentContext, (ClassMapContext != NULL) ? ClassMapContext->NextComponentContext : NULL, PacketInfo); }
LOCK_PIPE(Pipe);
if(Flow->State == TS_FLOW_DELETED) { UNLOCK_PIPE(Pipe); return FALSE; }
/* At this point, the conf-time of the packet is in TransmitTime
and the packetino->DelayTime has this info. */
PacketInfo->FlowContext = FlowContext;
// If packet queue is not empty just queue the packet regardless of
// whether it is eligible. If it is eligible, the timer proc will
// detect this and send the packet. If not, it will insert the flow
// into the correct location in the flow list if necessary.
if (!IsListEmpty(&Flow->PacketQueue)) { PsDbgSched(DBG_INFO, DBG_SCHED_SHAPER, SHAPER, PKT_ENQUEUE, Flow->PsFlowContext, Packet, PacketInfo->PacketLength, 0, 0, PacketInfo->DelayTime.QuadPart, Pipe->PacketsInShaper, 0);
PacketInfo->ClassMapContext = ClassMapContext; InsertTailList(&Flow->PacketQueue, &PacketInfo->SchedulerLinks); } else if(PacketIsEligible(PacketInfo, Flow, CurrentTime, ((TIMER_WHEEL_QTY/2) * MSIN100NS) )) { // Packet is eligible, so pass the packet on.
UNLOCK_PIPE(Pipe);
PsDbgSched(DBG_INFO, DBG_SCHED_SHAPER, SHAPER, PKT_DEQUEUE, Flow->PsFlowContext, Packet, PacketInfo->PacketLength, 0, CurrentTime.QuadPart, PacketInfo->DelayTime.QuadPart, Pipe->PacketsInShaper, 0);
return (*Pipe->ContextInfo.NextComponent->SubmitPacket)( Pipe->ContextInfo.NextComponentContext, Flow->ContextInfo.NextComponentContext, (ClassMapContext != NULL) ? ClassMapContext->NextComponentContext : NULL, PacketInfo); } else { // So, the packet is not eligible to be sent out right now, and the pkt-queue is empty
ULONG Slot= 0; LARGE_INTEGER Ms; LARGE_INTEGER TenMs; LARGE_INTEGER CurrentTimeInMs, CurrentTimeInTenMs; LONGLONG DeltaTimeInMs; PLIST_ENTRY pList = NULL; BOOL Success = FALSE; //
// The first thing we do here is: If there is no timer allocated for this pipe, allocate one
// The FIRST packet to be shaped on the pipe will take a hit due to this..
//
if( !Pipe->pTimerWheel ) { ULONG i =0; PsAllocatePool( Pipe->pTimerWheel, (sizeof(LIST_ENTRY) << Pipe->TimerWheelShift ), TimerTag);
if( !Pipe->pTimerWheel) { UNLOCK_PIPE(Pipe);
// If we could not allocate memory for the timer, we are not going to shape the packet //
return (*Pipe->ContextInfo.NextComponent->SubmitPacket)( Pipe->ContextInfo.NextComponentContext, Flow->ContextInfo.NextComponentContext, (ClassMapContext != NULL) ? ClassMapContext->NextComponentContext : NULL, PacketInfo); }
// Initialize the Timer wheel //
pList = (PLIST_ENTRY)(Pipe->pTimerWheel); for( i = 0; i < (ULONG) (1 << Pipe->TimerWheelShift); i++) { InitializeListHead( pList ); pList = (PLIST_ENTRY)((PCHAR)pList + sizeof(LIST_ENTRY)); } }
Ms.QuadPart= 0;
PsDbgSched(DBG_INFO, DBG_SCHED_SHAPER, SHAPER, PKT_ENQUEUE, Flow->PsFlowContext, Packet, PacketInfo->PacketLength, 0, CurrentTime.QuadPart, PacketInfo->DelayTime.QuadPart, Pipe->PacketsInShaper, 0);
PacketInfo->ClassMapContext = ClassMapContext; InsertTailList(&Flow->PacketQueue, &PacketInfo->SchedulerLinks);
/* update the eligibility timer of the flow.. */ Flow->FlowEligibilityTime.QuadPart = PacketInfo->DelayTime.QuadPart;
/* Conf time in ms and 10ms */ Ms.QuadPart = OS_TIME_TO_MILLISECS( Flow->FlowEligibilityTime.QuadPart ); TenMs.QuadPart = Ms.QuadPart >> TIMER_WHEEL_SHIFT;
CurrentTimeInMs.QuadPart = OS_TIME_TO_MILLISECS( CurrentTime.QuadPart); CurrentTimeInTenMs.QuadPart = CurrentTimeInMs.QuadPart >> TIMER_WHEEL_SHIFT;
/* Update the loop count too */ Flow->LoopCount = (ULONG)( (TenMs.QuadPart - CurrentTimeInTenMs.QuadPart) >> Pipe->TimerWheelShift );
if( Pipe->TimerStatus == TIMER_INACTIVE) { /* Figure out the Slot for this time.. */ Slot = (ULONG)( (TenMs.QuadPart) & ((1 << Pipe->TimerWheelShift) - 1 ) );
Pipe->SetTimerValue.QuadPart = TenMs.QuadPart - (Flow->LoopCount << Pipe->TimerWheelShift); Pipe->SetSlotValue = Slot;
/* Need to insert the flow to the timer-wheel in slot's position*/ pList = (PLIST_ENTRY)( (char*)Pipe->pTimerWheel + ( sizeof(LIST_ENTRY) * Slot) ); InsertTailList(pList, &Flow->Links);
Pipe->TimerStatus = TIMER_SET; NdisMSetTimer(&Pipe->Timer, (UINT)((Pipe->SetTimerValue.QuadPart - CurrentTimeInTenMs.QuadPart) << TIMER_WHEEL_SHIFT) ); } else if( Pipe->TimerStatus == TIMER_SET) { if( TenMs.QuadPart <= Pipe->SetTimerValue.QuadPart) { Flow->LoopCount = 0; /* Try to cancel the timer and re-set it */ NdisMCancelTimer( &Pipe->Timer, (PBOOLEAN)&Success );
if( Success) { /* Figure out the Slot for this time.. */ Slot = (ULONG)( (TenMs.QuadPart) & ((1 << Pipe->TimerWheelShift) - 1) );
// Pipe->SetTimerValue.QuadPart = TenMs.QuadPart - Flow->LoopCount * Pipe->TimerWheelSize ;
Pipe->SetTimerValue.QuadPart = TenMs.QuadPart - (Flow->LoopCount << Pipe->TimerWheelShift) ; Pipe->SetSlotValue = Slot;
/* Need to insert the flow to the timer-wheel in slot's position*/ pList = (PLIST_ENTRY)( (char*)Pipe->pTimerWheel + ( sizeof(LIST_ENTRY) * Slot) ); InsertTailList(pList, &Flow->Links);
NdisMSetTimer(&Pipe->Timer, (UINT)((Pipe->SetTimerValue.QuadPart - CurrentTimeInTenMs.QuadPart) << TIMER_WHEEL_SHIFT)); } else { /* Need to insert the flow to the timer-wheel in slot's position*/ pList = (PLIST_ENTRY)( (char*)Pipe->pTimerWheel + ( sizeof(LIST_ENTRY) * Pipe->SetSlotValue) ); InsertTailList(pList, &Flow->Links); } } else { Flow->LoopCount = (ULONG)( (TenMs.QuadPart - Pipe->SetTimerValue.QuadPart) >> Pipe->TimerWheelShift );
/* Figure out the Slot for this time.. */ Slot = (ULONG)( (TenMs.QuadPart) & ((1 << Pipe->TimerWheelShift) - 1) );
/* Need to insert the flow to the timer-wheel in slot's position*/ pList = (PLIST_ENTRY)( (char*)Pipe->pTimerWheel + ( sizeof(LIST_ENTRY) * Slot) ); InsertTailList(pList, &Flow->Links); } } else { PsAssert( Pipe->TimerStatus == TIMER_PROC_EXECUTING);
if( TenMs.QuadPart <= Pipe->ExecTimerValue.QuadPart) { PsAssert( Flow->LoopCount == 0); Slot = (ULONG)((Pipe->ExecSlot + 1) & ((1 << Pipe->TimerWheelShift) - 1) );
/* Need to insert the flow to the timer-wheel in slot's position*/ pList = (PLIST_ENTRY)( (char*)Pipe->pTimerWheel + ( sizeof(LIST_ENTRY) * Slot) ); InsertTailList(pList, &Flow->Links); } else { Flow->LoopCount = (ULONG)( (TenMs.QuadPart - Pipe->ExecTimerValue.QuadPart) >> Pipe->TimerWheelShift );
/* Figure out the Slot for this time.. */ Slot = (ULONG)( (TenMs.QuadPart) & ((1 << Pipe->TimerWheelShift) - 1) );
if( Slot == Pipe->ExecSlot) Slot = ( (Slot + 1) & ((1 << Pipe->TimerWheelShift) - 1) );
/* Need to insert the flow to the timer-wheel in slot's position*/ pList = (PLIST_ENTRY)( (char*)Pipe->pTimerWheel + ( sizeof(LIST_ENTRY) * Slot) ); InsertTailList(pList, &Flow->Links); } } }
Pipe->PacketsInShaper++; if(Pipe->PacketsInShaper > Pipe->sStats.MaxPacketsInShaper){ Pipe->sStats.MaxPacketsInShaper = Pipe->PacketsInShaper; } Flow->PacketsInShaper++; if (Flow->PacketsInShaper > Flow->sStats.MaxPacketsInShaper) { Flow->sStats.MaxPacketsInShaper = Flow->PacketsInShaper; }
if(gEnableAvgStats) { Pipe->sStats.AveragePacketsInShaper = RunningAverage(Pipe->PacketsInShaperAveragingArray, Pipe->PacketsInShaper);
Flow->sStats.AveragePacketsInShaper = RunningAverage(Flow->PacketsInShaperAveragingArray, Flow->PacketsInShaper); }
UNLOCK_PIPE(Pipe);
return TRUE;
} // TbcSubmitPacket
VOID TbcSetInformation ( IN PPS_PIPE_CONTEXT ComponentPipeContext, IN PPS_FLOW_CONTEXT ComponentFlowContext, IN NDIS_OID Oid, IN ULONG Len, IN PVOID Data) { PTBC_PIPE Pipe = (PTBC_PIPE)ComponentPipeContext; PTBC_FLOW Flow = (PTBC_FLOW)ComponentFlowContext;
switch(Oid) { case OID_QOS_STATISTICS_BUFFER:
if(Flow) { NdisZeroMemory(&Flow->cStats, sizeof(PS_CONFORMER_STATS)); NdisZeroMemory(&Flow->sStats, sizeof(PS_SHAPER_STATS)); } else { NdisZeroMemory(&Pipe->cStats, sizeof(PS_CONFORMER_STATS)); NdisZeroMemory(&Pipe->sStats, sizeof(PS_SHAPER_STATS)); }
break; default: break; }
(*Pipe->ContextInfo.NextComponent->SetInformation)( Pipe->ContextInfo.NextComponentContext, (Flow)?Flow->ContextInfo.NextComponentContext:0, Oid, Len, Data); }
VOID TbcQueryInformation ( 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) { PTBC_PIPE Pipe = (PTBC_PIPE)ComponentPipeContext; PTBC_FLOW Flow = (PTBC_FLOW)ComponentFlowContext; ULONG Size; ULONG cSize, sSize; ULONG RemainingLength;
switch(Oid) { case OID_QOS_STATISTICS_BUFFER:
cSize = sizeof(PS_CONFORMER_STATS) + FIELD_OFFSET(PS_COMPONENT_STATS, Stats); sSize = sizeof(PS_SHAPER_STATS) + FIELD_OFFSET(PS_COMPONENT_STATS, Stats); Size = cSize + sSize;
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) { // Per flow stats
Cstats->Type = PS_COMPONENT_CONFORMER; Cstats->Length = sizeof(PS_CONFORMER_STATS); NdisMoveMemory(&Cstats->Stats, &Flow->cStats, sizeof(PS_CONFORMER_STATS));
// Move the pointer to point after the conf. stats.. //
Cstats = (PPS_COMPONENT_STATS)((PUCHAR)Cstats + cSize);
Cstats->Type = PS_COMPONENT_SHAPER; Cstats->Length = sizeof(PS_SHAPER_STATS); NdisMoveMemory(&Cstats->Stats, &Flow->sStats, sizeof(PS_SHAPER_STATS)); } else { // Per adapter stats
Cstats->Type = PS_COMPONENT_CONFORMER; Cstats->Length = sizeof(PS_CONFORMER_STATS); NdisMoveMemory(&Cstats->Stats, &Pipe->cStats, sizeof(PS_CONFORMER_STATS));
// Move the pointer to point after the shaper. stats.. //
Cstats = (PPS_COMPONENT_STATS)((PUCHAR)Cstats + cSize); Cstats->Type = PS_COMPONENT_SHAPER; Cstats->Length = sizeof(PS_SHAPER_STATS); NdisMoveMemory(&Cstats->Stats, &Pipe->sStats, sizeof(PS_SHAPER_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); }
|