/*++ 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); }