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.

1312 lines
36 KiB

  1. /*++
  2. Copyright (c) 1989 Microsoft Corporation
  3. Module Name:
  4. blkwork.c
  5. Abstract:
  6. This module implements routines for managing work context blocks.
  7. Author:
  8. Chuck Lenzmeier (chuckl) 4-Oct-1989
  9. David Treadwell (davidtr)
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #include "blkwork.tmh"
  14. #pragma hdrstop
  15. #define BugCheckFileId SRV_FILE_BLKWORK
  16. #define FREE_EXTRA_SMB_BUFFER( _wc ) { \
  17. ASSERT( (_wc)->UsingExtraSmbBuffer ); \
  18. ASSERT( (_wc)->ResponseBuffer != NULL ); \
  19. DEALLOCATE_NONPAGED_POOL( (_wc)->ResponseBuffer ); \
  20. DEBUG (_wc)->ResponseBuffer = NULL; \
  21. DEBUG (_wc)->ResponseHeader = NULL; \
  22. DEBUG (_wc)->ResponseParameters = NULL; \
  23. (_wc)->UsingExtraSmbBuffer = FALSE; \
  24. }
  25. //
  26. // Local functions.
  27. //
  28. #define TransportHeaderSize 80
  29. PWORK_CONTEXT
  30. InitializeWorkItem (
  31. IN PVOID WorkItem,
  32. IN UCHAR BlockType,
  33. IN CLONG TotalSize,
  34. IN CLONG IrpSize,
  35. IN CCHAR IrpStackSize,
  36. IN CLONG MdlSize,
  37. IN CLONG BufferSize,
  38. IN PVOID Buffer
  39. );
  40. #ifdef ALLOC_PRAGMA
  41. #pragma alloc_text( PAGE, SrvAllocateInitialWorkItems )
  42. #pragma alloc_text( PAGE, SrvAllocateRawModeWorkItem )
  43. #pragma alloc_text( PAGE, SrvFreeInitialWorkItems )
  44. #pragma alloc_text( PAGE, SrvFreeNormalWorkItem )
  45. #pragma alloc_text( PAGE, SrvFreeRawModeWorkItem )
  46. //#pragma alloc_text( PAGE, SrvDereferenceWorkItem )
  47. #pragma alloc_text( PAGE, SrvAllocateExtraSmbBuffer )
  48. #pragma alloc_text( PAGE, SrvGetRawModeWorkItem )
  49. #pragma alloc_text( PAGE, SrvRequeueRawModeWorkItem )
  50. #endif
  51. #if 0
  52. NOT PAGEABLE -- SrvFsdDereferenceWorkItem
  53. #endif
  54. NTSTATUS
  55. SrvAllocateInitialWorkItems (
  56. VOID
  57. )
  58. /*++
  59. Routine Description:
  60. This routine allocates the initial set of normal server work items.
  61. It allocates one large block of memory to contain the entire set.
  62. The purpose of this single allocation is to eliminate the wasted
  63. space inherent in the allocation of a single work item. (Normal
  64. work items occupy about 5K bytes. Because of the way nonpaged pool
  65. is managed, allocating 5K actually uses 8K.)
  66. Each normal work item includes enough memory to hold the following:
  67. - work context block,
  68. - IRP,
  69. - buffer descriptor,
  70. - two MDLs, and
  71. - buffer for sends and receives
  72. This routine also queues each of the work items to the receive
  73. work item list.
  74. Arguments:
  75. None.
  76. Return Value:
  77. NTSTATUS - Returns STATUS_INSUFFICIENT_RESOURCES if unable to
  78. allocate nonpaged pool; STATUS_SUCCESS otherwise.
  79. --*/
  80. {
  81. CLONG totalSize;
  82. CLONG workItemSize = 0;
  83. CLONG irpSize = SrvReceiveIrpSize;
  84. CLONG mdlSize = SrvMaxMdlSize;
  85. CLONG bufferSize = SrvReceiveBufferSize;
  86. ULONG cacheLineSize = SrvCacheLineSize;
  87. PVOID workItem;
  88. PVOID buffer;
  89. PWORK_CONTEXT workContext;
  90. CLONG i;
  91. PWORK_QUEUE queue;
  92. PAGED_CODE();
  93. //
  94. // If the initial set of work items is to be empty, don't do
  95. // anything.
  96. //
  97. // *** This will almost certainly never happen, but let's be
  98. // prepared just in case.
  99. //
  100. if ( SrvInitialReceiveWorkItemCount == 0 ) {
  101. return STATUS_SUCCESS;
  102. }
  103. while( SrvInitialWorkItemBlock == NULL && SrvInitialReceiveWorkItemCount != 0 ) {
  104. //
  105. // Find out the sizes of the IRP, the SMB buffer, and the MDLs. The
  106. // MDL size is "worst case" -- the actual MDL size may be smaller,
  107. // but this calculation ensures that the MDL will be large enough.
  108. //
  109. // *** Note that the space allocated for the SMB buffer must be made
  110. // large enough to allow the buffer to be aligned such that it
  111. // falls, alone, within a set of cache-line-sized blocks. This
  112. // allows I/O to be performed to or from the buffer without
  113. // concern for cache line tearing. (Note the assumption below
  114. // that the cache line size is a power of two.)
  115. //
  116. //
  117. // Determine how large a buffer is needed for a single work item,
  118. // not including the SMB buffer. Round this number to a quadword
  119. // boundary.
  120. //
  121. workItemSize = sizeof(WORK_CONTEXT) + irpSize + sizeof(BUFFER) +
  122. (mdlSize * 2);
  123. workItemSize = (workItemSize + (MEMORY_ALLOCATION_ALIGNMENT - 1)) & ~(MEMORY_ALLOCATION_ALIGNMENT - 1);
  124. //
  125. // Determine the total amount of space needed. The allocation
  126. // must be padded in order to allow the SMB buffers to be aligned
  127. // on cache line boundaries.
  128. //
  129. totalSize = (bufferSize + TransportHeaderSize + workItemSize) * SrvInitialReceiveWorkItemCount +
  130. cacheLineSize;
  131. IF_DEBUG(HEAP) {
  132. SrvPrint0( "SrvAllocateInitialWorkItems:\n" );
  133. SrvPrint1( " work item size = 0x%lx bytes\n", workItemSize );
  134. SrvPrint1( " buffer size = 0x%lx bytes\n", bufferSize );
  135. SrvPrint1( " Backfill size = 0x%lx bytes\n", TransportHeaderSize );
  136. SrvPrint1( " number of work items = %ld\n",
  137. SrvInitialReceiveWorkItemCount );
  138. SrvPrint1( " total allocation = 0x%lx bytes\n", totalSize );
  139. SrvPrint1( " wasted space = 0x%p bytes\n",
  140. (PVOID)(ROUND_TO_PAGES( totalSize ) - totalSize) );
  141. SrvPrint1( " amount saved over separate allocation = 0x%p bytes\n",
  142. (PVOID)(((ROUND_TO_PAGES( workItemSize ) +
  143. ROUND_TO_PAGES( bufferSize )) *
  144. SrvInitialReceiveWorkItemCount) -
  145. ROUND_TO_PAGES( totalSize )) );
  146. }
  147. //
  148. // Attempt to allocate from nonpaged pool.
  149. //
  150. SrvInitialWorkItemBlock = ALLOCATE_NONPAGED_POOL(
  151. totalSize,
  152. BlockTypeWorkContextInitial
  153. );
  154. if ( SrvInitialWorkItemBlock == NULL ) {
  155. INTERNAL_ERROR(
  156. ERROR_LEVEL_EXPECTED,
  157. "SrvAllocateInitialWorkItems: Unable to allocate %d bytes "
  158. "from nonpaged pool.",
  159. totalSize,
  160. NULL
  161. );
  162. //
  163. // Let's try reducing the count and give it another shot.
  164. //
  165. SrvInitialReceiveWorkItemCount /= 2;
  166. }
  167. }
  168. if( SrvInitialWorkItemBlock == 0 ) {
  169. return STATUS_INSUFFICIENT_RESOURCES;
  170. }
  171. //
  172. // Round the allocation to a cache line boundary, then reserve
  173. // space for SMB buffers and control structures.
  174. //
  175. buffer = (PVOID)(((ULONG_PTR)SrvInitialWorkItemBlock + cacheLineSize) &
  176. ~((LONG_PTR)cacheLineSize));
  177. workItem = (PCHAR)buffer + ((bufferSize + TransportHeaderSize) * SrvInitialReceiveWorkItemCount);
  178. //
  179. // Initialize the work items and update the count of work items in
  180. // the server.
  181. //
  182. // *** Note that the update is not synchronized -- that shouldn't be
  183. // necessary at this stage of server initialization.
  184. //
  185. queue = SrvWorkQueues;
  186. for ( i = 0; i < SrvInitialReceiveWorkItemCount; i++ ) {
  187. if (((PAGE_SIZE - 1) - BYTE_OFFSET(buffer)) < (TransportHeaderSize + sizeof(SMB_HEADER))) {
  188. buffer = (PCHAR)buffer + PAGE_SIZE - BYTE_OFFSET(buffer);
  189. i++;
  190. IF_DEBUG(HEAP) {
  191. SrvPrint2("buffer adjusted!! %p offset %x \n",buffer,BYTE_OFFSET(buffer));
  192. }
  193. }
  194. workContext = InitializeWorkItem(
  195. workItem,
  196. BlockTypeWorkContextInitial,
  197. workItemSize,
  198. irpSize,
  199. SrvReceiveIrpStackSize,
  200. mdlSize,
  201. bufferSize,
  202. buffer
  203. );
  204. workContext->PartOfInitialAllocation = TRUE;
  205. workContext->FreeList = &queue->InitialWorkItemList;
  206. workContext->CurrentWorkQueue = queue;
  207. if( ++queue == eSrvWorkQueues )
  208. queue = SrvWorkQueues;
  209. //
  210. // Setup the work item and queue it to the free list
  211. //
  212. SrvPrepareReceiveWorkItem( workContext, TRUE );
  213. buffer = (PCHAR)buffer + TransportHeaderSize + bufferSize;
  214. workItem = (PCHAR)workItem + workItemSize;
  215. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.WorkContextInfo.Allocations );
  216. }
  217. return STATUS_SUCCESS;
  218. } // SrvAllocateInitialWorkItems
  219. NTSTATUS
  220. SrvAllocateNormalWorkItem (
  221. OUT PWORK_CONTEXT *WorkContext,
  222. PWORK_QUEUE queue
  223. )
  224. /*++
  225. Routine Description:
  226. This routine allocates a normal server work item. It allocates
  227. enough memory to hold the following:
  228. - work context block,
  229. - IRP,
  230. - buffer descriptor,
  231. - two MDLs, and
  232. - buffer for sends and receives
  233. It then initializes each of these blocks in the buffer.
  234. If the number of normal work items in the server is already at the
  235. configured maximum, this routine refuses to create a new one.
  236. Arguments:
  237. WorkContext - Returns a pointer to the Work Context Block, or NULL
  238. if the limit has been reached or if no space is available. The
  239. work context block has pointers to the other blocks.
  240. Return Value:
  241. None.
  242. --*/
  243. {
  244. CLONG totalSize;
  245. CLONG workItemSize;
  246. CLONG irpSize = SrvReceiveIrpSize;
  247. CLONG mdlSize = SrvMaxMdlSize;
  248. CLONG bufferSize = SrvReceiveBufferSize;
  249. CLONG cacheLineSize = SrvCacheLineSize;
  250. PVOID workItem;
  251. PVOID buffer;
  252. CLONG oldWorkItemCount;
  253. //
  254. // If we're already at the limit of how many work items we can
  255. // have, don't create another one.
  256. //
  257. // *** Note that the method used below leaves a small window in
  258. // which we may refuse to create a work item when we're not
  259. // really at the limit -- we increment the value, another thread
  260. // frees a work item and decrements the value, yet another
  261. // thread tests to see whether it can create a new work item.
  262. // Both testing threads will refuse to create a new work item,
  263. // even though the final number of work items is one less than
  264. // the maximum.
  265. //
  266. if ( queue->AllocatedWorkItems >= queue->MaximumWorkItems ) {
  267. //
  268. // Can't create any more work items just now.
  269. //
  270. IF_DEBUG(ERRORS) {
  271. SrvPrint0( "SrvAllocateNormalWorkItem: Work item limit reached\n" );
  272. }
  273. *WorkContext = NULL;
  274. return STATUS_INSUFF_SERVER_RESOURCES;
  275. }
  276. InterlockedIncrement( &queue->AllocatedWorkItems );
  277. //
  278. // Find out the sizes of the IRP, the SMB buffer, and the MDLs. The
  279. // MDL size is "worst case" -- the actual MDL size may be smaller,
  280. // but this calculation ensures that the MDL will be large enough.
  281. //
  282. // *** Note that the space allocated for the SMB buffer must be made
  283. // large enough to allow the buffer to be aligned such that it
  284. // falls, alone, within a set of cache-line-sized blocks. This
  285. // allows I/O to be performed to or from the buffer without
  286. // concern for cache line tearing. (Note the assumption below
  287. // that the cache line size is a power of two.)
  288. //
  289. //
  290. // Determine how large a buffer is needed for the SMB buffer and
  291. // control structures. The allocation must be padded in order to
  292. // allow the SMB buffer to be aligned on a cache line boundary.
  293. //
  294. workItemSize = sizeof(WORK_CONTEXT) + irpSize + sizeof(BUFFER) +
  295. (mdlSize * 2);
  296. totalSize = workItemSize + bufferSize + TransportHeaderSize+ cacheLineSize;
  297. //
  298. // Attempt to allocate from nonpaged pool.
  299. //
  300. workItem = ALLOCATE_NONPAGED_POOL( totalSize, BlockTypeWorkContextNormal );
  301. if ( workItem == NULL ) {
  302. INTERNAL_ERROR(
  303. ERROR_LEVEL_EXPECTED,
  304. "SrvAllocateNormalWorkItem: Unable to allocate %d bytes "
  305. "from nonpaged pool.",
  306. totalSize,
  307. NULL
  308. );
  309. InterlockedDecrement( &queue->AllocatedWorkItems );
  310. *WorkContext = NULL;
  311. return STATUS_INSUFFICIENT_RESOURCES;
  312. }
  313. //
  314. // Reserve space for the SMB buffer on a cache line boundary.
  315. //
  316. buffer = (PVOID)(((ULONG_PTR)workItem + workItemSize + cacheLineSize) &
  317. ~((LONG_PTR)cacheLineSize));
  318. if (((PAGE_SIZE - 1) - BYTE_OFFSET(buffer)) < (TransportHeaderSize + sizeof(SMB_HEADER))) {
  319. INTERNAL_ERROR(
  320. ERROR_LEVEL_EXPECTED,
  321. "SrvAllocateNormalWorkItem: Unable to allocate header with in a page ",
  322. totalSize,
  323. NULL
  324. );
  325. InterlockedDecrement( &queue->AllocatedWorkItems );
  326. DEALLOCATE_NONPAGED_POOL( workItem );
  327. *WorkContext = NULL;
  328. return STATUS_INSUFFICIENT_RESOURCES;
  329. }
  330. //
  331. // Initialize the work item and increment the count of work items in
  332. // the server.
  333. //
  334. *WorkContext = InitializeWorkItem(
  335. workItem,
  336. BlockTypeWorkContextNormal,
  337. workItemSize,
  338. irpSize,
  339. SrvReceiveIrpStackSize,
  340. mdlSize,
  341. bufferSize,
  342. buffer
  343. );
  344. (*WorkContext)->PartOfInitialAllocation = FALSE;
  345. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.WorkContextInfo.Allocations );
  346. (*WorkContext)->FreeList = &queue->NormalWorkItemList;
  347. (*WorkContext)->CurrentWorkQueue = queue;
  348. return STATUS_SUCCESS;
  349. } // SrvAllocateNormalWorkItem
  350. VOID
  351. SrvAllocateRawModeWorkItem (
  352. OUT PWORK_CONTEXT *WorkContext,
  353. IN PWORK_QUEUE queue
  354. )
  355. /*++
  356. Routine Description:
  357. This routine allocates a raw mode work item. It allocates enough
  358. memory to hold the following:
  359. - work context block,
  360. - IRP,
  361. - buffer descriptor, and
  362. - one MDL
  363. It then initializes each of these blocks in the buffer.
  364. If the number of raw mode work items in the server is already at the
  365. configured maximum, this routine refuses to create a new one.
  366. Arguments:
  367. WorkContext - Returns a pointer to the Work Context Block, or NULL
  368. if no space was available. The work context block has pointers
  369. to the other blocks.
  370. Return Value:
  371. None.
  372. --*/
  373. {
  374. CLONG workItemSize;
  375. CLONG irpSize = SrvReceiveIrpSize;
  376. CLONG mdlSize = SrvMaxMdlSize;
  377. PVOID workItem;
  378. CLONG oldWorkItemCount;
  379. PAGED_CODE( );
  380. //
  381. // If we're already at the limit of how many work items we can
  382. // have, don't create another one.
  383. //
  384. // *** Note that the method used below leaves a small window in
  385. // which we may refuse to create a work item when we're not
  386. // really at the limit -- we increment the value, another thread
  387. // frees a work item and decrements the value, yet another
  388. // thread tests to see whether it can create a new work item.
  389. // Both testing threads will refuse to create a new work item,
  390. // even though the final number of work items is one less than
  391. // the maximum.
  392. //
  393. if ( (ULONG)queue->AllocatedRawModeWorkItems >=
  394. SrvMaxRawModeWorkItemCount / SrvNumberOfProcessors ) {
  395. //
  396. // Can't create any more work items just now.
  397. //
  398. // !!! This should be logged somehow, but we don't want to
  399. // breakpoint the server when it happens.
  400. //
  401. IF_DEBUG(ERRORS) {
  402. SrvPrint0( "SrvAllocateRawModeWorkItem: Work item limit reached\n" );
  403. }
  404. *WorkContext = NULL;
  405. return;
  406. }
  407. InterlockedIncrement( &queue->AllocatedRawModeWorkItems );
  408. //
  409. // Find out the sizes of the IRP and the MDL. The MDL size is
  410. // "worst case" -- the actual MDL size may be smaller, but this
  411. // calculation ensures that the MDL will be large enough.
  412. //
  413. workItemSize = sizeof(WORK_CONTEXT) + sizeof(BUFFER) + irpSize + mdlSize;
  414. //
  415. // Attempt to allocate from nonpaged pool.
  416. //
  417. workItem = ALLOCATE_NONPAGED_POOL( workItemSize, BlockTypeWorkContextRaw );
  418. if ( workItem == NULL ) {
  419. INTERNAL_ERROR(
  420. ERROR_LEVEL_EXPECTED,
  421. "SrvAllocateRawModeWorkItem: Unable to allocate %d bytes "
  422. "from nonpaged pool.",
  423. workItemSize,
  424. NULL
  425. );
  426. InterlockedDecrement( &queue->AllocatedRawModeWorkItems );
  427. *WorkContext = NULL;
  428. return;
  429. }
  430. //
  431. // Initialize the work item and increment the count of work items in
  432. // the server.
  433. //
  434. *WorkContext = InitializeWorkItem(
  435. workItem,
  436. BlockTypeWorkContextRaw,
  437. workItemSize,
  438. irpSize,
  439. SrvReceiveIrpStackSize,
  440. mdlSize,
  441. 0,
  442. NULL
  443. );
  444. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.WorkContextInfo.Allocations );
  445. (*WorkContext)->FreeList = &queue->RawModeWorkItemList;
  446. (*WorkContext)->CurrentWorkQueue = queue;
  447. } // SrvAllocateRawModeWorkItem
  448. PWORK_CONTEXT
  449. SrvGetRawModeWorkItem ()
  450. {
  451. PSLIST_ENTRY listEntry;
  452. PWORK_CONTEXT workContext;
  453. PWORK_QUEUE queue = PROCESSOR_TO_QUEUE();
  454. PAGED_CODE();
  455. //
  456. // Attempt to allocate a raw mode work item off the current processor's queue
  457. //
  458. listEntry = ExInterlockedPopEntrySList( &queue->RawModeWorkItemList, &queue->SpinLock );
  459. if( listEntry != NULL ) {
  460. workContext = CONTAINING_RECORD( listEntry, WORK_CONTEXT, SingleListEntry );
  461. InterlockedDecrement( &queue->FreeRawModeWorkItems );
  462. ASSERT( queue->FreeRawModeWorkItems >= 0 );
  463. } else {
  464. SrvAllocateRawModeWorkItem( &workContext, queue );
  465. }
  466. if( workContext != NULL || SrvNumberOfProcessors == 1 ) {
  467. return workContext;
  468. }
  469. //
  470. // We were unable to get or allocate a raw mode workitem off the current
  471. // work queue. We're a multiprocessor system, so look around for one off
  472. // of a different work queue.
  473. //
  474. for( queue = SrvWorkQueues; queue < eSrvWorkQueues; queue++ ) {
  475. listEntry = ExInterlockedPopEntrySList( &queue->RawModeWorkItemList, &queue->SpinLock );
  476. if ( listEntry != NULL ) {
  477. InterlockedDecrement( &queue->FreeRawModeWorkItems );
  478. ASSERT( queue->FreeRawModeWorkItems >= 0 );
  479. workContext = CONTAINING_RECORD( listEntry, WORK_CONTEXT, SingleListEntry );
  480. return workContext;
  481. }
  482. }
  483. //
  484. // We were unable to get a free raw mode workitem off a different processor's
  485. // raw work item queue. See if any of the queues allow allocation of a new one.
  486. //
  487. for( queue = SrvWorkQueues; queue < eSrvWorkQueues; queue++ ) {
  488. SrvAllocateRawModeWorkItem( &workContext, queue );
  489. if( workContext != NULL ) {
  490. break;
  491. }
  492. }
  493. return workContext;
  494. } // SrvGetRawModeWorkItem
  495. VOID
  496. SrvRequeueRawModeWorkItem (
  497. PWORK_CONTEXT WorkContext
  498. )
  499. {
  500. PWORK_QUEUE queue = CONTAINING_RECORD( WorkContext->FreeList,
  501. WORK_QUEUE, RawModeWorkItemList );
  502. PAGED_CODE();
  503. InterlockedIncrement( &queue->FreeRawModeWorkItems );
  504. ExInterlockedPushEntrySList( &queue->RawModeWorkItemList,
  505. &WorkContext->SingleListEntry,
  506. &queue->SpinLock
  507. );
  508. return;
  509. } // SrvRequeueRawModeWorkItem
  510. VOID
  511. SrvFreeInitialWorkItems (
  512. VOID
  513. )
  514. /*++
  515. Routine Description:
  516. This function deallocates the large block of work items allocated
  517. at server startup.
  518. Arguments:
  519. None.
  520. Return Value:
  521. None.
  522. --*/
  523. {
  524. PAGED_CODE( );
  525. if ( SrvInitialWorkItemBlock != NULL ) {
  526. IF_DEBUG(BLOCK1) {
  527. SrvPrint1( "Releasing initial work item block at 0x%p\n",
  528. SrvInitialWorkItemBlock );
  529. }
  530. DEALLOCATE_NONPAGED_POOL( SrvInitialWorkItemBlock );
  531. IF_DEBUG(HEAP) {
  532. SrvPrint1( "SrvFreeInitialWorkItems: Freed initial work item block at 0x%p\n", SrvInitialWorkItemBlock );
  533. }
  534. SrvInitialWorkItemBlock = NULL;
  535. }
  536. return;
  537. } // SrvFreeInitialWorkItems
  538. VOID
  539. SrvFreeNormalWorkItem (
  540. IN PWORK_CONTEXT WorkContext
  541. )
  542. /*++
  543. Routine Description:
  544. This function deallocates a work item block.
  545. Arguments:
  546. WorkContext - Address of Work Context block that heads up the work
  547. item.
  548. Return Value:
  549. None.
  550. --*/
  551. {
  552. PWORK_QUEUE queue = WorkContext->CurrentWorkQueue;
  553. PAGED_CODE( );
  554. IF_DEBUG(BLOCK1) {
  555. SrvPrint1( "Closing work item at 0x%p\n", WorkContext );
  556. }
  557. ASSERT( GET_BLOCK_STATE( WorkContext ) == BlockStateActive );
  558. ASSERT( !WorkContext->PartOfInitialAllocation );
  559. //
  560. // Free the work item block itself.
  561. //
  562. DEBUG SET_BLOCK_TYPE_STATE_SIZE( WorkContext, BlockTypeGarbage, BlockStateDead, -1 );
  563. DEBUG WorkContext->BlockHeader.ReferenceCount = (ULONG)-1;
  564. DEALLOCATE_NONPAGED_POOL( WorkContext );
  565. IF_DEBUG(HEAP) {
  566. SrvPrint1( "SrvFreeNormalWorkItem: Freed Work Item block at 0x%p\n",
  567. WorkContext );
  568. }
  569. //
  570. // Update the count of work items in the server.
  571. //
  572. InterlockedDecrement( &queue->AllocatedWorkItems );
  573. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.WorkContextInfo.Frees );
  574. return;
  575. } // SrvFreeNormalWorkItem
  576. VOID
  577. SrvFreeRawModeWorkItem (
  578. IN PWORK_CONTEXT WorkContext
  579. )
  580. /*++
  581. Routine Description:
  582. This function deallocates a raw mode work item block.
  583. Arguments:
  584. WorkContext - Address of Work Context block that heads up the work
  585. item.
  586. Return Value:
  587. None.
  588. --*/
  589. {
  590. PWORK_QUEUE queue = CONTAINING_RECORD( WorkContext->FreeList,
  591. WORK_QUEUE, RawModeWorkItemList );
  592. PAGED_CODE( );
  593. IF_DEBUG(BLOCK1) {
  594. SrvPrint1( "Closing workitem at 0x%p\n", WorkContext );
  595. }
  596. ASSERT( GET_BLOCK_STATE( WorkContext ) == BlockStateActive );
  597. ASSERT( !WorkContext->PartOfInitialAllocation );
  598. //
  599. // Free the work item block itself.
  600. //
  601. DEBUG SET_BLOCK_TYPE_STATE_SIZE( WorkContext, BlockTypeGarbage, BlockStateDead, -1 );
  602. DEBUG WorkContext->BlockHeader.ReferenceCount = (ULONG)-1;
  603. DEALLOCATE_NONPAGED_POOL( WorkContext );
  604. IF_DEBUG(HEAP) {
  605. SrvPrint1( "SrvFreeRawModeWorkItem: Freed Work Item block at 0x%p\n",
  606. WorkContext );
  607. }
  608. //
  609. // Update the count of work items in the server.
  610. //
  611. InterlockedDecrement( &queue->AllocatedRawModeWorkItems );
  612. ASSERT( queue->AllocatedRawModeWorkItems >= 0 );
  613. INCREMENT_DEBUG_STAT2( SrvDbgStatistics.WorkContextInfo.Frees );
  614. return;
  615. } // SrvFreeRawModeWorkItem
  616. PWORK_CONTEXT
  617. InitializeWorkItem (
  618. IN PVOID WorkItem,
  619. IN UCHAR BlockType,
  620. IN CLONG WorkItemSize,
  621. IN CLONG IrpSize,
  622. IN CCHAR IrpStackSize,
  623. IN CLONG MdlSize,
  624. IN CLONG BufferSize,
  625. IN PVOID Buffer
  626. )
  627. /*++
  628. Routine Description:
  629. This routine initializes the following components of a work item:
  630. - a work context block,
  631. - an IRP,
  632. - the CurrentWorkQueue
  633. - optionally, a buffer descriptor,
  634. - one or two MDLs, and
  635. - optionally, a buffer for sends and receives
  636. The storage for these components must have been allocated by the
  637. caller, in contiguous storage starting at WorkContext.
  638. Arguments:
  639. WorkItem - Supplies a pointer to the storage allocated to the
  640. work item.
  641. BlockType - The type of work item being initialized.
  642. WorkItemSize - Indicates the total amount of space allocated to the
  643. work item control structures (i.e., not including the data
  644. buffer, if any).
  645. IrpSize - Indicates the amount of space in the work item to be
  646. reserved for the IRP.
  647. IrpStackSize - Indicates the number of stack locations in the IRP.
  648. MdlSize - Indicates the amount of space in the work item to be
  649. reserved for each MDL. One MDL is created if Buffer is NULL;
  650. two are created if Buffer is not NULL.
  651. BufferSize - Indicates the amount of space allocated to be
  652. data buffer. This parameter is ignored if Buffer is NULL.
  653. Buffer - Supplies a pointer to a data buffer. NULL indicates that
  654. no data buffer was allocated. (This is used for raw mode work
  655. items.)
  656. Return Value:
  657. PWORK_CONTEXT - Returns a pointer to the work context block that
  658. forms the "root" of the work item.
  659. --*/
  660. {
  661. PVOID nextAddress;
  662. PWORK_CONTEXT workContext;
  663. PIRP irp;
  664. PBUFFER bufferDescriptor;
  665. PMDL fullMdl;
  666. PMDL partialMdl;
  667. ASSERT( ((ULONG_PTR)WorkItem & 7) == 0 );
  668. //
  669. // Zero the work item control structures.
  670. //
  671. RtlZeroMemory( WorkItem, WorkItemSize );
  672. //
  673. // Allocate and initialize the work context block.
  674. //
  675. workContext = WorkItem;
  676. nextAddress = workContext + 1;
  677. ASSERT( ((ULONG_PTR)nextAddress & 7) == 0 );
  678. SET_BLOCK_TYPE_STATE_SIZE( workContext, BlockType, BlockStateActive, sizeof(WORK_CONTEXT) );
  679. workContext->BlockHeader.ReferenceCount = 0;
  680. INITIALIZE_REFERENCE_HISTORY( workContext );
  681. INITIALIZE_SPIN_LOCK( &workContext->SpinLock );
  682. //
  683. // Allocate and initialize an IRP.
  684. //
  685. irp = nextAddress;
  686. nextAddress = (PCHAR)irp + IrpSize;
  687. ASSERT( ((ULONG_PTR)nextAddress & 7) == 0 );
  688. workContext->Irp = irp;
  689. IoInitializeIrp( irp, (USHORT)IrpSize, IrpStackSize );
  690. CHECKIRP( irp );
  691. //
  692. // Allocate a buffer descriptor. It will be initialized as we
  693. // find out the necessary information.
  694. //
  695. bufferDescriptor = nextAddress;
  696. nextAddress = bufferDescriptor + 1;
  697. ASSERT( ((ULONG_PTR)nextAddress & 7) == 0 );
  698. workContext->RequestBuffer = bufferDescriptor;
  699. workContext->ResponseBuffer = bufferDescriptor;
  700. //
  701. // Allocate an MDL. In normal work items, this is the "full MDL"
  702. // describing the entire SMB buffer. In raw mode work items, this
  703. // MDL is used to describe raw buffers.
  704. //
  705. fullMdl = nextAddress;
  706. nextAddress = (PCHAR)fullMdl + MdlSize;
  707. ASSERT( ((ULONG_PTR)nextAddress & 7) == 0 );
  708. bufferDescriptor->Mdl = fullMdl;
  709. //
  710. // If this is a normal work item, initialize the first MDL and
  711. // allocate and initialize a second MDL and the SMB buffer.
  712. //
  713. if ( Buffer != NULL ) {
  714. partialMdl = nextAddress;
  715. bufferDescriptor->Buffer = TransportHeaderSize + (PCHAR)Buffer;
  716. MmInitializeMdl( fullMdl, TransportHeaderSize + (PCHAR)Buffer, BufferSize );
  717. memset(Buffer,'N', TransportHeaderSize);
  718. bufferDescriptor->PartialMdl = partialMdl;
  719. MmInitializeMdl( partialMdl, (PVOID)(PAGE_SIZE-1), MAX_PARTIAL_BUFFER_SIZE );
  720. bufferDescriptor->BufferLength = BufferSize;
  721. MmBuildMdlForNonPagedPool( fullMdl );
  722. fullMdl->MdlFlags|=MDL_NETWORK_HEADER;
  723. ASSERT( fullMdl->ByteOffset >= TransportHeaderSize );
  724. }
  725. //
  726. // Initialize the client address pointer
  727. //
  728. workContext->ClientAddress = &workContext->ClientAddressData;
  729. //
  730. // Initialize the processor
  731. //
  732. workContext->CurrentWorkQueue = PROCESSOR_TO_QUEUE();
  733. //
  734. // Print debugging information.
  735. //
  736. IF_DEBUG(HEAP) {
  737. SrvPrint2( " InitializeWorkItem: work item of 0x%lx bytes at 0x%p\n", WorkItemSize, WorkItem );
  738. SrvPrint2( " Work Context: 0x%lx bytes at 0x%p\n",
  739. sizeof(WORK_CONTEXT), workContext );
  740. SrvPrint2( " IRP: 0x%lx bytes at 0x%p\n",
  741. workContext->Irp->Size, workContext->Irp );
  742. SrvPrint2( " Buffer Descriptor: 0x%lx bytes at 0x%p\n",
  743. sizeof(BUFFER), workContext->RequestBuffer );
  744. SrvPrint2( " Full MDL: 0x%lx bytes at 0x%p\n",
  745. MdlSize, workContext->RequestBuffer->Mdl );
  746. if ( Buffer != NULL ) {
  747. SrvPrint2( " Partial MDL: 0x%lx bytes at 0x%p\n",
  748. MdlSize, workContext->ResponseBuffer->PartialMdl );
  749. SrvPrint2( " Buffer: 0x%lx bytes at 0x%p\n",
  750. workContext->RequestBuffer->BufferLength,
  751. workContext->RequestBuffer->Buffer );
  752. } else {
  753. SrvPrint0( " No buffer allocated\n" );
  754. }
  755. }
  756. //
  757. // Return the address of the work context block, which is the "root"
  758. // of the work item.
  759. //
  760. return workContext;
  761. } // InitializeWorkItem
  762. VOID SRVFASTCALL
  763. SrvDereferenceWorkItem (
  764. IN PWORK_CONTEXT WorkContext
  765. )
  766. /*++
  767. Routine Description:
  768. This function decrements the reference count of a work context block.
  769. *** This routine must not be called at DPC level! Use
  770. SrvFsdDereferenceWorkItem from DPC level.
  771. Arguments:
  772. WorkContext - Pointer to the work context block to reference.
  773. Return Value:
  774. None.
  775. --*/
  776. {
  777. ULONG oldCount;
  778. PAGED_CODE( );
  779. ASSERT( (LONG)WorkContext->BlockHeader.ReferenceCount > 0 );
  780. ASSERT( (GET_BLOCK_TYPE(WorkContext) == BlockTypeWorkContextInitial) ||
  781. (GET_BLOCK_TYPE(WorkContext) == BlockTypeWorkContextNormal) ||
  782. (GET_BLOCK_TYPE(WorkContext) == BlockTypeWorkContextRaw) );
  783. UPDATE_REFERENCE_HISTORY( WorkContext, TRUE );
  784. //
  785. // Decrement the WCB's reference count.
  786. //
  787. oldCount = ExInterlockedAddUlong(
  788. (PULONG)&WorkContext->BlockHeader.ReferenceCount,
  789. (ULONG)-1,
  790. &WorkContext->SpinLock
  791. );
  792. IF_DEBUG(REFCNT) {
  793. SrvPrint2( "Dereferencing WorkContext 0x%p; new refcnt 0x%lx\n",
  794. WorkContext, WorkContext->BlockHeader.ReferenceCount );
  795. }
  796. if ( oldCount == 1 ) {
  797. //
  798. // We are done with the work context, replace it on the free queue.
  799. //
  800. // If we are using an extra SMB buffer, free it now.
  801. //
  802. SrvWmiTraceEvent(WorkContext);
  803. if ( WorkContext->UsingExtraSmbBuffer ) {
  804. FREE_EXTRA_SMB_BUFFER( WorkContext );
  805. }
  806. ASSERT( !WorkContext->UsingExtraSmbBuffer );
  807. //
  808. // Release references.
  809. //
  810. SrvReleaseContext( WorkContext );
  811. SrvFsdRequeueReceiveWorkItem( WorkContext );
  812. }
  813. return;
  814. } // SrvDereferenceWorkItem
  815. VOID
  816. SrvFsdDereferenceWorkItem (
  817. IN PWORK_CONTEXT WorkContext
  818. )
  819. /*++
  820. Routine Description:
  821. This function decrements the reference count of a work context block.
  822. Arguments:
  823. WorkContext - Pointer to the work context block to reference.
  824. Return Value:
  825. None.
  826. --*/
  827. {
  828. ULONG oldCount;
  829. ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL );
  830. ASSERT( (LONG)WorkContext->BlockHeader.ReferenceCount > 0 );
  831. ASSERT( (GET_BLOCK_TYPE(WorkContext) == BlockTypeWorkContextInitial) ||
  832. (GET_BLOCK_TYPE(WorkContext) == BlockTypeWorkContextNormal) ||
  833. (GET_BLOCK_TYPE(WorkContext) == BlockTypeWorkContextRaw) );
  834. UPDATE_REFERENCE_HISTORY( WorkContext, TRUE );
  835. //
  836. // Decrement the WCB's reference count.
  837. //
  838. oldCount = ExInterlockedAddUlong(
  839. (PULONG)&WorkContext->BlockHeader.ReferenceCount,
  840. (ULONG)-1,
  841. &WorkContext->SpinLock
  842. );
  843. IF_DEBUG(REFCNT) {
  844. SrvPrint2( "Dereferencing WorkContext 0x%p; new refcnt 0x%lx\n",
  845. WorkContext, WorkContext->BlockHeader.ReferenceCount );
  846. }
  847. if ( oldCount == 1 ) {
  848. //
  849. // We are done with the work context, replace it on the free queue.
  850. //
  851. // If we are using an extra SMB buffer, free it now.
  852. //
  853. if ( WorkContext->UsingExtraSmbBuffer ) {
  854. FREE_EXTRA_SMB_BUFFER( WorkContext );
  855. }
  856. ASSERT( !WorkContext->UsingExtraSmbBuffer );
  857. //
  858. // If the work context block has references to a share, a
  859. // session, or a tree connect, queue it to the FSP immediately.
  860. // These blocks are not in nonpaged pool, so they can't be
  861. // touched at DPC level.
  862. //
  863. if ( (WorkContext->Share != NULL) ||
  864. (WorkContext->Session != NULL) ||
  865. (WorkContext->TreeConnect != NULL) ||
  866. (WorkContext->SecurityContext != NULL) ) {
  867. UPDATE_REFERENCE_HISTORY( WorkContext, FALSE );
  868. ExInterlockedAddUlong(
  869. (PULONG)&WorkContext->BlockHeader.ReferenceCount,
  870. 1,
  871. &WorkContext->SpinLock
  872. );
  873. WorkContext->QueueToHead = TRUE;
  874. WorkContext->FspRestartRoutine = SrvDereferenceWorkItem;
  875. QUEUE_WORK_TO_FSP( WorkContext );
  876. } else {
  877. //
  878. // Try to requeue the work item. This will fail if the
  879. // reference count on the connection goes to zero.
  880. //
  881. // *** Note that even if the requeueing fails, the work item
  882. // is still removed from the in-progress list, so we
  883. // can't just requeue to SrvDereferenceWorkItem.
  884. //
  885. SrvFsdRequeueReceiveWorkItem( WorkContext );
  886. }
  887. }
  888. return;
  889. } // SrvFsdDereferenceWorkItem
  890. NTSTATUS
  891. SrvAllocateExtraSmbBuffer (
  892. IN OUT PWORK_CONTEXT WorkContext
  893. )
  894. {
  895. ULONG cacheLineSize = SrvCacheLineSize;
  896. ULONG bufferSize = SrvReceiveBufferSize;
  897. ULONG mdlSize = SrvMaxMdlSize;
  898. PBUFFER bufferDescriptor;
  899. PMDL fullMdl;
  900. PMDL partialMdl;
  901. PVOID data;
  902. PAGED_CODE( );
  903. ASSERT( !WorkContext->UsingExtraSmbBuffer );
  904. //
  905. // Allocate an SMB buffer for use with SMB's that require a separate
  906. // request and response buffer.
  907. //
  908. bufferDescriptor = ALLOCATE_NONPAGED_POOL(
  909. sizeof(BUFFER) +
  910. mdlSize * 2 +
  911. bufferSize +
  912. TransportHeaderSize +
  913. cacheLineSize,
  914. BlockTypeDataBuffer
  915. );
  916. if ( bufferDescriptor == NULL) {
  917. return STATUS_INSUFF_SERVER_RESOURCES;
  918. }
  919. //
  920. // Initialize one MDL. This is the "full MDL" describing the
  921. // entire SMB buffer.
  922. //
  923. fullMdl = (PMDL)(bufferDescriptor + 1);
  924. partialMdl = (PMDL)( (PCHAR)fullMdl + mdlSize );
  925. data = (PVOID)( ((ULONG_PTR)partialMdl + mdlSize + TransportHeaderSize + cacheLineSize) & ~(LONG_PTR)(cacheLineSize) );
  926. bufferDescriptor->Mdl = fullMdl;
  927. MmInitializeMdl( fullMdl, data, bufferSize );
  928. fullMdl->MdlFlags |= MDL_NETWORK_HEADER;
  929. //
  930. // Initialize a second MDL and the SMB buffer.
  931. //
  932. bufferDescriptor->PartialMdl = partialMdl;
  933. MmInitializeMdl( partialMdl, (PVOID)(PAGE_SIZE-1), MAX_PARTIAL_BUFFER_SIZE );
  934. MmBuildMdlForNonPagedPool( fullMdl );
  935. bufferDescriptor->Buffer = data;
  936. bufferDescriptor->BufferLength = bufferSize;
  937. WorkContext->ResponseBuffer = bufferDescriptor;
  938. WorkContext->ResponseHeader = bufferDescriptor->Buffer;
  939. WorkContext->ResponseParameters = (PCHAR)bufferDescriptor->Buffer +
  940. sizeof( SMB_HEADER );
  941. WorkContext->UsingExtraSmbBuffer = TRUE;
  942. return STATUS_SUCCESS;
  943. } // SrvAllocateExtraSmbBuffer