Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

517 lines
15 KiB

  1. /*++
  2. Copyright (c) 1997-2001 Microsoft Corporation
  3. Module Name:
  4. timer.c
  5. Abstract:
  6. Contains timer management structures.
  7. Author:
  8. Sanjay Anand (SanjayAn) 26-May-1997
  9. ChunYe
  10. Environment:
  11. Kernel mode
  12. Revision History:
  13. --*/
  14. #include "precomp.h"
  15. #ifdef RUN_WPP
  16. #include "timer.tmh"
  17. #endif
  18. #pragma hdrstop
  19. BOOLEAN
  20. IPSecInitTimer()
  21. /*++
  22. Routine Description:
  23. Initialize the timer structures.
  24. Arguments:
  25. None
  26. Return Value:
  27. None
  28. --*/
  29. {
  30. PIPSEC_TIMER_LIST pTimerList;
  31. LONG i;
  32. INIT_LOCK(&g_ipsec.TimerLock);
  33. //
  34. // Allocate timer structures
  35. //
  36. for (i = 0; i < IPSEC_CLASS_MAX; i++) {
  37. pTimerList = &(g_ipsec.TimerList[i]);
  38. #if DBG
  39. pTimerList->atl_sig = atl_signature;
  40. #endif
  41. pTimerList->pTimers = IPSecAllocateMemory(
  42. sizeof(IPSEC_TIMER) * IPSecTimerListSize[i],
  43. IPSEC_TAG_TIMER);
  44. if (pTimerList->pTimers == NULL_PIPSEC_TIMER) {
  45. while (--i >= 0) {
  46. pTimerList = &(g_ipsec.TimerList[i]);
  47. IPSecFreeMemory(pTimerList->pTimers);
  48. }
  49. return FALSE;
  50. }
  51. }
  52. //
  53. // Initialize timer wheels.
  54. //
  55. for (i = 0; i < IPSEC_CLASS_MAX; i++) {
  56. pTimerList = &(g_ipsec.TimerList[i]);
  57. IPSecZeroMemory(pTimerList->pTimers,
  58. sizeof(IPSEC_TIMER) * IPSecTimerListSize[i]);
  59. pTimerList->MaxTimer = IPSecMaxTimerValue[i];
  60. pTimerList->TimerPeriod = IPSecTimerPeriod[i];
  61. pTimerList->TimerListSize = IPSecTimerListSize[i];
  62. IPSEC_INIT_SYSTEM_TIMER(
  63. &(pTimerList->NdisTimer),
  64. IPSecTickHandler,
  65. (PVOID)pTimerList);
  66. }
  67. return TRUE;
  68. }
  69. VOID
  70. IPSecStartTimer(
  71. IN PIPSEC_TIMER pTimer,
  72. IN IPSEC_TIMEOUT_HANDLER TimeoutHandler,
  73. IN ULONG SecondsToGo,
  74. IN PVOID Context
  75. )
  76. /*++
  77. Routine Description:
  78. Start an IPSEC timer. Based on the length (SecondsToGo) of the
  79. timer, we decide on whether to insert it in the short duration
  80. timer list or in the long duration timer list in the Interface
  81. structure.
  82. NOTE: the caller is assumed to either hold a lock to the structure
  83. that contains the timer, or ensure that it is safe to access the
  84. timer structure.
  85. Arguments:
  86. pTimer - Pointer to IPSEC Timer structure
  87. TimeoutHandler - Handler function to be called if this timer expires
  88. SecondsToGo - When does this timer go off?
  89. Context - To be passed to timeout handler if this timer expires
  90. Return Value:
  91. None
  92. --*/
  93. {
  94. PIPSEC_TIMER_LIST pTimerList; // List to which this timer goes
  95. PIPSEC_TIMER pTimerListHead; // Head of above list
  96. ULONG Index; // Into timer wheel
  97. ULONG TicksToGo = 1;
  98. KIRQL kIrql;
  99. INT i;
  100. IPSEC_DEBUG(LL_A, DBF_TIMER,
  101. ("StartTimer: Secs %d, Handler %p, Ctxt %p, pTimer %p",
  102. SecondsToGo, TimeoutHandler, Context, pTimer));
  103. if (IPSEC_IS_TIMER_ACTIVE(pTimer)) {
  104. IPSEC_DEBUG(LL_A, DBF_TIMER,
  105. ("Start timer: pTimer %p: is active (list %p, hnd %p), stopping it",
  106. pTimer, pTimer->pTimerList, pTimer->TimeoutHandler));
  107. if (!IPSecStopTimer(pTimer)) {
  108. IPSEC_DEBUG(LL_A, DBF_TIMER, ("Couldnt stop prev timer - bail"));
  109. return;
  110. }
  111. }
  112. ACQUIRE_LOCK(&g_ipsec.TimerLock, &kIrql);
  113. ASSERT(!IPSEC_IS_TIMER_ACTIVE(pTimer));
  114. //
  115. // Find the list to which this timer should go, and the
  116. // offset (TicksToGo)
  117. //
  118. for (i = 0; i < IPSEC_CLASS_MAX; i++) {
  119. pTimerList = &(g_ipsec.TimerList[i]);
  120. if (SecondsToGo < pTimerList->MaxTimer) {
  121. //
  122. // Found it.
  123. //
  124. TicksToGo = SecondsToGo / (pTimerList->TimerPeriod);
  125. break;
  126. }
  127. }
  128. //
  129. // Find the position in the list for this timer
  130. //
  131. Index = pTimerList->CurrentTick + TicksToGo;
  132. if (Index >= pTimerList->TimerListSize) {
  133. Index -= pTimerList->TimerListSize;
  134. }
  135. ASSERT(Index < pTimerList->TimerListSize);
  136. pTimerListHead = &(pTimerList->pTimers[Index]);
  137. //
  138. // Fill in the timer
  139. //
  140. pTimer->pTimerList = pTimerList;
  141. pTimer->LastRefreshTime = pTimerList->CurrentTick;
  142. pTimer->Duration = TicksToGo;
  143. pTimer->TimeoutHandler = TimeoutHandler;
  144. pTimer->Context = Context;
  145. //
  146. // Insert this timer in the "ticking" list
  147. //
  148. pTimer->pPrevTimer = pTimerListHead;
  149. pTimer->pNextTimer = pTimerListHead->pNextTimer;
  150. if (pTimer->pNextTimer != NULL_PIPSEC_TIMER) {
  151. pTimer->pNextTimer->pPrevTimer = pTimer;
  152. }
  153. pTimerListHead->pNextTimer = pTimer;
  154. //
  155. // Start off the system tick timer if necessary.
  156. //
  157. pTimerList->TimerCount++;
  158. if (pTimerList->TimerCount == 1) {
  159. IPSEC_DEBUG(LL_A, DBF_TIMER,
  160. ("StartTimer: Starting system timer %p, class %d",
  161. &(pTimerList->NdisTimer), i));
  162. IPSEC_START_SYSTEM_TIMER(&(pTimerList->NdisTimer), pTimerList->TimerPeriod);
  163. }
  164. RELEASE_LOCK(&g_ipsec.TimerLock, kIrql);
  165. //
  166. // We're done
  167. //
  168. IPSEC_DEBUG(LL_A, DBF_TIMER,
  169. ("Started timer %p, Secs %d, Index %d, Head %p",
  170. pTimer,
  171. SecondsToGo,
  172. Index,
  173. pTimerListHead));
  174. IPSEC_INCREMENT(g_ipsec.NumTimers);
  175. return;
  176. }
  177. BOOLEAN
  178. IPSecStopTimer(
  179. IN PIPSEC_TIMER pTimer
  180. )
  181. /*++
  182. Routine Description:
  183. Stop an IPSEC timer, if it is running. We remove this timer from
  184. the active timer list and mark it so that we know it's not running.
  185. NOTE: the caller is assumed to either hold a lock to the structure
  186. that contains the timer, or ensure that it is safe to access the
  187. timer structure.
  188. SIDE EFFECT: If we happen to stop the last timer (of this "duration") on
  189. the Interface, we also stop the appropriate Tick function.
  190. Arguments:
  191. pTimer - Pointer to IPSEC Timer structure
  192. Return Value:
  193. TRUE if the timer was running, FALSE otherwise.
  194. --*/
  195. {
  196. PIPSEC_TIMER_LIST pTimerList; // Timer List to which this timer belongs
  197. BOOLEAN WasRunning;
  198. KIRQL kIrql;
  199. IPSEC_DEBUG(LL_A, DBF_TIMER,
  200. ("Stopping Timer %p, List %p, Prev %p, Next %p",
  201. pTimer,
  202. pTimer->pTimerList,
  203. pTimer->pPrevTimer,
  204. pTimer->pNextTimer));
  205. ACQUIRE_LOCK(&g_ipsec.TimerLock, &kIrql);
  206. if (IPSEC_IS_TIMER_ACTIVE(pTimer)) {
  207. WasRunning = TRUE;
  208. //
  209. // Unlink timer from the list
  210. //
  211. ASSERT(pTimer->pPrevTimer); // the list head always exists
  212. if (pTimer->pPrevTimer) {
  213. pTimer->pPrevTimer->pNextTimer = pTimer->pNextTimer;
  214. }
  215. if (pTimer->pNextTimer) {
  216. pTimer->pNextTimer->pPrevTimer = pTimer->pPrevTimer;
  217. }
  218. pTimer->pNextTimer = pTimer->pPrevTimer = NULL_PIPSEC_TIMER;
  219. //
  220. // Update timer count on Interface, for this class of timers
  221. //
  222. pTimerList = pTimer->pTimerList;
  223. pTimerList->TimerCount--;
  224. //
  225. // If all timers of this class are gone, stop the system tick timer
  226. // for this class
  227. //
  228. if (pTimerList->TimerCount == 0) {
  229. IPSEC_DEBUG(LL_A, DBF_TIMER, ("Stopping system timer %p, List %p",
  230. &(pTimerList->NdisTimer),
  231. pTimerList));
  232. pTimerList->CurrentTick = 0;
  233. IPSEC_STOP_SYSTEM_TIMER(&(pTimerList->NdisTimer));
  234. }
  235. //
  236. // Mark stopped timer as not active
  237. //
  238. pTimer->pTimerList = (PIPSEC_TIMER_LIST)NULL;
  239. IPSEC_DECREMENT(g_ipsec.NumTimers);
  240. } else {
  241. WasRunning = FALSE;
  242. }
  243. RELEASE_LOCK(&g_ipsec.TimerLock, kIrql);
  244. return WasRunning;
  245. }
  246. VOID
  247. IPSecTickHandler(
  248. IN PVOID SystemSpecific1,
  249. IN PVOID Context,
  250. IN PVOID SystemSpecific2,
  251. IN PVOID SystemSpecific3
  252. )
  253. /*++
  254. Routine Description:
  255. This is the handler we register with the system for processing each
  256. Timer List. This is called every "tick" seconds, where "tick" is
  257. determined by the granularity of the timer type.
  258. Arguments:
  259. Context - Actually a pointer to a Timer List structure
  260. SystemSpecific[1-3] - Not used
  261. Return Value:
  262. None
  263. --*/
  264. {
  265. PIPSEC_TIMER_LIST pTimerList;
  266. PIPSEC_TIMER pExpiredTimer; // Start of list of expired timers
  267. PIPSEC_TIMER pNextTimer; // for walking above list
  268. PIPSEC_TIMER pTimer; // temp, for walking timer list
  269. PIPSEC_TIMER pPrevExpiredTimer; // for creating expired timer list
  270. ULONG Index; // into the timer wheel
  271. ULONG NewIndex; // for refreshed timers
  272. KIRQL kIrql;
  273. pTimerList = (PIPSEC_TIMER_LIST)Context;
  274. IPSEC_DEBUG(LL_A, DBF_TIMER, ("Tick: List %p, Count %d",
  275. pTimerList, pTimerList->TimerCount));
  276. pExpiredTimer = NULL_PIPSEC_TIMER;
  277. ACQUIRE_LOCK(&g_ipsec.TimerLock, &kIrql);
  278. if (TRUE) {
  279. //
  280. // Pick up the list of timers scheduled to have expired at the
  281. // current tick. Some of these might have been refreshed.
  282. //
  283. Index = pTimerList->CurrentTick;
  284. pExpiredTimer = (pTimerList->pTimers[Index]).pNextTimer;
  285. (pTimerList->pTimers[Index]).pNextTimer = NULL_PIPSEC_TIMER;
  286. //
  287. // Go through the list of timers scheduled to expire at this tick.
  288. // Prepare a list of expired timers, using the pNextExpiredTimer
  289. // link to chain them together.
  290. //
  291. // Some timers may have been refreshed, in which case we reinsert
  292. // them in the active timer list.
  293. //
  294. pPrevExpiredTimer = NULL_PIPSEC_TIMER;
  295. for (pTimer = pExpiredTimer;
  296. pTimer != NULL_PIPSEC_TIMER;
  297. pTimer = pNextTimer) {
  298. //
  299. // Save a pointer to the next timer, for the next iteration.
  300. //
  301. pNextTimer = pTimer->pNextTimer;
  302. IPSEC_DEBUG(LL_A, DBF_TIMER,
  303. ("Tick Handler: looking at timer %p, next %p",
  304. pTimer, pNextTimer));
  305. //
  306. // Find out when this timer should actually expire.
  307. //
  308. NewIndex = pTimer->LastRefreshTime + pTimer->Duration;
  309. if (NewIndex >= pTimerList->TimerListSize) {
  310. NewIndex -= pTimerList->TimerListSize;
  311. }
  312. //
  313. // Check if we are currently at the point of expiry.
  314. //
  315. if (NewIndex != Index) {
  316. //
  317. // This timer still has some way to go, so put it back.
  318. //
  319. IPSEC_DEBUG(LL_A, DBF_TIMER,
  320. ("Tick: Reinserting Timer %p: Hnd %p, Durn %d, Ind %d, NewInd %d",
  321. pTimer, pTimer->TimeoutHandler, pTimer->Duration, Index, NewIndex));
  322. //
  323. // Remove it from the expired timer list. Note that we only
  324. // need to update the forward (pNextExpiredTimer) links.
  325. //
  326. if (pPrevExpiredTimer == NULL_PIPSEC_TIMER) {
  327. pExpiredTimer = pNextTimer;
  328. } else {
  329. pPrevExpiredTimer->pNextExpiredTimer = pNextTimer;
  330. }
  331. //
  332. // And insert it back into the running timer list.
  333. //
  334. pTimer->pNextTimer = (pTimerList->pTimers[NewIndex]).pNextTimer;
  335. if (pTimer->pNextTimer != NULL_PIPSEC_TIMER) {
  336. pTimer->pNextTimer->pPrevTimer = pTimer;
  337. }
  338. pTimer->pPrevTimer = &(pTimerList->pTimers[NewIndex]);
  339. (pTimerList->pTimers[NewIndex]).pNextTimer = pTimer;
  340. } else {
  341. //
  342. // This one has expired. Keep it in the expired timer list.
  343. //
  344. pTimer->pNextExpiredTimer = pNextTimer;
  345. if (pPrevExpiredTimer == NULL_PIPSEC_TIMER) {
  346. pExpiredTimer = pTimer;
  347. }
  348. pPrevExpiredTimer = pTimer;
  349. //
  350. // Mark it as inactive.
  351. //
  352. ASSERT(pTimer->pTimerList == pTimerList);
  353. pTimer->pTimerList = (PIPSEC_TIMER_LIST)NULL;
  354. //
  355. // Update the active timer count.
  356. //
  357. pTimerList->TimerCount--;
  358. }
  359. }
  360. //
  361. // Update current tick index in readiness for the next tick.
  362. //
  363. if (++Index == pTimerList->TimerListSize) {
  364. pTimerList->CurrentTick = 0;
  365. } else {
  366. pTimerList->CurrentTick = Index;
  367. }
  368. if (pTimerList->TimerCount > 0) {
  369. //
  370. // Re-arm the tick handler
  371. //
  372. IPSEC_DEBUG(LL_A, DBF_TIMER, ("Tick[%d]: Starting system timer %p",
  373. pTimerList->CurrentTick, &(pTimerList->NdisTimer)));
  374. IPSEC_START_SYSTEM_TIMER(&(pTimerList->NdisTimer), pTimerList->TimerPeriod);
  375. } else {
  376. pTimerList->CurrentTick = 0;
  377. }
  378. }
  379. RELEASE_LOCK(&g_ipsec.TimerLock, kIrql);
  380. //
  381. // Now pExpiredTimer is a list of expired timers.
  382. // Walk through the list and call the timeout handlers
  383. // for each timer.
  384. //
  385. while (pExpiredTimer != NULL_PIPSEC_TIMER) {
  386. pNextTimer = pExpiredTimer->pNextExpiredTimer;
  387. IPSEC_DEBUG(LL_A, DBF_TIMER,
  388. ("Expired timer %p: handler %p, next %p",
  389. pExpiredTimer, pExpiredTimer->TimeoutHandler, pNextTimer));
  390. (*(pExpiredTimer->TimeoutHandler))( pExpiredTimer,
  391. pExpiredTimer->Context);
  392. IPSEC_DECREMENT(g_ipsec.NumTimers);
  393. pExpiredTimer = pNextTimer;
  394. }
  395. }