/*++ Copyright (c) 1997-2001 Microsoft Corporation Module Name: timer.c Abstract: Contains timer management structures. Author: Sanjay Anand (SanjayAn) 26-May-1997 ChunYe Environment: Kernel mode Revision History: --*/ #include "precomp.h" #ifdef RUN_WPP #include "timer.tmh" #endif #pragma hdrstop BOOLEAN IPSecInitTimer() /*++ Routine Description: Initialize the timer structures. Arguments: None Return Value: None --*/ { PIPSEC_TIMER_LIST pTimerList; LONG i; INIT_LOCK(&g_ipsec.TimerLock); // // Allocate timer structures // for (i = 0; i < IPSEC_CLASS_MAX; i++) { pTimerList = &(g_ipsec.TimerList[i]); #if DBG pTimerList->atl_sig = atl_signature; #endif pTimerList->pTimers = IPSecAllocateMemory( sizeof(IPSEC_TIMER) * IPSecTimerListSize[i], IPSEC_TAG_TIMER); if (pTimerList->pTimers == NULL_PIPSEC_TIMER) { while (--i >= 0) { pTimerList = &(g_ipsec.TimerList[i]); IPSecFreeMemory(pTimerList->pTimers); } return FALSE; } } // // Initialize timer wheels. // for (i = 0; i < IPSEC_CLASS_MAX; i++) { pTimerList = &(g_ipsec.TimerList[i]); IPSecZeroMemory(pTimerList->pTimers, sizeof(IPSEC_TIMER) * IPSecTimerListSize[i]); pTimerList->MaxTimer = IPSecMaxTimerValue[i]; pTimerList->TimerPeriod = IPSecTimerPeriod[i]; pTimerList->TimerListSize = IPSecTimerListSize[i]; IPSEC_INIT_SYSTEM_TIMER( &(pTimerList->NdisTimer), IPSecTickHandler, (PVOID)pTimerList); } return TRUE; } VOID IPSecStartTimer( IN PIPSEC_TIMER pTimer, IN IPSEC_TIMEOUT_HANDLER TimeoutHandler, IN ULONG SecondsToGo, IN PVOID Context ) /*++ Routine Description: Start an IPSEC timer. Based on the length (SecondsToGo) of the timer, we decide on whether to insert it in the short duration timer list or in the long duration timer list in the Interface structure. NOTE: the caller is assumed to either hold a lock to the structure that contains the timer, or ensure that it is safe to access the timer structure. Arguments: pTimer - Pointer to IPSEC Timer structure TimeoutHandler - Handler function to be called if this timer expires SecondsToGo - When does this timer go off? Context - To be passed to timeout handler if this timer expires Return Value: None --*/ { PIPSEC_TIMER_LIST pTimerList; // List to which this timer goes PIPSEC_TIMER pTimerListHead; // Head of above list ULONG Index; // Into timer wheel ULONG TicksToGo = 1; KIRQL kIrql; INT i; IPSEC_DEBUG(LL_A, DBF_TIMER, ("StartTimer: Secs %d, Handler %p, Ctxt %p, pTimer %p", SecondsToGo, TimeoutHandler, Context, pTimer)); if (IPSEC_IS_TIMER_ACTIVE(pTimer)) { IPSEC_DEBUG(LL_A, DBF_TIMER, ("Start timer: pTimer %p: is active (list %p, hnd %p), stopping it", pTimer, pTimer->pTimerList, pTimer->TimeoutHandler)); if (!IPSecStopTimer(pTimer)) { IPSEC_DEBUG(LL_A, DBF_TIMER, ("Couldnt stop prev timer - bail")); return; } } ACQUIRE_LOCK(&g_ipsec.TimerLock, &kIrql); ASSERT(!IPSEC_IS_TIMER_ACTIVE(pTimer)); // // Find the list to which this timer should go, and the // offset (TicksToGo) // for (i = 0; i < IPSEC_CLASS_MAX; i++) { pTimerList = &(g_ipsec.TimerList[i]); if (SecondsToGo < pTimerList->MaxTimer) { // // Found it. // TicksToGo = SecondsToGo / (pTimerList->TimerPeriod); break; } } // // Find the position in the list for this timer // Index = pTimerList->CurrentTick + TicksToGo; if (Index >= pTimerList->TimerListSize) { Index -= pTimerList->TimerListSize; } ASSERT(Index < pTimerList->TimerListSize); pTimerListHead = &(pTimerList->pTimers[Index]); // // Fill in the timer // pTimer->pTimerList = pTimerList; pTimer->LastRefreshTime = pTimerList->CurrentTick; pTimer->Duration = TicksToGo; pTimer->TimeoutHandler = TimeoutHandler; pTimer->Context = Context; // // Insert this timer in the "ticking" list // pTimer->pPrevTimer = pTimerListHead; pTimer->pNextTimer = pTimerListHead->pNextTimer; if (pTimer->pNextTimer != NULL_PIPSEC_TIMER) { pTimer->pNextTimer->pPrevTimer = pTimer; } pTimerListHead->pNextTimer = pTimer; // // Start off the system tick timer if necessary. // pTimerList->TimerCount++; if (pTimerList->TimerCount == 1) { IPSEC_DEBUG(LL_A, DBF_TIMER, ("StartTimer: Starting system timer %p, class %d", &(pTimerList->NdisTimer), i)); IPSEC_START_SYSTEM_TIMER(&(pTimerList->NdisTimer), pTimerList->TimerPeriod); } RELEASE_LOCK(&g_ipsec.TimerLock, kIrql); // // We're done // IPSEC_DEBUG(LL_A, DBF_TIMER, ("Started timer %p, Secs %d, Index %d, Head %p", pTimer, SecondsToGo, Index, pTimerListHead)); IPSEC_INCREMENT(g_ipsec.NumTimers); return; } BOOLEAN IPSecStopTimer( IN PIPSEC_TIMER pTimer ) /*++ Routine Description: Stop an IPSEC timer, if it is running. We remove this timer from the active timer list and mark it so that we know it's not running. NOTE: the caller is assumed to either hold a lock to the structure that contains the timer, or ensure that it is safe to access the timer structure. SIDE EFFECT: If we happen to stop the last timer (of this "duration") on the Interface, we also stop the appropriate Tick function. Arguments: pTimer - Pointer to IPSEC Timer structure Return Value: TRUE if the timer was running, FALSE otherwise. --*/ { PIPSEC_TIMER_LIST pTimerList; // Timer List to which this timer belongs BOOLEAN WasRunning; KIRQL kIrql; IPSEC_DEBUG(LL_A, DBF_TIMER, ("Stopping Timer %p, List %p, Prev %p, Next %p", pTimer, pTimer->pTimerList, pTimer->pPrevTimer, pTimer->pNextTimer)); ACQUIRE_LOCK(&g_ipsec.TimerLock, &kIrql); if (IPSEC_IS_TIMER_ACTIVE(pTimer)) { WasRunning = TRUE; // // Unlink timer from the list // ASSERT(pTimer->pPrevTimer); // the list head always exists if (pTimer->pPrevTimer) { pTimer->pPrevTimer->pNextTimer = pTimer->pNextTimer; } if (pTimer->pNextTimer) { pTimer->pNextTimer->pPrevTimer = pTimer->pPrevTimer; } pTimer->pNextTimer = pTimer->pPrevTimer = NULL_PIPSEC_TIMER; // // Update timer count on Interface, for this class of timers // pTimerList = pTimer->pTimerList; pTimerList->TimerCount--; // // If all timers of this class are gone, stop the system tick timer // for this class // if (pTimerList->TimerCount == 0) { IPSEC_DEBUG(LL_A, DBF_TIMER, ("Stopping system timer %p, List %p", &(pTimerList->NdisTimer), pTimerList)); pTimerList->CurrentTick = 0; IPSEC_STOP_SYSTEM_TIMER(&(pTimerList->NdisTimer)); } // // Mark stopped timer as not active // pTimer->pTimerList = (PIPSEC_TIMER_LIST)NULL; IPSEC_DECREMENT(g_ipsec.NumTimers); } else { WasRunning = FALSE; } RELEASE_LOCK(&g_ipsec.TimerLock, kIrql); return WasRunning; } VOID IPSecTickHandler( IN PVOID SystemSpecific1, IN PVOID Context, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3 ) /*++ Routine Description: This is the handler we register with the system for processing each Timer List. This is called every "tick" seconds, where "tick" is determined by the granularity of the timer type. Arguments: Context - Actually a pointer to a Timer List structure SystemSpecific[1-3] - Not used Return Value: None --*/ { PIPSEC_TIMER_LIST pTimerList; PIPSEC_TIMER pExpiredTimer; // Start of list of expired timers PIPSEC_TIMER pNextTimer; // for walking above list PIPSEC_TIMER pTimer; // temp, for walking timer list PIPSEC_TIMER pPrevExpiredTimer; // for creating expired timer list ULONG Index; // into the timer wheel ULONG NewIndex; // for refreshed timers KIRQL kIrql; pTimerList = (PIPSEC_TIMER_LIST)Context; IPSEC_DEBUG(LL_A, DBF_TIMER, ("Tick: List %p, Count %d", pTimerList, pTimerList->TimerCount)); pExpiredTimer = NULL_PIPSEC_TIMER; ACQUIRE_LOCK(&g_ipsec.TimerLock, &kIrql); if (TRUE) { // // Pick up the list of timers scheduled to have expired at the // current tick. Some of these might have been refreshed. // Index = pTimerList->CurrentTick; pExpiredTimer = (pTimerList->pTimers[Index]).pNextTimer; (pTimerList->pTimers[Index]).pNextTimer = NULL_PIPSEC_TIMER; // // Go through the list of timers scheduled to expire at this tick. // Prepare a list of expired timers, using the pNextExpiredTimer // link to chain them together. // // Some timers may have been refreshed, in which case we reinsert // them in the active timer list. // pPrevExpiredTimer = NULL_PIPSEC_TIMER; for (pTimer = pExpiredTimer; pTimer != NULL_PIPSEC_TIMER; pTimer = pNextTimer) { // // Save a pointer to the next timer, for the next iteration. // pNextTimer = pTimer->pNextTimer; IPSEC_DEBUG(LL_A, DBF_TIMER, ("Tick Handler: looking at timer %p, next %p", pTimer, pNextTimer)); // // Find out when this timer should actually expire. // NewIndex = pTimer->LastRefreshTime + pTimer->Duration; if (NewIndex >= pTimerList->TimerListSize) { NewIndex -= pTimerList->TimerListSize; } // // Check if we are currently at the point of expiry. // if (NewIndex != Index) { // // This timer still has some way to go, so put it back. // IPSEC_DEBUG(LL_A, DBF_TIMER, ("Tick: Reinserting Timer %p: Hnd %p, Durn %d, Ind %d, NewInd %d", pTimer, pTimer->TimeoutHandler, pTimer->Duration, Index, NewIndex)); // // Remove it from the expired timer list. Note that we only // need to update the forward (pNextExpiredTimer) links. // if (pPrevExpiredTimer == NULL_PIPSEC_TIMER) { pExpiredTimer = pNextTimer; } else { pPrevExpiredTimer->pNextExpiredTimer = pNextTimer; } // // And insert it back into the running timer list. // pTimer->pNextTimer = (pTimerList->pTimers[NewIndex]).pNextTimer; if (pTimer->pNextTimer != NULL_PIPSEC_TIMER) { pTimer->pNextTimer->pPrevTimer = pTimer; } pTimer->pPrevTimer = &(pTimerList->pTimers[NewIndex]); (pTimerList->pTimers[NewIndex]).pNextTimer = pTimer; } else { // // This one has expired. Keep it in the expired timer list. // pTimer->pNextExpiredTimer = pNextTimer; if (pPrevExpiredTimer == NULL_PIPSEC_TIMER) { pExpiredTimer = pTimer; } pPrevExpiredTimer = pTimer; // // Mark it as inactive. // ASSERT(pTimer->pTimerList == pTimerList); pTimer->pTimerList = (PIPSEC_TIMER_LIST)NULL; // // Update the active timer count. // pTimerList->TimerCount--; } } // // Update current tick index in readiness for the next tick. // if (++Index == pTimerList->TimerListSize) { pTimerList->CurrentTick = 0; } else { pTimerList->CurrentTick = Index; } if (pTimerList->TimerCount > 0) { // // Re-arm the tick handler // IPSEC_DEBUG(LL_A, DBF_TIMER, ("Tick[%d]: Starting system timer %p", pTimerList->CurrentTick, &(pTimerList->NdisTimer))); IPSEC_START_SYSTEM_TIMER(&(pTimerList->NdisTimer), pTimerList->TimerPeriod); } else { pTimerList->CurrentTick = 0; } } RELEASE_LOCK(&g_ipsec.TimerLock, kIrql); // // Now pExpiredTimer is a list of expired timers. // Walk through the list and call the timeout handlers // for each timer. // while (pExpiredTimer != NULL_PIPSEC_TIMER) { pNextTimer = pExpiredTimer->pNextExpiredTimer; IPSEC_DEBUG(LL_A, DBF_TIMER, ("Expired timer %p: handler %p, next %p", pExpiredTimer, pExpiredTimer->TimeoutHandler, pNextTimer)); (*(pExpiredTimer->TimeoutHandler))( pExpiredTimer, pExpiredTimer->Context); IPSEC_DECREMENT(g_ipsec.NumTimers); pExpiredTimer = pNextTimer; } }