661 lines
21 KiB
C
661 lines
21 KiB
C
|
/*++
|
||
|
|
||
|
Copyright (c) 1996,1997 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
STATS.C
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
Session Statistics routines
|
||
|
|
||
|
Author:
|
||
|
|
||
|
Aaron Ogus (aarono)
|
||
|
|
||
|
Environment:
|
||
|
|
||
|
Win32/COM
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
Date Author Description
|
||
|
====== ====== ============================================================
|
||
|
7/30/97 aarono Original
|
||
|
6/6/98 aarono Turn on throttling and windowing
|
||
|
|
||
|
--*/
|
||
|
|
||
|
#include <windows.h>
|
||
|
#include "newdpf.h"
|
||
|
#include <dplay.h>
|
||
|
#include <dplaysp.h>
|
||
|
#include <dplaypr.h>
|
||
|
#include "mydebug.h"
|
||
|
#include "arpd.h"
|
||
|
#include "arpdint.h"
|
||
|
#include "protocol.h"
|
||
|
#include "macros.h"
|
||
|
#include "command.h"
|
||
|
|
||
|
#define STARTING_LONG_LATENCY 1 /* 1 ms (intentionally low so first sample fills) */
|
||
|
#define STARTING_SHORT_LATENCY 15000 /* 15 seconds (intentionally high so first sample fills) */
|
||
|
#define STARTING_AVERAGE_LATENCY 2000 /* 2 seconds (good start for internet) */
|
||
|
#define STARTING_AVERAGE_DEVIATION 0
|
||
|
|
||
|
#define STARTING_MAXRETRY 16 /* Maximum number of retries */
|
||
|
#define STARTING_MINDROPTIME 15000 /* Minimum time to retry before dropping connection (ms) */
|
||
|
#define STARTING_MAXDROPTIME 60000 /* Maximum time to retry before dropping connection (ms) */
|
||
|
|
||
|
#define STARTING_BANDWIDTH (28800/10) /* 28 kbps modem */
|
||
|
|
||
|
#define LATENCY_SHORT_BITS 4
|
||
|
#define LATENCY_LONG_BITS 7
|
||
|
|
||
|
#define STAT_LOCAL_LATENCY_SAMPLES 2^(LATENCY_SHORT_BITS) /* 2^4 */
|
||
|
#define STAT_LONG_LATENCY_SAMPLES 2^(LATENCY_LONG_BITS) /* 2^7 */
|
||
|
|
||
|
#define TARGET_CLOCK_OFFSET 10000000
|
||
|
|
||
|
|
||
|
#define Fp(_x) ((_x)<<8)
|
||
|
#define unFp(_x)((_x)>>8)
|
||
|
|
||
|
// Latency Averages and deviation averages are stored as fixed point 24.8
|
||
|
|
||
|
VOID InitSessionStats(PSESSION pSession)
|
||
|
{
|
||
|
pSession->ShortestLatency = STARTING_SHORT_LATENCY;
|
||
|
pSession->LongestLatency = STARTING_LONG_LATENCY;
|
||
|
|
||
|
pSession->FpAverageLatency = 1000;
|
||
|
pSession->FpLocalAverageLatency = 1000;
|
||
|
|
||
|
pSession->FpLocalAvgDeviation = 300;
|
||
|
pSession->FpAvgDeviation = 300;
|
||
|
|
||
|
pSession->Bandwidth = 28800/10;
|
||
|
pSession->HighestBandwidth=28800/10;
|
||
|
|
||
|
pSession->MaxRetry = STARTING_MAXRETRY;
|
||
|
pSession->MinDropTime = STARTING_MINDROPTIME;
|
||
|
pSession->MaxDropTime = STARTING_MAXDROPTIME;
|
||
|
}
|
||
|
|
||
|
|
||
|
// called with SESSIONLOCK.
|
||
|
VOID UpdateSessionStats(PSESSION pSession, PSENDSTAT pStat, PCMDINFO pCmdInfo, BOOL fBadDrop)
|
||
|
{
|
||
|
DWORD tLatency;
|
||
|
DWORD nBytesReceived;
|
||
|
DWORD tDeviation;
|
||
|
DWORD BytesLost=0;
|
||
|
DWORD BackLog=0;
|
||
|
|
||
|
DWORD fThrottleAdjusted=FALSE;
|
||
|
|
||
|
DWORD tRemoteDelta; // change in time on remote from last received ACK until this was ACKed.
|
||
|
|
||
|
DWORD tBiasedDelta; // a biased difference in local and remote clocks.
|
||
|
INT tDelta; // the unbiased difference (signed)
|
||
|
static DWORD cBiasReset;
|
||
|
|
||
|
|
||
|
// Get the statistics information we need.
|
||
|
tLatency = pCmdInfo->tReceived-pStat->tSent;
|
||
|
|
||
|
ASSERT((int)tLatency >= 0);
|
||
|
if(!tLatency){
|
||
|
DPF(8,"0ms observed latency, using 1ms\n");
|
||
|
tLatency=1;
|
||
|
}
|
||
|
|
||
|
Lock(&pSession->SessionStatLock);
|
||
|
|
||
|
// Calculates the number of bytes received at remote since this send was done.
|
||
|
pSession->RemoteBytesReceived = pCmdInfo->bytes;
|
||
|
pSession->tRemoteBytesReceived = pCmdInfo->tRemoteACK;
|
||
|
nBytesReceived = pSession->RemoteBytesReceived - pStat->RemoteBytesReceived;
|
||
|
|
||
|
BytesLost = pStat->LocalBytesSent-(pSession->RemoteBytesReceived+pSession->BytesLost);
|
||
|
|
||
|
if((int)BytesLost >= 0){
|
||
|
|
||
|
pSession->BytesLost += BytesLost;
|
||
|
|
||
|
// Note, Backlog may be as little as 1/2 this value.
|
||
|
BackLog = pSession->BytesSent -( pSession->RemoteBytesReceived + pSession->BytesLost );
|
||
|
|
||
|
if((int)BackLog < 0){
|
||
|
DPF(8,"Hmmm, upside down backlog?\n");
|
||
|
DPF(8,"pSession->BytesSent %d\n",pSession->BytesSent);
|
||
|
DPF(8,"pSession->RemoteBytesReceived %d\n",pSession->RemoteBytesReceived);
|
||
|
DPF(8,"pSession->BytesLost %d\n",pSession->BytesLost);
|
||
|
DPF(8,"Calculated BackLog %d\n",BackLog);
|
||
|
BackLog=0;
|
||
|
}
|
||
|
|
||
|
} else if((int)BytesLost < 0){
|
||
|
// Can be caused by out of order receives
|
||
|
DPF(1,"Out of order remote receive lots of these may affect throttling...\n");
|
||
|
DPF(8,"Hmmm, upside down byte counting?\n");
|
||
|
DPF(8,"pStat->LocalBytesSent %d\n",pStat->LocalBytesSent);
|
||
|
DPF(8,"pSession->RemoteBytesReceived %d\n",pSession->RemoteBytesReceived);
|
||
|
DPF(8,"pSession->BytesLost %d\n",pSession->BytesLost);
|
||
|
DPF(8,"Calculated Bytes Lost %d\n",BytesLost);
|
||
|
BytesLost=0;
|
||
|
// fixup lost count.
|
||
|
pSession->BytesLost=pSession->RemoteBytesReceived-pStat->LocalBytesSent;
|
||
|
}
|
||
|
|
||
|
Unlock(&pSession->SessionStatLock);
|
||
|
|
||
|
if(pSession->MaxCSends==1){
|
||
|
|
||
|
DWORD Bias;
|
||
|
|
||
|
// 1st ACK, adjust windows to normal operation.
|
||
|
pSession->MaxCSends = MAX_SMALL_CSENDS;
|
||
|
pSession->MaxCDGSends = MAX_SMALL_DG_CSENDS;
|
||
|
pSession->WindowSize = MAX_SMALL_WINDOW;
|
||
|
pSession->DGWindowSize = MAX_SMALL_WINDOW;
|
||
|
|
||
|
pSession->FpAverageLatency = 2*tLatency; // start high to avoid overthrottle
|
||
|
pSession->FpLocalAverageLatency = 2*tLatency;
|
||
|
|
||
|
pSession->FpLocalAvgDeviation = 1+tLatency/3;
|
||
|
pSession->FpAvgDeviation = 1+tLatency/3;
|
||
|
|
||
|
|
||
|
|
||
|
Bias = pCmdInfo->tRemoteACK - pStat->tSent;
|
||
|
|
||
|
if(Bias > TARGET_CLOCK_OFFSET){
|
||
|
Bias = -1*(Bias-TARGET_CLOCK_OFFSET);
|
||
|
} else {
|
||
|
Bias = TARGET_CLOCK_OFFSET - Bias;
|
||
|
}
|
||
|
|
||
|
pSession->RemAvgACKBias = Bias;
|
||
|
|
||
|
pSession->RemAvgACKDelta = (pCmdInfo->tRemoteACK - pStat->tSent)+pSession->RemAvgACKBias;
|
||
|
|
||
|
ASSERT(pSession->RemAvgACKDelta == TARGET_CLOCK_OFFSET);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Calculate shift in outbound latency.
|
||
|
//
|
||
|
tBiasedDelta = (pCmdInfo->tRemoteACK - pStat->tSent)+pSession->RemAvgACKBias;
|
||
|
tDelta = tBiasedDelta-TARGET_CLOCK_OFFSET;
|
||
|
|
||
|
if(tDelta < 0 || pStat->bResetBias || tDelta > (int)tLatency){
|
||
|
DWORD Bias;
|
||
|
|
||
|
// Either clock drift or lower server load shows latency down, so reset baseline.
|
||
|
|
||
|
Bias = pCmdInfo->tRemoteACK - pStat->tSent;
|
||
|
|
||
|
if(Bias > TARGET_CLOCK_OFFSET){
|
||
|
Bias = -1*(Bias-TARGET_CLOCK_OFFSET);
|
||
|
} else {
|
||
|
Bias = TARGET_CLOCK_OFFSET - Bias;
|
||
|
}
|
||
|
cBiasReset++;
|
||
|
|
||
|
pSession->RemAvgACKBias = Bias;
|
||
|
tBiasedDelta = (pCmdInfo->tRemoteACK - pStat->tSent)+pSession->RemAvgACKBias;
|
||
|
tDelta = tBiasedDelta-TARGET_CLOCK_OFFSET;
|
||
|
}
|
||
|
|
||
|
pSession->RemAvgACKDelta -= pSession->RemAvgACKDelta >> 7; // -1/128th
|
||
|
pSession->RemAvgACKDelta += tBiasedDelta >> 7; // +1/128th of new value
|
||
|
|
||
|
// keep the residue so we don't creep down due to rounding error.
|
||
|
pSession->RemAvgACKDeltaResidue += tBiasedDelta & 0x7f;
|
||
|
if(pSession->RemAvgACKDeltaResidue>>7){
|
||
|
pSession->RemAvgACKDelta += pSession->RemAvgACKDeltaResidue>>7;
|
||
|
pSession->RemAvgACKDeltaResidue &= 0x7f;
|
||
|
}
|
||
|
|
||
|
|
||
|
DPF(8,"tRemoteACK %d tSent %d Bias %d tBiasedDelta %d tDelta %d\n", pCmdInfo->tRemoteACK, pStat->tSent,
|
||
|
pSession->RemAvgACKBias, tBiasedDelta, tDelta);
|
||
|
|
||
|
|
||
|
//
|
||
|
// Update latency statistics
|
||
|
//
|
||
|
|
||
|
ASSERT(!(nBytesReceived & 0x80000000)); // received in interval +ve
|
||
|
ASSERT(!(tLatency & 0x80000000)); // latency is +ve
|
||
|
|
||
|
if(tLatency < pSession->ShortestLatency){
|
||
|
pSession->ShortestLatency=tLatency;
|
||
|
DPF(8,"Shortest Latency %d ms\n",tLatency);
|
||
|
}
|
||
|
|
||
|
if(tLatency > pSession->LongestLatency){
|
||
|
pSession->LongestLatency=tLatency;
|
||
|
DPF(8,"Longest Latency %d ms\n", tLatency);
|
||
|
}
|
||
|
|
||
|
pSession->LastLatency=tLatency;
|
||
|
|
||
|
// Throw out 1/16 of local latency and add in the new statistic.
|
||
|
// Note we only use local latency for retry calculations.
|
||
|
|
||
|
if(pSession->FpLocalAverageLatency){
|
||
|
if(Fp(tLatency) > pSession->FpAverageLatency){
|
||
|
pSession->FpLocalAverageLatency -= (pSession->FpLocalAverageLatency >> LATENCY_SHORT_BITS);
|
||
|
pSession->FpLocalAverageLatency += (tLatency << (8-LATENCY_SHORT_BITS));
|
||
|
} else {
|
||
|
// Ratched down when we get a latency that is below average, so we can better
|
||
|
// detect backlog due to latency.
|
||
|
pSession->FpLocalAverageLatency = Fp(tLatency);
|
||
|
}
|
||
|
} else {
|
||
|
// this only happens once at startup.
|
||
|
pSession->FpLocalAverageLatency = Fp(tLatency);
|
||
|
pSession->FpAverageLatency = Fp(tLatency);
|
||
|
}
|
||
|
|
||
|
if(Fp(tLatency) > pSession->FpAverageLatency){
|
||
|
|
||
|
// Thow out 1/128 of average latency and add in the new statistic.
|
||
|
pSession->FpAverageLatency -= (pSession->FpAverageLatency >> LATENCY_LONG_BITS);
|
||
|
pSession->FpAverageLatency += (tLatency << (8-LATENCY_LONG_BITS));
|
||
|
|
||
|
} else {
|
||
|
// Ratched down when we get a latency that is below average, so we can better
|
||
|
// detect backlog due to latency.
|
||
|
pSession->FpAverageLatency = Fp(tLatency);
|
||
|
}
|
||
|
|
||
|
tDeviation=unFp(pSession->FpLocalAverageLatency)-tLatency;
|
||
|
if((int)tDeviation < 0){
|
||
|
tDeviation = 0-tDeviation;
|
||
|
}
|
||
|
|
||
|
pSession->FpLocalAvgDeviation -= (pSession->FpLocalAvgDeviation >> LATENCY_SHORT_BITS);
|
||
|
pSession->FpLocalAvgDeviation += (tDeviation << (8-LATENCY_SHORT_BITS));
|
||
|
|
||
|
pSession->FpAvgDeviation -= (pSession->FpAvgDeviation >> LATENCY_LONG_BITS);
|
||
|
pSession->FpAvgDeviation += (tDeviation << (8-LATENCY_LONG_BITS));
|
||
|
|
||
|
|
||
|
DPF(8,"Got ACK, tLat: %d Avg: %d.%d Dev: %d AvgDev: %d.%d \n",
|
||
|
tLatency, pSession->FpLocalAverageLatency >> 8, ((pSession->FpLocalAverageLatency&0xFF)*100)/256,
|
||
|
tDeviation, pSession->FpLocalAvgDeviation >> 8, ((pSession->FpLocalAvgDeviation&0xFF)*100)/256);
|
||
|
|
||
|
|
||
|
//
|
||
|
// Do Bandwidth calculations
|
||
|
//
|
||
|
|
||
|
tRemoteDelta= pCmdInfo->tRemoteACK - pStat->tRemoteBytesReceived;
|
||
|
if(!tRemoteDelta){
|
||
|
tRemoteDelta=1;
|
||
|
}
|
||
|
|
||
|
if(pStat->tRemoteBytesReceived){
|
||
|
pSession->Bandwidth = (1000*nBytesReceived)/(tRemoteDelta);
|
||
|
// could adjust throttle here if Bandwidth is higher, but this
|
||
|
// might pimp high speed links. (BUGBUG:).
|
||
|
} else {
|
||
|
// backup calculation, not as good. Only used early in the link
|
||
|
// before we have received an ACK from the remote prior to issuing
|
||
|
// a send.
|
||
|
pSession->Bandwidth = (2000*nBytesReceived)/tLatency; // 2000, not 1000 since tLatency is round trip.
|
||
|
}
|
||
|
if(pSession->Bandwidth > pSession->HighestBandwidth){
|
||
|
pSession->HighestBandwidth = pSession->Bandwidth;
|
||
|
}
|
||
|
|
||
|
|
||
|
DPF(8,"tRemoteDelta %d Remote bytes Received %d\n",tRemoteDelta,nBytesReceived);
|
||
|
|
||
|
// Adjust sending...
|
||
|
|
||
|
if ( BackLog && pSession->Bandwidth)
|
||
|
{
|
||
|
|
||
|
DWORD tAvgLat;
|
||
|
DWORD tBackLog;
|
||
|
DWORD ExcessBackLog; // amount of backlog (bytes) we need to clear before hitting avg latency again.
|
||
|
DWORD tLatCheck;
|
||
|
DWORD AvgLat133; // 133% of local average latency (tolerance for slow links)
|
||
|
DWORD AvgLat200; // 200% of local average latency (tolerance for fast links)
|
||
|
|
||
|
|
||
|
if(pSession->fFastLink){
|
||
|
tAvgLat=unFp(pSession->FpAverageLatency);
|
||
|
tLatCheck = (tAvgLat*3)/2;
|
||
|
AvgLat133 = max(100,3*unFp(pSession->FpAvgDeviation)+(unFp(pSession->FpAverageLatency)*4)/3); // don't throttle <100ms lat
|
||
|
AvgLat200 = max(100,3*unFp(pSession->FpAvgDeviation)+unFp(pSession->FpAverageLatency)*2);
|
||
|
} else {
|
||
|
tAvgLat=unFp(pSession->FpLocalAverageLatency);
|
||
|
tLatCheck = (tAvgLat*3)/2;
|
||
|
AvgLat133 = max(100,3*unFp(pSession->FpLocalAvgDeviation)+(unFp(pSession->FpLocalAverageLatency)*4)/3); // don't throttle <100ms lat
|
||
|
AvgLat200 = max(100,3*unFp(pSession->FpLocalAvgDeviation)+unFp(pSession->FpLocalAverageLatency)*2);
|
||
|
}
|
||
|
|
||
|
if(tLatCheck < AvgLat133){
|
||
|
tLatCheck = AvgLat133;
|
||
|
}
|
||
|
|
||
|
if(tLatency > tLatCheck){
|
||
|
// check link speed
|
||
|
if(pSession->fFastLink){
|
||
|
if(pSession->Bandwidth <= 10000){
|
||
|
pSession->fFastLink=FALSE;
|
||
|
}
|
||
|
} else {
|
||
|
if(pSession->Bandwidth >= 25000){
|
||
|
pSession->fFastLink=TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(pSession->fFastLink && tLatCheck < AvgLat200){
|
||
|
tLatCheck=AvgLat200;
|
||
|
}
|
||
|
|
||
|
DPF(8,"tLat %d, tLatCheck %d, tDelta %d, tLat/3 %d\n",tLatency,tLatCheck,tDelta,tLatency/3);
|
||
|
DPF(8,"pSession->ShortestLatency %d, Shortest+MaxPacketTime %d\n",pSession->ShortestLatency,
|
||
|
pSession->ShortestLatency+(pSession->MaxPacketSize*1000)/pSession->Bandwidth);
|
||
|
|
||
|
|
||
|
if((tLatency > tLatCheck && tDelta > (int)(tLatency/3)) ||
|
||
|
((!pSession->fFastLink)&&
|
||
|
(tLatency > pSession->ShortestLatency+((pSession->MaxPacketSize*2000)/pSession->Bandwidth))
|
||
|
)
|
||
|
)
|
||
|
{
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
if(pSession->SendRateThrottle){
|
||
|
DPF(8,"BackLog %d, SendRate %d BackLog ms %d, tLatency %d tAvgLat %d Used Bandwidth %d tBacklog %d \n",
|
||
|
BackLog,
|
||
|
pSession->SendRateThrottle,
|
||
|
(BackLog*1000 / pSession->SendRateThrottle),
|
||
|
tLatency,
|
||
|
tAvgLat,
|
||
|
pSession->Bandwidth,
|
||
|
((BackLog*1000) / pSession->Bandwidth)
|
||
|
);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
tBackLog = (BackLog * 1000)/pSession->Bandwidth;
|
||
|
|
||
|
if(tBackLog > 4*tLatency){
|
||
|
DPF(8,"1: tBackLog %d was >> tLatency %d, using 4*tLatency instead\n",tBackLog,tLatency);
|
||
|
tBackLog=4*tLatency; //never wait more than 4 latency periods
|
||
|
}
|
||
|
if(tBackLog > 8000){
|
||
|
DPF(8,"Disalowing backlog > 8 seconds, using 8 instead\n");
|
||
|
tBackLog=8000;
|
||
|
}
|
||
|
|
||
|
// if the backlog is greater than the bandwidth*latency, then we need to slow down our sending.
|
||
|
// don't slow down due to backlog until we are over 100ms on way latency (200 round trip)
|
||
|
|
||
|
if((tBackLog > 200) && (tBackLog > tAvgLat)){
|
||
|
|
||
|
BOOL fWait=TRUE;
|
||
|
|
||
|
// at max we cut send rate in 1/2.
|
||
|
|
||
|
if(pSession->SendRateThrottle/2 > pSession->Bandwidth){
|
||
|
DPF(8,"Asked for too aggresive throttle adjust %d, going from %d to %d\n",pSession->Bandwidth,pSession->SendRateThrottle,pSession->SendRateThrottle/2);
|
||
|
pSession->SendRateThrottle /= 2;
|
||
|
// Recheck if we are really backlogged at the new rate
|
||
|
tBackLog = (BackLog * 1000)/pSession->SendRateThrottle;
|
||
|
if(tBackLog > tLatency){
|
||
|
DPF(8,"2: tBackLog %d was > tLatency %d, using tLatency instead\n",tBackLog,tLatency);
|
||
|
tBackLog=tLatency;// never wait more than last latency period
|
||
|
}
|
||
|
} else {
|
||
|
// set new throttle rate and current observed bandwidth (+5% to avoid overthrottle)
|
||
|
pSession->SendRateThrottle=pSession->Bandwidth+pSession->Bandwidth/16;
|
||
|
}
|
||
|
|
||
|
// don't adjust for a while.
|
||
|
pSession->bhitThrottle=FALSE;
|
||
|
pSession->tLastThrottleAdjust = pCmdInfo->tReceived;
|
||
|
|
||
|
if(fWait && (tBackLog > tAvgLat)){
|
||
|
|
||
|
ExcessBackLog = ((tBackLog-tAvgLat)*pSession->Bandwidth)/1000;
|
||
|
|
||
|
DPF(8,"Throttling back due to BACKLOG, excess = %d\n",ExcessBackLog);
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
if(tBackLog-tAvgLat > 30000){
|
||
|
DPF(5,"WARNING: BACKLOG THROTTLE %d ms seems kinda large\n",tBackLog-tAvgLat);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
// wait until backlog is down to avg latency before sending again
|
||
|
Lock(&pSession->SessionStatLock);
|
||
|
pSession->bResetBias = 2; // could be in the middle of a send, so count down from 2.
|
||
|
Unlock(&pSession->SessionStatLock);
|
||
|
UpdateSendTime(pSession,ExcessBackLog,timeGetTime(),TRUE);
|
||
|
} else {
|
||
|
DPF(8,"Not throttling due to BACKLOG because of smaller adjustment\n");
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
DPF(8,"NOT Throttling back due to BACKLOG\n");
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else if(tDelta > (int)tLatency) {
|
||
|
// tDelta is bogus due to clock drift, force throttle so we can correct.
|
||
|
Lock(&pSession->SessionStatLock);
|
||
|
pSession->bResetBias=2;
|
||
|
Unlock(&pSession->SessionStatLock);
|
||
|
pSession->tNextSend=timeGetTime()+2*tLatency;
|
||
|
DPF(8,"tDelta %d > tLatency %d, need to correct for clock drift, time %d set next send time to %d\n", tDelta, tLatency,timeGetTime(),pSession->tNextSend);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Adjust Throttle if not already adjusted.
|
||
|
//
|
||
|
|
||
|
if((pSession->ThrottleState==Begin) ||
|
||
|
(pCmdInfo->tReceived-pSession->tLastThrottleAdjust) > (1+1*pSession->fFastLink)*unFp(pSession->FpLocalAverageLatency) )
|
||
|
{
|
||
|
if(!fThrottleAdjusted){
|
||
|
DPF(8,"Current Send Rate %d\n", pSession->SendRateThrottle);
|
||
|
if(!BytesLost && pSession->bhitThrottle){
|
||
|
pSession->bhitThrottle=FALSE;
|
||
|
pSession->tLastThrottleAdjust = pCmdInfo->tReceived;
|
||
|
// Good Send, push up send rate if we hit throttle.
|
||
|
switch(pSession->ThrottleState){
|
||
|
case Begin:
|
||
|
pSession->SendRateThrottle = (pSession->SendRateThrottle*(100+START_GROWTH_RATE))/100;
|
||
|
pSession->GrowCount++;
|
||
|
pSession->ShrinkCount=0;
|
||
|
break;
|
||
|
|
||
|
case MetaStable:
|
||
|
pSession->SendRateThrottle = (pSession->SendRateThrottle*(100+METASTABLE_GROWTH_RATE))/100;
|
||
|
pSession->GrowCount++;
|
||
|
pSession->ShrinkCount=0;
|
||
|
break;
|
||
|
|
||
|
case Stable:
|
||
|
pSession->SendRateThrottle = (pSession->SendRateThrottle*(100+STABLE_GROWTH_RATE))/100;
|
||
|
pSession->GrowCount++;
|
||
|
pSession->ShrinkCount=0;
|
||
|
if(pSession->GrowCount > (UINT)(20+60*pSession->fFastLink)){
|
||
|
pSession->ThrottleState = MetaStable;
|
||
|
pSession->GrowCount=0;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
DPF(0,"Session in wierd ThrottleState %d\n",pSession->ThrottleState);
|
||
|
break;
|
||
|
}
|
||
|
DPF(8,"Successful Send Adjusted Throttle, SendRate %d\n",pSession->SendRateThrottle);
|
||
|
} else if(BytesLost){
|
||
|
// Figure out how much we dropped
|
||
|
if(fBadDrop || (BytesLost > pSession->pProtocol->m_dwSPMaxFrame)){
|
||
|
// Very bad send, back off
|
||
|
pSession->tLastThrottleAdjust = pCmdInfo->tReceived;
|
||
|
switch(pSession->ThrottleState){
|
||
|
case Begin:
|
||
|
pSession->SendRateThrottle = (pSession->SendRateThrottle*(100-START_ADJUST_LARGE_ERR))/100;
|
||
|
pSession->GrowCount=0;
|
||
|
pSession->ShrinkCount++;
|
||
|
break;
|
||
|
case MetaStable:
|
||
|
pSession->SendRateThrottle = (pSession->SendRateThrottle*(100-METASTABLE_ADJUST_LARGE_ERR))/100;
|
||
|
pSession->GrowCount=0;
|
||
|
pSession->ShrinkCount++;
|
||
|
break;
|
||
|
case Stable:
|
||
|
pSession->SendRateThrottle = (pSession->SendRateThrottle*(100-STABLE_ADJUST_LARGE_ERR))/100;
|
||
|
pSession->ShrinkCount++;
|
||
|
if(pSession->ShrinkCount > 1){
|
||
|
pSession->ShrinkCount=0;
|
||
|
pSession->GrowCount=0;
|
||
|
pSession->ThrottleState=MetaStable;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
DPF(0,"Session in wierd ThrottleState %d\n",pSession->ThrottleState);
|
||
|
break;
|
||
|
}
|
||
|
DPF(8,"VERY BAD SEND Adjusted Throttle, SendRate %d\n",pSession->SendRateThrottle);
|
||
|
} else {
|
||
|
// Bad send, back off a bit
|
||
|
pSession->tLastThrottleAdjust = pCmdInfo->tReceived;
|
||
|
switch(pSession->ThrottleState){
|
||
|
case Begin:
|
||
|
pSession->SendRateThrottle = (pSession->SendRateThrottle*(100-START_ADJUST_SMALL_ERR))/100;
|
||
|
pSession->GrowCount=0;
|
||
|
pSession->ShrinkCount=0;
|
||
|
pSession->ThrottleState = MetaStable;
|
||
|
break;
|
||
|
case MetaStable:
|
||
|
pSession->SendRateThrottle = (pSession->SendRateThrottle*(100-METASTABLE_ADJUST_SMALL_ERR))/100;
|
||
|
pSession->ShrinkCount++;
|
||
|
pSession->GrowCount=0;
|
||
|
break;
|
||
|
case Stable:
|
||
|
pSession->SendRateThrottle = (pSession->SendRateThrottle*(100-STABLE_ADJUST_SMALL_ERR))/100;
|
||
|
pSession->ShrinkCount++;
|
||
|
pSession->GrowCount=0;
|
||
|
if(pSession->ShrinkCount > 2){
|
||
|
pSession->ShrinkCount=0;
|
||
|
pSession->ThrottleState = MetaStable;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
DPF(0,"Session in wierd ThrottleState %d\n",pSession->ThrottleState);
|
||
|
break;
|
||
|
}
|
||
|
DPF(8,"BAD SEND Adjusted Throttle, SendRate %d\n",pSession->SendRateThrottle);
|
||
|
} /* if (BadDrop... ) */
|
||
|
|
||
|
} /* if (BytesLost ...) */
|
||
|
|
||
|
}/*if (ThrottleAdjusted) */
|
||
|
|
||
|
}
|
||
|
|
||
|
if(!BytesLost && pSession->Bandwidth && pSession->SendRateThrottle < pSession->Bandwidth){
|
||
|
DPF(8,"Avoid goofyness, throttle was %d, setting to observed bandwidth %d\n",pSession->SendRateThrottle,pSession->Bandwidth);
|
||
|
pSession->SendRateThrottle=pSession->Bandwidth;
|
||
|
}
|
||
|
if(pSession->SendRateThrottle < 100){
|
||
|
DPF(8,"WARNING: SendRateThrottle %d below 100, keeping at 100 to avoid starvation\n",pSession->SendRateThrottle);
|
||
|
pSession->SendRateThrottle=100;
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
{
|
||
|
IN_WRITESTATS InWS;
|
||
|
memset((PVOID)&InWS,0xFF,sizeof(IN_WRITESTATS));
|
||
|
|
||
|
InWS.stat_ThrottleRate = pSession->SendRateThrottle;
|
||
|
InWS.stat_BytesSent = pSession->BytesSent;
|
||
|
InWS.stat_BackLog = BackLog;
|
||
|
InWS.stat_BytesLost = pSession->BytesLost;
|
||
|
//InWS.stat_RemBytesReceived;
|
||
|
InWS.stat_Latency = tLatency;
|
||
|
InWS.stat_MinLatency=pSession->ShortestLatency;
|
||
|
InWS.stat_AvgLatency=unFp(pSession->FpLocalAverageLatency);
|
||
|
InWS.stat_AvgDevLatency=unFp(pSession->FpLocalAvgDeviation);
|
||
|
//InWS.stat_USER1=
|
||
|
//InWS.stat_USER2=
|
||
|
//InWS.stat_USER3=
|
||
|
InWS.stat_USER5 = tDelta;
|
||
|
InWS.stat_USER6 = cBiasReset;
|
||
|
|
||
|
DbgWriteStats(&InWS);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
DPF(8,"Bandwidth %d, Highest %d\n",pSession->Bandwidth, pSession->HighestBandwidth);
|
||
|
|
||
|
}
|
||
|
|
||
|
// Called with SessionLock and SendLock
|
||
|
// Statistics are stored on the send in send order on a BILINK.
|
||
|
// most recent sends are at the end of the list. We scan from
|
||
|
// the end of the list to the beginning until we find the SENDSTAT
|
||
|
// that records the sequence and serial we got ACKED. We then
|
||
|
// update our statistics and throw out all SENDSTATs
|
||
|
// before this entry.
|
||
|
VOID UpdateSessionSendStats(PSESSION pSession, PSEND pSend, PCMDINFO pCmdInfo, BOOL fBadDrop)
|
||
|
{
|
||
|
PSENDSTAT pStatWalker,pStat=NULL;
|
||
|
BILINK *pStatBilink;
|
||
|
|
||
|
pSend->tLastACK=pCmdInfo->tReceived;
|
||
|
pSend->RetryCount=0;
|
||
|
// Find the last STAT for this ACK.
|
||
|
pStatBilink=pSend->StatList.prev;
|
||
|
|
||
|
while(pStatBilink != &pSend->StatList){
|
||
|
pStatWalker=CONTAINING_RECORD(pStatBilink, SENDSTAT, StatList);
|
||
|
if(pStatWalker->serial==pCmdInfo->serial &&
|
||
|
pStatWalker->sequence==pCmdInfo->sequence)
|
||
|
{
|
||
|
ASSERT(pStatWalker->messageid==pSend->messageid);
|
||
|
ASSERT(pSend->messageid==pCmdInfo->messageid);
|
||
|
pStat=pStatWalker;
|
||
|
break;
|
||
|
}
|
||
|
pStatBilink=pStatBilink->prev;
|
||
|
}
|
||
|
|
||
|
if(pStat){
|
||
|
UpdateSessionStats(pSession,pStat,pCmdInfo,fBadDrop);
|
||
|
|
||
|
// Unlink All Previous SENDSTATS;
|
||
|
pStat->StatList.next->prev=&pSend->StatList;
|
||
|
pSend->StatList.next=pStat->StatList.next;
|
||
|
|
||
|
// Put the SENDSTATS back in the pool.
|
||
|
while(pStatBilink != &pSend->StatList){
|
||
|
pStatWalker=CONTAINING_RECORD(pStatBilink, SENDSTAT, StatList);
|
||
|
pStatBilink=pStatBilink->prev;
|
||
|
ReleaseSendStat(pStatWalker);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
|