📄 teardown.c
字号:
//
// Receive a VRR TearDown message. Called from ReceiveSRPacket.
//
// Note: basic packet parsing performd in ReceiveSRPacket, which
// provides us the VrrSetup structure in an SRPacket option.
//
void
ReceiveTearDown(
MiniportAdapter *VA,
ProtocolAdapter *PA,
InternalVrrTearDown *InternalTearDown)
{
KIRQL OldIrql;
RouteTable *RT = &VA->RT;
Time Now = KeQueryInterruptTime();
TeardownInformQueue *InformQueue = NULL;
VrrGlobalPathId *FirstPPId;
VrrGlobalPathId *PPId;
VrrGlobalPathId *PTDBuff;
NeighborCacheEntry *NCE;
VrrTearDown *TearDown;
uint NumPId;
VirtualAddress *PVSET = NULL;
VirtualAddress *MsgVSet = NULL;
uint VSetLen;
uint CountVSet;
uint i;
VSetRepairQueue *VSetRepair = NULL;
//
// Validate the VrrTearDown message.
//
VRRASSERT(InternalTearDown != NULL);
TearDown = &InternalTearDown->Opt;
VrrTrace(VA,2,"TD:TD=enter TD",TearDown->Source, NULL,TearDown->Dest,NULL,0,NULL,0);
//
// Ignore teardown messages not destined to self.
// ref C# "// ignore teardown messages not destined to me"
//
if (! VirtualAddressEqual(VA->Address, TearDown->Dest)) {
VrrTrace(VA,1,"TD:TD=drop TD self(s)!=TD->Dest(d)",TearDown->Source, NULL,TearDown->Dest,NULL,0,NULL,0);
InterlockedIncrement((PLONG)&VA->CountMisroutedTearDown);
return;
}
NumPId = RtlUlongByteSwap(TearDown->NumGlobalPathId);
if (NumPId == 0) {
VrrTrace(VA,1,"TD:TD=ignoring because NumPId==0",TearDown->Source, NULL,TearDown->Dest,NULL,0,NULL,0);
return;
}
FirstPPId = TearDown->PId;
MsgVSet = (VirtualAddress *)&FirstPPId[NumPId];
VSetLen = (uint)InternalTearDown->Opt.OptDataLen
- sizeof(TearDown->Source)
- sizeof(TearDown->Dest)
- sizeof(TearDown->Proxy)
- sizeof(TearDown->Flags)
- sizeof(TearDown->FrameSeqNo)
- sizeof(TearDown->HopCount)
- sizeof(TearDown->NumGlobalPathId)
- (sizeof(VrrGlobalPathId) * NumPId);
CountVSet = VSetLen / sizeof(VirtualAddress);
UpdatePacketIndex(VA, TearDown->Source, RtlUlongByteSwap(TearDown->FrameSeqNo), TearDown->HopCount,
TearDown->Source, TearDown->Dest, (VRRIf)PA->Index, VRR_OPTION_TYPE_TEARDOWN);
KeAcquireSpinLock(&VA->RT.Lock, &OldIrql);
KeAcquireSpinLockAtDpcLevel(&VA->NC.Lock);
KeAcquireSpinLockAtDpcLevel(&VA->TDC.Lock);
//
// Drop TD unless the sender is a physical neighbor.
// Note TD->Source is the last node that forwarded the TD.
// ref C# "// XXX do we really need the clause on physical neighbors"
//
if (FindNCE(&VA->NC,
TearDown->Source,
VRR_IFID_UNSPECIFIED,
VRR_IFID_UNSPECIFIED,
VRR_NCE_STATE_LINKED) == NULL) {
VrrTrace(VA,1,"TD:TD=drop TD sender(s) not a physn",TearDown->Source, NULL,TearDown->Dest,NULL,0,NULL,0);
CountVSet = 0;
goto ReleaseAndReturn;
}
//
// Construct list of neighbors for fwd tailored image of this TearDown.
//
for (NCE = VA->NC.FirstNCE; NCE != SentinelNCE(&VA->NC); NCE = NCE->Next) {
RouteTableEntry *RTE;
TeardownInformQueue *TIQ;
VirtualAddress *Inform;
uint TearDownSize = 0;
uint TearDownCount = 0;
VrrGlobalPathId *TDBuff = NULL;
uint NumPIdOut;
//
// Do not fwd the TD to the node that sent this TearDown.
//
if (VirtualAddressEqual(NCE->VAddress, TearDown->Source))
continue;
//
// Count paths mentioned in TearDown for which the NCE is a NextHop.
//
PPId = FirstPPId;
for (i = 0; i < NumPId; i++) {
for (RTE = RT->FirstRTE; RTE != SentinelRTE(RT); RTE = RTE->Next) {
//
// Ignore TearDown elements that fail validation.
//
if (! IsValidTearDownElement(TearDown->Source,PPId,RTE))
continue;
//
// Teardown is send in direction of NextA or Next B ref C#:
// "Debug.Assert(fte.NextA == sender || fte.NextB == sender);"
// "NodeId neighbor = (fte.NextA != sender) ? fte.NextA : fte.NextB;"
//
if (RTE->A.Next == NCE || RTE->B.Next == NCE) {
//
// Increment PathId count for this NCE.
//
if (++TearDownCount == VRRTEARDOWN_MAX_PID(VA)) {
VRRASSERT("RecvTD: overflowed TD buffer");
break;
}
}
}
//
// Proceed to next PathId in the TearDown message.
//
PPId++;
}
//
// Proceed to next NCE if nothing to tell this NCE.
//
if (TearDownCount == 0)
continue;
//
// Add this NCE to list of TearDown recipients.
//
if ((TIQ = ExAllocatePool(NonPagedPool, sizeof *TIQ)) == NULL) {
VrrTrace(VA,1,"TD:TD=failed to allocate TIQ",TearDown->Source, NULL,TearDown->Dest,NULL,0,NULL,0);
continue;
}
RtlZeroMemory(TIQ, sizeof *TIQ);
RtlCopyMemory(TIQ->SendTo, NCE->VAddress, sizeof(VirtualAddress));
TIQ->Next = InformQueue;
InformQueue = TIQ;
//
// Allocate memory to hold the flat PId[] buffer for this NCE.
// N.B. allow space also for copy of originator's vset.
//
TearDownSize = TearDownCount * sizeof(VrrGlobalPathId);
TearDownSize += VSetLen;
TDBuff = ExAllocatePool(NonPagedPool, TearDownSize);
if (TDBuff == NULL) {
VrrTrace(VA,1,"TD:TD=failed to allocate TDBuff",TearDown->Source, NULL,TearDown->Dest,NULL,0,NULL,0);
continue;
}
RtlZeroMemory(TDBuff,TearDownSize);
PTDBuff = TDBuff;
//
// Populate the PId[] buffer for the tailored TearDown to this NCE.
//
NumPIdOut = TearDownCount;
for (i = 0; i < NumPId && TearDownCount > 0; i++) {
PPId = &FirstPPId[i];
for (RTE = RT->FirstRTE; RTE != SentinelRTE(RT); RTE = RTE->Next) {
//
// Ignore TearDown elements that fail validation.
//
if (! IsValidTearDownElement(TearDown->Source,PPId,RTE))
continue;
if (RTE->A.Next == NCE || RTE->B.Next == NCE) {
//
// Include this PathId in TearDown sent to this NCE.
//
RtlCopyMemory(PTDBuff->Address, PPId->Address, sizeof(VirtualAddress));
PTDBuff->PathId = PPId->PathId;
PTDBuff++;
TearDownCount--;
VrrTrace(VA,3,"TD:TD=Fwd_",VA->Address,TearDown->Source,PPId->Address,"Pid",RtlUlongByteSwap(PPId->PathId),NULL,0);
}
}
}
//
// Append the originator's vset onto TDBuff.
//
RtlCopyMemory(PTDBuff, MsgVSet, VSetLen);
//
// Encode the flat PId[] buffer in an InternalVrrTearDown encoding this TearDown.
//
TIQ->ITD = CreateTearDownOpt(VA,TIQ->SendTo, NumPIdOut, TearDownSize, TDBuff, FALSE, TRUE);
ExFreePool(TDBuff);
} // ForAllNCE.
//
// Apply the TearDown to our own RTE state and
// add it to our TearDown cache.
//
PPId = FirstPPId;
for (i = 0; i < NumPId; i++) {
RouteTableEntry *RTE;
VrrTrace(VA,3,"TD:TD=Exec",TearDown->Source,TearDown->Source,PPId->Address,"Pid",RtlUlongByteSwap(PPId->PathId),NULL,0);
for (RTE = RT->FirstRTE; RTE != SentinelRTE(RT); RTE = RTE->Next) {
//
// Ignore TearDown elements that fail validation.
//
if (! IsValidTearDownElement(TearDown->Source,PPId,RTE))
continue;
//
// We know that this RTE matches (A,PathId) and that
// TearDown was sent by NextA or NextB from the RTE.
// On this basis we are prepared to retire this RTE.
//
RTE->State = VRR_RTE_STATE_RETIRED;
RTE->Timeout = RTERetireTimeout(RTE->Flags);
VrrTrace(VA,2,"TD:TD=retired RTE(s=A,d=B)",RTE->A.Address, NULL,RTE->B.Address,"Pid",RTE->PathId,NULL,0);
VSetRepair = ScheduleVSetRepair(VA,VSetRepair,RTE);
}
//
// Cache copy of the TearDown in case it overtook
// the corresponding Setup while in flight.
//
FindOrCreateTDCE(&VA->TDC,
PPId->Address,
RtlUlongByteSwap(PPId->PathId),
TearDown->Source);
//
// Proceed to next PathId in the TearDown message.
//
PPId++;
}
ReleaseAndReturn:
KeReleaseSpinLockFromDpcLevel(&VA->TDC.Lock);
KeReleaseSpinLockFromDpcLevel(&VA->NC.Lock);
KeReleaseSpinLock(&VA->RT.Lock, OldIrql);
//
// If the TearDown included a vset, process it now.
//
if (CountVSet > 0)
ReceiveVSet(VA,CountVSet,MsgVSet);
//
// Schedule a TearDown message to each NCE in InformQueue.
// Then free the InformQueue and InternalVrrTearDown memory.
//
while (NULL != InformQueue) {
TeardownInformQueue *TIQ = InformQueue;
InformQueue = InformQueue->Next;
if (TIQ->ITD != NULL)
MsgQueueMessage(VA, TIQ->SendTo, (InternalOption *) TIQ->ITD, VRR_IFID_UNSPECIFIED,VRR_IFID_UNSPECIFIED,TDOWN_DELAY,NULL);
ExFreePool(TIQ);
}
//
// Perform any required vset repair.
//
if (VSetRepair != NULL)
ProcessVSetRepairQueue(VA,VSetRepair);
}
//* TearDownCacheTimeout
//
// Housekeeping of the TearDown cache.
//
Time
TearDownCacheTimeout(
MiniportAdapter *VA,
Time Now)
{
KIRQL OldIrql;
Time NextTimeout = Now + TDOWN_MAX_HK_INTERVAL;
TearDownCacheEntry *TDCE;
TearDownCacheEntry *NextTDCE;
KeAcquireSpinLock(&VA->TDC.Lock, &OldIrql);
for (TDCE = VA->TDC.FirstTDCE;
TDCE != SentinelTDCE(&VA->TDC);
TDCE = NextTDCE) {
NextTDCE = TDCE->Next;
if (TDCE->Timeout <= Now)
RemoveTDCE(&VA->TDC,TDCE);
else if (TDCE->Timeout < NextTimeout)
NextTimeout = TDCE->Timeout;
}
KeReleaseSpinLock(&VA->TDC.Lock, OldIrql);
return NextTimeout;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -