/*++ Copyright (c) 1996-1999 Microsoft Corporation Module Name: mpvc.c Abstract: miniport handlers for VC mgmt 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 GetSchedulerFlowContext( PGPC_CLIENT_VC AdapterVc ); NDIS_STATUS MpDeactivateVc( IN NDIS_HANDLE MiniportVcContext ); HANDLE GetNdisFlowHandle ( IN HANDLE PsFlowContext ); /* End Forward */ NDIS_STATUS AddFlowToScheduler( IN ULONG Operation, IN PGPC_CLIENT_VC Vc, IN OUT PCO_CALL_PARAMETERS NewCallParameters, IN OUT PCO_CALL_PARAMETERS OldCallParameters ) /*++ Routine Description: Add the Vc to the scheduler. Arguments: See the DDK... Return Values: None --*/ { PADAPTER Adapter = Vc->Adapter; NDIS_STATUS Status = NDIS_STATUS_SUCCESS; PCO_CALL_MANAGER_PARAMETERS NewCmParams; PCO_CALL_MANAGER_PARAMETERS OldCmParams; SERVICETYPE ServiceType; ULONG ParamsLength; LPQOS_OBJECT_HDR QoSObject; ULONG OriginalTokenRate; CheckLLTag(Vc, GpcClientVc); PsStructAssert(Adapter); PsDbgOut(DBG_TRACE, DBG_VC, ("(%08X) AddFlowToScheduler\n", Vc)); NewCmParams = NewCallParameters->CallMgrParameters; ServiceType = NewCmParams->Transmit.ServiceType; // // We might need to change the rate at which the scheduling components shape the packet. // OriginalTokenRate = NewCmParams->Transmit.TokenRate; NewCmParams->Transmit.TokenRate = Vc->ShapeTokenRate; // // Is this a new VC? or a modification of an existing VC? // PS_LOCK(&Adapter->Lock); if(Operation == NEW_VC){ PsAssert(!OldCallParameters); // // New Vc. // // Check the type of service we're activating. If best // effort and we're limiting total best effort bandwidth, // and it's not our internal best effort vc, then we don't // want to add the flow in the scheduler. // if((ServiceType == SERVICETYPE_BESTEFFORT) && (Adapter->BestEffortLimit != UNSPECIFIED_RATE) && !IsBestEffortVc(Vc)){ PS_UNLOCK(&Adapter->Lock); // // Just merge the VC into the internal, existing best // effort VC. The internal best-effort VC is created // internally without calling AddFlowToScheduler. // // Give this VC the same flow context as our internal. // The scheduler then thinks it is all the same VC // if(Adapter->MediaType == NdisMediumWan) { Vc->PsFlowContext = Vc->WanLink->BestEffortVc.PsFlowContext; } else { Vc->PsFlowContext = Adapter->BestEffortVc.PsFlowContext; } Status = NDIS_STATUS_SUCCESS; } else{ // // Need to actually create a new flow in the scheduler. // first allocate the flow context buffer // PS_UNLOCK(&Adapter->Lock); Status = GetSchedulerFlowContext(Vc); if(Status != NDIS_STATUS_SUCCESS){ goto Exit; } Status = (*Vc->PsComponent->CreateFlow)( Vc->PsPipeContext, Vc, NewCallParameters, Vc->PsFlowContext); } } else{ // // Must be a modify. Check old params. OldCmParams = OldCallParameters->CallMgrParameters; // // If BestEffortLimit != UNSPECIFIED_RATE, then there // are two special cases we have to handle: // // 1. A non-private flow, created for SERVICETYPE_BESTEFFORT // is being modified to a ServiceType other than best-effort. // // 2. A non-private flow, created for a ServiceType other // than best-effort, is now being modified to best-effort. // // In the first case, we have to call the scheduler to // create a flow, since previously the client's flow was // just merged with a single best-effort flow. // // In the second case, we have to close the flow that existed // and remap the client's flow to the single best-efort flow, // thereby merging the client's flow with the existing b/e // flow. // if((Adapter->BestEffortLimit != UNSPECIFIED_RATE) && (OldCmParams->Transmit.ServiceType == SERVICETYPE_BESTEFFORT) && (NewCmParams->Transmit.ServiceType != SERVICETYPE_BESTEFFORT)){ // // Unmerge // PS_UNLOCK(&Adapter->Lock); Status = GetSchedulerFlowContext(Vc); if(Status != NDIS_STATUS_SUCCESS){ goto Exit; } Status = (*Vc->PsComponent->CreateFlow)( Vc->PsPipeContext, Vc, NewCallParameters, Vc->PsFlowContext); } else{ if((Adapter->BestEffortLimit != UNSPECIFIED_RATE) && (OldCmParams->Transmit.ServiceType != SERVICETYPE_BESTEFFORT) && (NewCmParams->Transmit.ServiceType == SERVICETYPE_BESTEFFORT)){ // // Merge // PS_UNLOCK(&Adapter->Lock); (*Vc->PsComponent->DeleteFlow)( Vc->PsPipeContext, Vc->PsFlowContext); Vc->PsFlowContext = Adapter->BestEffortVc.PsFlowContext; Status = NDIS_STATUS_SUCCESS; } else{ PS_UNLOCK(&Adapter->Lock); Status = (*Vc->PsComponent->ModifyFlow)( Vc->PsPipeContext, Vc->PsFlowContext, NewCallParameters); } } } // Modify Exit: // // Revert the call parameters. // NewCmParams->Transmit.TokenRate = OriginalTokenRate; return(Status); } // AddFlowToScheduler NDIS_STATUS GetSchedulerFlowContext( PGPC_CLIENT_VC AdapterVc ) /*++ Routine Description: Allocate the pipe context area for the scheduler. Arguments: AdapterVc- pointer to adapter VC context struct Return Value: NDIS_STATUS_SUCCESS, otherwise appropriate error value --*/ { PADAPTER Adapter = AdapterVc->Adapter; NDIS_STATUS Status; PPSI_INFO *SchedulerConfig; PPSI_INFO PsComponent = AdapterVc->PsComponent; ULONG ContextLength = 0; ULONG FlowContextLength = 0; ULONG Index = 0; PPS_PIPE_CONTEXT PipeContext = AdapterVc->PsPipeContext; PPS_FLOW_CONTEXT FlowContext; PPS_FLOW_CONTEXT PrevContext; // // The length of the flow context buffer for this pipe was calculated // when the pipe was initialized. // PsAllocatePool(AdapterVc->PsFlowContext, Adapter->FlowContextLength, FlowContextTag ); if ( AdapterVc->PsFlowContext == NULL ) { return NDIS_STATUS_RESOURCES; } // Set up the context buffer FlowContext = (PPS_FLOW_CONTEXT)AdapterVc->PsFlowContext; PrevContext = NULL; while (PsComponent != NULL) { FlowContext->NextComponentContext = (PPS_FLOW_CONTEXT) ((UINT_PTR)FlowContext + PsComponent->FlowContextLength); FlowContext->PrevComponentContext = PrevContext; PsComponent = PipeContext->NextComponent; PipeContext = PipeContext->NextComponentContext; PrevContext = FlowContext; FlowContext = FlowContext->NextComponentContext; } return NDIS_STATUS_SUCCESS; } // GetSchedulerFlowContext NDIS_STATUS EmptyPacketsFromScheduler( PGPC_CLIENT_VC Vc ) /*++ Routine Description: Cleans up (DROPS) the pending packets on this Vc in each of the components --*/ { PADAPTER Adapter = Vc->Adapter; NDIS_STATUS Status; CheckLLTag(Vc, GpcClientVc); PsStructAssert(Adapter); PsDbgOut(DBG_TRACE, DBG_VC, ("(%08X) EmptyPacketsFromScheduler\n", Vc)); if(Adapter->MediaType == NdisMediumWan) { if(Vc->PsFlowContext != Vc->WanLink->BestEffortVc.PsFlowContext){ // // Different context - definitely should be removed. // (*Vc->PsComponent->EmptyFlow)(Vc->PsPipeContext, Vc->PsFlowContext); } else { // // Same context. Remove only if it is actually the best-effort // VC. // if(Vc == &Vc->WanLink->BestEffortVc){ (*Vc->PsComponent->EmptyFlow)(Vc->PsPipeContext, Vc->PsFlowContext); } } } else { if(Vc->PsFlowContext != Adapter->BestEffortVc.PsFlowContext){ // // Different context - definitely should be removed. // (*Vc->PsComponent->EmptyFlow)(Vc->PsPipeContext, Vc->PsFlowContext); } else { // // Same context. Remove only if it is actually the best-effort // VC. // if(Vc == &Adapter->BestEffortVc){ (*Vc->PsComponent->EmptyFlow)(Vc->PsPipeContext, Vc->PsFlowContext); } } } return NDIS_STATUS_SUCCESS; } NDIS_STATUS RemoveFlowFromScheduler( PGPC_CLIENT_VC Vc ) /*++ Routine Description: Notify the PSA that the flow is going away Arguments: See the DDK... Return Values: None --*/ { PADAPTER Adapter = Vc->Adapter; NDIS_STATUS Status; CheckLLTag(Vc, GpcClientVc); PsStructAssert(Adapter); PsDbgOut(DBG_TRACE, DBG_VC, ("(%08X) RemoveFlowFromScheduler\n", Vc)); // // if this is a user vc which is merged into the scheduler's // internal best effort flow, then delete the vc without affecting // the scheduler. if it is not, then remove it from the scheduler // and delete the vc. // if(Adapter->MediaType == NdisMediumWan) { if(Vc->PsFlowContext != Vc->WanLink->BestEffortVc.PsFlowContext){ // // Different context - definitely should be removed. // (*Vc->PsComponent->DeleteFlow)(Vc->PsPipeContext, Vc->PsFlowContext); } else { // // Same context. Remove only if it is actually the best-effort // VC. // if(Vc == &Vc->WanLink->BestEffortVc){ (*Vc->PsComponent->DeleteFlow)(Vc->PsPipeContext, Vc->PsFlowContext); } } } else { if(Vc->PsFlowContext != Adapter->BestEffortVc.PsFlowContext){ // // Different context - definitely should be removed. // (*Vc->PsComponent->DeleteFlow)(Vc->PsPipeContext, Vc->PsFlowContext); } else { // // Same context. Remove only if it is actually the best-effort // VC. // if(Vc == &Adapter->BestEffortVc){ (*Vc->PsComponent->DeleteFlow)(Vc->PsPipeContext, Vc->PsFlowContext); } } } return NDIS_STATUS_SUCCESS; } // RemoveFlowFromScheduler NTSTATUS ModifyBestEffortBandwidth( PADAPTER Adapter, ULONG BestEffortRate) { PCO_CALL_PARAMETERS CallParams; PCO_CALL_MANAGER_PARAMETERS CallMgrParameters; ULONG CallParamsLength; PGPC_CLIENT_VC Vc; NDIS_STATUS Status; PsStructAssert(Adapter); Vc = &Adapter->BestEffortVc; CheckLLTag(Vc, GpcClientVc); // // This handles a TC API request to modify the default // best-effort bandwidth. Note that the b/e bandwidth // can only be modified if the PS is in limited b/e mode. // // Also - note that the b/e bandwidth can only be modified // while the adapter is in the Running state. We do not // have to worry about locking the VC since the b/e VC // will not be manipulated while it is in the running state // except by this thread. // PS_LOCK(&Adapter->Lock); if((Adapter->BestEffortLimit == UNSPECIFIED_RATE)) { PS_UNLOCK(&Adapter->Lock); return(STATUS_WMI_NOT_SUPPORTED); } if((BestEffortRate > Adapter->LinkSpeed) || (BestEffortRate == 0)){ PS_UNLOCK(&Adapter->Lock); return(STATUS_INVALID_PARAMETER); } else{ if(Adapter->PsMpState != AdapterStateRunning){ PS_UNLOCK(&Adapter->Lock); return(STATUS_WMI_NOT_SUPPORTED); } CallParamsLength = sizeof(CO_CALL_PARAMETERS) + sizeof(CO_CALL_MANAGER_PARAMETERS) + sizeof(QOS_SD_MODE) + sizeof(QOS_OBJECT_HDR); PsAllocatePool(CallParams, CallParamsLength, CmParamsTag); if(CallParams == NULL){ PS_UNLOCK(&Adapter->Lock); PsDbgOut(DBG_FAILURE, DBG_VC, ("ModifyBestEffortBandwidth: can't allocate call parms\n")); return(STATUS_INSUFFICIENT_RESOURCES); } // // build a call params struct describing the flow // CallMgrParameters = (PCO_CALL_MANAGER_PARAMETERS)(CallParams + 1); NdisFillMemory(CallParams, CallParamsLength, (UCHAR)QOS_UNSPECIFIED); CallParams->Flags = 0; CallParams->CallMgrParameters = CallMgrParameters; CallParams->MediaParameters = NULL; FillInCmParams(CallMgrParameters, SERVICETYPE_BESTEFFORT, BestEffortRate, (ULONG)UNSPECIFIED_RATE, Adapter->TotalSize, TC_NONCONF_SHAPE, QOS_UNSPECIFIED); Status = (*Vc->PsComponent->ModifyFlow)( Vc->PsPipeContext, Vc->PsFlowContext, CallParams); if(Status == NDIS_STATUS_SUCCESS){ Adapter->BestEffortLimit = BestEffortRate; } PS_UNLOCK(&Adapter->Lock); PsFreePool(CallParams); return(Status); } } /* end mpvc.c */