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.

4441 lines
121 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. scavengr.c
  5. Abstract:
  6. This module implements the LAN Manager server FSP resource and
  7. scavenger threads.
  8. Author:
  9. Chuck Lenzmeier (chuckl) 30-Dec-1989
  10. David Treadwell (davidtr)
  11. Environment:
  12. Kernel mode
  13. Revision History:
  14. --*/
  15. #include "precomp.h"
  16. #include <ntdddisk.h>
  17. #include "scavengr.tmh"
  18. #pragma hdrstop
  19. #define BugCheckFileId SRV_FILE_SCAVENGR
  20. //
  21. // Local data
  22. //
  23. ULONG LastNonPagedPoolLimitHitCount = 0;
  24. ULONG LastNonPagedPoolFailureCount = 0;
  25. ULONG LastPagedPoolLimitHitCount = 0;
  26. ULONG LastPagedPoolFailureCount = 0;
  27. ULONG SrvScavengerCheckRfcbActive = 5;
  28. LONG ScavengerUpdateQosCount = 0;
  29. LONG ScavengerCheckRfcbActive = 0;
  30. LONG FailedWorkItemAllocations = 0;
  31. ULONG OutOfFreeConnectionConsecutiveCount = 0;
  32. BOOLEAN EventSwitch = TRUE;
  33. LARGE_INTEGER NextScavengeTime = {0};
  34. LARGE_INTEGER NextAlertTime = {0};
  35. //
  36. // Fields used during shutdown to synchronize with EX worker threads. We
  37. // need to make sure that no worker thread is running server code before
  38. // we can declare shutdown to be complete -- otherwise the code may be
  39. // unloaded while it's running!
  40. //
  41. BOOLEAN ScavengerInitialized = FALSE;
  42. PKEVENT ScavengerTimerTerminationEvent = NULL;
  43. PKEVENT ScavengerThreadTerminationEvent = NULL;
  44. PKEVENT ResourceThreadTerminationEvent = NULL;
  45. PKEVENT ResourceAllocThreadTerminationEvent = NULL;
  46. //
  47. // Timer, DPC, and work item used to run the scavenger thread.
  48. //
  49. KTIMER ScavengerTimer = {0};
  50. KDPC ScavengerDpc = {0};
  51. PIO_WORKITEM ScavengerWorkItem = NULL;
  52. BOOLEAN ScavengerRunning = FALSE;
  53. KSPIN_LOCK ScavengerSpinLock = {0};
  54. //
  55. // Flags indicating which scavenger algorithms need to run.
  56. //
  57. BOOLEAN RunShortTermAlgorithm = FALSE;
  58. BOOLEAN RunScavengerAlgorithm = FALSE;
  59. BOOLEAN RunAlerterAlgorithm = FALSE;
  60. BOOLEAN RunSuspectConnectionAlgorithm = FALSE;
  61. //
  62. // Base scavenger timeout. A timer DPC runs each interval. It
  63. // schedules EX worker thread work when other longer intervals expire.
  64. //
  65. LARGE_INTEGER ScavengerBaseTimeout = { (ULONG)(-1*10*1000*1000*10), -1 };
  66. #define SRV_MAX_DOS_ATTACK_EVENT_LOGS 10
  67. //
  68. // Defined somewhere else.
  69. //
  70. LARGE_INTEGER
  71. SecondsToTime (
  72. IN ULONG Seconds,
  73. IN BOOLEAN MakeNegative
  74. );
  75. PIRP
  76. BuildCoreOfSyncIoRequest (
  77. IN HANDLE FileHandle,
  78. IN PFILE_OBJECT FileObject OPTIONAL,
  79. IN PKEVENT Event,
  80. IN PIO_STATUS_BLOCK IoStatusBlock,
  81. IN OUT PDEVICE_OBJECT *DeviceObject
  82. );
  83. NTSTATUS
  84. StartIoAndWait (
  85. IN PIRP Irp,
  86. IN PDEVICE_OBJECT DeviceObject,
  87. IN PKEVENT Event,
  88. IN PIO_STATUS_BLOCK IoStatusBlock
  89. );
  90. //
  91. // Local declarations
  92. //
  93. VOID
  94. ScavengerTimerRoutine (
  95. IN PKDPC Dpc,
  96. IN PVOID DeferredContext,
  97. IN PVOID SystemArgument1,
  98. IN PVOID SystemArgument2
  99. );
  100. VOID
  101. SrvResourceThread (
  102. IN PVOID Parameter
  103. );
  104. VOID
  105. ScavengerThread (
  106. IN PDEVICE_OBJECT DeviceObject,
  107. IN PVOID Parameter
  108. );
  109. VOID
  110. ScavengerAlgorithm (
  111. VOID
  112. );
  113. VOID
  114. AlerterAlgorithm (
  115. VOID
  116. );
  117. VOID
  118. CloseIdleConnection (
  119. IN PCONNECTION Connection,
  120. IN PLARGE_INTEGER CurrentTime,
  121. IN PLARGE_INTEGER DisconnectTime,
  122. IN PLARGE_INTEGER PastExpirationTime,
  123. IN PLARGE_INTEGER TwoMinuteWarningTime,
  124. IN PLARGE_INTEGER FiveMinuteWarningTime
  125. );
  126. VOID
  127. CreateConnections (
  128. VOID
  129. );
  130. VOID
  131. GeneratePeriodicEvents (
  132. VOID
  133. );
  134. VOID
  135. ProcessConnectionDisconnects (
  136. VOID
  137. );
  138. VOID
  139. ProcessOrphanedBlocks (
  140. VOID
  141. );
  142. VOID
  143. TimeoutSessions (
  144. IN PLARGE_INTEGER CurrentTime
  145. );
  146. VOID
  147. TimeoutWaitingOpens (
  148. IN PLARGE_INTEGER CurrentTime
  149. );
  150. VOID
  151. TimeoutStuckOplockBreaks (
  152. IN PLARGE_INTEGER CurrentTime
  153. );
  154. VOID
  155. UpdateConnectionQos (
  156. IN PLARGE_INTEGER currentTime
  157. );
  158. VOID
  159. UpdateSessionLastUseTime(
  160. IN PLARGE_INTEGER CurrentTime
  161. );
  162. VOID
  163. LazyFreeQueueDataStructures (
  164. PWORK_QUEUE queue
  165. );
  166. VOID
  167. SrvUserAlertRaise (
  168. IN ULONG Message,
  169. IN ULONG NumberOfStrings,
  170. IN PUNICODE_STRING String1 OPTIONAL,
  171. IN PUNICODE_STRING String2 OPTIONAL,
  172. IN PUNICODE_STRING ComputerName
  173. );
  174. VOID
  175. SrvAdminAlertRaise (
  176. IN ULONG Message,
  177. IN ULONG NumberOfStrings,
  178. IN PUNICODE_STRING String1 OPTIONAL,
  179. IN PUNICODE_STRING String2 OPTIONAL,
  180. IN PUNICODE_STRING String3 OPTIONAL
  181. );
  182. NTSTATUS
  183. TimeToTimeString (
  184. IN PLARGE_INTEGER Time,
  185. OUT PUNICODE_STRING TimeString
  186. );
  187. ULONG
  188. CalculateErrorSlot (
  189. PSRV_ERROR_RECORD ErrorRecord
  190. );
  191. VOID
  192. CheckErrorCount (
  193. PSRV_ERROR_RECORD ErrorRecord,
  194. BOOLEAN UseRatio
  195. );
  196. VOID
  197. CheckDiskSpace (
  198. VOID
  199. );
  200. NTSTATUS
  201. OpenAlerter (
  202. OUT PHANDLE AlerterHandle
  203. );
  204. VOID
  205. RecalcCoreSearchTimeout(
  206. VOID
  207. );
  208. #ifdef ALLOC_PRAGMA
  209. #pragma alloc_text( PAGE, SrvInitializeScavenger )
  210. #pragma alloc_text( PAGE, ScavengerAlgorithm )
  211. #pragma alloc_text( PAGE, AlerterAlgorithm )
  212. #pragma alloc_text( PAGE, CloseIdleConnection )
  213. #pragma alloc_text( PAGE, CreateConnections )
  214. #pragma alloc_text( PAGE, GeneratePeriodicEvents )
  215. #pragma alloc_text( PAGE, TimeoutSessions )
  216. #pragma alloc_text( PAGE, TimeoutWaitingOpens )
  217. #pragma alloc_text( PAGE, TimeoutStuckOplockBreaks )
  218. #pragma alloc_text( PAGE, UpdateConnectionQos )
  219. #pragma alloc_text( PAGE, UpdateSessionLastUseTime )
  220. #pragma alloc_text( PAGE, SrvUserAlertRaise )
  221. #pragma alloc_text( PAGE, SrvAdminAlertRaise )
  222. #pragma alloc_text( PAGE, TimeToTimeString )
  223. #pragma alloc_text( PAGE, CheckErrorCount )
  224. #pragma alloc_text( PAGE, CheckDiskSpace )
  225. #pragma alloc_text( PAGE, OpenAlerter )
  226. #pragma alloc_text( PAGE, ProcessOrphanedBlocks )
  227. #pragma alloc_text( PAGE, RecalcCoreSearchTimeout )
  228. #endif
  229. #if 0
  230. NOT PAGEABLE -- SrvTerminateScavenger
  231. NOT PAGEABLE -- ScavengerTimerRoutine
  232. NOT PAGEABLE -- SrvResourceThread
  233. NOT PAGEABLE -- ScavengerThread
  234. NOT PAGEABLE -- ProcessConnectionDisconnects
  235. NOT PAGEABLE -- SrvServiceWorkItemShortage
  236. NOT PAGEABLE -- LazyFreeQueueDataStructures
  237. NOT PAGEABLE -- SrvUpdateStatisticsFromQueues
  238. #endif
  239. NTSTATUS
  240. SrvInitializeScavenger (
  241. VOID
  242. )
  243. /*++
  244. Routine Description:
  245. This function creates the scavenger thread for the LAN Manager
  246. server FSP.
  247. Arguments:
  248. None.
  249. Return Value:
  250. NTSTATUS - Status of thread creation
  251. --*/
  252. {
  253. LARGE_INTEGER currentTime;
  254. PAGED_CODE( );
  255. //
  256. // Initialize the scavenger spin lock.
  257. //
  258. INITIALIZE_SPIN_LOCK( &ScavengerSpinLock );
  259. //
  260. // When this count is zero, we will update the QOS information for
  261. // each active connection.
  262. //
  263. ScavengerUpdateQosCount = SrvScavengerUpdateQosCount;
  264. //
  265. // When this count is zero, we will check the rfcb active status.
  266. //
  267. ScavengerCheckRfcbActive = SrvScavengerCheckRfcbActive;
  268. //
  269. // Get the current time and calculate the next time the scavenge and
  270. // alert algorithms need to run.
  271. //
  272. KeQuerySystemTime( &currentTime );
  273. NextScavengeTime.QuadPart = currentTime.QuadPart + SrvScavengerTimeout.QuadPart;
  274. NextAlertTime.QuadPart = currentTime.QuadPart + SrvAlertSchedule.QuadPart;
  275. ScavengerWorkItem = IoAllocateWorkItem( SrvDeviceObject );
  276. if( !ScavengerWorkItem )
  277. {
  278. return STATUS_INSUFFICIENT_RESOURCES;
  279. }
  280. //
  281. // Initialize the scavenger DPC, which will queue the work item.
  282. //
  283. KeInitializeDpc( &ScavengerDpc, ScavengerTimerRoutine, NULL );
  284. //
  285. // Start the scavenger timer. When the timer expires, the DPC will
  286. // run and will queue the work item.
  287. //
  288. KeInitializeTimer( &ScavengerTimer );
  289. ScavengerInitialized = TRUE;
  290. KeSetTimer( &ScavengerTimer, ScavengerBaseTimeout, &ScavengerDpc );
  291. return STATUS_SUCCESS;
  292. } // SrvInitializeScavenger
  293. VOID
  294. SrvTerminateScavenger (
  295. VOID
  296. )
  297. {
  298. KEVENT scavengerTimerTerminationEvent;
  299. KEVENT scavengerThreadTerminationEvent;
  300. KEVENT resourceThreadTerminationEvent;
  301. KEVENT resourceAllocThreadTerminationEvent;
  302. BOOLEAN waitForResourceThread;
  303. BOOLEAN waitForResourceAllocThread;
  304. BOOLEAN waitForScavengerThread;
  305. KIRQL oldIrql;
  306. if ( ScavengerInitialized ) {
  307. //
  308. // Initialize shutdown events before marking the scavenger as
  309. // shutting down.
  310. //
  311. KeInitializeEvent(
  312. &scavengerTimerTerminationEvent,
  313. NotificationEvent,
  314. FALSE
  315. );
  316. ScavengerTimerTerminationEvent = &scavengerTimerTerminationEvent;
  317. KeInitializeEvent(
  318. &scavengerThreadTerminationEvent,
  319. NotificationEvent,
  320. FALSE
  321. );
  322. ScavengerThreadTerminationEvent = &scavengerThreadTerminationEvent;
  323. KeInitializeEvent(
  324. &resourceThreadTerminationEvent,
  325. NotificationEvent,
  326. FALSE
  327. );
  328. ResourceThreadTerminationEvent = &resourceThreadTerminationEvent;
  329. KeInitializeEvent(
  330. &resourceAllocThreadTerminationEvent,
  331. NotificationEvent,
  332. FALSE
  333. );
  334. ResourceAllocThreadTerminationEvent = &resourceAllocThreadTerminationEvent;
  335. //
  336. // Lock the scavenger, then indicate that we're shutting down.
  337. // Also, notice whether the resource and scavenger threads are
  338. // running. Then release the lock. We must notice whether the
  339. // threads are running while holding the lock so that we can
  340. // know whether to expect the threads to set their termination
  341. // events. (We don't have to do this with the scavenger timer
  342. // because it's always running.)
  343. //
  344. ACQUIRE_SPIN_LOCK( &ScavengerSpinLock, &oldIrql );
  345. waitForScavengerThread = ScavengerRunning;
  346. waitForResourceThread = SrvResourceThreadRunning;
  347. waitForResourceAllocThread = SrvResourceAllocThreadRunning;
  348. ScavengerInitialized = FALSE;
  349. RELEASE_SPIN_LOCK( &ScavengerSpinLock, oldIrql );
  350. //
  351. // Cancel the scavenger timer. If this works, then we know that
  352. // the timer DPC code is not running. Otherwise, it is running
  353. // or queued to run, and we need to wait it to finish.
  354. //
  355. if ( !KeCancelTimer( &ScavengerTimer ) ) {
  356. KeWaitForSingleObject(
  357. &scavengerTimerTerminationEvent,
  358. Executive,
  359. KernelMode, // don't let stack be paged -- event on stack!
  360. FALSE,
  361. NULL
  362. );
  363. }
  364. //
  365. // If the scavenger thread was running when we marked the
  366. // shutdown, wait for it to finish. (If it wasn't running
  367. // before, we know that it can't be running now, because timer
  368. // DPC wouldn't have started it once we marked the shutdown.)
  369. //
  370. if ( waitForScavengerThread ) {
  371. KeWaitForSingleObject(
  372. &scavengerThreadTerminationEvent,
  373. Executive,
  374. KernelMode, // don't let stack be paged -- event on stack!
  375. FALSE,
  376. NULL
  377. );
  378. }
  379. else
  380. {
  381. IoFreeWorkItem( ScavengerWorkItem );
  382. }
  383. //
  384. // If the resource thread was running when we marked the
  385. // shutdown, wait for it to finish. (We know that it can't be
  386. // started because no other part of the server is running.)
  387. //
  388. if ( waitForResourceThread ) {
  389. KeWaitForSingleObject(
  390. &resourceThreadTerminationEvent,
  391. Executive,
  392. KernelMode, // don't let stack be paged -- event on stack!
  393. FALSE,
  394. NULL
  395. );
  396. }
  397. if ( waitForResourceAllocThread ) {
  398. KeWaitForSingleObject(
  399. &resourceAllocThreadTerminationEvent,
  400. Executive,
  401. KernelMode, // don't let stack be paged -- event on stack!
  402. FALSE,
  403. NULL
  404. );
  405. }
  406. }
  407. //
  408. // At this point, no part of the scavenger is running.
  409. //
  410. return;
  411. } // SrvTerminateScavenger
  412. VOID
  413. SrvResourceThread (
  414. IN PVOID Parameter
  415. )
  416. /*++
  417. Routine Description:
  418. Main routine for the resource thread. Is called via an executive
  419. work item when resource work is needed.
  420. Arguments:
  421. None.
  422. Return Value:
  423. None.
  424. --*/
  425. {
  426. BOOLEAN runAgain = TRUE;
  427. PWORK_CONTEXT workContext;
  428. KIRQL oldIrql;
  429. do {
  430. //
  431. // The resource event was signaled. This can indicate a number
  432. // of different things. Currently, this event is signaled for
  433. // the following reasons:
  434. //
  435. // 1. The TDI disconnect event handler was called. The
  436. // disconnected connection was marked. It is up to the
  437. // scavenger shutdown the connection.
  438. //
  439. // 2. A connection has been accepted.
  440. //
  441. IF_DEBUG(SCAV1) {
  442. KdPrint(( "SrvResourceThread: Resource event signaled!\n" ));
  443. }
  444. //
  445. // Service pending disconnects.
  446. //
  447. if ( SrvResourceDisconnectPending ) {
  448. SrvResourceDisconnectPending = FALSE;
  449. ProcessConnectionDisconnects( );
  450. }
  451. //
  452. // Service orphaned connections.
  453. //
  454. if ( SrvResourceOrphanedBlocks ) {
  455. ProcessOrphanedBlocks( );
  456. }
  457. //
  458. // At the end of the loop, check to see whether we need to run
  459. // the loop again.
  460. //
  461. ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
  462. if ( !SrvResourceDisconnectPending &&
  463. !SrvResourceOrphanedBlocks ) {
  464. //
  465. // No more work to do. If the server is shutting down,
  466. // set the event that tells SrvTerminateScavenger that the
  467. // resource thread is done running.
  468. //
  469. SrvResourceThreadRunning = FALSE;
  470. runAgain = FALSE;
  471. if ( !ScavengerInitialized ) {
  472. KeSetEvent( ResourceThreadTerminationEvent, 0, FALSE );
  473. }
  474. }
  475. RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
  476. } while ( runAgain );
  477. ObDereferenceObject( SrvDeviceObject );
  478. return;
  479. } // SrvResourceThread
  480. VOID
  481. SrvResourceAllocThread (
  482. IN PVOID Parameter
  483. )
  484. /*++
  485. Routine Description:
  486. Main routine for the resource thread. Is called via an executive
  487. work item when we need to allocate resources. This was split from
  488. SrvResourceThread because that thread can block on the local FS,
  489. resulting in resource shortages.
  490. Arguments:
  491. None.
  492. Return Value:
  493. None.
  494. --*/
  495. {
  496. BOOLEAN runAgain = TRUE;
  497. PWORK_CONTEXT workContext;
  498. KIRQL oldIrql;
  499. do {
  500. //
  501. // The resource event was signaled. This can indicate a number
  502. // of different things. Currently, this event is signaled for
  503. // the following reasons:
  504. //
  505. // 1. The TDI disconnect event handler was called. The
  506. // disconnected connection was marked. It is up to the
  507. // scavenger shutdown the connection.
  508. //
  509. // 2. A connection has been accepted.
  510. //
  511. IF_DEBUG(SCAV1) {
  512. KdPrint(( "SrvResourceThread: Resource event signaled!\n" ));
  513. }
  514. //
  515. // Service endpoints that need connections.
  516. //
  517. if ( SrvResourceAllocConnection ) {
  518. SrvResourceAllocConnection = FALSE;
  519. CreateConnections( );
  520. }
  521. //
  522. // At the end of the loop, check to see whether we need to run
  523. // the loop again.
  524. //
  525. ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
  526. if ( !SrvResourceAllocConnection ) {
  527. //
  528. // No more work to do. If the server is shutting down,
  529. // set the event that tells SrvTerminateScavenger that the
  530. // resource thread is done running.
  531. //
  532. SrvResourceAllocThreadRunning = FALSE;
  533. runAgain = FALSE;
  534. if ( !ScavengerInitialized ) {
  535. KeSetEvent( ResourceAllocThreadTerminationEvent, 0, FALSE );
  536. }
  537. }
  538. RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
  539. } while ( runAgain );
  540. ObDereferenceObject( SrvDeviceObject );
  541. return;
  542. } // SrvResourceAllocThread
  543. VOID
  544. ScavengerTimerRoutine (
  545. IN PKDPC Dpc,
  546. IN PVOID DeferredContext,
  547. IN PVOID SystemArgument1,
  548. IN PVOID SystemArgument2
  549. )
  550. {
  551. BOOLEAN runShortTerm;
  552. BOOLEAN runScavenger;
  553. BOOLEAN runAlerter;
  554. BOOLEAN start;
  555. LARGE_INTEGER currentTime;
  556. Dpc, DeferredContext; // prevent compiler warnings
  557. //
  558. // Query the system time (in ticks).
  559. //
  560. SET_SERVER_TIME( SrvWorkQueues );
  561. //
  562. // Capture the current time (in 100ns units).
  563. //
  564. currentTime.LowPart = PtrToUlong(SystemArgument1);
  565. currentTime.HighPart = PtrToUlong(SystemArgument2);
  566. //
  567. // Determine which algorithms (if any) need to run.
  568. //
  569. start = FALSE;
  570. if ( !IsListEmpty( &SrvOplockBreaksInProgressList ) ) {
  571. runShortTerm = TRUE;
  572. start = TRUE;
  573. } else {
  574. runShortTerm = FALSE;
  575. }
  576. if ( currentTime.QuadPart >= NextScavengeTime.QuadPart ) {
  577. runScavenger = TRUE;
  578. start = TRUE;
  579. } else {
  580. runScavenger = FALSE;
  581. }
  582. if ( currentTime.QuadPart >= NextAlertTime.QuadPart ) {
  583. runAlerter = TRUE;
  584. start = TRUE;
  585. } else {
  586. runAlerter = FALSE;
  587. }
  588. //
  589. // If necessary, start the scavenger thread. Don't do this if
  590. // the server is shutting down.
  591. //
  592. ACQUIRE_DPC_SPIN_LOCK( &ScavengerSpinLock );
  593. if ( !ScavengerInitialized ) {
  594. KeSetEvent( ScavengerTimerTerminationEvent, 0, FALSE );
  595. } else {
  596. if ( start ) {
  597. if ( runShortTerm ) {
  598. RunShortTermAlgorithm = TRUE;
  599. }
  600. if ( runScavenger ) {
  601. RunScavengerAlgorithm = TRUE;
  602. NextScavengeTime.QuadPart += SrvScavengerTimeout.QuadPart;
  603. }
  604. if ( runAlerter ) {
  605. RunAlerterAlgorithm = TRUE;
  606. NextAlertTime.QuadPart += SrvAlertSchedule.QuadPart;
  607. }
  608. if( !ScavengerRunning )
  609. {
  610. ScavengerRunning = TRUE;
  611. IoQueueWorkItem( ScavengerWorkItem, ScavengerThread, CriticalWorkQueue, NULL );
  612. }
  613. }
  614. //
  615. // Restart the timer.
  616. //
  617. KeSetTimer( &ScavengerTimer, ScavengerBaseTimeout, &ScavengerDpc );
  618. }
  619. RELEASE_DPC_SPIN_LOCK( &ScavengerSpinLock );
  620. return;
  621. } // ScavengerTimerRoutine
  622. #if DBG_STUCK
  623. //
  624. // This keeps a record of the operation which has taken the longest time
  625. // in the server
  626. //
  627. struct {
  628. ULONG Seconds;
  629. UCHAR Command;
  630. UCHAR ClientName[ 16 ];
  631. } SrvMostStuck;
  632. VOID
  633. SrvLookForStuckOperations()
  634. {
  635. USHORT index;
  636. PLIST_ENTRY listEntry;
  637. PLIST_ENTRY connectionListEntry;
  638. PENDPOINT endpoint;
  639. PCONNECTION connection;
  640. KIRQL oldIrql;
  641. BOOLEAN printed = FALSE;
  642. ULONG stuckCount = 0;
  643. //
  644. // Look at all of the InProgress work items and chatter about any
  645. // which look stuck
  646. //
  647. ACQUIRE_LOCK( &SrvEndpointLock );
  648. listEntry = SrvEndpointList.ListHead.Flink;
  649. while ( listEntry != &SrvEndpointList.ListHead ) {
  650. endpoint = CONTAINING_RECORD(
  651. listEntry,
  652. ENDPOINT,
  653. GlobalEndpointListEntry
  654. );
  655. //
  656. // If this endpoint is closing, skip to the next one.
  657. // Otherwise, reference the endpoint so that it can't go away.
  658. //
  659. if ( GET_BLOCK_STATE(endpoint) != BlockStateActive ) {
  660. listEntry = listEntry->Flink;
  661. continue;
  662. }
  663. SrvReferenceEndpoint( endpoint );
  664. index = (USHORT)-1;
  665. while ( TRUE ) {
  666. PLIST_ENTRY wlistEntry, wlistHead;
  667. LARGE_INTEGER now;
  668. //
  669. // Get the next active connection in the table. If no more
  670. // are available, WalkConnectionTable returns NULL.
  671. // Otherwise, it returns a referenced pointer to a
  672. // connection.
  673. //
  674. connection = WalkConnectionTable( endpoint, &index );
  675. if ( connection == NULL ) {
  676. break;
  677. }
  678. //
  679. // Now walk the InProgressWorkItemList to see if any work items
  680. // look stuck
  681. //
  682. wlistHead = &connection->InProgressWorkItemList;
  683. wlistEntry = wlistHead;
  684. KeQuerySystemTime( &now );
  685. ACQUIRE_SPIN_LOCK( connection->EndpointSpinLock, &oldIrql )
  686. while ( wlistEntry->Flink != wlistHead ) {
  687. PWORK_CONTEXT workContext;
  688. PSMB_HEADER header;
  689. LARGE_INTEGER interval;
  690. wlistEntry = wlistEntry->Flink;
  691. workContext = CONTAINING_RECORD(
  692. wlistEntry,
  693. WORK_CONTEXT,
  694. InProgressListEntry
  695. );
  696. interval.QuadPart = now.QuadPart - workContext->OpStartTime.QuadPart;
  697. //
  698. // Any operation over 45 seconds is VERY stuck....
  699. //
  700. if( workContext->IsNotStuck || interval.LowPart < 45 * 10000000 ) {
  701. continue;
  702. }
  703. header = workContext->RequestHeader;
  704. if ( (workContext->BlockHeader.ReferenceCount != 0) &&
  705. (workContext->ProcessingCount != 0) &&
  706. header != NULL ) {
  707. //
  708. // Convert to seconds
  709. //
  710. interval.LowPart /= 10000000;
  711. if( !printed ) {
  712. IF_STRESS() KdPrint(( "--- Potential stuck SRV.SYS Operations ---\n" ));
  713. printed = TRUE;
  714. }
  715. if( interval.LowPart > SrvMostStuck.Seconds ) {
  716. SrvMostStuck.Seconds = interval.LowPart;
  717. RtlCopyMemory( SrvMostStuck.ClientName,
  718. connection->OemClientMachineNameString.Buffer,
  719. MIN( 16, connection->OemClientMachineNameString.Length )),
  720. SrvMostStuck.ClientName[ MIN( 15, connection->OemClientMachineNameString.Length ) ] = 0;
  721. SrvMostStuck.Command = header->Command;
  722. }
  723. if( stuckCount++ < 5 ) {
  724. IF_STRESS() KdPrint(( "Client %s, %u secs, Context %p",
  725. connection->OemClientMachineNameString.Buffer,
  726. interval.LowPart, workContext ));
  727. switch( header->Command ) {
  728. case SMB_COM_NT_CREATE_ANDX:
  729. IF_STRESS() KdPrint(( " NT_CREATE_ANDX\n" ));
  730. break;
  731. case SMB_COM_OPEN_PRINT_FILE:
  732. IF_STRESS() KdPrint(( " OPEN_PRINT_FILE\n" ));
  733. break;
  734. case SMB_COM_CLOSE_PRINT_FILE:
  735. IF_STRESS() KdPrint(( " CLOSE_PRINT_FILE\n" ));
  736. break;
  737. case SMB_COM_CLOSE:
  738. IF_STRESS() KdPrint(( " CLOSE\n" ));
  739. break;
  740. case SMB_COM_SESSION_SETUP_ANDX:
  741. IF_STRESS() KdPrint(( " SESSION_SETUP\n" ));
  742. break;
  743. case SMB_COM_OPEN_ANDX:
  744. IF_STRESS() KdPrint(( " OPEN_ANDX\n" ));
  745. break;
  746. case SMB_COM_NT_TRANSACT:
  747. case SMB_COM_NT_TRANSACT_SECONDARY:
  748. IF_STRESS() KdPrint(( " NT_TRANSACT\n" ));
  749. break;
  750. case SMB_COM_TRANSACTION2:
  751. case SMB_COM_TRANSACTION2_SECONDARY:
  752. IF_STRESS() KdPrint(( " TRANSACTION2\n" ));
  753. break;
  754. case SMB_COM_TRANSACTION:
  755. case SMB_COM_TRANSACTION_SECONDARY:
  756. IF_STRESS() KdPrint(( " TRANSACTION\n" ));
  757. break;
  758. default:
  759. IF_STRESS() KdPrint(( " Cmd %X\n", header->Command ));
  760. break;
  761. }
  762. }
  763. }
  764. }
  765. RELEASE_SPIN_LOCK( connection->EndpointSpinLock, oldIrql );
  766. SrvDereferenceConnection( connection );
  767. } // walk connection list
  768. //
  769. // Capture a pointer to the next endpoint in the list (that one
  770. // can't go away because we hold the endpoint list), then
  771. // dereference the current endpoint.
  772. //
  773. listEntry = listEntry->Flink;
  774. SrvDereferenceEndpoint( endpoint );
  775. } // walk endpoint list
  776. if( printed && SrvMostStuck.Seconds ) {
  777. IF_STRESS() KdPrint(( "Longest so far: %s, %u secs, cmd %u\n", SrvMostStuck.ClientName, SrvMostStuck.Seconds, SrvMostStuck.Command ));
  778. }
  779. if( stuckCount ) {
  780. //DbgBreakPoint();
  781. }
  782. RELEASE_LOCK( &SrvEndpointLock );
  783. }
  784. #endif
  785. VOID
  786. ScavengerThread (
  787. IN PDEVICE_OBJECT DeviceObject,
  788. IN PVOID Parameter
  789. )
  790. /*++
  791. Routine Description:
  792. Main routine for the FSP scavenger thread. Is called via an
  793. executive work item when scavenger work is needed.
  794. Arguments:
  795. None.
  796. Return Value:
  797. None.
  798. --*/
  799. {
  800. BOOLEAN runAgain = TRUE;
  801. BOOLEAN oldPopupStatus;
  802. BOOLEAN finalExecution = FALSE;
  803. KIRQL oldIrql;
  804. Parameter; // prevent compiler warnings
  805. IF_DEBUG(SCAV1) KdPrint(( "ScavengerThread entered\n" ));
  806. //
  807. // Make sure that the thread does not generate pop-ups. We need to do
  808. // this because the scavenger might be called by an Ex worker thread,
  809. // which unlike the srv worker threads, don't have popups disabled.
  810. //
  811. oldPopupStatus = IoSetThreadHardErrorMode( FALSE );
  812. //
  813. // Main loop, executed until no scavenger events are set.
  814. //
  815. do {
  816. #if DBG_STUCK
  817. IF_STRESS() SrvLookForStuckOperations();
  818. #endif
  819. //
  820. // If the short-term timer expired, run that algorithm now.
  821. //
  822. if ( RunShortTermAlgorithm ) {
  823. LARGE_INTEGER currentTime;
  824. RunShortTermAlgorithm = FALSE;
  825. KeQuerySystemTime( &currentTime );
  826. //
  827. // Time out oplock break requests.
  828. //
  829. TimeoutStuckOplockBreaks( &currentTime );
  830. }
  831. //
  832. // If the scavenger timer expired, run that algorithm now.
  833. //
  834. if ( RunScavengerAlgorithm ) {
  835. //KePrintSpinLockCounts( 0 );
  836. RunScavengerAlgorithm = FALSE;
  837. ScavengerAlgorithm( );
  838. }
  839. //
  840. // If the short-term timer expired, run that algorithm now.
  841. // Note that we check the short-term timer twice in the loop
  842. // in order to get more timely processing of the algorithm.
  843. //
  844. //if ( RunShortTermAlgorithm ) {
  845. // RunShortTermAlgorithm = FALSE;
  846. // ShortTermAlgorithm( );
  847. //}
  848. //
  849. // If the alerter timer expired, run that algorithm now.
  850. //
  851. if ( RunAlerterAlgorithm ) {
  852. RunAlerterAlgorithm = FALSE;
  853. AlerterAlgorithm( );
  854. }
  855. //
  856. // At the end of the loop, check to see whether we need to run
  857. // the loop again.
  858. //
  859. ACQUIRE_SPIN_LOCK( &ScavengerSpinLock, &oldIrql );
  860. if ( !RunShortTermAlgorithm &&
  861. !RunScavengerAlgorithm &&
  862. !RunAlerterAlgorithm ) {
  863. //
  864. // No more work to do. If the server is shutting down,
  865. // set the event that tells SrvTerminateScavenger that the
  866. // scavenger thread is done running.
  867. //
  868. ScavengerRunning = FALSE;
  869. runAgain = FALSE;
  870. if ( !ScavengerInitialized ) {
  871. // The server was stopped while the scavenger was queued,
  872. // so we need to delete the WorkItem ourselves.
  873. finalExecution = TRUE;
  874. KeSetEvent( ScavengerThreadTerminationEvent, 0, FALSE );
  875. }
  876. }
  877. RELEASE_SPIN_LOCK( &ScavengerSpinLock, oldIrql );
  878. } while ( runAgain );
  879. //
  880. // reset popup status.
  881. //
  882. IoSetThreadHardErrorMode( oldPopupStatus );
  883. if( finalExecution )
  884. {
  885. IoFreeWorkItem( ScavengerWorkItem );
  886. ScavengerWorkItem = NULL;
  887. }
  888. return;
  889. } // ScavengerThread
  890. VOID
  891. DestroySuspectConnections(
  892. VOID
  893. )
  894. {
  895. USHORT index;
  896. PLIST_ENTRY listEntry;
  897. PLIST_ENTRY connectionListEntry;
  898. PENDPOINT endpoint;
  899. PCONNECTION connection;
  900. BOOLEAN printed = FALSE;
  901. ULONG stuckCount = 0;
  902. IF_DEBUG( SCAV1 ) {
  903. KdPrint(( "Looking for Suspect Connections.\n" ));
  904. }
  905. //
  906. // Look at all of the InProgress work items and chatter about any
  907. // which look stuck
  908. //
  909. ACQUIRE_LOCK( &SrvEndpointLock );
  910. listEntry = SrvEndpointList.ListHead.Flink;
  911. while ( listEntry != &SrvEndpointList.ListHead ) {
  912. endpoint = CONTAINING_RECORD(
  913. listEntry,
  914. ENDPOINT,
  915. GlobalEndpointListEntry
  916. );
  917. //
  918. // If this endpoint is closing, skip to the next one.
  919. // Otherwise, reference the endpoint so that it can't go away.
  920. //
  921. if ( GET_BLOCK_STATE(endpoint) != BlockStateActive ) {
  922. listEntry = listEntry->Flink;
  923. continue;
  924. }
  925. SrvReferenceEndpoint( endpoint );
  926. index = (USHORT)-1;
  927. while ( TRUE ) {
  928. PLIST_ENTRY wlistEntry, wlistHead;
  929. LARGE_INTEGER now;
  930. //
  931. // Get the next active connection in the table. If no more
  932. // are available, WalkConnectionTable returns NULL.
  933. // Otherwise, it returns a referenced pointer to a
  934. // connection.
  935. //
  936. connection = WalkConnectionTable( endpoint, &index );
  937. if ( connection == NULL ) {
  938. break;
  939. }
  940. if( connection->IsConnectionSuspect )
  941. {
  942. // Prevent us from flooding the eventlog by only logging X DOS attacks every 24 hours.
  943. LARGE_INTEGER CurrentTime;
  944. KeQuerySystemTime( &CurrentTime );
  945. if( CurrentTime.QuadPart > SrvLastDosAttackTime.QuadPart + SRV_ONE_DAY )
  946. {
  947. // reset the counter every 24 hours
  948. SrvDOSAttacks = 0;
  949. SrvLastDosAttackTime.QuadPart = CurrentTime.QuadPart;
  950. SrvLogEventOnDOS = TRUE;
  951. }
  952. IF_DEBUG( ERRORS )
  953. {
  954. KdPrint(( "Disconnected suspected DoS attack (%z)\n", (PCSTRING)&connection->OemClientMachineNameString ));
  955. }
  956. RELEASE_LOCK( &SrvEndpointLock );
  957. // Log an event if we need to
  958. if( SrvLogEventOnDOS )
  959. {
  960. SrvLogError(
  961. SrvDeviceObject,
  962. EVENT_SRV_DOS_ATTACK_DETECTED,
  963. STATUS_ACCESS_DENIED,
  964. NULL,
  965. 0,
  966. &connection->ClientMachineNameString,
  967. 1
  968. );
  969. SrvDOSAttacks++;
  970. if( SrvDOSAttacks > SRV_MAX_DOS_ATTACK_EVENT_LOGS )
  971. {
  972. SrvLogEventOnDOS = FALSE;
  973. SrvLogError(
  974. SrvDeviceObject,
  975. EVENT_SRV_TOO_MANY_DOS,
  976. STATUS_ACCESS_DENIED,
  977. NULL,
  978. 0,
  979. NULL,
  980. 0
  981. );
  982. }
  983. }
  984. connection->DisconnectReason = DisconnectSuspectedDOSConnection;
  985. SrvCloseConnection( connection, FALSE );
  986. ACQUIRE_LOCK( &SrvEndpointLock );
  987. }
  988. SrvDereferenceConnection( connection );
  989. } // walk connection list
  990. //
  991. // Capture a pointer to the next endpoint in the list (that one
  992. // can't go away because we hold the endpoint list), then
  993. // dereference the current endpoint.
  994. //
  995. listEntry = listEntry->Flink;
  996. SrvDereferenceEndpoint( endpoint );
  997. } // walk endpoint list
  998. RELEASE_LOCK( &SrvEndpointLock );
  999. RunSuspectConnectionAlgorithm = FALSE;
  1000. }
  1001. VOID
  1002. ScavengerAlgorithm (
  1003. VOID
  1004. )
  1005. {
  1006. LARGE_INTEGER currentTime;
  1007. ULONG currentTick;
  1008. UNICODE_STRING insertionString[2];
  1009. WCHAR secondsBuffer[20];
  1010. WCHAR shortageBuffer[20];
  1011. BOOLEAN logError = FALSE;
  1012. PWORK_QUEUE queue;
  1013. PAGED_CODE( );
  1014. IF_DEBUG(SCAV1) KdPrint(( "ScavengerAlgorithm entered\n" ));
  1015. KeQuerySystemTime( &currentTime );
  1016. GET_SERVER_TIME( SrvWorkQueues, &currentTick );
  1017. //
  1018. // EventSwitch is used to schedule parts of the scavenger algorithm
  1019. // to run every other iteration.
  1020. //
  1021. EventSwitch = !EventSwitch;
  1022. //
  1023. // Time out opens that are waiting too long for the other
  1024. // opener to break the oplock.
  1025. //
  1026. TimeoutWaitingOpens( &currentTime );
  1027. //
  1028. // Time out oplock break requests.
  1029. //
  1030. TimeoutStuckOplockBreaks( &currentTime );
  1031. //
  1032. // Check for malicious attacks
  1033. //
  1034. if( RunSuspectConnectionAlgorithm )
  1035. {
  1036. DestroySuspectConnections( );
  1037. }
  1038. //
  1039. // See if we can free some work items at this time.
  1040. //
  1041. for( queue = SrvWorkQueues; queue < eSrvWorkQueues; queue++ ) {
  1042. LazyFreeQueueDataStructures( queue );
  1043. }
  1044. //
  1045. // See if we need to update QOS information.
  1046. //
  1047. if ( --ScavengerUpdateQosCount < 0 ) {
  1048. UpdateConnectionQos( &currentTime );
  1049. ScavengerUpdateQosCount = SrvScavengerUpdateQosCount;
  1050. }
  1051. //
  1052. // See if we need to walk the rfcb list to update the session
  1053. // last use time.
  1054. //
  1055. if ( --ScavengerCheckRfcbActive < 0 ) {
  1056. UpdateSessionLastUseTime( &currentTime );
  1057. ScavengerCheckRfcbActive = SrvScavengerCheckRfcbActive;
  1058. }
  1059. //
  1060. // See if we need to log an error for resource shortages
  1061. //
  1062. if ( FailedWorkItemAllocations > 0 ||
  1063. SrvOutOfFreeConnectionCount > 0 ||
  1064. SrvOutOfRawWorkItemCount > 0 ||
  1065. SrvFailedBlockingIoCount > 0 ) {
  1066. //
  1067. // Setup the strings for use in logging work item allocation failures.
  1068. //
  1069. insertionString[0].Buffer = shortageBuffer;
  1070. insertionString[0].MaximumLength = sizeof(shortageBuffer);
  1071. insertionString[1].Buffer = secondsBuffer;
  1072. insertionString[1].MaximumLength = sizeof(secondsBuffer);
  1073. (VOID) RtlIntegerToUnicodeString(
  1074. SrvScavengerTimeoutInSeconds * 2,
  1075. 10,
  1076. &insertionString[1]
  1077. );
  1078. logError = TRUE;
  1079. }
  1080. if ( EventSwitch ) {
  1081. ULONG FailedCount;
  1082. //
  1083. // If we were unable to allocate any work items during
  1084. // the last two scavenger intervals, log an error.
  1085. //
  1086. FailedCount = InterlockedExchange( &FailedWorkItemAllocations, 0 );
  1087. if ( FailedCount != 0 ) {
  1088. (VOID) RtlIntegerToUnicodeString(
  1089. FailedCount,
  1090. 10,
  1091. &insertionString[0]
  1092. );
  1093. SrvLogError(
  1094. SrvDeviceObject,
  1095. EVENT_SRV_NO_WORK_ITEM,
  1096. STATUS_INSUFFICIENT_RESOURCES,
  1097. NULL,
  1098. 0,
  1099. insertionString,
  1100. 2
  1101. );
  1102. }
  1103. //
  1104. // Generate periodic events and alerts (for events that
  1105. // could happen very quickly, so we don't flood the event
  1106. // log).
  1107. //
  1108. GeneratePeriodicEvents( );
  1109. } else {
  1110. if ( logError ) {
  1111. //
  1112. // If we failed to find free connections during
  1113. // the last two scavenger intervals, log an error.
  1114. //
  1115. // The out-of-connection logic is being changed because most DC's see these
  1116. // occassionally (once a day) due to a short spike in CPU or network traffic. This one
  1117. // time spike is usually not indicative of an underlying problem, but the event log
  1118. // causes much administrator worry. Thus, we now increment the OutOfFreeConnection count
  1119. // by the number of failed connections each time this algorithm is run. If we encounter a
  1120. // situation where we failed many times in a row, or if the number of failures was unusually
  1121. // high, we will log an event.
  1122. if ( ((SrvOutOfFreeConnectionCount > 0) &&
  1123. (OutOfFreeConnectionConsecutiveCount > 25)) ||
  1124. (SrvOutOfFreeConnectionCount > 100) ) {
  1125. (VOID) RtlIntegerToUnicodeString(
  1126. SrvOutOfFreeConnectionCount+OutOfFreeConnectionConsecutiveCount,
  1127. 10,
  1128. &insertionString[0]
  1129. );
  1130. SrvLogError(
  1131. SrvDeviceObject,
  1132. EVENT_SRV_NO_FREE_CONNECTIONS,
  1133. STATUS_INSUFFICIENT_RESOURCES,
  1134. NULL,
  1135. 0,
  1136. insertionString,
  1137. 2
  1138. );
  1139. SrvOutOfFreeConnectionCount = 0;
  1140. OutOfFreeConnectionConsecutiveCount = 0;
  1141. }
  1142. else if (SrvOutOfFreeConnectionCount > 0) {
  1143. OutOfFreeConnectionConsecutiveCount+= SrvOutOfFreeConnectionCount;
  1144. SrvOutOfFreeConnectionCount = 0;
  1145. } else {
  1146. OutOfFreeConnectionConsecutiveCount = 0;
  1147. }
  1148. //
  1149. // If we failed to find free raw work items during
  1150. // the last two scavenger intervals, log an error.
  1151. //
  1152. if ( SrvOutOfRawWorkItemCount > 0 ) {
  1153. (VOID) RtlIntegerToUnicodeString(
  1154. SrvOutOfRawWorkItemCount,
  1155. 10,
  1156. &insertionString[0]
  1157. );
  1158. SrvLogError(
  1159. SrvDeviceObject,
  1160. EVENT_SRV_NO_FREE_RAW_WORK_ITEM,
  1161. STATUS_INSUFFICIENT_RESOURCES,
  1162. NULL,
  1163. 0,
  1164. insertionString,
  1165. 2
  1166. );
  1167. SrvOutOfRawWorkItemCount = 0;
  1168. }
  1169. //
  1170. // If we failed a blocking io due to resource shortages during
  1171. // the last two scavenger intervals, log an error.
  1172. //
  1173. if ( SrvFailedBlockingIoCount > 0 ) {
  1174. (VOID) RtlIntegerToUnicodeString(
  1175. SrvFailedBlockingIoCount,
  1176. 10,
  1177. &insertionString[0]
  1178. );
  1179. SrvLogError(
  1180. SrvDeviceObject,
  1181. EVENT_SRV_NO_BLOCKING_IO,
  1182. STATUS_INSUFFICIENT_RESOURCES,
  1183. NULL,
  1184. 0,
  1185. insertionString,
  1186. 2
  1187. );
  1188. SrvFailedBlockingIoCount = 0;
  1189. }
  1190. } // if ( logError )
  1191. else
  1192. {
  1193. // Reset the consecutive openconnection failure count, since we
  1194. // only want to detect cases where connections are unavailible for an
  1195. // extended period
  1196. OutOfFreeConnectionConsecutiveCount = 0;
  1197. }
  1198. //
  1199. // Recalculate the core search timeout time.
  1200. //
  1201. RecalcCoreSearchTimeout( );
  1202. //
  1203. // Time out users/connections that have been idle too long
  1204. // (autodisconnect).
  1205. //
  1206. TimeoutSessions( &currentTime );
  1207. //
  1208. // Update the statistics from the the queues
  1209. //
  1210. SrvUpdateStatisticsFromQueues( NULL );
  1211. }
  1212. // Update the DoS variables as necessary for rundown. This reduces the percentage
  1213. // of work-items we free up whenever we detect a DoS by running out of work items
  1214. if( SrvDoSRundownIncreased && !SrvDoSRundownDetector )
  1215. {
  1216. // We've increased the percentage at some time, but no DoS has been detected since
  1217. // the last execution of the scavenger, so we can reduce our tear down amount
  1218. SRV_DOS_DECREASE_TEARDOWN();
  1219. }
  1220. SrvDoSRundownDetector = FALSE;
  1221. return;
  1222. } // ScavengerAlgorithm
  1223. VOID
  1224. AlerterAlgorithm (
  1225. VOID
  1226. )
  1227. /*++
  1228. Routine Description:
  1229. The other scavenger thread. This routine checks the server for
  1230. alert conditions, and if necessary raises alerts.
  1231. Arguments:
  1232. None.
  1233. Return Value:
  1234. None.
  1235. --*/
  1236. {
  1237. PAGED_CODE( );
  1238. IF_DEBUG(SCAV1) KdPrint(( "AlerterAlgorithm entered\n" ));
  1239. CheckErrorCount( &SrvErrorRecord, FALSE );
  1240. CheckErrorCount( &SrvNetworkErrorRecord, TRUE );
  1241. CheckDiskSpace();
  1242. return;
  1243. } // AlerterAlgorithm
  1244. VOID
  1245. CloseIdleConnection (
  1246. IN PCONNECTION Connection,
  1247. IN PLARGE_INTEGER CurrentTime,
  1248. IN PLARGE_INTEGER DisconnectTime,
  1249. IN PLARGE_INTEGER PastExpirationTime,
  1250. IN PLARGE_INTEGER TwoMinuteWarningTime,
  1251. IN PLARGE_INTEGER FiveMinuteWarningTime
  1252. )
  1253. /*++
  1254. Routine Description:
  1255. The routine checks to see if some sessions need to be closed becaused
  1256. it has been idle too long or has exceeded its logon hours.
  1257. Endpoint lock assumed held.
  1258. Arguments:
  1259. Connection - The connection whose sessions we are currently looking at.
  1260. CurrentTime - The currest system time.
  1261. DisconnectTime - The time beyond which the session will be autodisconnected.
  1262. PastExpirationTime - Time when the past expiration message will be sent.
  1263. TwoMinuteWarningTime - Time when the 2 min warning will be sent.
  1264. FiveMinuteWarningTime - Time when the 5 min warning will be sent.
  1265. Return Value:
  1266. None.
  1267. --*/
  1268. {
  1269. PTABLE_HEADER tableHeader;
  1270. NTSTATUS status;
  1271. BOOLEAN sessionClosed = FALSE;
  1272. PPAGED_CONNECTION pagedConnection = Connection->PagedConnection;
  1273. LONG i;
  1274. ULONG AllSessionsIdle = TRUE;
  1275. ULONG HasSessions = FALSE;
  1276. PAGED_CODE( );
  1277. //
  1278. // Is this is a connectionless connection (IPX), check first to see
  1279. // if it's been too long since we heard from the client. The client
  1280. // is supposed to send Echo SMBs every few minutes if nothing else
  1281. // is going on.
  1282. //
  1283. if ( Connection->Endpoint->IsConnectionless ) {
  1284. //
  1285. // Calculate the number of clock ticks that have happened since
  1286. // we last heard from the client. If that's more than we allow,
  1287. // kill the connection.
  1288. //
  1289. GET_SERVER_TIME( Connection->CurrentWorkQueue, (PULONG)&i );
  1290. i -= Connection->LastRequestTime;
  1291. if ( i > 0 && (ULONG)i > SrvIpxAutodisconnectTimeout ) {
  1292. IF_DEBUG( IPX2 ) {
  1293. KdPrint(("CloseIdleConnection: closing IPX conn %p, idle %u\n", Connection, i ));
  1294. }
  1295. Connection->DisconnectReason = DisconnectIdleConnection;
  1296. SrvCloseConnection( Connection, FALSE );
  1297. return;
  1298. }
  1299. }
  1300. //
  1301. // Walk the active connection list, looking for connections that
  1302. // are idle.
  1303. //
  1304. tableHeader = &pagedConnection->SessionTable;
  1305. ACQUIRE_LOCK( &Connection->Lock );
  1306. for ( i = 0; i < tableHeader->TableSize; i++ ) {
  1307. PSESSION session = (PSESSION)tableHeader->Table[i].Owner;
  1308. if( session == NULL ) {
  1309. continue;
  1310. }
  1311. HasSessions = TRUE;
  1312. if ( GET_BLOCK_STATE( session ) == BlockStateActive ) {
  1313. SrvReferenceSession( session );
  1314. RELEASE_LOCK( &Connection->Lock );
  1315. //
  1316. // Test whether the session has been idle too long, and whether
  1317. // there are any files open on the session. If there are open
  1318. // files, we must not close the session, as this would seriously
  1319. // confuse the client. For purposes of autodisconnect, "open
  1320. // files" referes to open searches and blocking comm device
  1321. // requests as well as files actually opened.
  1322. //
  1323. if ( AllSessionsIdle == TRUE &&
  1324. (session->LastUseTime.QuadPart >= DisconnectTime->QuadPart ||
  1325. session->CurrentFileOpenCount != 0 ||
  1326. session->CurrentSearchOpenCount != 0 )
  1327. ) {
  1328. AllSessionsIdle = FALSE;
  1329. }
  1330. // Check if the session has expired
  1331. if( session->LogOffTime.QuadPart < CurrentTime->QuadPart )
  1332. {
  1333. session->IsSessionExpired = TRUE;
  1334. KdPrint(( "Marking session as expired (scavenger)\n" ));
  1335. }
  1336. // Look for forced log-offs
  1337. if ( !SrvEnableForcedLogoff &&
  1338. !session->LogonSequenceInProgress &&
  1339. !session->LogoffAlertSent &&
  1340. PastExpirationTime->QuadPart <
  1341. session->LastExpirationMessage.QuadPart ) {
  1342. //
  1343. // Checks for forced logoff. If the client is beyond his logon
  1344. // hours, force him off. If the end of logon hours is
  1345. // approaching, send a warning message. Forced logoff occurs
  1346. // regardless of whether the client has open files or searches.
  1347. //
  1348. UNICODE_STRING timeString;
  1349. status = TimeToTimeString( &session->KickOffTime, &timeString );
  1350. if ( NT_SUCCESS(status) ) {
  1351. //
  1352. // Only the scavenger thread sets this, so no mutual
  1353. // exclusion is necessary.
  1354. //
  1355. session->LastExpirationMessage = *CurrentTime;
  1356. SrvUserAlertRaise(
  1357. MTXT_Past_Expiration_Message,
  1358. 2,
  1359. &session->Connection->Endpoint->DomainName,
  1360. &timeString,
  1361. &Connection->ClientMachineNameString
  1362. );
  1363. RtlFreeUnicodeString( &timeString );
  1364. }
  1365. // !!! need to raise an admin alert in this case?
  1366. } else if ( !session->LogoffAlertSent &&
  1367. !session->LogonSequenceInProgress &&
  1368. session->KickOffTime.QuadPart < CurrentTime->QuadPart ) {
  1369. session->LogoffAlertSent = TRUE;
  1370. SrvUserAlertRaise(
  1371. MTXT_Expiration_Message,
  1372. 1,
  1373. &session->Connection->Endpoint->DomainName,
  1374. NULL,
  1375. &Connection->ClientMachineNameString
  1376. );
  1377. //
  1378. // If real forced logoff is not enabled, all we do is send an
  1379. // alert, don't actually close the session/connection.
  1380. //
  1381. if ( SrvEnableForcedLogoff ) {
  1382. //
  1383. // Increment the count of sessions that have been
  1384. // forced to logoff.
  1385. //
  1386. SrvStatistics.SessionsForcedLogOff++;
  1387. SrvCloseSession( session );
  1388. sessionClosed = TRUE;
  1389. }
  1390. } else if ( SrvEnableForcedLogoff &&
  1391. !session->LogonSequenceInProgress &&
  1392. !session->TwoMinuteWarningSent &&
  1393. session->KickOffTime.QuadPart <
  1394. TwoMinuteWarningTime->QuadPart ) {
  1395. UNICODE_STRING timeString;
  1396. status = TimeToTimeString( &session->KickOffTime, &timeString );
  1397. if ( NT_SUCCESS(status) ) {
  1398. //
  1399. // We only send a two-minute warning if "real" forced logoff
  1400. // is enabled. If it is not enabled, the client doesn't
  1401. // actually get kicked off, so the extra messages are not
  1402. // necessary.
  1403. //
  1404. session->TwoMinuteWarningSent = TRUE;
  1405. //
  1406. // Send a different alert message based on whether the client
  1407. // has open files and/or searches.
  1408. //
  1409. if ( session->CurrentFileOpenCount != 0 ||
  1410. session->CurrentSearchOpenCount != 0 ) {
  1411. SrvUserAlertRaise(
  1412. MTXT_Immediate_Kickoff_Warning,
  1413. 1,
  1414. &timeString,
  1415. NULL,
  1416. &Connection->ClientMachineNameString
  1417. );
  1418. } else {
  1419. SrvUserAlertRaise(
  1420. MTXT_Kickoff_Warning,
  1421. 1,
  1422. &session->Connection->Endpoint->DomainName,
  1423. NULL,
  1424. &Connection->ClientMachineNameString
  1425. );
  1426. }
  1427. RtlFreeUnicodeString( &timeString );
  1428. }
  1429. } else if ( !session->FiveMinuteWarningSent &&
  1430. !session->LogonSequenceInProgress &&
  1431. session->KickOffTime.QuadPart <
  1432. FiveMinuteWarningTime->QuadPart ) {
  1433. UNICODE_STRING timeString;
  1434. status = TimeToTimeString( &session->KickOffTime, &timeString );
  1435. if ( NT_SUCCESS(status) ) {
  1436. session->FiveMinuteWarningSent = TRUE;
  1437. SrvUserAlertRaise(
  1438. MTXT_Expiration_Warning,
  1439. 2,
  1440. &session->Connection->Endpoint->DomainName,
  1441. &timeString,
  1442. &Connection->ClientMachineNameString
  1443. );
  1444. RtlFreeUnicodeString( &timeString );
  1445. }
  1446. }
  1447. SrvDereferenceSession( session );
  1448. ACQUIRE_LOCK( &Connection->Lock );
  1449. } // if GET_BLOCK_STATE(session) == BlockStateActive
  1450. } // for
  1451. //
  1452. // Nuke the connection if no sessions are active, and we have not heard
  1453. // from the client for one minute, drop the connection
  1454. //
  1455. if( HasSessions == FALSE ) {
  1456. RELEASE_LOCK( &Connection->Lock );
  1457. GET_SERVER_TIME( Connection->CurrentWorkQueue, (PULONG)&i );
  1458. i -= Connection->LastRequestTime;
  1459. if ( i > 0 && (ULONG)i > SrvConnectionNoSessionsTimeout ) {
  1460. #if SRVDBG29
  1461. UpdateConnectionHistory( "IDLE", Connection->Endpoint, Connection );
  1462. #endif
  1463. Connection->DisconnectReason = DisconnectIdleConnection;
  1464. SrvCloseConnection( Connection, FALSE );
  1465. }
  1466. } else if ( (sessionClosed && (Connection->CurrentNumberOfSessions == 0)) ||
  1467. (HasSessions == TRUE && AllSessionsIdle == TRUE) ) {
  1468. //
  1469. // Update the statistics for the 'AllSessionsIdle' case
  1470. //
  1471. SrvStatistics.SessionsTimedOut += Connection->CurrentNumberOfSessions;
  1472. RELEASE_LOCK( &Connection->Lock );
  1473. #if SRVDBG29
  1474. UpdateConnectionHistory( "IDLE", Connection->Endpoint, Connection );
  1475. #endif
  1476. Connection->DisconnectReason = DisconnectIdleConnection;
  1477. SrvCloseConnection( Connection, FALSE );
  1478. } else {
  1479. //
  1480. // If this connection has more than 20 core searches, we go in and
  1481. // try to remove dups. 20 is an arbitrary number.
  1482. //
  1483. if ( (pagedConnection->CurrentNumberOfCoreSearches > 20) &&
  1484. SrvRemoveDuplicateSearches ) {
  1485. RemoveDuplicateCoreSearches( pagedConnection );
  1486. }
  1487. RELEASE_LOCK( &Connection->Lock );
  1488. }
  1489. } // CloseIdleConnection
  1490. VOID
  1491. CreateConnections (
  1492. VOID
  1493. )
  1494. /*++
  1495. Routine Description:
  1496. This function attempts to service all endpoints that do not have
  1497. free connections available.
  1498. Arguments:
  1499. None.
  1500. Return Value:
  1501. None.
  1502. --*/
  1503. {
  1504. ULONG count;
  1505. PLIST_ENTRY listEntry;
  1506. PENDPOINT endpoint;
  1507. PAGED_CODE( );
  1508. ACQUIRE_LOCK( &SrvEndpointLock );
  1509. //
  1510. // Walk the endpoint list, looking for endpoints that need
  1511. // connections. Note that we hold the endpoint lock for the
  1512. // duration of this routine. This keeps the endpoint list from
  1513. // changing.
  1514. //
  1515. // Note that we add connections based on level of need, so that
  1516. // if we are unable to create as many as we'd like, we at least
  1517. // take care of the most needy endpoints first.
  1518. //
  1519. for ( count = 0 ; count < SrvFreeConnectionMinimum; count++ ) {
  1520. listEntry = SrvEndpointList.ListHead.Flink;
  1521. while ( listEntry != &SrvEndpointList.ListHead ) {
  1522. endpoint = CONTAINING_RECORD(
  1523. listEntry,
  1524. ENDPOINT,
  1525. GlobalEndpointListEntry
  1526. );
  1527. //
  1528. // If the endpoint's free connection count is at or below
  1529. // our current level, try to create a connection now.
  1530. //
  1531. if ( (endpoint->FreeConnectionCount <= count) &&
  1532. (GET_BLOCK_STATE(endpoint) == BlockStateActive) ) {
  1533. //
  1534. // Try to create a connection. If this fails, leave.
  1535. //
  1536. if ( !NT_SUCCESS(SrvOpenConnection( endpoint )) ) {
  1537. RELEASE_LOCK( &SrvEndpointLock );
  1538. return;
  1539. }
  1540. }
  1541. listEntry = listEntry->Flink;
  1542. } // walk endpoint list
  1543. } // 0 <= count < SrvFreeConnectionMinimum
  1544. RELEASE_LOCK( &SrvEndpointLock );
  1545. return;
  1546. } // CreateConnections
  1547. VOID
  1548. GeneratePeriodicEvents (
  1549. VOID
  1550. )
  1551. /*++
  1552. Routine Description:
  1553. This function is called when the scavenger timeout occurs. It
  1554. generates events for things that have happened in the previous
  1555. period for which we did not want to immediately generate an event,
  1556. for fear of flooding the event log. An example of such an event is
  1557. being unable to allocate pool.
  1558. Arguments:
  1559. None.
  1560. Return Value:
  1561. None.
  1562. --*/
  1563. {
  1564. ULONG capturedNonPagedFailureCount;
  1565. ULONG capturedPagedFailureCount;
  1566. ULONG capturedNonPagedLimitHitCount;
  1567. ULONG capturedPagedLimitHitCount;
  1568. ULONG nonPagedFailureCount;
  1569. ULONG pagedFailureCount;
  1570. ULONG nonPagedLimitHitCount;
  1571. ULONG pagedLimitHitCount;
  1572. PAGED_CODE( );
  1573. //
  1574. // Capture pool allocation failure statistics.
  1575. //
  1576. capturedNonPagedLimitHitCount = SrvNonPagedPoolLimitHitCount;
  1577. capturedNonPagedFailureCount = SrvStatistics.NonPagedPoolFailures;
  1578. capturedPagedLimitHitCount = SrvPagedPoolLimitHitCount;
  1579. capturedPagedFailureCount = SrvStatistics.PagedPoolFailures;
  1580. //
  1581. // Compute failure counts for the last period. The FailureCount
  1582. // fields in the statistics structure count both hitting the
  1583. // server's configuration limit and hitting the system's limit. The
  1584. // local versions of FailureCount include only system failures.
  1585. //
  1586. nonPagedLimitHitCount =
  1587. capturedNonPagedLimitHitCount - LastNonPagedPoolLimitHitCount;
  1588. nonPagedFailureCount =
  1589. capturedNonPagedFailureCount - LastNonPagedPoolFailureCount -
  1590. nonPagedLimitHitCount;
  1591. pagedLimitHitCount =
  1592. capturedPagedLimitHitCount - LastPagedPoolLimitHitCount;
  1593. pagedFailureCount =
  1594. capturedPagedFailureCount - LastPagedPoolFailureCount -
  1595. pagedLimitHitCount;
  1596. //
  1597. // Saved the current failure counts for next time.
  1598. //
  1599. LastNonPagedPoolLimitHitCount = capturedNonPagedLimitHitCount;
  1600. LastNonPagedPoolFailureCount = capturedNonPagedFailureCount;
  1601. LastPagedPoolLimitHitCount = capturedPagedLimitHitCount;
  1602. LastPagedPoolFailureCount = capturedPagedFailureCount;
  1603. //
  1604. // If we hit the nonpaged pool limit at least once in the last
  1605. // period, generate an event.
  1606. //
  1607. if ( nonPagedLimitHitCount != 0 ) {
  1608. SrvLogError(
  1609. SrvDeviceObject,
  1610. EVENT_SRV_NONPAGED_POOL_LIMIT,
  1611. STATUS_INSUFFICIENT_RESOURCES,
  1612. &nonPagedLimitHitCount,
  1613. sizeof( nonPagedLimitHitCount ),
  1614. NULL,
  1615. 0
  1616. );
  1617. }
  1618. //
  1619. // If we had any nonpaged pool allocations failures in the last
  1620. // period, generate an event.
  1621. //
  1622. if ( nonPagedFailureCount != 0 ) {
  1623. SrvLogError(
  1624. SrvDeviceObject,
  1625. EVENT_SRV_NO_NONPAGED_POOL,
  1626. STATUS_INSUFFICIENT_RESOURCES,
  1627. &nonPagedFailureCount,
  1628. sizeof( nonPagedFailureCount ),
  1629. NULL,
  1630. 0
  1631. );
  1632. }
  1633. //
  1634. // If we hit the paged pool limit at least once in the last period,
  1635. // generate an event.
  1636. //
  1637. if ( pagedLimitHitCount != 0 ) {
  1638. SrvLogError(
  1639. SrvDeviceObject,
  1640. EVENT_SRV_PAGED_POOL_LIMIT,
  1641. STATUS_INSUFFICIENT_RESOURCES,
  1642. &pagedLimitHitCount,
  1643. sizeof( pagedLimitHitCount ),
  1644. NULL,
  1645. 0
  1646. );
  1647. }
  1648. //
  1649. // If we had any paged pool allocations failures in the last period,
  1650. // generate an event.
  1651. //
  1652. if ( pagedFailureCount != 0 ) {
  1653. SrvLogError(
  1654. SrvDeviceObject,
  1655. EVENT_SRV_NO_PAGED_POOL,
  1656. STATUS_INSUFFICIENT_RESOURCES,
  1657. &pagedFailureCount,
  1658. sizeof( pagedFailureCount ),
  1659. NULL,
  1660. 0
  1661. );
  1662. }
  1663. return;
  1664. } // GeneratePeriodicEvents
  1665. VOID
  1666. ProcessConnectionDisconnects (
  1667. VOID
  1668. )
  1669. /*++
  1670. Routine Description:
  1671. This function processes connection disconnects.
  1672. Arguments:
  1673. None.
  1674. Return Value:
  1675. None.
  1676. --*/
  1677. {
  1678. PLIST_ENTRY listEntry;
  1679. PCONNECTION connection;
  1680. KIRQL oldIrql;
  1681. //
  1682. // Run through the list of connection with pending disconnects.
  1683. // Do the work necessary to shut the disconnection connection
  1684. // down.
  1685. //
  1686. ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
  1687. while ( !IsListEmpty( &SrvDisconnectQueue ) ) {
  1688. //
  1689. // This thread already owns the disconnect queue spin lock
  1690. // and there is at least one entry on the queue. Proceed.
  1691. //
  1692. listEntry = RemoveHeadList( &SrvDisconnectQueue );
  1693. connection = CONTAINING_RECORD(
  1694. listEntry,
  1695. CONNECTION,
  1696. ListEntry
  1697. );
  1698. ASSERT( connection->DisconnectPending );
  1699. connection->DisconnectPending = FALSE;
  1700. RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
  1701. IF_STRESS() {
  1702. if( connection->InProgressWorkContextCount > 0 )
  1703. {
  1704. KdPrint(("Abortive Disconnect for %z while work-in-progress (reason %d)\n", (PCSTRING)&connection->OemClientMachineNameString, connection->DisconnectReason ));
  1705. }
  1706. }
  1707. //
  1708. // Do the disconnection processing. Dereference the connection
  1709. // an extra time to account for the reference made when it was
  1710. // put on the disconnect queue.
  1711. //
  1712. #if SRVDBG29
  1713. UpdateConnectionHistory( "PDSC", connection->Endpoint, connection );
  1714. #endif
  1715. SrvCloseConnection( connection, TRUE );
  1716. SrvDereferenceConnection( connection );
  1717. //
  1718. // We are about to go through the loop again, reacquire
  1719. // the disconnect queue spin lock first.
  1720. //
  1721. ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
  1722. }
  1723. RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
  1724. return;
  1725. } // ProcessConnectionDisconnects
  1726. VOID SRVFASTCALL
  1727. SrvServiceWorkItemShortage (
  1728. IN PWORK_CONTEXT workContext
  1729. )
  1730. {
  1731. PLIST_ENTRY listEntry;
  1732. PCONNECTION connection;
  1733. KIRQL oldIrql;
  1734. BOOLEAN moreWork;
  1735. PWORK_QUEUE queue;
  1736. ASSERT( workContext );
  1737. queue = workContext->CurrentWorkQueue;
  1738. IF_DEBUG( WORKITEMS ) {
  1739. KdPrint(("SrvServiceWorkItemShortage: Processor %p\n",
  1740. (PVOID)(queue - SrvWorkQueues) ));
  1741. }
  1742. workContext->FspRestartRoutine = SrvRestartReceive;
  1743. ASSERT( queue >= SrvWorkQueues && queue < eSrvWorkQueues );
  1744. //
  1745. // If we got called, it's likely that we're running short of WorkItems.
  1746. // Allocate more if it makes sense.
  1747. //
  1748. do {
  1749. PWORK_CONTEXT NewWorkContext;
  1750. SrvAllocateNormalWorkItem( &NewWorkContext, queue );
  1751. if ( NewWorkContext != NULL ) {
  1752. IF_DEBUG( WORKITEMS ) {
  1753. KdPrint(( "SrvServiceWorkItemShortage: Created new work context "
  1754. "block\n" ));
  1755. }
  1756. SrvPrepareReceiveWorkItem( NewWorkContext, TRUE );
  1757. } else {
  1758. InterlockedIncrement( &FailedWorkItemAllocations );
  1759. break;
  1760. }
  1761. } while ( queue->FreeWorkItems < queue->MinFreeWorkItems );
  1762. if( GET_BLOCK_TYPE(workContext) == BlockTypeWorkContextSpecial ) {
  1763. //
  1764. // We've been called with a special workitem telling us to allocate
  1765. // more standby WorkContext structures. Since our passed-in workContext
  1766. // is not a "standard one", we can't use it for any further work
  1767. // on starved connections. Just release this workContext and return.
  1768. //
  1769. ACQUIRE_SPIN_LOCK( &queue->SpinLock, &oldIrql );
  1770. SET_BLOCK_TYPE( workContext, BlockTypeGarbage );
  1771. RELEASE_SPIN_LOCK( &queue->SpinLock, oldIrql );
  1772. return;
  1773. }
  1774. ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
  1775. //
  1776. // Run through the list of queued connections and find one that
  1777. // we can service with this workContext. This will ignore processor
  1778. // affinity, but we're in exceptional times. The workContext will
  1779. // be freed back to the correct queue when done.
  1780. //
  1781. while( !IsListEmpty( &SrvNeedResourceQueue ) ) {
  1782. connection = CONTAINING_RECORD( SrvNeedResourceQueue.Flink, CONNECTION, ListEntry );
  1783. IF_DEBUG( WORKITEMS ) {
  1784. KdPrint(("SrvServiceWorkItemShortage: Processing connection %p.\n",
  1785. connection ));
  1786. }
  1787. ASSERT( connection->OnNeedResourceQueue );
  1788. ASSERT( connection->BlockHeader.ReferenceCount > 0 );
  1789. if( GET_BLOCK_STATE( connection ) != BlockStateActive ) {
  1790. IF_DEBUG( WORKITEMS ) {
  1791. KdPrint(("SrvServiceWorkItemShortage: Connection %p closing.\n", connection ));
  1792. }
  1793. //
  1794. // Take it off the queue
  1795. //
  1796. SrvRemoveEntryList(
  1797. &SrvNeedResourceQueue,
  1798. &connection->ListEntry
  1799. );
  1800. connection->OnNeedResourceQueue = FALSE;
  1801. RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
  1802. //
  1803. // Remove the queue reference
  1804. //
  1805. SrvDereferenceConnection( connection );
  1806. ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
  1807. continue;
  1808. }
  1809. //
  1810. // Reference this connection so no one can delete this from under us.
  1811. //
  1812. ACQUIRE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
  1813. SrvReferenceConnectionLocked( connection );
  1814. //
  1815. // Service the connection
  1816. //
  1817. do {
  1818. if( IsListEmpty( &connection->OplockWorkList ) && !connection->ReceivePending )
  1819. break;
  1820. IF_DEBUG( WORKITEMS ) {
  1821. KdPrint(("Work to do on connection %p\n", connection ));
  1822. }
  1823. INITIALIZE_WORK_CONTEXT( queue, workContext );
  1824. //
  1825. // Reference connection here.
  1826. //
  1827. workContext->Connection = connection;
  1828. SrvReferenceConnectionLocked( connection );
  1829. workContext->Endpoint = connection->Endpoint;
  1830. //
  1831. // Service this connection.
  1832. //
  1833. SrvFsdServiceNeedResourceQueue( &workContext, &oldIrql );
  1834. moreWork = (BOOLEAN) ( workContext != NULL &&
  1835. (!IsListEmpty(&connection->OplockWorkList) ||
  1836. connection->ReceivePending) &&
  1837. connection->OnNeedResourceQueue);
  1838. } while( moreWork );
  1839. //
  1840. // Is it now off the queue?
  1841. //
  1842. if ( !connection->OnNeedResourceQueue ) {
  1843. IF_DEBUG( WORKITEMS ) {
  1844. KdPrint(("SrvServiceWorkItemShortage: connection %p removed by another thread.\n", connection ));
  1845. }
  1846. RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
  1847. RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
  1848. //
  1849. // Remove this routine's reference.
  1850. //
  1851. SrvDereferenceConnection( connection );
  1852. if( workContext == NULL ) {
  1853. IF_DEBUG( WORKITEMS ) {
  1854. KdPrint(("SrvServiceWorkItemShortage: DONE at %d\n", __LINE__ ));
  1855. }
  1856. return;
  1857. }
  1858. ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
  1859. continue;
  1860. }
  1861. RELEASE_DPC_SPIN_LOCK( connection->EndpointSpinLock );
  1862. //
  1863. // The connection is still on the queue. Keep it on the queue if there is more
  1864. // work to be done for it.
  1865. //
  1866. if( !IsListEmpty(&connection->OplockWorkList) || connection->ReceivePending ) {
  1867. RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
  1868. if( workContext ) {
  1869. RETURN_FREE_WORKITEM( workContext );
  1870. }
  1871. //
  1872. // Remove this routine's reference.
  1873. //
  1874. SrvDereferenceConnection( connection );
  1875. IF_DEBUG( WORKITEMS ) {
  1876. KdPrint(("SrvServiceWorkItemShortage: More to do for %p. LATER\n", connection ));
  1877. }
  1878. return;
  1879. }
  1880. //
  1881. // All the work has been done for this connection. Get it off the resource queue
  1882. //
  1883. IF_DEBUG( WORKITEMS ) {
  1884. KdPrint(("SrvServiceWorkItemShortage: Take %p off resource queue\n", connection ));
  1885. }
  1886. SrvRemoveEntryList(
  1887. &SrvNeedResourceQueue,
  1888. &connection->ListEntry
  1889. );
  1890. connection->OnNeedResourceQueue = FALSE;
  1891. RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
  1892. //
  1893. // Remove queue reference
  1894. //
  1895. SrvDereferenceConnection( connection );
  1896. //
  1897. // Remove this routine's reference.
  1898. //
  1899. SrvDereferenceConnection( connection );
  1900. IF_DEBUG( WORKITEMS ) {
  1901. KdPrint(("SrvServiceWorkItemShortage: Connection %p removed from queue.\n", connection ));
  1902. }
  1903. if( workContext == NULL ) {
  1904. IF_DEBUG( WORKITEMS ) {
  1905. KdPrint(("SrvServiceWorkItemShortage: DONE at %d\n", __LINE__ ));
  1906. }
  1907. return;
  1908. }
  1909. ACQUIRE_GLOBAL_SPIN_LOCK( Fsd, &oldIrql );
  1910. }
  1911. RELEASE_GLOBAL_SPIN_LOCK( Fsd, oldIrql );
  1912. //
  1913. // See if we need to free the workContext
  1914. //
  1915. if ( workContext != NULL ) {
  1916. IF_DEBUG( WORKITEMS ) {
  1917. KdPrint(("SrvServiceWorkItemShortage: Freeing WorkContext block %p\n",
  1918. workContext ));
  1919. }
  1920. workContext->BlockHeader.ReferenceCount = 0;
  1921. RETURN_FREE_WORKITEM( workContext );
  1922. }
  1923. IF_DEBUG(WORKITEMS) KdPrint(( "SrvServiceWorkItemShortage DONE at %d\n", __LINE__ ));
  1924. } // SrvServiceWorkItemShortage
  1925. VOID SRVFASTCALL
  1926. SrvServiceDoSTearDown (
  1927. IN PWORK_CONTEXT WorkContext
  1928. )
  1929. /*++
  1930. Routine Description:
  1931. This routine is called when we perceive a DoS attack against us. It results
  1932. in us tearing down connections randomly who have WorkItems trapped in the
  1933. transports to help prevent the DoS.
  1934. Arguments:
  1935. WorkContext - The special work item used to trigger this routine
  1936. Return Value:
  1937. None
  1938. --*/
  1939. {
  1940. USHORT index;
  1941. PLIST_ENTRY listEntry;
  1942. PLIST_ENTRY connectionListEntry;
  1943. PENDPOINT endpoint;
  1944. PCONNECTION connection;
  1945. BOOLEAN printed = FALSE;
  1946. LONG TearDownAmount = SRV_DOS_GET_TEARDOWN();
  1947. ASSERT( GET_BLOCK_TYPE(WorkContext) == BlockTypeWorkContextSpecial );
  1948. ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );
  1949. SRV_DOS_INCREASE_TEARDOWN();
  1950. SrvDoSRundownDetector = TRUE;
  1951. // Tear down some connections. Look for ones with operations pending in the transport
  1952. ACQUIRE_LOCK( &SrvEndpointLock );
  1953. listEntry = SrvEndpointList.ListHead.Flink;
  1954. while ( (TearDownAmount > 0) && (listEntry != &SrvEndpointList.ListHead) ) {
  1955. endpoint = CONTAINING_RECORD(
  1956. listEntry,
  1957. ENDPOINT,
  1958. GlobalEndpointListEntry
  1959. );
  1960. //
  1961. // If this endpoint is closing, skip to the next one.
  1962. // Otherwise, reference the endpoint so that it can't go away.
  1963. //
  1964. if ( GET_BLOCK_STATE(endpoint) != BlockStateActive ) {
  1965. listEntry = listEntry->Flink;
  1966. continue;
  1967. }
  1968. SrvReferenceEndpoint( endpoint );
  1969. index = (USHORT)-1;
  1970. while ( TearDownAmount > 0 ) {
  1971. PLIST_ENTRY wlistEntry, wlistHead;
  1972. LARGE_INTEGER now;
  1973. //
  1974. // Get the next active connection in the table. If no more
  1975. // are available, WalkConnectionTable returns NULL.
  1976. // Otherwise, it returns a referenced pointer to a
  1977. // connection.
  1978. //
  1979. connection = WalkConnectionTable( endpoint, &index );
  1980. if ( connection == NULL ) {
  1981. break;
  1982. }
  1983. //
  1984. // To determine if we should tear this connection down, we require that there be work waiting on
  1985. // the transport, since that is the way anonymous users can attack us. If there is, we use a
  1986. // random method based on the timestamp of the last time this was ran. We cycle through the connections
  1987. // and use the index to determine if a teardown is issued (for a psuedo-random result)
  1988. //
  1989. if( (GET_BLOCK_STATE(connection) == BlockStateActive) && (connection->OperationsPendingOnTransport > 0) )
  1990. {
  1991. RELEASE_LOCK( &SrvEndpointLock );
  1992. KdPrint(( "Disconnected suspected DoS attacker by WorkItem shortage (%z)\n", (PCSTRING)&connection->OemClientMachineNameString ));
  1993. TearDownAmount -= connection->InProgressWorkContextCount;
  1994. SrvCloseConnection( connection, FALSE );
  1995. ACQUIRE_LOCK( &SrvEndpointLock );
  1996. }
  1997. SrvDereferenceConnection( connection );
  1998. } // walk connection list
  1999. index = (USHORT)-1;
  2000. while ( TearDownAmount > 0 ) {
  2001. PLIST_ENTRY wlistEntry, wlistHead;
  2002. LARGE_INTEGER now;
  2003. //
  2004. // Get the next active connection in the table. If no more
  2005. // are available, WalkConnectionTable returns NULL.
  2006. // Otherwise, it returns a referenced pointer to a
  2007. // connection.
  2008. //
  2009. connection = WalkConnectionTable( endpoint, &index );
  2010. if ( connection == NULL ) {
  2011. break;
  2012. }
  2013. //
  2014. // To determine if we should tear this connection down, we require that there be work waiting on
  2015. // the transport, since that is the way anonymous users can attack us. If there is, we use a
  2016. // random method based on the timestamp of the last time this was ran. We cycle through the connections
  2017. // and use the index to determine if a teardown is issued (for a psuedo-random result)
  2018. //
  2019. if( (GET_BLOCK_STATE(connection) == BlockStateActive) && (connection->InProgressWorkContextCount > 0) )
  2020. {
  2021. RELEASE_LOCK( &SrvEndpointLock );
  2022. KdPrint(( "Disconnected suspected DoS attack triggered by WorkItem shortage\n" ));
  2023. TearDownAmount -= connection->InProgressWorkContextCount;
  2024. SrvCloseConnection( connection, FALSE );
  2025. ACQUIRE_LOCK( &SrvEndpointLock );
  2026. }
  2027. SrvDereferenceConnection( connection );
  2028. } // walk connection list
  2029. //
  2030. // Capture a pointer to the next endpoint in the list (that one
  2031. // can't go away because we hold the endpoint list), then
  2032. // dereference the current endpoint.
  2033. //
  2034. listEntry = listEntry->Flink;
  2035. SrvDereferenceEndpoint( endpoint );
  2036. } // walk endpoint list
  2037. RELEASE_LOCK( &SrvEndpointLock );
  2038. // This is the special work item for tearing down connections to free WORK_ITEMS. We're done, so release it
  2039. SET_BLOCK_TYPE( WorkContext, BlockTypeGarbage );
  2040. SRV_DOS_COMPLETE_TEARDOWN();
  2041. return;
  2042. }
  2043. VOID
  2044. TimeoutSessions (
  2045. IN PLARGE_INTEGER CurrentTime
  2046. )
  2047. /*++
  2048. Routine Description:
  2049. This routine walks the ordered list of sessions and closes those
  2050. that have been idle too long, sends warning messages to those
  2051. that are about to be forced closed due to logon hours expiring,
  2052. and closes those whose logon hours have expired.
  2053. Arguments:
  2054. CurrentTime - the current system time.
  2055. Return Value:
  2056. None
  2057. --*/
  2058. {
  2059. USHORT index;
  2060. LARGE_INTEGER oldestTime;
  2061. LARGE_INTEGER pastExpirationTime;
  2062. LARGE_INTEGER twoMinuteWarningTime;
  2063. LARGE_INTEGER fiveMinuteWarningTime;
  2064. LARGE_INTEGER time;
  2065. LARGE_INTEGER searchCutoffTime;
  2066. PLIST_ENTRY listEntry;
  2067. PENDPOINT endpoint;
  2068. PCONNECTION connection;
  2069. PAGED_CODE( );
  2070. ACQUIRE_LOCK( &SrvConfigurationLock );
  2071. //
  2072. // If autodisconnect is turned off (the timeout == 0) set the oldest
  2073. // last use time to zero so that we and don't attempt to
  2074. // autodisconnect sessions.
  2075. //
  2076. if ( SrvAutodisconnectTimeout.QuadPart == 0 ) {
  2077. oldestTime.QuadPart = 0;
  2078. } else {
  2079. //
  2080. // Determine the oldest last use time a session can have and not
  2081. // be closed.
  2082. //
  2083. oldestTime.QuadPart = CurrentTime->QuadPart -
  2084. SrvAutodisconnectTimeout.QuadPart;
  2085. }
  2086. searchCutoffTime.QuadPart = (*CurrentTime).QuadPart - SrvSearchMaxTimeout.QuadPart;
  2087. RELEASE_LOCK( &SrvConfigurationLock );
  2088. //
  2089. // Set up the warning times. If a client's kick-off time is sooner
  2090. // than one of these times, an appropriate warning message is sent
  2091. // to the client.
  2092. //
  2093. time.QuadPart = 10*1000*1000*60*2; // two minutes
  2094. twoMinuteWarningTime.QuadPart = CurrentTime->QuadPart + time.QuadPart;
  2095. time.QuadPart = (ULONG)10*1000*1000*60*5; // five minutes
  2096. fiveMinuteWarningTime.QuadPart = CurrentTime->QuadPart + time.QuadPart;
  2097. pastExpirationTime.QuadPart = CurrentTime->QuadPart - time.QuadPart;
  2098. //
  2099. // Walk each connection and determine if we should close it.
  2100. //
  2101. ACQUIRE_LOCK( &SrvEndpointLock );
  2102. listEntry = SrvEndpointList.ListHead.Flink;
  2103. while ( listEntry != &SrvEndpointList.ListHead ) {
  2104. endpoint = CONTAINING_RECORD(
  2105. listEntry,
  2106. ENDPOINT,
  2107. GlobalEndpointListEntry
  2108. );
  2109. //
  2110. // If this endpoint is closing, skip to the next one.
  2111. // Otherwise, reference the endpoint so that it can't go away.
  2112. //
  2113. if ( GET_BLOCK_STATE(endpoint) != BlockStateActive ) {
  2114. listEntry = listEntry->Flink;
  2115. continue;
  2116. }
  2117. SrvReferenceEndpoint( endpoint );
  2118. //
  2119. // Walk the endpoint's connection table.
  2120. //
  2121. index = (USHORT)-1;
  2122. while ( TRUE ) {
  2123. //
  2124. // Get the next active connection in the table. If no more
  2125. // are available, WalkConnectionTable returns NULL.
  2126. // Otherwise, it returns a referenced pointer to a
  2127. // connection.
  2128. //
  2129. connection = WalkConnectionTable( endpoint, &index );
  2130. if ( connection == NULL ) {
  2131. break;
  2132. }
  2133. RELEASE_LOCK( &SrvEndpointLock );
  2134. CloseIdleConnection(
  2135. connection,
  2136. CurrentTime,
  2137. &oldestTime,
  2138. &pastExpirationTime,
  2139. &twoMinuteWarningTime,
  2140. &fiveMinuteWarningTime
  2141. );
  2142. //
  2143. // Time out old core search blocks.
  2144. //
  2145. if ( GET_BLOCK_STATE(connection) == BlockStateActive ) {
  2146. (VOID)SrvTimeoutSearches(
  2147. &searchCutoffTime,
  2148. connection,
  2149. FALSE
  2150. );
  2151. }
  2152. ACQUIRE_LOCK( &SrvEndpointLock );
  2153. SrvDereferenceConnection( connection );
  2154. } // walk connection table
  2155. //
  2156. // Capture a pointer to the next endpoint in the list (that one
  2157. // can't go away because we hold the endpoint list), then
  2158. // dereference the current endpoint.
  2159. //
  2160. listEntry = listEntry->Flink;
  2161. SrvDereferenceEndpoint( endpoint );
  2162. } // walk endpoint list
  2163. RELEASE_LOCK( &SrvEndpointLock );
  2164. } // TimeoutSessions
  2165. VOID
  2166. TimeoutWaitingOpens (
  2167. IN PLARGE_INTEGER CurrentTime
  2168. )
  2169. /*++
  2170. Routine Description:
  2171. This function times out opens that are waiting for another client
  2172. or local process to release its oplock. This opener's wait for
  2173. oplock break IRP is cancelled, causing the opener to return the
  2174. failure to the client.
  2175. Arguments:
  2176. CurrentTime - pointer to the current system time.
  2177. Return Value:
  2178. None.
  2179. --*/
  2180. {
  2181. PLIST_ENTRY listEntry;
  2182. PWAIT_FOR_OPLOCK_BREAK waitForOplockBreak;
  2183. PAGED_CODE( );
  2184. //
  2185. // Entries in wait for oplock break list are chronological, i.e. the
  2186. // oldest entries are closest to the head of the list.
  2187. //
  2188. ACQUIRE_LOCK( &SrvOplockBreakListLock );
  2189. while ( !IsListEmpty( &SrvWaitForOplockBreakList ) ) {
  2190. listEntry = SrvWaitForOplockBreakList.Flink;
  2191. waitForOplockBreak = CONTAINING_RECORD( listEntry,
  2192. WAIT_FOR_OPLOCK_BREAK,
  2193. ListEntry
  2194. );
  2195. if ( waitForOplockBreak->TimeoutTime.QuadPart > CurrentTime->QuadPart ) {
  2196. //
  2197. // No more wait for oplock breaks to timeout
  2198. //
  2199. break;
  2200. }
  2201. IF_DEBUG( OPLOCK ) {
  2202. KdPrint(( "srv!TimeoutWaitingOpens: Failing stuck open, "
  2203. "cancelling wait IRP %p\n", waitForOplockBreak->Irp ));
  2204. KdPrint(( "Timeout time = %08lx.%08lx, current time = %08lx.%08lx\n",
  2205. waitForOplockBreak->TimeoutTime.HighPart,
  2206. waitForOplockBreak->TimeoutTime.LowPart,
  2207. CurrentTime->HighPart,
  2208. CurrentTime->LowPart ));
  2209. }
  2210. //
  2211. // Timeout this wait for oplock break
  2212. //
  2213. RemoveHeadList( &SrvWaitForOplockBreakList );
  2214. IoCancelIrp( waitForOplockBreak->Irp );
  2215. waitForOplockBreak->WaitState = WaitStateOplockWaitTimedOut;
  2216. SrvDereferenceWaitForOplockBreak( waitForOplockBreak );
  2217. }
  2218. RELEASE_LOCK( &SrvOplockBreakListLock );
  2219. } // TimeoutWaitingOpens
  2220. VOID
  2221. TimeoutStuckOplockBreaks (
  2222. IN PLARGE_INTEGER CurrentTime
  2223. )
  2224. /*++
  2225. Routine Description:
  2226. This function times out blocked oplock breaks.
  2227. Arguments:
  2228. None.
  2229. Return Value:
  2230. None.
  2231. --*/
  2232. {
  2233. PLIST_ENTRY listEntry;
  2234. PRFCB rfcb;
  2235. PPAGED_RFCB pagedRfcb;
  2236. PAGED_CODE( );
  2237. //
  2238. // Entries in wait for oplock break list are chronological, i.e. the
  2239. // oldest entries are closest to the head of the list.
  2240. //
  2241. ACQUIRE_LOCK( &SrvOplockBreakListLock );
  2242. while ( !IsListEmpty( &SrvOplockBreaksInProgressList ) ) {
  2243. listEntry = SrvOplockBreaksInProgressList.Flink;
  2244. rfcb = CONTAINING_RECORD( listEntry, RFCB, ListEntry );
  2245. pagedRfcb = rfcb->PagedRfcb;
  2246. if ( pagedRfcb->OplockBreakTimeoutTime.QuadPart > CurrentTime->QuadPart ) {
  2247. //
  2248. // No more wait for oplock break requests to timeout
  2249. //
  2250. break;
  2251. }
  2252. IF_DEBUG( ERRORS ) {
  2253. KdPrint(( "srv!TimeoutStuckOplockBreaks: Failing stuck oplock, "
  2254. "break request. Closing %wZ\n",
  2255. &rfcb->Mfcb->FileName ));
  2256. }
  2257. IF_DEBUG( STUCK_OPLOCK ) {
  2258. KdPrint(( "srv!TimeoutStuckOplockBreaks: Failing stuck oplock, "
  2259. "break request. Closing %wZ\n",
  2260. &rfcb->Mfcb->FileName ));
  2261. KdPrint(( "Rfcb %p\n", rfcb ));
  2262. KdPrint(( "Timeout time = %08lx.%08lx, current time = %08lx.%08lx\n",
  2263. pagedRfcb->OplockBreakTimeoutTime.HighPart,
  2264. pagedRfcb->OplockBreakTimeoutTime.LowPart,
  2265. CurrentTime->HighPart,
  2266. CurrentTime->LowPart ));
  2267. DbgBreakPoint();
  2268. }
  2269. //
  2270. // We have been waiting too long for an oplock break response.
  2271. // Unilaterally acknowledge the oplock break, on the assumption
  2272. // that the client is dead.
  2273. //
  2274. rfcb->NewOplockLevel = NO_OPLOCK_BREAK_IN_PROGRESS;
  2275. rfcb->OnOplockBreaksInProgressList = FALSE;
  2276. //
  2277. // Remove the RFCB from the Oplock breaks in progress list, and
  2278. // release the RFCB reference.
  2279. //
  2280. SrvRemoveEntryList( &SrvOplockBreaksInProgressList, &rfcb->ListEntry );
  2281. #if DBG
  2282. rfcb->ListEntry.Flink = rfcb->ListEntry.Blink = NULL;
  2283. #endif
  2284. RELEASE_LOCK( &SrvOplockBreakListLock );
  2285. SrvAcknowledgeOplockBreak( rfcb, 0 );
  2286. ExInterlockedAddUlong(
  2287. &rfcb->Connection->OplockBreaksInProgress,
  2288. (ULONG)-1,
  2289. rfcb->Connection->EndpointSpinLock
  2290. );
  2291. SrvDereferenceRfcb( rfcb );
  2292. ACQUIRE_LOCK( &SrvOplockBreakListLock );
  2293. }
  2294. RELEASE_LOCK( &SrvOplockBreakListLock );
  2295. } // TimeoutStuckOplockBreaks
  2296. VOID
  2297. UpdateConnectionQos (
  2298. IN PLARGE_INTEGER CurrentTime
  2299. )
  2300. /*++
  2301. Routine Description:
  2302. This function updates the qos information for each connection.
  2303. Arguments:
  2304. CurrentTime - the current system time.
  2305. Return Value:
  2306. None.
  2307. --*/
  2308. {
  2309. USHORT index;
  2310. PENDPOINT endpoint;
  2311. PLIST_ENTRY listEntry;
  2312. PCONNECTION connection;
  2313. PAGED_CODE( );
  2314. //
  2315. // Go through each connection of each endpoint and update the qos
  2316. // information.
  2317. //
  2318. ACQUIRE_LOCK( &SrvEndpointLock );
  2319. listEntry = SrvEndpointList.ListHead.Flink;
  2320. while ( listEntry != &SrvEndpointList.ListHead ) {
  2321. endpoint = CONTAINING_RECORD(
  2322. listEntry,
  2323. ENDPOINT,
  2324. GlobalEndpointListEntry
  2325. );
  2326. //
  2327. // If this endpoint is closing, or is a connectionless (IPX)
  2328. // endpoint, skip to the next one. Otherwise, reference the
  2329. // endpoint so that it can't go away.
  2330. //
  2331. if ( (GET_BLOCK_STATE(endpoint) != BlockStateActive) ||
  2332. endpoint->IsConnectionless ) {
  2333. listEntry = listEntry->Flink;
  2334. continue;
  2335. }
  2336. SrvReferenceEndpoint( endpoint );
  2337. //
  2338. // Walk the endpoint's connection table.
  2339. //
  2340. index = (USHORT)-1;
  2341. while ( TRUE ) {
  2342. //
  2343. // Get the next active connection in the table. If no more
  2344. // are available, WalkConnectionTable returns NULL.
  2345. // Otherwise, it returns a referenced pointer to a
  2346. // connection.
  2347. //
  2348. connection = WalkConnectionTable( endpoint, &index );
  2349. if ( connection == NULL ) {
  2350. break;
  2351. }
  2352. RELEASE_LOCK( &SrvEndpointLock );
  2353. SrvUpdateVcQualityOfService( connection, CurrentTime );
  2354. ACQUIRE_LOCK( &SrvEndpointLock );
  2355. SrvDereferenceConnection( connection );
  2356. }
  2357. //
  2358. // Capture a pointer to the next endpoint in the list (that one
  2359. // can't go away because we hold the endpoint list), then
  2360. // dereference the current endpoint.
  2361. //
  2362. listEntry = listEntry->Flink;
  2363. SrvDereferenceEndpoint( endpoint );
  2364. }
  2365. RELEASE_LOCK( &SrvEndpointLock );
  2366. return;
  2367. } // UpdateConnectionQos
  2368. VOID
  2369. LazyFreeQueueDataStructures (
  2370. PWORK_QUEUE queue
  2371. )
  2372. /*++
  2373. Routine Description:
  2374. This function frees work context blocks and other per-queue data
  2375. structures that are held on linked lists when otherwise free. It
  2376. only frees a few at a time, to allow a slow ramp-down.
  2377. Arguments:
  2378. CurrentTime - the current system time.
  2379. Return Value:
  2380. None.
  2381. --*/
  2382. {
  2383. PSLIST_ENTRY listEntry;
  2384. KIRQL oldIrql;
  2385. ULONG i,j;
  2386. PWORK_CONTEXT workContext;
  2387. //
  2388. // Clean out the queue->FreeContext
  2389. //
  2390. workContext = NULL;
  2391. workContext = (PWORK_CONTEXT)InterlockedExchangePointer( &queue->FreeContext, workContext );
  2392. if( workContext != NULL ) {
  2393. ExInterlockedPushEntrySList( workContext->FreeList,
  2394. &workContext->SingleListEntry,
  2395. &queue->SpinLock
  2396. );
  2397. InterlockedIncrement( &queue->FreeWorkItems );
  2398. }
  2399. //
  2400. // Free up to 1/32 of the normal work items, if appropriate
  2401. //
  2402. i = MAX(1, queue->FreeWorkItems >> 5);
  2403. for( j=0; j<i; j++ )
  2404. {
  2405. if( queue->FreeWorkItems > queue->MinFreeWorkItems ) {
  2406. listEntry = ExInterlockedPopEntrySList( &queue->NormalWorkItemList,
  2407. &queue->SpinLock );
  2408. if( listEntry != NULL ) {
  2409. PWORK_CONTEXT innerWorkContext;
  2410. InterlockedDecrement( &queue->FreeWorkItems );
  2411. innerWorkContext = CONTAINING_RECORD( listEntry, WORK_CONTEXT, SingleListEntry );
  2412. SrvFreeNormalWorkItem( innerWorkContext );
  2413. }
  2414. else
  2415. {
  2416. break;
  2417. }
  2418. }
  2419. else
  2420. {
  2421. break;
  2422. }
  2423. }
  2424. //
  2425. // Free 1 raw mode work item, if appropriate
  2426. //
  2427. if( (ULONG)queue->AllocatedRawModeWorkItems > SrvMaxRawModeWorkItemCount / SrvNumberOfProcessors ) {
  2428. PWORK_CONTEXT innerWorkContext;
  2429. listEntry = ExInterlockedPopEntrySList( &queue->RawModeWorkItemList, &queue->SpinLock );
  2430. if( listEntry != NULL ) {
  2431. InterlockedDecrement( &queue->FreeRawModeWorkItems );
  2432. ASSERT( queue->FreeRawModeWorkItems >= 0 );
  2433. innerWorkContext = CONTAINING_RECORD( listEntry, WORK_CONTEXT, SingleListEntry );
  2434. SrvFreeRawModeWorkItem( innerWorkContext );
  2435. }
  2436. }
  2437. //
  2438. // Free 1 rfcb off the list
  2439. //
  2440. {
  2441. PRFCB rfcb = NULL;
  2442. rfcb = (PRFCB)InterlockedExchangePointer( &queue->CachedFreeRfcb, rfcb );
  2443. if( rfcb != NULL ) {
  2444. ExInterlockedPushEntrySList( &queue->RfcbFreeList,
  2445. &rfcb->SingleListEntry,
  2446. &queue->SpinLock
  2447. );
  2448. InterlockedIncrement( &queue->FreeRfcbs );
  2449. }
  2450. listEntry = ExInterlockedPopEntrySList( &queue->RfcbFreeList,
  2451. &queue->SpinLock );
  2452. if( listEntry ) {
  2453. InterlockedDecrement( &queue->FreeRfcbs );
  2454. rfcb = CONTAINING_RECORD( listEntry, RFCB, SingleListEntry );
  2455. INCREMENT_DEBUG_STAT( SrvDbgStatistics.RfcbInfo.Frees );
  2456. FREE_HEAP( rfcb->PagedRfcb );
  2457. DEALLOCATE_NONPAGED_POOL( rfcb );
  2458. }
  2459. }
  2460. //
  2461. // Free 1 Mfcb off the list
  2462. //
  2463. {
  2464. PNONPAGED_MFCB nonpagedMfcb = NULL;
  2465. nonpagedMfcb = (PNONPAGED_MFCB)InterlockedExchangePointer(&queue->CachedFreeMfcb,
  2466. nonpagedMfcb);
  2467. if( nonpagedMfcb != NULL ) {
  2468. ExInterlockedPushEntrySList( &queue->MfcbFreeList,
  2469. &nonpagedMfcb->SingleListEntry,
  2470. &queue->SpinLock
  2471. );
  2472. InterlockedIncrement( &queue->FreeMfcbs );
  2473. }
  2474. listEntry = ExInterlockedPopEntrySList( &queue->MfcbFreeList,
  2475. &queue->SpinLock );
  2476. if( listEntry ) {
  2477. InterlockedDecrement( &queue->FreeMfcbs );
  2478. nonpagedMfcb = CONTAINING_RECORD( listEntry, NONPAGED_MFCB, SingleListEntry );
  2479. DEALLOCATE_NONPAGED_POOL( nonpagedMfcb );
  2480. }
  2481. }
  2482. //
  2483. // Free memory in the per-queue pool free lists
  2484. //
  2485. {
  2486. //
  2487. // Free the paged pool chunks
  2488. //
  2489. SrvClearLookAsideList( &queue->PagedPoolLookAsideList, SrvFreePagedPool );
  2490. //
  2491. // Free the non paged pool chunks
  2492. //
  2493. SrvClearLookAsideList( &queue->NonPagedPoolLookAsideList, SrvFreeNonPagedPool );
  2494. }
  2495. } // LazyFreeQueueDataStructures
  2496. VOID
  2497. SrvUserAlertRaise (
  2498. IN ULONG Message,
  2499. IN ULONG NumberOfStrings,
  2500. IN PUNICODE_STRING String1 OPTIONAL,
  2501. IN PUNICODE_STRING String2 OPTIONAL,
  2502. IN PUNICODE_STRING ComputerName
  2503. )
  2504. {
  2505. NTSTATUS status;
  2506. IO_STATUS_BLOCK ioStatusBlock;
  2507. PSTD_ALERT alert;
  2508. PUSER_OTHER_INFO user;
  2509. LARGE_INTEGER currentTime;
  2510. ULONG mailslotLength;
  2511. ULONG string1Length = 0;
  2512. ULONG string2Length = 0;
  2513. PCHAR variableInfo;
  2514. UNICODE_STRING computerName;
  2515. HANDLE alerterHandle;
  2516. PAGED_CODE( );
  2517. ASSERT( (NumberOfStrings == 2 && String1 != NULL && String2 != NULL) ||
  2518. (NumberOfStrings == 1 && String1 != NULL) ||
  2519. (NumberOfStrings == 0) );
  2520. //
  2521. // Open a handle to the alerter service's mailslot.
  2522. //
  2523. status = OpenAlerter( &alerterHandle );
  2524. if ( !NT_SUCCESS(status) ) {
  2525. return;
  2526. }
  2527. //
  2528. // Get rid of the leading backslashes from the computer name.
  2529. //
  2530. computerName.Buffer = ComputerName->Buffer + 2;
  2531. computerName.Length = (USHORT)(ComputerName->Length - 2*sizeof(WCHAR));
  2532. computerName.MaximumLength =
  2533. (USHORT)(ComputerName->MaximumLength - 2*sizeof(WCHAR));
  2534. //
  2535. // Allocate a buffer to hold the mailslot we're going to send to the
  2536. // alerter.
  2537. //
  2538. if ( String1 != NULL ) {
  2539. string1Length = String1->Length + sizeof(WCHAR);
  2540. }
  2541. if ( String2 != NULL ) {
  2542. string2Length = String2->Length + sizeof(WCHAR);
  2543. }
  2544. mailslotLength = sizeof(STD_ALERT) + sizeof(USER_OTHER_INFO) +
  2545. string1Length + string2Length +
  2546. sizeof(WCHAR) +
  2547. ComputerName->Length + sizeof(WCHAR);
  2548. alert = ALLOCATE_HEAP_COLD( mailslotLength, BlockTypeDataBuffer );
  2549. if ( alert == NULL ) {
  2550. SRVDBG_RELEASE_HANDLE( alerterHandle, "ALR", 20, 0 );
  2551. SrvNtClose( alerterHandle, FALSE );
  2552. return;
  2553. }
  2554. //
  2555. // Set up the standard alert structure.
  2556. //
  2557. KeQuerySystemTime( &currentTime );
  2558. RtlTimeToSecondsSince1970( &currentTime, &alert->alrt_timestamp );
  2559. STRNCPY( alert->alrt_eventname, StrUserAlertEventName, EVLEN+1 );
  2560. STRNCPY( alert->alrt_servicename, SrvAlertServiceName, SNLEN+1 );
  2561. alert->alrt_eventname[EVLEN] = L'\0';
  2562. alert->alrt_servicename[SNLEN] = L'\0';
  2563. //
  2564. // Set up the user info in the alert.
  2565. //
  2566. user = (PUSER_OTHER_INFO)ALERT_OTHER_INFO(alert);
  2567. user->alrtus_errcode = Message;
  2568. user->alrtus_numstrings = NumberOfStrings;
  2569. //
  2570. // Set up the variable portion of the message.
  2571. //
  2572. variableInfo = ALERT_VAR_DATA(user);
  2573. if ( String1 != NULL ) {
  2574. RtlCopyMemory(
  2575. variableInfo,
  2576. String1->Buffer,
  2577. String1->Length
  2578. );
  2579. *(PWCH)(variableInfo + String1->Length) = UNICODE_NULL;
  2580. variableInfo += String1->Length + sizeof(WCHAR);
  2581. }
  2582. if ( String2 != NULL ) {
  2583. RtlCopyMemory(
  2584. variableInfo,
  2585. String2->Buffer,
  2586. String2->Length
  2587. );
  2588. *(PWCH)(variableInfo + String2->Length) = UNICODE_NULL;
  2589. variableInfo += String2->Length + sizeof(WCHAR);
  2590. }
  2591. *(PWCH)variableInfo = UNICODE_NULL;
  2592. variableInfo += sizeof(WCHAR);
  2593. RtlCopyMemory(
  2594. variableInfo,
  2595. ComputerName->Buffer,
  2596. ComputerName->Length
  2597. );
  2598. *(PWCH)(variableInfo + ComputerName->Length) = UNICODE_NULL;
  2599. variableInfo += ComputerName->Length + sizeof(WCHAR);
  2600. status = NtWriteFile(
  2601. alerterHandle,
  2602. NULL, // Event
  2603. NULL, // ApcRoutine
  2604. NULL, // ApcContext
  2605. &ioStatusBlock,
  2606. alert,
  2607. mailslotLength,
  2608. NULL, // ByteOffset
  2609. NULL // Key
  2610. );
  2611. if ( !NT_SUCCESS(status) ) {
  2612. INTERNAL_ERROR(
  2613. ERROR_LEVEL_UNEXPECTED,
  2614. "SrvUserAlertRaise: NtWriteFile failed: %X\n",
  2615. status,
  2616. NULL
  2617. );
  2618. SrvLogServiceFailure( SRV_SVC_NT_WRITE_FILE, status );
  2619. }
  2620. FREE_HEAP( alert );
  2621. SRVDBG_RELEASE_HANDLE( alerterHandle, "ALR", 21, 0 );
  2622. SrvNtClose( alerterHandle, FALSE );
  2623. return;
  2624. } // SrvUserAlertRaise
  2625. VOID
  2626. SrvAdminAlertRaise (
  2627. IN ULONG Message,
  2628. IN ULONG NumberOfStrings,
  2629. IN PUNICODE_STRING String1 OPTIONAL,
  2630. IN PUNICODE_STRING String2 OPTIONAL,
  2631. IN PUNICODE_STRING String3 OPTIONAL
  2632. )
  2633. {
  2634. NTSTATUS status;
  2635. IO_STATUS_BLOCK ioStatusBlock;
  2636. PSTD_ALERT alert;
  2637. PADMIN_OTHER_INFO admin;
  2638. LARGE_INTEGER currentTime;
  2639. ULONG mailslotLength;
  2640. ULONG string1Length = 0;
  2641. ULONG string2Length = 0;
  2642. ULONG string3Length = 0;
  2643. PCHAR variableInfo;
  2644. HANDLE alerterHandle;
  2645. PAGED_CODE( );
  2646. ASSERT( (NumberOfStrings == 3 && String1 != NULL && String2 != NULL && String3 != NULL ) ||
  2647. (NumberOfStrings == 2 && String1 != NULL && String2 != NULL && String3 == NULL ) ||
  2648. (NumberOfStrings == 1 && String1 != NULL && String2 == NULL && String3 == NULL ) ||
  2649. (NumberOfStrings == 0 && String1 == NULL && String2 == NULL && String3 == NULL ) );
  2650. //
  2651. // Open a handle to the alerter service's mailslot.
  2652. //
  2653. status = OpenAlerter( &alerterHandle );
  2654. if ( !NT_SUCCESS(status) ) {
  2655. return;
  2656. }
  2657. //
  2658. // Allocate a buffer to hold the mailslot we're going to send to the
  2659. // alerter.
  2660. //
  2661. if ( String1 != NULL ) {
  2662. string1Length = String1->Length + sizeof(WCHAR);
  2663. }
  2664. if ( String2 != NULL ) {
  2665. string2Length = String2->Length + sizeof(WCHAR);
  2666. }
  2667. if ( String3 != NULL ) {
  2668. string3Length = String3->Length + sizeof(WCHAR);
  2669. }
  2670. mailslotLength = sizeof(STD_ALERT) + sizeof(ADMIN_OTHER_INFO) +
  2671. string1Length + string2Length + string3Length;
  2672. alert = ALLOCATE_HEAP_COLD( mailslotLength, BlockTypeDataBuffer );
  2673. if ( alert == NULL ) {
  2674. SRVDBG_RELEASE_HANDLE( alerterHandle, "ALR", 22, 0 );
  2675. SrvNtClose( alerterHandle, FALSE );
  2676. return;
  2677. }
  2678. //
  2679. // Set up the standard alert structure.
  2680. //
  2681. KeQuerySystemTime( &currentTime );
  2682. RtlTimeToSecondsSince1970( &currentTime, &alert->alrt_timestamp );
  2683. STRCPY( alert->alrt_eventname, StrAdminAlertEventName );
  2684. STRCPY( alert->alrt_servicename, SrvAlertServiceName );
  2685. //
  2686. // Set up the user info in the alert.
  2687. //
  2688. admin = (PADMIN_OTHER_INFO)ALERT_OTHER_INFO(alert);
  2689. admin->alrtad_errcode = Message;
  2690. admin->alrtad_numstrings = NumberOfStrings;
  2691. //
  2692. // Set up the variable portion of the message.
  2693. //
  2694. variableInfo = ALERT_VAR_DATA(admin);
  2695. if ( String1 != NULL ) {
  2696. RtlCopyMemory(
  2697. variableInfo,
  2698. String1->Buffer,
  2699. String1->Length
  2700. );
  2701. *(PWCH)(variableInfo + String1->Length) = UNICODE_NULL;
  2702. variableInfo += string1Length;
  2703. }
  2704. if ( String2 != NULL ) {
  2705. RtlCopyMemory(
  2706. variableInfo,
  2707. String2->Buffer,
  2708. String2->Length
  2709. );
  2710. *(PWCH)(variableInfo + String2->Length) = UNICODE_NULL;
  2711. variableInfo += string2Length;
  2712. }
  2713. if ( String3 != NULL ){
  2714. RtlCopyMemory(
  2715. variableInfo,
  2716. String3->Buffer,
  2717. String3->Length
  2718. );
  2719. *(PWCH)(variableInfo + String3->Length) = UNICODE_NULL;
  2720. }
  2721. status = NtWriteFile(
  2722. alerterHandle,
  2723. NULL, // Event
  2724. NULL, // ApcRoutine
  2725. NULL, // ApcContext
  2726. &ioStatusBlock,
  2727. alert,
  2728. mailslotLength,
  2729. NULL, // ByteOffset
  2730. NULL // Key
  2731. );
  2732. if ( !NT_SUCCESS(status) ) {
  2733. INTERNAL_ERROR(
  2734. ERROR_LEVEL_UNEXPECTED,
  2735. "SrvAdminAlertRaise: NtWriteFile failed: %X\n",
  2736. status,
  2737. NULL
  2738. );
  2739. SrvLogServiceFailure( SRV_SVC_NT_WRITE_FILE, status );
  2740. }
  2741. FREE_HEAP( alert );
  2742. SRVDBG_RELEASE_HANDLE( alerterHandle, "ALR", 23, 0 );
  2743. SrvNtClose( alerterHandle, FALSE );
  2744. return;
  2745. } // SrvAdminAlertRaise
  2746. NTSTATUS
  2747. TimeToTimeString (
  2748. IN PLARGE_INTEGER Time,
  2749. OUT PUNICODE_STRING TimeString
  2750. )
  2751. {
  2752. TIME_FIELDS timeFields;
  2753. UCHAR buffer[6];
  2754. ANSI_STRING ansiTimeString;
  2755. LARGE_INTEGER localTime;
  2756. PAGED_CODE( );
  2757. // !!! need a better, internationalizable way to do this.
  2758. //
  2759. // Convert Time To Local Time
  2760. //
  2761. ExSystemTimeToLocalTime(
  2762. Time,
  2763. &localTime
  2764. );
  2765. RtlTimeToTimeFields( &localTime, &timeFields );
  2766. buffer[0] = (UCHAR)( (timeFields.Hour / 10) + '0' );
  2767. buffer[1] = (UCHAR)( (timeFields.Hour % 10) + '0' );
  2768. buffer[2] = ':';
  2769. buffer[3] = (UCHAR)( (timeFields.Minute / 10) + '0' );
  2770. buffer[4] = (UCHAR)( (timeFields.Minute % 10) + '0' );
  2771. buffer[5] = '\0';
  2772. RtlInitString( &ansiTimeString, buffer );
  2773. return RtlAnsiStringToUnicodeString( TimeString, &ansiTimeString, TRUE );
  2774. } // TimeToTimeString
  2775. VOID
  2776. CheckErrorCount (
  2777. PSRV_ERROR_RECORD ErrorRecord,
  2778. BOOLEAN UseRatio
  2779. )
  2780. /*++
  2781. Routine Description:
  2782. This routine checks the record of server operations and adds up the
  2783. count of successes to failures.
  2784. Arguments:
  2785. ErrorRecord - Points to an SRV_ERROR_RECORD structure
  2786. UseRatio - If TRUE, look at count of errors,
  2787. If FALSE, look at ratio of error to total.
  2788. Return Value:
  2789. None.
  2790. --*/
  2791. {
  2792. ULONG totalOperations;
  2793. ULONG failedOperations;
  2794. UNICODE_STRING string1, string2;
  2795. WCHAR buffer1[20], buffer2[20];
  2796. NTSTATUS status;
  2797. PAGED_CODE( );
  2798. failedOperations = ErrorRecord->FailedOperations;
  2799. totalOperations = failedOperations + ErrorRecord->SuccessfulOperations;
  2800. //
  2801. // Zero out the counters
  2802. //
  2803. ErrorRecord->SuccessfulOperations = 0;
  2804. ErrorRecord->FailedOperations = 0;
  2805. if ( (UseRatio &&
  2806. ( totalOperations != 0 &&
  2807. ((failedOperations * 100 / totalOperations) >
  2808. ErrorRecord->ErrorThreshold)))
  2809. ||
  2810. (!UseRatio &&
  2811. failedOperations > ErrorRecord->ErrorThreshold) ) {
  2812. //
  2813. // Raise an alert
  2814. //
  2815. string1.Buffer = buffer1;
  2816. string1.Length = string1.MaximumLength = sizeof(buffer1);
  2817. string2.Buffer = buffer2;
  2818. string2.Length = string2.MaximumLength = sizeof(buffer2);
  2819. status = RtlIntegerToUnicodeString( failedOperations, 10, &string1 );
  2820. ASSERT( NT_SUCCESS( status ) );
  2821. status = RtlIntegerToUnicodeString( SrvAlertMinutes, 10, &string2 );
  2822. ASSERT( NT_SUCCESS( status ) );
  2823. if ( ErrorRecord->AlertNumber == ALERT_NetIO) {
  2824. //
  2825. // We need a third string for the network name.
  2826. //
  2827. // This allocation is unfortunate. We need to maintain
  2828. // per xport error count so we can print out the actual
  2829. // xport name.
  2830. //
  2831. UNICODE_STRING string3;
  2832. RtlInitUnicodeString(
  2833. &string3,
  2834. StrNoNameTransport
  2835. );
  2836. //
  2837. // We need a third string for the network name
  2838. //
  2839. SrvAdminAlertRaise(
  2840. ErrorRecord->AlertNumber,
  2841. 3,
  2842. &string1,
  2843. &string2,
  2844. &string3
  2845. );
  2846. } else {
  2847. SrvAdminAlertRaise(
  2848. ErrorRecord->AlertNumber,
  2849. 2,
  2850. &string1,
  2851. &string2,
  2852. NULL
  2853. );
  2854. }
  2855. }
  2856. return;
  2857. } // CheckErrorCount
  2858. VOID
  2859. CheckDiskSpace (
  2860. VOID
  2861. )
  2862. /*++
  2863. Routine Description:
  2864. This routine check disk space on local drives. If a drive
  2865. is low on space, an alert is raised.
  2866. Arguments:
  2867. None.
  2868. Return Value:
  2869. None.
  2870. --*/
  2871. {
  2872. ULONG diskMask;
  2873. UNICODE_STRING insert1, insert2;
  2874. WCHAR buffer2[20];
  2875. UNICODE_STRING pathName;
  2876. WCHAR dosPathPrefix[] = L"\\DosDevices\\A:\\";
  2877. NTSTATUS status;
  2878. OBJECT_ATTRIBUTES objectAttributes;
  2879. IO_STATUS_BLOCK iosb;
  2880. FILE_FS_SIZE_INFORMATION sizeInformation;
  2881. FILE_FS_DEVICE_INFORMATION deviceInformation;
  2882. HANDLE handle;
  2883. ULONG percentFree;
  2884. PWCH currentDrive;
  2885. DWORD diskconfiguration;
  2886. PAGED_CODE( );
  2887. if( SrvFreeDiskSpaceThreshold == 0 ) {
  2888. return;
  2889. }
  2890. diskMask = 0x80000000; // Start at A:
  2891. pathName.Buffer = dosPathPrefix;
  2892. pathName.MaximumLength = 32;
  2893. pathName.Length = 28; // skip last backslash!
  2894. currentDrive = &dosPathPrefix[12];
  2895. insert1.Buffer = &dosPathPrefix[12];
  2896. insert1.Length = 4;
  2897. //
  2898. // SrvDiskConfiguration is a bitmask of drives that are
  2899. // administratively shared. It is updated by NetShareAdd and
  2900. // NetShareDel.
  2901. //
  2902. diskconfiguration = SrvDiskConfiguration;
  2903. for ( ; diskMask >= 0x40; diskMask >>= 1, dosPathPrefix[12]++ ) {
  2904. if ( !(diskconfiguration & diskMask) ) {
  2905. continue;
  2906. }
  2907. //
  2908. // Check disk space on this disk
  2909. //
  2910. SrvInitializeObjectAttributes_U(
  2911. &objectAttributes,
  2912. &pathName,
  2913. OBJ_CASE_INSENSITIVE,
  2914. NULL,
  2915. NULL
  2916. );
  2917. status = NtOpenFile(
  2918. &handle,
  2919. FILE_READ_ATTRIBUTES,
  2920. &objectAttributes,
  2921. &iosb,
  2922. FILE_SHARE_READ | FILE_SHARE_WRITE,
  2923. FILE_NON_DIRECTORY_FILE
  2924. );
  2925. if ( !NT_SUCCESS( status) ) {
  2926. continue;
  2927. }
  2928. SRVDBG_CLAIM_HANDLE( handle, "DSK", 16, 0 );
  2929. status = NtQueryVolumeInformationFile(
  2930. handle,
  2931. &iosb,
  2932. &deviceInformation,
  2933. sizeof( FILE_FS_DEVICE_INFORMATION ),
  2934. FileFsDeviceInformation
  2935. );
  2936. if ( NT_SUCCESS(status) ) {
  2937. status = iosb.Status;
  2938. }
  2939. SRVDBG_RELEASE_HANDLE( handle, "DSK", 24, 0 );
  2940. if ( !NT_SUCCESS( status ) ||
  2941. (deviceInformation.Characteristics &
  2942. (FILE_FLOPPY_DISKETTE | FILE_READ_ONLY_DEVICE | FILE_WRITE_ONCE_MEDIA)) ||
  2943. !(deviceInformation.Characteristics &
  2944. FILE_DEVICE_IS_MOUNTED) ) {
  2945. SrvNtClose( handle, FALSE );
  2946. continue;
  2947. }
  2948. // Validate its write-able
  2949. if( deviceInformation.Characteristics & FILE_REMOVABLE_MEDIA )
  2950. {
  2951. PIRP Irp;
  2952. PIO_STACK_LOCATION IrpSp;
  2953. KEVENT CompletionEvent;
  2954. PDEVICE_OBJECT DeviceObject;
  2955. // Create the IRP
  2956. KeInitializeEvent( &CompletionEvent, SynchronizationEvent, FALSE );
  2957. Irp = BuildCoreOfSyncIoRequest(
  2958. handle,
  2959. NULL,
  2960. &CompletionEvent,
  2961. &iosb,
  2962. &DeviceObject );
  2963. if( !Irp )
  2964. {
  2965. // If we are out of memory, don't log an entry
  2966. goto skip_volume;
  2967. }
  2968. // Initialize the other IRP fields
  2969. IrpSp = IoGetNextIrpStackLocation( Irp );
  2970. IrpSp->MajorFunction = IRP_MJ_DEVICE_CONTROL;
  2971. IrpSp->MinorFunction = 0;
  2972. IrpSp->Parameters.DeviceIoControl.OutputBufferLength = 0;
  2973. IrpSp->Parameters.DeviceIoControl.InputBufferLength = 0;
  2974. IrpSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_DISK_IS_WRITABLE;
  2975. IrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
  2976. // Issue the IO
  2977. status = StartIoAndWait( Irp, DeviceObject, &CompletionEvent, &iosb );
  2978. if( !NT_SUCCESS(status) )
  2979. {
  2980. skip_volume:
  2981. SrvNtClose( handle, FALSE );
  2982. continue;
  2983. }
  2984. }
  2985. SrvNtClose( handle, FALSE );
  2986. pathName.Length += 2; // include last backslash
  2987. status = NtOpenFile(
  2988. &handle,
  2989. FILE_READ_ATTRIBUTES,
  2990. &objectAttributes,
  2991. &iosb,
  2992. FILE_SHARE_READ | FILE_SHARE_WRITE,
  2993. FILE_DIRECTORY_FILE
  2994. );
  2995. pathName.Length -= 2; // skip last backslash
  2996. if ( !NT_SUCCESS( status) ) {
  2997. continue;
  2998. }
  2999. SRVDBG_CLAIM_HANDLE( handle, "DSK", 17, 0 );
  3000. status = NtQueryVolumeInformationFile(
  3001. handle,
  3002. &iosb,
  3003. &sizeInformation,
  3004. sizeof( FILE_FS_SIZE_INFORMATION ),
  3005. FileFsSizeInformation
  3006. );
  3007. if ( NT_SUCCESS(status) ) {
  3008. status = iosb.Status;
  3009. }
  3010. SRVDBG_RELEASE_HANDLE( handle, "DSK", 25, 0 );
  3011. SrvNtClose( handle, FALSE );
  3012. if ( !NT_SUCCESS( status) ) {
  3013. continue;
  3014. }
  3015. //
  3016. // Calculate % space available = AvailableSpace * 100 / TotalSpace
  3017. //
  3018. if( sizeInformation.TotalAllocationUnits.QuadPart > 0 )
  3019. {
  3020. LARGE_INTEGER mbFree;
  3021. LARGE_INTEGER mbTotal;
  3022. percentFree = (ULONG)(sizeInformation.AvailableAllocationUnits.QuadPart
  3023. * 100 / sizeInformation.TotalAllocationUnits.QuadPart);
  3024. mbFree.QuadPart = (ULONG)
  3025. (sizeInformation.AvailableAllocationUnits.QuadPart*
  3026. sizeInformation.SectorsPerAllocationUnit*
  3027. sizeInformation.BytesPerSector/
  3028. (1024*1024));
  3029. ASSERT( percentFree <= 100 );
  3030. //
  3031. // If space is low raise, and we have already raised an alert,
  3032. // then raise the alert.
  3033. //
  3034. // If space is not low, then clear the alert flag so the we will
  3035. // raise an alert if diskspace falls again.
  3036. //
  3037. if ( percentFree < SrvFreeDiskSpaceThreshold ) {
  3038. // If a ceiling is specified, make sure we have exceeded it
  3039. if( SrvFreeDiskSpaceCeiling &&
  3040. ((mbFree.LowPart > SrvFreeDiskSpaceCeiling) ||
  3041. (mbFree.HighPart != 0))
  3042. )
  3043. {
  3044. goto abort_error;
  3045. }
  3046. if ( !SrvDiskAlertRaised[ *currentDrive - L'A' ] ) {
  3047. ULONGLONG FreeSpace;
  3048. SrvLogError(
  3049. SrvDeviceObject,
  3050. EVENT_SRV_DISK_FULL,
  3051. status,
  3052. NULL,
  3053. 0,
  3054. &insert1,
  3055. 1
  3056. );
  3057. //
  3058. // Raise alert
  3059. //
  3060. insert2.Buffer = buffer2;
  3061. insert2.Length = insert2.MaximumLength = sizeof(buffer2);
  3062. FreeSpace = (ULONGLONG)(sizeInformation.AvailableAllocationUnits.QuadPart
  3063. * sizeInformation.SectorsPerAllocationUnit
  3064. * sizeInformation.BytesPerSector);
  3065. status = RtlInt64ToUnicodeString(
  3066. FreeSpace,
  3067. 10,
  3068. &insert2
  3069. );
  3070. ASSERT( NT_SUCCESS( status ) );
  3071. SrvAdminAlertRaise(
  3072. ALERT_Disk_Full,
  3073. 2,
  3074. &insert1,
  3075. &insert2,
  3076. NULL
  3077. );
  3078. SrvDiskAlertRaised[ *currentDrive - L'A' ] = TRUE;
  3079. }
  3080. } else { // if ( percentFree < SrvFreeDiskSpaceThreshold )
  3081. abort_error:
  3082. SrvDiskAlertRaised[ *currentDrive - L'A' ] = FALSE;
  3083. }
  3084. }
  3085. } // for ( ; diskMask >= 0x40; ... )
  3086. return;
  3087. } // CheckDiskSpace
  3088. NTSTATUS
  3089. OpenAlerter (
  3090. OUT PHANDLE AlerterHandle
  3091. )
  3092. /*++
  3093. Routine Description:
  3094. This routine opens the alerter server's mailslot.
  3095. Arguments:
  3096. AlerterHandle - returns a handle to the mailslot.
  3097. Return Value:
  3098. NTSTATUS - Indicates whether the mailslot was opened.
  3099. --*/
  3100. {
  3101. NTSTATUS status;
  3102. IO_STATUS_BLOCK iosb;
  3103. UNICODE_STRING alerterName;
  3104. OBJECT_ATTRIBUTES objectAttributes;
  3105. PAGED_CODE( );
  3106. //
  3107. // Open a handle to the alerter service's mailslot.
  3108. //
  3109. // !!! use a #define for the name!
  3110. //
  3111. RtlInitUnicodeString( &alerterName, StrAlerterMailslot );
  3112. SrvInitializeObjectAttributes_U(
  3113. &objectAttributes,
  3114. &alerterName,
  3115. 0,
  3116. NULL,
  3117. NULL
  3118. );
  3119. status = IoCreateFile(
  3120. AlerterHandle,
  3121. GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
  3122. &objectAttributes,
  3123. &iosb,
  3124. NULL,
  3125. FILE_ATTRIBUTE_NORMAL,
  3126. FILE_SHARE_READ | FILE_SHARE_WRITE,
  3127. FILE_OPEN,
  3128. FILE_SYNCHRONOUS_IO_NONALERT, // Create Options
  3129. NULL, // EA Buffer
  3130. 0, // EA Length
  3131. CreateFileTypeNone, // File type
  3132. NULL, // ExtraCreateParameters
  3133. 0 // Options
  3134. );
  3135. if ( !NT_SUCCESS(status) ) {
  3136. KdPrint(( "OpenAlerter: failed to open alerter mailslot: %X, "
  3137. "an alert was lost.\n", status ));
  3138. } else {
  3139. SRVDBG_CLAIM_HANDLE( AlerterHandle, "ALR", 18, 0 );
  3140. }
  3141. return status;
  3142. } // OpenAlerter
  3143. VOID
  3144. RecalcCoreSearchTimeout(
  3145. VOID
  3146. )
  3147. {
  3148. ULONG factor;
  3149. ULONG newTimeout;
  3150. PAGED_CODE( );
  3151. //
  3152. // we reduce the timeout time by 2**factor
  3153. //
  3154. factor = SrvStatistics.CurrentNumberOfOpenSearches >> 9;
  3155. //
  3156. // Minimum is 30 secs.
  3157. //
  3158. ACQUIRE_LOCK( &SrvConfigurationLock );
  3159. newTimeout = MAX(30, SrvCoreSearchTimeout >> factor);
  3160. SrvSearchMaxTimeout = SecondsToTime( newTimeout, FALSE );
  3161. RELEASE_LOCK( &SrvConfigurationLock );
  3162. return;
  3163. } // RecalcCoreSearchTimeout
  3164. VOID
  3165. SrvCaptureScavengerTimeout (
  3166. IN PLARGE_INTEGER ScavengerTimeout,
  3167. IN PLARGE_INTEGER AlerterTimeout
  3168. )
  3169. {
  3170. KIRQL oldIrql;
  3171. ACQUIRE_SPIN_LOCK( &ScavengerSpinLock, &oldIrql );
  3172. SrvScavengerTimeout = *ScavengerTimeout;
  3173. SrvAlertSchedule = *AlerterTimeout;
  3174. RELEASE_SPIN_LOCK( &ScavengerSpinLock, oldIrql );
  3175. return;
  3176. } // SrvCaptureScavengerTimeout
  3177. #if SRVDBG_PERF
  3178. extern ULONG Trapped512s;
  3179. #endif
  3180. VOID
  3181. SrvUpdateStatisticsFromQueues (
  3182. OUT PSRV_STATISTICS CapturedSrvStatistics OPTIONAL
  3183. )
  3184. {
  3185. KIRQL oldIrql;
  3186. PWORK_QUEUE queue;
  3187. ACQUIRE_GLOBAL_SPIN_LOCK( Statistics, &oldIrql );
  3188. SrvStatistics.TotalBytesSent.QuadPart = 0;
  3189. SrvStatistics.TotalBytesReceived.QuadPart = 0;
  3190. SrvStatistics.TotalWorkContextBlocksQueued.Time.QuadPart = 0;
  3191. SrvStatistics.TotalWorkContextBlocksQueued.Count = 0;
  3192. //
  3193. // Get the nonblocking statistics
  3194. //
  3195. for( queue = SrvWorkQueues; queue < eSrvWorkQueues; queue++ ) {
  3196. SrvStatistics.TotalBytesSent.QuadPart += queue->stats.BytesSent;
  3197. SrvStatistics.TotalBytesReceived.QuadPart += queue->stats.BytesReceived;
  3198. SrvStatistics.TotalWorkContextBlocksQueued.Count +=
  3199. queue->stats.WorkItemsQueued.Count * STATISTICS_SMB_INTERVAL;
  3200. SrvStatistics.TotalWorkContextBlocksQueued.Time.QuadPart +=
  3201. queue->stats.WorkItemsQueued.Time.QuadPart;
  3202. }
  3203. #if SRVDBG_PERF
  3204. SrvStatistics.TotalWorkContextBlocksQueued.Count += Trapped512s;
  3205. Trapped512s = 0;
  3206. #endif
  3207. if ( ARGUMENT_PRESENT(CapturedSrvStatistics) ) {
  3208. *CapturedSrvStatistics = SrvStatistics;
  3209. }
  3210. RELEASE_GLOBAL_SPIN_LOCK( Statistics, oldIrql );
  3211. ACQUIRE_SPIN_LOCK( (PKSPIN_LOCK)IoStatisticsLock, &oldIrql );
  3212. for( queue = SrvWorkQueues; queue < eSrvWorkQueues; queue++ ) {
  3213. **(PULONG *)&IoReadOperationCount += (ULONG)(queue->stats.ReadOperations - queue->saved.ReadOperations );
  3214. queue->saved.ReadOperations = queue->stats.ReadOperations;
  3215. **(PLONGLONG *)&IoReadTransferCount += (queue->stats.BytesRead - queue->saved.BytesRead );
  3216. queue->saved.BytesRead = queue->stats.BytesRead;
  3217. **(PULONG *)&IoWriteOperationCount += (ULONG)(queue->stats.WriteOperations - queue->saved.WriteOperations );
  3218. queue->saved.WriteOperations = queue->stats.WriteOperations;
  3219. **(PLONGLONG *)&IoWriteTransferCount += (queue->stats.BytesWritten - queue->saved.BytesWritten );
  3220. queue->saved.BytesWritten = queue->stats.BytesWritten;
  3221. }
  3222. RELEASE_SPIN_LOCK( (PKSPIN_LOCK)IoStatisticsLock, oldIrql );
  3223. return;
  3224. } // SrvUpdateStatisticsFromQueues
  3225. VOID
  3226. ProcessOrphanedBlocks (
  3227. VOID
  3228. )
  3229. /*++
  3230. Routine Description:
  3231. Orphaned connections are connections with ref counts of 1 but
  3232. with no workitem, etc associated with it. They need to be cleaned
  3233. up by a dereference.
  3234. Arguments:
  3235. None.
  3236. Return Value:
  3237. None.
  3238. --*/
  3239. {
  3240. PSLIST_ENTRY listEntry;
  3241. PQUEUEABLE_BLOCK_HEADER block;
  3242. PAGED_CODE( );
  3243. //
  3244. // Run through the list of connection with pending disconnects.
  3245. // Do the work necessary to shut the disconnection connection
  3246. // down.
  3247. //
  3248. while ( TRUE ) {
  3249. listEntry = ExInterlockedPopEntrySList(
  3250. &SrvBlockOrphanage,
  3251. &GLOBAL_SPIN_LOCK(Fsd)
  3252. );
  3253. if( listEntry == NULL ) {
  3254. break;
  3255. }
  3256. InterlockedDecrement( &SrvResourceOrphanedBlocks );
  3257. block = CONTAINING_RECORD(
  3258. listEntry,
  3259. QUEUEABLE_BLOCK_HEADER,
  3260. SingleListEntry
  3261. );
  3262. if ( GET_BLOCK_TYPE(block) == BlockTypeConnection ) {
  3263. SrvDereferenceConnection( (PCONNECTION)block );
  3264. } else if ( GET_BLOCK_TYPE(block) == BlockTypeRfcb ) {
  3265. SrvDereferenceRfcb( (PRFCB)block );
  3266. } else {
  3267. ASSERT(0);
  3268. }
  3269. }
  3270. return;
  3271. } // ProcessOrphanedBlocks
  3272. VOID
  3273. UpdateSessionLastUseTime(
  3274. IN PLARGE_INTEGER CurrentTime
  3275. )
  3276. /*++
  3277. Routine Description:
  3278. This routine walks the rfcb list and if it is found to be marked active,
  3279. the session LastUseTime is updated with the current time.
  3280. Arguments:
  3281. CurrentTime - the current system time.
  3282. Return Value:
  3283. None.
  3284. --*/
  3285. {
  3286. ULONG listEntryOffset = SrvRfcbList.ListEntryOffset;
  3287. PLIST_ENTRY listEntry;
  3288. PRFCB rfcb;
  3289. PAGED_CODE( );
  3290. //
  3291. // Acquire the lock that protects the SrvRfcbList
  3292. //
  3293. ACQUIRE_LOCK( SrvRfcbList.Lock );
  3294. //
  3295. // Walk the list of blocks until we find one with a resume handle
  3296. // greater than or equal to the specified resume handle.
  3297. //
  3298. for (
  3299. listEntry = SrvRfcbList.ListHead.Flink;
  3300. listEntry != &SrvRfcbList.ListHead;
  3301. listEntry = listEntry->Flink ) {
  3302. //
  3303. // Get a pointer to the actual block.
  3304. //
  3305. rfcb = (PRFCB)((PCHAR)listEntry - listEntryOffset);
  3306. //
  3307. // Check the state of the block and if it is active,
  3308. // reference it. This must be done as an atomic operation
  3309. // order to prevent the block from being deleted.
  3310. //
  3311. if ( rfcb->IsActive ) {
  3312. rfcb->Lfcb->Session->LastUseTime = *CurrentTime;
  3313. rfcb->IsActive = FALSE;
  3314. }
  3315. } // walk list
  3316. RELEASE_LOCK( SrvRfcbList.Lock );
  3317. return;
  3318. } // UpdateSessionLastUseTime