You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2238 lines
71 KiB
2238 lines
71 KiB
/*++
|
|
|
|
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);
|
|
}
|
|
|