/*++ Copyright (c) 1991 Microsoft Corporation Module Name: tcpip\ip\mcasttmr.c Abstract: Timer routine for cleanup of MFEs Author: Amritansh Raghav Revision History: AmritanR Created Notes: --*/ #include "precomp.h" #if IPMCAST #define __FILE_SIG__ TMR_SIG #include "ipmcast.h" #include "ipmcstxt.h" #include "mcastmfe.h" VOID CompleteNotificationIrp( IN PNOTIFICATION_MSG pMsg ); VOID McastTimerRoutine( PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2 ); // // MUST BE PAGED IN // #pragma alloc_text(PAGEIPMc, McastTimerRoutine) VOID McastTimerRoutine( PKDPC Dpc, PVOID DeferredContext, PVOID SystemArgument1, PVOID SystemArgument2 ) /*++ Routine Description: The DPC routine associated with the timer. The global variable g_ulNextHashIndex keeps track of the bucket that needs to be walked and checked for activity. The routine walks all the groups in BUCKETS_PER_QUANTUM number of buckets. N.B.: We should probably use a JOB object for this. Locks: Takes the lock for each hash bucket walked as writer Arguments: Dpc DeferredContext SystemArgument1 SystemArgument2 Return Value: NONE --*/ { LONGLONG llCurrentTime, llTime; ULONG ulIndex, ulNumBuckets, ulMsgIndex; PLIST_ENTRY pleGrpNode, pleSrcNode; PGROUP pGroup; PSOURCE pSource; LARGE_INTEGER liDueTime; PNOTIFICATION_MSG pCopy; PIPMCAST_MFE_MSG pMsg; UNREFERENCED_PARAMETER(Dpc); UNREFERENCED_PARAMETER(DeferredContext); UNREFERENCED_PARAMETER(SystemArgument1); UNREFERENCED_PARAMETER(SystemArgument2); TraceEnter(TMR, "McastTimerRoutine"); #pragma warning(push) #pragma warning(disable:4127) KeQueryTickCount((PLARGE_INTEGER)&llCurrentTime); #pragma warning(pop) ulIndex = g_ulNextHashIndex; ulMsgIndex = 0; pCopy = NULL; pMsg = NULL; Trace(TMR, TRACE, ("McastTimerRoutine: Starting at index %d\n", ulIndex)); for(ulNumBuckets = 0; ulNumBuckets < BUCKETS_PER_QUANTUM; ulNumBuckets++) { // // Acquire the bucket lock as writer. Since we are off a TIMER // we are at DPC // EnterWriterAtDpcLevel(&g_rgGroupTable[ulIndex].rwlLock); pleGrpNode = g_rgGroupTable[ulIndex].leHashHead.Flink; while(pleGrpNode isnot &(g_rgGroupTable[ulIndex].leHashHead)) { pGroup = CONTAINING_RECORD(pleGrpNode, GROUP, leHashLink); pleGrpNode = pleGrpNode->Flink; pleSrcNode = pGroup->leSrcHead.Flink; while(pleSrcNode isnot &pGroup->leSrcHead) { // // We look at the SOURCE without taking the lock, because // the source can not be deleted without removing it from the // group list, which can not happen since we have the group // bucket locked as writer // pSource = CONTAINING_RECORD(pleSrcNode, SOURCE, leGroupLink); pleSrcNode = pleSrcNode->Flink; // // The TimeOut and CreateTime can be looked at without // a lock, but the LastActivity should be read only with // the lock held. However, we shall take the chance and // not use a lock // if(pSource->llTimeOut isnot 0) { // // Timeout value has been supplied, lets use that // llTime = llCurrentTime - pSource->llCreateTime; if((llCurrentTime > pSource->llCreateTime) and (llTime < pSource->llTimeOut)) { continue; } Trace(TMR, TRACE, ("McastTimerRoutine: %d.%d.%d.%d %d.%d.%d.%d entry being removed due to user supplied timeout\n", PRINT_IPADDR(pGroup->dwGroup), PRINT_IPADDR(pSource->dwSource))); } else { // // Otherwise, just do this based on activity // llTime = llCurrentTime - pSource->llLastActivity; if((llCurrentTime > pSource->llLastActivity) and (llTime < SECS_TO_TICKS(INACTIVITY_PERIOD))) { continue; } Trace(TMR, TRACE, ("McastTimerRoutine: %d.%d.%d.%d %d.%d.%d.%d entry being removed due to inactiviy\n", PRINT_IPADDR(pGroup->dwGroup), PRINT_IPADDR(pSource->dwSource))); } // // Otherwise we need to delete the source, and complete an // IRP back to the router manager // if(ulMsgIndex is 0) { RtAssert(!pCopy); pCopy = ExAllocateFromNPagedLookasideList(&g_llMsgBlocks); if(pCopy is NULL) { continue; } pCopy->inMessage.dwEvent = IPMCAST_DELETE_MFE_MSG; pMsg = &(pCopy->inMessage.immMfe); pMsg->ulNumMfes = 0; } pMsg->ulNumMfes++; pMsg->idmMfe[ulMsgIndex].dwGroup = pGroup->dwGroup; pMsg->idmMfe[ulMsgIndex].dwSource = pSource->dwSource; pMsg->idmMfe[ulMsgIndex].dwSrcMask = pSource->dwSrcMask; ulMsgIndex++; ulMsgIndex %= NUM_DEL_MFES; if(ulMsgIndex is 0) { // // Complete the Irp // CompleteNotificationIrp(pCopy); pCopy = NULL; pMsg = NULL; } // // The function needs the SOURCE ref'ed and locked // ReferenceSource(pSource); RtAcquireSpinLockAtDpcLevel(&(pSource->mlLock)); RemoveSource(pGroup->dwGroup, pSource->dwSource, pSource->dwSrcMask, pGroup, pSource); } } ExitWriterFromDpcLevel(&g_rgGroupTable[ulIndex].rwlLock); // // Done walking this bucket // ulIndex++; ulIndex %= GROUP_TABLE_SIZE; } // // The last message may not have been indicated up. See if it needs // to be completed // if(pCopy) { CompleteNotificationIrp(pCopy); } g_ulNextHashIndex = ulIndex; liDueTime = RtlEnlargedUnsignedMultiply(TIMER_IN_MILLISECS, SYS_UNITS_IN_ONE_MILLISEC); liDueTime = RtlLargeIntegerNegate(liDueTime); KeSetTimerEx(&g_ktTimer, liDueTime, 0, &g_kdTimerDpc); TraceLeave(TMR, "McastTimerRoutine"); } #endif