windows-nt/Source/XPSP1/NT/net/qos/psched/sys/conformr.c
2020-09-26 16:20:57 +08:00

2231 lines
69 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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