Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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