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