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.

2770 lines
73 KiB

  1. /*++
  2. Copyright (c) 1989, 1990, 1991 Microsoft Corporation
  3. Module Name:
  4. timer.c
  5. Abstract:
  6. This module contains code that implements the lightweight timer system
  7. for the NBF protocol provider. This is not a general-purpose timer system;
  8. rather, it is specific to servicing LLC (802.2) links with three timers
  9. each.
  10. Services are provided in macro form (see NBFPROCS.H) to start and stop
  11. timers. This module contains the code that gets control when the timer
  12. in the device context expires as a result of calling kernel services.
  13. The routine scans the device context's link database, looking for timers
  14. that have expired, and for those that have expired, their expiration
  15. routines are executed.
  16. Author:
  17. David Beaver (dbeaver) 1-July-1991
  18. Environment:
  19. Kernel mode
  20. Revision History:
  21. --*/
  22. #include "precomp.h"
  23. #pragma hdrstop
  24. ULONG StartTimer = 0;
  25. ULONG StartTimerSet = 0;
  26. ULONG StartTimerT1 = 0;
  27. ULONG StartTimerT2 = 0;
  28. ULONG StartTimerDelayedAck = 0;
  29. ULONG StartTimerLinkDeferredAdd = 0;
  30. ULONG StartTimerLinkDeferredDelete = 0;
  31. #if DBG
  32. extern ULONG NbfDebugPiggybackAcks;
  33. ULONG NbfDebugShortTimer = 0;
  34. #endif
  35. #if DBG
  36. //
  37. // These are temp, to track how the timers are working
  38. //
  39. ULONG TimerInsertsAtEnd = 0;
  40. ULONG TimerInsertsEmpty = 0;
  41. ULONG TimerInsertsInMiddle = 0;
  42. #endif
  43. //
  44. // These are constants calculated by InitializeTimerSystem
  45. // to be the indicated amound divided by the tick increment.
  46. //
  47. ULONG NbfTickIncrement = 0;
  48. ULONG NbfTwentyMillisecondsTicks = 0;
  49. ULONG NbfShortTimerDeltaTicks = 0;
  50. ULONG NbfMaximumIntervalTicks = 0; // usually 60 seconds in ticks
  51. LARGE_INTEGER DueTimeDelta = { (ULONG)(-SHORT_TIMER_DELTA), -1 };
  52. VOID
  53. ExpireT2Timer(
  54. PTP_LINK Link
  55. );
  56. VOID
  57. StopStalledConnections(
  58. IN PDEVICE_CONTEXT DeviceContext
  59. );
  60. ULONG
  61. GetTimerInterval(
  62. IN PTP_LINK Link
  63. )
  64. /*++
  65. Routine Description:
  66. GetTimerInterval returns the difference in time between the
  67. current time and Link->CurrentTimerStart (in ticks).
  68. We limit the interval to 60 seconds. A value of 0 may
  69. be returned which should be interpreted as 1/2.
  70. NOTE: This routine should be called with the link spinlock
  71. held.
  72. Arguments:
  73. Link - Pointer to a transport link object.
  74. Return Value:
  75. The interval.
  76. --*/
  77. {
  78. LARGE_INTEGER CurrentTick;
  79. LARGE_INTEGER Interval;
  80. //
  81. // Determine the current tick; the start tick has been saved
  82. // in Link->CurrentTimerStart.
  83. //
  84. KeQueryTickCount (&CurrentTick);
  85. //
  86. // Determine the difference between now and then.
  87. //
  88. Interval.QuadPart = CurrentTick.QuadPart -
  89. (Link->CurrentTimerStart).QuadPart;
  90. //
  91. // If the gap is too big, return 1 minute.
  92. //
  93. if (Interval.HighPart != 0 || (Interval.LowPart > NbfMaximumIntervalTicks)) {
  94. return NbfMaximumIntervalTicks;
  95. }
  96. return Interval.LowPart;
  97. } /* GetTimerInterval */
  98. VOID
  99. BackoffCurrentT1Timeout(
  100. IN PTP_LINK Link
  101. )
  102. /*++
  103. Routine Description:
  104. This routine is called if T1 expires and we are about to
  105. retransmit a poll frame. It backs off CurrentT1Timeout,
  106. up to a limit of 10 seconds.
  107. NOTE: This routine should be called with the link spinlock
  108. held.
  109. Arguments:
  110. Link - Pointer to a transport link object.
  111. Return Value:
  112. None.
  113. --*/
  114. {
  115. //
  116. // We must have previously sent a poll frame if we are
  117. // calling this.
  118. //
  119. // do we need spinlock guarding for MP ?
  120. //
  121. if (!Link->CurrentPollOutstanding) {
  122. return;
  123. }
  124. ++Link->CurrentPollRetransmits;
  125. //
  126. // T1 backs off 1.5 times each time.
  127. //
  128. Link->CurrentT1Timeout += (Link->CurrentT1Timeout >> 1);
  129. //
  130. // Limit T1 to 10 seconds.
  131. //
  132. if (Link->CurrentT1Timeout > ((10 * SECONDS) / SHORT_TIMER_DELTA)) {
  133. Link->CurrentT1Timeout = (10 * SECONDS) / SHORT_TIMER_DELTA;
  134. }
  135. } /* BackoffCurrentT1Timeout */
  136. VOID
  137. UpdateBaseT1Timeout(
  138. IN PTP_LINK Link
  139. )
  140. /*++
  141. Routine Description:
  142. This routine is called when a response to a poll frame is
  143. received. StartT1 will have been called when the frame is
  144. sent. The routine updates the link's T1 timeout as well
  145. as delay and throughput.
  146. NOTE: This routine should be called with the link spinlock
  147. held.
  148. Arguments:
  149. Link - Pointer to a transport link object.
  150. Return Value:
  151. None.
  152. --*/
  153. {
  154. ULONG Delay;
  155. ULONG ShiftedTicksDelay;
  156. //
  157. // We must have previously sent a poll frame if we are
  158. // calling this.
  159. //
  160. if (!Link->CurrentPollOutstanding) {
  161. return;
  162. }
  163. Delay = GetTimerInterval (Link);
  164. if (Link->CurrentPollRetransmits == 0) {
  165. //
  166. // Convert the delay into NBF ticks, shifted by
  167. // DLC_TIMER_ACCURACY and also multiplied by 4.
  168. // We want to divide by SHORT_TIMER_DELTA, then
  169. // shift left by DLC_TIMER_ACCURACY+2. We divide
  170. // by NbfShortTimerDeltaTicks because the Delay
  171. // is returned in ticks.
  172. //
  173. // We treat a delay of 0 as 1/2, so we use 1
  174. // shifted left by (DLC_TIMER_ACCURACY+1).
  175. //
  176. if (Delay == 0) {
  177. ShiftedTicksDelay = (1 << (DLC_TIMER_ACCURACY + 1)) /
  178. NbfShortTimerDeltaTicks;
  179. } else {
  180. ShiftedTicksDelay = (Delay << (DLC_TIMER_ACCURACY + 2)) /
  181. NbfShortTimerDeltaTicks;
  182. }
  183. //
  184. // Use the timing information to update BaseT1Timeout,
  185. // if the last frame sent was large enough to matter
  186. // (we use half of the max frame size here). This is
  187. // so we don't shrink the timeout too much after sending
  188. // a short frame. However, we update even for small frames
  189. // if the last time we sent a poll we had to retransmit
  190. // it, since that means T1 is much too small and we should
  191. // increase it as much as we can. We also update for any
  192. // size frame if the new delay is bigger than the current
  193. // value, so we can ramp up quickly if needed.
  194. //
  195. if (ShiftedTicksDelay > Link->BaseT1Timeout) {
  196. //
  197. // If our new delay is more, than we weight it evenly
  198. // with the previous value.
  199. //
  200. Link->BaseT1Timeout = (Link->BaseT1Timeout +
  201. ShiftedTicksDelay) / 2;
  202. } else if (Link->CurrentT1Backoff) {
  203. //
  204. // If we got a retransmit last time, then weight
  205. // the new timer more heavily than usual.
  206. //
  207. Link->BaseT1Timeout = ((Link->BaseT1Timeout * 3) +
  208. ShiftedTicksDelay) / 4;
  209. } else if (Link->CurrentPollSize >= Link->BaseT1RecalcThreshhold) {
  210. //
  211. // Normally, the new timeout is 7/8 the previous value and
  212. // 1/8 the newly observed delay.
  213. //
  214. Link->BaseT1Timeout = ((Link->BaseT1Timeout * 7) +
  215. ShiftedTicksDelay) / 8;
  216. }
  217. //
  218. // Restrict the real timeout to a minimum based on
  219. // the link speed (always >= 400 ms).
  220. //
  221. if (Link->BaseT1Timeout < Link->MinimumBaseT1Timeout) {
  222. Link->BaseT1Timeout = Link->MinimumBaseT1Timeout;
  223. }
  224. //
  225. // Update link delay and throughput also. Remember
  226. // that a delay of 0 should be interpreted as 1/2.
  227. //
  228. UpdateDelayAndThroughput(
  229. Link,
  230. (Delay == 0) ?
  231. (NbfTickIncrement / 2) :
  232. (Delay * NbfTickIncrement));
  233. //
  234. // We had no retransmits last time, so go back to current base.
  235. //
  236. Link->CurrentT1Timeout = Link->BaseT1Timeout >> DLC_TIMER_ACCURACY;
  237. Link->CurrentT1Backoff = FALSE;
  238. } else {
  239. Link->CurrentT1Backoff = TRUE;
  240. if (!(Link->ThroughputAccurate)) {
  241. //
  242. // If we are just starting up, we have to update the
  243. // throughput even on a retransmit, so we get *some*
  244. // value there.
  245. //
  246. UpdateDelayAndThroughput(
  247. Link,
  248. (Delay == 0) ?
  249. (NbfTickIncrement / 2) :
  250. (Delay * NbfTickIncrement));
  251. }
  252. }
  253. } /* UpdateBaseT1Timeout */
  254. VOID
  255. CancelT1Timeout(
  256. IN PTP_LINK Link
  257. )
  258. /*++
  259. Routine Description:
  260. This routine is called when we have not received any
  261. responses to a poll frame and are giving up rather
  262. than retransmitting.
  263. NOTE: This routine should be called with the link spinlock
  264. held.
  265. Arguments:
  266. Link - Pointer to a transport link object.
  267. Return Value:
  268. None.
  269. --*/
  270. {
  271. //
  272. // We must have previously sent a poll frame if we are
  273. // calling this.
  274. //
  275. // do we need spinlock guarding for MP ?
  276. //
  277. if (!Link->CurrentPollOutstanding) {
  278. return;
  279. }
  280. //
  281. // We are stopping a polling condition, so reset T1.
  282. //
  283. Link->CurrentT1Timeout = Link->BaseT1Timeout >> DLC_TIMER_ACCURACY;
  284. Link->CurrentT1Backoff = FALSE;
  285. //
  286. // Again, this isn't safe on MP (or UP, maybe).
  287. //
  288. Link->CurrentPollOutstanding = FALSE;
  289. } /* CancelT1Timeout */
  290. VOID
  291. UpdateDelayAndThroughput(
  292. IN PTP_LINK Link,
  293. IN ULONG TimerInterval
  294. )
  295. /*++
  296. Routine Description:
  297. This routine is called when a response packet used to time
  298. link delay has been received. It is assumed that StartT1
  299. or FakeStartT1 was called when the initial packet was sent.
  300. NOTE: For now, we also calculate throughput based on this.
  301. NOTE: This routine should be called with the link spinlock
  302. held.
  303. Arguments:
  304. Link - Pointer to a transport link object.
  305. TimerInterval - The link delay measured.
  306. Return Value:
  307. None.
  308. --*/
  309. {
  310. ULONG PacketSize;
  311. if (Link->Delay == 0xffffffff) {
  312. //
  313. // If delay is unknown, use this.
  314. //
  315. Link->Delay = TimerInterval;
  316. } else if (Link->CurrentPollSize <= 64) {
  317. //
  318. // Otherwise, for small frames calculate the new
  319. // delay by averaging with the old one.
  320. //
  321. Link->Delay = (Link->Delay + TimerInterval) / 2;
  322. }
  323. //
  324. // Calculate the packet size times the number of time units
  325. // in 10 milliseconds, which will allow us to calculate
  326. // throughput in bytes/10ms (we later multiply by 100
  327. // to obtain the real throughput in bytes/s).
  328. //
  329. // Given the size of MILLISECONDS, this allows packets of up
  330. // to ~20K, so for bigger packets we just assume that (since
  331. // throughput won't be an issue there).
  332. //
  333. if (Link->CurrentPollSize > 20000) {
  334. PacketSize = 20000 * (10 * MILLISECONDS);
  335. } else {
  336. PacketSize = Link->CurrentPollSize * (10*MILLISECONDS);
  337. }
  338. //
  339. // If throughput is not accurate, then we will use this
  340. // packet only to calculate it. To avoid being confused
  341. // by very small packets, assume a minimum size of 64.
  342. //
  343. if ((!Link->ThroughputAccurate) && (PacketSize < (64*(10*MILLISECONDS)))) {
  344. PacketSize = 64 * (10*MILLISECONDS);
  345. }
  346. //
  347. // PacketSize is going to be divided by TimerInterval;
  348. // to prevent a zero throughput, we boost it up if needed.
  349. //
  350. if (PacketSize < TimerInterval) {
  351. PacketSize = TimerInterval;
  352. }
  353. if (Link->CurrentPollSize >= 512) {
  354. //
  355. // Calculate throughput here by removing the established delay
  356. // from the time.
  357. //
  358. if ((Link->Delay + (2*MILLISECONDS)) < TimerInterval) {
  359. //
  360. // If the current delay is less than the new timer
  361. // interval (plus 2 ms), then subtract it off for a
  362. // more accurate throughput calculation.
  363. //
  364. TimerInterval -= Link->Delay;
  365. }
  366. //
  367. // We assume by this point (sending a > 512-byte frame) we
  368. // already have something established as Link->Throughput.
  369. //
  370. if (!(Link->ThroughputAccurate)) {
  371. Link->Throughput.QuadPart =
  372. UInt32x32To64((PacketSize / TimerInterval), 100);
  373. Link->ThroughputAccurate = TRUE;
  374. #if 0
  375. NbfPrint2 ("INT: %ld.%1.1d us\n",
  376. TimerInterval / 10, TimerInterval % 10);
  377. NbfPrint4 ("D: %ld.%1.1d us T: %ld (%d)/s\n",
  378. Link->Delay / 10, Link->Delay % 10,
  379. Link->Throughput.LowPart, Link->CurrentPollSize);
  380. #endif
  381. } else {
  382. LARGE_INTEGER TwiceThroughput;
  383. //
  384. // New throughput is the average of the old throughput, and
  385. // the current packet size divided by the delay just observed.
  386. // First we calculate the sum, then we shift right by one.
  387. //
  388. TwiceThroughput.QuadPart = Link->Throughput.QuadPart +
  389. UInt32x32To64((PacketSize / TimerInterval), 100);
  390. Link->Throughput.QuadPart = TwiceThroughput.QuadPart >> 1;
  391. }
  392. } else if (!(Link->ThroughputAccurate)) {
  393. //
  394. // We don't have accurate throughput, so just get an estimate
  395. // by ignoring the delay on this small frame.
  396. //
  397. Link->Throughput.QuadPart =
  398. UInt32x32To64((PacketSize / TimerInterval), 100);
  399. }
  400. } /* UpdateDelayAndThroughput */
  401. VOID
  402. FakeStartT1(
  403. IN PTP_LINK Link,
  404. IN ULONG PacketSize
  405. )
  406. /*++
  407. Routine Description:
  408. This routine is called before sending a packet that will be used
  409. to time link delay, but where StartT1 will not be started.
  410. It is assumed that FakeUpdateBaseT1Timeout will be called
  411. when the response is received. This is used for timing
  412. frames that have a known immediate response, but are not
  413. poll frames.
  414. NOTE: This routine should be called with the link spinlock
  415. held.
  416. Arguments:
  417. Link - Pointer to a transport link object.
  418. PacketSize - The size of the packet that was just sent.
  419. Return Value:
  420. None.
  421. --*/
  422. {
  423. Link->CurrentPollSize = PacketSize;
  424. KeQueryTickCount(&Link->CurrentTimerStart);
  425. } /* FakeStartT1 */
  426. VOID
  427. FakeUpdateBaseT1Timeout(
  428. IN PTP_LINK Link
  429. )
  430. /*++
  431. Routine Description:
  432. This routine is called when a response to a frame is
  433. received, and we called FakeStartT1 when the initial
  434. frame was sent. This is used for timing frames that have
  435. a known immediate response, but are not poll frames.
  436. NOTE: This routine should be called with the link spinlock
  437. held.
  438. Arguments:
  439. Link - Pointer to a transport link object.
  440. Return Value:
  441. None.
  442. --*/
  443. {
  444. ULONG Delay;
  445. Delay = GetTimerInterval (Link);
  446. //
  447. // Convert the delay into NBF ticks, shifted by
  448. // DLC_TIMER_ACCURACY and also multiplied by 4.
  449. // We want to divide by SHORT_TIMER_DELTA, then
  450. // shift left by DLC_TIMER_ACCURACY+2. We divide
  451. // by NbfShortTimerDeltaTicks because the Delay
  452. // is returned in ticks. We treat a Delay of 0
  453. // as 1/2 and calculate ((1/2) << x) as (1 << (x-1)).
  454. //
  455. // This timeout is treated as the correct value.
  456. //
  457. if (Delay == 0) {
  458. Link->BaseT1Timeout = (1 << (DLC_TIMER_ACCURACY + 1)) /
  459. NbfShortTimerDeltaTicks;
  460. } else {
  461. Link->BaseT1Timeout = (Delay << (DLC_TIMER_ACCURACY + 2)) /
  462. NbfShortTimerDeltaTicks;
  463. }
  464. //
  465. // Restrict the real timeout to a minimum based on
  466. // the link speed (always >= 400 ms).
  467. //
  468. if (Link->BaseT1Timeout < Link->MinimumBaseT1Timeout) {
  469. Link->BaseT1Timeout = Link->MinimumBaseT1Timeout;
  470. }
  471. Link->CurrentT1Timeout = Link->BaseT1Timeout >> DLC_TIMER_ACCURACY;
  472. //
  473. // Update link delay and throughput also.
  474. //
  475. UpdateDelayAndThroughput(
  476. Link,
  477. (Delay == 0) ?
  478. (NbfTickIncrement / 2) :
  479. (Delay * NbfTickIncrement));
  480. } /* FakeUpdateBaseT1Timeout */
  481. VOID
  482. StartT1(
  483. IN PTP_LINK Link,
  484. IN ULONG PacketSize
  485. )
  486. /*++
  487. Routine Description:
  488. This routine starts the T1 timer for the given link. If the link was
  489. already on the list, it is moved to the tail. If not, it is inserted at
  490. tail.
  491. NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL.
  492. Arguments:
  493. Link - pointer to the link of interest.
  494. PollPacketSize - If a poll packet was just sent it is its size;
  495. otherwise this will be 0 (when non-poll I-frames are sent).
  496. Return Value:
  497. None.
  498. --*/
  499. {
  500. PDEVICE_CONTEXT DeviceContext = Link->Provider;
  501. if (PacketSize > 0) {
  502. //
  503. // If we are sending an initial poll frame, then do timing stuff.
  504. //
  505. Link->CurrentPollRetransmits = 0;
  506. Link->CurrentPollSize = PacketSize;
  507. Link->CurrentPollOutstanding = TRUE;
  508. KeQueryTickCount(&Link->CurrentTimerStart);
  509. } else {
  510. Link->CurrentPollOutstanding = FALSE;
  511. }
  512. //
  513. // Insert us in the queue if we aren't in it.
  514. //
  515. Link->T1 = DeviceContext->ShortAbsoluteTime+Link->CurrentT1Timeout;
  516. if (!Link->OnShortList) {
  517. ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
  518. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  519. if (!Link->OnShortList) {
  520. Link->OnShortList = TRUE;
  521. InsertTailList (&DeviceContext->ShortList, &Link->ShortList);
  522. }
  523. if (!DeviceContext->a.i.ShortListActive) {
  524. StartTimerT1++;
  525. NbfStartShortTimer (DeviceContext);
  526. DeviceContext->a.i.ShortListActive = TRUE;
  527. }
  528. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  529. }
  530. }
  531. VOID
  532. StartT2(
  533. IN PTP_LINK Link
  534. )
  535. /*++
  536. Routine Description:
  537. This routine adds the given link to the T2 queue and starts the timer.
  538. If the link is already on the queue, it is moved to the queue end.
  539. NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL.
  540. Arguments:
  541. Link - pointer to the link of interest.
  542. Return Value:
  543. None.
  544. --*/
  545. {
  546. PDEVICE_CONTEXT DeviceContext = Link->Provider;
  547. if (DeviceContext->MacInfo.MediumAsync) {
  548. //
  549. // On an async line, expire it as soon as possible.
  550. //
  551. Link->T2 = DeviceContext->ShortAbsoluteTime;
  552. } else {
  553. Link->T2 = DeviceContext->ShortAbsoluteTime+Link->T2Timeout;
  554. }
  555. if (!Link->OnShortList) {
  556. ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
  557. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  558. if (!Link->OnShortList) {
  559. Link->OnShortList = TRUE;
  560. InsertTailList (&DeviceContext->ShortList, &Link->ShortList);
  561. }
  562. if (!DeviceContext->a.i.ShortListActive) {
  563. StartTimerT2++;
  564. NbfStartShortTimer (DeviceContext);
  565. DeviceContext->a.i.ShortListActive = TRUE;
  566. }
  567. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  568. }
  569. }
  570. VOID
  571. StartTi(
  572. IN PTP_LINK Link
  573. )
  574. /*++
  575. Routine Description:
  576. This routine adds the given link to the Ti queue and starts the timer.
  577. As above, if the link is already on the queue it is moved to the queue end.
  578. NOTE: THIS ROUTINE MUST BE CALLED AT DPC LEVEL.
  579. Arguments:
  580. Link - pointer to the link of interest.
  581. Return Value:
  582. None.
  583. --*/
  584. {
  585. PDEVICE_CONTEXT DeviceContext = Link->Provider;
  586. //
  587. // On an easily disconnected link, with only server connections
  588. // on this link, we set a long Ti timeout, and when it
  589. // expires with no activity we start checkpointing, otherwise
  590. // we assume things are OK.
  591. //
  592. if (DeviceContext->EasilyDisconnected && Link->NumberOfConnectors == 0) {
  593. Link->Ti = DeviceContext->LongAbsoluteTime + (2 * Link->TiTimeout);
  594. Link->TiStartPacketsReceived = Link->PacketsReceived;
  595. } else {
  596. Link->Ti = DeviceContext->LongAbsoluteTime+Link->TiTimeout;
  597. }
  598. if (!Link->OnLongList) {
  599. ASSERT (KeGetCurrentIrql() == DISPATCH_LEVEL);
  600. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  601. if (!Link->OnLongList) {
  602. Link->OnLongList = TRUE;
  603. InsertTailList (&DeviceContext->LongList, &Link->LongList);
  604. }
  605. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  606. }
  607. }
  608. #if DBG
  609. VOID
  610. StopT1(
  611. IN PTP_LINK Link
  612. )
  613. /*++
  614. Routine Description:
  615. This routine
  616. Arguments:
  617. Link - pointer to the link of interest.
  618. Return Value:
  619. None.
  620. --*/
  621. {
  622. //
  623. // Again, this isn't safe on MP (or UP, maybe).
  624. //
  625. Link->CurrentPollOutstanding = FALSE;
  626. Link->T1 = 0;
  627. }
  628. VOID
  629. StopT2(
  630. IN PTP_LINK Link
  631. )
  632. /*++
  633. Routine Description:
  634. This routine
  635. Arguments:
  636. Link - pointer to the link of interest.
  637. Return Value:
  638. None.
  639. --*/
  640. {
  641. Link->ConsecutiveIFrames = 0;
  642. Link->T2 = 0;
  643. }
  644. VOID
  645. StopTi(
  646. IN PTP_LINK Link
  647. )
  648. /*++
  649. Routine Description:
  650. This routine
  651. Arguments:
  652. Link - pointer to the link of interest.
  653. Return Value:
  654. None.
  655. --*/
  656. {
  657. Link->Ti = 0;
  658. }
  659. #endif
  660. VOID
  661. ExpireT1Timer(
  662. PTP_LINK Link
  663. )
  664. /*++
  665. Routine Description:
  666. This routine is called when a link's T1 timer expires. T1 is the
  667. retransmission timer, and is used to remember that a response is
  668. expected to any of the following: (1) a checkpoint, (2) a transmitted
  669. I-frame, (3) a SABME, or (4) a DISC. Cases 3 and 4 are actually
  670. special forms of a checkpoint, since they are sent by this protocol
  671. implementation with the poll bit set, effectively making them a
  672. checkpoint sequence.
  673. Arguments:
  674. Link - Pointer to the TP_LINK object whose T1 timer has expired.
  675. Return Value:
  676. none.
  677. --*/
  678. {
  679. PDLC_I_FRAME DlcHeader;
  680. IF_NBFDBG (NBF_DEBUG_TIMER) {
  681. NbfPrint0 ("ExpireT1Timer: Entered.\n");
  682. }
  683. ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
  684. switch (Link->State) {
  685. case LINK_STATE_ADM:
  686. RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
  687. IF_NBFDBG (NBF_DEBUG_TIMER) {
  688. NbfPrint0 ("ExpireT1Timer: State=ADM, timeout not expected.\n");
  689. }
  690. break;
  691. case LINK_STATE_READY:
  692. //
  693. // We've sent an I-frame and haven't received an acknowlegement
  694. // yet, or we are checkpointing, and must retry the checkpoint.
  695. // Another possibility is that we're rejecting, and he hasn't
  696. // sent anything yet.
  697. //
  698. switch (Link->SendState) {
  699. case SEND_STATE_DOWN:
  700. RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
  701. IF_NBFDBG (NBF_DEBUG_TIMER) {
  702. NbfPrint0 ("ExpireT1Timer: Link READY but SendState=DOWN.\n");
  703. }
  704. break;
  705. case SEND_STATE_READY:
  706. //
  707. // We sent an I-frame and didn't get an acknowlegement.
  708. // Initiate a checkpoint sequence.
  709. //
  710. IF_NBFDBG (NBF_DEBUG_TIMER) {
  711. {PTP_PACKET packet;
  712. PLIST_ENTRY p;
  713. NbfPrint0 ("ExpireT1Timer: Link State=READY, SendState=READY .\n");
  714. NbfDumpLinkInfo (Link);
  715. p=Link->WackQ.Flink;
  716. NbfPrint0 ("ExpireT1Timer: Link WackQ entries:\n");
  717. while (p != &Link->WackQ) {
  718. packet = CONTAINING_RECORD (p, TP_PACKET, Linkage);
  719. DlcHeader = (PDLC_I_FRAME)&(packet->Header[Link->HeaderLength]);
  720. NbfPrint2 (" %08lx %03d\n", p,
  721. (DlcHeader->SendSeq >> 1));
  722. p = p->Flink;
  723. }}
  724. }
  725. Link->SendRetries = (UCHAR)Link->LlcRetries;
  726. Link->SendState = SEND_STATE_CHECKPOINTING;
  727. // Don't BackoffT1Timeout yet.
  728. NbfSendRr (Link, TRUE, TRUE);// send RR-c/p, StartT1, release lock
  729. break;
  730. case SEND_STATE_REJECTING:
  731. IF_NBFDBG (NBF_DEBUG_TIMER) {
  732. NbfPrint0 ("ExpireT1Timer: Link State=READY, SendState=REJECTING.\n");
  733. NbfPrint0 ("so what do we do here? consult the manual...\n");
  734. }
  735. Link->SendState = SEND_STATE_CHECKPOINTING;
  736. // Link->SendRetries = Link->LlcRetries;
  737. // break; // DGB: doing nothing is obviously wrong, we've
  738. // // gotten a T1 expiration during resend. Try
  739. // // an RR to say hey.
  740. case SEND_STATE_CHECKPOINTING:
  741. IF_NBFDBG (NBF_DEBUG_TIMER) {
  742. NbfPrint0 ("ExpireT1Timer: Link State=READY, SendState=CHECKPOINTING.\n");
  743. NbfDumpLinkInfo (Link);
  744. }
  745. if (--Link->SendRetries == 0) {
  746. //
  747. // We have not gotten any response to RR-p packets,
  748. // initiate orderly link teardown.
  749. //
  750. CancelT1Timeout (Link); // we are stopping a polling state
  751. Link->State = LINK_STATE_W_DISC_RSP; // we are awaiting a DISC/f.
  752. Link->SendState = SEND_STATE_DOWN;
  753. Link->ReceiveState = RECEIVE_STATE_DOWN;
  754. Link->SendRetries = (UCHAR)Link->LlcRetries;
  755. RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
  756. NbfStopLink (Link);
  757. StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // retransmit timer.
  758. NbfSendDisc (Link, TRUE); // send DISC-c/p.
  759. #if DBG
  760. if (NbfDisconnectDebug) {
  761. NbfPrint0( "ExpireT1Timer sending DISC (checkpoint failed)\n" );
  762. }
  763. #endif
  764. } else {
  765. BackoffCurrentT1Timeout (Link);
  766. NbfSendRr (Link, TRUE, TRUE); // send RR-c/p, StartT1, release lock.
  767. }
  768. break;
  769. default:
  770. RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
  771. IF_NBFDBG (NBF_DEBUG_TIMER) {
  772. NbfPrint1 ("ExpireT1Timer: Link State=READY, SendState=%ld (UNKNOWN).\n",
  773. Link->SendState);
  774. }
  775. }
  776. break;
  777. case LINK_STATE_CONNECTING:
  778. //
  779. // We sent a SABME-c/p and have not yet received UA-r/f. This
  780. // means we must decrement the retry count and if it is not yet
  781. // zero, we issue another SABME command, because he has probably
  782. // dropped our first one.
  783. //
  784. if (--Link->SendRetries == 0) {
  785. CancelT1Timeout (Link); // we are stopping a polling state
  786. Link->State = LINK_STATE_ADM;
  787. NbfSendDm (Link, FALSE); // send DM/0, release lock
  788. #if DBG
  789. if (NbfDisconnectDebug) {
  790. NbfPrint0( "ExpireT1Timer calling NbfStopLink (no response to SABME)\n" );
  791. }
  792. #endif
  793. NbfStopLink (Link);
  794. // moving to ADM, remove reference
  795. NbfDereferenceLinkSpecial("Expire T1 in CONNECTING mode", Link, LREF_NOT_ADM);
  796. return; // skip extra spinlock release.
  797. } else {
  798. BackoffCurrentT1Timeout (Link);
  799. NbfSendSabme (Link, TRUE); // send SABME/p, StartT1, release lock
  800. }
  801. break;
  802. case LINK_STATE_W_POLL:
  803. RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
  804. IF_NBFDBG (NBF_DEBUG_TIMER) {
  805. NbfPrint0 ("ExpireT1Timer: State=W_POLL, timeout not expected.\n");
  806. }
  807. break;
  808. case LINK_STATE_W_FINAL:
  809. //
  810. // We sent our initial RR-c/p and have not received his RR-r/f.
  811. // We have to restart the checkpoint, unless our retries have
  812. // run out, in which case we just abort the link.
  813. //
  814. IF_NBFDBG (NBF_DEBUG_TIMER) {
  815. NbfPrint0 ("ExpireT1Timer: Link State=W_FINAL.\n");
  816. NbfDumpLinkInfo (Link);
  817. }
  818. if (--Link->SendRetries == 0) {
  819. CancelT1Timeout (Link); // we are stopping a polling state
  820. Link->State = LINK_STATE_ADM;
  821. NbfSendDm (Link, FALSE); // send DM/0, release lock
  822. #if DBG
  823. if (NbfDisconnectDebug) {
  824. NbfPrint0( "ExpireT1Timer calling NbfStopLink (no final received)\n" );
  825. }
  826. #endif
  827. NbfStopLink (Link);
  828. // moving to ADM, remove reference
  829. NbfDereferenceLinkSpecial("Expire T1 in W_FINAL mode", Link, LREF_NOT_ADM);
  830. return; // skip extra spinlock release.
  831. } else {
  832. BackoffCurrentT1Timeout (Link);
  833. NbfSendRr (Link, TRUE, TRUE); // send RR-c/p, StartT1, release lock
  834. }
  835. break;
  836. case LINK_STATE_W_DISC_RSP:
  837. //
  838. // We sent a DISC-c/p to disconnect this link and are awaiting
  839. // his response, either a UA-r/f or DM-r/f. We have to issue
  840. // the DISC again, unless we've tried a few times, in which case
  841. // we just shut the link down.
  842. //
  843. IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
  844. NbfPrint0 ("ExpireT1Timer: Link State=W_DISC_RESP.\n");
  845. NbfDumpLinkInfo (Link);
  846. }
  847. if (--Link->SendRetries == 0) {
  848. CancelT1Timeout (Link); // we are stopping a polling state
  849. Link->State = LINK_STATE_ADM;
  850. NbfSendDm (Link, FALSE); // send DM/0, release lock
  851. #if DBG
  852. if (NbfDisconnectDebug) {
  853. NbfPrint0( "ExpireT1Timer calling NbfStopLink (no response to DISC)\n" );
  854. }
  855. #endif
  856. NbfStopLink (Link);
  857. // moving to ADM, remove reference
  858. NbfDereferenceLinkSpecial("Expire T1 in W_DISC_RSP mode", Link, LREF_NOT_ADM);
  859. return; // skip extra spinlock release.
  860. } else {
  861. // we don't bother calling BackoffCurrentT1Timeout for DISCs.
  862. ++Link->CurrentPollRetransmits;
  863. StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // startup timer again.
  864. RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
  865. NbfSendDisc (Link, TRUE); // send DISC/p.
  866. }
  867. break;
  868. default:
  869. RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
  870. IF_NBFDBG (NBF_DEBUG_TIMER) {
  871. NbfPrint1 ("ExpireT1Timer: State=%ld (UNKNOWN), timeout not expected.\n",
  872. Link->State);
  873. }
  874. }
  875. } /* ExpireT1Timer */
  876. VOID
  877. ExpireT2Timer(
  878. PTP_LINK Link
  879. )
  880. /*++
  881. Routine Description:
  882. This routine is called when a link's T2 timer expires. T2 is the
  883. delayed acknowlegement timer in the LLC connection-oriented procedures.
  884. The T2 timer is started when a valid I-frame is received but not
  885. immediately acknowleged. Then, if reverse I-frame traffic is sent,
  886. the timer is stopped, since the reverse traffic will acknowlege the
  887. received I-frames. If no reverse I-frame traffic becomes available
  888. to send, then this timer fires, causing a RR-r/0 to be sent so as
  889. to acknowlege the received but as yet unacked I-frames.
  890. Arguments:
  891. Link - Pointer to the TP_LINK object whose T2 timer has expired.
  892. Return Value:
  893. none.
  894. --*/
  895. {
  896. IF_NBFDBG (NBF_DEBUG_TIMER) {
  897. NbfPrint0 ("ExpireT2Timer: Entered.\n");
  898. }
  899. ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
  900. NbfSendRr (Link, FALSE, FALSE); // send RR-r/f, release lock.
  901. } /* ExpireT2Timer */
  902. VOID
  903. ExpireTiTimer(
  904. PTP_LINK Link
  905. )
  906. /*++
  907. Routine Description:
  908. This routine is called when a link's Ti timer expires. Ti is the
  909. inactivity timer, and serves as a keep-alive on a link basis, to
  910. periodically perform some protocol exchange with the remote connection
  911. partner that will implicitly reveal whether the link is still active
  912. or not. This implementation simply uses a checkpoint sequence, but
  913. some other protocols may choose to add protocol, including sending
  914. a NetBIOS SESSION_ALIVE frame. If a checkpoint sequence is already
  915. in progress, then we do nothing.
  916. This timer expiration routine is self-perpetuating; that is, it starts
  917. itself after finishing its tasks every time.
  918. Arguments:
  919. Link - Pointer to the TP_LINK object whose Ti timer has expired.
  920. Return Value:
  921. none.
  922. --*/
  923. {
  924. IF_NBFDBG (NBF_DEBUG_TIMER) {
  925. NbfPrint0 ("ExpireTiTimer: Entered.\n");
  926. }
  927. ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
  928. if ((Link->State != LINK_STATE_ADM) &&
  929. (Link->State != LINK_STATE_W_DISC_RSP) &&
  930. (Link->SendState != SEND_STATE_CHECKPOINTING)) {
  931. IF_NBFDBG (NBF_DEBUG_TIMER) {
  932. NbfPrint0 ("ExpireTiTimer: Entered.\n");
  933. NbfDumpLinkInfo (Link);
  934. }
  935. if (Link->Provider->EasilyDisconnected && Link->NumberOfConnectors == 0) {
  936. //
  937. // On an easily disconnected network with only server connections,
  938. // if there has been no activity in this timeout period then
  939. // we trash the connection.
  940. //
  941. if (Link->PacketsReceived == Link->TiStartPacketsReceived) {
  942. Link->State = LINK_STATE_ADM;
  943. NbfSendDm (Link, FALSE); // send DM/0, release lock
  944. #if DBG
  945. if (NbfDisconnectDebug) {
  946. NbfPrint0( "ExpireT1Timer calling NbfStopLink (no final received)\n" );
  947. }
  948. #endif
  949. NbfStopLink (Link);
  950. // moving to ADM, remove reference
  951. NbfDereferenceLinkSpecial("Expire T1 in W_FINAL mode", Link, LREF_NOT_ADM);
  952. } else {
  953. //
  954. // There was traffic, restart the timer.
  955. //
  956. StartTi (Link);
  957. RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
  958. }
  959. } else {
  960. #if 0
  961. if ((Link->SendState == SEND_STATE_READY) &&
  962. (Link->T1 == 0) &&
  963. (!IsListEmpty (&Link->WackQ))) {
  964. //
  965. // If we think the link is idle but there are packets
  966. // on the WackQ, the link is messed up, disconnect it.
  967. //
  968. NbfPrint1 ("NBF: Link %d hung at Ti expiration, recovering\n", Link);
  969. RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
  970. NbfStopLink (Link);
  971. } else {
  972. #endif
  973. Link->SendState = SEND_STATE_CHECKPOINTING;
  974. Link->PacketsSent = 0;
  975. Link->PacketsResent = 0;
  976. Link->PacketsReceived = 0;
  977. NbfSendRr (Link, TRUE, TRUE); // send RR-c/p, StartT1, release lock.
  978. #if 0
  979. }
  980. #endif
  981. }
  982. } else {
  983. Link->PacketsSent = 0;
  984. Link->PacketsResent = 0;
  985. Link->PacketsReceived = 0;
  986. RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
  987. #if DBG
  988. if (Link->SendState == SEND_STATE_REJECTING) {
  989. NbfPrint0 ("ExpireTiTimer: link state == rejecting, shouldn't be\n");
  990. }
  991. #endif
  992. }
  993. #if 0
  994. //
  995. // Startup the inactivity timer again.
  996. //
  997. if (Link->State != LINK_STATE_ADM) {
  998. StartTi (Link);
  999. }
  1000. #endif
  1001. } /* ExpireTiTimer */
  1002. #if 0
  1003. VOID
  1004. ExpirePurgeTimer(
  1005. PDEVICE_CONTEXT DeviceContext
  1006. )
  1007. /*++
  1008. Routine Description:
  1009. This routine is called when the device context's periodic adaptive
  1010. window algorithm timer expires. The timer perpetuates itself on a
  1011. regular basis.
  1012. Arguments:
  1013. DeviceContext - Pointer to the device context whose purge timer has expired.
  1014. Return Value:
  1015. none.
  1016. --*/
  1017. {
  1018. PTP_LINK Link;
  1019. PLIST_ENTRY p;
  1020. IF_NBFDBG (NBF_DEBUG_TIMER) {
  1021. NbfPrint0 ("ExpirePurgeTimer: Entered.\n");
  1022. }
  1023. //
  1024. // Scan through the link database on this device context and clear
  1025. // their worst window size limit. This will allow stuck links to
  1026. // grow their window again even though they encountered temporary
  1027. // congestion at the remote link station's adapter.
  1028. //
  1029. while (!IsListEmpty (&DeviceContext->PurgeList)) {
  1030. p = RemoveHeadList (&DeviceContext->PurgeList);
  1031. Link = CONTAINING_RECORD (p, TP_LINK, PurgeList);
  1032. Link->WorstWindowSize = Link->MaxWindowSize; // maximum window possible.
  1033. }
  1034. //
  1035. // Restart purge timer.
  1036. //
  1037. DeviceContext->AdaptivePurge = DeviceContext->ShortAbsoluteTime + TIMER_PURGE_TICKS;
  1038. } /* ExpirePurgeTimer */
  1039. #endif
  1040. VOID
  1041. ScanShortTimersDpc(
  1042. IN PKDPC Dpc,
  1043. IN PVOID DeferredContext,
  1044. IN PVOID SystemArgument1,
  1045. IN PVOID SystemArgument2
  1046. )
  1047. /*++
  1048. Routine Description:
  1049. This routine is called at DISPATCH_LEVEL by the system at regular
  1050. intervals to determine if any link-level timers have expired, and
  1051. if they have, to execute their expiration routines.
  1052. Arguments:
  1053. DeferredContext - Pointer to our DEVICE_CONTEXT object.
  1054. Return Value:
  1055. none.
  1056. --*/
  1057. {
  1058. PLIST_ENTRY p, nextp;
  1059. PDEVICE_CONTEXT DeviceContext;
  1060. PTP_LINK Link;
  1061. PTP_CONNECTION Connection;
  1062. BOOLEAN RestartTimer = FALSE;
  1063. LARGE_INTEGER CurrentTick;
  1064. LARGE_INTEGER TickDifference;
  1065. ULONG TickDelta;
  1066. Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings
  1067. ENTER_NBF;
  1068. DeviceContext = DeferredContext;
  1069. IF_NBFDBG (NBF_DEBUG_TIMERDPC) {
  1070. NbfPrint0 ("ScanShortTimersDpc: Entered.\n");
  1071. }
  1072. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1073. //
  1074. // This prevents anybody from starting the timer while we
  1075. // are in this routine (the main reason for this is that it
  1076. // makes it easier to determine whether we should restart
  1077. // it at the end of this routine).
  1078. //
  1079. DeviceContext->ProcessingShortTimer = TRUE;
  1080. //
  1081. // Advance the up-counter used to mark time in SHORT_TIMER_DELTA units. If we
  1082. // advance it all the way to 0xf0000000, then reset it to 0x10000000.
  1083. // We also run all the lists, decreasing all counters by 0xe0000000.
  1084. //
  1085. KeQueryTickCount (&CurrentTick);
  1086. TickDifference.QuadPart = CurrentTick.QuadPart -
  1087. (DeviceContext->ShortTimerStart).QuadPart;
  1088. TickDelta = TickDifference.LowPart / NbfShortTimerDeltaTicks;
  1089. if (TickDelta == 0) {
  1090. TickDelta = 1;
  1091. }
  1092. DeviceContext->ShortAbsoluteTime += TickDelta;
  1093. if (DeviceContext->ShortAbsoluteTime >= 0xf0000000) {
  1094. ULONG Timeout;
  1095. DeviceContext->ShortAbsoluteTime -= 0xe0000000;
  1096. p = DeviceContext->ShortList.Flink;
  1097. while (p != &DeviceContext->ShortList) {
  1098. Link = CONTAINING_RECORD (p, TP_LINK, ShortList);
  1099. Timeout = Link->T1;
  1100. if (Timeout) {
  1101. Link->T1 = Timeout - 0xe0000000;
  1102. }
  1103. Timeout = Link->T2;
  1104. if (Timeout) {
  1105. Link->T2 = Timeout - 0xe0000000;
  1106. }
  1107. p = p->Flink;
  1108. }
  1109. }
  1110. //
  1111. // now, as the timers are started, links are added to the end of the
  1112. // respective queue for that timer. since we know the additions are
  1113. // done in an orderly fashion and are sequential, we must only traverse
  1114. // a particular timer list to the first entry that is greater than our
  1115. // timer. That entry and all further entries will not need service.
  1116. // When a timer is cancelled, we remove the link from the list. With all
  1117. // of this fooling around, we wind up only visiting those links that are
  1118. // actually in danger of timing out, minimizing time in this routine.
  1119. //
  1120. // T1 timers first; this is the link-level response expected timer, and is
  1121. // the shortest one.
  1122. // T2 timers. This is the iframe response expected timer, and is typically
  1123. // about 300 ms.
  1124. p = DeviceContext->ShortList.Flink;
  1125. while (p != &DeviceContext->ShortList) {
  1126. Link = CONTAINING_RECORD (p, TP_LINK, ShortList);
  1127. ASSERT (Link->OnShortList);
  1128. //
  1129. // To avoid problems with the refcount being 0, don't
  1130. // do this if we are in ADM.
  1131. //
  1132. if (Link->State != LINK_STATE_ADM) {
  1133. if (Link->T1 && (DeviceContext->ShortAbsoluteTime > Link->T1)) {
  1134. Link->T1 = 0;
  1135. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1136. ExpireT1Timer (Link); // no spinlocks held
  1137. INCREMENT_COUNTER (DeviceContext, ResponseTimerExpirations);
  1138. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1139. }
  1140. if (Link->T2 && (DeviceContext->ShortAbsoluteTime > Link->T2)) {
  1141. Link->T2 = 0;
  1142. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1143. ExpireT2Timer (Link); // no spinlocks held
  1144. INCREMENT_COUNTER (DeviceContext, AckTimerExpirations);
  1145. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1146. }
  1147. }
  1148. if (!Link->OnShortList) {
  1149. //
  1150. // The link has been taken out of the list while
  1151. // we were processing it. In this (rare) case we
  1152. // stop processing the whole list, we'll get it
  1153. // next time.
  1154. //
  1155. #if DBG
  1156. DbgPrint ("NBF: Stop processing ShortList, %lx removed\n", Link);
  1157. #endif
  1158. break;
  1159. }
  1160. nextp = p->Flink;
  1161. if ((Link->T1 == 0) && (Link->T2 == 0)) {
  1162. Link->OnShortList = FALSE;
  1163. RemoveEntryList(p);
  1164. //
  1165. // Do another check; that way if someone slipped in between
  1166. // the check of Link->Tx and the OnShortList = FALSE and
  1167. // therefore exited without inserting, we'll catch that here.
  1168. //
  1169. if ((Link->T1 != 0) || (Link->T2 != 0)) {
  1170. InsertTailList(&DeviceContext->ShortList, &Link->ShortList);
  1171. Link->OnShortList = TRUE;
  1172. }
  1173. }
  1174. p = nextp;
  1175. }
  1176. //
  1177. // If the list is empty note that, otherwise ShortListActive
  1178. // remains TRUE.
  1179. //
  1180. if (IsListEmpty (&DeviceContext->ShortList)) {
  1181. DeviceContext->a.i.ShortListActive = FALSE;
  1182. }
  1183. //
  1184. // NOTE: DeviceContext->TimerSpinLock is held here.
  1185. //
  1186. //
  1187. // Connection Data Ack timers. This queue is used to indicate
  1188. // that a piggyback ack is pending for this connection. We walk
  1189. // the queue, for each element we check if the connection has
  1190. // been on the queue for NbfDeferredPasses times through
  1191. // here. If so, we take it off and send an ack. Note that
  1192. // we have to be very careful how we walk the queue, since
  1193. // it may be changing while this is running.
  1194. //
  1195. // NOTE: There is no expiration time for connections on this
  1196. // queue; it "expires" every time ScanShortTimersDpc runs.
  1197. //
  1198. for (p = DeviceContext->DataAckQueue.Flink;
  1199. p != &DeviceContext->DataAckQueue;
  1200. p = p->Flink) {
  1201. Connection = CONTAINING_RECORD (p, TP_CONNECTION, DataAckLinkage);
  1202. //
  1203. // Skip this connection if it is not queued or it is
  1204. // too recent to matter. We may skip incorrectly if
  1205. // the connection is just being queued, but that is
  1206. // OK, we will get it next time.
  1207. //
  1208. if (((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) == 0) &&
  1209. ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_NOT_Q) == 0)) {
  1210. continue;
  1211. }
  1212. TickDifference.QuadPart = CurrentTick.QuadPart -
  1213. (Connection->ConnectStartTime).QuadPart;
  1214. if ((TickDifference.HighPart == 0) &&
  1215. (TickDifference.LowPart <= NbfTwentyMillisecondsTicks)) {
  1216. continue;
  1217. }
  1218. NbfReferenceConnection ("ScanShortTimersDpc", Connection, CREF_DATA_ACK_QUEUE);
  1219. DeviceContext->DataAckQueueChanged = FALSE;
  1220. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1221. //
  1222. // Check the correct connection flag, to ensure that a
  1223. // send has not just taken him off the queue.
  1224. //
  1225. ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
  1226. if (((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) &&
  1227. ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_NOT_Q) == 0)) {
  1228. //
  1229. // Yes, we were waiting to piggyback an ack, but no send
  1230. // has come along. Turn off the flags and send an ack.
  1231. //
  1232. // We have to ensure we nest the spin lock acquisition
  1233. // correctly.
  1234. //
  1235. Connection->DeferredFlags &= ~CONNECTION_FLAGS_DEFERRED_ACK;
  1236. RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
  1237. INCREMENT_COUNTER (DeviceContext, PiggybackAckTimeouts);
  1238. #if DBG
  1239. if (NbfDebugPiggybackAcks) {
  1240. NbfPrint0("T");
  1241. }
  1242. #endif
  1243. NbfSendDataAck (Connection);
  1244. } else {
  1245. RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
  1246. }
  1247. NbfDereferenceConnection ("ScanShortTimersDpc", Connection, CREF_DATA_ACK_QUEUE);
  1248. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1249. //
  1250. // If the list has changed, then we need to stop processing
  1251. // since p->Flink is not valid.
  1252. //
  1253. if (DeviceContext->DataAckQueueChanged) {
  1254. break;
  1255. }
  1256. }
  1257. if (IsListEmpty (&DeviceContext->DataAckQueue)) {
  1258. DeviceContext->a.i.DataAckQueueActive = FALSE;
  1259. }
  1260. #if 0
  1261. //
  1262. // NOTE: This is currently disabled, it may be reenabled
  1263. // at some point - adamba 9/1/92
  1264. //
  1265. // If the adaptive purge timer has expired, then run the purge
  1266. // algorithm on all affected links.
  1267. //
  1268. if (DeviceContext->ShortAbsoluteTime > DeviceContext->AdaptivePurge) {
  1269. DeviceContext->AdaptivePurge = DeviceContext->ShortAbsoluteTime +
  1270. TIMER_PURGE_TICKS;
  1271. ExpirePurgeTimer (DeviceContext);
  1272. }
  1273. #endif
  1274. //
  1275. // deferred processing. We will handle all link structure additions and
  1276. // deletions here; we must be the exclusive user of the link tree to do
  1277. // this. We verify that we are by examining the semaphore that tells us
  1278. // how many readers of the tree are curretly processing it. If there are
  1279. // any readers, we simply increment our "deferred processing locked out"
  1280. // counter and do something else. If we defer too many times, we simply
  1281. // bugcheck, as something is wrong somewhere in the system.
  1282. //
  1283. if (!IsListEmpty (&DeviceContext->LinkDeferred)) {
  1284. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1285. //
  1286. // now do additions or deletions if we can.
  1287. //
  1288. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock);
  1289. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1290. while (!IsListEmpty (&DeviceContext->LinkDeferred)) {
  1291. p = RemoveHeadList (&DeviceContext->LinkDeferred);
  1292. DeviceContext->DeferredNotSatisfied = 0;
  1293. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1294. //
  1295. // now do an addition or deletion if we can.
  1296. //
  1297. Link = CONTAINING_RECORD (p, TP_LINK, DeferredList);
  1298. IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
  1299. NbfPrint4 ("ScanShortTimersDPC: link off deferred queue %lx %lx %lx Flags: %lx \n",
  1300. Link, DeviceContext->LinkDeferred.Flink,
  1301. DeviceContext->LinkDeferred.Blink, Link->DeferredFlags);
  1302. }
  1303. Link->DeferredList.Flink = Link->DeferredList.Blink =
  1304. &Link->DeferredList;
  1305. if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_MASK) == 0) {
  1306. // Tried to do an operation we don't understand; whine.
  1307. IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
  1308. NbfPrint2 ("ScanTimerDPC: Attempting deferred operation on nothing! \nScanTimerDPC: Link: %lx, DeviceContext->DeferredQueue: %lx\n",
  1309. Link, &DeviceContext->LinkDeferred);
  1310. DbgBreakPoint ();
  1311. }
  1312. InitializeListHead (&DeviceContext->LinkDeferred);
  1313. // We could have a hosed deferred operations queue here;
  1314. // take some time to figure out if it is ok.
  1315. }
  1316. if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_ADD) != 0) {
  1317. Link->DeferredFlags &= ~LINK_FLAGS_DEFERRED_ADD;
  1318. if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) != 0) {
  1319. //
  1320. // It is being added and deleted; just destroy it.
  1321. //
  1322. Link->DeferredFlags &= ~LINK_FLAGS_DEFERRED_DELETE;
  1323. NbfDestroyLink (Link);
  1324. IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
  1325. NbfPrint1 ("ScanTimerDPC: deferred processing: Add AND Delete link: %lx\n",Link);
  1326. }
  1327. } else {
  1328. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1329. NbfAddLinkToTree (DeviceContext, Link);
  1330. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1331. IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
  1332. NbfPrint1 ("ScanTimerDPC: deferred processing: Added link to tree: %lx\n",Link);
  1333. }
  1334. }
  1335. } else if ((Link->DeferredFlags & LINK_FLAGS_DEFERRED_DELETE) != 0) {
  1336. Link->DeferredFlags &= ~LINK_FLAGS_DEFERRED_DELETE;
  1337. NbfRemoveLinkFromTree (DeviceContext, Link);
  1338. NbfDestroyLink (Link);
  1339. IF_NBFDBG (NBF_DEBUG_TEARDOWN) {
  1340. NbfPrint1 ("ScanTimerDPC: deferred processing: returning link %lx to LinkPool.\n", Link);
  1341. }
  1342. }
  1343. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1344. }
  1345. InitializeListHead (&DeviceContext->LinkDeferred);
  1346. DeviceContext->a.i.LinkDeferredActive = FALSE;
  1347. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1348. RELEASE_DPC_SPIN_LOCK (&DeviceContext->LinkSpinLock);
  1349. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1350. }
  1351. //
  1352. // Update the real counters from the temp ones.
  1353. //
  1354. ADD_TO_LARGE_INTEGER(
  1355. &DeviceContext->Statistics.DataFrameBytesSent,
  1356. DeviceContext->TempIFrameBytesSent);
  1357. DeviceContext->Statistics.DataFramesSent += DeviceContext->TempIFramesSent;
  1358. DeviceContext->TempIFrameBytesSent = 0;
  1359. DeviceContext->TempIFramesSent = 0;
  1360. ADD_TO_LARGE_INTEGER(
  1361. &DeviceContext->Statistics.DataFrameBytesReceived,
  1362. DeviceContext->TempIFrameBytesReceived);
  1363. DeviceContext->Statistics.DataFramesReceived += DeviceContext->TempIFramesReceived;
  1364. DeviceContext->TempIFrameBytesReceived = 0;
  1365. DeviceContext->TempIFramesReceived = 0;
  1366. //
  1367. // Determine if we have to restart the timer.
  1368. //
  1369. DeviceContext->ProcessingShortTimer = FALSE;
  1370. if (DeviceContext->a.AnyActive &&
  1371. (DeviceContext->State != DEVICECONTEXT_STATE_STOPPING)) {
  1372. RestartTimer = TRUE;
  1373. }
  1374. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1375. if (RestartTimer) {
  1376. //
  1377. // Start up the timer again. Note that because we start the timer
  1378. // after doing work (above), the timer values will slip somewhat,
  1379. // depending on the load on the protocol. This is entirely acceptable
  1380. // and will prevent us from using the timer DPC in two different
  1381. // threads of execution.
  1382. //
  1383. KeQueryTickCount(&DeviceContext->ShortTimerStart);
  1384. START_TIMER(DeviceContext,
  1385. SHORT_TIMER,
  1386. &DeviceContext->ShortSystemTimer,
  1387. DueTimeDelta,
  1388. &DeviceContext->ShortTimerSystemDpc);
  1389. } else {
  1390. #if DBG
  1391. if (NbfDebugShortTimer) {
  1392. DbgPrint("x");
  1393. }
  1394. #endif
  1395. NbfDereferenceDeviceContext ("Don't restart short timer", DeviceContext, DCREF_SCAN_TIMER);
  1396. }
  1397. LEAVE_TIMER(DeviceContext, SHORT_TIMER);
  1398. LEAVE_NBF;
  1399. return;
  1400. } /* ScanShortTimersDpc */
  1401. VOID
  1402. ScanLongTimersDpc(
  1403. IN PKDPC Dpc,
  1404. IN PVOID DeferredContext,
  1405. IN PVOID SystemArgument1,
  1406. IN PVOID SystemArgument2
  1407. )
  1408. /*++
  1409. Routine Description:
  1410. This routine is called at DISPATCH_LEVEL by the system at regular
  1411. intervals to determine if any long timers have expired, and
  1412. if they have, to execute their expiration routines.
  1413. Arguments:
  1414. DeferredContext - Pointer to our DEVICE_CONTEXT object.
  1415. Return Value:
  1416. none.
  1417. --*/
  1418. {
  1419. LARGE_INTEGER DueTime;
  1420. PLIST_ENTRY p, nextp;
  1421. PDEVICE_CONTEXT DeviceContext;
  1422. PTP_LINK Link;
  1423. PTP_CONNECTION Connection;
  1424. Dpc, SystemArgument1, SystemArgument2; // prevent compiler warnings
  1425. ENTER_NBF;
  1426. DeviceContext = DeferredContext;
  1427. IF_NBFDBG (NBF_DEBUG_TIMERDPC) {
  1428. NbfPrint0 ("ScanLongTimersDpc: Entered.\n");
  1429. }
  1430. //
  1431. // Advance the up-counter used to mark time in LONG_TIMER_DELTA units.If we
  1432. // advance it all the way to 0xf0000000, then reset it to 0x10000000.
  1433. // We also run all the lists, decreasing all counters by 0xe0000000.
  1434. //
  1435. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1436. if (++DeviceContext->LongAbsoluteTime == 0xf0000000) {
  1437. ULONG Timeout;
  1438. DeviceContext->LongAbsoluteTime = 0x10000000;
  1439. p = DeviceContext->LongList.Flink;
  1440. while (p != &DeviceContext->LongList) {
  1441. Link = CONTAINING_RECORD (p, TP_LINK, LongList);
  1442. Timeout = Link->Ti;
  1443. if (Timeout) {
  1444. Link->Ti = Timeout - 0xe0000000;
  1445. }
  1446. p = p->Flink;
  1447. }
  1448. }
  1449. //
  1450. // now, as the timers are started, links are added to the end of the
  1451. // respective queue for that timer. since we know the additions are
  1452. // done in an orderly fashion and are sequential, we must only traverse
  1453. // a particular timer list to the first entry that is greater than our
  1454. // timer. That entry and all further entries will not need service.
  1455. // When a timer is cancelled, we remove the link from the list. With all
  1456. // of this fooling around, we wind up only visiting those links that are
  1457. // actually in danger of timing out, minimizing time in this routine.
  1458. //
  1459. //
  1460. // Ti timers. This is the inactivity timer for the link, used when no
  1461. // activity has occurred on the link in some time. We only check this
  1462. // every four expirations of the timer since the granularity is usually
  1463. // in the 30 second range.
  1464. // NOTE: DeviceContext->TimerSpinLock is held here.
  1465. //
  1466. if ((DeviceContext->LongAbsoluteTime % 4) == 0) {
  1467. p = DeviceContext->LongList.Flink;
  1468. while (p != &DeviceContext->LongList) {
  1469. Link = CONTAINING_RECORD (p, TP_LINK, LongList);
  1470. ASSERT (Link->OnLongList);
  1471. //
  1472. // To avoid problems with the refcount being 0, don't
  1473. // do this if we are in ADM.
  1474. //
  1475. #if DBG
  1476. if (Link->SendState == SEND_STATE_REJECTING) {
  1477. NbfPrint0 ("Timer: link state == rejecting, shouldn't be\n");
  1478. }
  1479. #endif
  1480. if (Link->State != LINK_STATE_ADM) {
  1481. if (Link->Ti && (DeviceContext->LongAbsoluteTime > Link->Ti)) {
  1482. Link->Ti = 0;
  1483. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1484. ExpireTiTimer (Link); // no spinlocks held
  1485. ++DeviceContext->TiExpirations;
  1486. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1487. }
  1488. }
  1489. if (!Link->OnLongList) {
  1490. //
  1491. // The link has been taken out of the list while
  1492. // we were processing it. In this (rare) case we
  1493. // stop processing the whole list, we'll get it
  1494. // next time.
  1495. //
  1496. #if DBG
  1497. DbgPrint ("NBF: Stop processing LongList, %lx removed\n", Link);
  1498. #endif
  1499. break;
  1500. }
  1501. nextp = p->Flink;
  1502. if (Link->Ti == 0) {
  1503. Link->OnLongList = FALSE;
  1504. RemoveEntryList(p);
  1505. if (Link->Ti != 0) {
  1506. InsertTailList(&DeviceContext->LongList, &Link->LongList);
  1507. Link->OnLongList = TRUE;
  1508. }
  1509. }
  1510. p = nextp;
  1511. }
  1512. }
  1513. //
  1514. // Now scan the data ack queue, looking for connections with
  1515. // no acks queued that we can get rid of.
  1516. //
  1517. // Note: The timer spinlock is held here.
  1518. //
  1519. p = DeviceContext->DataAckQueue.Flink;
  1520. while (p != &DeviceContext->DataAckQueue &&
  1521. !DeviceContext->DataAckQueueChanged) {
  1522. Connection = CONTAINING_RECORD (DeviceContext->DataAckQueue.Flink, TP_CONNECTION, DataAckLinkage);
  1523. if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) {
  1524. p = p->Flink;
  1525. continue;
  1526. }
  1527. NbfReferenceConnection ("ScanShortTimersDpc", Connection, CREF_DATA_ACK_QUEUE);
  1528. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1529. ACQUIRE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
  1530. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1531. //
  1532. // Have to check again, because the connection might
  1533. // just have been stopped.
  1534. //
  1535. if (Connection->OnDataAckQueue) {
  1536. Connection->OnDataAckQueue = FALSE;
  1537. RemoveEntryList (&Connection->DataAckLinkage);
  1538. if ((Connection->DeferredFlags & CONNECTION_FLAGS_DEFERRED_ACK) != 0) {
  1539. InsertTailList (&DeviceContext->DataAckQueue, &Connection->DataAckLinkage);
  1540. Connection->OnDataAckQueue = TRUE;
  1541. }
  1542. DeviceContext->DataAckQueueChanged = TRUE;
  1543. }
  1544. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1545. RELEASE_DPC_SPIN_LOCK (Connection->LinkSpinLock);
  1546. NbfDereferenceConnection ("ScanShortTimersDpc", Connection, CREF_DATA_ACK_QUEUE);
  1547. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1548. //
  1549. // Since we have changed the list, we can't tell if p->Flink
  1550. // is valid, so break. The effect is that we gradually peel
  1551. // connections off the queue.
  1552. //
  1553. break;
  1554. }
  1555. RELEASE_DPC_SPIN_LOCK (&DeviceContext->TimerSpinLock);
  1556. //
  1557. // See if we got any multicast traffic last time.
  1558. //
  1559. if (DeviceContext->MulticastPacketCount == 0) {
  1560. ++DeviceContext->LongTimeoutsWithoutMulticast;
  1561. if (DeviceContext->EasilyDisconnected &&
  1562. (DeviceContext->LongTimeoutsWithoutMulticast > 5)) {
  1563. PLIST_ENTRY p;
  1564. PTP_ADDRESS address;
  1565. //
  1566. // We have had five timeouts in a row with no
  1567. // traffic, mark all the addresses as needing
  1568. // reregistration next time a connect is
  1569. // done on them.
  1570. //
  1571. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
  1572. for (p = DeviceContext->AddressDatabase.Flink;
  1573. p != &DeviceContext->AddressDatabase;
  1574. p = p->Flink) {
  1575. address = CONTAINING_RECORD (p, TP_ADDRESS, Linkage);
  1576. address->Flags |= ADDRESS_FLAGS_NEED_REREGISTER;
  1577. }
  1578. RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
  1579. DeviceContext->LongTimeoutsWithoutMulticast = 0;
  1580. }
  1581. } else {
  1582. DeviceContext->LongTimeoutsWithoutMulticast = 0;
  1583. }
  1584. DeviceContext->MulticastPacketCount = 0;
  1585. //
  1586. // Every thirty seconds, check for stalled connections
  1587. //
  1588. ++DeviceContext->StalledConnectionCount;
  1589. if (DeviceContext->StalledConnectionCount ==
  1590. (USHORT)((30 * SECONDS) / LONG_TIMER_DELTA)) {
  1591. DeviceContext->StalledConnectionCount = 0;
  1592. StopStalledConnections (DeviceContext);
  1593. }
  1594. //
  1595. // Scan for any uncompleted receive IRPs, this may happen if
  1596. // the cable is pulled and we don't get any more ReceiveComplete
  1597. // indications.
  1598. NbfReceiveComplete((NDIS_HANDLE)DeviceContext);
  1599. //
  1600. // Start up the timer again. Note that because we start the timer
  1601. // after doing work (above), the timer values will slip somewhat,
  1602. // depending on the load on the protocol. This is entirely acceptable
  1603. // and will prevent us from using the timer DPC in two different
  1604. // threads of execution.
  1605. //
  1606. if (DeviceContext->State != DEVICECONTEXT_STATE_STOPPING) {
  1607. DueTime.HighPart = -1;
  1608. DueTime.LowPart = (ULONG)-(LONG_TIMER_DELTA); // delta time to next click.
  1609. START_TIMER(DeviceContext,
  1610. LONG_TIMER,
  1611. &DeviceContext->LongSystemTimer,
  1612. DueTime,
  1613. &DeviceContext->LongTimerSystemDpc);
  1614. } else {
  1615. NbfDereferenceDeviceContext ("Don't restart long timer", DeviceContext, DCREF_SCAN_TIMER);
  1616. }
  1617. LEAVE_TIMER(DeviceContext, LONG_TIMER);
  1618. LEAVE_NBF;
  1619. return;
  1620. } /* ScanLongTimersDpc */
  1621. VOID
  1622. StopStalledConnections(
  1623. IN PDEVICE_CONTEXT DeviceContext
  1624. )
  1625. /*++
  1626. Routine Description:
  1627. This routine is called from ScanLongTimersDpc every 30 seconds.
  1628. It checks for connections that have not made any progress in
  1629. their sends in the last two minutes, and stops them.
  1630. Arguments:
  1631. DeviceContext - The device context to check.
  1632. Return Value:
  1633. none.
  1634. --*/
  1635. {
  1636. PTP_ADDRESS Address, PrevAddress;
  1637. PTP_CONNECTION Connection, StalledConnection;
  1638. PLIST_ENTRY p, q;
  1639. //
  1640. // If we have crossed a thirty-second interval, then
  1641. // check each address for connections that have not
  1642. // made any progress in two minutes.
  1643. //
  1644. PrevAddress = NULL;
  1645. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
  1646. for (p = DeviceContext->AddressDatabase.Flink;
  1647. p != &DeviceContext->AddressDatabase;
  1648. p = p->Flink) {
  1649. Address = CONTAINING_RECORD (
  1650. p,
  1651. TP_ADDRESS,
  1652. Linkage);
  1653. if ((Address->Flags & ADDRESS_FLAGS_STOPPING) != 0) {
  1654. continue;
  1655. }
  1656. //
  1657. // By referencing the address, we ensure that it will stay
  1658. // in the AddressDatabase, this its Flink will stay valid.
  1659. //
  1660. NbfReferenceAddress("checking for dead connections", Address, AREF_TIMER_SCAN);
  1661. RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
  1662. if (PrevAddress) {
  1663. NbfDereferenceAddress ("done checking", PrevAddress, AREF_TIMER_SCAN);
  1664. }
  1665. //
  1666. // Scan this addresses connection database for connections
  1667. // that have not made progress in the last two minutes; we
  1668. // kill the first one we find.
  1669. //
  1670. StalledConnection = NULL;
  1671. ACQUIRE_DPC_SPIN_LOCK (&Address->SpinLock);
  1672. for (q = Address->ConnectionDatabase.Flink;
  1673. q != &Address->ConnectionDatabase;
  1674. q = q->Flink) {
  1675. Connection = CONTAINING_RECORD (q, TP_CONNECTION, AddressList);
  1676. ACQUIRE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
  1677. if (!IsListEmpty (&Connection->SendQueue)) {
  1678. //
  1679. // If there is a connection on the queue...
  1680. //
  1681. if (Connection->StallBytesSent == Connection->sp.MessageBytesSent) {
  1682. //
  1683. // ...and it has not made any progress...
  1684. //
  1685. if (Connection->StallCount >= 4) {
  1686. //
  1687. // .. four times in a row, the connection is dead.
  1688. //
  1689. if (!StalledConnection) {
  1690. StalledConnection = Connection;
  1691. NbfReferenceConnection ("stalled", Connection, CREF_STALLED);
  1692. }
  1693. #if DBG
  1694. DbgPrint ("NBF: Found connection %lx [%d for %d] stalled on %lx\n",
  1695. Connection, Connection->StallBytesSent, Connection->StallCount, Address);
  1696. #endif
  1697. } else {
  1698. //
  1699. // If it is stuck, increment the count.
  1700. //
  1701. ++Connection->StallCount;
  1702. }
  1703. } else {
  1704. Connection->StallBytesSent = Connection->sp.MessageBytesSent;
  1705. }
  1706. }
  1707. RELEASE_DPC_C_SPIN_LOCK (&Connection->SpinLock);
  1708. }
  1709. RELEASE_DPC_SPIN_LOCK (&Address->SpinLock);
  1710. if (StalledConnection) {
  1711. PTP_LINK Link = StalledConnection->Link;
  1712. #if DBG
  1713. DbgPrint("NBF: Stopping stalled connection %lx, link %lx\n", StalledConnection, Link);
  1714. #endif
  1715. FailSend (StalledConnection, STATUS_IO_TIMEOUT, TRUE); // fail the send
  1716. ACQUIRE_DPC_SPIN_LOCK (&Link->SpinLock);
  1717. if (Link->State == LINK_STATE_READY) {
  1718. CancelT1Timeout (Link);
  1719. Link->State = LINK_STATE_W_DISC_RSP;
  1720. Link->SendState = SEND_STATE_DOWN;
  1721. Link->ReceiveState = RECEIVE_STATE_DOWN;
  1722. Link->SendRetries = (UCHAR)Link->LlcRetries;
  1723. RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
  1724. NbfStopLink (Link);
  1725. StartT1 (Link, Link->HeaderLength + sizeof(DLC_S_FRAME)); // retransmit timer.
  1726. NbfSendDisc (Link, TRUE); // send DISC-c/p.
  1727. } else {
  1728. RELEASE_DPC_SPIN_LOCK (&Link->SpinLock);
  1729. NbfStopLink (Link);
  1730. }
  1731. NbfDereferenceConnection ("stalled", StalledConnection, CREF_STALLED);
  1732. }
  1733. ACQUIRE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
  1734. PrevAddress = Address;
  1735. }
  1736. RELEASE_DPC_SPIN_LOCK (&DeviceContext->SpinLock);
  1737. if (PrevAddress) {
  1738. NbfDereferenceAddress ("done checking", PrevAddress, AREF_TIMER_SCAN);
  1739. }
  1740. } /* StopStalledConnections */
  1741. VOID
  1742. NbfStartShortTimer(
  1743. IN PDEVICE_CONTEXT DeviceContext
  1744. )
  1745. /*++
  1746. Routine Description:
  1747. This routine starts the short timer, if it is not already running.
  1748. Arguments:
  1749. DeviceContext - Pointer to our device context.
  1750. Return Value:
  1751. none.
  1752. --*/
  1753. {
  1754. //
  1755. // Start the timer unless it the DPC is already running (in
  1756. // which case it will restart the timer itself if needed),
  1757. // or some list is active (meaning the timer is already
  1758. // queued up).
  1759. //
  1760. // We use a trick to check all four active lists at the
  1761. // same time, but this depends on some alignment and
  1762. // size assumptions.
  1763. //
  1764. ASSERT (sizeof(ULONG) >= 3 * sizeof(BOOLEAN));
  1765. ASSERT ((PVOID)&DeviceContext->a.AnyActive ==
  1766. (PVOID)&DeviceContext->a.i.ShortListActive);
  1767. StartTimer++;
  1768. if ((!DeviceContext->ProcessingShortTimer) &&
  1769. (!(DeviceContext->a.AnyActive))) {
  1770. #if DBG
  1771. if (NbfDebugShortTimer) {
  1772. DbgPrint("X");
  1773. }
  1774. #endif
  1775. NbfReferenceDeviceContext ("Start short timer", DeviceContext, DCREF_SCAN_TIMER);
  1776. KeQueryTickCount(&DeviceContext->ShortTimerStart);
  1777. StartTimerSet++;
  1778. START_TIMER(DeviceContext,
  1779. SHORT_TIMER,
  1780. &DeviceContext->ShortSystemTimer,
  1781. DueTimeDelta,
  1782. &DeviceContext->ShortTimerSystemDpc);
  1783. }
  1784. } /* NbfStartShortTimer */
  1785. VOID
  1786. NbfInitializeTimerSystem(
  1787. IN PDEVICE_CONTEXT DeviceContext
  1788. )
  1789. /*++
  1790. Routine Description:
  1791. This routine initializes the lightweight timer system for the transport
  1792. provider.
  1793. Arguments:
  1794. DeviceContext - Pointer to our device context.
  1795. Return Value:
  1796. none.
  1797. --*/
  1798. {
  1799. LARGE_INTEGER DueTime;
  1800. IF_NBFDBG (NBF_DEBUG_TIMER) {
  1801. NbfPrint0 ("NbfInitializeTimerSystem: Entered.\n");
  1802. }
  1803. ASSERT(TIMERS_INITIALIZED(DeviceContext));
  1804. //
  1805. // Set these up.
  1806. //
  1807. NbfTickIncrement = KeQueryTimeIncrement();
  1808. if (NbfTickIncrement > (20 * MILLISECONDS)) {
  1809. NbfTwentyMillisecondsTicks = 1;
  1810. } else {
  1811. NbfTwentyMillisecondsTicks = (20 * MILLISECONDS) / NbfTickIncrement;
  1812. }
  1813. if (NbfTickIncrement > (SHORT_TIMER_DELTA)) {
  1814. NbfShortTimerDeltaTicks = 1;
  1815. } else {
  1816. NbfShortTimerDeltaTicks = (SHORT_TIMER_DELTA) / NbfTickIncrement;
  1817. }
  1818. //
  1819. // MaximumIntervalTicks represents 60 seconds, unless the value
  1820. // when shifted out by the accuracy required is too big.
  1821. //
  1822. if ((((ULONG)0xffffffff) >> (DLC_TIMER_ACCURACY+2)) > ((60 * SECONDS) / NbfTickIncrement)) {
  1823. NbfMaximumIntervalTicks = (60 * SECONDS) / NbfTickIncrement;
  1824. } else {
  1825. NbfMaximumIntervalTicks = ((ULONG)0xffffffff) >> (DLC_TIMER_ACCURACY + 2);
  1826. }
  1827. //
  1828. // The AbsoluteTime cycles between 0x10000000 and 0xf0000000.
  1829. //
  1830. DeviceContext->ShortAbsoluteTime = 0x10000000; // initialize our timer click up-counter.
  1831. DeviceContext->LongAbsoluteTime = 0x10000000; // initialize our timer click up-counter.
  1832. DeviceContext->AdaptivePurge = TIMER_PURGE_TICKS;
  1833. DeviceContext->MulticastPacketCount = 0;
  1834. DeviceContext->LongTimeoutsWithoutMulticast = 0;
  1835. KeInitializeDpc(
  1836. &DeviceContext->ShortTimerSystemDpc,
  1837. ScanShortTimersDpc,
  1838. DeviceContext);
  1839. KeInitializeDpc(
  1840. &DeviceContext->LongTimerSystemDpc,
  1841. ScanLongTimersDpc,
  1842. DeviceContext);
  1843. KeInitializeTimer (&DeviceContext->ShortSystemTimer);
  1844. KeInitializeTimer (&DeviceContext->LongSystemTimer);
  1845. DueTime.HighPart = -1;
  1846. DueTime.LowPart = (ULONG)-(LONG_TIMER_DELTA);
  1847. ENABLE_TIMERS(DeviceContext);
  1848. //
  1849. // One reference for the long timer.
  1850. //
  1851. NbfReferenceDeviceContext ("Long timer active", DeviceContext, DCREF_SCAN_TIMER);
  1852. START_TIMER(DeviceContext,
  1853. LONG_TIMER,
  1854. &DeviceContext->LongSystemTimer,
  1855. DueTime,
  1856. &DeviceContext->LongTimerSystemDpc);
  1857. } /* NbfInitializeTimerSystem */
  1858. VOID
  1859. NbfStopTimerSystem(
  1860. IN PDEVICE_CONTEXT DeviceContext
  1861. )
  1862. /*++
  1863. Routine Description:
  1864. This routine stops the lightweight timer system for the transport
  1865. provider.
  1866. Arguments:
  1867. DeviceContext - Pointer to our device context.
  1868. Return Value:
  1869. none.
  1870. --*/
  1871. {
  1872. //
  1873. // If timers are currently executing timer code, then this
  1874. // function blocks until they are done executing. Also
  1875. // no new timers will be allowed to be queued after this.
  1876. //
  1877. {
  1878. if (KeCancelTimer(&DeviceContext->LongSystemTimer)) {
  1879. LEAVE_TIMER(DeviceContext, LONG_TIMER);
  1880. NbfDereferenceDeviceContext ("Long timer cancelled", DeviceContext, DCREF_SCAN_TIMER);
  1881. }
  1882. if (KeCancelTimer(&DeviceContext->ShortSystemTimer)) {
  1883. LEAVE_TIMER(DeviceContext, SHORT_TIMER);
  1884. NbfDereferenceDeviceContext ("Short timer cancelled", DeviceContext, DCREF_SCAN_TIMER);
  1885. }
  1886. }
  1887. DISABLE_TIMERS(DeviceContext);
  1888. }