Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1799 lines
55 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. rxworkq.c
  5. Abstract:
  6. This module implements the Work queue routines for the Rx File system.
  7. Author:
  8. JoeLinn [JoeLinn] 8-8-94 Initial Implementation
  9. Balan Sethu Raman [SethuR] 22-11-95 Implemented dispatch support for mini rdrs
  10. Balan Sethu Raman [SethuR] 20-03-96 Delinked from executive worker threads
  11. Notes:
  12. There are two kinds of support for asynchronous resumption provided in the RDBSS.
  13. The I/O requests that cannot be completed in the context of the thread in which
  14. the request was made are posted to a file system process for completion.
  15. The mini redirectors also require support for asynchronously completing requests as
  16. well as post requests that cannot be completed at DPC level.
  17. The requests for posting from the mini redirectors are classified into Critical(blocking and
  18. non blocking requests. In order to ensure progress these requests are handled through
  19. separate resources. There is no well known mechanism to ensure that the hyper critical
  20. requests will not block.
  21. The two functions that are available to all mini redirector writers are
  22. RxDispatchToWorkerThread
  23. RxPostToWorkerThread.
  24. These two routines enable the mini redirector writer to make the appropriate space
  25. time tradeoffs. The RxDispatchToWorkerThread trades time for reduction in space by
  26. dynamically allocating the work item as and when required, while RxPostToWorkerThread
  27. trades space for time by pre allocating a work item.
  28. --*/
  29. #include "precomp.h"
  30. #pragma hdrstop
  31. #ifdef ALLOC_PRAGMA
  32. #pragma alloc_text(PAGE, RxInitializeDispatcher)
  33. #pragma alloc_text(PAGE, RxInitializeMRxDispatcher)
  34. #pragma alloc_text(PAGE, RxSpinDownMRxDispatcher)
  35. #pragma alloc_text(PAGE, RxInitializeWorkQueueDispatcher)
  36. #pragma alloc_text(PAGE, RxInitializeWorkQueue)
  37. #pragma alloc_text(PAGE, RxTearDownDispatcher)
  38. #pragma alloc_text(PAGE, RxTearDownWorkQueueDispatcher)
  39. #pragma alloc_text(PAGE, RxpSpinUpWorkerThreads)
  40. #pragma alloc_text(PAGE, RxBootstrapWorkerThreadDispatcher)
  41. #pragma alloc_text(PAGE, RxWorkerThreadDispatcher)
  42. #endif
  43. //
  44. // The local debug trace level
  45. //
  46. #define Dbg (DEBUG_TRACE_FSP_DISPATCHER)
  47. //
  48. // We got the following structure from ntexapi.h. This should be moved to a
  49. // common header at some point.
  50. //
  51. #if defined(_IA64_)
  52. typedef ULONG SYSINF_PAGE_COUNT;
  53. #else
  54. typedef SIZE_T SYSINF_PAGE_COUNT;
  55. #endif
  56. typedef struct _SYSTEM_BASIC_INFORMATION {
  57. ULONG Reserved;
  58. ULONG TimerResolution;
  59. ULONG PageSize;
  60. SYSINF_PAGE_COUNT NumberOfPhysicalPages;
  61. SYSINF_PAGE_COUNT LowestPhysicalPageNumber;
  62. SYSINF_PAGE_COUNT HighestPhysicalPageNumber;
  63. ULONG AllocationGranularity;
  64. ULONG_PTR MinimumUserModeAddress;
  65. ULONG_PTR MaximumUserModeAddress;
  66. ULONG_PTR ActiveProcessorsAffinityMask;
  67. CCHAR NumberOfProcessors;
  68. } SYSTEM_BASIC_INFORMATION, *PSYSTEM_BASIC_INFORMATION;
  69. //
  70. // We got the following definition from zwapi.h. This should be moved to a
  71. // common header at some point.
  72. //
  73. NTSYSAPI
  74. NTSTATUS
  75. NTAPI
  76. ZwQuerySystemInformation (
  77. IN ULONG SystemInformationClass,
  78. OUT PVOID SystemInformation,
  79. IN ULONG SystemInformationLength,
  80. OUT PULONG ReturnLength OPTIONAL
  81. );
  82. //
  83. // had to steal this from ntifs.h in order to use ntsrv.h
  84. //
  85. extern POBJECT_TYPE *PsThreadType;
  86. //
  87. // Prototype forward declarations.
  88. //
  89. extern NTSTATUS
  90. RxInitializeWorkQueueDispatcher(
  91. PRX_WORK_QUEUE_DISPATCHER pDispatcher);
  92. extern
  93. VOID
  94. RxInitializeWorkQueue(
  95. PRX_WORK_QUEUE pWorkQueue,
  96. WORK_QUEUE_TYPE WorkQueueType,
  97. ULONG MaximumNumberOfWorkerThreads,
  98. ULONG MinimumNumberOfWorkerThreads);
  99. extern VOID
  100. RxTearDownWorkQueueDispatcher(
  101. PRX_WORK_QUEUE_DISPATCHER pDispatcher);
  102. extern VOID
  103. RxTearDownWorkQueue(
  104. PRX_WORK_QUEUE pWorkQueue);
  105. extern NTSTATUS
  106. RxSpinUpWorkerThread(
  107. PRX_WORK_QUEUE pWorkQueue,
  108. PRX_WORKERTHREAD_ROUTINE Routine,
  109. PVOID Parameter);
  110. extern VOID
  111. RxSpinUpWorkerThreads(
  112. PRX_WORK_QUEUE pWorkQueue);
  113. extern VOID
  114. RxSpinDownWorkerThreads(
  115. PRX_WORK_QUEUE pWorkQueue);
  116. extern VOID
  117. RxpWorkerThreadDispatcher(
  118. IN PRX_WORK_QUEUE pWorkQueue,
  119. IN PLARGE_INTEGER pWaitInterval);
  120. VOID
  121. RxBootstrapWorkerThreadDispatcher(
  122. IN PRX_WORK_QUEUE pWorkQueue);
  123. VOID
  124. RxWorkerThreadDispatcher(
  125. IN PRX_WORK_QUEUE pWorkQueue);
  126. extern VOID
  127. RxWorkItemDispatcher(
  128. PVOID pContext);
  129. extern VOID
  130. RxSpinUpRequestsDispatcher(
  131. PRX_DISPATCHER pDispatcher);
  132. // The spin up requests thread
  133. PETHREAD RxSpinUpRequestsThread = NULL;
  134. //
  135. // The delay parameter for KQUEUE wait requests in the RX_WORK_QUEUE implemenation
  136. //
  137. LARGE_INTEGER RxWorkQueueWaitInterval[MaximumWorkQueue];
  138. LARGE_INTEGER RxSpinUpDispatcherWaitInterval;
  139. //
  140. // Currently the levels correspond to the three levels defined in ex.h
  141. // Delayed,Critical and HyperCritical. As regards mini redirectors if any work
  142. // is not dependent on any mini redirector/RDBSS resource, i.e., it will not wait
  143. // it can be classified as a hypercritical1 work item. There is no good way to
  144. // enforce this, therefore one should exercise great caution before classifying
  145. // something as hypercritical.
  146. //
  147. NTSTATUS
  148. RxInitializeDispatcher()
  149. /*++
  150. Routine Description:
  151. This routine initializes the work queues dispatcher
  152. Return Value:
  153. STATUS_SUCCESS -- successful
  154. other status codes indicate failure to initialize
  155. Notes:
  156. The dispatching mechanism is implemented as a two tiered approach. Each
  157. processor in the system is associated with a set of work queues. A best
  158. effort is made to schedule all work emanating from a processor onto the
  159. same processor. This prevents excessive sloshing of state information from
  160. one processor cache to another.
  161. For a given processor there are three work queues corresponding to three
  162. levels of classification -- Delayed Work items, Critical Work items and
  163. Hyper Critical work items. Each of these levels is associated with a
  164. Kernel Queue (KQUEUE). The number of threads associated with each of these
  165. queues can be independently controlled.
  166. Currently the tuning parameters for the dispatcher are all hard coded. A
  167. mechanism to intialize them from the registry needs to be implemented.
  168. The following parameters associated with the dispatcher can be tuned ...
  169. 1) the wait time intervals associated with the kernel queue for each level.
  170. 2) the minimum and amximum number of worker threads associated with each
  171. level.
  172. --*/
  173. {
  174. ULONG ProcessorIndex,NumberOfProcessors;
  175. NTSTATUS Status;
  176. PAGED_CODE();
  177. // Currently we set the number of processors to 1. In future the
  178. // dispatcher can be tailored to multi processor implementation
  179. // by appropriately initializing it as follows
  180. // NumberOfProcessors = KeNumberProcessors;
  181. NumberOfProcessors = 1;
  182. RxFileSystemDeviceObject->DispatcherContext.NumberOfWorkerThreads = 0;
  183. RxFileSystemDeviceObject->DispatcherContext.pTearDownEvent = NULL;
  184. // Currently the default values for the wait intervals are set as
  185. // 10 seconds ( expressed in system time units of 100 ns ticks ).
  186. RxWorkQueueWaitInterval[DelayedWorkQueue].QuadPart = -10 * TICKS_PER_SECOND;
  187. RxWorkQueueWaitInterval[CriticalWorkQueue].QuadPart = -10 * TICKS_PER_SECOND;
  188. RxWorkQueueWaitInterval[HyperCriticalWorkQueue].QuadPart = -10 * TICKS_PER_SECOND;
  189. RxSpinUpDispatcherWaitInterval.QuadPart = -60 * TICKS_PER_SECOND;
  190. RxDispatcher.NumberOfProcessors = NumberOfProcessors;
  191. RxDispatcher.OwnerProcess = IoGetCurrentProcess();
  192. RxDispatcher.pWorkQueueDispatcher = &RxDispatcherWorkQueues;
  193. if (RxDispatcher.pWorkQueueDispatcher != NULL) {
  194. for (
  195. ProcessorIndex = 0;
  196. ProcessorIndex < NumberOfProcessors;
  197. ProcessorIndex++
  198. ) {
  199. Status = RxInitializeWorkQueueDispatcher(
  200. &RxDispatcher.pWorkQueueDispatcher[ProcessorIndex]);
  201. if (Status != STATUS_SUCCESS) {
  202. break;
  203. }
  204. }
  205. if (Status == STATUS_SUCCESS) {
  206. Status = RxInitializeMRxDispatcher(RxFileSystemDeviceObject);
  207. }
  208. } else {
  209. Status = STATUS_INSUFFICIENT_RESOURCES;
  210. }
  211. if (Status == STATUS_SUCCESS) {
  212. HANDLE ThreadHandle;
  213. KeInitializeEvent(
  214. &RxDispatcher.SpinUpRequestsEvent,
  215. NotificationEvent,
  216. FALSE);
  217. KeInitializeEvent(
  218. &RxDispatcher.SpinUpRequestsTearDownEvent,
  219. NotificationEvent,
  220. FALSE);
  221. InitializeListHead(
  222. &RxDispatcher.SpinUpRequests);
  223. RxDispatcher.State = RxDispatcherActive;
  224. KeInitializeSpinLock(&RxDispatcher.SpinUpRequestsLock);
  225. Status = PsCreateSystemThread(
  226. &ThreadHandle,
  227. PROCESS_ALL_ACCESS,
  228. NULL,
  229. NULL,
  230. NULL,
  231. RxSpinUpRequestsDispatcher,
  232. &RxDispatcher);
  233. if (NT_SUCCESS(Status)) {
  234. // Close the handle so the thread can die when needed
  235. ZwClose(ThreadHandle);
  236. }
  237. }
  238. return Status;
  239. }
  240. NTSTATUS
  241. RxInitializeMRxDispatcher(PRDBSS_DEVICE_OBJECT pMRxDeviceObject)
  242. /*++
  243. Routine Description:
  244. This routine initializes the dispatcher context for a mini rdr
  245. Return Value:
  246. STATUS_SUCCESS -- successful
  247. other status codes indicate failure to initialize
  248. Notes:
  249. --*/
  250. {
  251. PAGED_CODE();
  252. pMRxDeviceObject->DispatcherContext.NumberOfWorkerThreads = 0;
  253. pMRxDeviceObject->DispatcherContext.pTearDownEvent = NULL;
  254. return STATUS_SUCCESS;
  255. }
  256. NTSTATUS
  257. RxSpinDownMRxDispatcher(PRDBSS_DEVICE_OBJECT pMRxDeviceObject)
  258. /*++
  259. Routine Description:
  260. This routine tears down the dispatcher context for a mini rdr
  261. Return Value:
  262. STATUS_SUCCESS -- successful
  263. other status codes indicate failure to initialize
  264. Notes:
  265. --*/
  266. {
  267. LONG FinalRefCount;
  268. KEVENT TearDownEvent;
  269. KIRQL SavedIrql;
  270. PAGED_CODE();
  271. KeInitializeEvent(
  272. &TearDownEvent,
  273. NotificationEvent,
  274. FALSE);
  275. InterlockedIncrement(&pMRxDeviceObject->DispatcherContext.NumberOfWorkerThreads);
  276. pMRxDeviceObject->DispatcherContext.pTearDownEvent = &TearDownEvent;
  277. FinalRefCount = InterlockedDecrement(&pMRxDeviceObject->DispatcherContext.NumberOfWorkerThreads);
  278. if (FinalRefCount > 0) {
  279. KeWaitForSingleObject(
  280. &TearDownEvent,
  281. Executive,
  282. KernelMode,
  283. FALSE,
  284. NULL);
  285. } else {
  286. InterlockedExchangePointer(
  287. &pMRxDeviceObject->DispatcherContext.pTearDownEvent,
  288. NULL);
  289. }
  290. ASSERT(pMRxDeviceObject->DispatcherContext.pTearDownEvent == NULL);
  291. return STATUS_SUCCESS;
  292. }
  293. NTSTATUS
  294. RxInitializeWorkQueueDispatcher(
  295. PRX_WORK_QUEUE_DISPATCHER pDispatcher)
  296. /*++
  297. Routine Description:
  298. This routine initializes the work queue dispatcher for a particular processor
  299. Arguments:
  300. pDispatcher - Work Queue Dispatcher
  301. Return Value:
  302. STATUS_SUCCESS -- successful
  303. other status codes indicate failure to initialize
  304. Notes:
  305. For each of the work queues associated with a processor the minimum number of
  306. worker threads and maximum number of worker threads can be independently
  307. specified and tuned.
  308. The two factors that influence these decision are (1) the cost of spinnning up/
  309. spinning down worker threads and (2) the amount of resources consumed by an
  310. idle worker thread.
  311. Currently these numbers are hard coded, a desirable extension would be a mechanism
  312. to initialize them from a registry setting. This will enable us to tune the
  313. parameters easily. This has to be implemented.
  314. --*/
  315. {
  316. NTSTATUS Status;
  317. MM_SYSTEMSIZE SystemSize;
  318. ULONG MaxNumberOfCriticalWorkerThreads;
  319. ULONG MinNumberOfCriticalWorkerThreads;
  320. RTL_OSVERSIONINFOEXW OsVersion;
  321. SYSTEM_BASIC_INFORMATION SystemBasicInfo;
  322. ULONGLONG SystemMemorySize = 0;
  323. PAGED_CODE();
  324. RxInitializeWorkQueue(&pDispatcher->WorkQueue[DelayedWorkQueue],
  325. DelayedWorkQueue,
  326. 2,
  327. 1);
  328. SystemSize = MmQuerySystemSize();
  329. Status = ZwQuerySystemInformation(0, // SystemBasicInformation,
  330. &SystemBasicInfo,
  331. sizeof(SystemBasicInfo),
  332. NULL);
  333. if (Status == STATUS_SUCCESS) {
  334. SystemMemorySize = ( (ULONGLONG)SystemBasicInfo.NumberOfPhysicalPages * (ULONGLONG)SystemBasicInfo.PageSize );
  335. }
  336. OsVersion.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
  337. Status = RtlGetVersion( (PRTL_OSVERSIONINFOW)&(OsVersion) );
  338. if (Status == STATUS_SUCCESS) {
  339. if (SystemMemorySize == 0) {
  340. //
  341. // If we could not query the SystemMemorySize then we launch 5
  342. // CriticalQueue threads if this machine is a SERVER and an
  343. // MmLargeSystem.
  344. //
  345. if (SystemSize == MmLargeSystem && OsVersion.wProductType == VER_NT_SERVER) {
  346. MaxNumberOfCriticalWorkerThreads = 10;
  347. MinNumberOfCriticalWorkerThreads = 5;
  348. } else {
  349. MaxNumberOfCriticalWorkerThreads = 5;
  350. MinNumberOfCriticalWorkerThreads = 1;
  351. }
  352. } else {
  353. //
  354. // If the SystemMemorySize (physical memory) is more than 512MB
  355. // and the machine is a SERVER, we launch 5 and keep a minimum
  356. // of 5 CriticalQueue threads. 512MB is 0x1E848000 bytes.
  357. //
  358. if ( (SystemMemorySize >= (ULONGLONG)0x1E848000) && OsVersion.wProductType == VER_NT_SERVER) {
  359. MaxNumberOfCriticalWorkerThreads = 10;
  360. MinNumberOfCriticalWorkerThreads = 5;
  361. } else {
  362. MaxNumberOfCriticalWorkerThreads = 5;
  363. MinNumberOfCriticalWorkerThreads = 1;
  364. }
  365. }
  366. RxInitializeWorkQueue(&pDispatcher->WorkQueue[CriticalWorkQueue],
  367. CriticalWorkQueue,
  368. MaxNumberOfCriticalWorkerThreads,
  369. MinNumberOfCriticalWorkerThreads);
  370. RxInitializeWorkQueue(&pDispatcher->WorkQueue[HyperCriticalWorkQueue],
  371. HyperCriticalWorkQueue,
  372. 5,
  373. 1);
  374. Status = RxSpinUpWorkerThread(&pDispatcher->WorkQueue[HyperCriticalWorkQueue],
  375. RxBootstrapWorkerThreadDispatcher,
  376. &pDispatcher->WorkQueue[HyperCriticalWorkQueue]);
  377. }
  378. if (Status == STATUS_SUCCESS) {
  379. for ( ; ; ) {
  380. if (MinNumberOfCriticalWorkerThreads == 0 || Status != STATUS_SUCCESS) {
  381. break;
  382. }
  383. Status = RxSpinUpWorkerThread(&pDispatcher->WorkQueue[CriticalWorkQueue],
  384. RxBootstrapWorkerThreadDispatcher,
  385. &pDispatcher->WorkQueue[CriticalWorkQueue]);
  386. MinNumberOfCriticalWorkerThreads--;
  387. }
  388. }
  389. if (Status == STATUS_SUCCESS) {
  390. Status = RxSpinUpWorkerThread(&pDispatcher->WorkQueue[DelayedWorkQueue],
  391. RxBootstrapWorkerThreadDispatcher,
  392. &pDispatcher->WorkQueue[DelayedWorkQueue]);
  393. }
  394. return Status;
  395. }
  396. VOID
  397. RxInitializeWorkQueue(
  398. PRX_WORK_QUEUE pWorkQueue,
  399. WORK_QUEUE_TYPE WorkQueueType,
  400. ULONG MaximumNumberOfWorkerThreads,
  401. ULONG MinimumNumberOfWorkerThreads)
  402. /*++
  403. Routine Description:
  404. This routine initializes a work queue
  405. Arguments:
  406. pWorkQueue - Work Queue Dispatcher
  407. MaximumNumberOfWorkerThreads - the upper bound on worker threads
  408. MinimumNumberOfWorkerThreads - the lower bound on the threads.
  409. --*/
  410. {
  411. PAGED_CODE();
  412. pWorkQueue->Type = (UCHAR)WorkQueueType;
  413. pWorkQueue->State = RxWorkQueueActive;
  414. pWorkQueue->SpinUpRequestPending = FALSE;
  415. pWorkQueue->pRundownContext = NULL;
  416. pWorkQueue->NumberOfWorkItemsDispatched = 0;
  417. pWorkQueue->NumberOfWorkItemsToBeDispatched = 0;
  418. pWorkQueue->CumulativeQueueLength = 0;
  419. pWorkQueue->NumberOfSpinUpRequests = 0;
  420. pWorkQueue->MaximumNumberOfWorkerThreads = MaximumNumberOfWorkerThreads;
  421. pWorkQueue->MinimumNumberOfWorkerThreads = MinimumNumberOfWorkerThreads;
  422. pWorkQueue->NumberOfActiveWorkerThreads = 0;
  423. pWorkQueue->NumberOfIdleWorkerThreads = 0;
  424. pWorkQueue->NumberOfFailedSpinUpRequests = 0;
  425. pWorkQueue->WorkQueueItemForSpinUpWorkerThreadInUse = 0;
  426. ExInitializeWorkItem(&pWorkQueue->WorkQueueItemForTearDownWorkQueue,NULL,NULL);
  427. ExInitializeWorkItem(&pWorkQueue->WorkQueueItemForSpinUpWorkerThread,NULL,NULL);
  428. ExInitializeWorkItem(&pWorkQueue->WorkQueueItemForSpinDownWorkerThread,NULL,NULL);
  429. pWorkQueue->WorkQueueItemForSpinDownWorkerThread.pDeviceObject = NULL;
  430. pWorkQueue->WorkQueueItemForSpinUpWorkerThread.pDeviceObject = NULL;
  431. pWorkQueue->WorkQueueItemForTearDownWorkQueue.pDeviceObject = NULL;
  432. KeInitializeQueue(&pWorkQueue->Queue,MaximumNumberOfWorkerThreads);
  433. KeInitializeSpinLock(&pWorkQueue->SpinLock);
  434. }
  435. NTSTATUS
  436. RxTearDownDispatcher()
  437. /*++
  438. Routine Description:
  439. This routine tears down the dispatcher
  440. Return Value:
  441. STATUS_SUCCESS -- successful
  442. other status codes indicate failure to initialize
  443. --*/
  444. {
  445. LONG ProcessorIndex;
  446. NTSTATUS Status;
  447. PAGED_CODE();
  448. if (RxDispatcher.pWorkQueueDispatcher != NULL) {
  449. RxDispatcher.State = RxDispatcherInactive;
  450. KeSetEvent(
  451. &RxDispatcher.SpinUpRequestsEvent,
  452. IO_NO_INCREMENT,
  453. FALSE);
  454. KeWaitForSingleObject(
  455. &RxDispatcher.SpinUpRequestsTearDownEvent,
  456. Executive,
  457. KernelMode,
  458. FALSE,
  459. NULL);
  460. if (RxSpinUpRequestsThread != NULL) {
  461. if (!PsIsThreadTerminating(RxSpinUpRequestsThread)) {
  462. // Wait for the thread to terminate.
  463. KeWaitForSingleObject(
  464. RxSpinUpRequestsThread,
  465. Executive,
  466. KernelMode,
  467. FALSE,
  468. NULL);
  469. ASSERT(PsIsThreadTerminating(RxSpinUpRequestsThread));
  470. }
  471. ObDereferenceObject(RxSpinUpRequestsThread);
  472. }
  473. for (
  474. ProcessorIndex = 0;
  475. ProcessorIndex < RxDispatcher.NumberOfProcessors;
  476. ProcessorIndex++
  477. ) {
  478. RxTearDownWorkQueueDispatcher(&RxDispatcher.pWorkQueueDispatcher[ProcessorIndex]);
  479. }
  480. //RxFreePool(RxDispatcher.pWorkQueueDispatcher);
  481. }
  482. return STATUS_SUCCESS;
  483. }
  484. VOID
  485. RxTearDownWorkQueueDispatcher(
  486. PRX_WORK_QUEUE_DISPATCHER pDispatcher)
  487. /*++
  488. Routine Description:
  489. This routine tears dwon the work queue dispatcher for a particular processor
  490. Arguments:
  491. pDispatcher - Work Queue Dispatcher
  492. --*/
  493. {
  494. PAGED_CODE();
  495. RxTearDownWorkQueue(
  496. &pDispatcher->WorkQueue[DelayedWorkQueue]);
  497. RxTearDownWorkQueue(
  498. &pDispatcher->WorkQueue[CriticalWorkQueue]);
  499. RxTearDownWorkQueue(
  500. &pDispatcher->WorkQueue[HyperCriticalWorkQueue]);
  501. }
  502. VOID
  503. RxTearDownWorkQueue(
  504. PRX_WORK_QUEUE pWorkQueue)
  505. /*++
  506. Routine Description:
  507. This routine tears down a work queue
  508. Arguments:
  509. pWorkQueue - Work Queue
  510. Notes:
  511. Tearing down a work queue is a more complex process when compared to initializing
  512. a work queue. This is because of the threads associated with the queue. In order
  513. to ensure that the work queue can be torn down correctly each of the threads
  514. associated with the queue must be spun down correctly.
  515. This is accomplished by changing the state of the work queue from
  516. RxWorkQueueActive to RxWorkQueueRundownInProgress. This prevents further requests
  517. from being inserted into the queue. Having done that the currently active threads
  518. must be spundown.
  519. The spinning down process is accelerated by posting a dummy work item onto the
  520. work queue so that the waits are immediately satisfied.
  521. --*/
  522. {
  523. KIRQL SavedIrql;
  524. ULONG NumberOfActiveThreads;
  525. PLIST_ENTRY pFirstListEntry,pNextListEntry;
  526. PRX_WORK_QUEUE_RUNDOWN_CONTEXT pRundownContext;
  527. pRundownContext = (PRX_WORK_QUEUE_RUNDOWN_CONTEXT)
  528. RxAllocatePoolWithTag(
  529. NonPagedPool,
  530. sizeof(RX_WORK_QUEUE_RUNDOWN_CONTEXT) +
  531. pWorkQueue->MaximumNumberOfWorkerThreads * sizeof(PETHREAD),
  532. RX_WORKQ_POOLTAG);
  533. if (pRundownContext != NULL) {
  534. KeInitializeEvent(
  535. &pRundownContext->RundownCompletionEvent,
  536. NotificationEvent,
  537. FALSE);
  538. pRundownContext->NumberOfThreadsSpunDown = 0;
  539. pRundownContext->ThreadPointers = (PETHREAD *)(pRundownContext + 1);
  540. KeAcquireSpinLock(&pWorkQueue->SpinLock,&SavedIrql);
  541. ASSERT((pWorkQueue->pRundownContext == NULL) &&
  542. (pWorkQueue->State == RxWorkQueueActive));
  543. pWorkQueue->pRundownContext = pRundownContext;
  544. pWorkQueue->State = RxWorkQueueRundownInProgress;
  545. NumberOfActiveThreads = pWorkQueue->NumberOfActiveWorkerThreads;
  546. KeReleaseSpinLock(&pWorkQueue->SpinLock,SavedIrql);
  547. if (NumberOfActiveThreads > 0) {
  548. pWorkQueue->WorkQueueItemForTearDownWorkQueue.pDeviceObject = RxFileSystemDeviceObject;
  549. InterlockedIncrement(&RxFileSystemDeviceObject->DispatcherContext.NumberOfWorkerThreads);
  550. ExInitializeWorkItem(&pWorkQueue->WorkQueueItemForTearDownWorkQueue,RxSpinDownWorkerThreads,pWorkQueue);
  551. KeInsertQueue(&pWorkQueue->Queue,&pWorkQueue->WorkQueueItemForTearDownWorkQueue.List);
  552. KeWaitForSingleObject(
  553. &pWorkQueue->pRundownContext->RundownCompletionEvent,
  554. Executive,
  555. KernelMode,
  556. FALSE,
  557. NULL);
  558. }
  559. if (pRundownContext->NumberOfThreadsSpunDown > 0) {
  560. LONG Index = 0;
  561. for (
  562. Index = pRundownContext->NumberOfThreadsSpunDown - 1;
  563. Index >= 0;
  564. Index--
  565. ) {
  566. PETHREAD pThread;
  567. pThread = pRundownContext->ThreadPointers[Index];
  568. ASSERT(pThread != NULL);
  569. if (!PsIsThreadTerminating(pThread)) {
  570. // Wait for the thread to terminate.
  571. KeWaitForSingleObject(
  572. pThread,
  573. Executive,
  574. KernelMode,
  575. FALSE,
  576. NULL);
  577. ASSERT(PsIsThreadTerminating(pThread));
  578. }
  579. ObDereferenceObject(pThread);
  580. }
  581. }
  582. RxFreePool(pRundownContext);
  583. }
  584. ASSERT(pWorkQueue->NumberOfActiveWorkerThreads == 0);
  585. pFirstListEntry = KeRundownQueue(&pWorkQueue->Queue);
  586. if (pFirstListEntry != NULL) {
  587. pNextListEntry = pFirstListEntry;
  588. do {
  589. PWORK_QUEUE_ITEM pWorkQueueItem;
  590. pWorkQueueItem = (PWORK_QUEUE_ITEM)
  591. CONTAINING_RECORD(
  592. pNextListEntry,
  593. WORK_QUEUE_ITEM,
  594. List);
  595. pNextListEntry = pNextListEntry->Flink;
  596. if (pWorkQueueItem->WorkerRoutine == RxWorkItemDispatcher) {
  597. RxFreePool(pWorkQueueItem);
  598. }
  599. } while (pNextListEntry != pFirstListEntry);
  600. }
  601. }
  602. NTSTATUS
  603. RxSpinUpWorkerThread(
  604. PRX_WORK_QUEUE pWorkQueue,
  605. PRX_WORKERTHREAD_ROUTINE Routine,
  606. PVOID Parameter)
  607. /*++
  608. Routine Description:
  609. This routine spins up a worker thread associated with the given queue.
  610. Arguments:
  611. pWorkQueue - the WorkQueue instance.
  612. Routine - the thread routine
  613. Parameter - the thread routine parameter
  614. Return Value:
  615. STATUS_SUCCESS if successful,
  616. otherwise appropriate error code
  617. --*/
  618. {
  619. NTSTATUS Status;
  620. HANDLE ThreadHandle;
  621. KIRQL SavedIrql;
  622. PAGED_CODE();
  623. KeAcquireSpinLock(&pWorkQueue->SpinLock,&SavedIrql);
  624. if( pWorkQueue->State == RxWorkQueueActive )
  625. {
  626. pWorkQueue->NumberOfActiveWorkerThreads++;
  627. Status = STATUS_SUCCESS;
  628. //RxLogRetail(("SpinUpWT %x %d %d\n", pWorkQueue, pWorkQueue->State, pWorkQueue->NumberOfActiveWorkerThreads ));
  629. }
  630. else
  631. {
  632. Status = STATUS_UNSUCCESSFUL;
  633. RxLogRetail(("SpinUpWT Fail %x %d %d\n", pWorkQueue, pWorkQueue->State, pWorkQueue->NumberOfActiveWorkerThreads ));
  634. //DbgPrint("[dkruse] RDBSS would have crashed here without this fix!\n");
  635. }
  636. KeReleaseSpinLock(&pWorkQueue->SpinLock, SavedIrql );
  637. if( NT_SUCCESS(Status) )
  638. {
  639. Status = PsCreateSystemThread(
  640. &ThreadHandle,
  641. PROCESS_ALL_ACCESS,
  642. NULL,
  643. NULL,
  644. NULL,
  645. Routine,
  646. Parameter);
  647. if (NT_SUCCESS(Status)) {
  648. // Close the handle so the thread can die when needed
  649. ZwClose(ThreadHandle);
  650. } else {
  651. // Log the inability to create a worker thread.
  652. RxLog(("WorkQ: %lx SpinUpStat %lx\n",pWorkQueue,Status));
  653. RxWmiLogError(Status,
  654. LOG,
  655. RxSpinUpWorkerThread,
  656. LOGPTR(pWorkQueue)
  657. LOGULONG(Status));
  658. // Change the thread count back, and set the rundown completion event if necessary
  659. KeAcquireSpinLock( &pWorkQueue->SpinLock, &SavedIrql );
  660. pWorkQueue->NumberOfActiveWorkerThreads--;
  661. pWorkQueue->NumberOfFailedSpinUpRequests++;
  662. if( (pWorkQueue->NumberOfActiveWorkerThreads == 0) &&
  663. (pWorkQueue->State == RxWorkQueueRundownInProgress) )
  664. {
  665. KeSetEvent(
  666. &pWorkQueue->pRundownContext->RundownCompletionEvent,
  667. IO_NO_INCREMENT,
  668. FALSE);
  669. }
  670. RxLogRetail(("SpinUpWT Fail2 %x %d %d\n", pWorkQueue, pWorkQueue->State, pWorkQueue->NumberOfActiveWorkerThreads ));
  671. KeReleaseSpinLock( &pWorkQueue->SpinLock, SavedIrql );
  672. }
  673. }
  674. return Status;
  675. }
  676. VOID
  677. RxpSpinUpWorkerThreads(
  678. PRX_WORK_QUEUE pWorkQueue)
  679. /*++
  680. Routine Description:
  681. This routine ensures that the dispatcher is not torn down while requests
  682. are pending in the kernel worker threads for spin ups
  683. Arguments:
  684. pWorkQueue - the WorkQueue instance.
  685. Notes:
  686. There is implicit reliance on the fact that the RxDispatcher owner process
  687. is the same as the system process. If this is not TRUE then an alternate
  688. way needs to be implemented for ensuring that spinup requests are not stuck
  689. behind other requests.
  690. --*/
  691. {
  692. LONG NumberOfWorkerThreads;
  693. PAGED_CODE();
  694. ASSERT(IoGetCurrentProcess() == RxDispatcher.OwnerProcess);
  695. RxSpinUpWorkerThreads(pWorkQueue);
  696. NumberOfWorkerThreads = InterlockedDecrement(
  697. &RxFileSystemDeviceObject->DispatcherContext.NumberOfWorkerThreads);
  698. if (NumberOfWorkerThreads == 0) {
  699. PKEVENT pTearDownEvent;
  700. pTearDownEvent = (PKEVENT)
  701. InterlockedExchangePointer(
  702. &RxFileSystemDeviceObject->DispatcherContext.pTearDownEvent,
  703. NULL);
  704. if (pTearDownEvent != NULL) {
  705. KeSetEvent(
  706. pTearDownEvent,
  707. IO_NO_INCREMENT,
  708. FALSE);
  709. }
  710. }
  711. }
  712. VOID
  713. RxSpinUpRequestsDispatcher(
  714. PRX_DISPATCHER pDispatcher)
  715. /*++
  716. Routine Description:
  717. This routine ensures that there is an independent thread to handle spinup
  718. requests for all types of threads. This routine will be active as long as
  719. the dispatcher is active
  720. Arguments:
  721. pDispatcher - the dispatcher instance.
  722. Notes:
  723. There is implicit reliance on the fact that the RxDispatcher owner process
  724. is the same as the system process. If this is not TRUE then an alternate
  725. way needs to be implemented for ensuring that spinup requests are not stuck
  726. behind other requests.
  727. --*/
  728. {
  729. PETHREAD ThisThread;
  730. NTSTATUS Status;
  731. RxDbgTrace(0,Dbg,("+++++ Worker SpinUp Requests Thread Startup %lx\n",PsGetCurrentThread()));
  732. ThisThread = PsGetCurrentThread();
  733. Status = ObReferenceObjectByPointer(
  734. ThisThread,
  735. THREAD_ALL_ACCESS,
  736. *PsThreadType,
  737. KernelMode);
  738. if (Status == STATUS_SUCCESS) {
  739. RxSpinUpRequestsThread = ThisThread;
  740. for (;;) {
  741. NTSTATUS Status;
  742. RX_DISPATCHER_STATE State;
  743. KIRQL SavedIrql;
  744. LIST_ENTRY SpinUpRequests;
  745. PLIST_ENTRY pListEntry;
  746. InitializeListHead(&SpinUpRequests);
  747. Status = KeWaitForSingleObject(
  748. &pDispatcher->SpinUpRequestsEvent,
  749. Executive,
  750. KernelMode,
  751. FALSE,
  752. &RxSpinUpDispatcherWaitInterval);
  753. ASSERT((Status == STATUS_SUCCESS) || (Status == STATUS_TIMEOUT));
  754. KeAcquireSpinLock(
  755. &pDispatcher->SpinUpRequestsLock,
  756. &SavedIrql);
  757. RxTransferList(
  758. &SpinUpRequests,
  759. &pDispatcher->SpinUpRequests);
  760. State = pDispatcher->State;
  761. KeResetEvent(
  762. &pDispatcher->SpinUpRequestsEvent);
  763. KeReleaseSpinLock(
  764. &pDispatcher->SpinUpRequestsLock,
  765. SavedIrql);
  766. // Process the spin up requests
  767. while (!IsListEmpty(&SpinUpRequests)) {
  768. PRX_WORKERTHREAD_ROUTINE Routine;
  769. PVOID pParameter;
  770. PWORK_QUEUE_ITEM pWorkQueueItem;
  771. PRX_WORK_QUEUE pWorkQueue;
  772. LONG ItemInUse;
  773. pListEntry = RemoveHeadList(&SpinUpRequests);
  774. pWorkQueueItem = (PWORK_QUEUE_ITEM)
  775. CONTAINING_RECORD(
  776. pListEntry,
  777. WORK_QUEUE_ITEM,
  778. List);
  779. Routine = pWorkQueueItem->WorkerRoutine;
  780. pParameter = pWorkQueueItem->Parameter;
  781. pWorkQueue = (PRX_WORK_QUEUE)pParameter;
  782. ItemInUse = InterlockedDecrement(&pWorkQueue->WorkQueueItemForSpinUpWorkerThreadInUse);
  783. RxLog(("WORKQ:SR %lx %lx\n", Routine, pParameter ));
  784. RxWmiLog(LOG,
  785. RxSpinUpRequestsDispatcher,
  786. LOGPTR(Routine)
  787. LOGPTR(pParameter));
  788. Routine(pParameter);
  789. }
  790. if (State != RxDispatcherActive) {
  791. KeSetEvent(
  792. &pDispatcher->SpinUpRequestsTearDownEvent,
  793. IO_NO_INCREMENT,
  794. FALSE);
  795. break;
  796. }
  797. }
  798. }
  799. PsTerminateSystemThread(STATUS_SUCCESS);
  800. }
  801. VOID
  802. RxSpinUpWorkerThreads(
  803. PRX_WORK_QUEUE pWorkQueue)
  804. /*++
  805. Routine Description:
  806. This routine spins up one or more worker thread associated with the given queue.
  807. Arguments:
  808. pWorkQueue - the WorkQueue instance.
  809. Return Value:
  810. STATUS_SUCCESS if successful,
  811. otherwise appropriate error code
  812. --*/
  813. {
  814. NTSTATUS Status = STATUS_SUCCESS;
  815. HANDLE ThreadHandle;
  816. LONG NumberOfThreads;
  817. KIRQL SavedIrql;
  818. LONG ItemInUse;
  819. if ((IoGetCurrentProcess() != RxDispatcher.OwnerProcess) ||
  820. (KeGetCurrentIrql() != PASSIVE_LEVEL)) {
  821. ItemInUse = InterlockedIncrement(&pWorkQueue->WorkQueueItemForSpinUpWorkerThreadInUse);
  822. if (ItemInUse > 1) {
  823. // A work queue item is already on the SpinUpRequests waiting to be processed.
  824. // No need to post another one.
  825. InterlockedDecrement(&pWorkQueue->WorkQueueItemForSpinUpWorkerThreadInUse);
  826. return;
  827. }
  828. InterlockedIncrement(
  829. &RxFileSystemDeviceObject->DispatcherContext.NumberOfWorkerThreads);
  830. ExInitializeWorkItem(
  831. (PWORK_QUEUE_ITEM)&pWorkQueue->WorkQueueItemForSpinUpWorkerThread,
  832. RxpSpinUpWorkerThreads,
  833. pWorkQueue);
  834. KeAcquireSpinLock(&RxDispatcher.SpinUpRequestsLock, &SavedIrql);
  835. InsertTailList(
  836. &RxDispatcher.SpinUpRequests,
  837. &pWorkQueue->WorkQueueItemForSpinUpWorkerThread.List);
  838. KeSetEvent(
  839. &RxDispatcher.SpinUpRequestsEvent,
  840. IO_NO_INCREMENT,
  841. FALSE);
  842. KeReleaseSpinLock(&RxDispatcher.SpinUpRequestsLock,SavedIrql);
  843. } else {
  844. // Decide on the number of worker threads that need to be spun up.
  845. KeAcquireSpinLock(&pWorkQueue->SpinLock, &SavedIrql);
  846. if( pWorkQueue->State != RxWorkQueueRundownInProgress )
  847. {
  848. NumberOfThreads = pWorkQueue->MaximumNumberOfWorkerThreads -
  849. pWorkQueue->NumberOfActiveWorkerThreads;
  850. if (NumberOfThreads > pWorkQueue->NumberOfWorkItemsToBeDispatched) {
  851. NumberOfThreads = pWorkQueue->NumberOfWorkItemsToBeDispatched;
  852. }
  853. }
  854. else
  855. {
  856. // We're running down, so don't increment
  857. NumberOfThreads = 0;
  858. //DbgPrint( "[dkruse] Preventing rundown!\n" );
  859. }
  860. pWorkQueue->SpinUpRequestPending = FALSE;
  861. KeReleaseSpinLock(&pWorkQueue->SpinLock, SavedIrql);
  862. while (NumberOfThreads-- > 0) {
  863. Status = RxSpinUpWorkerThread(
  864. pWorkQueue,
  865. RxWorkerThreadDispatcher,
  866. pWorkQueue);
  867. if (Status != STATUS_SUCCESS) {
  868. break;
  869. }
  870. }
  871. if (Status != STATUS_SUCCESS) {
  872. ItemInUse = InterlockedIncrement(&pWorkQueue->WorkQueueItemForSpinUpWorkerThreadInUse);
  873. if (ItemInUse > 1) {
  874. // A work queue item is already on the SpinUpRequests waiting to be processed.
  875. // No need to post another one.
  876. InterlockedDecrement(&pWorkQueue->WorkQueueItemForSpinUpWorkerThreadInUse);
  877. return;
  878. }
  879. ExInitializeWorkItem(
  880. (PWORK_QUEUE_ITEM)&pWorkQueue->WorkQueueItemForSpinUpWorkerThread,
  881. RxpSpinUpWorkerThreads,
  882. pWorkQueue);
  883. KeAcquireSpinLock(&pWorkQueue->SpinLock, &SavedIrql);
  884. pWorkQueue->SpinUpRequestPending = TRUE;
  885. KeReleaseSpinLock(&pWorkQueue->SpinLock, SavedIrql);
  886. KeAcquireSpinLock(&RxDispatcher.SpinUpRequestsLock, &SavedIrql);
  887. // An attempt to spin up a worker thread failed. Reschedule the
  888. // requests to attempt this operation later.
  889. InterlockedIncrement(
  890. &RxFileSystemDeviceObject->DispatcherContext.NumberOfWorkerThreads);
  891. InsertTailList(
  892. &RxDispatcher.SpinUpRequests,
  893. &pWorkQueue->WorkQueueItemForSpinUpWorkerThread.List);
  894. KeReleaseSpinLock(&RxDispatcher.SpinUpRequestsLock,SavedIrql);
  895. }
  896. }
  897. }
  898. VOID
  899. RxSpinDownWorkerThreads(
  900. PRX_WORK_QUEUE pWorkQueue)
  901. /*++
  902. Routine Description:
  903. This routine spins down one or more worker thread associated with the given queue.
  904. Arguments:
  905. pWorkQueue - the WorkQueue instance.
  906. --*/
  907. {
  908. KIRQL SavedIrql;
  909. BOOLEAN RepostSpinDownRequest = FALSE;
  910. // Decide on the number of worker threads that need to be spun up.
  911. KeAcquireSpinLock(&pWorkQueue->SpinLock, &SavedIrql);
  912. if (pWorkQueue->NumberOfActiveWorkerThreads > 1) {
  913. RepostSpinDownRequest = TRUE;
  914. }
  915. KeReleaseSpinLock(&pWorkQueue->SpinLock, SavedIrql);
  916. if (RepostSpinDownRequest) {
  917. if (pWorkQueue->WorkQueueItemForSpinDownWorkerThread.pDeviceObject == NULL) {
  918. pWorkQueue->WorkQueueItemForSpinDownWorkerThread.pDeviceObject = RxFileSystemDeviceObject;
  919. }
  920. ExInitializeWorkItem(&pWorkQueue->WorkQueueItemForSpinDownWorkerThread,RxSpinDownWorkerThreads,pWorkQueue);
  921. KeInsertQueue(&pWorkQueue->Queue,&pWorkQueue->WorkQueueItemForSpinDownWorkerThread.List);
  922. }
  923. }
  924. BOOLEAN DumpDispatchRoutine = FALSE;
  925. VOID
  926. RxpWorkerThreadDispatcher(
  927. IN PRX_WORK_QUEUE pWorkQueue,
  928. IN PLARGE_INTEGER pWaitInterval)
  929. /*++
  930. Routine Description:
  931. This routine dispatches a work item and frees the associated work item
  932. Arguments:
  933. pWorkQueue - the WorkQueue instance.
  934. pWaitInterval - the interval for waiting on the KQUEUE.
  935. --*/
  936. {
  937. NTSTATUS Status;
  938. PLIST_ENTRY pListEntry;
  939. PRX_WORK_QUEUE_ITEM pWorkQueueItem;
  940. PRX_WORKERTHREAD_ROUTINE Routine;
  941. PVOID pParameter;
  942. BOOLEAN SpindownThread,DereferenceThread;
  943. KIRQL SavedIrql;
  944. PETHREAD ThisThread;
  945. RxDbgTrace(0,Dbg,("+++++ Worker Thread Startup %lx\n",PsGetCurrentThread()));
  946. InterlockedIncrement(&pWorkQueue->NumberOfIdleWorkerThreads);
  947. ThisThread = PsGetCurrentThread();
  948. Status = ObReferenceObjectByPointer(
  949. ThisThread,
  950. THREAD_ALL_ACCESS,
  951. *PsThreadType,
  952. KernelMode);
  953. ASSERT(Status == STATUS_SUCCESS);
  954. SpindownThread = FALSE;
  955. DereferenceThread = FALSE;
  956. for (;;) {
  957. pListEntry = KeRemoveQueue(
  958. &pWorkQueue->Queue,
  959. KernelMode,
  960. pWaitInterval);
  961. if ((NTSTATUS)(ULONG_PTR)pListEntry != STATUS_TIMEOUT) {
  962. LONG FinalRefCount;
  963. PRDBSS_DEVICE_OBJECT pMRxDeviceObject;
  964. InterlockedIncrement(&pWorkQueue->NumberOfWorkItemsDispatched);
  965. InterlockedDecrement(&pWorkQueue->NumberOfWorkItemsToBeDispatched);
  966. InterlockedDecrement(&pWorkQueue->NumberOfIdleWorkerThreads);
  967. InitializeListHead(pListEntry);
  968. pWorkQueueItem = (PRX_WORK_QUEUE_ITEM)
  969. CONTAINING_RECORD(
  970. pListEntry,
  971. RX_WORK_QUEUE_ITEM,
  972. List);
  973. pMRxDeviceObject = pWorkQueueItem->pDeviceObject;
  974. // This is a regular work item. Invoke the routine in the context of
  975. // a try catch block.
  976. Routine = pWorkQueueItem->WorkerRoutine;
  977. pParameter = pWorkQueueItem->Parameter;
  978. // Reset the fields in the Work item.
  979. ExInitializeWorkItem(pWorkQueueItem,NULL,NULL);
  980. pWorkQueueItem->pDeviceObject = NULL;
  981. RxDbgTrace(0, Dbg, ("RxWorkerThreadDispatcher Routine(%lx) Parameter(%lx)\n",Routine,pParameter));
  982. //RxLog(("WORKQ:Ex Dev(%lx) %lx %lx\n", pMRxDeviceObject,Routine, pParameter ));
  983. //RxWmiLog(LOG,
  984. // RxpWorkerThreadDispatcher,
  985. // LOGPTR(pMRxDeviceObject)
  986. // LOGPTR(Routine)
  987. // LOGPTR(pParameter));
  988. Routine(pParameter);
  989. FinalRefCount = InterlockedDecrement(&pMRxDeviceObject->DispatcherContext.NumberOfWorkerThreads);
  990. if (FinalRefCount == 0) {
  991. PKEVENT pTearDownEvent;
  992. pTearDownEvent = (PKEVENT)
  993. InterlockedExchangePointer(
  994. &pMRxDeviceObject->DispatcherContext.pTearDownEvent,
  995. NULL);
  996. if (pTearDownEvent != NULL) {
  997. KeSetEvent(
  998. pTearDownEvent,
  999. IO_NO_INCREMENT,
  1000. FALSE);
  1001. }
  1002. }
  1003. InterlockedIncrement(&pWorkQueue->NumberOfIdleWorkerThreads);
  1004. }
  1005. KeAcquireSpinLock(&pWorkQueue->SpinLock,&SavedIrql);
  1006. switch (pWorkQueue->State) {
  1007. case RxWorkQueueActive:
  1008. {
  1009. if (pWorkQueue->NumberOfWorkItemsToBeDispatched > 0) {
  1010. // Delay spinning down a worker thread till the existing work
  1011. // items have been dispatched.
  1012. break;
  1013. }
  1014. }
  1015. // lack of break intentional.
  1016. // Ensure that the number of idle threads is not more than the
  1017. // minimum number of worker threads permitted for the work queue
  1018. case RxWorkQueueInactive:
  1019. {
  1020. ASSERT(pWorkQueue->NumberOfActiveWorkerThreads > 0);
  1021. if (pWorkQueue->NumberOfActiveWorkerThreads >
  1022. pWorkQueue->MinimumNumberOfWorkerThreads) {
  1023. SpindownThread = TRUE;
  1024. DereferenceThread = TRUE;
  1025. InterlockedDecrement(&pWorkQueue->NumberOfActiveWorkerThreads);
  1026. }
  1027. }
  1028. break;
  1029. case RxWorkQueueRundownInProgress:
  1030. {
  1031. PRX_WORK_QUEUE_RUNDOWN_CONTEXT pRundownContext;
  1032. pRundownContext = pWorkQueue->pRundownContext;
  1033. // The work queue is no longer active. Spin down all the worker
  1034. // threads associated with the work queue.
  1035. ASSERT(pRundownContext != NULL);
  1036. pRundownContext->ThreadPointers[pRundownContext->NumberOfThreadsSpunDown++] = ThisThread;
  1037. InterlockedDecrement(&pWorkQueue->NumberOfActiveWorkerThreads);
  1038. SpindownThread = TRUE;
  1039. DereferenceThread = FALSE;
  1040. if (pWorkQueue->NumberOfActiveWorkerThreads == 0) {
  1041. KeSetEvent(
  1042. &pWorkQueue->pRundownContext->RundownCompletionEvent,
  1043. IO_NO_INCREMENT,
  1044. FALSE);
  1045. }
  1046. }
  1047. break;
  1048. default:
  1049. ASSERT(!"Valid State For Work Queue");
  1050. }
  1051. if (SpindownThread) {
  1052. InterlockedDecrement(&pWorkQueue->NumberOfIdleWorkerThreads);
  1053. }
  1054. KeReleaseSpinLock(&pWorkQueue->SpinLock,SavedIrql);
  1055. if (SpindownThread) {
  1056. RxDbgTrace(0,Dbg,("----- Worker Thread Exit %lx\n",PsGetCurrentThread()));
  1057. break;
  1058. }
  1059. }
  1060. if (DereferenceThread) {
  1061. ObDereferenceObject(ThisThread);
  1062. }
  1063. if (DumpDispatchRoutine) {
  1064. // just to keep them around on free build for debug purpose
  1065. DbgPrint("Dispatch routine %lx %lx %lx\n",Routine,pParameter,pWorkQueueItem);
  1066. }
  1067. PsTerminateSystemThread(STATUS_SUCCESS);
  1068. }
  1069. VOID
  1070. RxBootstrapWorkerThreadDispatcher(
  1071. PRX_WORK_QUEUE pWorkQueue)
  1072. /*++
  1073. Routine Description:
  1074. This routine is for worker threads that use a infinite time interval
  1075. for waiting on the KQUEUE data structure. These threads cannot be throtled
  1076. back and are used for ensuring that the bare minimum number of threads
  1077. are always active ( primarily startup purposes )
  1078. Arguments:
  1079. pWorkQueue - the WorkQueue instance.
  1080. --*/
  1081. {
  1082. PAGED_CODE();
  1083. RxpWorkerThreadDispatcher(pWorkQueue,NULL);
  1084. }
  1085. VOID
  1086. RxWorkerThreadDispatcher(
  1087. PRX_WORK_QUEUE pWorkQueue)
  1088. /*++
  1089. Routine Description:
  1090. This routine is for worker threads that use a finite time interval to wait
  1091. on the KQUEUE data structure. Such threads have a self regulatory mechanism
  1092. built in which causes them to spin down if the work load eases off. The
  1093. time interval is based on the type of the work queue
  1094. Arguments:
  1095. pWorkQueue - the WorkQueue instance.
  1096. --*/
  1097. {
  1098. PAGED_CODE();
  1099. RxpWorkerThreadDispatcher(
  1100. pWorkQueue,
  1101. &RxWorkQueueWaitInterval[pWorkQueue->Type]);
  1102. }
  1103. NTSTATUS
  1104. RxInsertWorkQueueItem(
  1105. PRDBSS_DEVICE_OBJECT pDeviceObject,
  1106. WORK_QUEUE_TYPE WorkQueueType,
  1107. PRX_WORK_QUEUE_ITEM pWorkQueueItem)
  1108. /*++
  1109. Routine Description:
  1110. This routine inserts a work item into the appropriate queue.
  1111. Arguments:
  1112. pDeviceObject - the device object
  1113. WorkQueueType - the type of work item
  1114. pWorkQueueItem - the work queue item
  1115. Return Value:
  1116. STATUS_SUCCESS -- successful
  1117. other status codes indicate error conditions
  1118. STATUS_INSUFFICIENT_RESOURCES -- could not dispatch
  1119. Notes:
  1120. This routine inserts the work item into the appropriate queue and spins
  1121. up a worker thread if required.
  1122. There are some extensions to this routine that needs to be implemented. These
  1123. have been delayed in order to get an idea of the costs and the benefits of
  1124. the various tradeoffs involved.
  1125. The current implementation follows a very simple logic in queueing work
  1126. from the various sources onto the same processor from which it originated.
  1127. The benefits associated with this approach are the prevention of cache/state
  1128. sloshing as the work is moved around from one processor to another. The
  1129. undesirable charecterstic is the skewing of work load on the various processors.
  1130. The important question that needs to be answered is when is it beneficial to
  1131. sacrifice the affinity to a processor. This depends upon the workload associated
  1132. with the current processor and the amount of information associated with the
  1133. given processor. The later is more difficult to determine.
  1134. --*/
  1135. {
  1136. NTSTATUS Status = STATUS_SUCCESS;
  1137. KIRQL SavedIrql;
  1138. BOOLEAN SpinUpWorkerThread = FALSE;
  1139. ULONG ProcessorNumber;
  1140. // If the dispatcher were on a per processor basis the ProcessorNumber
  1141. // would be indx for accessing the dispatcher data structure
  1142. // ProcessorNumber = KeGetCurrentProcessorNumber();
  1143. PRX_WORK_QUEUE_DISPATCHER pWorkQueueDispatcher;
  1144. PRX_WORK_QUEUE pWorkQueue;
  1145. ProcessorNumber = 0;
  1146. pWorkQueueDispatcher = &RxDispatcher.pWorkQueueDispatcher[ProcessorNumber];
  1147. pWorkQueue = &pWorkQueueDispatcher->WorkQueue[WorkQueueType];
  1148. if (RxDispatcher.State != RxDispatcherActive)
  1149. {
  1150. return STATUS_UNSUCCESSFUL;
  1151. }
  1152. KeAcquireSpinLock(&pWorkQueue->SpinLock, &SavedIrql);
  1153. if ((pWorkQueue->State == RxWorkQueueActive) &&
  1154. (pDeviceObject->DispatcherContext.pTearDownEvent == NULL)) {
  1155. pWorkQueueItem->pDeviceObject = pDeviceObject;
  1156. InterlockedIncrement(&pDeviceObject->DispatcherContext.NumberOfWorkerThreads);
  1157. pWorkQueue->CumulativeQueueLength += pWorkQueue->NumberOfWorkItemsToBeDispatched;
  1158. InterlockedIncrement(&pWorkQueue->NumberOfWorkItemsToBeDispatched);
  1159. if ((pWorkQueue->NumberOfIdleWorkerThreads < pWorkQueue->NumberOfWorkItemsToBeDispatched) &&
  1160. (pWorkQueue->NumberOfActiveWorkerThreads < pWorkQueue->MaximumNumberOfWorkerThreads) &&
  1161. (!pWorkQueue->SpinUpRequestPending)) {
  1162. pWorkQueue->SpinUpRequestPending = TRUE;
  1163. SpinUpWorkerThread = TRUE;
  1164. }
  1165. } else {
  1166. Status = STATUS_UNSUCCESSFUL;
  1167. }
  1168. KeReleaseSpinLock(&pWorkQueue->SpinLock, SavedIrql);
  1169. if (Status == STATUS_SUCCESS) {
  1170. KeInsertQueue(&pWorkQueue->Queue,&pWorkQueueItem->List);
  1171. if (SpinUpWorkerThread) {
  1172. RxSpinUpWorkerThreads(
  1173. pWorkQueue);
  1174. }
  1175. } else {
  1176. RxWmiLogError(Status,
  1177. LOG,
  1178. RxInsertWorkQueueItem,
  1179. LOGPTR(pDeviceObject)
  1180. LOGULONG(WorkQueueType)
  1181. LOGPTR(pWorkQueueItem)
  1182. LOGUSTR(pDeviceObject->DeviceName));
  1183. }
  1184. return Status;
  1185. }
  1186. VOID
  1187. RxWorkItemDispatcher(
  1188. PVOID pContext)
  1189. /*++
  1190. Routine Description:
  1191. This routine serves as a wrapper for dispatching a work item and for
  1192. performing the related cleanup actions
  1193. Arguments:
  1194. pContext - the Context parameter that is passed to the driver routine.
  1195. Notes:
  1196. There are two cases of dispatching to worker threads. When an instance is going to
  1197. be repeatedly dispatched time is conserved by allocating the WORK_QUEUE_ITEM as
  1198. part of the data structure to be dispatched. On the other hand if it is a very
  1199. infrequent operation space can be conserved by dynamically allocating and freeing
  1200. memory for the work queue item. This tradesoff time for space.
  1201. This routine implements a wrapper for those instances in which time was traded
  1202. off for space. It invokes the desired routine and frees the memory.
  1203. --*/
  1204. {
  1205. PRX_WORK_DISPATCH_ITEM pDispatchItem;
  1206. PRX_WORKERTHREAD_ROUTINE Routine;
  1207. PVOID Parameter;
  1208. pDispatchItem = (PRX_WORK_DISPATCH_ITEM)pContext;
  1209. Routine = pDispatchItem->DispatchRoutine;
  1210. Parameter = pDispatchItem->DispatchRoutineParameter;
  1211. //RxLog(("WORKQ:Ds %lx %lx\n", Routine, Parameter ));
  1212. //RxWmiLog(LOG,
  1213. // RxWorkItemDispatcher,
  1214. // LOGPTR(Routine)
  1215. // LOGPTR(Parameter));
  1216. Routine(Parameter);
  1217. RxFreePool(pDispatchItem);
  1218. }
  1219. NTSTATUS
  1220. RxDispatchToWorkerThread(
  1221. IN OUT PRDBSS_DEVICE_OBJECT pMRxDeviceObject,
  1222. IN WORK_QUEUE_TYPE WorkQueueType,
  1223. IN PRX_WORKERTHREAD_ROUTINE Routine,
  1224. IN PVOID pContext)
  1225. /*++
  1226. Routine Description:
  1227. This routine invokes the routine in the context of a worker thread.
  1228. Arguments:
  1229. pMRxDeviceObject - the device object of the corresponding mini redirector
  1230. WorkQueueType - the type of the work queue
  1231. Routine - routine to be invoked
  1232. pContext - the Context parameter that is passed to the driver routine.
  1233. Return Value:
  1234. STATUS_SUCCESS -- successful
  1235. STATUS_INSUFFICIENT_RESOURCES -- could not dispatch
  1236. Notes:
  1237. There are two cases of dispatching to worker threads. When an instance is going to
  1238. be repeatedly dispatched time is conserved by allocating the WORK_QUEUE_ITEM as
  1239. part of the data structure to be dispatched. On the other hand if it is a very
  1240. infrequent operation space can be conserved by dynamically allocating and freeing
  1241. memory for the work queue item. This tradesoff time for space.
  1242. --*/
  1243. {
  1244. NTSTATUS Status;
  1245. PRX_WORK_DISPATCH_ITEM pDispatchItem;
  1246. KIRQL SavedIrql;
  1247. pDispatchItem = RxAllocatePoolWithTag(
  1248. NonPagedPool,
  1249. sizeof(RX_WORK_DISPATCH_ITEM),
  1250. RX_WORKQ_POOLTAG);
  1251. if (pDispatchItem != NULL) {
  1252. pDispatchItem->DispatchRoutine = Routine;
  1253. pDispatchItem->DispatchRoutineParameter = pContext;
  1254. ExInitializeWorkItem(
  1255. &pDispatchItem->WorkQueueItem,
  1256. RxWorkItemDispatcher,
  1257. pDispatchItem);
  1258. Status = RxInsertWorkQueueItem(pMRxDeviceObject,WorkQueueType,&pDispatchItem->WorkQueueItem);
  1259. } else {
  1260. Status = STATUS_INSUFFICIENT_RESOURCES;
  1261. }
  1262. if (Status != STATUS_SUCCESS) {
  1263. if (pDispatchItem != NULL) {
  1264. RxFreePool(pDispatchItem);
  1265. }
  1266. RxLog(("WORKQ:Queue(D) %ld %lx %lx %lx\n", WorkQueueType,Routine,pContext,Status));
  1267. RxWmiLogError(Status,
  1268. LOG,
  1269. RxDispatchToWorkerThread,
  1270. LOGULONG(WorkQueueType)
  1271. LOGPTR(Routine)
  1272. LOGPTR(pContext)
  1273. LOGULONG(Status));
  1274. }
  1275. return Status;
  1276. }
  1277. NTSTATUS
  1278. RxPostToWorkerThread(
  1279. IN OUT PRDBSS_DEVICE_OBJECT pMRxDeviceObject,
  1280. IN WORK_QUEUE_TYPE WorkQueueType,
  1281. IN OUT PRX_WORK_QUEUE_ITEM pWorkQueueItem,
  1282. IN PRX_WORKERTHREAD_ROUTINE Routine,
  1283. IN PVOID pContext)
  1284. /*++
  1285. Routine Description:
  1286. This routine invokes the routine in the context of a worker thread.
  1287. Arguments:
  1288. WorkQueueType - the priority of the task at hand.
  1289. WorkQueueItem - the work queue item
  1290. Routine - routine to be invoked
  1291. pContext - the Context parameter that is passed to the driver routine.
  1292. Return Value:
  1293. STATUS_SUCCESS -- successful
  1294. STATUS_INSUFFICIENT_RESOURCES -- could not dispatch
  1295. Notes:
  1296. There are two cases of dispatching to worker threads. When an instance is going to
  1297. be repeatedly dispatched time is conserved by allocating the WORK_QUEUE_ITEM as
  1298. part of the data structure to be dispatched. On the other hand if it is a very
  1299. infrequent operation space can be conserved by dynamically allocating and freeing
  1300. memory for the work queue item. This tradesoff time for space.
  1301. --*/
  1302. {
  1303. NTSTATUS Status;
  1304. ExInitializeWorkItem( pWorkQueueItem,Routine,pContext );
  1305. Status = RxInsertWorkQueueItem(pMRxDeviceObject,WorkQueueType,pWorkQueueItem);
  1306. if (Status != STATUS_SUCCESS) {
  1307. RxLog(("WORKQ:Queue(P) %ld %lx %lx %lx\n", WorkQueueType,Routine,pContext,Status));
  1308. RxWmiLogError(Status,
  1309. LOG,
  1310. RxPostToWorkerThread,
  1311. LOGULONG(WorkQueueType)
  1312. LOGPTR(Routine)
  1313. LOGPTR(pContext)
  1314. LOGULONG(Status));
  1315. }
  1316. return Status;
  1317. }
  1318. PEPROCESS
  1319. RxGetRDBSSProcess()
  1320. {
  1321. return RxData.OurProcess;
  1322. }