685 lines
17 KiB
C
685 lines
17 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
heapmgr.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains initialization and termination routines for
|
|||
|
server FSP heap, as well as debug routines for memory tracking.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "precomp.h"
|
|||
|
#include "heapmgr.tmh"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
// Make the retry time 15 milli-seconds
|
|||
|
#define SRV_LOW_PRIORITY_RETRY_TIME -1*1000*10*15
|
|||
|
|
|||
|
#ifdef POOL_TAGGING
|
|||
|
//
|
|||
|
// Array correlating BlockType numbers to pool tags.
|
|||
|
//
|
|||
|
// *** This array must be maintained in concert with the BlockType
|
|||
|
// definitions in srvblock.h!
|
|||
|
//
|
|||
|
|
|||
|
ULONG SrvPoolTags[BlockTypeMax-1] = {
|
|||
|
'fbSL', // BlockTypeBuffer
|
|||
|
'ncSL', // BlockTypeConnection
|
|||
|
'peSL', // BlockTypeEndpoint
|
|||
|
'flSL', // BlockTypeLfcb
|
|||
|
'fmSL', // BlockTypeMfcb
|
|||
|
'frSL', // BlockTypeRfcb
|
|||
|
'rsSL', // BlockTypeSearch
|
|||
|
'csSL', // BlockTypeSearchCore
|
|||
|
'lbSL', // BlockTypeByteRangeLock for persistent handles
|
|||
|
'ssSL', // BlockTypeSession
|
|||
|
'hsSL', // BlockTypeShare
|
|||
|
'rtSL', // BlockTypeTransaction
|
|||
|
'ctSL', // BlockTypeTreeConnect
|
|||
|
'poSL', // BlockTypeOplockBreak
|
|||
|
'dcSL', // BlockTypeCommDevice
|
|||
|
'iwSL', // BlockTypeWorkContextInitial
|
|||
|
'nwSL', // BlockTypeWorkContextNormal
|
|||
|
'rwSL', // BlockTypeWorkContextRaw
|
|||
|
'swSL', // BlockTypeWorkContextSpecial
|
|||
|
'dcSL', // BlockTypeCachedDirectory
|
|||
|
'bdSL', // BlockTypeDataBuffer
|
|||
|
'btSL', // BlockTypeTable
|
|||
|
'hnSL', // BlockTypeNonpagedHeader
|
|||
|
'cpSL', // BlockTypePagedConnection
|
|||
|
'rpSL', // BlockTypePagedRfcb
|
|||
|
'mpSL', // BlockTypePagedMfcb
|
|||
|
'itSL', // BlockTypeTimer
|
|||
|
'caSL', // BlockTypeAdminCheck
|
|||
|
'qwSL', // BlockTypeWorkQueue
|
|||
|
'fsSL', // BlockTypeDfs
|
|||
|
'rlSL', // BlockTypeLargeReadX
|
|||
|
'saSL', // BlockTypeAdapterStatus
|
|||
|
'rsSL', // BlockTypeShareRemark
|
|||
|
'dsSL', // BlockTypeShareSecurityDescriptor
|
|||
|
'ivSL', // BlockTypeVolumeInformation
|
|||
|
'nfSL', // BlockTypeFSName
|
|||
|
'inSL', // BlockTypeNameInfo
|
|||
|
'idSL', // BlockTypeDirectoryInfo
|
|||
|
'cdSL', // BlockTypeDirCache
|
|||
|
'imSL', // BlockTypeMisc
|
|||
|
'nsSL', // BlockTypeSnapShot
|
|||
|
#ifdef INCLUDE_SMB_PERSISTENT
|
|||
|
'spSL', // BlockTypePersistentState
|
|||
|
'bpSL', // BlockTypePersistentBitMap
|
|||
|
'hpSL', // BlockTypePersistShareState
|
|||
|
#endif
|
|||
|
};
|
|||
|
|
|||
|
//
|
|||
|
// Macro to map from block type to pool tag.
|
|||
|
//
|
|||
|
|
|||
|
#define TAG_FROM_TYPE(_type) SrvPoolTags[(_type)-1]
|
|||
|
|
|||
|
#else
|
|||
|
|
|||
|
#define TAG_FROM_TYPE(_type) ignoreme
|
|||
|
|
|||
|
#endif // def POOL_TAGGING
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text( PAGE, SrvAllocatePagedPool )
|
|||
|
#pragma alloc_text( PAGE, SrvFreePagedPool )
|
|||
|
#pragma alloc_text( PAGE, SrvClearLookAsideList )
|
|||
|
#endif
|
|||
|
#if 0
|
|||
|
NOT PAGEABLE -- SrvAllocateNonPagedPool
|
|||
|
NOT PAGEABLE -- SrvFreeNonPagedPool
|
|||
|
#endif
|
|||
|
|
|||
|
extern LONG SrvMemoryAllocationRetries;
|
|||
|
extern LONG SrvMemoryAllocationRetriesSuccessful;
|
|||
|
|
|||
|
|
|||
|
PVOID SRVFASTCALL
|
|||
|
SrvInterlockedAllocate( PLOOK_ASIDE_LIST l, ULONG NumberOfBytes, PLONG statistics )
|
|||
|
{
|
|||
|
PPOOL_HEADER newPool;
|
|||
|
PPOOL_HEADER *pentry = NumberOfBytes > LOOK_ASIDE_SWITCHOVER ?
|
|||
|
l->LargeFreeList : l->SmallFreeList;
|
|||
|
|
|||
|
PPOOL_HEADER *pend = pentry + LOOK_ASIDE_MAX_ELEMENTS;
|
|||
|
|
|||
|
do {
|
|||
|
//
|
|||
|
// Exchange with the lookaside spot and see if we get anything
|
|||
|
//
|
|||
|
|
|||
|
newPool = NULL;
|
|||
|
newPool = (PPOOL_HEADER)InterlockedExchangePointer( pentry, newPool );
|
|||
|
|
|||
|
if( newPool == NULL ) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if( newPool->RequestedSize >= NumberOfBytes ) {
|
|||
|
//
|
|||
|
// The one we got is big enough! Return it.
|
|||
|
//
|
|||
|
++(l->AllocHit);
|
|||
|
return newPool + 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// It wasn't big enough, so put it back.
|
|||
|
//
|
|||
|
newPool = (PPOOL_HEADER)InterlockedExchangePointer( pentry, newPool );
|
|||
|
if( newPool == NULL ) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Oops, somebody else freed some memory to this spot. Can we use it?
|
|||
|
//
|
|||
|
if( newPool->RequestedSize >= NumberOfBytes ) {
|
|||
|
//
|
|||
|
// We can use it!
|
|||
|
//
|
|||
|
++(l->AllocHit);
|
|||
|
return newPool + 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Can't use the memory -- so really free it and keep looking
|
|||
|
//
|
|||
|
if( statistics ) {
|
|||
|
InterlockedExchangeAdd(
|
|||
|
statistics,
|
|||
|
-(LONG)newPool->RequestedSize
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
ExFreePool( newPool );
|
|||
|
|
|||
|
} while( ++pentry < pend );
|
|||
|
|
|||
|
++(l->AllocMiss);
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
PPOOL_HEADER SRVFASTCALL
|
|||
|
SrvInterlockedFree( PPOOL_HEADER block )
|
|||
|
{
|
|||
|
PPOOL_HEADER *pentry = block->FreeList;
|
|||
|
PPOOL_HEADER *pend = pentry + LOOK_ASIDE_MAX_ELEMENTS;
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
block = (PPOOL_HEADER)InterlockedExchangePointer( pentry, block );
|
|||
|
|
|||
|
} while( block != NULL && ++pentry < pend );
|
|||
|
|
|||
|
return block;
|
|||
|
}
|
|||
|
|
|||
|
VOID SRVFASTCALL
|
|||
|
SrvClearLookAsideList( PLOOK_ASIDE_LIST l, VOID (SRVFASTCALL *FreeRoutine )( PVOID ) )
|
|||
|
{
|
|||
|
PPOOL_HEADER *pentry, *pend, block;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Clear out the list of large chunks
|
|||
|
//
|
|||
|
pentry = l->LargeFreeList;
|
|||
|
pend = pentry + LOOK_ASIDE_MAX_ELEMENTS;
|
|||
|
|
|||
|
do {
|
|||
|
block = NULL;
|
|||
|
block = (PPOOL_HEADER)InterlockedExchangePointer( pentry, block );
|
|||
|
|
|||
|
if( block != NULL ) {
|
|||
|
block->FreeList = NULL;
|
|||
|
FreeRoutine( block + 1 );
|
|||
|
}
|
|||
|
|
|||
|
} while( ++pentry < pend );
|
|||
|
|
|||
|
//
|
|||
|
// Clear out the list of small chunks
|
|||
|
//
|
|||
|
pentry = l->SmallFreeList;
|
|||
|
pend = pentry + LOOK_ASIDE_MAX_ELEMENTS;
|
|||
|
|
|||
|
do {
|
|||
|
block = NULL;
|
|||
|
block = (PPOOL_HEADER)InterlockedExchangePointer( pentry, block );
|
|||
|
|
|||
|
if( block != NULL ) {
|
|||
|
block->FreeList = NULL;
|
|||
|
FreeRoutine( block + 1 );
|
|||
|
}
|
|||
|
|
|||
|
} while( ++pentry < pend );
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
PVOID SRVFASTCALL
|
|||
|
SrvAllocateNonPagedPool (
|
|||
|
IN CLONG NumberOfBytes
|
|||
|
#ifdef POOL_TAGGING
|
|||
|
, IN CLONG BlockType
|
|||
|
#endif
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine allocates nonpaged pool in the server. A check is
|
|||
|
made to ensure that the server's total nonpaged pool usage is below
|
|||
|
the configurable limit.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
NumberOfBytes - the number of bytes to allocate.
|
|||
|
|
|||
|
BlockType - the type of block (used to pass pool tag to allocator)
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
PVOID - a pointer to the allocated memory or NULL if the memory could
|
|||
|
not be allocated.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PPOOL_HEADER newPool;
|
|||
|
PPOOL_HEADER *FreeList = NULL;
|
|||
|
ULONG newUsage;
|
|||
|
BOOLEAN IsLowPriority = FALSE;
|
|||
|
LARGE_INTEGER interval;
|
|||
|
|
|||
|
#ifdef POOL_TAGGING
|
|||
|
ASSERT( BlockType > 0 && BlockType < BlockTypeMax );
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Pull this allocation off the per-processor free list if we can
|
|||
|
//
|
|||
|
if( SrvWorkQueues ) {
|
|||
|
|
|||
|
PWORK_QUEUE queue = PROCESSOR_TO_QUEUE();
|
|||
|
|
|||
|
if( NumberOfBytes <= queue->NonPagedPoolLookAsideList.MaxSize ) {
|
|||
|
|
|||
|
newPool = SrvInterlockedAllocate(
|
|||
|
&queue->NonPagedPoolLookAsideList,
|
|||
|
NumberOfBytes,
|
|||
|
(PLONG)&SrvStatistics.CurrentNonPagedPoolUsage
|
|||
|
);
|
|||
|
|
|||
|
if( newPool != NULL ) {
|
|||
|
return newPool;
|
|||
|
}
|
|||
|
|
|||
|
FreeList = NumberOfBytes > LOOK_ASIDE_SWITCHOVER ?
|
|||
|
queue->NonPagedPoolLookAsideList.LargeFreeList :
|
|||
|
queue->NonPagedPoolLookAsideList.SmallFreeList ;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Account for this allocation in the statistics database and make
|
|||
|
// sure that this allocation will not put us over the limit of
|
|||
|
// nonpaged pool that we can allocate.
|
|||
|
//
|
|||
|
|
|||
|
newUsage = InterlockedExchangeAdd( (PLONG)&SrvStatistics.CurrentNonPagedPoolUsage,
|
|||
|
(LONG)NumberOfBytes );
|
|||
|
newUsage += NumberOfBytes;
|
|||
|
|
|||
|
if ( newUsage > SrvMaxNonPagedPoolUsage ) {
|
|||
|
|
|||
|
//
|
|||
|
// Count the failure, but do NOT log an event. The scavenger
|
|||
|
// will log an event when it next wakes up. This keeps us from
|
|||
|
// flooding the event log.
|
|||
|
//
|
|||
|
|
|||
|
SrvNonPagedPoolLimitHitCount++;
|
|||
|
SrvStatistics.NonPagedPoolFailures++;
|
|||
|
|
|||
|
InterlockedExchangeAdd( (PLONG)&SrvStatistics.CurrentNonPagedPoolUsage,
|
|||
|
-(LONG)NumberOfBytes );
|
|||
|
|
|||
|
return NULL;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (SrvStatistics.CurrentNonPagedPoolUsage > SrvStatistics.PeakNonPagedPoolUsage) {
|
|||
|
SrvStatistics.PeakNonPagedPoolUsage = SrvStatistics.CurrentNonPagedPoolUsage;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Do the actual memory allocation. Allocate extra space so that we
|
|||
|
// can store the size of the allocation for the free routine.
|
|||
|
//
|
|||
|
if( NumberOfBytes > 2*4096 )
|
|||
|
{
|
|||
|
IsLowPriority = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
newPool = ExAllocatePoolWithTagPriority(
|
|||
|
NonPagedPool,
|
|||
|
NumberOfBytes + sizeof(POOL_HEADER),
|
|||
|
TAG_FROM_TYPE(BlockType),
|
|||
|
IsLowPriority ? LowPoolPriority : NormalPoolPriority
|
|||
|
);
|
|||
|
|
|||
|
if( (newPool == NULL) && IsLowPriority && (KeGetCurrentIrql() <= APC_LEVEL) )
|
|||
|
{
|
|||
|
interval.QuadPart = SRV_LOW_PRIORITY_RETRY_TIME;
|
|||
|
InterlockedIncrement( &SrvMemoryAllocationRetries );
|
|||
|
|
|||
|
// Wait and try again
|
|||
|
KeDelayExecutionThread( KernelMode, FALSE, &interval );
|
|||
|
|
|||
|
newPool = ExAllocatePoolWithTagPriority(
|
|||
|
NonPagedPool,
|
|||
|
NumberOfBytes + sizeof(POOL_HEADER),
|
|||
|
TAG_FROM_TYPE(BlockType),
|
|||
|
LowPoolPriority
|
|||
|
);
|
|||
|
|
|||
|
if( newPool )
|
|||
|
{
|
|||
|
InterlockedIncrement( &SrvMemoryAllocationRetriesSuccessful );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// If the system couldn't satisfy the request, return NULL.
|
|||
|
//
|
|||
|
|
|||
|
if ( newPool != NULL ) {
|
|||
|
//
|
|||
|
// Save the size of this block in the extra space we allocated.
|
|||
|
//
|
|||
|
|
|||
|
newPool->RequestedSize = NumberOfBytes;
|
|||
|
newPool->FreeList = FreeList;
|
|||
|
|
|||
|
//
|
|||
|
// Return a pointer to the memory after the size longword.
|
|||
|
//
|
|||
|
|
|||
|
return (PVOID)( newPool + 1 );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Count the failure, but do NOT log an event. The scavenger
|
|||
|
// will log an event when it next wakes up. This keeps us from
|
|||
|
// flooding the event log.
|
|||
|
//
|
|||
|
|
|||
|
SrvStatistics.NonPagedPoolFailures++;
|
|||
|
|
|||
|
|
|||
|
InterlockedExchangeAdd( (PLONG)&SrvStatistics.CurrentNonPagedPoolUsage,
|
|||
|
-(LONG)NumberOfBytes );
|
|||
|
|
|||
|
return NULL;
|
|||
|
|
|||
|
} // SrvAllocateNonPagedPool
|
|||
|
|
|||
|
VOID SRVFASTCALL
|
|||
|
SrvFreeNonPagedPool (
|
|||
|
IN PVOID Address
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Frees the memory allocated by a call to SrvAllocateNonPagedPool.
|
|||
|
The statistics database is updated to reflect the current nonpaged
|
|||
|
pool usage.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Address - the address of allocated memory returned by
|
|||
|
SrvAllocateNonPagedPool.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PPOOL_HEADER actualBlock = (PPOOL_HEADER)Address - 1;
|
|||
|
|
|||
|
//
|
|||
|
// See if we can stash this bit of memory away in the NonPagedPoolFreeList
|
|||
|
//
|
|||
|
if( actualBlock->FreeList ) {
|
|||
|
|
|||
|
actualBlock = SrvInterlockedFree( actualBlock );
|
|||
|
}
|
|||
|
|
|||
|
if( actualBlock != NULL ) {
|
|||
|
|
|||
|
//
|
|||
|
// Update the nonpaged pool usage statistic.
|
|||
|
//
|
|||
|
InterlockedExchangeAdd(
|
|||
|
(PLONG)&SrvStatistics.CurrentNonPagedPoolUsage,
|
|||
|
-(LONG)actualBlock->RequestedSize
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Free the pool and return.
|
|||
|
//
|
|||
|
|
|||
|
ExFreePool( actualBlock );
|
|||
|
}
|
|||
|
|
|||
|
} // SrvFreeNonPagedPool
|
|||
|
|
|||
|
|
|||
|
PVOID SRVFASTCALL
|
|||
|
SrvAllocatePagedPool (
|
|||
|
IN POOL_TYPE PoolType,
|
|||
|
IN CLONG NumberOfBytes
|
|||
|
#ifdef POOL_TAGGING
|
|||
|
, IN CLONG BlockType
|
|||
|
#endif
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine allocates Paged pool in the server. A check is
|
|||
|
made to ensure that the server's total Paged pool usage is below
|
|||
|
the configurable limit.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
NumberOfBytes - the number of bytes to allocate.
|
|||
|
|
|||
|
BlockType - the type of block (used to pass pool tag to allocator)
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
PVOID - a pointer to the allocated memory or NULL if the memory could
|
|||
|
not be allocated.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PPOOL_HEADER newPool;
|
|||
|
PPOOL_HEADER *FreeList = NULL;
|
|||
|
ULONG newUsage;
|
|||
|
BOOLEAN IsLowPriority = FALSE;
|
|||
|
LARGE_INTEGER interval;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
#ifdef POOL_TAGGING
|
|||
|
ASSERT( BlockType > 0 && BlockType < BlockTypeMax );
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Pull this allocation off the per-processor free list if we can
|
|||
|
//
|
|||
|
if( SrvWorkQueues ) {
|
|||
|
|
|||
|
PWORK_QUEUE queue = PROCESSOR_TO_QUEUE();
|
|||
|
|
|||
|
if( NumberOfBytes <= queue->PagedPoolLookAsideList.MaxSize ) {
|
|||
|
|
|||
|
newPool = SrvInterlockedAllocate(
|
|||
|
&queue->PagedPoolLookAsideList,
|
|||
|
NumberOfBytes,
|
|||
|
(PLONG)&SrvStatistics.CurrentPagedPoolUsage
|
|||
|
);
|
|||
|
|
|||
|
if( newPool != NULL ) {
|
|||
|
return newPool;
|
|||
|
}
|
|||
|
|
|||
|
FreeList = NumberOfBytes > LOOK_ASIDE_SWITCHOVER ?
|
|||
|
queue->PagedPoolLookAsideList.LargeFreeList :
|
|||
|
queue->PagedPoolLookAsideList.SmallFreeList ;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Account for this allocation in the statistics database and make
|
|||
|
// sure that this allocation will not put us over the limit of
|
|||
|
// nonpaged pool that we can allocate.
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
newUsage = InterlockedExchangeAdd( (PLONG)&SrvStatistics.CurrentPagedPoolUsage,
|
|||
|
(LONG)NumberOfBytes );
|
|||
|
newUsage += NumberOfBytes;
|
|||
|
|
|||
|
if ( newUsage > SrvMaxPagedPoolUsage ) {
|
|||
|
|
|||
|
//
|
|||
|
// Count the failure, but do NOT log an event. The scavenger
|
|||
|
// will log an event when it next wakes up. This keeps us from
|
|||
|
// flooding the event log.
|
|||
|
//
|
|||
|
|
|||
|
SrvPagedPoolLimitHitCount++;
|
|||
|
SrvStatistics.PagedPoolFailures++;
|
|||
|
|
|||
|
InterlockedExchangeAdd( (PLONG)&SrvStatistics.CurrentPagedPoolUsage,
|
|||
|
-(LONG)NumberOfBytes );
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
if (SrvStatistics.CurrentPagedPoolUsage > SrvStatistics.PeakPagedPoolUsage ) {
|
|||
|
SrvStatistics.PeakPagedPoolUsage = SrvStatistics.CurrentPagedPoolUsage;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Do the actual memory allocation. Allocate extra space so that we
|
|||
|
// can store the size of the allocation for the free routine.
|
|||
|
//
|
|||
|
if( NumberOfBytes > 2*4096 )
|
|||
|
{
|
|||
|
IsLowPriority = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
newPool = ExAllocatePoolWithTagPriority(
|
|||
|
PoolType,
|
|||
|
NumberOfBytes + sizeof(POOL_HEADER),
|
|||
|
TAG_FROM_TYPE(BlockType),
|
|||
|
IsLowPriority ? LowPoolPriority : NormalPoolPriority
|
|||
|
);
|
|||
|
|
|||
|
if( (newPool == NULL) && IsLowPriority && (KeGetCurrentIrql() <= APC_LEVEL) )
|
|||
|
{
|
|||
|
interval.QuadPart = SRV_LOW_PRIORITY_RETRY_TIME;
|
|||
|
InterlockedIncrement( &SrvMemoryAllocationRetries );
|
|||
|
|
|||
|
// Wait and try again
|
|||
|
KeDelayExecutionThread( KernelMode, FALSE, &interval );
|
|||
|
|
|||
|
newPool = ExAllocatePoolWithTagPriority(
|
|||
|
PoolType,
|
|||
|
NumberOfBytes + sizeof(POOL_HEADER),
|
|||
|
TAG_FROM_TYPE(BlockType),
|
|||
|
LowPoolPriority
|
|||
|
);
|
|||
|
|
|||
|
if( newPool )
|
|||
|
{
|
|||
|
InterlockedIncrement( &SrvMemoryAllocationRetriesSuccessful );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if( newPool != NULL ) {
|
|||
|
|
|||
|
newPool->FreeList = FreeList;
|
|||
|
newPool->RequestedSize = NumberOfBytes;
|
|||
|
|
|||
|
//
|
|||
|
// Return a pointer to the memory after the POOL_HEADER
|
|||
|
//
|
|||
|
|
|||
|
return newPool + 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the system couldn't satisfy the request, return NULL.
|
|||
|
//
|
|||
|
// Count the failure, but do NOT log an event. The scavenger
|
|||
|
// will log an event when it next wakes up. This keeps us from
|
|||
|
// flooding the event log.
|
|||
|
//
|
|||
|
|
|||
|
SrvStatistics.PagedPoolFailures++;
|
|||
|
|
|||
|
InterlockedExchangeAdd( (PLONG)&SrvStatistics.CurrentPagedPoolUsage,
|
|||
|
-(LONG)NumberOfBytes );
|
|||
|
|
|||
|
|
|||
|
return NULL;
|
|||
|
|
|||
|
|
|||
|
} // SrvAllocatePagedPool
|
|||
|
|
|||
|
VOID SRVFASTCALL
|
|||
|
SrvFreePagedPool (
|
|||
|
IN PVOID Address
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Frees the memory allocated by a call to SrvAllocatePagedPool.
|
|||
|
The statistics database is updated to reflect the current Paged
|
|||
|
pool usage. If this routine is change, look at scavengr.c
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Address - the address of allocated memory returned by
|
|||
|
SrvAllocatePagedPool.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PPOOL_HEADER actualBlock = (PPOOL_HEADER)Address - 1;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ASSERT( actualBlock != NULL );
|
|||
|
|
|||
|
//
|
|||
|
// See if we can stash this bit of memory away in the PagedPoolFreeList
|
|||
|
//
|
|||
|
if( actualBlock->FreeList ) {
|
|||
|
|
|||
|
actualBlock = SrvInterlockedFree( actualBlock );
|
|||
|
}
|
|||
|
|
|||
|
if( actualBlock != NULL ) {
|
|||
|
|
|||
|
//
|
|||
|
// Update the Paged pool usage statistic.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( SrvStatistics.CurrentPagedPoolUsage >= actualBlock->RequestedSize );
|
|||
|
|
|||
|
InterlockedExchangeAdd(
|
|||
|
(PLONG)&SrvStatistics.CurrentPagedPoolUsage,
|
|||
|
-(LONG)actualBlock->RequestedSize
|
|||
|
);
|
|||
|
|
|||
|
ASSERT( (LONG)SrvStatistics.CurrentPagedPoolUsage >= 0 );
|
|||
|
|
|||
|
//
|
|||
|
// Free the pool and return.
|
|||
|
//
|
|||
|
|
|||
|
ExFreePool( actualBlock );
|
|||
|
}
|
|||
|
|
|||
|
} // SrvFreePagedPool
|