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.

756 lines
19 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. llctimr.c
  5. Abstract:
  6. This module contains code that implements a lightweight timer system
  7. for the data link driver.
  8. This module gets control once in 40 ms when a DPC timer expires.
  9. The routine scans the device context's link database, looking for timers
  10. that have expired, and for those that have expired, their expiration
  11. routines are executed.
  12. This is how timers work in DLC:
  13. Each adapter has a singly-linked list of timer ticks (terminated by NULL).
  14. A tick just specifies work to be done at a certain time in the future.
  15. Ticks are ordered by increasing time (multiples of 40 mSec). The work
  16. list that has to be performed when a tick comes due is described by a
  17. doubly-linked list of timers (LLC_TIMER) that the tick structure points
  18. at through the pFront field. For each timer added to a tick's list, the
  19. tick reference count is incremented; it is decremented when a timer is
  20. removed. When the reference count is decremented to zero, the timer
  21. tick is unlinked and deallocated
  22. Every 40 mSec a kernel timer fires and executes our DPC routine
  23. (ScanTimersDpc). This grabs the requisite spinlocks and searches through
  24. all timer ticks on all adapter context structures looking for work to
  25. do
  26. Pictorially:
  27. +---------+ --> other adapter contexts
  28. +--------| Adapter |
  29. | +---------+
  30. |
  31. +-> +------+---> +------+---> 0 (end of singly-linked list)
  32. | Tick | | Tick |
  33. | | | |
  34. +------+ +------+
  35. | ^
  36. | +------------+-------------+
  37. v | | |
  38. +--> +-------+---> +-------+---> +-------+-----+
  39. | +--| Timer | <---| Timer | <---| Timer | <-+ |
  40. | | +-------+ +-------+ +-------+ | |
  41. | | | |
  42. | +------------------------------------------+ |
  43. +----------------------------------------------+
  44. The procedures in this module can be called only when SendSpinLock is set.
  45. Contents:
  46. ScanTimersDpc
  47. LlcInitializeTimerSystem
  48. LlcTerminateTimerSystem
  49. TerminateTimer
  50. InitializeLinkTimers
  51. InitializeTimer
  52. StartTimer
  53. StopTimer
  54. Author:
  55. Antti Saarenheimo (o-anttis) 30-MAY-1991
  56. Environment:
  57. Kernel mode
  58. Revision History:
  59. 28-Apr-1994 rfirth
  60. * Changed to use single driver-level spinlock
  61. * Added useful picture & description above to aid any other poor saps -
  62. er - programmers - who get tricked into - er - who are lucky enough
  63. to work on DLC
  64. --*/
  65. #include <llc.h>
  66. //
  67. // DLC timer tick is 40 ms !!!
  68. //
  69. #define TIMER_DELTA 400000L
  70. //
  71. // global data
  72. //
  73. ULONG AbsoluteTime = 0;
  74. BOOLEAN DlcIsTerminating = FALSE;
  75. BOOLEAN DlcTerminated = FALSE;
  76. //
  77. // private data
  78. //
  79. static LARGE_INTEGER DueTime = { (ULONG) -TIMER_DELTA, (ULONG) -1 };
  80. static KTIMER SystemTimer;
  81. static KDPC TimerSystemDpc;
  82. VOID
  83. ScanTimersDpc(
  84. IN PKDPC Dpc,
  85. IN PVOID DeferredContext,
  86. IN PVOID SystemArgument1,
  87. IN PVOID SystemArgument2
  88. )
  89. /*++
  90. Routine Description:
  91. This routine is called at DISPATCH_LEVEL by the system at regular
  92. intervals to determine if any link-level timers have expired, and
  93. if they have, to execute their expiration routines.
  94. Arguments:
  95. Dpc - Ignored
  96. DeferredContext - Ignored
  97. SystemArgument1 - Ignored
  98. SystemArgument2 - Ignored
  99. Return Value:
  100. None.
  101. --*/
  102. {
  103. PLLC_TIMER pTimer;
  104. PADAPTER_CONTEXT pAdapterContext;
  105. PLLC_TIMER pNextTimer;
  106. PTIMER_TICK pTick;
  107. PTIMER_TICK pNextTick;
  108. BOOLEAN boolRunBackgroundProcess;
  109. KIRQL irql;
  110. UNREFERENCED_PARAMETER(DeferredContext);
  111. UNREFERENCED_PARAMETER(Dpc);
  112. UNREFERENCED_PARAMETER(SystemArgument1);
  113. UNREFERENCED_PARAMETER(SystemArgument2);
  114. ASSUME_IRQL(DISPATCH_LEVEL);
  115. AbsoluteTime++;
  116. //
  117. // The global spinlock keeps the adapters alive over this
  118. //
  119. ACQUIRE_DRIVER_LOCK();
  120. ACQUIRE_LLC_LOCK(irql);
  121. //
  122. // scan timer queues for all adapters
  123. //
  124. for (pAdapterContext = pAdapters; pAdapterContext; pAdapterContext = pAdapterContext->pNext) {
  125. boolRunBackgroundProcess = FALSE;
  126. ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
  127. //
  128. // The timer ticks are protected by a reference counter
  129. //
  130. for (pTick = pAdapterContext->pTimerTicks; pTick; pTick = pNextTick) {
  131. if (pTick->pFront) {
  132. //
  133. // This keeps the tick alive, we cannot use spin lock,
  134. // because the timers are called and deleted within
  135. // SendSpinLock. (=> deadlock)
  136. //
  137. pTick->ReferenceCount++;
  138. //
  139. // Send spin lock prevents anybody to remove a timer
  140. // when we are processing it.
  141. //
  142. for (pTimer = pTick->pFront;
  143. pTimer && pTimer->ExpirationTime <= AbsoluteTime;
  144. pTimer = pNextTimer) {
  145. if ( (pNextTimer = pTimer->pNext) == pTick->pFront) {
  146. pNextTimer = NULL;
  147. }
  148. //
  149. // DLC driver needs a timer tick every 0.5 second to
  150. // implement timer services defined by the API
  151. //
  152. if (pTick->Input == LLC_TIMER_TICK_EVENT) {
  153. RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
  154. ((PBINDING_CONTEXT)pTimer->hContext)->pfEventIndication(
  155. ((PBINDING_CONTEXT)pTimer->hContext)->hClientContext,
  156. NULL,
  157. LLC_TIMER_TICK_EVENT,
  158. NULL,
  159. 0
  160. );
  161. ACQUIRE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
  162. StartTimer(pTimer);
  163. } else {
  164. StopTimer(pTimer);
  165. RunStateMachineCommand(
  166. pTimer->hContext,
  167. pTick->Input
  168. );
  169. boolRunBackgroundProcess = TRUE;
  170. }
  171. }
  172. pNextTick = pTick->pNext;
  173. //
  174. // Delete the timer tick, if there are no references to it.
  175. //
  176. if ((--pTick->ReferenceCount) == 0) {
  177. //
  178. // The timers are in a single entry list!
  179. //
  180. RemoveFromLinkList((PVOID*)&pAdapterContext->pTimerTicks, pTick);
  181. FREE_MEMORY_ADAPTER(pTick);
  182. }
  183. } else {
  184. pNextTick = pTick->pNext;
  185. }
  186. }
  187. if (boolRunBackgroundProcess) {
  188. BackgroundProcessAndUnlock(pAdapterContext);
  189. } else {
  190. RELEASE_SPIN_LOCK(&pAdapterContext->SendSpinLock);
  191. }
  192. }
  193. RELEASE_LLC_LOCK(irql);
  194. RELEASE_DRIVER_LOCK();
  195. //
  196. // Start up the timer again. Note that because we start the timer
  197. // after doing work (above), the timer values will slip somewhat,
  198. // depending on the load on the protocol. This is entirely acceptable
  199. // and will prevent us from using the timer DPC in two different
  200. // threads of execution.
  201. //
  202. if (!DlcIsTerminating) {
  203. ASSUME_IRQL(ANY_IRQL);
  204. KeSetTimer(&SystemTimer, DueTime, &TimerSystemDpc);
  205. } else {
  206. DlcTerminated = TRUE;
  207. }
  208. }
  209. VOID
  210. LlcInitializeTimerSystem(
  211. VOID
  212. )
  213. /*++
  214. Routine Description:
  215. This routine initializes the lightweight timer system for the
  216. data link driver.
  217. Arguments:
  218. None.
  219. Return Value:
  220. None.
  221. --*/
  222. {
  223. ASSUME_IRQL(PASSIVE_LEVEL);
  224. KeInitializeDpc(&TimerSystemDpc, ScanTimersDpc, NULL);
  225. KeInitializeTimer(&SystemTimer);
  226. KeSetTimer(&SystemTimer, DueTime, &TimerSystemDpc);
  227. }
  228. VOID
  229. LlcTerminateTimerSystem(
  230. VOID
  231. )
  232. /*++
  233. Routine Description:
  234. This routine terminates the timer system of the data link driver.
  235. Arguments:
  236. None.
  237. Return Value:
  238. None.
  239. --*/
  240. {
  241. ASSUME_IRQL(PASSIVE_LEVEL);
  242. DlcIsTerminating = TRUE;
  243. //
  244. // if KeCancelTimer returns FALSE then the timer was not set. Assume the DPC
  245. // is either waiting to be scheduled or is already in progress
  246. //
  247. if (!KeCancelTimer(&SystemTimer)) {
  248. //
  249. // if timer is not set, wait for DPC to complete
  250. //
  251. while (!DlcTerminated) {
  252. //
  253. // wait 40 milliseconds - period of DLC's tick
  254. //
  255. LlcSleep(40000);
  256. }
  257. }
  258. }
  259. BOOLEAN
  260. TerminateTimer(
  261. IN PADAPTER_CONTEXT pAdapterContext,
  262. IN PLLC_TIMER pTimer
  263. )
  264. /*++
  265. Routine Description:
  266. Terminate a timer tick by stopping pTimer (remove it from the tick's active
  267. timer list). If pTimer was the last timer on the tick's list then unlink and
  268. deallocate the timer tick.
  269. This routine assumes that if a timer (LLC_TIMER) has a non-NULL pointer to
  270. a tick (TIMER_TICK) then the timer tick owns the timer (i.e. the timer is
  271. started) and this ownership is reflected in the reference count. Even if a
  272. timer is stopped, if its pointer to the timer tick 'object' is valid then
  273. the timer tick still owns the timer
  274. Arguments:
  275. pAdapterContext - adapter context which owns ticks/timers
  276. pTimer - timer tick object of a link station
  277. Return Value:
  278. None
  279. --*/
  280. {
  281. BOOLEAN timerActive;
  282. PTIMER_TICK pTick;
  283. ASSUME_IRQL(DISPATCH_LEVEL);
  284. //
  285. // Timer may not always be initialized, when this is called
  286. // from the cleanup processing of a failed OpenAdapter call.
  287. //
  288. if (!pTimer->pTimerTick) {
  289. return FALSE;
  290. }
  291. pTick = pTimer->pTimerTick;
  292. timerActive = StopTimer(pTimer);
  293. //
  294. // if that was the last timer on the list for this tick then remove the
  295. // tick from the list and deallocate it
  296. //
  297. if (!--pTick->ReferenceCount) {
  298. RemoveFromLinkList((PVOID*)&pAdapterContext->pTimerTicks, pTick);
  299. FREE_MEMORY_ADAPTER(pTick);
  300. }
  301. return timerActive;
  302. }
  303. DLC_STATUS
  304. InitializeLinkTimers(
  305. IN OUT PDATA_LINK pLink
  306. )
  307. /*++
  308. Routine Description:
  309. This routine initializes a timer tick objects of a link station.
  310. Arguments:
  311. pAdapterContext - the device context
  312. pLink - the link context
  313. Return Value:
  314. DLC_STATUS
  315. Success - STATUS_SUCCESS
  316. Failure - DLC_STATUS_NO_MEMORY
  317. out of system memory
  318. --*/
  319. {
  320. DLC_STATUS LlcStatus;
  321. PADAPTER_CONTEXT pAdapterContext = pLink->Gen.pAdapterContext;
  322. ASSUME_IRQL(DISPATCH_LEVEL);
  323. LlcStatus = InitializeTimer(pAdapterContext,
  324. &pLink->T1,
  325. pLink->TimerT1,
  326. pAdapterContext->ConfigInfo.TimerTicks.T1TickOne,
  327. pAdapterContext->ConfigInfo.TimerTicks.T1TickTwo,
  328. T1_Expired,
  329. pLink,
  330. pLink->AverageResponseTime,
  331. FALSE
  332. );
  333. if (LlcStatus != STATUS_SUCCESS) {
  334. return LlcStatus;
  335. }
  336. LlcStatus = InitializeTimer(pAdapterContext,
  337. &pLink->T2,
  338. pLink->TimerT2,
  339. pAdapterContext->ConfigInfo.TimerTicks.T2TickOne,
  340. pAdapterContext->ConfigInfo.TimerTicks.T2TickTwo,
  341. T2_Expired,
  342. pLink,
  343. 0, // T2 is not based on the response time
  344. FALSE
  345. );
  346. if (LlcStatus != STATUS_SUCCESS) {
  347. return LlcStatus;
  348. }
  349. LlcStatus = InitializeTimer(pAdapterContext,
  350. &pLink->Ti,
  351. pLink->TimerTi,
  352. pAdapterContext->ConfigInfo.TimerTicks.TiTickOne,
  353. pAdapterContext->ConfigInfo.TimerTicks.TiTickTwo,
  354. Ti_Expired,
  355. pLink,
  356. pLink->AverageResponseTime,
  357. TRUE
  358. );
  359. return LlcStatus;
  360. }
  361. DLC_STATUS
  362. InitializeTimer(
  363. IN PADAPTER_CONTEXT pAdapterContext,
  364. IN OUT PLLC_TIMER pTimer,
  365. IN UCHAR TickCount,
  366. IN UCHAR TickOne,
  367. IN UCHAR TickTwo,
  368. IN UINT Input,
  369. IN PVOID hContextHandle,
  370. IN UINT ResponseDelay,
  371. IN BOOLEAN StartNewTimer
  372. )
  373. /*++
  374. Routine Description:
  375. This routine initializes a timer tick objects of a link station.
  376. Arguments:
  377. pTimer - timer tick object of a link station
  378. TickCount - DLC ticks, see DLC documentation (or code)
  379. TickOne - see DLC documentation
  380. TickTwo - see DLC documentation
  381. Input - the used state machine input, when the timer expires
  382. hContextHandle - context handle when the state machine is called
  383. StartNewTimer - set if the timer must be started when it is initialized
  384. for the first time. Subsequent times, the timer keeps its
  385. old state
  386. ResponseDelay - an optional base value that is added to the timer value
  387. Return Value:
  388. DLC_STATUS
  389. Success - STATUS_SUCCESS
  390. Failure - DLC_STATUS_NO_MEMORY
  391. out of system memory
  392. --*/
  393. {
  394. UINT DeltaTime;
  395. PTIMER_TICK pTick;
  396. ASSUME_IRQL(DISPATCH_LEVEL);
  397. //
  398. // All times are multiples of 40 milliseconds
  399. // (I am not sure how portable is this design)
  400. // See LAN Manager Network Device Driver Guide
  401. // ('Remoteboot protocol') for further details
  402. // about TickOne and TickTwo
  403. // We have already checked, that the
  404. // timer tick count is less than 11.
  405. //
  406. DeltaTime = (TickCount > 5 ? (UINT)(TickCount - 5) * (UINT)TickTwo
  407. : (UINT)TickCount * (UINT)TickOne);
  408. //
  409. // We discard the low bits in the reponse delay.
  410. //
  411. DeltaTime += (ResponseDelay & 0xfff0);
  412. //
  413. // Return immediately, if the old value is the
  414. // same as the new one (T2 link station is reinitialized
  415. // unnecessary, when the T1 and Ti timers are retuned
  416. // for changed response time.
  417. //
  418. if (pTimer->pTimerTick && (pTimer->pTimerTick->DeltaTime == DeltaTime)) {
  419. return STATUS_SUCCESS;
  420. }
  421. //
  422. // Try to find a timer tick object having the same delta time and input
  423. //
  424. for (pTick = pAdapterContext->pTimerTicks; pTick; pTick = pTick->pNext) {
  425. if ((pTick->DeltaTime == DeltaTime) && (pTick->Input == (USHORT)Input)) {
  426. break;
  427. }
  428. }
  429. if (!pTick) {
  430. pTick = ALLOCATE_ZEROMEMORY_ADAPTER(sizeof(TIMER_TICK));
  431. if (!pTick) {
  432. return DLC_STATUS_NO_MEMORY;
  433. }
  434. pTick->DeltaTime = DeltaTime;
  435. pTick->Input = (USHORT)Input;
  436. pTick->pNext = pAdapterContext->pTimerTicks;
  437. pAdapterContext->pTimerTicks = pTick;
  438. }
  439. pTick->ReferenceCount++;
  440. //
  441. // We must delete the previous timer reference
  442. // when we know if the memory allocation operation
  443. // was successfull or not. Otherwise the setting of
  444. // the link parameters might delete old timer tick,
  445. // but it would not be able to allocate the new one.
  446. // The link must be protected, when this routine is called.
  447. //
  448. if (pTimer->pTimerTick) {
  449. StartNewTimer = TerminateTimer(pAdapterContext, pTimer);
  450. }
  451. pTimer->pTimerTick = pTick;
  452. pTimer->hContext = hContextHandle;
  453. if (StartNewTimer) {
  454. StartTimer(pTimer);
  455. }
  456. return STATUS_SUCCESS;
  457. }
  458. VOID
  459. StartTimer(
  460. IN OUT PLLC_TIMER pTimer
  461. )
  462. /*++
  463. Routine Description:
  464. This starts the given timer within spin locks
  465. Arguments:
  466. pTimer - timer tick object of a link station
  467. Return Value:
  468. None.
  469. --*/
  470. {
  471. PLLC_TIMER pFront;
  472. PTIMER_TICK pTimerTick = pTimer->pTimerTick;
  473. ASSUME_IRQL(DISPATCH_LEVEL);
  474. //
  475. // We always reset the pNext pointer, when a item is
  476. // removed from a link list => the timer element cannot be
  477. // in the link list of a timer tick object if its next pointer is null
  478. //
  479. if (pTimer->pNext) {
  480. //
  481. // We don't need to change the timer's position, if the new timer
  482. // would be the same as the old time.
  483. //
  484. if (pTimer->ExpirationTime != AbsoluteTime + pTimerTick->DeltaTime) {
  485. //
  486. // The timer has already been started, move it to the top of
  487. // the link list.
  488. //
  489. if (pTimer != (pFront = pTimerTick->pFront)) {
  490. pTimer->pPrev->pNext = pTimer->pNext;
  491. pTimer->pNext->pPrev = pTimer->pPrev;
  492. pTimer->pNext = pFront;
  493. pTimer->pPrev = pFront->pPrev;
  494. pFront->pPrev->pNext = pTimer;
  495. pFront->pPrev = pTimer;
  496. }
  497. }
  498. } else {
  499. if (!(pFront = pTimerTick->pFront)) {
  500. pTimerTick->pFront = pTimer->pNext = pTimer->pPrev = pTimer;
  501. } else {
  502. pTimer->pNext = pFront;
  503. pTimer->pPrev = pFront->pPrev;
  504. pFront->pPrev->pNext = pTimer;
  505. pFront->pPrev = pTimer;
  506. }
  507. }
  508. pTimer->ExpirationTime = AbsoluteTime + pTimerTick->DeltaTime;
  509. }
  510. BOOLEAN
  511. StopTimer(
  512. IN PLLC_TIMER pTimer
  513. )
  514. /*++
  515. Routine Description:
  516. This stops the given timer within spin locks
  517. Arguments:
  518. pTimer - timer tick object of a link station
  519. Return Value:
  520. BOOLEAN
  521. TRUE - timer was running
  522. FALSE - timer was not running
  523. --*/
  524. {
  525. ASSUME_IRQL(DISPATCH_LEVEL);
  526. if (pTimer->pNext) {
  527. PTIMER_TICK pTimerTick = pTimer->pTimerTick;
  528. //
  529. // if the timer points to itself then its the only thing on the list:
  530. // zap the link in the timer tick structure (no more timers for this
  531. // tick) and zap the next field in the timer structure to indicate
  532. // the timer has been removed from the tick list. If the timer points
  533. // to another timer, then remove this timer from the doubly-linked list
  534. // of timers
  535. //
  536. if (pTimer != pTimer->pNext) {
  537. if (pTimer == pTimerTick->pFront) {
  538. pTimerTick->pFront = pTimer->pNext;
  539. }
  540. pTimer->pPrev->pNext = pTimer->pNext;
  541. pTimer->pNext->pPrev = pTimer->pPrev;
  542. pTimer->pNext = NULL;
  543. } else {
  544. pTimerTick->pFront = pTimer->pNext = NULL;
  545. }
  546. return TRUE;
  547. } else {
  548. //
  549. // this timer was not on a timer tick list
  550. //
  551. return FALSE;
  552. }
  553. }