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.

906 lines
23 KiB

  1. /*++
  2. Copyright (c) 1993 Microsoft Corporation
  3. Module Name:
  4. Scavengr.c
  5. Abstract:
  6. This module implements the Netware Redirector scavenger thread.
  7. Author:
  8. Manny Weiser [MannyW] 15-Feb-1993
  9. Revision History:
  10. Tommy Evans (tommye) 04-27-2000 MS bug 33463 - added bShuttingDown flag
  11. to CleanupSupplementalCredentials() to force cleanup
  12. of cached credentials when we are unloaded.
  13. --*/
  14. #include "Procs.h"
  15. //
  16. // The debug trace level
  17. //
  18. #define Dbg (DEBUG_TRACE_SCAVENGER)
  19. extern BOOLEAN WorkerRunning; // From timer.c
  20. #ifdef NWDBG
  21. DWORD DumpIcbFlag = 0 ;
  22. #endif
  23. VOID
  24. CleanupVcbs(
  25. LARGE_INTEGER Now
  26. );
  27. VOID
  28. CleanupObjectCache(
  29. VOID
  30. );
  31. #ifdef ALLOC_PRAGMA
  32. #ifndef QFE_BUILD
  33. #pragma alloc_text( PAGE1, NwAllocateExtraIrpContext )
  34. #pragma alloc_text( PAGE1, NwFreeExtraIrpContext )
  35. #pragma alloc_text( PAGE1, CleanupScbs )
  36. #pragma alloc_text( PAGE1, DisconnectTimedOutScbs )
  37. #endif
  38. #endif
  39. //
  40. // Not pageable:
  41. //
  42. // NwScavengerRoutine - Acquires a spin lock.
  43. // CleanupVcbs - Acquires a spin lock.
  44. //
  45. VOID
  46. NwScavengerRoutine(
  47. IN PWORK_QUEUE_ITEM WorkItem
  48. )
  49. /*++
  50. Routine Description:
  51. This routine implements the scavenger. The scavenger runs
  52. periodically in the context of an executive worker thread to
  53. do background cleanup operations on redirector data.
  54. Arguments:
  55. WorkItem - The work item for this routine.
  56. Return Value:
  57. None.
  58. --*/
  59. {
  60. LARGE_INTEGER Now;
  61. PMDL LineChangeMdl;
  62. PWORK_QUEUE_ITEM LineChangeWorkItem;
  63. KIRQL OldIrql;
  64. PAGED_CODE();
  65. DebugTrace(+1, Dbg, "NwScavengerRoutine\n", 0);
  66. KeQuerySystemTime( &Now );
  67. #ifdef NWDBG
  68. if (DumpIcbFlag != 0)
  69. DumpIcbs();
  70. #endif
  71. //
  72. // Try to free unused VCBs.
  73. //
  74. CleanupVcbs(Now);
  75. //
  76. // Try disconnect from SCBs that are timed out.
  77. //
  78. DisconnectTimedOutScbs(Now) ;
  79. //
  80. // Try to invalidate old object cache entries.
  81. //
  82. CleanupObjectCache();
  83. //
  84. // Try to free unused SCBs.
  85. //
  86. CleanupScbs(Now);
  87. //
  88. // Clean up supplemental credentials that are
  89. // no longer being used.
  90. //
  91. CleanupSupplementalCredentials(Now, FALSE);
  92. //
  93. // Flag we're finished now to avoid deadlock in stop timer.
  94. //
  95. KeAcquireSpinLock( &NwScavengerSpinLock, &OldIrql );
  96. if ( DelayedProcessLineChange ) {
  97. DebugTrace( 0, Dbg, "Scavenger processing a delayed line change notification.\n", 0 );
  98. LineChangeMdl = DelayedLineChangeIrp->MdlAddress;
  99. LineChangeWorkItem = ALLOCATE_POOL( NonPagedPool, sizeof( WORK_QUEUE_ITEM ) );
  100. if ( LineChangeWorkItem == NULL ) {
  101. //
  102. // If we couldn't get a work queue item, just blow
  103. // it all off for now.
  104. //
  105. FREE_POOL( LineChangeMdl->MappedSystemVa );
  106. FREE_MDL( LineChangeMdl );
  107. FREE_IRP( DelayedLineChangeIrp );
  108. DelayedLineChangeIrp = NULL;
  109. DelayedProcessLineChange = FALSE;
  110. WorkerRunning = FALSE;
  111. KeReleaseSpinLock( &NwScavengerSpinLock, OldIrql );
  112. } else {
  113. //
  114. // Leave WorkRunning set to TRUE so that the scavenger can't run
  115. // while the process line change is running, but clear the line
  116. // change flag. The FspProcessLineChange function will clear the
  117. // WorkerRunning flag.
  118. //
  119. DelayedProcessLineChange = FALSE;
  120. KeReleaseSpinLock( &NwScavengerSpinLock, OldIrql );
  121. //
  122. // Use the user buffer field as a convenient place to remember where
  123. // the address of the WorkQueueItem. We can get away with this since
  124. // we don't let this IRP complete.
  125. //
  126. DelayedLineChangeIrp->UserBuffer = LineChangeWorkItem;
  127. //
  128. // Process the line change in the FSP.
  129. //
  130. ExInitializeWorkItem( LineChangeWorkItem, FspProcessLineChange, DelayedLineChangeIrp );
  131. ExQueueWorkItem( LineChangeWorkItem, DelayedWorkQueue );
  132. }
  133. } else {
  134. //
  135. // No line change happened while the scavenger was running.
  136. //
  137. WorkerRunning = FALSE;
  138. KeReleaseSpinLock( &NwScavengerSpinLock, OldIrql );
  139. }
  140. //
  141. // Unlock discardable code, if we are inactive. Don't block
  142. // if can't get resource.
  143. //
  144. NwUnlockCodeSections(FALSE);
  145. DebugTrace(-1, Dbg, "NwScavengerRoutine\n", 0);
  146. return;
  147. }
  148. VOID
  149. CleanupScbs(
  150. LARGE_INTEGER Now
  151. )
  152. /*++
  153. Routine Description:
  154. This routine tries to free unused VCB structures.
  155. Arguments:
  156. None.
  157. Return Value:
  158. None.
  159. --*/
  160. {
  161. KIRQL OldIrql;
  162. PLIST_ENTRY ScbQueueEntry;
  163. PNONPAGED_SCB pNpScb;
  164. PLIST_ENTRY NextScbQueueEntry;
  165. PSCB pScb;
  166. LIST_ENTRY DyingScbs;
  167. LARGE_INTEGER KillTime ;
  168. DebugTrace(+1, Dbg, "CleanupScbs\n", 0);
  169. //
  170. // Calculate KillTime = Now - 2 minutes.
  171. //
  172. InitializeListHead( &DyingScbs );
  173. KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_SCB_KEEP_TIME );
  174. //
  175. // Scan through the SCBs holding the RCB.
  176. //
  177. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  178. KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
  179. //
  180. // find all SCBs that are no longer usable and put them on the dying list.
  181. // we will take a second pass thru to remove timed out ones, based on
  182. // what is left.
  183. //
  184. for (ScbQueueEntry = ScbQueue.Flink ;
  185. ScbQueueEntry != &ScbQueue ;
  186. ScbQueueEntry = NextScbQueueEntry )
  187. {
  188. pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
  189. NextScbQueueEntry = pNpScb->ScbLinks.Flink;
  190. if ( ( pNpScb->Reference == 0 ) &&
  191. ( ( pNpScb->LastUsedTime.QuadPart < KillTime.QuadPart ) ||
  192. ( pNpScb->State == SCB_STATE_FLAG_SHUTDOWN ) ) )
  193. {
  194. DebugTrace( 0, Dbg,
  195. "Moving SCB %08lx to dead list\n", pNpScb);
  196. //
  197. // The SCB has no references and is not logged in nor attached.
  198. //
  199. RemoveEntryList( &pNpScb->ScbLinks );
  200. InsertHeadList( &DyingScbs, &pNpScb->ScbLinks );
  201. }
  202. #ifdef MSWDBG
  203. //
  204. // Look for blocked connections. If there's something
  205. // queued for this server yet nothing was added or removed
  206. // since the last time the scavenger ran then stop
  207. //
  208. if ((!IsListEmpty( &pNpScb->Requests ) ) &&
  209. (pNpScb->RequestQueued == FALSE) &&
  210. (pNpScb->RequestDequeued == FALSE )) {
  211. DebugTrace( 0, Dbg, "Server %08lx seems to be locked up!\n", pNpScb );
  212. ASSERT( FALSE );
  213. } else {
  214. pNpScb->RequestQueued = FALSE;
  215. pNpScb->RequestDequeued = FALSE;
  216. }
  217. #endif
  218. }
  219. //
  220. // Now that the dying SCBs are off the ScbQueue we can release
  221. // the SCB spin lock.
  222. //
  223. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  224. //
  225. // Walk the list of Dying SCBs and kill them off. Note that we are
  226. // still holding the RCB.
  227. //
  228. while ( !IsListEmpty( &DyingScbs ) ) {
  229. pNpScb = CONTAINING_RECORD( DyingScbs.Flink, NONPAGED_SCB, ScbLinks );
  230. pScb = pNpScb->pScb;
  231. RemoveHeadList( &DyingScbs );
  232. NwDeleteScb( pScb );
  233. }
  234. NwReleaseRcb( &NwRcb );
  235. DebugTrace(-1, Dbg, "CleanupScbs\n", 0);
  236. }
  237. VOID
  238. CleanupVcbs(
  239. LARGE_INTEGER Now
  240. )
  241. /*++
  242. Routine Description:
  243. This routine tries to free unused VCB structures.
  244. Arguments:
  245. None.
  246. Return Value:
  247. None.
  248. --*/
  249. {
  250. KIRQL OldIrql;
  251. PLIST_ENTRY ScbQueueEntry;
  252. PLIST_ENTRY VcbQueueEntry;
  253. PLIST_ENTRY NextVcbQueueEntry;
  254. PNONPAGED_SCB pNpScb;
  255. PSCB pScb;
  256. PVCB pVcb;
  257. LARGE_INTEGER KillTime;
  258. NTSTATUS Status;
  259. PIRP_CONTEXT IrpContext = NULL;
  260. BOOLEAN VcbDeleted;
  261. PAGED_CODE();
  262. DebugTrace(+1, Dbg, "CleanupVcbs...\n", 0 );
  263. //
  264. // Calculate KillTime = Now - 5 minutes.
  265. //
  266. KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_VCB_KEEP_TIME );
  267. //
  268. // Scan through the SCBs.
  269. //
  270. KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
  271. ScbQueueEntry = ScbQueue.Flink;
  272. while ( ScbQueueEntry != &ScbQueue ) {
  273. pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
  274. //
  275. // Reference the SCB so that it won't go away when we release
  276. // the SCB spin lock.
  277. //
  278. NwReferenceScb( pNpScb );
  279. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  280. pScb = pNpScb->pScb;
  281. if ( pScb == NULL) {
  282. //
  283. // This must be the permanent SCB. Just skip it.
  284. //
  285. ASSERT( pNpScb == &NwPermanentNpScb );
  286. } else {
  287. //
  288. // Get an irp context and get to the head of the queue.
  289. //
  290. if ( NwAllocateExtraIrpContext( &IrpContext, pNpScb ) ) {
  291. IrpContext->pNpScb = pNpScb;
  292. IrpContext->pScb = pNpScb->pScb;
  293. NwAppendToQueueAndWait( IrpContext );
  294. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  295. VcbDeleted = TRUE;
  296. //
  297. // NwCleanupVcb releases the RCB, but we can't be guaranteed
  298. // the state of the Vcb list when we release the RCB.
  299. //
  300. // If we need to cleanup a VCB, release the lock, and start
  301. // processing the list again.
  302. //
  303. while ( VcbDeleted ) {
  304. VcbDeleted = FALSE;
  305. for ( VcbQueueEntry = pScb->ScbSpecificVcbQueue.Flink ;
  306. VcbQueueEntry != &pScb->ScbSpecificVcbQueue;
  307. VcbQueueEntry = NextVcbQueueEntry ) {
  308. pVcb = CONTAINING_RECORD( VcbQueueEntry, VCB, VcbListEntry );
  309. NextVcbQueueEntry = VcbQueueEntry->Flink;
  310. //
  311. // The VCB has no references, and hasn't been used for
  312. // a long time. Kill it.
  313. //
  314. if ( pVcb->Reference == 0 ) {
  315. Status = STATUS_SUCCESS;
  316. DebugTrace(0, Dbg, "Cleaning up VCB %08lx\n", pVcb );
  317. DebugTrace(0, Dbg, "VCB name = %wZ\n", &pVcb->Name );
  318. // Lock down so that we can send a packet.
  319. NwReferenceUnlockableCodeSection();
  320. NwCleanupVcb( pVcb, IrpContext );
  321. NwDereferenceUnlockableCodeSection ();
  322. //
  323. // Get back to the head of the queue, re-acquire
  324. // the VCB, and restart the processing of this list.
  325. //
  326. NwAppendToQueueAndWait( IrpContext );
  327. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  328. VcbDeleted = TRUE;
  329. break;
  330. }
  331. } // for
  332. } // while
  333. } else {
  334. IrpContext = NULL;
  335. DebugTrace( 0, Dbg, "Couldn't cleanup SCB: %08lx\n", pNpScb );
  336. }
  337. NwReleaseRcb( &NwRcb );
  338. }
  339. //
  340. // Free the irp context allocated for this SCB.
  341. //
  342. if ( IrpContext != NULL ) {
  343. NwDequeueIrpContext( IrpContext, FALSE );
  344. NwFreeExtraIrpContext( IrpContext );
  345. IrpContext = NULL;
  346. }
  347. KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
  348. ScbQueueEntry = pNpScb->ScbLinks.Flink;
  349. NwDereferenceScb( pNpScb );
  350. }
  351. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  352. DebugTrace(-1, Dbg, "CleanupVcbs -> VOID\n", 0 );
  353. }
  354. VOID
  355. DisconnectTimedOutScbs(
  356. LARGE_INTEGER Now
  357. )
  358. /*++
  359. Routine Description:
  360. This routine disconnects any timed out SCBs before they get
  361. nuked by CleanupScbs() which does not disconnect.
  362. NOTE: The SCB's are destroyed on a timeout for a couple of
  363. reasons. The first is because if we used a reference count then
  364. normal use of UNCs would cause us to be continually reconnecting.
  365. Another is in FindNearestServer where its useful to collect the
  366. Near servers that are out of connections so we can avoid them when
  367. we iterate through the 5 nearest servers and we escalate to General
  368. SAP response.
  369. Arguments:
  370. None.
  371. Return Value:
  372. None.
  373. --*/
  374. {
  375. KIRQL OldIrql;
  376. PLIST_ENTRY ScbQueueEntry;
  377. PNONPAGED_SCB pNpScb;
  378. LARGE_INTEGER KillTime ;
  379. PIRP_CONTEXT IrpContext = NULL;
  380. PAGED_CODE();
  381. DebugTrace(+1, Dbg, "DisconnectTimedOutScbs...\n", 0 );
  382. //
  383. // Calculate KillTime = Now - 5 minutes.
  384. //
  385. KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_SCB_KEEP_TIME );
  386. //
  387. // Scan through the SCBs.
  388. //
  389. KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
  390. ScbQueueEntry = ScbQueue.Flink;
  391. while ( ScbQueueEntry != &ScbQueue )
  392. {
  393. pNpScb = CONTAINING_RECORD( ScbQueueEntry, NONPAGED_SCB, ScbLinks );
  394. if ( (pNpScb != &NwPermanentNpScb) &&
  395. (pNpScb->Reference == 0 ) &&
  396. (pNpScb->LastUsedTime.QuadPart < KillTime.QuadPart) )
  397. {
  398. //
  399. // Reference the SCB so that it won't go away when we release
  400. // the SCB spin lock.
  401. //
  402. NwReferenceScb( pNpScb );
  403. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  404. //
  405. // Not the permanent SCB and the reference count is the one
  406. // we just added, So this is really at zero & has not been used
  407. // for a while. Note we only allocate the IrpContext once.
  408. //
  409. if ( IrpContext ||
  410. NwAllocateExtraIrpContext( &IrpContext, pNpScb ) )
  411. {
  412. IrpContext->pNpScb = pNpScb;
  413. // Lock down so that we can send a packet.
  414. NwReferenceUnlockableCodeSection();
  415. //
  416. // get to front of queue and recheck to make sure we are
  417. // still with a ref count of 1.
  418. //
  419. NwAppendToQueueAndWait( IrpContext );
  420. if (pNpScb->Reference == 1)
  421. {
  422. //
  423. // make sure we do not reconnect.
  424. //
  425. ClearFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE );
  426. //
  427. // This will result in a logoff and/or disconnect as
  428. // need.
  429. //
  430. NwLogoffAndDisconnect(IrpContext, pNpScb) ;
  431. }
  432. NwDequeueIrpContext(IrpContext, FALSE) ;
  433. NwDereferenceUnlockableCodeSection ();
  434. }
  435. else
  436. {
  437. //
  438. // Could not allocate IrpContext. Oh well, we'll just leave
  439. // this connection for the watch dog.
  440. //
  441. }
  442. KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
  443. NwDereferenceScb( pNpScb );
  444. }
  445. else
  446. {
  447. //
  448. // not timed out or is permanent SCB. dont disconnect.
  449. //
  450. }
  451. ScbQueueEntry = pNpScb->ScbLinks.Flink;
  452. }
  453. if ( IrpContext )
  454. NwFreeExtraIrpContext( IrpContext );
  455. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  456. DebugTrace(-1, Dbg, "DisconnectTimedOutScbs -> VOID\n", 0 );
  457. }
  458. BOOLEAN
  459. NwAllocateExtraIrpContext(
  460. OUT PIRP_CONTEXT *ppIrpContext,
  461. IN PNONPAGED_SCB pNpScb
  462. )
  463. {
  464. PIRP Irp;
  465. BOOLEAN Success = TRUE;
  466. try {
  467. //
  468. // Try to allocate an IRP
  469. //
  470. Irp = ALLOCATE_IRP( pNpScb->Server.pDeviceObject->StackSize, FALSE );
  471. if ( Irp == NULL ) {
  472. ExRaiseStatus( STATUS_INSUFFICIENT_RESOURCES );
  473. }
  474. //
  475. // Try to allocate an IRP Context. This will
  476. // raise an excpetion if it fails.
  477. //
  478. *ppIrpContext = AllocateIrpContext( Irp );
  479. Irp->Tail.Overlay.Thread = PsGetCurrentThread();
  480. } except( NwExceptionFilter( Irp, GetExceptionInformation() )) {
  481. Success = FALSE;
  482. }
  483. return( Success );
  484. }
  485. VOID
  486. NwFreeExtraIrpContext(
  487. IN PIRP_CONTEXT pIrpContext
  488. )
  489. {
  490. FREE_IRP( pIrpContext->pOriginalIrp );
  491. pIrpContext->pOriginalIrp = NULL; // Avoid FreeIrpContext modifying freed Irp.
  492. FreeIrpContext( pIrpContext );
  493. return;
  494. }
  495. VOID
  496. CleanupSupplementalCredentials(
  497. LARGE_INTEGER Now,
  498. BOOLEAN bShuttingDown
  499. ) {
  500. PLIST_ENTRY pLogonList;
  501. PLIST_ENTRY pCredList;
  502. PLOGON pLogon;
  503. PNDS_SECURITY_CONTEXT pCredential;
  504. LARGE_INTEGER KillTime;
  505. DebugTrace( 0, Dbg, "CleanupSupplementalCredentials...\n", 0 );
  506. //
  507. // Grab the RCB to protect the logon list.
  508. //
  509. NwAcquireExclusiveRcb( &NwRcb, TRUE );
  510. pLogonList = LogonList.Flink;
  511. //
  512. // Walk the logon list.
  513. //
  514. while ( pLogonList != &LogonList ) {
  515. pLogon = CONTAINING_RECORD( pLogonList, LOGON, Next );
  516. //
  517. // Grab the credential resource to protect the credential list.
  518. // If we can't have exclusive access to the credential list,
  519. // don't wait for it or we may deadlock.
  520. //
  521. if ( ExAcquireResourceExclusiveLite( &((pLogon)->CredentialListResource), FALSE ) ) {
  522. pCredList = pLogon->NdsCredentialList.Flink;
  523. while ( pCredList != &(pLogon->NdsCredentialList) ) {
  524. BOOLEAN bRemove = FALSE;
  525. pCredential = CONTAINING_RECORD( pCredList, NDS_SECURITY_CONTEXT, Next );
  526. pCredList = pCredential->Next.Flink;
  527. if (bShuttingDown) {
  528. bRemove = TRUE;
  529. }
  530. else {
  531. if ( ( IsCredentialName( &(pCredential->NdsTreeName) ) ) &&
  532. ( pCredential->SupplementalHandleCount == 0 ) ) {
  533. //
  534. // Calculate KillTime.
  535. //
  536. KillTime.QuadPart = Now.QuadPart - ( NwOneSecond * DORMANT_SCB_KEEP_TIME );
  537. if ( pCredential->LastUsedTime.QuadPart < KillTime.QuadPart ) {
  538. bRemove = TRUE;
  539. }
  540. }
  541. }
  542. /** If we are supposed to clean this guy up - do it **/
  543. if (bRemove) {
  544. DebugTrace( 0, Dbg, "Removing credentials for %wZ\n", &pCredential->NdsTreeName );
  545. RemoveEntryList( &pCredential->Next );
  546. FreeNdsContext( pCredential );
  547. }
  548. }
  549. ExReleaseResourceLite( &((pLogon)->CredentialListResource) );
  550. }
  551. pLogonList = pLogon->Next.Flink;
  552. }
  553. NwReleaseRcb( &NwRcb );
  554. return;
  555. }
  556. VOID
  557. CleanupObjectCache(
  558. VOID
  559. )
  560. {
  561. NTSTATUS Status;
  562. KIRQL OldIrql;
  563. PLIST_ENTRY ScbEntry;
  564. PLIST_ENTRY CacheEntry;
  565. PNONPAGED_SCB NonpagedScb;
  566. PSCB Scb;
  567. PNDS_OBJECT_CACHE_ENTRY ObjectCache;
  568. LARGE_INTEGER CurrentTick;
  569. //
  570. // Get the current tick count for checking timeouts.
  571. //
  572. KeQueryTickCount( &CurrentTick );
  573. //
  574. // Walk the SCB queue.
  575. //
  576. KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
  577. ScbEntry = ScbQueue.Flink;
  578. while( ScbEntry != &ScbQueue ) {
  579. NonpagedScb = CONTAINING_RECORD( ScbEntry, NONPAGED_SCB, ScbLinks );
  580. //
  581. // Make sure this isn't the permanent SCB.
  582. //
  583. if( NonpagedScb != &NwPermanentNpScb ) {
  584. //
  585. // Reference the SCB so it won't go away when we release the SCB lock.
  586. //
  587. NwReferenceScb( NonpagedScb );
  588. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  589. Scb = NonpagedScb->pScb;
  590. if( Scb->ObjectCacheBuffer != NULL ) {
  591. //
  592. // Acquire the cache lock so that the cache can be messed with.
  593. // This wait should never fail, but if it does, act as if there
  594. // is no cache for this SCB.
  595. //
  596. Status = KeWaitForSingleObject( &(Scb->ObjectCacheLock),
  597. Executive,
  598. KernelMode,
  599. FALSE,
  600. NULL );
  601. if( NT_SUCCESS(Status) ) {
  602. //
  603. // Walk the object cache and invalidate any timed-out entries.
  604. //
  605. CacheEntry = Scb->ObjectCacheList.Flink;
  606. while( CacheEntry != &(Scb->ObjectCacheList) ) {
  607. ObjectCache = CONTAINING_RECORD( CacheEntry, NDS_OBJECT_CACHE_ENTRY, Links );
  608. //
  609. // If this entry has timed out, invalidate it.
  610. //
  611. if( ObjectCache->Scb != NULL && CurrentTick.QuadPart < ObjectCache->Timeout.QuadPart ) {
  612. NwDereferenceScb( ObjectCache->Scb->pNpScb );
  613. ObjectCache->Scb = NULL;
  614. }
  615. //
  616. // Move to the next entry.
  617. //
  618. CacheEntry = CacheEntry->Flink;
  619. }
  620. KeReleaseSemaphore( &(Scb->ObjectCacheLock),
  621. 0,
  622. 1,
  623. FALSE );
  624. }
  625. }
  626. //
  627. // Reacquire the SCB lock and dereference the current SCB.
  628. //
  629. KeAcquireSpinLock( &ScbSpinLock, &OldIrql );
  630. NwDereferenceScb( NonpagedScb );
  631. }
  632. ScbEntry = ScbEntry->Flink;
  633. }
  634. KeReleaseSpinLock( &ScbSpinLock, OldIrql );
  635. return;
  636. }