/*++ Copyright (c) 1996-1999 Microsoft Corporation Module Name: cmvc.c Abstract: Author: Charlie Wickham (charlwi) 13-Sep-1996. Rajesh Sundaram (rajeshsu) 01-Aug-1998. Environment: Kernel Mode Revision History: --*/ #include "psched.h" #pragma hdrstop /* External */ /* Static */ /* Forward */ NDIS_STATUS ValidateCallParameters( PGPC_CLIENT_VC Vc, PCO_CALL_MANAGER_PARAMETERS CallParameters ); NDIS_STATUS AcquireFlowResources( PGPC_CLIENT_VC Vc, PCO_CALL_MANAGER_PARAMETERS NewCallParams, PCO_CALL_MANAGER_PARAMETERS OldCallParams, PULONG RemainingBandWidthChanged ); VOID ReturnFlowResources( PGPC_CLIENT_VC Vc, PULONG RemainingBandWidthChanged ); VOID CancelAcquiredFlowResources( PGPC_CLIENT_VC Vc ); /* End Forward */ NDIS_STATUS CmCreateVc(PGPC_CLIENT_VC *GpcClientVc, PADAPTER Adapter, PPS_WAN_LINK WanLink, PCO_CALL_PARAMETERS CallParams, GPC_HANDLE GpcCfInfoHandle, PCF_INFO_QOS CfInfoPtr, GPC_CLIENT_HANDLE ClientContext) { PGPC_CLIENT_VC Vc; *GpcClientVc = NULL; PsAllocFromLL(&Vc, &GpcClientVcLL, GpcClientVc); if(Vc == NULL) { return NDIS_STATUS_RESOURCES; } InitGpcClientVc(Vc, 0, Adapter); SetLLTag(Vc, GpcClientVc); // // Allocate space for the instance name for the Vc. // PsAllocatePool(Vc->InstanceName.Buffer, Adapter->WMIInstanceName.Length + VcPrefix.Length + INSTANCE_ID_SIZE, PsMiscTag); if(!Vc->InstanceName.Buffer) { PsFreeToLL(Vc, &GpcClientVcLL, GpcClientVc); return NDIS_STATUS_RESOURCES; } Vc->CfInfoHandle = GpcCfInfoHandle; Vc->CfType = ClientContext; Vc->CfInfoQoS = CfInfoPtr; Vc->CallParameters = CallParams; PS_LOCK(&Adapter->Lock); if(Adapter->PsMpState == AdapterStateRunning) { // // Insert the Vc in the adapter list // InsertHeadList(&Adapter->GpcClientVcList, &Vc->Linkage); PS_UNLOCK(&Adapter->Lock); } else { PsFreePool(Vc->InstanceName.Buffer); PsFreeToLL(Vc, &GpcClientVcLL, GpcClientVc); PS_UNLOCK(&Adapter->Lock); return GPC_STATUS_NOTREADY; } if(WanLink) { Vc->Flags |= GPC_WANLINK_VC; // // We need to link the VC to the WanLink. This has to be done because // we have to clean up when we get a NDIS_STATUS_WAN_LINE_DOWN // Vc->AdapterStats = &WanLink->Stats; Vc->WanLink = WanLink; Vc->PsPipeContext = WanLink->PsPipeContext; Vc->PsComponent = WanLink->PsComponent; } else { Vc->AdapterStats = &Adapter->Stats; Vc->PsPipeContext = Adapter->PsPipeContext; Vc->PsComponent = Adapter->PsComponent; } *GpcClientVc = Vc; return NDIS_STATUS_SUCCESS; } // CmCreateVc BOOLEAN IsIsslowFlow( IN PGPC_CLIENT_VC Vc, IN PCO_CALL_PARAMETERS CallParameters ) { LONG ParamsLength; LPQOS_OBJECT_HDR QoSObject; PADAPTER Adapter = Vc->Adapter; PCO_MEDIA_PARAMETERS CallMgrParams = CallParameters->MediaParameters; ULONGLONG i,j,k; ParamsLength = (LONG)CallMgrParams->MediaSpecific.Length; QoSObject = (LPQOS_OBJECT_HDR)CallMgrParams->MediaSpecific.Parameters; while(ParamsLength > 0) { if(QoSObject->ObjectType == QOS_OBJECT_WAN_MEDIA) { if((Vc->WanLink->LinkSpeed <= Adapter->ISSLOWLinkSpeed) && (CallParameters->CallMgrParameters->Transmit.ServiceType != SERVICETYPE_BESTEFFORT)) { i = (ULONGLONG) Adapter->ISSLOWTokenRate * (ULONGLONG) CallParameters->CallMgrParameters->Transmit.MaxSduSize; j = (ULONGLONG) Adapter->ISSLOWPacketSize * (ULONGLONG) CallParameters->CallMgrParameters->Transmit.TokenRate; k = (ULONGLONG) Adapter->ISSLOWTokenRate * (ULONGLONG)Adapter->ISSLOWPacketSize; if((i+j)ObjectLength <= 0) || ((LONG)QoSObject->ObjectLength > ParamsLength) ) { return(FALSE); } ParamsLength -= QoSObject->ObjectLength; QoSObject = (LPQOS_OBJECT_HDR)((UINT_PTR)QoSObject + QoSObject->ObjectLength); } } return FALSE; } NDIS_STATUS CmMakeCall( IN PGPC_CLIENT_VC Vc ) { ULONG CmParamsLength; NDIS_STATUS Status; ULONG RemainingBandWidthChanged; PADAPTER Adapter = Vc->Adapter; PCO_CALL_PARAMETERS CallParameters = Vc->CallParameters; // // Validate parameters // Status = ValidateCallParameters(Vc, CallParameters->CallMgrParameters); if(Status != NDIS_STATUS_SUCCESS) { PsDbgOut(DBG_INFO, DBG_VC, ("[CmMakeCall]: Vc %08X, invalid QoS parameters\n", Vc)); return Status; } // // make sure we can admit the flow onto our adapter. if this // succeeds, the resources are committed and we'll have to call // CancelAcquiredFlowResources to return them. // Status = AcquireFlowResources(Vc, CallParameters->CallMgrParameters, NULL, &RemainingBandWidthChanged); if(Status != NDIS_STATUS_SUCCESS) { PsDbgOut(DBG_INFO, DBG_VC, ("[CmMakeCall]: Vc %08X, no flow resc\n", Vc)); return Status; } // // In the integrated call manager/miniport model, the activation // is internal. Activating the Vc consists of adding the flow to the // scheduler. If it succeeds, we will later call NdisMCmActivateVc, // just as a courtesy, to notify NDIS. // if( Adapter->MediaType == NdisMediumWan && !IsBestEffortVc(Vc) && IsIsslowFlow( Vc, CallParameters ) ) { // Need to do this before we add a flow to the sched components. Vc->Flags |= GPC_ISSLOW_FLOW; } Status = AddFlowToScheduler(NEW_VC, Vc, CallParameters, 0); // Let's revert it back, to avoid any side effects.. Vc->Flags = Vc->Flags & ~GPC_ISSLOW_FLOW; if(Status != NDIS_STATUS_SUCCESS) { PsDbgOut(DBG_FAILURE, DBG_VC, ("[CmMakeCall]: Vc %08X, AddFlowToScheduler failed %08X\n", Vc, Status)); CancelAcquiredFlowResources(Vc); return(Status); } // // A flow has been added to psched after this point. So, whenever the Vc goes away, Psched's flow // has to be removed from an explicit call. // Vc->bRemoveFlow = TRUE; // // If there is an NDIS 5.0, connection oriented driver below us, then // we need to call it, with the call parameters, to complete the VC // setup. // if(Adapter->MediaType == NdisMediumWan && !IsBestEffortVc(Vc)) { Status = WanMakeCall(Vc, CallParameters); PsAssert(Status == NDIS_STATUS_PENDING); return Status; } else { if(TRUE == RemainingBandWidthChanged) { LONG RemainingBandWidth; PS_LOCK(&Adapter->Lock); RemainingBandWidth = (LONG) Adapter->RemainingBandWidth; PS_UNLOCK(&Adapter->Lock); PsTcNotify(Adapter, 0, OID_QOS_REMAINING_BANDWIDTH, &RemainingBandWidth, sizeof(LONG)); } Vc->TokenRateChange = 0; return NDIS_STATUS_SUCCESS; } } VOID CompleteMakeCall( PGPC_CLIENT_VC Vc, PCO_CALL_PARAMETERS CallParameters, NDIS_STATUS Status ) { PADAPTER Adapter = Vc->Adapter; PsAssert(Adapter->MediaType == NdisMediumWan); PsAssert(!IsBestEffortVc(Vc)); if(Status != NDIS_STATUS_SUCCESS) { CancelAcquiredFlowResources(Vc); } Vc->TokenRateChange = 0; CmMakeCallComplete(Status, Vc, CallParameters); } NDIS_STATUS CmModifyCall( IN PGPC_CLIENT_VC Vc ) /*++ Routine Description: Modify the QoS of an existing flow based on the supplied call params. First see if the request can be handled locally. Arguments: See the DDK... Return Values: NDIS_STATUS_SUCCESS if everything worked ok. --*/ { NDIS_STATUS Status; ULONG CmParamsLength; PCO_CALL_PARAMETERS CallParameters; PADAPTER Adapter; ULONG RemainingBandWidthChanged; Adapter = Vc->Adapter; PsStructAssert(Adapter); PsAssert(Vc->TokenRateChange == 0); // // Validate parameters // CallParameters = Vc->ModifyCallParameters; Status = ValidateCallParameters(Vc, CallParameters->CallMgrParameters); if(Status != NDIS_STATUS_SUCCESS) { PsDbgOut(DBG_INFO, DBG_VC, ("[CmModifyCallQoS]: Vc %08X, invalid QoS parameters\n", Vc)); return Status; } // // make sure we can admit the flow onto our adapter. if this // succeeds, the resources are committed and we'll have to call // CancelAcquiredFlowResources to return them. // Status = AcquireFlowResources(Vc, CallParameters->CallMgrParameters, Vc->CallParameters->CallMgrParameters, &RemainingBandWidthChanged); if(Status != NDIS_STATUS_SUCCESS){ PsDbgOut(DBG_INFO, DBG_VC, ("[CmModifyCallQoS]: Vc %08X, no flow resc\n", Vc)); return Status; } Status = AddFlowToScheduler(MODIFY_VC, Vc, CallParameters, Vc->CallParameters); if(Status != NDIS_STATUS_SUCCESS){ PsDbgOut(DBG_FAILURE, DBG_VC, ("[CmModifyCallQoS]: Vc %08X, failed %08X\n", Vc, Status)); // // Free the copy we made, Cancel the committed resources. // CancelAcquiredFlowResources(Vc); return(Status); } // // If there is an NDIS 5.0, connection oriented driver below us, then // we need to call it, with the call parameters, to complete the VC // setup. // if(Adapter->MediaType == NdisMediumWan){ Status = WanModifyCall(Vc, CallParameters); PsAssert(Status == NDIS_STATUS_PENDING); return(Status); } else { if(TRUE == RemainingBandWidthChanged) { LONG RemainingBandWidth; PS_LOCK(&Adapter->Lock); RemainingBandWidth = (LONG) Adapter->RemainingBandWidth; PS_UNLOCK(&Adapter->Lock); PsTcNotify(Adapter, 0, OID_QOS_REMAINING_BANDWIDTH, &RemainingBandWidth, sizeof(LONG)); } Vc->TokenRateChange = 0; return(NDIS_STATUS_SUCCESS); } } // CmModifyCallQoS VOID ModifyCallComplete( PGPC_CLIENT_VC Vc, PCO_CALL_PARAMETERS CallParameters, NDIS_STATUS Status ) { PADAPTER Adapter = Vc->Adapter; PsAssert(Adapter->MediaType == NdisMediumWan); PsAssert(!IsBestEffortVc(Vc)); if(Status != NDIS_STATUS_SUCCESS) { // // Undo the add flow done above, by reversing the new and old parameters. // ValidateCallParameters(Vc, Vc->CallParameters->CallMgrParameters); Status = AddFlowToScheduler(MODIFY_VC, Vc, Vc->CallParameters, CallParameters); CancelAcquiredFlowResources(Vc); } Vc->TokenRateChange = 0; CmModifyCallComplete(Status, Vc, CallParameters); } NDIS_STATUS CmCloseCall( PGPC_CLIENT_VC Vc ) { NDIS_STATUS Status; PADAPTER Adapter = Vc->Adapter; ULONG RemainingBandWidthChanged; PsStructAssert(Adapter); // // Here, we used to call RemoveFlowFromScheduler, which used to call "DeleteFlow". Instead, we will // call a new interface "EmptyPacketsFromScheduler", which will call "EmptyFlow" to empty all the // packets queued up in each of the components corresponding to this flow. // EmptyPacketsFromScheduler( Vc ); ReturnFlowResources(Vc, &RemainingBandWidthChanged); if(TRUE == RemainingBandWidthChanged) { LONG RemainingBandWidth; PS_LOCK(&Adapter->Lock); RemainingBandWidth = (LONG) Adapter->RemainingBandWidth; PS_UNLOCK(&Adapter->Lock); PsTcNotify(Adapter, 0, OID_QOS_REMAINING_BANDWIDTH, &RemainingBandWidth, sizeof(LONG)); } if(!IsBestEffortVc(Vc)) { CmCloseCallComplete(NDIS_STATUS_SUCCESS, Vc); } else { DerefClVc(Vc); } return NDIS_STATUS_PENDING; } NDIS_STATUS CmDeleteVc( IN PGPC_CLIENT_VC Vc ) { PsAssert(Vc->RefCount == 0); if(Vc->InstanceName.Buffer) { PsFreePool(Vc->InstanceName.Buffer); } if( Vc->bRemoveFlow) { Vc->bRemoveFlow = FALSE; RemoveFlowFromScheduler(Vc); } if(Vc->PsFlowContext) { if(Vc->Adapter->MediaType == NdisMediumWan) { if(Vc->PsFlowContext != Vc->WanLink->BestEffortVc.PsFlowContext) { PsFreePool(Vc->PsFlowContext); } else { if(Vc == &Vc->WanLink->BestEffortVc) { PsFreePool(Vc->PsFlowContext); } } } else { if(Vc->PsFlowContext != Vc->Adapter->BestEffortVc.PsFlowContext) { PsFreePool(Vc->PsFlowContext); } else { if(Vc == &Vc->Adapter->BestEffortVc) { PsFreePool(Vc->PsFlowContext); } } } } NdisFreeSpinLock(&Vc->Lock); NdisFreeSpinLock(&Vc->BytesScheduledLock); NdisFreeSpinLock(&Vc->BytesTransmittedLock); if(Vc->CallParameters){ PsFreePool(Vc->CallParameters); Vc->CallParameters = NULL; } if(!IsBestEffortVc(Vc)) { PS_LOCK(&Vc->Adapter->Lock); RemoveEntryList(&Vc->Linkage); PS_UNLOCK(&Vc->Adapter->Lock); if(Vc->Flags & GPC_WANLINK_VC) { REFDEL(&Vc->WanLink->RefCount, FALSE, 'WANV'); } REFDEL(&Vc->Adapter->RefCount, FALSE, 'ADVC'); PsFreeToLL(Vc, &GpcClientVcLL, GpcClientVc); } else { PADAPTER Adapter = Vc->Adapter; if(Vc->Flags & GPC_WANLINK_VC) { REFDEL(&Vc->WanLink->RefCount, FALSE, 'WANV'); } REFDEL(&Adapter->RefCount, FALSE, 'ADVC'); } return(NDIS_STATUS_SUCCESS); } // CmDeleteVc VOID FillInCmParams( PCO_CALL_MANAGER_PARAMETERS CmParams, SERVICETYPE ServiceType, ULONG TokenRate, ULONG PeakBandwidth, ULONG TokenBucketSize, ULONG DSMode, ULONG Priority) { PCO_SPECIFIC_PARAMETERS SpecificParameters; QOS_SD_MODE * QoSObjectSDMode; QOS_PRIORITY * QoSObjectPriority; QOS_OBJECT_HDR * QoSObjectHdr; CmParams->Transmit.ServiceType = ServiceType; CmParams->Transmit.TokenRate = TokenRate; CmParams->Transmit.PeakBandwidth = PeakBandwidth; CmParams->Transmit.TokenBucketSize = TokenBucketSize; CmParams->CallMgrSpecific.ParamType = PARAM_TYPE_GQOS_INFO; CmParams->CallMgrSpecific.Length = 0; SpecificParameters = (PCO_SPECIFIC_PARAMETERS)&CmParams->CallMgrSpecific.Parameters; if(DSMode != QOS_UNSPECIFIED){ CmParams->CallMgrSpecific.Length += sizeof(QOS_SD_MODE); QoSObjectSDMode = (QOS_SD_MODE *)SpecificParameters; QoSObjectSDMode->ObjectHdr.ObjectType = QOS_OBJECT_SD_MODE; QoSObjectSDMode->ObjectHdr.ObjectLength = sizeof(QOS_SD_MODE); QoSObjectSDMode->ShapeDiscardMode = DSMode; (QOS_SD_MODE *)SpecificParameters++; } if(Priority != QOS_UNSPECIFIED){ CmParams->CallMgrSpecific.Length += sizeof(QOS_PRIORITY); QoSObjectPriority = (QOS_PRIORITY *)SpecificParameters; QoSObjectPriority->ObjectHdr.ObjectType = QOS_OBJECT_PRIORITY; QoSObjectPriority->ObjectHdr.ObjectLength = sizeof(QOS_PRIORITY); QoSObjectPriority->SendPriority = (UCHAR)Priority; (QOS_PRIORITY *)SpecificParameters++; } QoSObjectHdr = (QOS_OBJECT_HDR *)SpecificParameters; QoSObjectHdr->ObjectType = QOS_OBJECT_END_OF_LIST; QoSObjectHdr->ObjectLength = sizeof(QOS_OBJECT_HDR); } NDIS_STATUS ValidateCallParameters( PGPC_CLIENT_VC Vc, PCO_CALL_MANAGER_PARAMETERS CallParameters ) { ULONG TokenRate = CallParameters->Transmit.TokenRate; SERVICETYPE ServiceType = CallParameters->Transmit.ServiceType; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; UCHAR SendPriority; ULONG SDMode; ULONG PeakBandwidth; LONG ParamsLength; LPQOS_OBJECT_HDR QoSObject; ULONG Class; ULONG DSFieldCount; LPQOS_DIFFSERV_RULE pDiffServRule; ULONG i; ULONG ShapingRate; ParamsLength = (LONG)CallParameters->CallMgrSpecific.Length; PeakBandwidth = CallParameters->Transmit.PeakBandwidth; // // By default, we want to shape to the TokenRate // Vc->ShapeTokenRate = TokenRate; QoSObject = (LPQOS_OBJECT_HDR)CallParameters->CallMgrSpecific.Parameters; while (ParamsLength > sizeof(QOS_OBJECT_HDR)) { switch(QoSObject->ObjectType){ case QOS_OBJECT_TRAFFIC_CLASS: if (ParamsLength < sizeof(QOS_TRAFFIC_CLASS)) return QOS_STATUS_TC_OBJECT_LENGTH_INVALID; Class = (((LPQOS_TRAFFIC_CLASS)QoSObject)->TrafficClass); if(Class > USER_PRIORITY_MAX_VALUE) { return QOS_STATUS_INVALID_TRAFFIC_CLASS; } break; case QOS_OBJECT_DS_CLASS: if (ParamsLength < sizeof(QOS_DS_CLASS)) return QOS_STATUS_TC_OBJECT_LENGTH_INVALID; Class = (((LPQOS_DS_CLASS)QoSObject)->DSField); if(Class > PREC_MAX_VALUE) { return QOS_STATUS_INVALID_DS_CLASS; } break; case QOS_OBJECT_SHAPING_RATE: if (ParamsLength < sizeof(QOS_SHAPING_RATE)) return QOS_STATUS_TC_OBJECT_LENGTH_INVALID; ShapingRate = (((LPQOS_SHAPING_RATE)QoSObject)->ShapingRate); if(ShapingRate == 0 || ShapingRate > TokenRate) { return QOS_STATUS_INVALID_SHAPE_RATE; } else { // // If this QoS object is present, we want to shape to this // rate. // Vc->ShapeTokenRate = ShapingRate; } break; case QOS_OBJECT_PRIORITY: if (ParamsLength < sizeof(QOS_PRIORITY)) return QOS_STATUS_TC_OBJECT_LENGTH_INVALID; SendPriority = ((LPQOS_PRIORITY)QoSObject)->SendPriority; if((SendPriority < 0) || (SendPriority > 7)){ // bad priority value - reject return(QOS_STATUS_INVALID_QOS_PRIORITY); } break; case QOS_OBJECT_SD_MODE: if (ParamsLength < sizeof(QOS_SD_MODE)) return QOS_STATUS_TC_OBJECT_LENGTH_INVALID; SDMode = ((LPQOS_SD_MODE)QoSObject)->ShapeDiscardMode; // // Since SDMode is a ULONG, it can never be < TC_NONCONF_BORROW, which has a value of 0. // so, we just check to see if SDMode is > TC_NONCONF_BORROW_PLUS. This covers all cases. // if(SDMode > TC_NONCONF_BORROW_PLUS){ // bad shape discard mode - reject return(QOS_STATUS_INVALID_SD_MODE); } if((SDMode > TC_NONCONF_BORROW) && (TokenRate == UNSPECIFIED_RATE)){ // must have TokenRate specified if any SDMode // other than TC_NONCONF_BORROW return(QOS_STATUS_INVALID_TOKEN_RATE); } break; // Pass any provider specific objects that we don't recognize default: return(QOS_STATUS_TC_OBJECT_LENGTH_INVALID); } if( ((LONG)QoSObject->ObjectLength <= 0) || ((LONG)QoSObject->ObjectLength > ParamsLength) ) { return(QOS_STATUS_TC_OBJECT_LENGTH_INVALID); } ParamsLength -= QoSObject->ObjectLength; QoSObject = (LPQOS_OBJECT_HDR)((UINT_PTR)QoSObject + QoSObject->ObjectLength); } if (ParamsLength == sizeof(QOS_OBJECT_HDR)) { if (QoSObject->ObjectType != QOS_OBJECT_END_OF_LIST) { return(QOS_STATUS_TC_OBJECT_LENGTH_INVALID); } } else if (ParamsLength != 0) { return(QOS_STATUS_TC_OBJECT_LENGTH_INVALID); } // // If there is a specified PeakBandwidth, it must be geq to the // TokenRate - meaning - there must be a TokenRate specified also. // This is reasonable for LAN, although ATM does allow a // PeakBandwidth to be specified with no TokenRate. // // We also reject a TokenRate of zero. // if(PeakBandwidth != UNSPECIFIED_RATE){ if(TokenRate == UNSPECIFIED_RATE){ return(QOS_STATUS_INVALID_PEAK_RATE); } if(TokenRate > PeakBandwidth){ return(QOS_STATUS_INVALID_PEAK_RATE); } } if(TokenRate == 0){ return(QOS_STATUS_INVALID_TOKEN_RATE); } switch(ServiceType){ case SERVICETYPE_BESTEFFORT: case SERVICETYPE_NETWORK_CONTROL: case SERVICETYPE_QUALITATIVE: break; case SERVICETYPE_CONTROLLEDLOAD: case SERVICETYPE_GUARANTEED: // Must specify a TokenRate for these services if(TokenRate == QOS_UNSPECIFIED) { return(QOS_STATUS_INVALID_TOKEN_RATE); } break; default: return(QOS_STATUS_INVALID_SERVICE_TYPE); } return(Status); } NDIS_STATUS AcquireFlowResources( PGPC_CLIENT_VC Vc, PCO_CALL_MANAGER_PARAMETERS NewCallParams, PCO_CALL_MANAGER_PARAMETERS OldCallParams, PULONG RemainingBandWidthChanged ) /*++ Routine Description: See if this adapter can support the requested flow. If it can, NDIS_STATUS_SUCCESS is returned, indicating that the resources have been committed. Arguments: Vc - pointer to vc's context block NewCallParams - struct describing the flow to add or to modify to. OldCallParams - in case of a modify, this describes the old params. Return Value: NDIS_STATUS_SUCCESS if everything worked ok --*/ { PADAPTER Adapter; ULONG OldTokenRate; SERVICETYPE OldServiceType; ULONG NewTokenRate = NewCallParams->Transmit.TokenRate; SERVICETYPE NewServiceType = NewCallParams->Transmit.ServiceType; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PULONG RemainingBandWidth; PULONG NonBestEffortLimit; PPS_SPIN_LOCK Lock; Adapter = Vc->Adapter; PsStructAssert(Adapter); *RemainingBandWidthChanged = FALSE; if(Adapter->MediaType == NdisMediumWan && (!IsBestEffortVc(Vc))) { RemainingBandWidth = &Vc->WanLink->RemainingBandWidth; NonBestEffortLimit = &Vc->WanLink->NonBestEffortLimit; Lock = &Vc->WanLink->Lock; return NDIS_STATUS_SUCCESS; } else { RemainingBandWidth = &Adapter->RemainingBandWidth; NonBestEffortLimit = &Adapter->NonBestEffortLimit; Lock = &Adapter->Lock; } if(OldCallParams) { OldTokenRate = OldCallParams->Transmit.TokenRate; OldServiceType = OldCallParams->Transmit.ServiceType; } // // sanity check passed; now see if we have the resouces locally // // for best-effort flows, the token rate, for the purpose of // admission control, is considered to be zero // if(NewServiceType == SERVICETYPE_BESTEFFORT || NewServiceType == SERVICETYPE_NETWORK_CONTROL || NewServiceType == SERVICETYPE_QUALITATIVE) { NewTokenRate = 0; } // // Handle add differently from a modify // if(!OldCallParams){ PS_LOCK(Lock); if((((LONG)(*RemainingBandWidth)) < 0) || (NewTokenRate > *RemainingBandWidth)){ PS_UNLOCK(Lock); return(NDIS_STATUS_RESOURCES); } else{ if(NewTokenRate) { *RemainingBandWidthChanged = TRUE; } *RemainingBandWidth -= NewTokenRate; // // Record the change we made, in case we have // to cancel the addition. // Vc->TokenRateChange = NewTokenRate; Vc->RemainingBandwidthIncreased = FALSE; PsAssert((*RemainingBandWidth <= *NonBestEffortLimit)); PS_UNLOCK(Lock); } } else{ // // it's a modify // // If the OldServiceType is best-effort, // then the OldTokenRate can be considered // to be zero, for the purpose of admission control. // if(OldServiceType == SERVICETYPE_BESTEFFORT || OldServiceType == SERVICETYPE_NETWORK_CONTROL || OldServiceType == SERVICETYPE_QUALITATIVE) { OldTokenRate = 0; } PS_LOCK(Lock); if(NewTokenRate != OldTokenRate){ if((((LONG) *RemainingBandWidth) < 0 )|| ((NewTokenRate > OldTokenRate) && ((NewTokenRate - OldTokenRate) > (*RemainingBandWidth)))){ // // asked for more and none was available // PS_UNLOCK( Lock ); return(NDIS_STATUS_RESOURCES); } else{ // // either asked for less or rate increment was available // *RemainingBandWidth -= NewTokenRate; *RemainingBandWidth += OldTokenRate; if((NewTokenRate != 0) || (OldTokenRate != 0)) { *RemainingBandWidthChanged = TRUE; } // // Now we've acquired the resources. If // the VC activation fails for any reason, // we'll need to return resources. We should // return the difference between the old token // rate and the new token rate, not the new token // rate. // if(NewTokenRate > OldTokenRate){ // Can't use signed ints, cause we'll lose range Vc->TokenRateChange = NewTokenRate - OldTokenRate; Vc->RemainingBandwidthIncreased = FALSE; } else{ Vc->TokenRateChange = OldTokenRate - NewTokenRate; Vc->RemainingBandwidthIncreased = TRUE; } PS_UNLOCK( Lock ); } } else{ PS_UNLOCK(Lock); } } return Status; } // AcquireFlowResources VOID CancelAcquiredFlowResources( PGPC_CLIENT_VC Vc ) /*++ Routine Description: Called when a modify or add flwo failed, after we did admission control. Arguments: Vc - pointer to client vc's context block Return Value: None --*/ { PADAPTER Adapter; PPS_SPIN_LOCK Lock; PULONG RemainingBandWidth; Adapter = Vc->Adapter; PsStructAssert(Adapter); if(Adapter->MediaType == NdisMediumWan && (!IsBestEffortVc(Vc))) { Lock = &Vc->WanLink->Lock; RemainingBandWidth = &Vc->WanLink->RemainingBandWidth; return; } else { Lock = &Adapter->Lock; RemainingBandWidth = &Adapter->RemainingBandWidth; } if(!Vc->TokenRateChange){ return; } PS_LOCK( Lock ); if(Vc->RemainingBandwidthIncreased){ *RemainingBandWidth -= Vc->TokenRateChange; } else{ *RemainingBandWidth += Vc->TokenRateChange; } // // Now that we have already returned the correct TokenRate, we need to set it to 0 // so that this is not used in subsequent VC operations. // Vc->TokenRateChange = 0; // PsAssert(Adapter->RemainingBandWidth <= Adapter->NonBestEffortLimit); PS_UNLOCK( Lock ); } // CancelAcquiredFlowResources VOID ReturnFlowResources( PGPC_CLIENT_VC Vc, PULONG RemainingBandWidthChanged ) /*++ Routine Description: Return all the resources acquired for this flow Arguments: Vc - pointer to client vc's context block Return Value: None --*/ { PADAPTER Adapter; PCO_CALL_MANAGER_PARAMETERS CmParams = Vc->CallParameters->CallMgrParameters; ULONG TokenRate = CmParams->Transmit.TokenRate; SERVICETYPE ServiceType = CmParams->Transmit.ServiceType; PPS_SPIN_LOCK Lock; PULONG RemainingBandWidth; Adapter = Vc->Adapter; PsStructAssert(Adapter); *RemainingBandWidthChanged = FALSE; if(Adapter->MediaType == NdisMediumWan && (!IsBestEffortVc(Vc))) { RemainingBandWidth = &Vc->WanLink->RemainingBandWidth; Lock = &Vc->WanLink->Lock; return; } else { RemainingBandWidth = &Adapter->RemainingBandWidth; Lock = &Adapter->Lock; } if (ServiceType == SERVICETYPE_BESTEFFORT || ServiceType == SERVICETYPE_NETWORK_CONTROL || ServiceType == SERVICETYPE_QUALITATIVE) { return; } *RemainingBandWidthChanged = TRUE; PsAssert((LONG)TokenRate > 0); PS_LOCK( Lock ); *RemainingBandWidth += TokenRate; // PsAssert(Adapter->RemainingBandWidth <= Adapter->NonBestEffortLimit); PS_UNLOCK( Lock ); } // ReturnFlowResources NDIS_STATUS CreateBestEffortVc( PADAPTER Adapter, PGPC_CLIENT_VC Vc, PPS_WAN_LINK WanLink ) { PCO_CALL_PARAMETERS CallParams; PCO_CALL_MANAGER_PARAMETERS CallMgrParameters; PCO_MEDIA_PARAMETERS MediaParameters; ULONG CallParamsLength; NDIS_STATUS Status; int i; InitGpcClientVc(Vc, GPC_CLIENT_BEST_EFFORT_VC, Adapter); SetLLTag(Vc, GpcClientVc); // // Invalidate all the port numbers for( i = 0; i < PORT_LIST_LEN; i++) { Vc->SrcPort[i] = 0; Vc->DstPort[i] = 0; } // Next Insertion will be at index 0 Vc->NextSlot = 0; // // Allocate the resources for the call manager parameters. // CallParamsLength = sizeof(CO_CALL_PARAMETERS) + sizeof(CO_CALL_MANAGER_PARAMETERS) + sizeof(QOS_SD_MODE) + sizeof(QOS_OBJECT_HDR) + FIELD_OFFSET(CO_MEDIA_PARAMETERS, MediaSpecific) + FIELD_OFFSET(CO_SPECIFIC_PARAMETERS, Parameters); if(Adapter->MediaType == NdisMediumWan) { CallParamsLength += sizeof(QOS_WAN_MEDIA); Vc->PsPipeContext = WanLink->PsPipeContext; Vc->PsComponent = WanLink->PsComponent; Vc->AdapterStats = &WanLink->Stats; Vc->WanLink = WanLink; Vc->Flags |= GPC_WANLINK_VC; if(Adapter->BestEffortLimit != UNSPECIFIED_RATE) { // // If LBE is specified over WAN, use UBE // PsAdapterWriteEventLog( EVENT_PS_WAN_LIMITED_BESTEFFORT, 0, &Adapter->MpDeviceName, 0, NULL); Adapter->BestEffortLimit = UNSPECIFIED_RATE; } } else { Vc->PsPipeContext = Adapter->PsPipeContext; Vc->PsComponent = Adapter->PsComponent; Vc->AdapterStats = &Adapter->Stats; } PsAllocatePool(CallParams, CallParamsLength, CmParamsTag); if(CallParams == NULL) { return NDIS_STATUS_RESOURCES; } // // build a call params struct describing the flow // NdisZeroMemory(CallParams, CallParamsLength); // // Build the Call Manager Parameters. // CallMgrParameters = (PCO_CALL_MANAGER_PARAMETERS)(CallParams + 1); if(Adapter->BestEffortLimit == UNSPECIFIED_RATE) { FillInCmParams(CallMgrParameters, SERVICETYPE_BESTEFFORT, (ULONG)UNSPECIFIED_RATE, (ULONG)UNSPECIFIED_RATE, Adapter->TotalSize, QOS_UNSPECIFIED, QOS_UNSPECIFIED); } else { // // Limited Best Effort // PsAssert(Adapter->MediaType != NdisMediumWan); if(Adapter->BestEffortLimit >= Adapter->LinkSpeed) { // If the specified limit is greater than the link speed, // then we should operate in unlimited best-effort mode. PsAdapterWriteEventLog( EVENT_PS_BAD_BESTEFFORT_LIMIT, 0, &Adapter->MpDeviceName, 0, NULL); PsDbgOut(DBG_INFO, DBG_PROTOCOL, ("[CreateBestEffortVc]: b/e limit %d exceeds link speed %d\n", Adapter->BestEffortLimit, Adapter->LinkSpeed)); Adapter->BestEffortLimit = UNSPECIFIED_RATE; FillInCmParams(CallMgrParameters, SERVICETYPE_BESTEFFORT, (ULONG)UNSPECIFIED_RATE, (ULONG)UNSPECIFIED_RATE, Adapter->TotalSize, QOS_UNSPECIFIED, QOS_UNSPECIFIED); } else { FillInCmParams(CallMgrParameters, SERVICETYPE_BESTEFFORT, Adapter->BestEffortLimit, (ULONG)UNSPECIFIED_RATE, Adapter->TotalSize, TC_NONCONF_SHAPE, QOS_UNSPECIFIED); } } // // Build the MediaParameters. // CallParams->MediaParameters = (PCO_MEDIA_PARAMETERS)(CallMgrParameters + 1); MediaParameters = (PCO_MEDIA_PARAMETERS)((PUCHAR) CallMgrParameters + sizeof(CO_CALL_MANAGER_PARAMETERS) + sizeof(QOS_SD_MODE) + sizeof(QOS_OBJECT_HDR)); MediaParameters->Flags = 0; MediaParameters->ReceivePriority = 0; MediaParameters->ReceiveSizeHint = 0; MediaParameters->MediaSpecific.ParamType = PARAM_TYPE_GQOS_INFO; MediaParameters->MediaSpecific.Length = 0; CallParams->Flags = 0; CallParams->CallMgrParameters = CallMgrParameters; CallParams->MediaParameters = (PCO_MEDIA_PARAMETERS)MediaParameters; if(Adapter->MediaType == NdisMediumWan) { LPQOS_WAN_MEDIA WanMedia; MediaParameters->MediaSpecific.Length += sizeof(QOS_WAN_MEDIA); WanMedia = (LPQOS_WAN_MEDIA) MediaParameters->MediaSpecific.Parameters; NdisZeroMemory(WanMedia, sizeof(QOS_WAN_MEDIA)); WanMedia->ObjectHdr.ObjectType = QOS_OBJECT_WAN_MEDIA; WanMedia->ObjectHdr.ObjectLength = sizeof(QOS_WAN_MEDIA); NdisMoveMemory(&WanMedia->LinkId, &WanLink->OriginalRemoteMacAddress, 6); } Vc->CallParameters = CallParams; Status = CmMakeCall(Vc); PsAssert(Status != NDIS_STATUS_PENDING); if(Status == NDIS_STATUS_SUCCESS) { REFADD(&Adapter->RefCount, 'ADVC'); if(Adapter->MediaType == NdisMediumWan) { REFADD(&WanLink->RefCount, 'WANV'); } // // Also save the non conforming value - so that the sequencer can stamp it // for non conforming packets. This will not change between reboots & hence // need not be done in the ModifyCfInfo // Vc->UserPriorityNonConforming = Adapter->UserServiceTypeNonConforming; switch(Vc->CallParameters->CallMgrParameters->Transmit.ServiceType) { case SERVICETYPE_CONTROLLEDLOAD: Vc->UserPriorityConforming = Adapter->UserServiceTypeControlledLoad; Vc->IPPrecedenceNonConforming = Adapter->IPServiceTypeControlledLoadNC; break; case SERVICETYPE_GUARANTEED: Vc->UserPriorityConforming = Adapter->UserServiceTypeGuaranteed; Vc->IPPrecedenceNonConforming = Adapter->IPServiceTypeGuaranteedNC; break; case SERVICETYPE_BESTEFFORT: Vc->UserPriorityConforming = Adapter->UserServiceTypeBestEffort; Vc->IPPrecedenceNonConforming = Adapter->IPServiceTypeBestEffortNC; break; case SERVICETYPE_QUALITATIVE: Vc->UserPriorityConforming = Adapter->UserServiceTypeQualitative; Vc->IPPrecedenceNonConforming = Adapter->IPServiceTypeQualitativeNC; break; case SERVICETYPE_NETWORK_CONTROL: Vc->UserPriorityConforming = Adapter->UserServiceTypeNetworkControl; Vc->IPPrecedenceNonConforming = Adapter->IPServiceTypeNetworkControlNC; break; } // // Transistion to the Call complete state // CallSucceededStateTransition(Vc); } return Status; } /* end cmvc.c */