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.

1470 lines
36 KiB

  1. /*++
  2. Copyright (c) 2001-2002 Microsoft Corporation
  3. Module Name:
  4. timeouts.c
  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 Resetting a timer will cause
  21. the 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 -- UlSetMinBytesPerSecondTimer
  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_MinBytesPerSecondDivisor; // 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 in httptypes.h
  77. //
  78. CHAR *g_aTimeoutTimerNames[] = {
  79. "ConnectionIdle", // TimerConnectionIdle
  80. "HeaderWait", // TimerHeaderWait
  81. "MinBytesPerSecond", // TimerMinBytesPerSecond
  82. "EntityBody", // TimerEntityBody
  83. "Response", // TimerResponse
  84. "AppPool", // TimerAppPool
  85. };
  86. //
  87. // Timer Wheel(c)
  88. //
  89. static LIST_ENTRY g_TimerWheel[TIMER_WHEEL_SLOTS+1]; // TODO: alloc on its own page.
  90. static UL_SPIN_LOCK g_TimerWheelMutex;
  91. static USHORT g_TimerWheelCurrentSlot;
  92. //
  93. // Public functions.
  94. //
  95. /***************************************************************************++
  96. Routine Description:
  97. Initializes the Timeout Monitor and kicks off the first polling interval
  98. Arguments:
  99. (none)
  100. --***************************************************************************/
  101. VOID
  102. UlInitializeTimeoutMonitor(
  103. VOID
  104. )
  105. {
  106. int i;
  107. LARGE_INTEGER Now;
  108. //
  109. // Sanity check.
  110. //
  111. PAGED_CODE();
  112. UlTrace(TIMEOUTS, (
  113. "http!UlInitializeTimeoutMonitor\n"
  114. ));
  115. ASSERT( FALSE == g_TimeoutMonitorInitialized );
  116. //
  117. // Set default configuration information.
  118. //
  119. g_TM_ConnectionTimeout = 2 * 60 * C_NS_TICKS_PER_SEC; // 2 min
  120. g_TM_HeaderWaitTimeout = 2 * 60 * C_NS_TICKS_PER_SEC; // 2 min
  121. g_TM_MinBytesPerSecondDivisor = 150; // 150 == 1200 baud
  122. //
  123. // Init Timer Wheel(c) state
  124. //
  125. //
  126. // Set current slot
  127. //
  128. KeQuerySystemTime(&Now);
  129. g_TimerWheelCurrentSlot = UlpSystemTimeToTimerWheelSlot(Now.QuadPart);
  130. //
  131. // Init Timer Wheel(c) slots & mutex
  132. //
  133. ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); // InitializeListHead requirement
  134. for ( i = 0; i < TIMER_WHEEL_SLOTS ; i++ )
  135. {
  136. InitializeListHead( &(g_TimerWheel[i]) );
  137. }
  138. InitializeListHead( &(g_TimerWheel[TIMER_OFF_SLOT]) );
  139. UlInitializeSpinLock( &(g_TimerWheelMutex), "TimeoutMonitor" );
  140. UlInitializeWorkItem(&g_TimeoutMonitorWorkItem);
  141. //
  142. // Init DPC object & set DPC routine
  143. //
  144. KeInitializeDpc(
  145. &g_TimeoutMonitorDpc, // DPC object
  146. &UlpTimeoutMonitorDpcRoutine, // DPC routine
  147. NULL // context
  148. );
  149. KeInitializeTimer(
  150. &g_TimeoutMonitorTimer
  151. );
  152. //
  153. // Event to control rescheduling of the timeout monitor timer
  154. //
  155. KeInitializeEvent(
  156. &g_TimeoutMonitorAddListEvent,
  157. NotificationEvent,
  158. TRUE
  159. );
  160. //
  161. // Initialize the termination event.
  162. //
  163. KeInitializeEvent(
  164. &g_TimeoutMonitorTerminationEvent,
  165. NotificationEvent,
  166. FALSE
  167. );
  168. //
  169. // Init done!
  170. //
  171. InterlockedExchange( &g_TimeoutMonitorInitialized, TRUE );
  172. //
  173. // Kick-off the first monitor sleep period
  174. //
  175. UlpSetTimeoutMonitorTimer();
  176. }
  177. /***************************************************************************++
  178. Routine Description:
  179. Terminate the Timeout Monitor, including any pending timer events.
  180. Arguments:
  181. (none)
  182. --***************************************************************************/
  183. VOID
  184. UlTerminateTimeoutMonitor(
  185. VOID
  186. )
  187. {
  188. //
  189. // Sanity check.
  190. //
  191. PAGED_CODE();
  192. UlTrace(TIMEOUTS, (
  193. "http!UlTerminateTimeoutMonitor\n"
  194. ));
  195. //
  196. // Clear the "initialized" flag. If the timeout monitor runs soon,
  197. // it will see this flag, set the termination event, and exit
  198. // quickly.
  199. //
  200. if ( TRUE == InterlockedCompareExchange(
  201. &g_TimeoutMonitorInitialized,
  202. FALSE,
  203. TRUE) )
  204. {
  205. //
  206. // Cancel the timeout monitor timer. If it fails, the monitor
  207. // is running. Wait for it to complete.
  208. //
  209. if ( !KeCancelTimer( &g_TimeoutMonitorTimer ) )
  210. {
  211. NTSTATUS Status;
  212. Status = KeWaitForSingleObject(
  213. (PVOID)&g_TimeoutMonitorTerminationEvent,
  214. UserRequest,
  215. KernelMode,
  216. FALSE,
  217. NULL
  218. );
  219. ASSERT( STATUS_SUCCESS == Status );
  220. }
  221. }
  222. UlTrace(TIMEOUTS, (
  223. "http!UlTerminateTimeoutMonitor: Done!\n"
  224. ));
  225. } // UlpTerminateTimeoutMonitor
  226. /***************************************************************************++
  227. Routine Description:
  228. Sets the global Timeout Monitor configuration information
  229. Arguments:
  230. pInfo pointer to HTTP_CONTROL_CHANNEL_TIMEOUT_LIMIT structure
  231. --***************************************************************************/
  232. VOID
  233. UlSetTimeoutMonitorInformation(
  234. IN PHTTP_CONTROL_CHANNEL_TIMEOUT_LIMIT pInfo
  235. )
  236. {
  237. //
  238. // Sanity check.
  239. //
  240. PAGED_CODE();
  241. ASSERT( pInfo );
  242. UlTrace(TIMEOUTS, (
  243. "http!UlSetTimeoutMonitorInformation:\n"
  244. " ConnectionTimeout: %d\n"
  245. " HeaderWaitTimeout: %d\n"
  246. " MinFileKbSec: %d\n",
  247. pInfo->ConnectionTimeout,
  248. pInfo->HeaderWaitTimeout,
  249. pInfo->MinFileKbSec
  250. ));
  251. if ( pInfo->ConnectionTimeout )
  252. {
  253. UlInterlockedExchange64(
  254. &g_TM_ConnectionTimeout,
  255. (LONGLONG)(pInfo->ConnectionTimeout * C_NS_TICKS_PER_SEC)
  256. );
  257. }
  258. if ( pInfo->HeaderWaitTimeout )
  259. {
  260. UlInterlockedExchange64(
  261. &g_TM_HeaderWaitTimeout,
  262. (LONGLONG)(pInfo->HeaderWaitTimeout * C_NS_TICKS_PER_SEC)
  263. );
  264. }
  265. //
  266. // Allow MinBytesPerSecond to be set to zero (for long running
  267. // transactions)
  268. //
  269. InterlockedExchange( (PLONG)&g_TM_MinBytesPerSecondDivisor, pInfo->MinFileKbSec );
  270. } // UlSetTimeoutMonitorInformation
  271. /***************************************************************************++
  272. Routine Description:
  273. Sets up a timer event to fire after the polling interval has expired.
  274. Arguments:
  275. (none)
  276. --***************************************************************************/
  277. VOID
  278. UlpSetTimeoutMonitorTimer(
  279. VOID
  280. )
  281. {
  282. LARGE_INTEGER TimeoutMonitorInterval;
  283. ASSERT( TRUE == g_TimeoutMonitorInitialized );
  284. UlTraceVerbose(TIMEOUTS, (
  285. "http!UlpSetTimeoutMonitorTimer\n"
  286. ));
  287. //
  288. // Don't want to execute this more often than few seconds.
  289. // In particular, do not want to execute this every 0 seconds, as the
  290. // machine will become completely unresponsive.
  291. //
  292. //
  293. // negative numbers mean relative time
  294. //
  295. TimeoutMonitorInterval.QuadPart = -DEFAULT_POLLING_INTERVAL;
  296. KeSetTimer(
  297. &g_TimeoutMonitorTimer,
  298. TimeoutMonitorInterval,
  299. &g_TimeoutMonitorDpc
  300. );
  301. }
  302. /***************************************************************************++
  303. Routine Description:
  304. Dispatch routine called by the timer event that queues up the Timeout
  305. Montior
  306. Arguments:
  307. (all ignored)
  308. --***************************************************************************/
  309. VOID
  310. UlpTimeoutMonitorDpcRoutine(
  311. IN PKDPC Dpc,
  312. IN PVOID DeferredContext,
  313. IN PVOID SystemArgument1,
  314. IN PVOID SystemArgument2
  315. )
  316. {
  317. UNREFERENCED_PARAMETER(Dpc);
  318. UNREFERENCED_PARAMETER(DeferredContext);
  319. UNREFERENCED_PARAMETER(SystemArgument1);
  320. UNREFERENCED_PARAMETER(SystemArgument2);
  321. if( g_TimeoutMonitorInitialized )
  322. {
  323. //
  324. // Do that timeout monitor thang.
  325. //
  326. UL_QUEUE_WORK_ITEM(
  327. &g_TimeoutMonitorWorkItem,
  328. &UlpTimeoutMonitorWorker
  329. );
  330. }
  331. } // UlpTimeoutMonitorDpcRoutine
  332. /***************************************************************************++
  333. Routine Description:
  334. Timeout Monitor thread
  335. Arguments:
  336. pWorkItem (ignored)
  337. --***************************************************************************/
  338. VOID
  339. UlpTimeoutMonitorWorker(
  340. IN PUL_WORK_ITEM pWorkItem
  341. )
  342. {
  343. UNREFERENCED_PARAMETER(pWorkItem);
  344. //
  345. // sanity check
  346. //
  347. PAGED_CODE();
  348. UlTraceVerbose(TIMEOUTS, (
  349. "http!UlpTimeoutMonitorWorker\n"
  350. ));
  351. //
  352. // Check for things to expire now.
  353. //
  354. UlpTimeoutCheckExpiry();
  355. UlTrace(TIMEOUTS, (
  356. "http!UlpTimeoutMonitorWorker: g_TimerWheelCurrentSlot is now %d\n",
  357. g_TimerWheelCurrentSlot
  358. ));
  359. if ( g_TimeoutMonitorInitialized )
  360. {
  361. //
  362. // Reschedule ourselves
  363. //
  364. UlpSetTimeoutMonitorTimer();
  365. }
  366. else
  367. {
  368. //
  369. // Signal shutdown event
  370. //
  371. KeSetEvent(
  372. &g_TimeoutMonitorTerminationEvent,
  373. 0,
  374. FALSE
  375. );
  376. }
  377. } // UlpTimeoutMonitor
  378. /***************************************************************************++
  379. Routine Description:
  380. Walks a given timeout watch list looking for items that should be expired
  381. Returns:
  382. Count of connections remaining on list (after all expired connections removed)
  383. Notes:
  384. Possible Issue: Since we use the system time to check to see if something
  385. should be expired, it's possible we will mis-expire some connections if the
  386. system time is set forward.
  387. Similarly, if the clock is set backward, we may not expire connections as
  388. expected.
  389. --***************************************************************************/
  390. ULONG
  391. UlpTimeoutCheckExpiry(
  392. VOID
  393. )
  394. {
  395. LARGE_INTEGER Now;
  396. KIRQL OldIrql;
  397. PLIST_ENTRY pEntry;
  398. PLIST_ENTRY pHead;
  399. PUL_HTTP_CONNECTION pHttpConn;
  400. PUL_TIMEOUT_INFO_ENTRY pInfo;
  401. LIST_ENTRY ZombieList;
  402. ULONG Entries;
  403. USHORT Limit;
  404. USHORT CurrentSlot;
  405. BOOLEAN bLowMemoryCondition;
  406. PAGED_CODE();
  407. //
  408. // Init zombie list
  409. //
  410. InitializeListHead(&ZombieList);
  411. //
  412. // Get current time
  413. //
  414. KeQuerySystemTime(&Now);
  415. Limit = UlpSystemTimeToTimerWheelSlot( Now.QuadPart );
  416. ASSERT( TIMER_OFF_SLOT != Limit );
  417. //
  418. // Must check low memory condition outside of spin lock
  419. //
  420. bLowMemoryCondition = UlIsLowNPPCondition();
  421. //
  422. // Lock Timer Wheel(c)
  423. //
  424. UlAcquireSpinLock(
  425. &g_TimerWheelMutex,
  426. &OldIrql
  427. );
  428. CurrentSlot = g_TimerWheelCurrentSlot;
  429. //
  430. // Walk the slots up until Limit
  431. //
  432. Entries = 0;
  433. while ( CurrentSlot != Limit )
  434. {
  435. pHead = &(g_TimerWheel[CurrentSlot]);
  436. pEntry = pHead->Flink;
  437. ASSERT( pEntry );
  438. //
  439. // Walk this slot's queue
  440. //
  441. while ( pEntry != pHead )
  442. {
  443. pInfo = CONTAINING_RECORD(
  444. pEntry,
  445. UL_TIMEOUT_INFO_ENTRY,
  446. QueueEntry
  447. );
  448. ASSERT( MmIsAddressValid(pInfo) );
  449. pHttpConn = CONTAINING_RECORD(
  450. pInfo,
  451. UL_HTTP_CONNECTION,
  452. TimeoutInfo
  453. );
  454. ASSERT( (pHttpConn != NULL) && \
  455. (pHttpConn->Signature == UL_HTTP_CONNECTION_POOL_TAG) );
  456. //
  457. // go to next node (in case we remove the current one from the list)
  458. //
  459. pEntry = pEntry->Flink;
  460. Entries++;
  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 connection will go away soon.
  483. // Add pseudo-ref to the pHttpConn to prevent it being killed
  484. // before we can kill it ourselves. (zombifying)
  485. //
  486. if (1 == InterlockedIncrement(&pHttpConn->RefCount))
  487. {
  488. //
  489. // If the ref-count has gone to zero, the httpconn will be
  490. // cleaned up soon; ignore this item and let the cleanup
  491. // do its job.
  492. //
  493. InterlockedDecrement(&pHttpConn->RefCount);
  494. Entries--;
  495. continue; // inner while loop
  496. }
  497. //
  498. // If we are in this slot, we should expire this connection now.
  499. //
  500. WRITE_REF_TRACE_LOG2(
  501. g_pHttpConnectionTraceLog,
  502. pHttpConn->pConnection->pHttpTraceLog,
  503. REF_ACTION_EXPIRE_HTTP_CONNECTION,
  504. pHttpConn->RefCount,
  505. pHttpConn,
  506. __FILE__,
  507. __LINE__
  508. );
  509. UlTrace(TIMEOUTS, (
  510. "http!UlpTimeoutCheckExpiry: pInfo %p expired because %s timer\n",
  511. pInfo,
  512. g_aTimeoutTimerNames[pInfo->CurrentTimer]
  513. ));
  514. //
  515. // Expired. Remove entry from list & move to Zombie list
  516. //
  517. UlAcquireSpinLockAtDpcLevel( &pInfo->Lock );
  518. pInfo->Expired = TRUE;
  519. RemoveEntryList(
  520. &pInfo->QueueEntry
  521. );
  522. pInfo->QueueEntry.Flink = NULL;
  523. InsertTailList(
  524. &ZombieList,
  525. &pInfo->ZombieEntry
  526. );
  527. UlReleaseSpinLockFromDpcLevel( &pInfo->Lock );
  528. } // Walk slot queue
  529. CurrentSlot = ((CurrentSlot + 1) % TIMER_WHEEL_SLOTS);
  530. } // ( CurrentSlot != Limit )
  531. //
  532. // Low-memory check & cleanup here
  533. //
  534. if ( bLowMemoryCondition )
  535. {
  536. USHORT LowMemoryLimit;
  537. LONGLONG MaxTimeoutTime;
  538. MaxTimeoutTime = MAX( g_TM_HeaderWaitTimeout, g_TM_ConnectionTimeout );
  539. MaxTimeoutTime += Now.QuadPart;
  540. LowMemoryLimit = UlpSystemTimeToTimerWheelSlot( MaxTimeoutTime );
  541. ASSERT( TIMER_OFF_SLOT != LowMemoryLimit );
  542. //
  543. // Walk slots from Current to largest slot which could contain
  544. // a ConnectionIdle, HeaderWait or EntityBodyWait timer
  545. //
  546. while ( CurrentSlot != LowMemoryLimit )
  547. {
  548. pHead = &(g_TimerWheel[CurrentSlot]);
  549. pEntry = pHead->Flink;
  550. ASSERT( pEntry );
  551. //
  552. // Walk this slot's queue
  553. //
  554. while ( pEntry != pHead )
  555. {
  556. pInfo = CONTAINING_RECORD(
  557. pEntry,
  558. UL_TIMEOUT_INFO_ENTRY,
  559. QueueEntry
  560. );
  561. ASSERT( MmIsAddressValid(pInfo) );
  562. pHttpConn = CONTAINING_RECORD(
  563. pInfo,
  564. UL_HTTP_CONNECTION,
  565. TimeoutInfo
  566. );
  567. ASSERT( (pHttpConn != NULL) && \
  568. (pHttpConn->Signature == UL_HTTP_CONNECTION_POOL_TAG) );
  569. //
  570. // go to next node (in case we remove the current one from the list)
  571. //
  572. pEntry = pEntry->Flink;
  573. Entries++;
  574. //
  575. // See if connection will go away soon.
  576. // Add pseudo-ref to the pHttpConn to prevent it being killed
  577. // before we can kill it ourselves. (zombifying)
  578. //
  579. if (1 == InterlockedIncrement(&pHttpConn->RefCount))
  580. {
  581. //
  582. // If the ref-count has gone to zero, the httpconn will be
  583. // cleaned up soon; ignore this item and let the cleanup
  584. // do its job.
  585. //
  586. InterlockedDecrement(&pHttpConn->RefCount);
  587. Entries--;
  588. continue; // inner while loop
  589. }
  590. ASSERT(pHttpConn->RefCount > 0);
  591. if ( (pInfo->SlotEntry == CurrentSlot)
  592. && ((pInfo->CurrentTimer == TimerConnectionIdle)
  593. ||(pInfo->CurrentTimer == TimerHeaderWait)
  594. ||(pInfo->CurrentTimer == TimerAppPool)))
  595. {
  596. //
  597. // Expire connection that hasn't been sent up to user mode yet
  598. //
  599. WRITE_REF_TRACE_LOG2(
  600. g_pHttpConnectionTraceLog,
  601. pHttpConn->pConnection->pHttpTraceLog,
  602. REF_ACTION_EXPIRE_HTTP_CONNECTION,
  603. pHttpConn->RefCount,
  604. pHttpConn,
  605. __FILE__,
  606. __LINE__
  607. );
  608. UlTrace(TIMEOUTS, (
  609. "http!UlpTimeoutCheckExpiry: pInfo %p expired because"
  610. " of low memory condition. Timer [%s]\n",
  611. pInfo,
  612. g_aTimeoutTimerNames[pInfo->CurrentTimer]
  613. ));
  614. //
  615. // Expired. Remove entry from list & move to Zombie list
  616. //
  617. UlAcquireSpinLockAtDpcLevel( &pInfo->Lock );
  618. pInfo->Expired = TRUE;
  619. RemoveEntryList(
  620. &pInfo->QueueEntry
  621. );
  622. pInfo->QueueEntry.Flink = NULL;
  623. InsertTailList(
  624. &ZombieList,
  625. &pInfo->ZombieEntry
  626. );
  627. UlReleaseSpinLockFromDpcLevel( &pInfo->Lock );
  628. }
  629. else
  630. {
  631. // remove pseudo-reference added above
  632. UL_DEREFERENCE_HTTP_CONNECTION(pHttpConn);
  633. }
  634. }
  635. CurrentSlot = ((CurrentSlot + 1) % TIMER_WHEEL_SLOTS);
  636. }
  637. } // low memory check
  638. g_TimerWheelCurrentSlot = Limit;
  639. UlReleaseSpinLock(
  640. &g_TimerWheelMutex,
  641. OldIrql
  642. );
  643. //
  644. // Remove entries on zombie list
  645. //
  646. while ( !IsListEmpty(&ZombieList) )
  647. {
  648. pEntry = RemoveHeadList( &ZombieList );
  649. pInfo = CONTAINING_RECORD(
  650. pEntry,
  651. UL_TIMEOUT_INFO_ENTRY,
  652. ZombieEntry
  653. );
  654. ASSERT( MmIsAddressValid(pInfo) );
  655. pHttpConn = CONTAINING_RECORD(
  656. pInfo,
  657. UL_HTTP_CONNECTION,
  658. TimeoutInfo
  659. );
  660. ASSERT( UL_IS_VALID_HTTP_CONNECTION(pHttpConn) );
  661. pEntry = pEntry->Flink;
  662. //
  663. // Close the conn and error log (if no one has done it yet)
  664. //
  665. UlAcquirePushLockExclusive(&pHttpConn->PushLock);
  666. UlErrorLog(pHttpConn,
  667. NULL,
  668. (PCHAR) TimeoutInfoTable[pInfo->CurrentTimer].pInfo,
  669. TimeoutInfoTable[pInfo->CurrentTimer].InfoSize,
  670. TRUE
  671. );
  672. UlReleasePushLockExclusive(&pHttpConn->PushLock);
  673. UlCloseConnection(
  674. pHttpConn->pConnection,
  675. TRUE,
  676. NULL,
  677. NULL
  678. );
  679. //
  680. // Remove the reference we added when zombifying
  681. //
  682. UL_DEREFERENCE_HTTP_CONNECTION(pHttpConn);
  683. Entries--;
  684. }
  685. return Entries;
  686. } // UlpTimeoutCheckExpiry
  687. //
  688. // New Timer Wheel(c) primatives
  689. //
  690. /***************************************************************************++
  691. Routine Description:
  692. Arguments:
  693. --***************************************************************************/
  694. VOID
  695. UlInitializeConnectionTimerInfo(
  696. PUL_TIMEOUT_INFO_ENTRY pInfo
  697. )
  698. {
  699. LARGE_INTEGER Now;
  700. int i;
  701. ASSERT( TRUE == g_TimeoutMonitorInitialized );
  702. //
  703. // Get current time
  704. //
  705. KeQuerySystemTime(&Now);
  706. //
  707. // Init Lock
  708. //
  709. UlInitializeSpinLock( &pInfo->Lock, "TimeoutInfoLock" );
  710. //
  711. // Timer state
  712. //
  713. ASSERT( 0 == TimerConnectionIdle );
  714. for ( i = 0; i < TimerMaximumTimer; i++ )
  715. {
  716. pInfo->Timers[i] = TIMER_OFF_TICK;
  717. }
  718. pInfo->CurrentTimer = TimerConnectionIdle;
  719. pInfo->Timers[TimerConnectionIdle] = TIMER_WHEEL_TICKS(Now.QuadPart + g_TM_ConnectionTimeout);
  720. pInfo->CurrentExpiry = pInfo->Timers[TimerConnectionIdle];
  721. pInfo->MinBytesPerSecondSystemTime = TIMER_OFF_SYSTIME;
  722. pInfo->Expired = FALSE;
  723. pInfo->SendCount = 0;
  724. pInfo->ConnectionTimeoutValue = g_TM_ConnectionTimeout;
  725. pInfo->BytesPerSecondDivisor = g_TM_MinBytesPerSecondDivisor;
  726. //
  727. // Wheel state
  728. //
  729. pInfo->SlotEntry = UlpTimerWheelTicksToTimerWheelSlot( pInfo->CurrentExpiry );
  730. UlpTimeoutInsertTimerWheelEntry(pInfo);
  731. } // UlInitializeConnectionTimerInfo
  732. /***************************************************************************++
  733. Routine Description:
  734. Arguments:
  735. --***************************************************************************/
  736. VOID
  737. UlpTimeoutInsertTimerWheelEntry(
  738. PUL_TIMEOUT_INFO_ENTRY pInfo
  739. )
  740. {
  741. KIRQL OldIrql;
  742. ASSERT( NULL != pInfo );
  743. ASSERT( TRUE == g_TimeoutMonitorInitialized );
  744. ASSERT( IS_VALID_TIMER_WHEEL_SLOT(pInfo->SlotEntry) );
  745. UlTrace(TIMEOUTS, (
  746. "http!UlTimeoutInsertTimerWheelEntry: pInfo %p Slot %d\n",
  747. pInfo,
  748. pInfo->SlotEntry
  749. ));
  750. UlAcquireSpinLock(
  751. &g_TimerWheelMutex,
  752. &OldIrql
  753. );
  754. InsertTailList(
  755. &(g_TimerWheel[pInfo->SlotEntry]),
  756. &pInfo->QueueEntry
  757. );
  758. UlReleaseSpinLock(
  759. &g_TimerWheelMutex,
  760. OldIrql
  761. );
  762. } // UlTimeoutInsertTimerWheel
  763. /***************************************************************************++
  764. Routine Description:
  765. Arguments:
  766. --***************************************************************************/
  767. VOID
  768. UlTimeoutRemoveTimerWheelEntry(
  769. PUL_TIMEOUT_INFO_ENTRY pInfo
  770. )
  771. {
  772. KIRQL OldIrql;
  773. ASSERT( NULL != pInfo );
  774. ASSERT( !IsListEmpty(&pInfo->QueueEntry) );
  775. UlTrace(TIMEOUTS, (
  776. "http!UlTimeoutRemoveTimerWheelEntry: pInfo %p\n",
  777. pInfo
  778. ));
  779. UlAcquireSpinLock(
  780. &g_TimerWheelMutex,
  781. &OldIrql
  782. );
  783. if (pInfo->QueueEntry.Flink != NULL)
  784. {
  785. RemoveEntryList(
  786. &pInfo->QueueEntry
  787. );
  788. pInfo->QueueEntry.Flink = NULL;
  789. }
  790. UlReleaseSpinLock(
  791. &g_TimerWheelMutex,
  792. OldIrql
  793. );
  794. } // UlTimeoutRemoveTimerWheelEntry
  795. /***************************************************************************++
  796. Routine Description:
  797. Set the per Site Connection Timeout Value override
  798. Arguments:
  799. pInfo Timeout info block
  800. TimeoutValue Override value
  801. --***************************************************************************/
  802. VOID
  803. UlSetPerSiteConnectionTimeoutValue(
  804. PUL_TIMEOUT_INFO_ENTRY pInfo,
  805. LONGLONG TimeoutValue
  806. )
  807. {
  808. ASSERT( NULL != pInfo );
  809. ASSERT( 0L != TimeoutValue );
  810. PAGED_CODE();
  811. UlTrace(TIMEOUTS, (
  812. "http!UlSetPerSiteConnectionTimeoutValue: pInfo %p TimeoutValue = %ld secs.\n",
  813. pInfo,
  814. (LONG) (TimeoutValue / C_NS_TICKS_PER_SEC)
  815. ));
  816. ExInterlockedCompareExchange64(
  817. &pInfo->ConnectionTimeoutValue, // Destination
  818. &TimeoutValue, // Exchange
  819. &pInfo->ConnectionTimeoutValue, // Comperand
  820. &pInfo->Lock.KSpinLock // Lock
  821. );
  822. } // UlSetPerSiteConnectionTimeoutValue
  823. /***************************************************************************++
  824. Routine Description:
  825. Starts a given timer in the timer info block.
  826. Arguments:
  827. pInfo Timer info block
  828. Timer Timer to set
  829. --***************************************************************************/
  830. VOID
  831. UlSetConnectionTimer(
  832. PUL_TIMEOUT_INFO_ENTRY pInfo,
  833. CONNECTION_TIMEOUT_TIMER Timer
  834. )
  835. {
  836. LARGE_INTEGER Now;
  837. ASSERT( NULL != pInfo );
  838. ASSERT( IS_VALID_TIMEOUT_TIMER(Timer) );
  839. ASSERT( UlDbgSpinLockOwned( &pInfo->Lock ) );
  840. //
  841. // Get current time
  842. //
  843. KeQuerySystemTime(&Now);
  844. //
  845. // Set timer to apropriate value
  846. //
  847. switch ( Timer )
  848. {
  849. case TimerConnectionIdle:
  850. case TimerEntityBody:
  851. case TimerResponse:
  852. case TimerAppPool:
  853. // all can be handled with the same timeout value
  854. UlTraceVerbose(TIMEOUTS, (
  855. "http!UlSetConnectionTimer: pInfo %p Timer %s, Timeout = %ld secs\n",
  856. pInfo,
  857. g_aTimeoutTimerNames[Timer],
  858. (LONG) (pInfo->ConnectionTimeoutValue / C_NS_TICKS_PER_SEC)
  859. ));
  860. pInfo->Timers[Timer]
  861. = TIMER_WHEEL_TICKS(Now.QuadPart + pInfo->ConnectionTimeoutValue);
  862. break;
  863. case TimerHeaderWait:
  864. UlTraceVerbose(TIMEOUTS, (
  865. "http!UlSetConnectionTimer: pInfo %p Timer %s, Timeout = %ld secs\n",
  866. pInfo,
  867. g_aTimeoutTimerNames[Timer],
  868. (LONG) (g_TM_HeaderWaitTimeout / C_NS_TICKS_PER_SEC)
  869. ));
  870. pInfo->Timers[TimerHeaderWait]
  871. = TIMER_WHEEL_TICKS(Now.QuadPart + g_TM_HeaderWaitTimeout);
  872. break;
  873. // NOTE: TimerMinBytesPerSecond is handled in UlSetMinBytesPerSecondTimer()
  874. default:
  875. UlTrace(TIMEOUTS, (
  876. "http!UlSetConnectionTimer: Bad Timer! (%d)\n",
  877. Timer
  878. ));
  879. ASSERT( !"Bad Timer" );
  880. }
  881. } // UlSetConnectionTimer
  882. /***************************************************************************++
  883. Routine Description:
  884. Turns on the MinBytesPerSecond timer, adds the number of secs given the minimum
  885. bandwidth specified.
  886. Arguments:
  887. pInfo - Timer info block
  888. BytesToSend - Bytes to be sent
  889. --***************************************************************************/
  890. VOID
  891. UlSetMinBytesPerSecondTimer(
  892. PUL_TIMEOUT_INFO_ENTRY pInfo,
  893. LONGLONG BytesToSend
  894. )
  895. {
  896. LONGLONG XmitTicks;
  897. KIRQL OldIrql;
  898. ULONG NewTick;
  899. BOOLEAN bCallEvaluate = FALSE;
  900. ASSERT( NULL != pInfo );
  901. if ( 0 == pInfo->BytesPerSecondDivisor )
  902. {
  903. UlTraceVerbose(TIMEOUTS, (
  904. "http!UlSetMinBytesPerSecondTimer: pInfo %p, disabled\n",
  905. pInfo
  906. ));
  907. return;
  908. }
  909. //
  910. // Calculate the estimated time required to send BytesToSend
  911. //
  912. XmitTicks = BytesToSend / pInfo->BytesPerSecondDivisor;
  913. if (0 == XmitTicks)
  914. {
  915. XmitTicks = C_NS_TICKS_PER_SEC;
  916. }
  917. else
  918. {
  919. XmitTicks *= C_NS_TICKS_PER_SEC;
  920. }
  921. UlAcquireSpinLock(
  922. &pInfo->Lock,
  923. &OldIrql
  924. );
  925. if ( TIMER_OFF_SYSTIME == pInfo->MinBytesPerSecondSystemTime )
  926. {
  927. LARGE_INTEGER Now;
  928. //
  929. // Get current time
  930. //
  931. KeQuerySystemTime(&Now);
  932. pInfo->MinBytesPerSecondSystemTime = (Now.QuadPart + XmitTicks);
  933. }
  934. else
  935. {
  936. pInfo->MinBytesPerSecondSystemTime += XmitTicks;
  937. }
  938. NewTick = TIMER_WHEEL_TICKS(pInfo->MinBytesPerSecondSystemTime);
  939. if ( NewTick != pInfo->Timers[TimerMinBytesPerSecond] )
  940. {
  941. bCallEvaluate = TRUE;
  942. pInfo->Timers[TimerMinBytesPerSecond] = NewTick;
  943. }
  944. pInfo->SendCount++;
  945. UlTraceVerbose(TIMEOUTS, (
  946. "http!UlSetMinBytesPerSecondTimer: pInfo %p BytesToSend %ld SendCount %d\n",
  947. pInfo,
  948. BytesToSend,
  949. pInfo->SendCount
  950. ));
  951. UlReleaseSpinLock(
  952. &pInfo->Lock,
  953. OldIrql
  954. );
  955. if ( TRUE == bCallEvaluate )
  956. {
  957. UlEvaluateTimerState(pInfo);
  958. }
  959. } // UlSetMinBytesPerSecondTimer
  960. /***************************************************************************++
  961. Routine Description:
  962. Turns off a given timer in the timer info block.
  963. Arguments:
  964. pInfo Timer info block
  965. Timer Timer to reset
  966. --***************************************************************************/
  967. VOID
  968. UlResetConnectionTimer(
  969. PUL_TIMEOUT_INFO_ENTRY pInfo,
  970. CONNECTION_TIMEOUT_TIMER Timer
  971. )
  972. {
  973. ASSERT( NULL != pInfo );
  974. ASSERT( IS_VALID_TIMEOUT_TIMER(Timer) );
  975. ASSERT( UlDbgSpinLockOwned( &pInfo->Lock ) );
  976. UlTraceVerbose(TIMEOUTS, (
  977. "http!UlResetConnectionTimer: pInfo %p Timer %s\n",
  978. pInfo,
  979. g_aTimeoutTimerNames[Timer]
  980. ));
  981. //
  982. // Turn off timer
  983. //
  984. // CODEWORK: handle case when MinBytes/Sec is disabled/enabled while
  985. // CODEWORK: timer is running.
  986. if ( TimerMinBytesPerSecond == Timer && pInfo->BytesPerSecondDivisor )
  987. {
  988. ASSERT( pInfo->SendCount > 0 );
  989. pInfo->SendCount--;
  990. if ( pInfo->SendCount )
  991. {
  992. // Do not reset timer if there are sends outstanding
  993. return;
  994. }
  995. }
  996. pInfo->Timers[Timer] = TIMER_OFF_TICK;
  997. if ( TimerMinBytesPerSecond == Timer && pInfo->BytesPerSecondDivisor )
  998. {
  999. pInfo->MinBytesPerSecondSystemTime = TIMER_OFF_SYSTIME;
  1000. }
  1001. } // UlResetConnectionTimer
  1002. //
  1003. // Private functions
  1004. //
  1005. /***************************************************************************++
  1006. Routine Description:
  1007. Evaluate which timer will expire first and move pInfo to a new
  1008. timer wheel slot if necessary
  1009. Arguments:
  1010. --***************************************************************************/
  1011. VOID
  1012. UlEvaluateTimerState(
  1013. PUL_TIMEOUT_INFO_ENTRY pInfo
  1014. )
  1015. {
  1016. int i;
  1017. ULONG MinTimeout = TIMER_OFF_TICK;
  1018. CONNECTION_TIMEOUT_TIMER MinTimeoutTimer = TimerConnectionIdle;
  1019. ASSERT( NULL != pInfo );
  1020. ASSERT( !UlDbgSpinLockOwned( &pInfo->Lock ) );
  1021. for ( i = 0; i < TimerMaximumTimer; i++ )
  1022. {
  1023. if (pInfo->Timers[i] < MinTimeout)
  1024. {
  1025. MinTimeout = pInfo->Timers[i];
  1026. MinTimeoutTimer = (CONNECTION_TIMEOUT_TIMER) i;
  1027. }
  1028. }
  1029. //
  1030. // If we've found a different expiry time, update expiry state.
  1031. //
  1032. if (pInfo->CurrentExpiry != MinTimeout)
  1033. {
  1034. KIRQL OldIrql;
  1035. USHORT NewSlot;
  1036. //
  1037. // Calculate new slot
  1038. //
  1039. NewSlot = UlpTimerWheelTicksToTimerWheelSlot(MinTimeout);
  1040. ASSERT(IS_VALID_TIMER_WHEEL_SLOT(NewSlot));
  1041. InterlockedExchange((LONG *) &pInfo->SlotEntry, NewSlot);
  1042. //
  1043. // Move to new slot if necessary
  1044. //
  1045. if ( (NewSlot != TIMER_OFF_SLOT) && (MinTimeout < pInfo->CurrentExpiry) )
  1046. {
  1047. //
  1048. // Only move if it's on the Wheel; If Flink is null, it's in
  1049. // the process of being expired.
  1050. //
  1051. UlAcquireSpinLock(
  1052. &g_TimerWheelMutex,
  1053. &OldIrql
  1054. );
  1055. if ( NULL != pInfo->QueueEntry.Flink )
  1056. {
  1057. UlTrace(TIMEOUTS, (
  1058. "http!UlEvaluateTimerInfo: pInfo %p: Moving to new slot %hd\n",
  1059. pInfo,
  1060. NewSlot
  1061. ));
  1062. RemoveEntryList(
  1063. &pInfo->QueueEntry
  1064. );
  1065. InsertTailList(
  1066. &(g_TimerWheel[NewSlot]),
  1067. &pInfo->QueueEntry
  1068. );
  1069. }
  1070. UlReleaseSpinLock(
  1071. &g_TimerWheelMutex,
  1072. OldIrql
  1073. );
  1074. }
  1075. //
  1076. // Update timer wheel state
  1077. //
  1078. pInfo->CurrentExpiry = MinTimeout;
  1079. pInfo->CurrentTimer = MinTimeoutTimer;
  1080. UlTraceVerbose(TIMEOUTS, (
  1081. "http!UlEvaluateTimerState: pInfo %p -> Timer %s, Expiry %d\n",
  1082. pInfo,
  1083. g_aTimeoutTimerNames[MinTimeoutTimer],
  1084. MinTimeout
  1085. ));
  1086. }
  1087. } // UlEvaluateTimerState