/*++ Copyright (c) 1989 Microsoft Corporation Module Name: blkwork.c Abstract: This module implements routines for managing work context blocks. Author: Chuck Lenzmeier (chuckl) 4-Oct-1989 David Treadwell (davidtr) Revision History: --*/ #include "precomp.h" #include "blkwork.tmh" #pragma hdrstop #define BugCheckFileId SRV_FILE_BLKWORK #define FREE_EXTRA_SMB_BUFFER( _wc ) { \ ASSERT( (_wc)->UsingExtraSmbBuffer ); \ ASSERT( (_wc)->ResponseBuffer != NULL ); \ DEALLOCATE_NONPAGED_POOL( (_wc)->ResponseBuffer ); \ DEBUG (_wc)->ResponseBuffer = NULL; \ DEBUG (_wc)->ResponseHeader = NULL; \ DEBUG (_wc)->ResponseParameters = NULL; \ (_wc)->UsingExtraSmbBuffer = FALSE; \ } // // Local functions. // #define TransportHeaderSize 80 PWORK_CONTEXT InitializeWorkItem ( IN PVOID WorkItem, IN UCHAR BlockType, IN CLONG TotalSize, IN CLONG IrpSize, IN CCHAR IrpStackSize, IN CLONG MdlSize, IN CLONG BufferSize, IN PVOID Buffer ); #ifdef ALLOC_PRAGMA #pragma alloc_text( PAGE, SrvAllocateInitialWorkItems ) #pragma alloc_text( PAGE, SrvAllocateRawModeWorkItem ) #pragma alloc_text( PAGE, SrvFreeInitialWorkItems ) #pragma alloc_text( PAGE, SrvFreeNormalWorkItem ) #pragma alloc_text( PAGE, SrvFreeRawModeWorkItem ) //#pragma alloc_text( PAGE, SrvDereferenceWorkItem ) #pragma alloc_text( PAGE, SrvAllocateExtraSmbBuffer ) #pragma alloc_text( PAGE, SrvGetRawModeWorkItem ) #pragma alloc_text( PAGE, SrvRequeueRawModeWorkItem ) #endif #if 0 NOT PAGEABLE -- SrvFsdDereferenceWorkItem #endif NTSTATUS SrvAllocateInitialWorkItems ( VOID ) /*++ Routine Description: This routine allocates the initial set of normal server work items. It allocates one large block of memory to contain the entire set. The purpose of this single allocation is to eliminate the wasted space inherent in the allocation of a single work item. (Normal work items occupy about 5K bytes. Because of the way nonpaged pool is managed, allocating 5K actually uses 8K.) Each normal work item includes enough memory to hold the following: - work context block, - IRP, - buffer descriptor, - two MDLs, and - buffer for sends and receives This routine also queues each of the work items to the receive work item list. Arguments: None. Return Value: NTSTATUS - Returns STATUS_INSUFFICIENT_RESOURCES if unable to allocate nonpaged pool; STATUS_SUCCESS otherwise. --*/ { CLONG totalSize; CLONG workItemSize; CLONG irpSize = SrvReceiveIrpSize; CLONG mdlSize = SrvMaxMdlSize; CLONG bufferSize = SrvReceiveBufferSize; ULONG cacheLineSize = SrvCacheLineSize; PVOID workItem; PVOID buffer; PWORK_CONTEXT workContext; CLONG i; PWORK_QUEUE queue; PAGED_CODE(); // // If the initial set of work items is to be empty, don't do // anything. // // *** This will almost certainly never happen, but let's be // prepared just in case. // if ( SrvInitialReceiveWorkItemCount == 0 ) { return STATUS_SUCCESS; } while( SrvInitialWorkItemBlock == NULL && SrvInitialReceiveWorkItemCount != 0 ) { // // Find out the sizes of the IRP, the SMB buffer, and the MDLs. The // MDL size is "worst case" -- the actual MDL size may be smaller, // but this calculation ensures that the MDL will be large enough. // // *** Note that the space allocated for the SMB buffer must be made // large enough to allow the buffer to be aligned such that it // falls, alone, within a set of cache-line-sized blocks. This // allows I/O to be performed to or from the buffer without // concern for cache line tearing. (Note the assumption below // that the cache line size is a power of two.) // // // Determine how large a buffer is needed for a single work item, // not including the SMB buffer. Round this number to a quadword // boundary. // workItemSize = sizeof(WORK_CONTEXT) + irpSize + sizeof(BUFFER) + (mdlSize * 2); workItemSize = (workItemSize + (MEMORY_ALLOCATION_ALIGNMENT - 1)) & ~(MEMORY_ALLOCATION_ALIGNMENT - 1); // // Determine the total amount of space needed. The allocation // must be padded in order to allow the SMB buffers to be aligned // on cache line boundaries. // totalSize = (bufferSize + TransportHeaderSize + workItemSize) * SrvInitialReceiveWorkItemCount + cacheLineSize; IF_DEBUG(HEAP) { SrvPrint0( "SrvAllocateInitialWorkItems:\n" ); SrvPrint1( " work item size = 0x%lx bytes\n", workItemSize ); SrvPrint1( " buffer size = 0x%lx bytes\n", bufferSize ); SrvPrint1( " Backfill size = 0x%lx bytes\n", TransportHeaderSize ); SrvPrint1( " number of work items = %ld\n", SrvInitialReceiveWorkItemCount ); SrvPrint1( " total allocation = 0x%lx bytes\n", totalSize ); SrvPrint1( " wasted space = 0x%p bytes\n", (PVOID)(ROUND_TO_PAGES( totalSize ) - totalSize) ); SrvPrint1( " amount saved over separate allocation = 0x%p bytes\n", (PVOID)(((ROUND_TO_PAGES( workItemSize ) + ROUND_TO_PAGES( bufferSize )) * SrvInitialReceiveWorkItemCount) - ROUND_TO_PAGES( totalSize )) ); } // // Attempt to allocate from nonpaged pool. // SrvInitialWorkItemBlock = ALLOCATE_NONPAGED_POOL( totalSize, BlockTypeWorkContextInitial ); if ( SrvInitialWorkItemBlock == NULL ) { INTERNAL_ERROR( ERROR_LEVEL_EXPECTED, "SrvAllocateInitialWorkItems: Unable to allocate %d bytes " "from nonpaged pool.", totalSize, NULL ); // // Let's try reducing the count and give it another shot. // SrvInitialReceiveWorkItemCount /= 2; } } if( SrvInitialWorkItemBlock == 0 ) { return STATUS_INSUFFICIENT_RESOURCES; } // // Round the allocation to a cache line boundary, then reserve // space for SMB buffers and control structures. // buffer = (PVOID)(((ULONG_PTR)SrvInitialWorkItemBlock + cacheLineSize) & ~((LONG_PTR)cacheLineSize)); workItem = (PCHAR)buffer + ((bufferSize + TransportHeaderSize) * SrvInitialReceiveWorkItemCount); // // Initialize the work items and update the count of work items in // the server. // // *** Note that the update is not synchronized -- that shouldn't be // necessary at this stage of server initialization. // queue = SrvWorkQueues; for ( i = 0; i < SrvInitialReceiveWorkItemCount; i++ ) { if (((PAGE_SIZE - 1) - BYTE_OFFSET(buffer)) < (TransportHeaderSize + sizeof(SMB_HEADER))) { buffer = (PCHAR)buffer + PAGE_SIZE - BYTE_OFFSET(buffer); i++; IF_DEBUG(HEAP) { SrvPrint2("buffer adjusted!! %p offset %x \n",buffer,BYTE_OFFSET(buffer)); } } workContext = InitializeWorkItem( workItem, BlockTypeWorkContextInitial, workItemSize, irpSize, SrvReceiveIrpStackSize, mdlSize, bufferSize, buffer ); workContext->PartOfInitialAllocation = TRUE; workContext->FreeList = &queue->InitialWorkItemList; workContext->CurrentWorkQueue = queue; if( ++queue == eSrvWorkQueues ) queue = SrvWorkQueues; // // Setup the work item and queue it to the free list // SrvPrepareReceiveWorkItem( workContext, TRUE ); buffer = (PCHAR)buffer + TransportHeaderSize + bufferSize; workItem = (PCHAR)workItem + workItemSize; INCREMENT_DEBUG_STAT2( SrvDbgStatistics.WorkContextInfo.Allocations ); } return STATUS_SUCCESS; } // SrvAllocateInitialWorkItems NTSTATUS SrvAllocateNormalWorkItem ( OUT PWORK_CONTEXT *WorkContext, PWORK_QUEUE queue ) /*++ Routine Description: This routine allocates a normal server work item. It allocates enough memory to hold the following: - work context block, - IRP, - buffer descriptor, - two MDLs, and - buffer for sends and receives It then initializes each of these blocks in the buffer. If the number of normal work items in the server is already at the configured maximum, this routine refuses to create a new one. Arguments: WorkContext - Returns a pointer to the Work Context Block, or NULL if the limit has been reached or if no space is available. The work context block has pointers to the other blocks. Return Value: None. --*/ { CLONG totalSize; CLONG workItemSize; CLONG irpSize = SrvReceiveIrpSize; CLONG mdlSize = SrvMaxMdlSize; CLONG bufferSize = SrvReceiveBufferSize; CLONG cacheLineSize = SrvCacheLineSize; PVOID workItem; PVOID buffer; CLONG oldWorkItemCount; // // If we're already at the limit of how many work items we can // have, don't create another one. // // *** Note that the method used below leaves a small window in // which we may refuse to create a work item when we're not // really at the limit -- we increment the value, another thread // frees a work item and decrements the value, yet another // thread tests to see whether it can create a new work item. // Both testing threads will refuse to create a new work item, // even though the final number of work items is one less than // the maximum. // if ( queue->AllocatedWorkItems >= queue->MaximumWorkItems ) { // // Can't create any more work items just now. // IF_DEBUG(ERRORS) { SrvPrint0( "SrvAllocateNormalWorkItem: Work item limit reached\n" ); } *WorkContext = NULL; return STATUS_INSUFF_SERVER_RESOURCES; } InterlockedIncrement( &queue->AllocatedWorkItems ); // // Find out the sizes of the IRP, the SMB buffer, and the MDLs. The // MDL size is "worst case" -- the actual MDL size may be smaller, // but this calculation ensures that the MDL will be large enough. // // *** Note that the space allocated for the SMB buffer must be made // large enough to allow the buffer to be aligned such that it // falls, alone, within a set of cache-line-sized blocks. This // allows I/O to be performed to or from the buffer without // concern for cache line tearing. (Note the assumption below // that the cache line size is a power of two.) // // // Determine how large a buffer is needed for the SMB buffer and // control structures. The allocation must be padded in order to // allow the SMB buffer to be aligned on a cache line boundary. // workItemSize = sizeof(WORK_CONTEXT) + irpSize + sizeof(BUFFER) + (mdlSize * 2); totalSize = workItemSize + bufferSize + TransportHeaderSize+ cacheLineSize; // // Attempt to allocate from nonpaged pool. // workItem = ALLOCATE_NONPAGED_POOL( totalSize, BlockTypeWorkContextNormal ); if ( workItem == NULL ) { INTERNAL_ERROR( ERROR_LEVEL_EXPECTED, "SrvAllocateNormalWorkItem: Unable to allocate %d bytes " "from nonpaged pool.", totalSize, NULL ); InterlockedDecrement( &queue->AllocatedWorkItems ); *WorkContext = NULL; return STATUS_INSUFFICIENT_RESOURCES; } // // Reserve space for the SMB buffer on a cache line boundary. // buffer = (PVOID)(((ULONG_PTR)workItem + workItemSize + cacheLineSize) & ~((LONG_PTR)cacheLineSize)); if (((PAGE_SIZE - 1) - BYTE_OFFSET(buffer)) < (TransportHeaderSize + sizeof(SMB_HEADER))) { INTERNAL_ERROR( ERROR_LEVEL_EXPECTED, "SrvAllocateNormalWorkItem: Unable to allocate header with in a page ", totalSize, NULL ); InterlockedDecrement( &queue->AllocatedWorkItems ); DEALLOCATE_NONPAGED_POOL( workItem ); *WorkContext = NULL; return STATUS_INSUFFICIENT_RESOURCES; } // // Initialize the work item and increment the count of work items in // the server. // *WorkContext = InitializeWorkItem( workItem, BlockTypeWorkContextNormal, workItemSize, irpSize, SrvReceiveIrpStackSize, mdlSize, bufferSize, buffer ); (*WorkContext)->PartOfInitialAllocation = FALSE; INCREMENT_DEBUG_STAT2( SrvDbgStatistics.WorkContextInfo.Allocations ); (*WorkContext)->FreeList = &queue->NormalWorkItemList; (*WorkContext)->CurrentWorkQueue = queue; return STATUS_SUCCESS; } // SrvAllocateNormalWorkItem VOID SrvAllocateRawModeWorkItem ( OUT PWORK_CONTEXT *WorkContext, IN PWORK_QUEUE queue ) /*++ Routine Description: This routine allocates a raw mode work item. It allocates enough memory to hold the following: - work context block, - IRP, - buffer descriptor, and - one MDL It then initializes each of these blocks in the buffer. If the number of raw mode work items in the server is already at the configured maximum, this routine refuses to create a new one. Arguments: WorkContext - Returns a pointer to the Work Context Block, or NULL if no space was available. The work context block has pointers to the other blocks. Return Value: None. --*/ { CLONG workItemSize; CLONG irpSize = SrvReceiveIrpSize; CLONG mdlSize = SrvMaxMdlSize; PVOID workItem; CLONG oldWorkItemCount; PAGED_CODE( ); // // If we're already at the limit of how many work items we can // have, don't create another one. // // *** Note that the method used below leaves a small window in // which we may refuse to create a work item when we're not // really at the limit -- we increment the value, another thread // frees a work item and decrements the value, yet another // thread tests to see whether it can create a new work item. // Both testing threads will refuse to create a new work item, // even though the final number of work items is one less than // the maximum. // if ( (ULONG)queue->AllocatedRawModeWorkItems >= SrvMaxRawModeWorkItemCount / SrvNumberOfProcessors ) { // // Can't create any more work items just now. // // !!! This should be logged somehow, but we don't want to // breakpoint the server when it happens. // IF_DEBUG(ERRORS) { SrvPrint0( "SrvAllocateRawModeWorkItem: Work item limit reached\n" ); } *WorkContext = NULL; return; } InterlockedIncrement( &queue->AllocatedRawModeWorkItems ); // // Find out the sizes of the IRP and the MDL. The MDL size is // "worst case" -- the actual MDL size may be smaller, but this // calculation ensures that the MDL will be large enough. // workItemSize = sizeof(WORK_CONTEXT) + sizeof(BUFFER) + irpSize + mdlSize; // // Attempt to allocate from nonpaged pool. // workItem = ALLOCATE_NONPAGED_POOL( workItemSize, BlockTypeWorkContextRaw ); if ( workItem == NULL ) { INTERNAL_ERROR( ERROR_LEVEL_EXPECTED, "SrvAllocateRawModeWorkItem: Unable to allocate %d bytes " "from nonpaged pool.", workItemSize, NULL ); InterlockedDecrement( &queue->AllocatedRawModeWorkItems ); *WorkContext = NULL; return; } // // Initialize the work item and increment the count of work items in // the server. // *WorkContext = InitializeWorkItem( workItem, BlockTypeWorkContextRaw, workItemSize, irpSize, SrvReceiveIrpStackSize, mdlSize, 0, NULL ); INCREMENT_DEBUG_STAT2( SrvDbgStatistics.WorkContextInfo.Allocations ); (*WorkContext)->FreeList = &queue->RawModeWorkItemList; (*WorkContext)->CurrentWorkQueue = queue; } // SrvAllocateRawModeWorkItem PWORK_CONTEXT SrvGetRawModeWorkItem () { PSINGLE_LIST_ENTRY listEntry; PWORK_CONTEXT workContext; PWORK_QUEUE queue = PROCESSOR_TO_QUEUE(); PAGED_CODE(); // // Attempt to allocate a raw mode work item off the current processor's queue // listEntry = ExInterlockedPopEntrySList( &queue->RawModeWorkItemList, &queue->SpinLock ); if( listEntry != NULL ) { workContext = CONTAINING_RECORD( listEntry, WORK_CONTEXT, SingleListEntry ); InterlockedDecrement( &queue->FreeRawModeWorkItems ); ASSERT( queue->FreeRawModeWorkItems >= 0 ); } else { SrvAllocateRawModeWorkItem( &workContext, queue ); } if( workContext != NULL || SrvNumberOfProcessors == 1 ) { return workContext; } // // We were unable to get or allocate a raw mode workitem off the current // work queue. We're a multiprocessor system, so look around for one off // of a different work queue. // for( queue = SrvWorkQueues; queue < eSrvWorkQueues; queue++ ) { listEntry = ExInterlockedPopEntrySList( &queue->RawModeWorkItemList, &queue->SpinLock ); if ( listEntry != NULL ) { InterlockedDecrement( &queue->FreeRawModeWorkItems ); ASSERT( queue->FreeRawModeWorkItems >= 0 ); workContext = CONTAINING_RECORD( listEntry, WORK_CONTEXT, SingleListEntry ); return workContext; } } // // We were unable to get a free raw mode workitem off a different processor's // raw work item queue. See if any of the queues allow allocation of a new one. // for( queue = SrvWorkQueues; queue < eSrvWorkQueues; queue++ ) { SrvAllocateRawModeWorkItem( &workContext, queue ); if( workContext != NULL ) { break; } } return workContext; } // SrvGetRawModeWorkItem VOID SrvRequeueRawModeWorkItem ( PWORK_CONTEXT WorkContext ) { PWORK_QUEUE queue = CONTAINING_RECORD( WorkContext->FreeList, WORK_QUEUE, RawModeWorkItemList ); PAGED_CODE(); InterlockedIncrement( &queue->FreeRawModeWorkItems ); ExInterlockedPushEntrySList( &queue->RawModeWorkItemList, &WorkContext->SingleListEntry, &queue->SpinLock ); return; } // SrvRequeueRawModeWorkItem VOID SrvFreeInitialWorkItems ( VOID ) /*++ Routine Description: This function deallocates the large block of work items allocated at server startup. Arguments: None. Return Value: None. --*/ { PAGED_CODE( ); if ( SrvInitialWorkItemBlock != NULL ) { IF_DEBUG(BLOCK1) { SrvPrint1( "Releasing initial work item block at 0x%p\n", SrvInitialWorkItemBlock ); } DEALLOCATE_NONPAGED_POOL( SrvInitialWorkItemBlock ); IF_DEBUG(HEAP) { SrvPrint1( "SrvFreeInitialWorkItems: Freed initial work item block at 0x%p\n", SrvInitialWorkItemBlock ); } SrvInitialWorkItemBlock = NULL; } return; } // SrvFreeInitialWorkItems VOID SrvFreeNormalWorkItem ( IN PWORK_CONTEXT WorkContext ) /*++ Routine Description: This function deallocates a work item block. Arguments: WorkContext - Address of Work Context block that heads up the work item. Return Value: None. --*/ { PWORK_QUEUE queue = WorkContext->CurrentWorkQueue; PAGED_CODE( ); IF_DEBUG(BLOCK1) { SrvPrint1( "Closing work item at 0x%p\n", WorkContext ); } ASSERT( GET_BLOCK_STATE( WorkContext ) == BlockStateActive ); ASSERT( !WorkContext->PartOfInitialAllocation ); // // Free the work item block itself. // DEBUG SET_BLOCK_TYPE_STATE_SIZE( WorkContext, BlockTypeGarbage, BlockStateDead, -1 ); DEBUG WorkContext->BlockHeader.ReferenceCount = (ULONG)-1; DEALLOCATE_NONPAGED_POOL( WorkContext ); IF_DEBUG(HEAP) { SrvPrint1( "SrvFreeNormalWorkItem: Freed Work Item block at 0x%p\n", WorkContext ); } // // Update the count of work items in the server. // InterlockedDecrement( &queue->AllocatedWorkItems ); INCREMENT_DEBUG_STAT2( SrvDbgStatistics.WorkContextInfo.Frees ); return; } // SrvFreeNormalWorkItem VOID SrvFreeRawModeWorkItem ( IN PWORK_CONTEXT WorkContext ) /*++ Routine Description: This function deallocates a raw mode work item block. Arguments: WorkContext - Address of Work Context block that heads up the work item. Return Value: None. --*/ { PWORK_QUEUE queue = CONTAINING_RECORD( WorkContext->FreeList, WORK_QUEUE, RawModeWorkItemList ); PAGED_CODE( ); IF_DEBUG(BLOCK1) { SrvPrint1( "Closing workitem at 0x%p\n", WorkContext ); } ASSERT( GET_BLOCK_STATE( WorkContext ) == BlockStateActive ); ASSERT( !WorkContext->PartOfInitialAllocation ); // // Free the work item block itself. // DEBUG SET_BLOCK_TYPE_STATE_SIZE( WorkContext, BlockTypeGarbage, BlockStateDead, -1 ); DEBUG WorkContext->BlockHeader.ReferenceCount = (ULONG)-1; DEALLOCATE_NONPAGED_POOL( WorkContext ); IF_DEBUG(HEAP) { SrvPrint1( "SrvFreeRawModeWorkItem: Freed Work Item block at 0x%p\n", WorkContext ); } // // Update the count of work items in the server. // InterlockedDecrement( &queue->AllocatedRawModeWorkItems ); ASSERT( queue->AllocatedRawModeWorkItems >= 0 ); INCREMENT_DEBUG_STAT2( SrvDbgStatistics.WorkContextInfo.Frees ); return; } // SrvFreeRawModeWorkItem PWORK_CONTEXT InitializeWorkItem ( IN PVOID WorkItem, IN UCHAR BlockType, IN CLONG WorkItemSize, IN CLONG IrpSize, IN CCHAR IrpStackSize, IN CLONG MdlSize, IN CLONG BufferSize, IN PVOID Buffer ) /*++ Routine Description: This routine initializes the following components of a work item: - a work context block, - an IRP, - the CurrentWorkQueue - optionally, a buffer descriptor, - one or two MDLs, and - optionally, a buffer for sends and receives The storage for these components must have been allocated by the caller, in contiguous storage starting at WorkContext. Arguments: WorkItem - Supplies a pointer to the storage allocated to the work item. BlockType - The type of work item being initialized. WorkItemSize - Indicates the total amount of space allocated to the work item control structures (i.e., not including the data buffer, if any). IrpSize - Indicates the amount of space in the work item to be reserved for the IRP. IrpStackSize - Indicates the number of stack locations in the IRP. MdlSize - Indicates the amount of space in the work item to be reserved for each MDL. One MDL is created if Buffer is NULL; two are created if Buffer is not NULL. BufferSize - Indicates the amount of space allocated to be data buffer. This parameter is ignored if Buffer is NULL. Buffer - Supplies a pointer to a data buffer. NULL indicates that no data buffer was allocated. (This is used for raw mode work items.) Return Value: PWORK_CONTEXT - Returns a pointer to the work context block that forms the "root" of the work item. --*/ { PVOID nextAddress; PWORK_CONTEXT workContext; PIRP irp; PBUFFER bufferDescriptor; PMDL fullMdl; PMDL partialMdl; ASSERT( ((ULONG_PTR)WorkItem & 7) == 0 ); // // Zero the work item control structures. // RtlZeroMemory( WorkItem, WorkItemSize ); // // Allocate and initialize the work context block. // workContext = WorkItem; nextAddress = workContext + 1; ASSERT( ((ULONG_PTR)nextAddress & 7) == 0 ); SET_BLOCK_TYPE_STATE_SIZE( workContext, BlockType, BlockStateActive, sizeof(WORK_CONTEXT) ); workContext->BlockHeader.ReferenceCount = 0; INITIALIZE_REFERENCE_HISTORY( workContext ); INITIALIZE_SPIN_LOCK( &workContext->SpinLock ); // // Allocate and initialize an IRP. // irp = nextAddress; nextAddress = (PCHAR)irp + IrpSize; ASSERT( ((ULONG_PTR)nextAddress & 7) == 0 ); workContext->Irp = irp; IoInitializeIrp( irp, (USHORT)IrpSize, IrpStackSize ); CHECKIRP( irp ); // // Allocate a buffer descriptor. It will be initialized as we // find out the necessary information. // bufferDescriptor = nextAddress; nextAddress = bufferDescriptor + 1; ASSERT( ((ULONG_PTR)nextAddress & 7) == 0 ); workContext->RequestBuffer = bufferDescriptor; workContext->ResponseBuffer = bufferDescriptor; // // Allocate an MDL. In normal work items, this is the "full MDL" // describing the entire SMB buffer. In raw mode work items, this // MDL is used to describe raw buffers. // fullMdl = nextAddress; nextAddress = (PCHAR)fullMdl + MdlSize; ASSERT( ((ULONG_PTR)nextAddress & 7) == 0 ); bufferDescriptor->Mdl = fullMdl; // // If this is a normal work item, initialize the first MDL and // allocate and initialize a second MDL and the SMB buffer. // if ( Buffer != NULL ) { partialMdl = nextAddress; bufferDescriptor->Buffer = TransportHeaderSize + (PCHAR)Buffer; MmInitializeMdl( fullMdl, TransportHeaderSize + (PCHAR)Buffer, BufferSize ); memset(Buffer,'N', TransportHeaderSize); bufferDescriptor->PartialMdl = partialMdl; MmInitializeMdl( partialMdl, (PVOID)(PAGE_SIZE-1), MAX_PARTIAL_BUFFER_SIZE ); bufferDescriptor->BufferLength = BufferSize; MmBuildMdlForNonPagedPool( fullMdl ); fullMdl->MdlFlags|=MDL_NETWORK_HEADER; ASSERT( fullMdl->ByteOffset >= TransportHeaderSize ); } // // Initialize the client address pointer // workContext->ClientAddress = &workContext->ClientAddressData; // // Initialize the processor // workContext->CurrentWorkQueue = PROCESSOR_TO_QUEUE(); // // Print debugging information. // IF_DEBUG(HEAP) { SrvPrint2( " InitializeWorkItem: work item of 0x%lx bytes at 0x%p\n", WorkItemSize, WorkItem ); SrvPrint2( " Work Context: 0x%lx bytes at 0x%p\n", sizeof(WORK_CONTEXT), workContext ); SrvPrint2( " IRP: 0x%lx bytes at 0x%p\n", workContext->Irp->Size, workContext->Irp ); SrvPrint2( " Buffer Descriptor: 0x%lx bytes at 0x%p\n", sizeof(BUFFER), workContext->RequestBuffer ); SrvPrint2( " Full MDL: 0x%lx bytes at 0x%p\n", MdlSize, workContext->RequestBuffer->Mdl ); if ( Buffer != NULL ) { SrvPrint2( " Partial MDL: 0x%lx bytes at 0x%p\n", MdlSize, workContext->ResponseBuffer->PartialMdl ); SrvPrint2( " Buffer: 0x%lx bytes at 0x%p\n", workContext->RequestBuffer->BufferLength, workContext->RequestBuffer->Buffer ); } else { SrvPrint0( " No buffer allocated\n" ); } } // // Return the address of the work context block, which is the "root" // of the work item. // return workContext; } // InitializeWorkItem VOID SRVFASTCALL SrvDereferenceWorkItem ( IN PWORK_CONTEXT WorkContext ) /*++ Routine Description: This function decrements the reference count of a work context block. *** This routine must not be called at DPC level! Use SrvFsdDereferenceWorkItem from DPC level. Arguments: WorkContext - Pointer to the work context block to reference. Return Value: None. --*/ { ULONG oldCount; PAGED_CODE( ); ASSERT( (LONG)WorkContext->BlockHeader.ReferenceCount > 0 ); ASSERT( (GET_BLOCK_TYPE(WorkContext) == BlockTypeWorkContextInitial) || (GET_BLOCK_TYPE(WorkContext) == BlockTypeWorkContextNormal) || (GET_BLOCK_TYPE(WorkContext) == BlockTypeWorkContextRaw) ); UPDATE_REFERENCE_HISTORY( WorkContext, TRUE ); // // Decrement the WCB's reference count. // oldCount = ExInterlockedAddUlong( (PULONG)&WorkContext->BlockHeader.ReferenceCount, (ULONG)-1, &WorkContext->SpinLock ); IF_DEBUG(REFCNT) { SrvPrint2( "Dereferencing WorkContext 0x%p; new refcnt 0x%lx\n", WorkContext, WorkContext->BlockHeader.ReferenceCount ); } if ( oldCount == 1 ) { // // We are done with the work context, replace it on the free queue. // // If we are using an extra SMB buffer, free it now. // SrvWmiTraceEvent(WorkContext); if ( WorkContext->UsingExtraSmbBuffer ) { FREE_EXTRA_SMB_BUFFER( WorkContext ); } ASSERT( !WorkContext->UsingExtraSmbBuffer ); // // Release references. // SrvReleaseContext( WorkContext ); SrvFsdRequeueReceiveWorkItem( WorkContext ); } return; } // SrvDereferenceWorkItem VOID SrvFsdDereferenceWorkItem ( IN PWORK_CONTEXT WorkContext ) /*++ Routine Description: This function decrements the reference count of a work context block. Arguments: WorkContext - Pointer to the work context block to reference. Return Value: None. --*/ { ULONG oldCount; ASSERT( KeGetCurrentIrql() == DISPATCH_LEVEL ); ASSERT( (LONG)WorkContext->BlockHeader.ReferenceCount > 0 ); ASSERT( (GET_BLOCK_TYPE(WorkContext) == BlockTypeWorkContextInitial) || (GET_BLOCK_TYPE(WorkContext) == BlockTypeWorkContextNormal) || (GET_BLOCK_TYPE(WorkContext) == BlockTypeWorkContextRaw) ); UPDATE_REFERENCE_HISTORY( WorkContext, TRUE ); // // Decrement the WCB's reference count. // oldCount = ExInterlockedAddUlong( (PULONG)&WorkContext->BlockHeader.ReferenceCount, (ULONG)-1, &WorkContext->SpinLock ); IF_DEBUG(REFCNT) { SrvPrint2( "Dereferencing WorkContext 0x%p; new refcnt 0x%lx\n", WorkContext, WorkContext->BlockHeader.ReferenceCount ); } if ( oldCount == 1 ) { // // We are done with the work context, replace it on the free queue. // // If we are using an extra SMB buffer, free it now. // if ( WorkContext->UsingExtraSmbBuffer ) { FREE_EXTRA_SMB_BUFFER( WorkContext ); } ASSERT( !WorkContext->UsingExtraSmbBuffer ); // // If the work context block has references to a share, a // session, or a tree connect, queue it to the FSP immediately. // These blocks are not in nonpaged pool, so they can't be // touched at DPC level. // if ( (WorkContext->Share != NULL) || (WorkContext->Session != NULL) || (WorkContext->TreeConnect != NULL) ) { UPDATE_REFERENCE_HISTORY( WorkContext, FALSE ); ExInterlockedAddUlong( (PULONG)&WorkContext->BlockHeader.ReferenceCount, 1, &WorkContext->SpinLock ); WorkContext->QueueToHead = TRUE; WorkContext->FspRestartRoutine = SrvDereferenceWorkItem; QUEUE_WORK_TO_FSP( WorkContext ); } else { // // Try to requeue the work item. This will fail if the // reference count on the connection goes to zero. // // *** Note that even if the requeueing fails, the work item // is still removed from the in-progress list, so we // can't just requeue to SrvDereferenceWorkItem. // SrvFsdRequeueReceiveWorkItem( WorkContext ); } } return; } // SrvFsdDereferenceWorkItem NTSTATUS SrvAllocateExtraSmbBuffer ( IN OUT PWORK_CONTEXT WorkContext ) { ULONG cacheLineSize = SrvCacheLineSize; ULONG bufferSize = SrvReceiveBufferSize; ULONG mdlSize = SrvMaxMdlSize; PBUFFER bufferDescriptor; PMDL fullMdl; PMDL partialMdl; PVOID data; PAGED_CODE( ); ASSERT( !WorkContext->UsingExtraSmbBuffer ); // // Allocate an SMB buffer for use with SMB's that require a separate // request and response buffer. // bufferDescriptor = ALLOCATE_NONPAGED_POOL( sizeof(BUFFER) + mdlSize * 2 + bufferSize + TransportHeaderSize + cacheLineSize, BlockTypeDataBuffer ); if ( bufferDescriptor == NULL) { return STATUS_INSUFF_SERVER_RESOURCES; } // // Initialize one MDL. This is the "full MDL" describing the // entire SMB buffer. // fullMdl = (PMDL)(bufferDescriptor + 1); partialMdl = (PMDL)( (PCHAR)fullMdl + mdlSize ); data = (PVOID)( ((ULONG_PTR)partialMdl + mdlSize + TransportHeaderSize + cacheLineSize) & ~(LONG_PTR)(cacheLineSize) ); bufferDescriptor->Mdl = fullMdl; MmInitializeMdl( fullMdl, data, bufferSize ); fullMdl->MdlFlags |= MDL_NETWORK_HEADER; // // Initialize a second MDL and the SMB buffer. // bufferDescriptor->PartialMdl = partialMdl; MmInitializeMdl( partialMdl, (PVOID)(PAGE_SIZE-1), MAX_PARTIAL_BUFFER_SIZE ); MmBuildMdlForNonPagedPool( fullMdl ); bufferDescriptor->Buffer = data; bufferDescriptor->BufferLength = bufferSize; WorkContext->ResponseBuffer = bufferDescriptor; WorkContext->ResponseHeader = bufferDescriptor->Buffer; WorkContext->ResponseParameters = (PCHAR)bufferDescriptor->Buffer + sizeof( SMB_HEADER ); WorkContext->UsingExtraSmbBuffer = TRUE; return STATUS_SUCCESS; } // SrvAllocateExtraSmbBuffer