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.

584 lines
16 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. timer.c
  5. Abstract:
  6. This module contains code which implements the receive and send timeouts
  7. for each connection.
  8. Author:
  9. Colin Watson [ColinW] 21-Feb-1993
  10. Anoop Anantha [AnoopA] 24-Jun-1998
  11. Environment:
  12. Kernel mode
  13. Revision History:
  14. --*/
  15. #include "procs.h"
  16. //
  17. // The debug trace level
  18. //
  19. #define Dbg (DEBUG_TRACE_TIMER)
  20. LARGE_INTEGER DueTime;
  21. KDPC NwDpc; // DPC object for timeouts.
  22. KTIMER Timer; // kernel timer for this request.
  23. ULONG ScavengerTickCount;
  24. BOOLEAN WorkerRunning = FALSE;
  25. WORK_QUEUE_ITEM WorkItem;
  26. #ifdef NWDBG
  27. BOOLEAN DisableTimer = FALSE;
  28. #endif
  29. //
  30. // TimerStop reflects the state of the timer.
  31. //
  32. BOOLEAN TimerStop = TRUE;
  33. VOID
  34. TimerDPC(
  35. IN PKDPC Dpc,
  36. IN PVOID Context,
  37. IN PVOID SystemArgument1,
  38. IN PVOID SystemArgument2
  39. );
  40. #if 0
  41. //
  42. // Not Pageable because it may be called from PnPSetPower() and because
  43. // it holds a spinlock
  44. //
  45. StartTimer (VOID)
  46. StopTimer (VOID)
  47. #endif
  48. VOID
  49. StartTimer(
  50. VOID
  51. )
  52. /*++
  53. Routine Description:
  54. This routine starts the timer ticking.
  55. Arguments:
  56. None.
  57. Return Value:
  58. None.
  59. --*/
  60. {
  61. KIRQL OldIrql;
  62. DebugTrace(+0, Dbg, "Entering StartTimer\n", 0);
  63. KeAcquireSpinLock( &NwTimerSpinLock, &OldIrql );
  64. if (TimerStop) {
  65. //
  66. // We need 18.21 ticks per second
  67. //
  68. DueTime.QuadPart = (( 100000 * MILLISECONDS ) / 1821) * -1;
  69. //
  70. // This is the first connection with timeouts specified.
  71. // Set up the timer so that every 500 milliseconds we scan all the
  72. // connections for timed out receive and sends.
  73. //
  74. KeInitializeDpc( &NwDpc, TimerDPC, NULL );
  75. KeInitializeTimer( &Timer );
  76. (VOID)KeSetTimer(&Timer, DueTime, &NwDpc);
  77. TimerStop = FALSE;
  78. DebugTrace(+0, Dbg, "StartTimer started timer\n", 0);
  79. }
  80. KeReleaseSpinLock( &NwTimerSpinLock, OldIrql );
  81. }
  82. VOID
  83. StopTimer(
  84. VOID
  85. )
  86. /*++
  87. Routine Description:
  88. This routine stops the timer. It blocks until the timer has stopped.
  89. Arguments:
  90. None.
  91. Return Value:
  92. None.
  93. --*/
  94. {
  95. KIRQL OldIrql;
  96. DebugTrace(+0, Dbg, "Entering StopTimer\n", 0);
  97. KeAcquireSpinLock( &NwTimerSpinLock, &OldIrql );
  98. if (!TimerStop) {
  99. KeCancelTimer( &Timer );
  100. TimerStop = TRUE;
  101. DebugTrace(+0, Dbg, "StopTimer stopped timer\n", 0);
  102. }
  103. KeReleaseSpinLock( &NwTimerSpinLock, OldIrql );
  104. }
  105. VOID
  106. TimerDPC(
  107. IN PKDPC Dpc,
  108. IN PVOID Context,
  109. IN PVOID SystemArgument1,
  110. IN PVOID SystemArgument2
  111. )
  112. /*++
  113. Routine Description:
  114. This routine is called to search for timed out send and receive
  115. requests. This routine is called at DPC level.
  116. Arguments:
  117. Dpc - Unused.
  118. Context - Unused.
  119. SystemArgument1 - Unused.
  120. SystemArgument2 - Unused.
  121. Return Value:
  122. None.
  123. --*/
  124. {
  125. PLIST_ENTRY ScbQueueEntry;
  126. PLIST_ENTRY NextScbQueueEntry;
  127. PLIST_ENTRY IrpContextEntry;
  128. PLIST_ENTRY NextIrpContextEntry;
  129. SHORT RetryCount;
  130. PIRP_CONTEXT pIrpContext;
  131. LARGE_INTEGER CurrentTime = {0, 0};
  132. WCHAR AnonymousName[] = L"UNKNOWN";
  133. PWCHAR ServerLogName;
  134. PWORK_CONTEXT workContext;
  135. //
  136. // For each Server see if there is a timeout to process.
  137. //
  138. #ifdef NWDBG
  139. if ( DisableTimer ) {
  140. //
  141. // Reset the timer to run for another tick.
  142. //
  143. (VOID)KeSetTimer ( &Timer, DueTime, &NwDpc);
  144. return;
  145. }
  146. #endif
  147. //DebugTrace(+1, Dbg, "TimerDpc....\n", 0);
  148. //
  149. // Scan through the Scb's looking for timed out requests.
  150. //
  151. KeAcquireSpinLockAtDpcLevel( &ScbSpinLock );
  152. ScbQueueEntry = ScbQueue.Flink;
  153. if (ScbQueueEntry != &ScbQueue) {
  154. PNONPAGED_SCB pNpScb = CONTAINING_RECORD(ScbQueueEntry,
  155. NONPAGED_SCB,
  156. ScbLinks);
  157. NwQuietReferenceScb( pNpScb );
  158. }
  159. for (;
  160. ScbQueueEntry != &ScbQueue ;
  161. ScbQueueEntry = NextScbQueueEntry ) {
  162. PNONPAGED_SCB pNpScb = CONTAINING_RECORD(ScbQueueEntry,
  163. NONPAGED_SCB,
  164. ScbLinks);
  165. // Obtain a pointer to the next SCB in the SCB list before
  166. // dereferencing the current one.
  167. //
  168. NextScbQueueEntry = pNpScb->ScbLinks.Flink;
  169. if (NextScbQueueEntry != &ScbQueue) {
  170. PNONPAGED_SCB pNextNpScb = CONTAINING_RECORD(NextScbQueueEntry,
  171. NONPAGED_SCB,
  172. ScbLinks);
  173. //
  174. // Reference the next entry in the list to ensure the scavenger
  175. // doesn't put it on another list or destroy it.
  176. //
  177. NwQuietReferenceScb( pNextNpScb );
  178. }
  179. KeReleaseSpinLockFromDpcLevel( &ScbSpinLock );
  180. //
  181. // Acquire the Scb specific spin lock to protect access
  182. // the the Scb fields.
  183. //
  184. KeAcquireSpinLockAtDpcLevel( &pNpScb->NpScbSpinLock );
  185. //
  186. // Look at the first request on the queue only (since it is
  187. // the only active request).
  188. //
  189. if ( ( !IsListEmpty( &pNpScb->Requests )) &&
  190. ( !pNpScb->Sending ) &&
  191. ( pNpScb->OkToReceive ) &&
  192. ( --pNpScb->TimeOut <= 0 ) ) {
  193. PIRP_CONTEXT pIrpContext;
  194. //
  195. // This request has timed out. Try to retransmit the request.
  196. //
  197. pIrpContext = CONTAINING_RECORD(
  198. pNpScb->Requests.Flink,
  199. IRP_CONTEXT,
  200. NextRequest);
  201. pNpScb->TimeOut = pNpScb->MaxTimeOut;
  202. //
  203. // Check the retry count while we own the spin lock.
  204. //
  205. RetryCount = --pNpScb->RetryCount;
  206. NwQuietDereferenceScb( pNpScb );
  207. //
  208. // Set OkToReceive to FALSE, so that if we receive a response
  209. // right now, our receive handler won't handle the response
  210. // and cause IRP context to be freed.
  211. //
  212. pNpScb->OkToReceive = FALSE;
  213. KeReleaseSpinLockFromDpcLevel( &pNpScb->NpScbSpinLock );
  214. if ( pIrpContext->pOriginalIrp->Cancel ) {
  215. //
  216. // This IRP has been cancelled. Call the callback routine.
  217. //
  218. DebugTrace(+0, Dbg, "Timer cancel IRP %X\n", pIrpContext->pOriginalIrp );
  219. pIrpContext->pEx( pIrpContext, 0, NULL );
  220. } else if ( RetryCount >= 0) {
  221. //
  222. // We're not out of retries. Resend the request packet.
  223. //
  224. // First adjust the send timeout up. Adjust the timeout
  225. // more slowly on a close by server.
  226. //
  227. if ( pNpScb->SendTimeout < pNpScb->MaxTimeOut ) {
  228. if ( pNpScb->TickCount <= 4 ) {
  229. pNpScb->SendTimeout++;
  230. } else {
  231. pNpScb->SendTimeout = pNpScb->SendTimeout * 3 / 2;
  232. if ( pNpScb->SendTimeout > pNpScb->MaxTimeOut ) {
  233. pNpScb->SendTimeout = pNpScb->MaxTimeOut;
  234. }
  235. }
  236. }
  237. pNpScb->TimeOut = pNpScb->SendTimeout;
  238. DebugTrace(+0, Dbg, "Adjusting send timeout: %x\n", pIrpContext );
  239. DebugTrace(+0, Dbg, "Adjusting send timeout to: %d\n", pNpScb->TimeOut );
  240. if ( pIrpContext->TimeoutRoutine != NULL ) {
  241. DebugTrace(+0, Dbg, "Timeout Routine, retry %x\n", RetryCount+1);
  242. DebugTrace(+0, Dbg, "Calling TimeoutRoutine, %x\n", pIrpContext->TimeoutRoutine);
  243. pIrpContext->TimeoutRoutine( pIrpContext );
  244. } else {
  245. DebugTrace(+0, Dbg, "Resending Packet, retry %x\n", RetryCount+1);
  246. PreparePacket( pIrpContext, pIrpContext->pOriginalIrp, pIrpContext->TxMdl );
  247. SetFlag( pIrpContext->Flags, IRP_FLAG_RETRY_SEND );
  248. SendNow( pIrpContext );
  249. }
  250. Stats.FailedSessions++;
  251. } else {
  252. ASSERT( pIrpContext->pEx != NULL );
  253. //
  254. // We are out of retries.
  255. //
  256. if ( (!BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_REROUTE_IN_PROGRESS ) &&
  257. ((BooleanFlagOn( pIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED ) ||
  258. ((NwAbsoluteTotalWaitTime != 0) && (pNpScb->TotalWaitTime >= NwAbsoluteTotalWaitTime )))))) {
  259. ClearFlag( pIrpContext->Flags, IRP_FLAG_RETRY_SEND );
  260. //
  261. // He have already attempted to reroute the request.
  262. // Give up.
  263. //
  264. DebugTrace(+0, Dbg, "Abandon Exchange\n", 0 );
  265. if ( pIrpContext->pNpScb != &NwPermanentNpScb ) {
  266. //
  267. // Reset to the attaching state. If the server
  268. // is dead, the next attempt to open a handle will
  269. // fail with a better error than unexpected network
  270. // error.
  271. //
  272. pIrpContext->pNpScb->State = SCB_STATE_ATTACHING;
  273. //
  274. // Determine the CurrentTime. We need to know if
  275. // TimeOutEventInterval minutes have passed before
  276. // we log the next time-out event.
  277. //
  278. KeQuerySystemTime( &CurrentTime );
  279. if ( CanLogTimeOutEvent( pNpScb->NwNextEventTime,
  280. CurrentTime
  281. )) {
  282. if ( pNpScb->ServerName.Buffer != NULL ) {
  283. ServerLogName = pNpScb->ServerName.Buffer;
  284. } else {
  285. ServerLogName = &AnonymousName[0];
  286. }
  287. Error(
  288. EVENT_NWRDR_TIMEOUT,
  289. STATUS_UNEXPECTED_NETWORK_ERROR,
  290. NULL,
  291. 0,
  292. 1,
  293. ServerLogName );
  294. //
  295. // Set the LastEventTime to the CurrentTime
  296. //
  297. UpdateNextEventTime(
  298. pNpScb->NwNextEventTime,
  299. CurrentTime,
  300. TimeOutEventInterval
  301. );
  302. }
  303. }
  304. pIrpContext->ResponseParameters.Error = ERROR_UNEXP_NET_ERR;
  305. pIrpContext->pEx( pIrpContext, 0, NULL );
  306. } else if (!BooleanFlagOn(pIrpContext->Flags, IRP_FLAG_REROUTE_IN_PROGRESS)) {
  307. //
  308. // Attempt to reroute the request if it hasn't already been rerouted
  309. //
  310. SetFlag( pIrpContext->Flags, IRP_FLAG_REROUTE_ATTEMPTED );
  311. if ((WorkerThreadRunning == TRUE) && (workContext = AllocateWorkContext())){
  312. //
  313. // Prepare the work context
  314. //
  315. workContext->pIrpC = pIrpContext;
  316. workContext->NodeWorkCode = NWC_NWC_REROUTE;
  317. //
  318. // and queue it.
  319. //
  320. DebugTrace( 0, Dbg, "Queueing reroute work.\n", 0 );
  321. //
  322. // Make sure we don't give up on this IrpContext. Also, reference
  323. // the SCB so that it doesn't get scavenged.
  324. //
  325. SetFlag( pIrpContext->Flags, IRP_FLAG_REROUTE_IN_PROGRESS );
  326. NwReferenceScb( pIrpContext->pNpScb );
  327. KeInsertQueue( &KernelQueue,
  328. &workContext->Next
  329. );
  330. } else {
  331. //
  332. // The worker thread is not running or, we could not
  333. // allocate a work context. Hence, we cannot
  334. // attempt the reroute.
  335. //
  336. pIrpContext->ResponseParameters.Error = ERROR_UNEXP_NET_ERR;
  337. pIrpContext->pEx( pIrpContext, 0, NULL );
  338. }
  339. }
  340. }
  341. } else {
  342. if ( ( !IsListEmpty( &pNpScb->Requests )) &&
  343. ( !pNpScb->Sending ) &&
  344. ( pNpScb->OkToReceive ) ) {
  345. DebugTrace( 0, Dbg, "TimeOut %d\n", pNpScb->TimeOut );
  346. }
  347. //
  348. // Nothing to do for this SCB. Dereference this SCB and
  349. // release the spin lock.
  350. //
  351. KeReleaseSpinLockFromDpcLevel( &pNpScb->NpScbSpinLock );
  352. NwQuietDereferenceScb( pNpScb );
  353. }
  354. KeAcquireSpinLockAtDpcLevel( &ScbSpinLock );
  355. }
  356. KeReleaseSpinLockFromDpcLevel( &ScbSpinLock );
  357. //
  358. // Now see if the scavenger routine needs to be run.
  359. // Only ever queue one workitem.
  360. //
  361. KeAcquireSpinLockAtDpcLevel( &NwScavengerSpinLock );
  362. NwScavengerTickCount++;
  363. if (( !WorkerRunning ) && ( !fPoweringDown ) &&
  364. ( NwScavengerTickCount > NwScavengerTickRunCount )) {
  365. ExInitializeWorkItem( &WorkItem, NwScavengerRoutine, &WorkItem );
  366. ExQueueWorkItem( &WorkItem, DelayedWorkQueue );
  367. NwScavengerTickCount = 0;
  368. WorkerRunning = TRUE;
  369. }
  370. KeReleaseSpinLockFromDpcLevel( &NwScavengerSpinLock );
  371. //
  372. // Scan the list of pending locks, looking for locks to retry.
  373. //
  374. KeAcquireSpinLockAtDpcLevel( &NwPendingLockSpinLock );
  375. for (IrpContextEntry = NwPendingLockList.Flink ;
  376. IrpContextEntry != &NwPendingLockList ;
  377. IrpContextEntry = NextIrpContextEntry ) {
  378. NextIrpContextEntry = IrpContextEntry->Flink;
  379. pIrpContext = CONTAINING_RECORD( IrpContextEntry, IRP_CONTEXT, NextRequest );
  380. if ( --pIrpContext->Specific.Lock.Key <= 0 ) {
  381. //
  382. // Remove the IRP Context from the queue and reattempt the lock.
  383. // Set the SEQUENCE_NO_REQUIRED flag so that the packet gets
  384. // renumbered.
  385. //
  386. RemoveEntryList( &pIrpContext->NextRequest );
  387. SetFlag( pIrpContext->Flags, IRP_FLAG_SEQUENCE_NO_REQUIRED );
  388. PrepareAndSendPacket( pIrpContext );
  389. }
  390. }
  391. KeReleaseSpinLockFromDpcLevel( &NwPendingLockSpinLock );
  392. //
  393. // Reset the timer to run for another tick if nobody has cancelled it
  394. // in the meantime.
  395. //
  396. KeAcquireSpinLockAtDpcLevel( &NwTimerSpinLock );
  397. if (!TimerStop) {
  398. (VOID)KeSetTimer ( &Timer, DueTime, &NwDpc);
  399. }
  400. KeReleaseSpinLockFromDpcLevel( &NwTimerSpinLock );
  401. //DebugTrace(-1, Dbg, "TimerDpc\n", 0);
  402. return;
  403. UNREFERENCED_PARAMETER (Dpc);
  404. UNREFERENCED_PARAMETER (Context);
  405. UNREFERENCED_PARAMETER (SystemArgument1);
  406. UNREFERENCED_PARAMETER (SystemArgument2);
  407. }