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
1312 lines
36 KiB
/*++
|
|
|
|
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 = 0;
|
|
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 ()
|
|
{
|
|
PSLIST_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) ||
|
|
(WorkContext->SecurityContext != 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
|
|
|