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.

1293 lines
28 KiB

  1. /*++
  2. Copyright (c) 2001-2001 Microsoft Corporation
  3. Module Name:
  4. timeouts.cxx
  5. Abstract:
  6. Implement connection timeout quality-of-service (QoS) functionality.
  7. The following timers must be monitored during the lifetime of
  8. a connection:
  9. * Connection Timeout
  10. * Header Wait Timeout
  11. * Entity Body Receive Timeout
  12. * Response Processing Timeout
  13. * Minimum Bandwidth (implemented as a Timeout)
  14. When any one of these timeout values expires, the connection should be
  15. terminated.
  16. The timer information is maintained in a timeout info block,
  17. UL_TIMEOUT_INFO_ENTRY, as part of the UL_HTTP_CONNECTION object.
  18. A timer can be Set or Reset. Setting a timer calculates when the specific
  19. timer should expire, and updates the timeout info block. Resetting a timer
  20. turns a specific timer off. Both Setting and Resettnig a timer will cause the
  21. timeout block to be re-evaluated to find the least valued expiry time.
  22. // TODO:
  23. The timeout manager uses a Timer Wheel(c) technology, as used
  24. by NT's TCP/IP stack for monitoring TCB timeouts. We will reimplement
  25. and modify the logic they use. The linkage for the Timer Wheel(c)
  26. queues is provided in the timeout info block.
  27. // TODO: CONVERT TO USING Timer Wheel Ticks instead of SystemTime.
  28. There are three separate units of time: SystemTime (100ns intervals), Timer
  29. Wheel Ticks (SystemTime / slot interval length), and Timer Wheel Slot
  30. (Timer Wheel Ticks modulo the number of slots in the Timer Wheel).
  31. Author:
  32. Eric Stenson (EricSten) 24-Mar-2001
  33. Revision History:
  34. This was originally implemented as the Connection Timeout Monitor.
  35. --*/
  36. #include "precomp.h"
  37. #include "timeoutsp.h"
  38. //
  39. // Private globals.
  40. //
  41. #ifdef ALLOC_PRAGMA
  42. #pragma alloc_text( INIT, UlInitializeTimeoutMonitor )
  43. #pragma alloc_text( PAGE, UlTerminateTimeoutMonitor )
  44. #pragma alloc_text( PAGE, UlSetTimeoutMonitorInformation )
  45. #pragma alloc_text( PAGE, UlpTimeoutMonitorWorker )
  46. #pragma alloc_text( PAGE, UlSetPerSiteConnectionTimeoutValue )
  47. #endif // ALLOC_PRAGMA
  48. #if 0
  49. NOT PAGEABLE -- UlpSetTimeoutMonitorTimer
  50. NOT PAGEABLE -- UlpTimeoutMonitorDpcRoutine
  51. NOT PAGEABLE -- UlpTimeoutCheckExpiry
  52. NOT PAGEABLE -- UlpTimeoutInsertTimerWheelEntry
  53. NOT PAGEABLE -- UlTimeoutRemoveTimerWheelEntry
  54. NOT PAGEABLE -- UlInitializeConnectionTimerInfo
  55. NOT PAGEABLE -- UlSetConnectionTimer
  56. NOT PAGEABLE -- UlSetMinKBSecTimer
  57. NOT PAGEABLE -- UlResetConnectionTimer
  58. NOT PAGEABLE -- UlEvaluateTimerState
  59. #endif // 0
  60. //
  61. // Connection Timeout Montior globals
  62. //
  63. LONG g_TimeoutMonitorInitialized = FALSE;
  64. KDPC g_TimeoutMonitorDpc;
  65. KTIMER g_TimeoutMonitorTimer;
  66. KEVENT g_TimeoutMonitorTerminationEvent;
  67. KEVENT g_TimeoutMonitorAddListEvent;
  68. UL_WORK_ITEM g_TimeoutMonitorWorkItem;
  69. //
  70. // Timeout constants
  71. //
  72. ULONG g_TM_MinKBSecDivisor; // Bytes/Sec
  73. LONGLONG g_TM_ConnectionTimeout; // 100ns ticks (Global...can be overriden)
  74. LONGLONG g_TM_HeaderWaitTimeout; // 100ns ticks
  75. //
  76. // NOTE: Must be in sync with the _CONNECTION_TIMEOUT_TIMERS enum
  77. //
  78. CHAR *g_aTimeoutTimerNames[] = {
  79. "ConnectionIdle", // TimerConnectionIdle
  80. "HeaderWait", // TimerHeaderWait
  81. "MinKBSec", // TimerMinKBSec
  82. "EntityBody", // TimerEntityBody
  83. "Response", // TimerResponse
  84. };
  85. //
  86. // Timer Wheel(c)
  87. //
  88. static LIST_ENTRY g_TimerWheel[TIMER_WHEEL_SLOTS+1]; // TODO: alloc on its own page.
  89. static UL_SPIN_LOCK g_TimerWheelMutex;
  90. static USHORT g_TimerWheelCurrentSlot;
  91. //
  92. // Public functions.
  93. //
  94. /***************************************************************************++
  95. Routine Description:
  96. Initializes the Timeout Monitor and kicks off the first polling interval
  97. Arguments:
  98. (none)
  99. --***************************************************************************/
  100. VOID
  101. UlInitializeTimeoutMonitor(
  102. VOID
  103. )
  104. {
  105. int i;
  106. LARGE_INTEGER Now;
  107. //
  108. // Sanity check.
  109. //
  110. PAGED_CODE();
  111. UlTrace(TIMEOUTS, (
  112. "http!UlInitializeTimeoutMonitor\n"
  113. ));
  114. ASSERT( FALSE == g_TimeoutMonitorInitialized );
  115. //
  116. // Set default configuration information.
  117. //
  118. g_TM_ConnectionTimeout = 15 * 60 * C_NS_TICKS_PER_SEC; // 15 min
  119. g_TM_HeaderWaitTimeout = 15 * 60 * C_NS_TICKS_PER_SEC; // 15 min
  120. g_TM_MinKBSecDivisor = 0; // 0 == Disabled
  121. //
  122. // Init Timer Wheel(c) state
  123. //
  124. //
  125. // Set current slot
  126. //
  127. KeQuerySystemTime(&Now);
  128. g_TimerWheelCurrentSlot = UlpSystemTimeToTimerWheelSlot(Now.QuadPart);
  129. //
  130. // Init Timer Wheel(c) slots & mutex
  131. //
  132. ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); // InitializeListHead requirement
  133. for ( i = 0; i < TIMER_WHEEL_SLOTS ; i++ )
  134. {
  135. InitializeListHead( &(g_TimerWheel[i]) );
  136. }
  137. InitializeListHead( &(g_TimerWheel[TIMER_OFF_SLOT]) );
  138. UlInitializeSpinLock( &(g_TimerWheelMutex), "TimeoutMonitor" );
  139. //
  140. // Init DPC object & set DPC routine
  141. //
  142. KeInitializeDpc(
  143. &g_TimeoutMonitorDpc, // DPC object
  144. &UlpTimeoutMonitorDpcRoutine, // DPC routine
  145. NULL // context
  146. );
  147. KeInitializeTimer(
  148. &g_TimeoutMonitorTimer
  149. );
  150. //
  151. // Event to control rescheduling of the timeout monitor timer
  152. //
  153. KeInitializeEvent(
  154. &g_TimeoutMonitorAddListEvent,
  155. NotificationEvent,
  156. TRUE
  157. );
  158. //
  159. // Initialize the termination event.
  160. //
  161. KeInitializeEvent(
  162. &g_TimeoutMonitorTerminationEvent,
  163. NotificationEvent,
  164. FALSE
  165. );
  166. //
  167. // Init done!
  168. //
  169. InterlockedExchange( &g_TimeoutMonitorInitialized, TRUE );
  170. //
  171. // Kick-off the first monitor sleep period
  172. //
  173. UlpSetTimeoutMonitorTimer();
  174. }
  175. /***************************************************************************++
  176. Routine Description:
  177. Terminate the Timeout Monitor, including any pending timer events.
  178. Arguments:
  179. (none)
  180. --***************************************************************************/
  181. VOID
  182. UlTerminateTimeoutMonitor(
  183. VOID
  184. )
  185. {
  186. //
  187. // Sanity check.
  188. //
  189. PAGED_CODE();
  190. UlTrace(TIMEOUTS, (
  191. "http!UlTerminateTimeoutMonitor\n"
  192. ));
  193. //
  194. // Clear the "initialized" flag. If the timeout monitor runs soon,
  195. // it will see this flag, set the termination event, and exit
  196. // quickly.
  197. //
  198. if ( TRUE == InterlockedCompareExchange(
  199. &g_TimeoutMonitorInitialized,
  200. FALSE,
  201. TRUE) )
  202. {
  203. //
  204. // Cancel the timeout monitor timer. If it fails, the monitor
  205. // is running. Wait for it to complete.
  206. //
  207. if ( !KeCancelTimer( &g_TimeoutMonitorTimer ) )
  208. {
  209. KeWaitForSingleObject(
  210. (PVOID)&g_TimeoutMonitorTerminationEvent,
  211. UserRequest,
  212. KernelMode,
  213. FALSE,
  214. NULL
  215. );
  216. }
  217. }
  218. UlTrace(TIMEOUTS, (
  219. "http!UlTerminateTimeoutMonitor: Done!\n"
  220. ));
  221. } // UlpTerminateTimeoutMonitor
  222. /***************************************************************************++
  223. Routine Description:
  224. Sets the global Timeout Monitor configuration information
  225. Arguments:
  226. pInfo pointer to HTTP_CONTROL_CHANNEL_TIMEOUT_LIMIT structure
  227. --***************************************************************************/
  228. VOID
  229. UlSetTimeoutMonitorInformation(
  230. IN PHTTP_CONTROL_CHANNEL_TIMEOUT_LIMIT pInfo
  231. )
  232. {
  233. LONGLONG localValue;
  234. LONGLONG newValue;
  235. LONGLONG originalValue;
  236. //
  237. // Sanity check.
  238. //
  239. PAGED_CODE();
  240. ASSERT( pInfo );
  241. UlTrace(TIMEOUTS, (
  242. "http!UlSetTimeoutMonitorInformation:\n"
  243. " ConnectionTimeout: %d\n"
  244. " HeaderWaitTimeout: %d\n"
  245. " MinFileKbSec: %d\n",
  246. pInfo->ConnectionTimeout,
  247. pInfo->HeaderWaitTimeout,
  248. pInfo->MinFileKbSec
  249. ));
  250. if ( pInfo->ConnectionTimeout )
  251. {
  252. UlInterlockedExchange64(
  253. &g_TM_ConnectionTimeout,
  254. (LONGLONG)(pInfo->ConnectionTimeout * C_NS_TICKS_PER_SEC)
  255. );
  256. }
  257. if ( pInfo->HeaderWaitTimeout )
  258. {
  259. UlInterlockedExchange64(
  260. &g_TM_HeaderWaitTimeout,
  261. (LONGLONG)(pInfo->HeaderWaitTimeout * C_NS_TICKS_PER_SEC)
  262. );
  263. }
  264. if ( pInfo->MinFileKbSec )
  265. {
  266. InterlockedExchange( (PLONG)&g_TM_MinKBSecDivisor, pInfo->MinFileKbSec );
  267. }
  268. } // UlSetTimeoutMonitorInformation
  269. /***************************************************************************++
  270. Routine Description:
  271. Sets up a timer event to fire after the polling interval has expired.
  272. Arguments:
  273. (none)
  274. --***************************************************************************/
  275. VOID
  276. UlpSetTimeoutMonitorTimer(
  277. VOID
  278. )
  279. {
  280. LARGE_INTEGER TimeoutMonitorInterval;
  281. ASSERT( TRUE == g_TimeoutMonitorInitialized );
  282. UlTrace(TIMEOUTS, (
  283. "http!UlpSetTimeoutMonitorTimer\n"
  284. ));
  285. //
  286. // Don't want to execute this more often than few seconds.
  287. // In particular, do not want to execute this every 0 seconds, as the
  288. // machine will become completely unresponsive.
  289. //
  290. //
  291. // negative numbers mean relative time
  292. //
  293. TimeoutMonitorInterval.QuadPart = -DEFAULT_POLLING_INTERVAL;
  294. KeSetTimer(
  295. &g_TimeoutMonitorTimer,
  296. TimeoutMonitorInterval,
  297. &g_TimeoutMonitorDpc
  298. );
  299. }
  300. /***************************************************************************++
  301. Routine Description:
  302. Dispatch routine called by the timer event that queues up the Timeout
  303. Montior
  304. Arguments:
  305. (all ignored)
  306. --***************************************************************************/
  307. VOID
  308. UlpTimeoutMonitorDpcRoutine(
  309. IN PKDPC Dpc,
  310. IN PVOID DeferredContext,
  311. IN PVOID SystemArgument1,
  312. IN PVOID SystemArgument2
  313. )
  314. {
  315. if( g_TimeoutMonitorInitialized )
  316. {
  317. //
  318. // Do that timeout monitor thang.
  319. //
  320. UL_QUEUE_WORK_ITEM(
  321. &g_TimeoutMonitorWorkItem,
  322. &UlpTimeoutMonitorWorker
  323. );
  324. }
  325. } // UlpTimeoutMonitorDpcRoutine
  326. /***************************************************************************++
  327. Routine Description:
  328. Timeout Monitor thread
  329. Arguments:
  330. pWorkItem (ignored)
  331. --***************************************************************************/
  332. VOID
  333. UlpTimeoutMonitorWorker(
  334. IN PUL_WORK_ITEM pWorkItem
  335. )
  336. {
  337. ULONG TimeoutMonitorEntriesSeen;
  338. LARGE_INTEGER Now;
  339. //
  340. // sanity check
  341. //
  342. PAGED_CODE();
  343. UlTrace(TIMEOUTS, (
  344. "http!UlpTimeoutMonitorWorker\n"
  345. ));
  346. //
  347. // Check for things to expire now.
  348. //
  349. UlpTimeoutCheckExpiry();
  350. UlTrace(TIMEOUTS, (
  351. "http!UlpTimeoutMonitorWorker: g_TimerWheelCurrentSlot is now %d\n",
  352. g_TimerWheelCurrentSlot
  353. ));
  354. if ( g_TimeoutMonitorInitialized )
  355. {
  356. //
  357. // Reschedule ourselves
  358. //
  359. UlpSetTimeoutMonitorTimer();
  360. }
  361. else
  362. {
  363. //
  364. // Signal shutdown event
  365. //
  366. KeSetEvent(
  367. &g_TimeoutMonitorTerminationEvent,
  368. 0,
  369. FALSE
  370. );
  371. }
  372. } // UlpTimeoutMonitor
  373. /***************************************************************************++
  374. Routine Description:
  375. Walks a given timeout watch list looking for items that should be expired
  376. Returns:
  377. Count of connections remaining on list (after all expired connections removed)
  378. Notes:
  379. Possible Issue: Since we use the system time to check to see if something
  380. should be expired, it's possible we will mis-expire some connections if the
  381. system time is set forward.
  382. Similarly, if the clock is set backward, we may not expire connections as
  383. expected.
  384. --***************************************************************************/
  385. ULONG
  386. UlpTimeoutCheckExpiry(
  387. VOID
  388. )
  389. {
  390. LARGE_INTEGER Now;
  391. KIRQL OldIrql;
  392. PLIST_ENTRY pEntry;
  393. PLIST_ENTRY pHead;
  394. PUL_HTTP_CONNECTION pHttpConn;
  395. PUL_TIMEOUT_INFO_ENTRY pInfo;
  396. LIST_ENTRY ZombieList;
  397. ULONG Entries;
  398. USHORT Limit;
  399. USHORT CurrentSlot;
  400. PAGED_CODE();
  401. //
  402. // Init zombie list
  403. //
  404. InitializeListHead(&ZombieList);
  405. //
  406. // Get current time
  407. //
  408. KeQuerySystemTime(&Now);
  409. Limit = UlpSystemTimeToTimerWheelSlot( Now.QuadPart );
  410. ASSERT( TIMER_OFF_SLOT != Limit );
  411. //
  412. // Lock Timer Wheel(c)
  413. //
  414. UlAcquireSpinLock(
  415. &g_TimerWheelMutex,
  416. &OldIrql
  417. );
  418. CurrentSlot = g_TimerWheelCurrentSlot;
  419. //
  420. // Walk the slots up until Limit
  421. //
  422. Entries = 0;
  423. while ( CurrentSlot != Limit )
  424. {
  425. pHead = &(g_TimerWheel[CurrentSlot]);
  426. pEntry = pHead->Flink;
  427. ASSERT( pEntry );
  428. //
  429. // Walk this slot's queue
  430. //
  431. while ( pEntry != pHead )
  432. {
  433. pInfo = CONTAINING_RECORD(
  434. pEntry,
  435. UL_TIMEOUT_INFO_ENTRY,
  436. QueueEntry
  437. );
  438. ASSERT( MmIsAddressValid(pInfo) );
  439. pHttpConn = CONTAINING_RECORD(
  440. pInfo,
  441. UL_HTTP_CONNECTION,
  442. TimeoutInfo
  443. );
  444. ASSERT( (pHttpConn != NULL) && \
  445. (pHttpConn->Signature == UL_HTTP_CONNECTION_POOL_TAG) );
  446. //
  447. // go to next node (in case we remove the current one from the list)
  448. //
  449. pEntry = pEntry->Flink;
  450. Entries++;
  451. if (0 == pHttpConn->RefCount)
  452. {
  453. //
  454. // If the ref-count has gone to zero, the httpconn will be
  455. // cleaned up soon; ignore this item and let the cleanup
  456. // do its job.
  457. //
  458. Entries--;
  459. continue; // inner while loop
  460. }
  461. //
  462. // See if we should move this entry to a different slot
  463. //
  464. if ( pInfo->SlotEntry != CurrentSlot )
  465. {
  466. ASSERT( IS_VALID_TIMER_WHEEL_SLOT(pInfo->SlotEntry) );
  467. ASSERT( pInfo->QueueEntry.Flink != NULL );
  468. //
  469. // Move to correct slot
  470. //
  471. RemoveEntryList(
  472. &pInfo->QueueEntry
  473. );
  474. InsertTailList(
  475. &(g_TimerWheel[pInfo->SlotEntry]),
  476. &pInfo->QueueEntry
  477. );
  478. Entries--;
  479. continue; // inner while loop
  480. }
  481. //
  482. // See if we should expire this connection
  483. //
  484. UlAcquireSpinLockAtDpcLevel( &pInfo->Lock );
  485. if ( pInfo->CurrentExpiry < Now.QuadPart )
  486. {
  487. UlTrace(TIMEOUTS, (
  488. "http!UlpTimeoutCheckExpiry: pInfo %p expired because %s timer\n",
  489. pInfo,
  490. g_aTimeoutTimerNames[pInfo->CurrentTimer]
  491. ));
  492. //
  493. // Expired. Remove entry from list & move to Zombie list
  494. //
  495. RemoveEntryList(
  496. &pInfo->QueueEntry
  497. );
  498. pInfo->QueueEntry.Flink = NULL;
  499. InsertTailList(
  500. &ZombieList,
  501. &pInfo->ZombieEntry
  502. );
  503. //
  504. // Add ref the pHttpConn to prevent it being killed before we
  505. // can kill it ourselves. (zombifying)
  506. //
  507. UL_REFERENCE_HTTP_CONNECTION(pHttpConn);
  508. }
  509. UlReleaseSpinLockFromDpcLevel( &pInfo->Lock );
  510. } // Walk slot queue
  511. CurrentSlot = ((CurrentSlot + 1) % TIMER_WHEEL_SLOTS);
  512. } // ( CurrentSlot != Limit )
  513. g_TimerWheelCurrentSlot = Limit;
  514. UlReleaseSpinLock(
  515. &g_TimerWheelMutex,
  516. OldIrql
  517. );
  518. //
  519. // remove entries on zombie list
  520. //
  521. if ( !IsListEmpty(&ZombieList) )
  522. {
  523. pEntry = ZombieList.Flink;
  524. while ( &ZombieList != pEntry )
  525. {
  526. pInfo = CONTAINING_RECORD(
  527. pEntry,
  528. UL_TIMEOUT_INFO_ENTRY,
  529. ZombieEntry
  530. );
  531. ASSERT( MmIsAddressValid(pInfo) );
  532. pHttpConn = CONTAINING_RECORD(
  533. pInfo,
  534. UL_HTTP_CONNECTION,
  535. TimeoutInfo
  536. );
  537. ASSERT( UL_IS_VALID_HTTP_CONNECTION(pHttpConn) );
  538. pEntry = pEntry->Flink;
  539. UlCloseConnection(pHttpConn->pConnection, TRUE, NULL, NULL);
  540. //
  541. // Remove the reference we added when zombifying
  542. //
  543. UL_DEREFERENCE_HTTP_CONNECTION(pHttpConn);
  544. Entries--;
  545. }
  546. }
  547. return Entries;
  548. } // UlpTimeoutCheckExpiry
  549. //
  550. // New Timer Wheel(c) primatives
  551. //
  552. /***************************************************************************++
  553. Routine Description:
  554. Arguments:
  555. --***************************************************************************/
  556. VOID
  557. UlInitializeConnectionTimerInfo(
  558. PUL_TIMEOUT_INFO_ENTRY pInfo
  559. )
  560. {
  561. LARGE_INTEGER Now;
  562. int i;
  563. ASSERT( TRUE == g_TimeoutMonitorInitialized );
  564. //
  565. // Get current time
  566. //
  567. KeQuerySystemTime(&Now);
  568. //
  569. // Init Lock
  570. //
  571. UlInitializeSpinLock( &pInfo->Lock, "TimeoutInfoLock" );
  572. //
  573. // Timer state
  574. //
  575. ASSERT( 0 == TimerConnectionIdle );
  576. pInfo->Timers[TimerConnectionIdle] = TIMER_WHEEL_TICKS(Now.QuadPart + g_TM_ConnectionTimeout);
  577. for ( i = 1; i < TimerMaximumTimer; i++ )
  578. {
  579. pInfo->Timers[i] = TIMER_OFF_TICK;
  580. }
  581. pInfo->CurrentTimer = TimerConnectionIdle;
  582. pInfo->CurrentExpiry = TIMER_WHEEL_TICKS(Now.QuadPart + g_TM_ConnectionTimeout);
  583. pInfo->MinKBSecSystemTime = TIMER_OFF_SYSTIME;
  584. pInfo->ConnectionTimeoutValue = g_TM_ConnectionTimeout;
  585. //
  586. // Wheel state
  587. //
  588. pInfo->SlotEntry = UlpTimerWheelTicksToTimerWheelSlot( pInfo->CurrentExpiry );
  589. UlpTimeoutInsertTimerWheelEntry(pInfo);
  590. } // UlInitializeConnectionTimerInfo
  591. /***************************************************************************++
  592. Routine Description:
  593. Arguments:
  594. --***************************************************************************/
  595. VOID
  596. UlpTimeoutInsertTimerWheelEntry(
  597. PUL_TIMEOUT_INFO_ENTRY pInfo
  598. )
  599. {
  600. KIRQL OldIrql;
  601. ASSERT( NULL != pInfo );
  602. ASSERT( TRUE == g_TimeoutMonitorInitialized );
  603. ASSERT( IS_VALID_TIMER_WHEEL_SLOT(pInfo->SlotEntry) );
  604. UlTrace(TIMEOUTS, (
  605. "http!UlTimeoutInsertTimerWheelEntry: pInfo %p Slot %d\n",
  606. pInfo,
  607. pInfo->SlotEntry
  608. ));
  609. UlAcquireSpinLock(
  610. &g_TimerWheelMutex,
  611. &OldIrql
  612. );
  613. InsertTailList(
  614. &(g_TimerWheel[pInfo->SlotEntry]),
  615. &pInfo->QueueEntry
  616. );
  617. UlReleaseSpinLock(
  618. &g_TimerWheelMutex,
  619. OldIrql
  620. );
  621. } // UlTimeoutInsertTimerWheel
  622. /***************************************************************************++
  623. Routine Description:
  624. Arguments:
  625. --***************************************************************************/
  626. VOID
  627. UlTimeoutRemoveTimerWheelEntry(
  628. PUL_TIMEOUT_INFO_ENTRY pInfo
  629. )
  630. {
  631. KIRQL OldIrql;
  632. ASSERT( NULL != pInfo );
  633. ASSERT( !IsListEmpty(&pInfo->QueueEntry) );
  634. UlTrace(TIMEOUTS, (
  635. "http!UlTimeoutRemoveTimerWheelEntry: pInfo %p\n",
  636. pInfo
  637. ));
  638. UlAcquireSpinLock(
  639. &g_TimerWheelMutex,
  640. &OldIrql
  641. );
  642. if (pInfo->QueueEntry.Flink != NULL)
  643. {
  644. RemoveEntryList(
  645. &pInfo->QueueEntry
  646. );
  647. pInfo->QueueEntry.Flink = NULL;
  648. }
  649. UlReleaseSpinLock(
  650. &g_TimerWheelMutex,
  651. OldIrql
  652. );
  653. } // UlTimeoutRemoveTimerWheelEntry
  654. /***************************************************************************++
  655. Routine Description:
  656. Set the per Site Connection Timeout Value override
  657. Arguments:
  658. pInfo Timeout info block
  659. TimeoutValue Override value
  660. --***************************************************************************/
  661. VOID
  662. UlSetPerSiteConnectionTimeoutValue(
  663. PUL_TIMEOUT_INFO_ENTRY pInfo,
  664. LONGLONG TimeoutValue
  665. )
  666. {
  667. ASSERT( NULL != pInfo );
  668. ASSERT( 0L != TimeoutValue );
  669. PAGED_CODE();
  670. UlTrace(TIMEOUTS, (
  671. "http!UlSetPerSiteConnectionTimeoutValue: pInfo %p TimeoutValue %I64X.\n",
  672. pInfo,
  673. TimeoutValue
  674. ));
  675. ExInterlockedCompareExchange64(
  676. &pInfo->ConnectionTimeoutValue, // Destination
  677. &TimeoutValue, // Exchange
  678. &pInfo->ConnectionTimeoutValue, // Comperand
  679. &pInfo->Lock.KSpinLock // Lock
  680. );
  681. } // UlSetPerSiteConnectionTimeoutValue
  682. /***************************************************************************++
  683. Routine Description:
  684. Starts a given timer in the timer info block.
  685. Arguments:
  686. pInfo Timer info block
  687. Timer Timer to set
  688. --***************************************************************************/
  689. VOID
  690. UlSetConnectionTimer(
  691. PUL_TIMEOUT_INFO_ENTRY pInfo,
  692. CONNECTION_TIMEOUT_TIMER Timer
  693. )
  694. {
  695. LARGE_INTEGER Now;
  696. ASSERT( NULL != pInfo );
  697. ASSERT( IS_VALID_TIMEOUT_TIMER(Timer) );
  698. ASSERT( UlDbgSpinLockOwned( &pInfo->Lock ) );
  699. UlTrace(TIMEOUTS, (
  700. "http!UlSetConnectionTimer: pInfo %p Timer %s\n",
  701. pInfo,
  702. g_aTimeoutTimerNames[Timer]
  703. ));
  704. //
  705. // Get current time
  706. //
  707. KeQuerySystemTime(&Now);
  708. //
  709. // Set timer to apropriate value
  710. //
  711. switch ( Timer )
  712. {
  713. case TimerConnectionIdle:
  714. case TimerEntityBody:
  715. case TimerResponse:
  716. // all can be handled with the same timeout value
  717. pInfo->Timers[Timer] = TIMER_WHEEL_TICKS(Now.QuadPart + pInfo->ConnectionTimeoutValue);
  718. break;
  719. case TimerHeaderWait:
  720. pInfo->Timers[TimerHeaderWait] = TIMER_WHEEL_TICKS(Now.QuadPart + g_TM_HeaderWaitTimeout);
  721. break;
  722. // NOTE: TimerMinKBSec is handled in UlSetMinKBSecTimer()
  723. default:
  724. UlTrace(TIMEOUTS, ( "http!UlSetConnectionTimer: Bad Timer! (%d)\n", Timer ));
  725. ASSERT( FALSE );
  726. }
  727. } // UlSetConnectionTimer
  728. /***************************************************************************++
  729. Routine Description:
  730. Turns on the MinKBSec timer, adds the number of secs given the minimum
  731. bandwidth specified.
  732. Arguments:
  733. pInfo Timer info block
  734. BytesToSend Bytes to be sent
  735. --***************************************************************************/
  736. VOID
  737. UlSetMinKBSecTimer(
  738. PUL_TIMEOUT_INFO_ENTRY pInfo,
  739. LONGLONG BytesToSend
  740. )
  741. {
  742. LONGLONG XmitTicks;
  743. KIRQL OldIrql;
  744. ULONG NewTick;
  745. BOOLEAN bCallEvaluate = FALSE;
  746. ASSERT( NULL != pInfo );
  747. UlTrace(TIMEOUTS, (
  748. "http!UlSetMinKBSecTimer: pInfo %p BytesToSend %ld\n",
  749. pInfo,
  750. BytesToSend
  751. ));
  752. if ( g_TM_MinKBSecDivisor )
  753. {
  754. if ( 0L != BytesToSend )
  755. {
  756. //
  757. // Calculate the estimated time required to send BytesToSend
  758. //
  759. XmitTicks = BytesToSend / g_TM_MinKBSecDivisor;
  760. if (0 == XmitTicks)
  761. {
  762. XmitTicks = C_NS_TICKS_PER_SEC;
  763. }
  764. else
  765. {
  766. XmitTicks *= C_NS_TICKS_PER_SEC;
  767. }
  768. UlAcquireSpinLock(
  769. &pInfo->Lock,
  770. &OldIrql
  771. );
  772. if ( TIMER_OFF_SYSTIME == pInfo->MinKBSecSystemTime )
  773. {
  774. LARGE_INTEGER Now;
  775. //
  776. // Get current time
  777. //
  778. KeQuerySystemTime(&Now);
  779. pInfo->MinKBSecSystemTime = (Now.QuadPart + XmitTicks);
  780. }
  781. else
  782. {
  783. pInfo->MinKBSecSystemTime += XmitTicks;
  784. }
  785. NewTick = TIMER_WHEEL_TICKS(pInfo->MinKBSecSystemTime);
  786. if ( NewTick != pInfo->Timers[TimerMinKBSec] )
  787. {
  788. bCallEvaluate = TRUE;
  789. pInfo->Timers[TimerMinKBSec] = NewTick;
  790. }
  791. UlReleaseSpinLock(
  792. &pInfo->Lock,
  793. OldIrql
  794. );
  795. if ( TRUE == bCallEvaluate )
  796. {
  797. UlEvaluateTimerState(pInfo);
  798. }
  799. }
  800. }
  801. } // UlSetMinKBSecTimer
  802. /***************************************************************************++
  803. Routine Description:
  804. Turns off a given timer in the timer info block.
  805. Arguments:
  806. pInfo Timer info block
  807. Timer Timer to reset
  808. --***************************************************************************/
  809. VOID
  810. UlResetConnectionTimer(
  811. PUL_TIMEOUT_INFO_ENTRY pInfo,
  812. CONNECTION_TIMEOUT_TIMER Timer
  813. )
  814. {
  815. ASSERT( NULL != pInfo );
  816. ASSERT( IS_VALID_TIMEOUT_TIMER(Timer) );
  817. ASSERT( UlDbgSpinLockOwned( &pInfo->Lock ) );
  818. UlTrace(TIMEOUTS, (
  819. "http!UlResetConnectionTimer: pInfo %p Timer %s\n",
  820. pInfo,
  821. g_aTimeoutTimerNames[Timer]
  822. ));
  823. //
  824. // Turn off timer
  825. //
  826. pInfo->Timers[Timer] = TIMER_OFF_TICK;
  827. if (TimerMinKBSec == Timer)
  828. {
  829. pInfo->MinKBSecSystemTime = TIMER_OFF_SYSTIME;
  830. }
  831. } // UlResetConnectionTimer
  832. /***************************************************************************++
  833. Routine Description:
  834. Turns off all timers
  835. Arguments:
  836. pInfo Timer info block
  837. --***************************************************************************/
  838. VOID
  839. UlResetAllConnectionTimers(
  840. PUL_TIMEOUT_INFO_ENTRY pInfo
  841. )
  842. {
  843. int i;
  844. ASSERT( NULL != pInfo );
  845. ASSERT( UlDbgSpinLockOwned( &pInfo->Lock ) );
  846. for ( i = 0; i < TimerMaximumTimer; i++ )
  847. {
  848. pInfo->Timers[i] = TIMER_OFF_TICK;
  849. }
  850. pInfo->CurrentTimer = TimerConnectionIdle;
  851. pInfo->CurrentExpiry = TIMER_OFF_TICK;
  852. pInfo->MinKBSecSystemTime = TIMER_OFF_SYSTIME;
  853. }
  854. //
  855. // Private functions
  856. //
  857. /***************************************************************************++
  858. Routine Description:
  859. Arguments:
  860. --***************************************************************************/
  861. VOID
  862. UlEvaluateTimerState(
  863. PUL_TIMEOUT_INFO_ENTRY pInfo
  864. )
  865. {
  866. int i;
  867. ULONG MinTimeout = TIMER_OFF_TICK;
  868. CONNECTION_TIMEOUT_TIMER MinTimeoutTimer = TimerConnectionIdle;
  869. ASSERT( NULL != pInfo );
  870. ASSERT( !UlDbgSpinLockOwned( &pInfo->Lock ) );
  871. for ( i = 0; i < TimerMaximumTimer; i++ )
  872. {
  873. if (pInfo->Timers[i] < MinTimeout)
  874. {
  875. MinTimeout = pInfo->Timers[i];
  876. MinTimeoutTimer = (CONNECTION_TIMEOUT_TIMER) i;
  877. }
  878. }
  879. //
  880. // If we've found a different expiry time, update expiry state.
  881. //
  882. if (pInfo->CurrentExpiry != MinTimeout)
  883. {
  884. KIRQL OldIrql;
  885. #if DBG
  886. LARGE_INTEGER Now;
  887. KeQuerySystemTime(&Now);
  888. ASSERT( MinTimeout >= TIMER_WHEEL_TICKS(Now.QuadPart) );
  889. #endif // DBG
  890. //
  891. // Calculate new slot
  892. //
  893. InterlockedExchange(
  894. (LONG *) &pInfo->SlotEntry,
  895. UlpTimerWheelTicksToTimerWheelSlot(MinTimeout)
  896. );
  897. //
  898. // Move to new slot if necessary
  899. //
  900. if ( (pInfo->SlotEntry != TIMER_OFF_SLOT) && (MinTimeout < pInfo->CurrentExpiry) )
  901. {
  902. //
  903. // Only move if it's on the Wheel; If Flink is null, it's in
  904. // the process of being expired.
  905. //
  906. if ( NULL != pInfo->QueueEntry.Flink )
  907. {
  908. UlAcquireSpinLock(
  909. &g_TimerWheelMutex,
  910. &OldIrql
  911. );
  912. UlTrace(TIMEOUTS, (
  913. "http!UlEvaluateTimerInfo: pInfo %p: Moving to new slot %d\n",
  914. pInfo,
  915. pInfo->SlotEntry
  916. ));
  917. RemoveEntryList(
  918. &pInfo->QueueEntry
  919. );
  920. InsertTailList(
  921. &(g_TimerWheel[pInfo->SlotEntry]),
  922. &pInfo->QueueEntry
  923. );
  924. UlReleaseSpinLock(
  925. &g_TimerWheelMutex,
  926. OldIrql
  927. );
  928. }
  929. }
  930. //
  931. // Update timer wheel state
  932. //
  933. pInfo->CurrentExpiry = MinTimeout;
  934. pInfo->CurrentTimer = MinTimeoutTimer;
  935. }
  936. } // UlpEvaluateTimerState