Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

775 lines
22 KiB

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name :
atqbw.c
Abstract:
This module implements functions required for bandwidth throttling
of network usage by ATQ module.
Author:
Murali R. Krishnan ( MuraliK ) 1-June-1995
Environment:
User Mode -- Win32
Project:
Internet Services Common DLL
Functions Exported:
BOOL AbwInitialize();
BOOL AbwCleanup();
BOOL AbwInitializeSamples();
DWORD AbwGetBandwidthLevel(IN DWORD Data);
DWORD AbwSetBandwidthLevel(IN DWORD Data);
DWORD AbwClearStatistics(VOID);
BOOL AbwBlockRequest( IN PATQ_CONT pAtqContext);
BOOL AbwRemoveFromBlockedList( IN PATQ_CONT pAtqContext);
BOOL AbwUpdateBytesXfered( IN PATQ_CONT pAtqContext, IN DWORD cbIo);
BOOL AbwUpdateBandwidth( IN DWORD msTimeInterval);
Revision History:
--*/
/************************************************************
* Include Headers
************************************************************/
# include <tcpdllp.hxx>
# include <atqtypes.hxx>
# include "dbgutil.h"
// Macros to decouple ATQ module from others
# define ATQ_ASSERT DBG_ASSERT
# define ATQ_REQUIRE DBG_REQUIRE
typedef struct _BANDWIDTH_LEVELS {
DWORD dwSpecifiedLevel;
DWORD dwLowThreshold; // uses 0.90 * bwSpecifiedLevel
DWORD dwHighThreshold; // uses 1.10 * bwSpecifiedLevel
} BANDWIDTH_LEVELS;
# define ATQ_LOW_BAND_THRESHOLD(bw) (bw.dwLowThreshold)
# define ATQ_HIGH_BAND_THRESHOLD(bw) (bw.dwHighThreshold)
typedef enum {
ZoneLevelLow = 0, // if MeasuredBw < Bandwidth specified
ZoneLevelMedium, // if MeasuredBw approxEquals Bandwidth specified
ZoneLevelHigh, // if MeasuredBw > Bandwidth specified
ZoneLevelMax // just a boundary element
} ZoneLevel;
/*++
The following array specifies the status of operations for different
operations in different zones of the measured bandwidth.
We split the range of bandwidth into three zones as specified in
the type ZoneLevel. Depending upon the zone of operation, differet
operations are enabled/disabled. Priority is given to minimize the amount
of CPU work that needs to be redone when an operation is to be rejected.
We block operations on which considerable amount of CPU is spent earlier.
--*/
static OPERATION_STATUS g_rgStatus[ZoneLevelMax][AtqIoMaxOp] = {
// For ZoneLevelLow: Allow All operations
{ StatusRejectOperation,
StatusAllowOperation,
StatusAllowOperation,
StatusAllowOperation,
StatusAllowOperation
},
// For ZoneLevelMedium:
{ StatusRejectOperation,
StatusBlockOperation, // Block Read
StatusAllowOperation,
StatusAllowOperation,
StatusAllowOperation
},
// For ZoneLevelHigh
{ StatusRejectOperation,
StatusRejectOperation, // Reject Read
StatusAllowOperation, // Allow Writes
StatusBlockOperation, // Block TransmitFile
StatusBlockOperation // Block TransmitFile
}
};
OPERATION_STATUS * g_pStatus = &g_rgStatus[ZoneLevelLow][0];
DWORD g_cCurrentBlockedRequests = 0;
DWORD g_cTotalBlockedRequests = 0;
DWORD g_cTotalAllowedRequests = 0;
DWORD g_cTotalRejectedRequests = 0;
DWORD g_dwMeasuredBw = 0; // measured bandwidth
/************************************************************
* Private Variables
************************************************************/
static LIST_ENTRY g_BlockedListHead;
static BANDWIDTH_LEVELS g_bandwidth = { INFINITE, INFINITE, INFINITE };
// We will use histogram averaging for bytes sent, using circular buffer.
static LARGE_INTEGER g_rgBytesXfered[ATQ_HISTOGRAM_SIZE];
static LARGE_INTEGER * g_pBytesXferCur = &g_rgBytesXfered[0];
static LARGE_INTEGER g_cbXfered = {0,0}; // cumulative sum
# define CIRCULAR_INCREMENT( g_pB, g_rgB, size) \
(g_pB) = ((((g_pB) + 1) < (g_rgB)+(size)) ? (g_pB)+1 : (g_rgB))
static CRITICAL_SECTION g_csAtqBandwidthThrottle;
# define AbwLockGlobals() EnterCriticalSection( &g_csAtqBandwidthThrottle)
# define AbwUnlockGlobals() LeaveCriticalSection( &g_csAtqBandwidthThrottle)
/************************************************************
* Functions
************************************************************/
BOOL AbwInitialize(VOID)
/*++
Initializes the private variables for Atq Bandwidth Throttling, if enabled.
Arguments:
None
Returns:
TRUE on success and FALSE if there is any failure.
--*/
{
InitializeListHead( &g_BlockedListHead);
InitializeCriticalSection( &g_csAtqBandwidthThrottle);
g_cCurrentBlockedRequests = 0;
AbwInitializeSamples();
ATQ_REQUIRE( AbwClearStatistics());
return (TRUE);
} // AbwInitialize()
BOOL AbwCleanup(VOID)
/*++
Cleans up the private variables and state maintained by the ATQ
Bandwidth Throttle module.
It also checks to see if there are any items pending on the blocked list.
If there are any, they are all freed.
Arguments:
None
Returns:
TRUE on success and FALSE if there is any failure
--*/
{
// NYI: need to cleanup contexts pending on the bandwidth throttle list
AbwLockGlobals();
ATQ_ASSERT( IsListEmpty( &g_BlockedListHead));
if ( !IsListEmpty( &g_BlockedListHead)) {
AbwUnlockGlobals();
return (FALSE);
}
AbwUnlockGlobals();
DeleteCriticalSection( &g_csAtqBandwidthThrottle);
return (TRUE);
} // AbwCleanup()
VOID
AbwInitializeSamples( VOID)
/*++
Initializes the histogram samples to contain starting values of ZERO.
g_pBytesXferCur points to the location for accumulating current bytes count.
--*/
{
int i;
AbwLockGlobals();
for( i = 0; i < ATQ_HISTOGRAM_SIZE; i++) {
g_rgBytesXfered[i].QuadPart = (ULONGLONG) 0;
} // for
g_pBytesXferCur = g_rgBytesXfered; // points to start of array
g_cbXfered.QuadPart = 0;
g_dwMeasuredBw = 0;
AbwUnlockGlobals();
return;
} // AbwInitializeSamples()
BOOL AbwBlockRequest(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)
--*/
{
ATQ_ASSERT( pAtqContext != NULL);
ATQ_ASSERT( pAtqContext->Signature == ATQ_SIGNATURE );
ATQ_ASSERT(IsValidAtqOp(pAtqContext->arInfo.atqOp));
// NYI: We may want to check the list length and reject some requests.
AbwLockGlobals();
pAtqContext->fBlocked = TRUE;
InsertTailList( &g_BlockedListHead, &pAtqContext->BlockedListEntry );
g_cCurrentBlockedRequests++;
AbwUnlockGlobals();
return ( TRUE);
}// AbwBlockRequest()
BOOL AbwRemoveFromBlockedList( 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->fBlocked ) {
// some other thread just removed this request from waiting list.
return (TRUE);
}
AbwLockGlobals();
RemoveEntryList(&pAtqContext->BlockedListEntry);
g_cCurrentBlockedRequests--;
pAtqContext->fBlocked = FALSE;
AbwUnlockGlobals();
//
// 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);
} // AbwRemoveFromBlockedList()
BOOL AbwUnblockRequest(IN OUT PATQ_CONT pAtqContext)
/*++
Unblocks this request from the queue of requests waiting to be processed.
Call this function only when
g_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 locking using AbwLockGlobals()
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_SIGNATURE );
// Remove the request from the blocked list entry
RemoveEntryList( &pAtqContext->BlockedListEntry);
g_cCurrentBlockedRequests--;
pAtqContext->fBlocked = FALSE;
// Check and re enable the operation of pAtqContext
switch ( g_pStatus[pAtqContext->arInfo.atqOp]) {
case StatusAllowOperation:
INC_ATQ_COUNTER( g_cTotalAllowedRequests);
switch ( pAtqContext->arInfo.atqOp) {
case AtqIoRead:
{
DWORD cbRead; // Discard after calling ReadFile()
fRet = ( ReadFile( pAtqContext->hAsyncIO,
pAtqContext->arInfo.uop.opReadWrite.lpBuffer,
pAtqContext->arInfo.uop.opReadWrite.cbBuffer,
&cbRead,
pAtqContext->arInfo.lpOverlapped ) ||
GetLastError() == ERROR_IO_PENDING);
break;
}
case AtqIoWrite:
{
DWORD cbWrite; // Discard after calling WriteFile()
fRet = ( WriteFile( pAtqContext->hAsyncIO,
pAtqContext->arInfo.uop.opReadWrite.lpBuffer,
pAtqContext->arInfo.uop.opReadWrite.cbBuffer,
&cbWrite,
pAtqContext->arInfo.lpOverlapped ) ||
GetLastError() == ERROR_IO_PENDING);
break;
}
case AtqIoXmitFile:
{
fRet = ( TransmitFile( (SOCKET ) pAtqContext->hAsyncIO,
pAtqContext->arInfo.uop.opXmit.hFile,
pAtqContext->arInfo.uop.opXmit.
liBytesInFile.LowPart,
0,
pAtqContext->arInfo.lpOverlapped,
pAtqContext->arInfo.uop.
opXmit.lpXmitBuffers,
0) ||
GetLastError() == ERROR_IO_PENDING);
break;
}
case AtqIoXmitFileEx:
{
fRet = TransmitFile( (SOCKET ) pAtqContext->hAsyncIO,
pAtqContext->arInfo.uop.opXmit.hFile,
pAtqContext->arInfo.uop.opXmit.liBytesInFile.LowPart,
0,
pAtqContext->arInfo.lpOverlapped,
pAtqContext->arInfo.uop.
opXmit.lpXmitBuffers,
0 );
if ( fRet ) {
//
// The TransmitFile write-behind path succeeded immediately.
// This means that the completion port will not get a hit, so
// we have to call the completion function manually ourselves.
//
InterlockedExchange( (LPLONG )&pAtqContext->NextTimeout,
(LONG ) ATQ_INFINITE);
//
// Update Bandwidth information on successful completion, if
// needed
//
if ( g_fBandwidthThrottle &&
pAtqContext->arInfo.uop.opXmit.liBytesInFile.LowPart > 0) {
AbwUpdateBytesXfered(pAtqContext,
pAtqContext->arInfo.uop.opXmit.
liBytesInFile.LowPart);
}
if ( pAtqContext->pfnCompletion ) {
pAtqContext->pfnCompletion( pAtqContext->ClientContext,
pAtqContext->arInfo.uop.opXmit.
liBytesInFile.LowPart,
NO_ERROR,
pAtqContext->arInfo.lpOverlapped );
}
} else if ( GetLastError() == ERROR_IO_PENDING ) {
fRet = TRUE;
}
break;
}
default:
ATQ_ASSERT( FALSE);
break;
} // switch
pAtqContext->arInfo.atqOp = AtqIoNone; // reset since operation done.
break;
case StatusRejectOperation:
INC_ATQ_COUNTER( g_cTotalRejectedRequests);
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
);
if ( pAtqContext->pfnCompletion ) {
pAtqContext->pfnCompletion(pAtqContext->ClientContext,
0,
GetLastError(),
pAtqContext->arInfo.lpOverlapped );
}
} // on failure.
return (fRet);
} // AbwUnblockRequest()
BOOL
AbwCheckAndUnblockRequests( 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 acquiring Abw global lock.
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( &g_BlockedListHead ) ) {
PLIST_ENTRY pentry;
//
// Scan the blocked requests list looking for pending requests
// that needs to be unblocked and unblock these requests.
//
for (pentry = g_BlockedListHead.Flink;
pentry != &g_BlockedListHead;
pentry = pentry->Flink )
{
PATQ_CONT pContext = CONTAINING_RECORD(pentry,
ATQ_CONTEXT,
BlockedListEntry );
if ( pContext->Signature != ATQ_SIGNATURE)
{
ATQ_ASSERT( pContext->Signature == ATQ_SIGNATURE );
fRet = FALSE;
break;
}
if ( !pContext->fBlocked) {
// This should not happen.
ATQ_ASSERT( !pContext->fBlocked);
fRet = FALSE;
continue;
}
//
// Check to see if the status for operation has changed.
// If so, unblock the request.
//
if ( g_pStatus[pContext->arInfo.atqOp] !=
StatusBlockOperation) {
fRet &= AbwUnblockRequest( pContext);
}
} // scan list
}
return (fRet);
} // AbwCheckAndUnblockRequests()
BOOL AbwUpdateBytesXfered( IN PATQ_CONT pAtqContext, IN DWORD cbIo)
/*++
This function updates a global counter that keeps track of count of
bytes transferred.
Arguments:
pAtqContext pointer to Atq Context, whose IO just completed.
cbIo count of bytes of Data involved in IO operation.
Returns:
TRUE on success and FALSE if there is any failure.
--*/
{
// Update the global estimated Bandwidth
AbwLockGlobals();
g_pBytesXferCur->QuadPart = g_pBytesXferCur->QuadPart + cbIo;
AbwUnlockGlobals();
return (TRUE);
} // AbwUpdateBytesSent()
BOOL AbwUpdateBandwidth( IN DWORD msTimeInterval)
/*++
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 g_pcbXfered pointer is advanced forward using the
histogram entries as a circular buffer, to obtain the cout of bytes
for next interval.
Arguments:
msTimeInterval time interval for the current sample in milliseconds.
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;
UNREFERENCED_PARAMETER( msTimeInterval);
AbwLockGlobals();
// accumulate current byte count to global counter, to minimize computation
g_cbXfered.QuadPart = g_cbXfered.QuadPart + g_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.
//
g_dwMeasuredBw = (DWORD ) (g_cbXfered.QuadPart/ATQ_AVERAGING_PERIOD);
CIRCULAR_INCREMENT( g_pBytesXferCur, g_rgBytesXfered, ATQ_HISTOGRAM_SIZE);
// Adjust the global cumulative bytes sent after increment.
g_cbXfered .QuadPart = g_cbXfered.QuadPart - g_pBytesXferCur->QuadPart;
// Reset the counter to start with the new counter value.
g_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 ( g_dwMeasuredBw < ATQ_LOW_BAND_THRESHOLD(g_bandwidth)) {
//
// Lower zone. Modify the pointer to OPERATION_STATUS accordingly.
//
zonelevel = ZoneLevelLow;
} else if ( g_dwMeasuredBw > ATQ_HIGH_BAND_THRESHOLD(g_bandwidth)) {
//
// Higher zone. Modify the pointer to OPERATION_STATUS accordingly.
//
zonelevel = ZoneLevelHigh;
} else {
zonelevel = ZoneLevelMedium;
}
/*++
Above calculation can be implemented as:
zonelevel = (( g_dwMeasuredBw > ATQ_LOW_BAND_THRESHOLD( g_bandwidth)) +
( g_dwMeasuredBw > ATQ_HIGH_BAND_THRESHOLD( g_bandwidth)));
This is based on implicit dependence of ordering of ZoneLevel entries.
So avoided for present now.
--*/
if ( g_pStatus != &g_rgStatus[zonelevel][0]) {
// Status needs to be changed.
g_pStatus = &g_rgStatus[zonelevel][0];
// Examine and reenable blocked operations if any.
fRet &= AbwCheckAndUnblockRequests();
}
AbwUnlockGlobals();
return (fRet);
} // AbwUpdateBandwidth()
DWORD AbwSetBandwidthLevel(IN DWORD Data)
{
DWORD dwOldVal;
AbwLockGlobals();
dwOldVal = g_bandwidth.dwSpecifiedLevel;
if ( Data != INFINITE) {
DWORD dwTemp;
g_bandwidth.dwSpecifiedLevel = ATQ_ROUNDUP_BANDWIDTH( Data );
dwTemp = ( Data *9)/10; //low threshold = 0.9*specified
g_bandwidth.dwLowThreshold = ATQ_ROUNDUP_BANDWIDTH( dwTemp);
dwTemp = ( Data *11)/10; //high threshold= 1.1*specified
g_bandwidth.dwHighThreshold = ATQ_ROUNDUP_BANDWIDTH( dwTemp);
g_fBandwidthThrottle = TRUE;
// we should recheck the throttling and blocked requests
// Will be done when the next timeout occurs in the ATQ Timeout Thread
} else {
g_bandwidth.dwSpecifiedLevel = INFINITE;
g_bandwidth.dwLowThreshold = INFINITE;
g_bandwidth.dwHighThreshold = INFINITE;
g_fBandwidthThrottle = FALSE;
// enable all operations, since we are in low zone
g_pStatus = &g_rgStatus[ZoneLevelLow][0];
// we should recheck and enable all blocked requests.
if ( g_cCurrentBlockedRequests > 0) {
ATQ_REQUIRE( AbwCheckAndUnblockRequests());
}
}
AbwUnlockGlobals();
return (dwOldVal);
} // AbwSetBandwidthLevel()
DWORD AbwGetBandwidthLevel( VOID)
{
DWORD dwBw;
AbwLockGlobals();
dwBw = g_bandwidth.dwSpecifiedLevel;
AbwUnlockGlobals();
return (dwBw);
} // AbwGetBandwidthLevel()
BOOL AbwClearStatistics( VOID)
{
AbwLockGlobals();
g_cTotalAllowedRequests = 0;
g_cTotalBlockedRequests = 0;
g_cTotalRejectedRequests = 0;
AbwUnlockGlobals();
return (TRUE);
} // AbwClearStatistics()
/************************ End of File ***********************/