1209 lines
32 KiB
C++
1209 lines
32 KiB
C++
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name :
|
||
|
||
abw2.cxx
|
||
|
||
Abstract:
|
||
This module implements functions required for bandwidth throttling
|
||
of network usage by ATQ module.
|
||
|
||
Author:
|
||
|
||
Murali R. Krishnan ( MuraliK ) 1-June-1995
|
||
Bilal Alam ( t-bilala ) 7-March-1997
|
||
|
||
Environment:
|
||
|
||
User Mode -- Win32
|
||
|
||
Project:
|
||
|
||
Internet Services Asynchronous Thread Queue DLL
|
||
|
||
--*/
|
||
|
||
#include "isatq.hxx"
|
||
|
||
//
|
||
// Global variables
|
||
//
|
||
|
||
extern PBANDWIDTH_INFO g_pBandwidthInfo;
|
||
|
||
//
|
||
// Bandwidth Info shared variables
|
||
//
|
||
|
||
CRITICAL_SECTION BANDWIDTH_INFO::sm_csSharedLock;
|
||
LIST_ENTRY BANDWIDTH_INFO::sm_BornListHead;
|
||
LIST_ENTRY BANDWIDTH_INFO::sm_ActiveListHead;
|
||
DWORD BANDWIDTH_INFO::sm_cBornList;
|
||
DWORD BANDWIDTH_INFO::sm_cActiveList;
|
||
ALLOC_CACHE_HANDLER* BANDWIDTH_INFO::sm_pachBWInfos;
|
||
BOOL BANDWIDTH_INFO::sm_fGlobalEnabled;
|
||
BOOL BANDWIDTH_INFO::sm_fGlobalActive;
|
||
DWORD BANDWIDTH_INFO::sm_cNonInfinite;
|
||
DWORD BANDWIDTH_INFO::sm_cSamplesForTimeout;
|
||
|
||
//
|
||
// BANDWIDTH_INFO methods
|
||
//
|
||
|
||
VOID
|
||
BANDWIDTH_INFO::Initialize(
|
||
IN BOOL fPersistent
|
||
)
|
||
/*++
|
||
Initialize bandwidth info object. This is a pseudo-constructor for the
|
||
class.
|
||
|
||
Arguments:
|
||
fPersistent - TRUE if this object is destroyed explicitly
|
||
FALSE if destroyed when refcount hits 0
|
||
|
||
Returns:
|
||
None
|
||
|
||
--*/
|
||
{
|
||
_fMemberOfActiveList = FALSE;
|
||
_bandwidth.dwSpecifiedLevel = INFINITE;
|
||
_bandwidth.dwLowThreshold = INFINITE;
|
||
_bandwidth.dwHighThreshold = INFINITE;
|
||
_cMaxBlockedList = INFINITE;
|
||
_fEnabled = FALSE;
|
||
_Signature = ATQ_BW_INFO_SIGNATURE;
|
||
_fIsFreed = FALSE;
|
||
_fPersistent = fPersistent;
|
||
_cReference = 1;
|
||
|
||
INITIALIZE_CRITICAL_SECTION( &_csPrivateLock );
|
||
InitializeListHead( &_BlockedListHead );
|
||
|
||
ZeroMemory( _rgBytesXfered, sizeof( _rgBytesXfered ) );
|
||
|
||
_pBytesXferCur = _rgBytesXfered; // points to start of array
|
||
_cbXfered.QuadPart = 0;
|
||
|
||
_pStatus = &sm_rgStatus[ ZoneLevelLow ][ 0 ];
|
||
|
||
ClearStatistics();
|
||
|
||
AddToBornList();
|
||
|
||
SetDescription( "Default" );
|
||
}
|
||
|
||
VOID
|
||
BANDWIDTH_INFO::Terminate( VOID )
|
||
/*++
|
||
Destroys bandwidth info object. This is a pseudo-destructor.
|
||
|
||
Arguments:
|
||
None
|
||
|
||
Returns:
|
||
None
|
||
|
||
--*/
|
||
{
|
||
Lock();
|
||
|
||
// first prevent any new requests from getting blocked
|
||
|
||
InterlockedExchangePointer( (PVOID *) &_pStatus,
|
||
(PVOID) &sm_rgStatus[ZoneLevelLow][0] );
|
||
|
||
// disable the descriptor
|
||
|
||
InterlockedExchange( (LPLONG) &_fEnabled, FALSE );
|
||
|
||
// now remove any blocked requests
|
||
|
||
ATQ_REQUIRE( CheckAndUnblockRequests() );
|
||
ATQ_ASSERT( _cCurrentBlockedRequests == 0 );
|
||
ATQ_ASSERT( IsListEmpty( &_BlockedListHead ) );
|
||
|
||
Unlock();
|
||
|
||
DeleteCriticalSection( &_csPrivateLock );
|
||
|
||
// remove self from shared bandwidth info list
|
||
|
||
SharedLock();
|
||
|
||
RemoveFromBornList();
|
||
|
||
SharedUnlock();
|
||
|
||
_Signature = ATQ_BW_INFO_SIGNATURE_FREE;
|
||
}
|
||
|
||
BOOL
|
||
BANDWIDTH_INFO::PrepareToFree( VOID )
|
||
{
|
||
InterlockedExchange( (LPLONG) &_fIsFreed, TRUE );
|
||
Dereference();
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL
|
||
BANDWIDTH_INFO::BlockRequest(
|
||
IN OUT PATQ_CONT pAtqContext
|
||
)
|
||
/*++
|
||
Block this request on the queue of requests waiting to be processed.
|
||
|
||
Arguments:
|
||
pAtqContext pointer to ATQ context information for request that needs
|
||
to be blocked.
|
||
|
||
Returns:
|
||
TRUE on success. FALSE if there are any errors.
|
||
(Use GetLastError() for details)
|
||
|
||
--*/
|
||
{
|
||
BOOL fRet = TRUE;
|
||
|
||
ATQ_ASSERT( pAtqContext != NULL);
|
||
ATQ_ASSERT( pAtqContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
||
ATQ_ASSERT( IsValidAtqOp( pAtqContext->arInfo.atqOp ) );
|
||
|
||
Lock();
|
||
|
||
if ( _cCurrentBlockedRequests == _cMaxBlockedList )
|
||
{
|
||
fRet = FALSE;
|
||
}
|
||
else
|
||
{
|
||
pAtqContext->SetFlag( ACF_BLOCKED );
|
||
|
||
InsertTailList( &_BlockedListHead, &pAtqContext->BlockedListEntry );
|
||
|
||
IncCurrentBlockedRequests();
|
||
}
|
||
|
||
Unlock();
|
||
|
||
return fRet;
|
||
}
|
||
|
||
BOOL
|
||
BANDWIDTH_INFO::RemoveFromBlockedList(
|
||
IN PATQ_CONT pAtqContext
|
||
)
|
||
/*++
|
||
This function forcibly removes an ATQ context from blocked list of requests.
|
||
|
||
Argument:
|
||
pAtqContext pointer to ATQ context whose request is in blocked list.
|
||
|
||
Returns:
|
||
TRUE on success and FALSE if there is any error.
|
||
--*/
|
||
{
|
||
if ( !pAtqContext->IsBlocked() ) {
|
||
|
||
// some other thread just removed this request from waiting list.
|
||
return TRUE;
|
||
}
|
||
|
||
Lock();
|
||
|
||
RemoveEntryList(&pAtqContext->BlockedListEntry);
|
||
|
||
DecCurrentBlockedRequests();
|
||
|
||
pAtqContext->ResetFlag( ACF_BLOCKED);
|
||
|
||
Unlock();
|
||
|
||
//
|
||
// On such a forcible removal, we may have to make a callback indicating
|
||
// failure. Ignored! To be done by the caller of this API.
|
||
//
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL
|
||
BANDWIDTH_INFO::UnblockRequest(
|
||
IN OUT PATQ_CONT pAtqContext
|
||
)
|
||
/*++
|
||
Unblocks this request from the queue of requests waiting to be processed.
|
||
Call this function only when
|
||
_pStatus[pAtqContext->atqOp] != StatusBlockOperation.
|
||
First, this function removes the request from queue of requests and processes
|
||
it according to status and operation to be performed.
|
||
If the status is AllowRequest ==> this function restarts the operation.
|
||
If the status is reject operation ==> rejects operation and invokes
|
||
call back function indicating the error status.
|
||
|
||
Call this function after lock()ing
|
||
|
||
|
||
Arguments:
|
||
pAtqContext pointer to ATQ context information for request that needs
|
||
to be unblocked.
|
||
|
||
Returns:
|
||
TRUE on success. FALSE if there are any errors.
|
||
(Use GetLastError() for details)
|
||
|
||
--*/
|
||
{
|
||
BOOL fRet = FALSE;
|
||
|
||
ATQ_ASSERT( pAtqContext != NULL);
|
||
ATQ_ASSERT( pAtqContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
||
|
||
// Remove the request from the blocked list entry
|
||
RemoveEntryList( &pAtqContext->BlockedListEntry);
|
||
DecCurrentBlockedRequests();
|
||
pAtqContext->ResetFlag( ACF_BLOCKED );
|
||
|
||
// Check and re enable the operation of pAtqContext
|
||
|
||
switch ( _pStatus[ pAtqContext->arInfo.atqOp ] ) {
|
||
|
||
case StatusAllowOperation:
|
||
|
||
IncTotalAllowedRequests();
|
||
switch ( pAtqContext->arInfo.atqOp) {
|
||
|
||
case AtqIoRead:
|
||
{
|
||
DWORD cbRead; // Discard after calling ReadFile()
|
||
DWORD dwFlags = 0;
|
||
|
||
// assume that this is a socket operation!
|
||
if ( pAtqContext->arInfo.uop.opReadWrite.dwBufferCount > 1) {
|
||
ATQ_ASSERT( NULL !=
|
||
pAtqContext->arInfo.uop.opReadWrite.pBufAll);
|
||
fRet =
|
||
((WSARecv( HANDLE_TO_SOCKET(pAtqContext->hAsyncIO),
|
||
pAtqContext->arInfo.uop.opReadWrite.pBufAll,
|
||
pAtqContext->arInfo.uop.opReadWrite.dwBufferCount,
|
||
&cbRead,
|
||
&dwFlags,
|
||
pAtqContext->arInfo.lpOverlapped,
|
||
NULL
|
||
) == 0)||
|
||
(WSAGetLastError() == WSA_IO_PENDING)
|
||
);
|
||
|
||
// free up the socket buffers
|
||
::LocalFree( pAtqContext->arInfo.uop.opReadWrite.pBufAll);
|
||
pAtqContext->arInfo.uop.opReadWrite.pBufAll = NULL;
|
||
} else {
|
||
WSABUF wsaBuf =
|
||
{ pAtqContext->arInfo.uop.opReadWrite.buf1.len,
|
||
pAtqContext->arInfo.uop.opReadWrite.buf1.buf
|
||
};
|
||
fRet = (( WSARecv( HANDLE_TO_SOCKET(pAtqContext->hAsyncIO),
|
||
&wsaBuf,
|
||
1,
|
||
&cbRead,
|
||
&dwFlags,
|
||
pAtqContext->arInfo.lpOverlapped,
|
||
NULL
|
||
) == 0)||
|
||
(WSAGetLastError() == WSA_IO_PENDING)
|
||
);
|
||
}
|
||
break;
|
||
}
|
||
|
||
case AtqIoWrite:
|
||
{
|
||
DWORD cbWrite; // Discard after calling WriteFile()
|
||
|
||
// assume that this is a socket operation!
|
||
if ( pAtqContext->arInfo.uop.opReadWrite.dwBufferCount > 1) {
|
||
ATQ_ASSERT( NULL !=
|
||
pAtqContext->arInfo.uop.opReadWrite.pBufAll);
|
||
fRet =
|
||
((WSASend( HANDLE_TO_SOCKET(pAtqContext->hAsyncIO),
|
||
pAtqContext->arInfo.uop.opReadWrite.pBufAll,
|
||
pAtqContext->arInfo.uop.opReadWrite.dwBufferCount,
|
||
&cbWrite,
|
||
0,
|
||
pAtqContext->arInfo.lpOverlapped,
|
||
NULL
|
||
) == 0)||
|
||
(WSAGetLastError() == WSA_IO_PENDING)
|
||
);
|
||
|
||
// free up the socket buffers
|
||
::LocalFree( pAtqContext->arInfo.uop.opReadWrite.pBufAll);
|
||
pAtqContext->arInfo.uop.opReadWrite.pBufAll = NULL;
|
||
} else {
|
||
WSABUF wsaBuf =
|
||
{ pAtqContext->arInfo.uop.opReadWrite.buf1.len,
|
||
pAtqContext->arInfo.uop.opReadWrite.buf1.buf
|
||
};
|
||
fRet = (( WSASend( HANDLE_TO_SOCKET(pAtqContext->hAsyncIO),
|
||
&wsaBuf,
|
||
1,
|
||
&cbWrite,
|
||
0,
|
||
pAtqContext->arInfo.lpOverlapped,
|
||
NULL
|
||
) == 0)||
|
||
(WSAGetLastError() == WSA_IO_PENDING)
|
||
);
|
||
}
|
||
break;
|
||
}
|
||
|
||
case AtqIoXmitFile:
|
||
{
|
||
fRet = g_pfnTransmitFile( HANDLE_TO_SOCKET(pAtqContext->hAsyncIO),
|
||
pAtqContext->arInfo.uop.opXmit.hFile,
|
||
pAtqContext->arInfo.uop.opXmit.
|
||
dwBytesInFile,
|
||
0,
|
||
pAtqContext->arInfo.lpOverlapped,
|
||
pAtqContext->arInfo.uop.
|
||
opXmit.lpXmitBuffers,
|
||
pAtqContext->arInfo.uop.
|
||
opXmit.dwFlags );
|
||
|
||
if ( !fRet && (GetLastError() == ERROR_IO_PENDING) ) {
|
||
fRet = TRUE;
|
||
}
|
||
break;
|
||
}
|
||
|
||
case AtqIoXmitFileRecv:
|
||
{
|
||
DWORD cbRead;
|
||
|
||
// assume that this is a socket operation!
|
||
if ( pAtqContext->arInfo.uop.opXmitRecv.dwBufferCount > 1) {
|
||
ATQ_ASSERT( NULL !=
|
||
pAtqContext->arInfo.uop.opXmitRecv.pBufAll);
|
||
fRet = I_AtqTransmitFileAndRecv
|
||
( (PATQ_CONTEXT) pAtqContext,
|
||
pAtqContext->arInfo.uop.opXmitRecv.hFile,
|
||
pAtqContext->arInfo.uop.opXmitRecv.dwBytesInFile,
|
||
pAtqContext->arInfo.uop.opXmitRecv.lpXmitBuffers,
|
||
pAtqContext->arInfo.uop.opXmitRecv.dwTFFlags,
|
||
pAtqContext->arInfo.uop.opXmitRecv.pBufAll,
|
||
pAtqContext->arInfo.uop.opXmitRecv.dwBufferCount
|
||
);
|
||
// free up the socket buffers
|
||
::LocalFree( pAtqContext->arInfo.uop.opXmitRecv.pBufAll);
|
||
pAtqContext->arInfo.uop.opXmitRecv.pBufAll = NULL;
|
||
}
|
||
else
|
||
{
|
||
WSABUF wsaBuf =
|
||
{ pAtqContext->arInfo.uop.opXmitRecv.buf1.len,
|
||
pAtqContext->arInfo.uop.opXmitRecv.buf1.buf
|
||
};
|
||
fRet = I_AtqTransmitFileAndRecv
|
||
( (PATQ_CONTEXT) pAtqContext,
|
||
pAtqContext->arInfo.uop.opXmitRecv.hFile,
|
||
pAtqContext->arInfo.uop.opXmitRecv.dwBytesInFile,
|
||
pAtqContext->arInfo.uop.opXmitRecv.lpXmitBuffers,
|
||
pAtqContext->arInfo.uop.opXmitRecv.dwTFFlags,
|
||
&wsaBuf,
|
||
1 );
|
||
}
|
||
|
||
if ( !fRet && GetLastError() == ERROR_IO_PENDING )
|
||
{
|
||
fRet = TRUE;
|
||
}
|
||
break;
|
||
}
|
||
|
||
case AtqIoSendRecv:
|
||
{
|
||
WSABUF wsaSendBuf =
|
||
{ pAtqContext->arInfo.uop.opSendRecv.sendbuf1.len,
|
||
pAtqContext->arInfo.uop.opSendRecv.sendbuf1.buf
|
||
};
|
||
|
||
WSABUF wsaRecvBuf =
|
||
{
|
||
pAtqContext->arInfo.uop.opSendRecv.recvbuf1.len,
|
||
pAtqContext->arInfo.uop.opSendRecv.recvbuf1.buf
|
||
};
|
||
|
||
LPWSABUF pSendBuf = &wsaSendBuf;
|
||
LPWSABUF pRecvBuf = &wsaRecvBuf;
|
||
|
||
if ( pAtqContext->arInfo.uop.opSendRecv.dwSendBufferCount > 1 )
|
||
{
|
||
pSendBuf = pAtqContext->arInfo.uop.opSendRecv.pSendBufAll;
|
||
}
|
||
|
||
if ( pAtqContext->arInfo.uop.opSendRecv.dwRecvBufferCount > 1 )
|
||
{
|
||
pRecvBuf = pAtqContext->arInfo.uop.opSendRecv.pRecvBufAll;
|
||
}
|
||
|
||
ATQ_ASSERT( pSendBuf != NULL );
|
||
ATQ_ASSERT( pRecvBuf != NULL );
|
||
|
||
fRet = I_AtqSendAndRecv
|
||
( (PATQ_CONTEXT) pAtqContext,
|
||
pSendBuf,
|
||
pAtqContext->arInfo.uop.opSendRecv.
|
||
dwSendBufferCount,
|
||
pRecvBuf,
|
||
pAtqContext->arInfo.uop.opSendRecv.
|
||
dwRecvBufferCount );
|
||
|
||
if ( pAtqContext->arInfo.uop.opSendRecv.pSendBufAll != NULL )
|
||
{
|
||
::LocalFree( pAtqContext->arInfo.uop.opSendRecv.pSendBufAll );
|
||
}
|
||
|
||
if ( pAtqContext->arInfo.uop.opSendRecv.pRecvBufAll != NULL )
|
||
{
|
||
::LocalFree( pAtqContext->arInfo.uop.opSendRecv.pSendBufAll );
|
||
}
|
||
|
||
if ( !fRet && (GetLastError() == ERROR_IO_PENDING ) ) {
|
||
fRet = TRUE;
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
default:
|
||
ATQ_ASSERT( FALSE);
|
||
break;
|
||
} // switch
|
||
|
||
pAtqContext->arInfo.atqOp = AtqIoNone; // reset since operation done.
|
||
break;
|
||
|
||
case StatusRejectOperation:
|
||
|
||
IncTotalRejectedRequests();
|
||
if ( ((pAtqContext->arInfo.atqOp == AtqIoRead) ||
|
||
(pAtqContext->arInfo.atqOp == AtqIoRead)) &&
|
||
(pAtqContext->arInfo.uop.opReadWrite.pBufAll != NULL)
|
||
) {
|
||
::LocalFree( pAtqContext->arInfo.uop.opReadWrite.pBufAll);
|
||
pAtqContext->arInfo.uop.opReadWrite.pBufAll = NULL;
|
||
}
|
||
pAtqContext->arInfo.atqOp = AtqIoNone; // reset since op rejected.
|
||
SetLastError( ERROR_NETWORK_BUSY);
|
||
fRet = FALSE;
|
||
break;
|
||
|
||
case StatusBlockOperation:
|
||
// do nothing. we cannot unblock
|
||
ATQ_ASSERT(FALSE);
|
||
return (TRUE);
|
||
|
||
default:
|
||
ATQ_ASSERT( FALSE);
|
||
break;
|
||
} // switch
|
||
|
||
if (!fRet) {
|
||
|
||
// Call the completion function to signify the error in operation.
|
||
|
||
//
|
||
// Reset the timeout value so requests don't
|
||
// timeout multiple times
|
||
//
|
||
|
||
InterlockedExchange(
|
||
(LPLONG ) &pAtqContext->NextTimeout,
|
||
ATQ_INFINITE
|
||
);
|
||
|
||
InterlockedDecrement( &pAtqContext->m_nIO);
|
||
|
||
pAtqContext->IOCompletion( 0,
|
||
GetLastError(),
|
||
pAtqContext->arInfo.lpOverlapped );
|
||
} // on failure.
|
||
|
||
return (fRet);
|
||
}
|
||
|
||
BOOL
|
||
BANDWIDTH_INFO::CheckAndUnblockRequests( VOID )
|
||
/*++
|
||
Checks the list of blocked requests and identifies all operations
|
||
that needs to be unblocked. This function unblocks those requests and
|
||
removes them from blocked list.
|
||
|
||
Always call this function after lock()ing
|
||
|
||
Returns:
|
||
TRUE on success and FALSE on failure.
|
||
|
||
--*/
|
||
{
|
||
BOOL fRet = TRUE;
|
||
|
||
//
|
||
// If the list is not empty, then check and process blocked requests.
|
||
//
|
||
|
||
if ( !IsListEmpty( &_BlockedListHead ) ) {
|
||
|
||
PLIST_ENTRY pentry;
|
||
|
||
//
|
||
// Scan the blocked requests list looking for pending requests
|
||
// that needs to be unblocked and unblock these requests.
|
||
//
|
||
|
||
for (pentry = _BlockedListHead.Flink;
|
||
pentry != &_BlockedListHead;
|
||
pentry = pentry->Flink )
|
||
{
|
||
PATQ_CONT pContext = CONTAINING_RECORD(pentry,
|
||
ATQ_CONTEXT,
|
||
BlockedListEntry );
|
||
|
||
if ( pContext->Signature != ATQ_CONTEXT_SIGNATURE)
|
||
{
|
||
ATQ_ASSERT( pContext->Signature == ATQ_CONTEXT_SIGNATURE );
|
||
fRet = FALSE;
|
||
break;
|
||
}
|
||
|
||
if ( !pContext->IsBlocked()) {
|
||
|
||
// This should not happen.
|
||
ATQ_ASSERT( !pContext->IsBlocked());
|
||
fRet = FALSE;
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Check to see if the status for operation has changed.
|
||
// If so, unblock the request.
|
||
//
|
||
|
||
if ( _pStatus[pContext->arInfo.atqOp] !=
|
||
StatusBlockOperation) {
|
||
|
||
fRet &= UnblockRequest( pContext );
|
||
}
|
||
|
||
} // scan list
|
||
}
|
||
|
||
return (fRet);
|
||
}
|
||
|
||
BOOL
|
||
BANDWIDTH_INFO::UpdateBandwidth(
|
||
VOID
|
||
)
|
||
/*++
|
||
This function updates the current bandwidth value using the histogram
|
||
of bytes transferred.
|
||
The histogram maintains a history of bytes transferred over different sample
|
||
periods of a single minute. Each entry in the histogram corresponds to one
|
||
interval of sample. The sum of all entries gives the total bytes transferred
|
||
in a single minute. We divide this measure by 60 to obtain the count of
|
||
bytes transferred over a second. This update bandwidth is used to
|
||
reevalute the tuner of bandwidth throttle based on our throttling policy
|
||
(specified in throttling algorithm). The updated action information is
|
||
used by subsequent requests.
|
||
In addition the _pcbXfered pointer is advanced forward using the
|
||
histogram entries as a circular buffer, to obtain the count of bytes
|
||
for next interval.
|
||
|
||
Arguments:
|
||
pdwPrivateBw - Filled with bandwidth for this descriptor
|
||
|
||
Returns:
|
||
TRUE on success. FALSE otherwise.
|
||
|
||
Note:
|
||
It is recommended that this function be called as infrequently as
|
||
possible, using reasonable sample intervals.
|
||
|
||
--*/
|
||
{
|
||
BOOL fRet = TRUE;
|
||
ZoneLevel zonelevel;
|
||
|
||
Lock();
|
||
|
||
// accumulate current byte count to global counter, to minimize computation
|
||
_cbXfered.QuadPart = _cbXfered.QuadPart + _pBytesXferCur->QuadPart;
|
||
|
||
//
|
||
// Current n/ws support a max of 1 to 100 MB/s. We can represent
|
||
// 4GB/s in a DWORD. Hence the cast is used. This may need revision later.
|
||
// Better yet, later we should store bandwidth as KB/seconds.
|
||
//
|
||
_dwMeasuredBw = (DWORD ) (_cbXfered.QuadPart/ATQ_AVERAGING_PERIOD);
|
||
|
||
CIRCULAR_INCREMENT( _pBytesXferCur, _rgBytesXfered, ATQ_HISTOGRAM_SIZE);
|
||
// Adjust the global cumulative bytes sent after increment.
|
||
_cbXfered .QuadPart = _cbXfered.QuadPart - _pBytesXferCur->QuadPart;
|
||
// Reset the counter to start with the new counter value.
|
||
_pBytesXferCur->QuadPart = 0;
|
||
|
||
//
|
||
// update the operation status depending upon the bandwidth comparisons.
|
||
// we use band/zone calculations to split the result into 3 zones.
|
||
// Depending upon the zone we update the global status pointer to
|
||
// appropriate row.
|
||
//
|
||
|
||
if ( _dwMeasuredBw < ATQ_LOW_BAND_THRESHOLD(_bandwidth)) {
|
||
|
||
//
|
||
// Lower zone. Modify the pointer to OPERATION_STATUS accordingly.
|
||
//
|
||
|
||
zonelevel = ZoneLevelLow;
|
||
|
||
} else if ( _dwMeasuredBw > ATQ_HIGH_BAND_THRESHOLD(_bandwidth)) {
|
||
|
||
//
|
||
// Higher zone. Modify the pointer to OPERATION_STATUS accordingly.
|
||
//
|
||
|
||
zonelevel = ZoneLevelHigh;
|
||
|
||
} else {
|
||
|
||
zonelevel = ZoneLevelMedium;
|
||
}
|
||
|
||
/*++
|
||
Above calculation can be implemented as:
|
||
zonelevel = (( sm_dwMeasuredBw > ATQ_LOW_BAND_THRESHOLD( sm_bandwidth)) +
|
||
( sm_dwMeasuredBw > ATQ_HIGH_BAND_THRESHOLD( sm_bandwidth)));
|
||
|
||
This is based on implicit dependence of ordering of ZoneLevel entries.
|
||
So avoided for present now.
|
||
--*/
|
||
|
||
if ( _pStatus != &sm_rgStatus[zonelevel][0]) {
|
||
|
||
// Status needs to be changed.
|
||
_pStatus = &sm_rgStatus[zonelevel][0];
|
||
|
||
// Examine and reenable blocked operations if any.
|
||
fRet &= CheckAndUnblockRequests();
|
||
}
|
||
|
||
// remove the bandwidth info object from the list if it is
|
||
// "inactive" (bandwidth = 0)
|
||
|
||
if ( !_dwMeasuredBw )
|
||
{
|
||
// there should be no requests in the blocked queue!
|
||
|
||
ATQ_ASSERT( _cCurrentBlockedRequests == 0 );
|
||
|
||
RemoveFromActiveList();
|
||
}
|
||
|
||
Unlock();
|
||
|
||
return fRet;
|
||
}
|
||
|
||
DWORD
|
||
BANDWIDTH_INFO::SetBandwidthLevel(
|
||
IN DWORD Data
|
||
)
|
||
/*++
|
||
Sets the bandwidth threshold
|
||
|
||
Arguments:
|
||
Data - Bandwidth threshold
|
||
|
||
Returns:
|
||
Old bandwidth threshold (DWORD)
|
||
--*/
|
||
{
|
||
DWORD dwOldVal;
|
||
INT iListDelta = 0;
|
||
|
||
Lock();
|
||
|
||
dwOldVal = _bandwidth.dwSpecifiedLevel;
|
||
|
||
if ( Data != INFINITE) {
|
||
|
||
DWORD dwTemp;
|
||
|
||
_bandwidth.dwSpecifiedLevel = ATQ_ROUNDUP_BANDWIDTH( Data );
|
||
dwTemp = ( Data *9)/10; //low threshold = 0.9*specified
|
||
_bandwidth.dwLowThreshold = ATQ_ROUNDUP_BANDWIDTH( dwTemp);
|
||
dwTemp = ( Data *11)/10; //high threshold= 1.1*specified
|
||
_bandwidth.dwHighThreshold = ATQ_ROUNDUP_BANDWIDTH( dwTemp);
|
||
|
||
_fEnabled = TRUE;
|
||
// we should recheck the throttling and blocked requests
|
||
// Will be done when the next timeout occurs in the ATQ Timeout Thread
|
||
|
||
if ( dwOldVal == INFINITE )
|
||
{
|
||
iListDelta = 1;
|
||
}
|
||
|
||
} else {
|
||
|
||
_bandwidth.dwSpecifiedLevel = INFINITE;
|
||
_bandwidth.dwLowThreshold = INFINITE;
|
||
_bandwidth.dwHighThreshold = INFINITE;
|
||
|
||
_fEnabled = FALSE;
|
||
|
||
// enable all operations, since we are in low zone
|
||
_pStatus = &sm_rgStatus[ZoneLevelLow][0];
|
||
|
||
// we should recheck and enable all blocked requests.
|
||
if ( _cCurrentBlockedRequests > 0) {
|
||
ATQ_REQUIRE( CheckAndUnblockRequests());
|
||
}
|
||
|
||
if ( dwOldVal != INFINITE )
|
||
{
|
||
iListDelta = -1;
|
||
}
|
||
}
|
||
|
||
Unlock();
|
||
|
||
// update the static counter of how many non-infinite throttles we have
|
||
|
||
if ( iListDelta )
|
||
{
|
||
SharedLock();
|
||
|
||
if ( iListDelta > 0 )
|
||
{
|
||
sm_cNonInfinite++;
|
||
}
|
||
else
|
||
{
|
||
sm_cNonInfinite--;
|
||
}
|
||
sm_fGlobalEnabled = !!sm_cNonInfinite;
|
||
|
||
SharedUnlock();
|
||
}
|
||
|
||
return dwOldVal;
|
||
|
||
}
|
||
|
||
DWORD
|
||
BANDWIDTH_INFO::SetMaxBlockedListSize(
|
||
IN DWORD cMaxSize
|
||
)
|
||
/*++
|
||
Sets the maximum size of blocked request list
|
||
|
||
Arguments:
|
||
cMaxSize - maximum size of list
|
||
|
||
Returns:
|
||
Old max size (DWORD)
|
||
--*/
|
||
{
|
||
DWORD cOldMax;
|
||
|
||
Lock();
|
||
|
||
cOldMax = _cMaxBlockedList;
|
||
_cMaxBlockedList = cMaxSize;
|
||
|
||
Unlock();
|
||
|
||
return cOldMax;
|
||
}
|
||
|
||
DWORD
|
||
BANDWIDTH_INFO::QueryBandwidthLevel( VOID )
|
||
/*++
|
||
Retrieve the current bandwidth level
|
||
|
||
Arguments:
|
||
None
|
||
|
||
Returns:
|
||
Set Bandwidth level (DWORD)
|
||
--*/
|
||
{
|
||
DWORD dwBw;
|
||
|
||
Lock();
|
||
|
||
dwBw = _bandwidth.dwSpecifiedLevel;
|
||
|
||
Unlock();
|
||
|
||
return dwBw;
|
||
}
|
||
|
||
BOOL
|
||
BANDWIDTH_INFO::ClearStatistics( VOID )
|
||
{
|
||
Lock();
|
||
|
||
_cTotalAllowedRequests = 0;
|
||
_cTotalBlockedRequests = 0;
|
||
_cTotalRejectedRequests = 0;
|
||
|
||
Unlock();
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL
|
||
BANDWIDTH_INFO::GetStatistics( OUT ATQ_STATISTICS * pAtqStats )
|
||
{
|
||
ATQ_ASSERT( pAtqStats != NULL );
|
||
|
||
pAtqStats->cRejectedRequests = _cTotalRejectedRequests;
|
||
pAtqStats->cBlockedRequests = _cTotalBlockedRequests;
|
||
pAtqStats->cAllowedRequests = _cTotalAllowedRequests;
|
||
pAtqStats->cCurrentBlockedRequests = _cCurrentBlockedRequests;
|
||
pAtqStats->MeasuredBandwidth = _dwMeasuredBw;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL
|
||
BANDWIDTH_INFO::UpdateAllBandwidths( VOID )
|
||
{
|
||
PLIST_ENTRY pEntry;
|
||
BOOL fRet = TRUE;
|
||
DWORD dwCounter = 0;
|
||
|
||
SharedLock();
|
||
|
||
for ( pEntry = sm_ActiveListHead.Flink;
|
||
pEntry != &sm_ActiveListHead; )
|
||
{
|
||
BANDWIDTH_INFO *pBandwidthInfo = CONTAINING_RECORD( pEntry,
|
||
BANDWIDTH_INFO,
|
||
_ActiveListEntry );
|
||
|
||
ATQ_ASSERT( pBandwidthInfo != NULL );
|
||
|
||
// we might be deleting this entry from the list. Grab the next
|
||
// link now before we do so the traversal can happen smoothly
|
||
|
||
pEntry = pEntry->Flink;
|
||
|
||
if ( !pBandwidthInfo->Enabled() )
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if ( !pBandwidthInfo->UpdateBandwidth() )
|
||
{
|
||
fRet = FALSE;
|
||
break;
|
||
}
|
||
|
||
#ifndef _NO_TRACING_
|
||
CHKINFO(( DBG_CONTEXT, "pBWInfo = %p (%s), Bandwidth = %u, Threshold = %d\n",
|
||
pBandwidthInfo,
|
||
pBandwidthInfo->_achDescription,
|
||
pBandwidthInfo->QueryMeasuredBw(),
|
||
pBandwidthInfo->QueryBandwidthLevel() ) );
|
||
#else
|
||
ATQ_PRINTF(( DBG_CONTEXT, "pBWInfo = %p (%s), Bandwidth = %u, Threshold = %d\n",
|
||
pBandwidthInfo,
|
||
pBandwidthInfo->_achDescription,
|
||
pBandwidthInfo->QueryMeasuredBw(),
|
||
pBandwidthInfo->QueryBandwidthLevel() ) );
|
||
#endif
|
||
}
|
||
|
||
SharedUnlock();
|
||
|
||
return fRet;
|
||
}
|
||
|
||
BOOL
|
||
BANDWIDTH_INFO::AbwInitialize( VOID )
|
||
{
|
||
ALLOC_CACHE_CONFIGURATION acConfig = { 1, 10, sizeof(BANDWIDTH_INFO)};
|
||
|
||
ATQ_ASSERT( sm_pachBWInfos == NULL );
|
||
|
||
sm_pachBWInfos = new ALLOC_CACHE_HANDLER( "BandwidthInfos",
|
||
&acConfig );
|
||
|
||
if ( sm_pachBWInfos == NULL )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
InitializeListHead( &sm_ActiveListHead );
|
||
InitializeListHead( &sm_BornListHead );
|
||
INITIALIZE_CRITICAL_SECTION( &sm_csSharedLock );
|
||
|
||
sm_cActiveList = 0;
|
||
sm_cBornList = 0;
|
||
sm_fGlobalEnabled = FALSE;
|
||
sm_fGlobalActive = FALSE;
|
||
sm_cNonInfinite = 0;
|
||
sm_cSamplesForTimeout = 1;
|
||
|
||
g_pBandwidthInfo = new BANDWIDTH_INFO( TRUE );
|
||
|
||
if ( !g_pBandwidthInfo )
|
||
{
|
||
return FALSE;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOL
|
||
BANDWIDTH_INFO::AbwTerminate( VOID )
|
||
{
|
||
PBANDWIDTH_INFO pBandwidthInfo;
|
||
|
||
ATQ_PRINTF(( DBG_CONTEXT,
|
||
"AbwTerminate() called. Born List Size = %d\n",
|
||
sm_cBornList ));
|
||
|
||
SharedLock();
|
||
|
||
while( !IsListEmpty( &sm_BornListHead ) )
|
||
{
|
||
pBandwidthInfo = CONTAINING_RECORD( sm_BornListHead.Flink,
|
||
BANDWIDTH_INFO,
|
||
_BornListEntry );
|
||
|
||
ATQ_ASSERT( pBandwidthInfo != NULL );
|
||
|
||
delete pBandwidthInfo;
|
||
}
|
||
|
||
ATQ_ASSERT( sm_cBornList == 0 );
|
||
ATQ_ASSERT( sm_cActiveList == 0 );
|
||
ATQ_ASSERT( IsListEmpty( &sm_BornListHead ) );
|
||
ATQ_ASSERT( IsListEmpty( &sm_ActiveListHead ) );
|
||
|
||
SharedUnlock();
|
||
|
||
DeleteCriticalSection( &sm_csSharedLock );
|
||
|
||
if ( sm_pachBWInfos != NULL )
|
||
{
|
||
delete sm_pachBWInfos;
|
||
sm_pachBWInfos = NULL;
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
PVOID
|
||
AtqCreateBandwidthInfo( VOID )
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Allocate a bandwidth throttling descriptor
|
||
|
||
Arguments:
|
||
|
||
none
|
||
|
||
Return Value:
|
||
|
||
If successful, pointer to allocated descriptor. Otherwise NULL.
|
||
|
||
--*/
|
||
{
|
||
PBANDWIDTH_INFO pBandwidthInfo;
|
||
|
||
pBandwidthInfo = new BANDWIDTH_INFO( FALSE );
|
||
if ( pBandwidthInfo == NULL )
|
||
{
|
||
return NULL;
|
||
}
|
||
else
|
||
{
|
||
ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
|
||
return pBandwidthInfo;
|
||
}
|
||
}
|
||
|
||
|
||
BOOL
|
||
AtqFreeBandwidthInfo(
|
||
IN PVOID pvBandwidthInfo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Free bandwidth throttling descriptor
|
||
|
||
Arguments:
|
||
|
||
pBandwidthInfo - Descriptor to destroy
|
||
|
||
Return Value:
|
||
|
||
TRUE If successful, else FALSE.
|
||
|
||
--*/
|
||
{
|
||
PBANDWIDTH_INFO pBandwidthInfo = (PBANDWIDTH_INFO) pvBandwidthInfo;
|
||
|
||
ATQ_ASSERT( pBandwidthInfo );
|
||
ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
|
||
|
||
return pBandwidthInfo->PrepareToFree();
|
||
}
|
||
|
||
|
||
ULONG_PTR
|
||
AtqBandwidthSetInfo(
|
||
IN PVOID pvBandwidthInfo,
|
||
IN ATQ_BANDWIDTH_INFO BwInfo,
|
||
IN ULONG_PTR Data
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Set member of bandwidth descriptor
|
||
|
||
Arguments:
|
||
|
||
pBandwidthInfo - Descriptor to change
|
||
BwInfo - Value of descriptor to set
|
||
Data - Data to set to
|
||
|
||
Return Value:
|
||
|
||
Previous value of descriptor
|
||
|
||
--*/
|
||
{
|
||
ULONG_PTR oldVal = 0;
|
||
PBANDWIDTH_INFO pBandwidthInfo = (PBANDWIDTH_INFO) pvBandwidthInfo;
|
||
|
||
ATQ_ASSERT( pBandwidthInfo );
|
||
ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
|
||
|
||
if ( pBandwidthInfo &&
|
||
pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE )
|
||
{
|
||
switch ( BwInfo )
|
||
{
|
||
case ATQ_BW_BANDWIDTH_LEVEL:
|
||
oldVal = (ULONG_PTR)pBandwidthInfo->QueryBandwidthLevel();
|
||
pBandwidthInfo->SetBandwidthLevel( (DWORD) Data );
|
||
break;
|
||
case ATQ_BW_MAX_BLOCKED:
|
||
oldVal = (ULONG_PTR)pBandwidthInfo->QueryMaxBlockedSize();
|
||
pBandwidthInfo->SetMaxBlockedListSize( (DWORD) Data );
|
||
break;
|
||
case ATQ_BW_DESCRIPTION:
|
||
oldVal = (ULONG_PTR)Data;
|
||
pBandwidthInfo->SetDescription( (CHAR*) Data );
|
||
break;
|
||
default:
|
||
SetLastError( ERROR_INVALID_PARAMETER );
|
||
ATQ_ASSERT( FALSE );
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
SetLastError( ERROR_INVALID_PARAMETER );
|
||
}
|
||
|
||
return oldVal;
|
||
|
||
}
|
||
|
||
|
||
BOOL
|
||
AtqBandwidthGetInfo(
|
||
IN PVOID pvBandwidthInfo,
|
||
IN ATQ_BANDWIDTH_INFO BwInfo,
|
||
OUT ULONG_PTR * pData
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Get member of bandwidth descriptor
|
||
|
||
Arguments:
|
||
|
||
pvBandwidthInfo - Descriptor to change
|
||
BwInfo - Value of descriptor to set
|
||
pData - Output here
|
||
|
||
Return Value:
|
||
|
||
TRUE if successful, FALSE otherwise
|
||
|
||
--*/
|
||
{
|
||
BOOL fRet = TRUE;
|
||
PBANDWIDTH_INFO pBandwidthInfo = (PBANDWIDTH_INFO) pvBandwidthInfo;
|
||
|
||
ATQ_ASSERT( pBandwidthInfo );
|
||
ATQ_ASSERT( pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE );
|
||
ATQ_ASSERT( pData );
|
||
|
||
if ( pBandwidthInfo &&
|
||
pBandwidthInfo->QuerySignature() == ATQ_BW_INFO_SIGNATURE &&
|
||
pData )
|
||
{
|
||
switch ( BwInfo )
|
||
{
|
||
case ATQ_BW_BANDWIDTH_LEVEL:
|
||
*pData = (ULONG_PTR)pBandwidthInfo->QueryBandwidthLevel();
|
||
break;
|
||
case ATQ_BW_MAX_BLOCKED:
|
||
*pData = (ULONG_PTR)pBandwidthInfo->QueryMaxBlockedSize();
|
||
break;
|
||
case ATQ_BW_STATISTICS:
|
||
fRet = pBandwidthInfo->GetStatistics( (ATQ_STATISTICS*) pData );
|
||
break;
|
||
case ATQ_BW_DESCRIPTION:
|
||
*pData = (ULONG_PTR)pBandwidthInfo->QueryDescription();
|
||
break;
|
||
default:
|
||
SetLastError( ERROR_INVALID_PARAMETER );
|
||
ATQ_ASSERT( FALSE );
|
||
fRet = FALSE;
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
SetLastError( ERROR_INVALID_PARAMETER );
|
||
fRet = FALSE;
|
||
}
|
||
|
||
return fRet;
|
||
}
|
||
|