windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/infocomm/atq/abw.cxx
2020-09-26 16:20:57 +08:00

1209 lines
32 KiB
C++
Raw Permalink 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) 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;
}