635 lines
15 KiB
C
635 lines
15 KiB
C
/*++
|
||
|
||
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 */
|