Source code of Windows XP (NT5)
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.

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