Windows NT 4.0 source code leak
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.

1252 lines
29 KiB

4 years ago
  1. /*++
  2. Copyright (c) 1990 Microsoft Corporation
  3. Module Name:
  4. workque.c
  5. Abstract:
  6. This module handles the communication between the NT redirector
  7. FSP and the NT redirector FSD.
  8. It defines routines that queue requests to the FSD, and routines
  9. that remove requests from the FSD work queue.
  10. Author:
  11. Larry Osterman (LarryO) 30-May-1990
  12. Revision History:
  13. 30-May-1990 LarryO
  14. Created
  15. --*/
  16. #include "precomp.h"
  17. #pragma hdrstop
  18. #define THREAD_EVENT_INTERVAL 60*60 // one hour
  19. ULONG LastThreadEventTime = (ULONG)(-1 * THREAD_EVENT_INTERVAL);
  20. KSPIN_LOCK
  21. IrpContextInterlock = {0};
  22. LIST_ENTRY
  23. IrpContextList = {0};
  24. ULONG
  25. IrpContextCount = {0};
  26. typedef struct _THREAD_SPINUP_CONTEXT {
  27. WORK_QUEUE_ITEM WorkHeader;
  28. PWORK_QUEUE WorkQueue;
  29. } THREAD_SPINUP_CONTEXT, *PTHREAD_SPINUP_CONTEXT;
  30. typedef struct _WORK_QUEUE_TERMINATION_CONTEXT {
  31. WORK_HEADER WorkHeader;
  32. PWORK_QUEUE WorkQueue;
  33. KEVENT TerminateEvent;
  34. } WORK_QUEUE_TERMINATION_CONTEXT, *PWORK_QUEUE_TERMINATION_CONTEXT;
  35. typedef struct _RDRWORK_QUEUE {
  36. PWORK_QUEUE_ITEM WorkItem;
  37. LIST_ENTRY WorkList;
  38. WORK_QUEUE_ITEM ActualWorkItem;
  39. } RDRWORK_QUEUE, *PRDRWORK_QUEUE;
  40. VOID
  41. SpinUpWorkQueue (
  42. IN PVOID Ctx
  43. );
  44. VOID
  45. TerminateWorkerThread(
  46. IN PWORK_HEADER Header
  47. );
  48. #if DBG
  49. EXCEPTION_DISPOSITION
  50. RdrWorkerThreadFilter(
  51. IN PWORKER_THREAD_ROUTINE WorkerRoutine,
  52. IN PVOID Parameter,
  53. IN PEXCEPTION_POINTERS ExceptionInfo
  54. );
  55. #endif
  56. VOID
  57. RdrExecutiveWorkerThreadRoutine(
  58. IN PVOID Ctx
  59. );
  60. #ifdef ALLOC_PRAGMA
  61. #pragma alloc_text(INIT, RdrpInitializeWorkQueue)
  62. #pragma alloc_text(PAGE, SpinUpWorkQueue)
  63. #pragma alloc_text(PAGE, RdrInitializeWorkQueue)
  64. #pragma alloc_text(PAGE, RdrAllocateIrpContext)
  65. #pragma alloc_text(PAGE3FILE, RdrQueueToWorkerThread)
  66. #pragma alloc_text(PAGE3FILE, RdrDequeueInWorkerThread)
  67. #pragma alloc_text(PAGE3FILE, RdrSpinUpWorkQueue)
  68. #pragma alloc_text(PAGE3FILE, RdrCancelQueuedIrpsForFile)
  69. #pragma alloc_text(PAGE3FILE, RdrUninitializeWorkQueue)
  70. #pragma alloc_text(PAGE3FILE, TerminateWorkerThread)
  71. #if DBG
  72. #pragma alloc_text(PAGE2VC, RdrWorkerThreadFilter)
  73. #endif
  74. //#pragma alloc_text(PAGE2VC, RdrExecutiveWorkerThreadRoutine)
  75. //#pragma alloc_text(PAGE2VC, RdrQueueWorkItem)
  76. #endif
  77. NTSTATUS
  78. RdrQueueToWorkerThread(
  79. PWORK_QUEUE WorkQueue,
  80. PWORK_ITEM Entry,
  81. BOOLEAN NotifyScavenger
  82. )
  83. /*++
  84. Routine Description:
  85. This routine passes the entry specified onto an FSP work queue, and kicks
  86. the appropriate FSP thread.
  87. Arguments:
  88. WorkQueue - Pointer to the device object for this driver.
  89. Entry - Pointer to the entry to queue.
  90. NotifyScavenger - Indicates whether the scavenger should be notified
  91. if no worker thread is currently available to process the new
  92. entry. The scavenger will create a new thread to handle the
  93. queue.
  94. Return Value:
  95. The function value is the status of the operation.
  96. Note:
  97. This routine is called at DPC_LEVEL.
  98. --*/
  99. {
  100. KIRQL OldIrql;
  101. RdrReferenceDiscardableCode(RdrFileDiscardableSection);
  102. DISCARDABLE_CODE(RdrFileDiscardableSection);
  103. // PAGED_CODE();
  104. //
  105. // If the user provided an IRP, we want to make it cancelable.
  106. //
  107. ACQUIRE_SPIN_LOCK(&WorkQueue->SpinLock, &OldIrql);
  108. if (Entry->Irp != NULL) {
  109. IoAcquireCancelSpinLock(&Entry->Irp->CancelIrql);
  110. Entry->Irp->IoStatus.Status = (ULONG)WorkQueue;
  111. //
  112. // This IRP was already canceled somehow. We want to simply call
  113. // the cancelation routine and return the appropriate error.
  114. //
  115. if (Entry->Irp->Cancel) {
  116. IoReleaseCancelSpinLock (Entry->Irp->CancelIrql);
  117. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  118. RdrCompleteRequest(Entry->Irp, STATUS_CANCELLED);
  119. RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
  120. return STATUS_CANCELLED;
  121. } else {
  122. //
  123. // This IRP has not been canceled, set a cancelation routine
  124. // that will be called when it is to enable us to clean up from
  125. // this request.
  126. //
  127. IoSetCancelRoutine( Entry->Irp, RdrCancelQueuedIrp );
  128. IoReleaseCancelSpinLock (Entry->Irp->CancelIrql);
  129. }
  130. }
  131. //
  132. // Bump the number of active requests by 1.
  133. //
  134. WorkQueue->NumberOfRequests += 1;
  135. //
  136. // If there are no blocked worker threads, queue a request to the
  137. // FSP to handle this request to create a thread for the request..
  138. //
  139. if ((WorkQueue->NumberOfRequests > WorkQueue->NumberOfThreads) &&
  140. (WorkQueue->NumberOfThreads < WorkQueue->MaximumThreads) &&
  141. !WorkQueue->SpinningUp &&
  142. WorkQueue->QueueInitialized &&
  143. NotifyScavenger) {
  144. PTHREAD_SPINUP_CONTEXT Context;
  145. Context = ALLOCATE_POOL(NonPagedPool, sizeof(THREAD_SPINUP_CONTEXT), POOL_THREADCTX);
  146. if (Context != NULL) {
  147. Context->WorkQueue = WorkQueue;
  148. ExInitializeWorkItem(&Context->WorkHeader, SpinUpWorkQueue, Context);
  149. RdrQueueWorkItem(&Context->WorkHeader, CriticalWorkQueue);
  150. WorkQueue->SpinningUp = TRUE;
  151. }
  152. }
  153. //
  154. // Insert the request on the work queue for the file system process.
  155. //
  156. InsertTailList(&WorkQueue->Queue, &Entry->Queue);
  157. //
  158. // Kick the file system process to get it to pull the request from the
  159. // list
  160. //
  161. KeSetEvent(&WorkQueue->Event,
  162. 0, // Priority boost
  163. FALSE); // No wait event will follow this.
  164. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  165. //
  166. // Return pending to the caller indicating that the request will be
  167. // handled at a later time
  168. //
  169. RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
  170. return STATUS_PENDING;
  171. }
  172. PWORK_ITEM
  173. RdrDequeueInWorkerThread (
  174. PWORK_QUEUE WorkQueue,
  175. PBOOLEAN FirstCall
  176. )
  177. /*++
  178. Routine Description:
  179. This routine's responsibility is to loop waiting for work to do.
  180. When a packet is placed onto the work queue, this routine takes the
  181. packet off of the queue and returns it.
  182. Arguments:
  183. WorkQueue - A Pointer to a work queue structure.
  184. Return Value:
  185. A pointer to the request that was pulled from the work queue.
  186. --*/
  187. {
  188. PLIST_ENTRY Entry;
  189. PWORK_ITEM Item;
  190. KIRQL OldIrql;
  191. NTSTATUS Status;
  192. RdrReferenceDiscardableCode(RdrFileDiscardableSection);
  193. DISCARDABLE_CODE(RdrFileDiscardableSection);
  194. //
  195. // If there are any requests already waiting on the request queue,
  196. // pull them off the queue and process them immediately instead of
  197. // blocking waiting for the request.
  198. //
  199. if (*FirstCall) {
  200. *FirstCall = FALSE;
  201. } else {
  202. WorkQueue->NumberOfRequests -= 1;
  203. }
  204. ACQUIRE_SPIN_LOCK(&WorkQueue->SpinLock, &OldIrql);
  205. while (TRUE) {
  206. //
  207. // If there are entries on the work queue already, remove one from
  208. // the queue.
  209. //
  210. if (!IsListEmpty(&WorkQueue->Queue)) {
  211. Entry = RemoveHeadList(&WorkQueue->Queue);
  212. Item = CONTAINING_RECORD(Entry, WORK_ITEM, Queue);
  213. //
  214. // If this is an IRP related request, check whether it's
  215. // been cancelled. If it has, complete it now. If not,
  216. // remove the cancel routine so that it can't be canceled.
  217. //
  218. if (Item->Irp != NULL) {
  219. IoAcquireCancelSpinLock( &Item->Irp->CancelIrql );
  220. if (Item->Irp->Cancel) {
  221. //
  222. // This IRP has been cancelled. Complete it now,
  223. // then go back to try again.
  224. //
  225. WorkQueue->NumberOfRequests -= 1;
  226. IoReleaseCancelSpinLock( Item->Irp->CancelIrql );
  227. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  228. RdrCompleteRequest(Item->Irp, STATUS_CANCELLED);
  229. ACQUIRE_SPIN_LOCK(&WorkQueue->SpinLock, &OldIrql);
  230. continue;
  231. }
  232. //
  233. // The IRP has not been cancelled. Don't let it be.
  234. //
  235. IoSetCancelRoutine ( Item->Irp, NULL );
  236. IoReleaseCancelSpinLock( Item->Irp->CancelIrql );
  237. }
  238. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  239. ASSERT (Item != NULL);
  240. RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
  241. return Item;
  242. }
  243. //
  244. // Wait for a request to be placed onto the work queue.
  245. //
  246. // We block UserMode to allow our stack to be paged out.
  247. //
  248. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  249. RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
  250. Status = KeWaitForSingleObject( &WorkQueue->Event,
  251. Executive,
  252. UserMode,
  253. FALSE,
  254. &WorkQueue->ThreadIdleLimit );
  255. RdrReferenceDiscardableCode(RdrFileDiscardableSection);
  256. ACQUIRE_SPIN_LOCK(&WorkQueue->SpinLock, &OldIrql);
  257. if ((Status == STATUS_TIMEOUT) &&
  258. (WorkQueue->NumberOfThreads > 1)) {
  259. if ( IsListEmpty(&WorkQueue->Queue) ) {
  260. WorkQueue->NumberOfThreads -= 1;
  261. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  262. RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
  263. PsTerminateSystemThread(STATUS_SUCCESS);
  264. }
  265. }
  266. }
  267. // Can't get here.
  268. }
  269. VOID
  270. SpinUpWorkQueue (
  271. IN PVOID Ctx
  272. )
  273. /*++
  274. Routine Description:
  275. This routine is called in the redirector scavenger to add more threads to
  276. the a redirector work queue.
  277. Arguments:
  278. IN PWORK_HEADER WorkHeader - Supplies a work header for the spin up.
  279. Return Value:
  280. None.
  281. --*/
  282. {
  283. PWORK_QUEUE WorkQueue;
  284. PTHREAD_SPINUP_CONTEXT Context;
  285. PAGED_CODE();
  286. Context = Ctx;
  287. WorkQueue = Context->WorkQueue;
  288. FREE_POOL(Context);
  289. RdrSpinUpWorkQueue(WorkQueue);
  290. WorkQueue->SpinningUp = FALSE;
  291. }
  292. VOID
  293. RdrSpinUpWorkQueue (
  294. IN PWORK_QUEUE WorkQueue
  295. )
  296. /*++
  297. Routine Description:
  298. This routine will spin up a redirector work queue.
  299. Arguments:
  300. IN PWORK_QUEUE WorkQueue - Supplies the work queue to spin up.
  301. Return Value:
  302. NTSTATUS - Status of spin up operation.
  303. --*/
  304. {
  305. KIRQL OldIrql;
  306. ULONG RetryCount;
  307. HANDLE ThreadId;
  308. NTSTATUS Status;
  309. KPRIORITY basePriority;
  310. RdrReferenceDiscardableCode(RdrFileDiscardableSection);
  311. DISCARDABLE_CODE(RdrFileDiscardableSection);
  312. ACQUIRE_SPIN_LOCK(&WorkQueue->SpinLock, &OldIrql);
  313. while (WorkQueue->QueueInitialized &&
  314. (WorkQueue->NumberOfRequests > WorkQueue->NumberOfThreads) &&
  315. (WorkQueue->NumberOfThreads < WorkQueue->MaximumThreads)) {
  316. RetryCount = 5;
  317. while ( RetryCount-- ) {
  318. WorkQueue->NumberOfThreads += 1 ;
  319. //
  320. // We cannot create threads while we own a spin lock, so
  321. // release the spin lock, start the thread, and claim the
  322. // spin lock again.
  323. //
  324. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  325. Status = PsCreateSystemThread(&ThreadId,
  326. THREAD_ALL_ACCESS,
  327. NULL, // ObjectAttributes (None)
  328. NULL, // Process Handle (System process)
  329. NULL, // Client Id (Ignored)
  330. WorkQueue->StartRoutine,
  331. WorkQueue->StartContext);
  332. if (NT_SUCCESS(Status)) {
  333. //
  334. // Set the base priority of the new thread to the lowest real
  335. // time priority.
  336. //
  337. basePriority = LOW_REALTIME_PRIORITY;
  338. Status = ZwSetInformationThread (
  339. ThreadId,
  340. ThreadPriority,
  341. &basePriority,
  342. sizeof(basePriority)
  343. );
  344. if ( !NT_SUCCESS(Status) ) {
  345. InternalError(("Unable to set thread priority: %X", Status));
  346. RdrWriteErrorLogEntry(
  347. NULL,
  348. IO_ERR_INSUFFICIENT_RESOURCES,
  349. EVENT_RDR_CANT_SET_THREAD,
  350. Status,
  351. NULL,
  352. 0
  353. );
  354. }
  355. ZwClose(ThreadId);
  356. ACQUIRE_SPIN_LOCK(&WorkQueue->SpinLock, &OldIrql);
  357. break; // out of retry loop
  358. } else {
  359. LARGE_INTEGER Timeout;
  360. Timeout.QuadPart = -1*10*1000*1000*5; // 5 seconds
  361. //
  362. // We were unable to spin up the thread, remove all traces
  363. // of the thread.
  364. //
  365. ACQUIRE_SPIN_LOCK(&WorkQueue->SpinLock, &OldIrql);
  366. WorkQueue->NumberOfThreads -= 1 ;
  367. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  368. //
  369. // Wait 5 seconds and try again
  370. //
  371. KdPrint(("RDR: Could not create system thread, waiting...\n"));
  372. KeDelayExecutionThread(KernelMode, FALSE, &Timeout);
  373. KdPrint(("RDR: Retrying system thread creation...\n"));
  374. ACQUIRE_SPIN_LOCK(&WorkQueue->SpinLock, &OldIrql);
  375. }
  376. } // while ( RetryCount-- )
  377. } // while (WorkQueue->QueueInitialized && ...
  378. //
  379. // If we wanted to create more threads but couldn't because we were at the
  380. // queue limit, log an event, but only if it's been a long time since we
  381. // last logged an event.
  382. //
  383. if (WorkQueue->QueueInitialized &&
  384. (WorkQueue->NumberOfRequests > WorkQueue->NumberOfThreads) &&
  385. (RdrCurrentTime > LastThreadEventTime+THREAD_EVENT_INTERVAL)) {
  386. ASSERT (WorkQueue->NumberOfThreads >= WorkQueue->MaximumThreads);
  387. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  388. LastThreadEventTime = RdrCurrentTime;
  389. RdrWriteErrorLogEntry(
  390. NULL,
  391. IO_ERR_INSUFFICIENT_RESOURCES,
  392. EVENT_RDR_AT_THREAD_MAX,
  393. STATUS_INSUFFICIENT_RESOURCES,
  394. NULL,
  395. 0
  396. );
  397. } else {
  398. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  399. }
  400. RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
  401. return;
  402. } // RdrSpinUpWorkQueue
  403. VOID
  404. RdrCancelQueuedIrp (
  405. IN PDEVICE_OBJECT DeviceObject,
  406. IN PIRP Irp
  407. )
  408. /*++
  409. Routine Description:
  410. This routine is called to cancel IRP's that have been queued to a redir
  411. FSP thread.
  412. Arguments:
  413. IN PDEVICE_OBJECT - Device object involved in the request (ignored).
  414. IN PIRP Irp - A Pointer to the IRP to cancel.
  415. Return Value:
  416. Status of initialization.
  417. --*/
  418. {
  419. PWORK_QUEUE WorkQueue = (PWORK_QUEUE) Irp->IoStatus.Status;
  420. KIRQL OldIrql;
  421. PLIST_ENTRY Entry, NextEntry;
  422. IoReleaseCancelSpinLock (Irp->CancelIrql);
  423. ACQUIRE_SPIN_LOCK(&WorkQueue->SpinLock, &OldIrql);
  424. for (Entry = WorkQueue->Queue.Flink;
  425. Entry != &WorkQueue->Queue;
  426. Entry = NextEntry ) {
  427. PWORK_ITEM Item = CONTAINING_RECORD(Entry, WORK_ITEM, Queue);
  428. if (Item->Irp != NULL && Item->Irp->Cancel) {
  429. //
  430. // Extract IrpContext address, leaving 3 low order bits as possible
  431. // flags across posts.
  432. //
  433. PIRP_CONTEXT IrpContext = (PIRP_CONTEXT)(Item->Irp->IoStatus.Information & ~7);
  434. NextEntry = Entry->Flink;
  435. //
  436. // Remove this entry from the list.
  437. //
  438. RemoveEntryList(Entry);
  439. WorkQueue->NumberOfRequests -= 1;
  440. //
  441. // Complete the request with an appropriate status (cancelled).
  442. //
  443. RdrCompleteRequest (Item->Irp, STATUS_CANCELLED);
  444. //
  445. // Now free up the IRP context associated with this request
  446. // if appropriate.
  447. //
  448. if (IrpContext != NULL) {
  449. RdrFreeIrpContext(IrpContext);
  450. }
  451. } else {
  452. NextEntry = Entry->Flink;
  453. }
  454. }
  455. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  456. }
  457. VOID
  458. RdrCancelQueuedIrpsForFile (
  459. IN PFILE_OBJECT FileObject,
  460. IN PWORK_QUEUE WorkQueue
  461. )
  462. /*++
  463. Routine Description:
  464. This routine is called to cancel IRP's that have been queued to a redir
  465. FSP thread for a specified file..
  466. Arguments:
  467. IN PFILE_OBJECT FileObject - A Pointer to the file object for which to cancel the IRP
  468. IN PWORK_QUEUE - Work queue used for the cancel.
  469. Return Value:
  470. Status of initialization.
  471. --*/
  472. {
  473. KIRQL OldIrql;
  474. PLIST_ENTRY Entry, NextEntry;
  475. RdrReferenceDiscardableCode(RdrFileDiscardableSection);
  476. DISCARDABLE_CODE(RdrFileDiscardableSection);
  477. ACQUIRE_SPIN_LOCK(&WorkQueue->SpinLock, &OldIrql);
  478. for (Entry = WorkQueue->Queue.Flink;
  479. Entry != &WorkQueue->Queue;
  480. Entry = NextEntry ) {
  481. PWORK_ITEM Item = CONTAINING_RECORD(Entry, WORK_ITEM, Queue);
  482. if (Item->Irp != NULL
  483. &&
  484. IoGetCurrentIrpStackLocation(Item->Irp)->FileObject == FileObject) {
  485. //
  486. // Extract IrpContext address, leaving 3 low order bits as possible
  487. // flags across posts.
  488. //
  489. PIRP_CONTEXT IrpContext = (PIRP_CONTEXT)(Item->Irp->IoStatus.Information & ~7);
  490. NextEntry = Entry->Flink;
  491. //
  492. // Remove this entry from the list.
  493. //
  494. RemoveEntryList(Entry);
  495. WorkQueue->NumberOfRequests -= 1;
  496. //
  497. // This IRP isn't cancelable any more.
  498. //
  499. IoAcquireCancelSpinLock( &Item->Irp->CancelIrql );
  500. IoSetCancelRoutine ( Item->Irp, NULL );
  501. if ( !Item->Irp->Cancel ) {
  502. //
  503. // The IRP hasn't been cancelled, so we can complete it.
  504. //
  505. IoReleaseCancelSpinLock( Item->Irp->CancelIrql );
  506. //
  507. // Complete the request with an appropriate status (cancelled).
  508. //
  509. RdrCompleteRequest (Item->Irp, STATUS_CANCELLED);
  510. } else {
  511. //
  512. // The IRP has been cancelled (and the cancel routine has
  513. // been called), so we can't complete it.
  514. //
  515. IoReleaseCancelSpinLock( Item->Irp->CancelIrql );
  516. }
  517. //
  518. // Now free up the IRP context associated with this request
  519. // if appropriate.
  520. //
  521. if (IrpContext != NULL) {
  522. RdrFreeIrpContext(IrpContext);
  523. }
  524. } else {
  525. NextEntry = Entry->Flink;
  526. }
  527. }
  528. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  529. RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
  530. }
  531. NTSTATUS
  532. RdrInitializeWorkQueue(
  533. IN PWORK_QUEUE WorkQueue,
  534. IN ULONG MaximumNumberOfThreads,
  535. IN ULONG ThreadIdleLimit,
  536. IN PKSTART_ROUTINE StartRoutine,
  537. IN PVOID StartContext
  538. )
  539. /*++
  540. Routine Description:
  541. Initialize a work queue structure, allocating all structures used for it.
  542. Arguments:
  543. PWORK_QUEUE WorkQueue - A Pointer to a work queue structure.
  544. ULONG MaximumNumberOfThreads - Max number of threads to create.
  545. ULONG ThreadIdleLimit - Number of seconds the thread is allowed to be idle.
  546. PKSTART_ROUTINE StartRoutine
  547. PVOID StartContext
  548. Return Value:
  549. Status of initialization.
  550. --*/
  551. {
  552. NTSTATUS Status;
  553. HANDLE ThreadId;
  554. KPRIORITY basePriority;
  555. PAGED_CODE();
  556. WorkQueue->Signature = STRUCTURE_SIGNATURE_WORK_QUEUE;
  557. //
  558. // Initialize the event, spinlock and IRP work queue header
  559. // for this device.
  560. //
  561. KeInitializeSpinLock( &WorkQueue->SpinLock );
  562. KeInitializeEvent( &WorkQueue->Event, SynchronizationEvent, FALSE );
  563. InitializeListHead(&WorkQueue->Queue);
  564. WorkQueue->NumberOfThreads = 1;
  565. WorkQueue->NumberOfRequests = 0;
  566. WorkQueue->MaximumThreads = MaximumNumberOfThreads;
  567. WorkQueue->StartRoutine = StartRoutine;
  568. WorkQueue->StartContext = StartContext;
  569. WorkQueue->QueueInitialized = TRUE;
  570. WorkQueue->SpinningUp = FALSE;
  571. WorkQueue->ThreadIdleLimit.QuadPart = Int32x32To64(ThreadIdleLimit, 1000 * -10000);
  572. Status = PsCreateSystemThread (
  573. &ThreadId, // Thread handle
  574. THREAD_ALL_ACCESS, // Desired Access
  575. NULL, // Object Attributes
  576. NULL, // Process handle
  577. NULL, // Client ID
  578. StartRoutine, // FSP Dispatch routine.
  579. StartContext); // Context for thread (parameter)
  580. //
  581. // Set the base priority of the redirector to the lowest real time
  582. // priority.
  583. //
  584. basePriority = LOW_REALTIME_PRIORITY;
  585. Status = ZwSetInformationThread (
  586. ThreadId,
  587. ThreadPriority,
  588. &basePriority,
  589. sizeof(basePriority)
  590. );
  591. if ( !NT_SUCCESS(Status) ) {
  592. InternalError(("RdrFsdInitialize: unable to set thread priority: %X", Status));
  593. RdrWriteErrorLogEntry(
  594. NULL,
  595. IO_ERR_INSUFFICIENT_RESOURCES,
  596. EVENT_RDR_CANT_SET_THREAD,
  597. Status,
  598. NULL,
  599. 0
  600. );
  601. ZwClose(ThreadId);
  602. return Status;
  603. }
  604. ZwClose(ThreadId);
  605. return Status;
  606. }
  607. VOID
  608. RdrUninitializeWorkQueue(
  609. IN PWORK_QUEUE WorkQueue
  610. )
  611. /*++
  612. Routine Description:
  613. Uninitialize a work queue structure, terminating all threads associated
  614. with it.
  615. Arguments:
  616. IN PWORK_QUEUE WorkQueue - A Pointer to a work queue structure.
  617. Return Value:
  618. Status of initialization.
  619. --*/
  620. {
  621. KIRQL OldIrql;
  622. // PAGED_CODE();
  623. if (!WorkQueue->QueueInitialized) {
  624. return;
  625. }
  626. RdrReferenceDiscardableCode(RdrFileDiscardableSection);
  627. DISCARDABLE_CODE(RdrFileDiscardableSection);
  628. ACQUIRE_SPIN_LOCK(&WorkQueue->SpinLock, &OldIrql);
  629. WorkQueue->QueueInitialized = FALSE;
  630. while (WorkQueue->NumberOfThreads != 0) {
  631. WORK_QUEUE_TERMINATION_CONTEXT TerminateContext;
  632. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  633. KeInitializeEvent(&TerminateContext.TerminateEvent, NotificationEvent, FALSE);
  634. TerminateContext.WorkQueue = WorkQueue;
  635. TerminateContext.WorkHeader.WorkerFunction = TerminateWorkerThread;
  636. TerminateContext.WorkHeader.WorkItem.Irp = NULL;
  637. RdrQueueToWorkerThread(WorkQueue, &TerminateContext.WorkHeader.WorkItem, FALSE);
  638. //
  639. // Wait for the thread to terminate.
  640. //
  641. KeWaitForSingleObject(&TerminateContext.TerminateEvent, Executive, KernelMode, FALSE, NULL);
  642. ACQUIRE_SPIN_LOCK(&WorkQueue->SpinLock, &OldIrql);
  643. }
  644. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  645. RdrDereferenceDiscardableCode(RdrFileDiscardableSection);
  646. return;
  647. }
  648. VOID
  649. TerminateWorkerThread(
  650. IN PWORK_HEADER Header
  651. )
  652. {
  653. KIRQL OldIrql;
  654. PWORK_QUEUE_TERMINATION_CONTEXT Context = CONTAINING_RECORD(Header, WORK_QUEUE_TERMINATION_CONTEXT, WorkHeader);
  655. PWORK_QUEUE WorkQueue = Context->WorkQueue;
  656. DISCARDABLE_CODE(RdrFileDiscardableSection);
  657. // PAGED_CODE();
  658. ACQUIRE_SPIN_LOCK(&WorkQueue->SpinLock, &OldIrql);
  659. //
  660. // There will be one less thread now.
  661. //
  662. WorkQueue->NumberOfThreads -= 1;
  663. WorkQueue->NumberOfRequests -= 1;
  664. RELEASE_SPIN_LOCK(&WorkQueue->SpinLock, OldIrql);
  665. //
  666. // Tell our caller that we're gone.
  667. //
  668. KeSetEvent(&Context->TerminateEvent, 0, FALSE);
  669. //
  670. // Terminate this thread now.
  671. //
  672. PsTerminateSystemThread(STATUS_SUCCESS);
  673. }
  674. PIRP_CONTEXT
  675. RdrAllocateIrpContext (
  676. VOID
  677. )
  678. /*++
  679. Routine Description:
  680. Initialize a work queue structure, allocating all structures used for it.
  681. Arguments:
  682. None
  683. Return Value:
  684. PIRP_CONTEXT - Newly allocated Irp Context.
  685. --*/
  686. {
  687. PIRP_CONTEXT IrpContext;
  688. PAGED_CODE();
  689. if ((IrpContext = (PIRP_CONTEXT )ExInterlockedRemoveHeadList(&IrpContextList, &IrpContextInterlock)) == NULL) {
  690. ULONG NumberOfContexts = ExInterlockedAddUlong(&IrpContextCount, 1, &IrpContextInterlock);
  691. #if RDRDBG
  692. if (NumberOfContexts > RdrData.MaximumNumberOfThreads*10) {
  693. InternalError(("Probable Irp Context Leak"));
  694. RdrInternalError(EVENT_RDR_CONTEXTS);
  695. }
  696. #endif
  697. //
  698. // If there are no IRP contexts in the "zone", allocate a new
  699. // Irp context from non paged pool.
  700. //
  701. IrpContext = ALLOCATE_POOL(NonPagedPool, sizeof(IRP_CONTEXT), POOL_IRPCTX);
  702. if (IrpContext == NULL) {
  703. InternalError(("Could not allocate pool for IRP context\n"));
  704. }
  705. return IrpContext;
  706. }
  707. #if RDRDBG
  708. if (IrpContext == NULL) {
  709. //
  710. // This should never be executed...
  711. //
  712. InternalError(("Could not allocate IRP context"));
  713. }
  714. #endif
  715. return IrpContext;
  716. }
  717. //
  718. // Redirector executive worker thread wrapper routine
  719. //
  720. //
  721. // In order to prevent the redirector from starving out the cache managers
  722. // use of executive worker threads, the redirector maintains its own queue
  723. // of work items and queues requests to that queue.
  724. //
  725. // If there are no items in progress on the queue, the redirector will spawn
  726. // an executive worker thread that will process these requests.
  727. //
  728. KSPIN_LOCK
  729. RdrWorkQueueSpinLock = {0};
  730. RDRWORK_QUEUE
  731. RdrWorkerQueue[MaximumWorkQueue] = {0};
  732. VOID
  733. RdrQueueWorkItem(
  734. IN PWORK_QUEUE_ITEM WorkItem,
  735. IN WORK_QUEUE_TYPE QueueType
  736. )
  737. /*++
  738. Routine Description:
  739. This function inserts a work item into a work queue that is processed
  740. by a worker thread of the corresponding type.
  741. Arguments:
  742. WorkItem - Supplies a pointer to the work item to add the the queue.
  743. This structure must be located in NonPagedPool. The work item
  744. structure contains a doubly linked list entry, the address of a
  745. routine to call and a parameter to pass to that routine.
  746. QueueType - Specifies the type of work queue that the work item
  747. should be placed in.
  748. Return Value:
  749. None
  750. --*/
  751. {
  752. KIRQL OldIrql;
  753. // DISCARDABLE_CODE(RdrVCDiscardableSection);
  754. ACQUIRE_SPIN_LOCK(&RdrWorkQueueSpinLock, &OldIrql);
  755. InsertTailList(&RdrWorkerQueue[QueueType].WorkList, &WorkItem->List);
  756. if (RdrWorkerQueue[QueueType].WorkItem != NULL) {
  757. ExQueueWorkItem(RdrWorkerQueue[QueueType].WorkItem, QueueType);
  758. RdrWorkerQueue[QueueType].WorkItem = NULL;
  759. }
  760. RELEASE_SPIN_LOCK(&RdrWorkQueueSpinLock, OldIrql);
  761. }
  762. VOID
  763. RdrExecutiveWorkerThreadRoutine(
  764. IN PVOID Ctx
  765. )
  766. {
  767. KIRQL OldIrql;
  768. PRDRWORK_QUEUE WorkQueue = Ctx;
  769. // RdrReferenceDiscardableCode(RdrVCDiscardableSection);
  770. //
  771. // DISCARDABLE_CODE(RdrVCDiscardableSection);
  772. ACQUIRE_SPIN_LOCK(&RdrWorkQueueSpinLock, &OldIrql);
  773. while (!IsListEmpty(&WorkQueue->WorkList)) {
  774. PWORK_QUEUE_ITEM WorkItem;
  775. #if DBG
  776. PVOID WorkerRoutine;
  777. PVOID Parameter;
  778. #endif
  779. WorkItem = (PWORK_QUEUE_ITEM)RemoveHeadList(&WorkQueue->WorkList);
  780. RELEASE_SPIN_LOCK(&RdrWorkQueueSpinLock, OldIrql);
  781. #if DBG
  782. try {
  783. WorkerRoutine = WorkItem->WorkerRoutine;
  784. Parameter = WorkItem->Parameter;
  785. (WorkItem->WorkerRoutine)(WorkItem->Parameter);
  786. if (KeGetCurrentIrql() != 0) {
  787. KdPrint(("RDRWORKER: worker exit raised IRQL, worker routine %lx\n",
  788. WorkerRoutine));
  789. DbgBreakPoint();
  790. }
  791. } except( RdrWorkerThreadFilter(WorkerRoutine,
  792. Parameter,
  793. GetExceptionInformation()) ) {
  794. }
  795. #else
  796. (WorkItem->WorkerRoutine)(WorkItem->Parameter);
  797. #endif
  798. ACQUIRE_SPIN_LOCK(&RdrWorkQueueSpinLock, &OldIrql);
  799. }
  800. WorkQueue->WorkItem = &WorkQueue->ActualWorkItem;
  801. RELEASE_SPIN_LOCK(&RdrWorkQueueSpinLock, OldIrql);
  802. // RdrDereferenceDiscardableCode(RdrVCDiscardableSection);
  803. }
  804. #if DBG
  805. EXCEPTION_DISPOSITION
  806. RdrWorkerThreadFilter(
  807. IN PWORKER_THREAD_ROUTINE WorkerRoutine,
  808. IN PVOID Parameter,
  809. IN PEXCEPTION_POINTERS ExceptionInfo
  810. )
  811. {
  812. DISCARDABLE_CODE(RdrVCDiscardableSection);
  813. KdPrint(("RDRWORKER: exception in worker routine %lx(%lx)\n", WorkerRoutine, Parameter));
  814. KdPrint((" exception record at %lx\n", ExceptionInfo->ExceptionRecord));
  815. KdPrint((" context record at %lx\n",ExceptionInfo->ContextRecord));
  816. try {
  817. DbgBreakPoint();
  818. } except (EXCEPTION_EXECUTE_HANDLER) {
  819. //
  820. // No kernel debugger attached, so let the system thread
  821. // exception handler call KeBugCheckEx.
  822. //
  823. return(EXCEPTION_CONTINUE_SEARCH);
  824. }
  825. return(EXCEPTION_EXECUTE_HANDLER);
  826. }
  827. #endif
  828. VOID
  829. RdrpInitializeWorkQueue(
  830. VOID
  831. )
  832. {
  833. ULONG i;
  834. PAGED_CODE();
  835. KeInitializeSpinLock(&RdrWorkQueueSpinLock);
  836. for (i = 0; i < MaximumWorkQueue; i++) {
  837. InitializeListHead(&RdrWorkerQueue[i].WorkList);
  838. ExInitializeWorkItem(&RdrWorkerQueue[i].ActualWorkItem, RdrExecutiveWorkerThreadRoutine, &RdrWorkerQueue[i]);
  839. RdrWorkerQueue[i].WorkItem = &RdrWorkerQueue[i].ActualWorkItem;
  840. }
  841. }