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.
1475 lines
36 KiB
1475 lines
36 KiB
/*
|
|
|
|
Copyright (c) 1992 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
memory.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the routines which allocates and free memory - both
|
|
paged and non-paged.
|
|
|
|
Author:
|
|
|
|
Jameel Hyder (microsoft!jameelh)
|
|
|
|
|
|
Revision History:
|
|
25 Apr 1992 Initial Version
|
|
11 Mar 1993 SueA - Fixed AfpAllocUnicodeString to expect byte count,
|
|
not char count
|
|
Notes: Tab stop: 4
|
|
--*/
|
|
|
|
#define FILENUM FILE_MEMORY
|
|
|
|
#define AFPMEM_LOCALS
|
|
#include <afp.h>
|
|
#include <iopool.h>
|
|
#include <scavengr.h>
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text( INIT, AfpMemoryInit)
|
|
#pragma alloc_text( PAGE, AfpMemoryDeInit)
|
|
#endif
|
|
|
|
|
|
/*** AfpMemoryInit
|
|
*
|
|
* Initialize the IO Pool system.
|
|
*/
|
|
NTSTATUS
|
|
AfpMemoryInit(
|
|
VOID
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
INITIALIZE_SPIN_LOCK(&afpIoPoolLock);
|
|
|
|
Status = AfpScavengerScheduleEvent(afpIoPoolAge,
|
|
NULL,
|
|
POOL_AGE_TIME,
|
|
False);
|
|
return Status;
|
|
}
|
|
|
|
/*** AfpMemoryDeInit
|
|
*
|
|
* Free any IO pool buffers.
|
|
*/
|
|
VOID
|
|
AfpMemoryDeInit(
|
|
VOID
|
|
)
|
|
{
|
|
PIOPOOL pPool, pFree;
|
|
|
|
for (pPool = afpIoPoolHead;
|
|
pPool != NULL;)
|
|
{
|
|
ASSERT(VALID_IOP(pPool));
|
|
pFree = pPool;
|
|
pPool = pPool->iop_Next;
|
|
ASSERT (pFree->iop_NumFreeBufs == NUM_BUFS_IN_POOL);
|
|
ASSERT (pFree->iop_NumFreeLocks == NUM_LOCKS_IN_POOL);
|
|
AfpFreeMemory(pFree);
|
|
}
|
|
}
|
|
|
|
/*** AfpAllocMemory
|
|
*
|
|
* Allocate a block of memory from either the paged or the non-paged pool
|
|
* based on the memory tag. This is just a wrapper over ExAllocatePool.
|
|
* Allocation failures are error-logged. We always allocate a DWORD more
|
|
* than the specified size to accomodate the size. This is used by
|
|
* AfpFreeMemory to update the statistics.
|
|
*
|
|
* While we are debugging, we also pad the block with a signature and test
|
|
* it when we free it. This detects memory overrun.
|
|
*/
|
|
PBYTE FASTCALL
|
|
AfpAllocMemory(
|
|
#ifdef TRACK_MEMORY_USAGE
|
|
IN LONG Size,
|
|
IN DWORD FileLine
|
|
#else
|
|
IN LONG Size
|
|
#endif
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
PBYTE Buffer;
|
|
DWORD OldMaxUsage;
|
|
POOL_TYPE PoolType;
|
|
PDWORD pCurUsage, pMaxUsage, pCount, pLimit;
|
|
PDWORD pDwordBuf;
|
|
BOOLEAN Zeroed;
|
|
#if DBG
|
|
DWORD Signature;
|
|
#endif
|
|
#ifdef PROFILING
|
|
TIME TimeS1, TimeS2, TimeE, TimeD;
|
|
|
|
AfpGetPerfCounter(&TimeS1);
|
|
#endif
|
|
|
|
ASSERT ((Size & ~MEMORY_TAG_MASK) < MAXIMUM_ALLOC_SIZE);
|
|
|
|
// Make sure that this allocation will not put us over the limit
|
|
// of paged/non-paged pool that we can allocate.
|
|
//
|
|
// Account for this allocation in the statistics database.
|
|
|
|
Zeroed = False;
|
|
if (Size & ZEROED_MEMORY_TAG)
|
|
{
|
|
Zeroed = True;
|
|
Size &= ~ZEROED_MEMORY_TAG;
|
|
}
|
|
|
|
if (Size & NON_PAGED_MEMORY_TAG)
|
|
{
|
|
PoolType = NonPagedPool;
|
|
pCurUsage = &AfpServerStatistics.stat_CurrNonPagedUsage;
|
|
pMaxUsage = &AfpServerStatistics.stat_MaxNonPagedUsage;
|
|
pCount = &AfpServerStatistics.stat_NonPagedCount;
|
|
pLimit = &AfpNonPagedPoolLimit;
|
|
#if DBG
|
|
Signature = NONPAGED_BLOCK_SIGNATURE;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
ASSERT (Size & PAGED_MEMORY_TAG);
|
|
PoolType = PagedPool;
|
|
pCurUsage = &AfpServerStatistics.stat_CurrPagedUsage;
|
|
pMaxUsage = &AfpServerStatistics.stat_MaxPagedUsage;
|
|
pCount = &AfpServerStatistics.stat_PagedCount;
|
|
pLimit = &AfpPagedPoolLimit;
|
|
#if DBG
|
|
Signature = PAGED_BLOCK_SIGNATURE;
|
|
#endif
|
|
}
|
|
|
|
Size &= ~MEMORY_TAG_MASK;
|
|
|
|
if (Size == 0 )
|
|
{
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_ERR,
|
|
("afpAllocMemory: Alloc for 0 bytes!\n"));
|
|
ASSERT(0);
|
|
return(NULL);
|
|
}
|
|
|
|
Size = DWORDSIZEBLOCK(Size) +
|
|
#if DBG
|
|
sizeof(DWORD) + // For the signature
|
|
#endif
|
|
LONGLONGSIZEBLOCK(sizeof(TAG));
|
|
|
|
ACQUIRE_SPIN_LOCK(&AfpStatisticsLock, &OldIrql);
|
|
|
|
*pCurUsage += Size;
|
|
(*pCount) ++;
|
|
|
|
OldMaxUsage = *pMaxUsage;
|
|
if (*pCurUsage > *pMaxUsage)
|
|
*pMaxUsage = *pCurUsage;
|
|
|
|
RELEASE_SPIN_LOCK(&AfpStatisticsLock, OldIrql);
|
|
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeS2);
|
|
#endif
|
|
|
|
// Do the actual memory allocation. Allocate four extra bytes so
|
|
// that we can store the size of the allocation for the free routine.
|
|
if ((Buffer = ExAllocatePoolWithTag(PoolType, Size, AFP_TAG)) == NULL)
|
|
{
|
|
ACQUIRE_SPIN_LOCK(&AfpStatisticsLock, &OldIrql);
|
|
|
|
*pCurUsage -= Size;
|
|
*pMaxUsage = OldMaxUsage;
|
|
|
|
RELEASE_SPIN_LOCK(&AfpStatisticsLock, OldIrql);
|
|
AFPLOG_DDERROR(AFPSRVMSG_PAGED_POOL, STATUS_NO_MEMORY, &Size,
|
|
sizeof(Size), NULL);
|
|
|
|
DBGPRINT(DBG_COMP_SDA, DBG_LEVEL_ERR,
|
|
("AfpAllocMemory: ExAllocatePool returned NULL for %d bytes\n",Size));
|
|
|
|
return NULL;
|
|
}
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeE);
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS2.QuadPart;
|
|
if (PoolType == NonPagedPool)
|
|
{
|
|
INTERLOCKED_INCREMENT_LONG(AfpServerProfile->perf_ExAllocCountN);
|
|
|
|
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_ExAllocTimeN,
|
|
TimeD,
|
|
&AfpStatisticsLock);
|
|
else
|
|
{
|
|
INTERLOCKED_INCREMENT_LONG(AfpServerProfile->perf_ExAllocCountP);
|
|
|
|
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_ExAllocTimeP,
|
|
TimeD,
|
|
&AfpStatisticsLock);
|
|
}
|
|
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS1.QuadPart;
|
|
if (PoolType == NonPagedPool)
|
|
{
|
|
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_AfpAllocCountN);
|
|
}
|
|
else
|
|
{
|
|
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_AfpAllocTimeP,
|
|
TimeD,
|
|
&AfpStatisticsLock);
|
|
}
|
|
#endif
|
|
|
|
// Save the size of this block along with the tag in the extra space we allocated.
|
|
pDwordBuf = (PDWORD)Buffer;
|
|
|
|
#if DBG
|
|
*pDwordBuf = 0xCAFEBEAD;
|
|
#endif
|
|
// skip past the unused dword (it's only use in life is to get the buffer quad-aligned!)
|
|
pDwordBuf++;
|
|
|
|
((PTAG)pDwordBuf)->tg_Size = Size;
|
|
((PTAG)pDwordBuf)->tg_Tag = (PoolType == PagedPool) ? PGD_MEM_TAG : NPG_MEM_TAG;
|
|
|
|
#if DBG
|
|
// Write the signature at the end
|
|
*(PDWORD)((PBYTE)Buffer + Size - sizeof(DWORD)) = Signature;
|
|
#endif
|
|
|
|
#ifdef TRACK_MEMORY_USAGE
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_INFO,
|
|
("AfpAllocMemory: %lx Allocated %lx bytes @%lx\n",
|
|
*(PDWORD)((PBYTE)(&Size) - sizeof(Size)), Size, Buffer));
|
|
AfpTrackMemoryUsage(Buffer, True, (BOOLEAN)(PoolType == PagedPool), FileLine);
|
|
#endif
|
|
|
|
// Return a pointer to the memory after the tag. Clear the memory, if requested
|
|
//
|
|
// We need the memory to be quad-aligned, so even though sizeof(TAG) is 4, we skip
|
|
// LONGLONG..., which is 8. The 1st dword is unused (for now?), the 2nd dword is the TAG
|
|
|
|
Buffer += LONGLONGSIZEBLOCK(sizeof(TAG));
|
|
|
|
Size -= LONGLONGSIZEBLOCK(sizeof(TAG));
|
|
|
|
if (Zeroed)
|
|
{
|
|
#if DBG
|
|
RtlZeroMemory(Buffer, Size - sizeof(DWORD));
|
|
#else
|
|
RtlZeroMemory(Buffer, Size);
|
|
#endif
|
|
}
|
|
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
|
|
/*** AfpAllocNonPagedLowPriority
|
|
*
|
|
* Allocate a block of non-paged memory with a low priority
|
|
*/
|
|
PBYTE FASTCALL
|
|
AfpAllocNonPagedLowPriority(
|
|
#ifdef TRACK_MEMORY_USAGE
|
|
IN LONG Size,
|
|
IN DWORD FileLine
|
|
#else
|
|
IN LONG Size
|
|
#endif
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
PBYTE Buffer;
|
|
DWORD OldMaxUsage;
|
|
POOL_TYPE PoolType;
|
|
PDWORD pCurUsage, pMaxUsage, pCount, pLimit;
|
|
PDWORD pDwordBuf;
|
|
#if DBG
|
|
DWORD Signature;
|
|
#endif
|
|
#ifdef PROFILING
|
|
TIME TimeS1, TimesS2, TimeE, TimeD;
|
|
|
|
AfpGetPerfCounter(&TimeS1);
|
|
#endif
|
|
|
|
ASSERT ((Size & ~MEMORY_TAG_MASK) < MAXIMUM_ALLOC_SIZE);
|
|
|
|
PoolType = NonPagedPool;
|
|
pCurUsage = &AfpServerStatistics.stat_CurrNonPagedUsage;
|
|
pMaxUsage = &AfpServerStatistics.stat_MaxNonPagedUsage;
|
|
pCount = &AfpServerStatistics.stat_NonPagedCount;
|
|
pLimit = &AfpNonPagedPoolLimit;
|
|
|
|
#if DBG
|
|
Signature = NONPAGED_BLOCK_SIGNATURE;
|
|
#endif
|
|
|
|
Size &= ~MEMORY_TAG_MASK;
|
|
Size = DWORDSIZEBLOCK(Size) +
|
|
#if DBG
|
|
sizeof(DWORD) + // For the signature
|
|
#endif
|
|
LONGLONGSIZEBLOCK(sizeof(TAG));
|
|
|
|
ACQUIRE_SPIN_LOCK(&AfpStatisticsLock, &OldIrql);
|
|
|
|
*pCurUsage += Size;
|
|
(*pCount) ++;
|
|
|
|
|
|
OldMaxUsage = *pMaxUsage;
|
|
if (*pCurUsage > *pMaxUsage)
|
|
*pMaxUsage = *pCurUsage;
|
|
|
|
RELEASE_SPIN_LOCK(&AfpStatisticsLock, OldIrql);
|
|
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeS2);
|
|
#endif
|
|
|
|
// Do the actual memory allocation. Allocate four extra bytes so
|
|
// that we can store the size of the allocation for the free routine.
|
|
|
|
Buffer = ExAllocatePoolWithTagPriority(PoolType,
|
|
Size,
|
|
AFP_TAG,
|
|
LowPoolPriority);
|
|
|
|
if (Buffer == NULL)
|
|
{
|
|
ASSERT(0);
|
|
|
|
ACQUIRE_SPIN_LOCK(&AfpStatisticsLock, &OldIrql);
|
|
|
|
*pCurUsage -= Size;
|
|
*pMaxUsage = OldMaxUsage;
|
|
|
|
RELEASE_SPIN_LOCK(&AfpStatisticsLock, OldIrql);
|
|
AFPLOG_DDERROR(AFPSRVMSG_PAGED_POOL, STATUS_NO_MEMORY, &Size,
|
|
sizeof(Size), NULL);
|
|
return NULL;
|
|
}
|
|
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeE);
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS2.QuadPart;
|
|
|
|
INTERLOCKED_INCREMENT_LONG(AfpServerProfile->perf_ExAllocCountN);
|
|
|
|
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_ExAllocTimeN,
|
|
TimeD,
|
|
&AfpStatisticsLock);
|
|
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS1.QuadPart;
|
|
|
|
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_AfpAllocCountN);
|
|
#endif
|
|
|
|
// Save the size of this block along with the tag in the extra space we allocated.
|
|
pDwordBuf = (PDWORD)Buffer;
|
|
|
|
#if DBG
|
|
*pDwordBuf = 0xCAFEBEAD;
|
|
#endif
|
|
// skip past the unused dword (it's only use in life is to get the buffer quad-aligned!)
|
|
pDwordBuf++;
|
|
|
|
((PTAG)pDwordBuf)->tg_Size = Size;
|
|
((PTAG)pDwordBuf)->tg_Tag = (PoolType == PagedPool) ? PGD_MEM_TAG : NPG_MEM_TAG;
|
|
|
|
#if DBG
|
|
// Write the signature at the end
|
|
*(PDWORD)((PBYTE)Buffer + Size - sizeof(DWORD)) = Signature;
|
|
#endif
|
|
|
|
#ifdef TRACK_MEMORY_USAGE
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_INFO,
|
|
("AfpAllocMemory: %lx Allocated %lx bytes @%lx\n",
|
|
*(PDWORD)((PBYTE)(&Size) - sizeof(Size)), Size, Buffer));
|
|
AfpTrackMemoryUsage(Buffer, True, (BOOLEAN)(PoolType == PagedPool), FileLine);
|
|
#endif
|
|
|
|
// Return a pointer to the memory after the tag. Clear the memory, if requested
|
|
//
|
|
// We need the memory to be quad-aligned, so even though sizeof(TAG) is 4, we skip
|
|
// LONGLONG..., which is 8. The 1st dword is unused (for now?), the 2nd dword is the TAG
|
|
|
|
Buffer += LONGLONGSIZEBLOCK(sizeof(TAG));
|
|
|
|
Size -= LONGLONGSIZEBLOCK(sizeof(TAG));
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
/*** AfpFreeMemory
|
|
*
|
|
* Free the block of memory allocated via AfpAllocMemory.
|
|
* This is a wrapper around ExFreePool.
|
|
*/
|
|
VOID FASTCALL
|
|
AfpFreeMemory(
|
|
IN PVOID pBuffer
|
|
)
|
|
{
|
|
BOOLEAN Paged = False;
|
|
DWORD Size;
|
|
DWORD numDwords;
|
|
PDWORD pDwordBuf;
|
|
PTAG pTag;
|
|
DWORD i;
|
|
#ifdef PROFILING
|
|
TIME TimeS1, TimeS2, TimeE, TimeD1, TimeD2;
|
|
|
|
AfpGetPerfCounter(&TimeS1);
|
|
#endif
|
|
|
|
|
|
//
|
|
// Get a pointer to the block allocated by ExAllocatePool.
|
|
//
|
|
pTag = (PTAG)((PBYTE)pBuffer - sizeof(TAG));
|
|
Size = pTag->tg_Size;
|
|
|
|
if (pTag->tg_Tag == IO_POOL_TAG)
|
|
{
|
|
AfpIOFreeBuffer(pBuffer);
|
|
return;
|
|
}
|
|
|
|
pBuffer = ((PBYTE)pBuffer - LONGLONGSIZEBLOCK(sizeof(TAG)));
|
|
|
|
if (pTag->tg_Tag == PGD_MEM_TAG)
|
|
Paged = True;
|
|
|
|
#if DBG
|
|
{
|
|
DWORD Signature;
|
|
|
|
// Check the signature at the end
|
|
Signature = (Paged) ? PAGED_BLOCK_SIGNATURE : NONPAGED_BLOCK_SIGNATURE;
|
|
|
|
if (*(PDWORD)((PBYTE)pBuffer + Size - sizeof(DWORD)) != Signature)
|
|
{
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_FATAL,
|
|
("AfpFreeMemory: Memory Overrun\n"));
|
|
DBGBRK(DBG_LEVEL_FATAL);
|
|
return;
|
|
}
|
|
// Clear the signature
|
|
*(PDWORD)((PBYTE)pBuffer + Size - sizeof(DWORD)) -= 1;
|
|
|
|
// change the memory so that we can catch weirdnesses like using freed memory
|
|
numDwords = (Size/sizeof(DWORD));
|
|
numDwords -= 3; // 2 dwords at the beginning and 1 at the end
|
|
|
|
pDwordBuf = (PULONG)pBuffer;
|
|
*pDwordBuf++ = 0xABABABAB; // to indicate that it's freed!
|
|
pDwordBuf++; // skip past the tag
|
|
for (i=0; i<numDwords; i++,pDwordBuf++)
|
|
{
|
|
*pDwordBuf = 0x55667788;
|
|
}
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
#ifdef TRACK_MEMORY_USAGE
|
|
AfpTrackMemoryUsage(pBuffer, False, Paged, 0);
|
|
#endif
|
|
|
|
//
|
|
// Update the pool usage statistic.
|
|
//
|
|
if (Paged)
|
|
{
|
|
INTERLOCKED_ADD_ULONG(&AfpServerStatistics.stat_CurrPagedUsage,
|
|
(ULONG)(-(LONG)Size),
|
|
&AfpStatisticsLock);
|
|
INTERLOCKED_ADD_ULONG(&AfpServerStatistics.stat_PagedCount,
|
|
(ULONG)-1,
|
|
&AfpStatisticsLock);
|
|
}
|
|
else
|
|
{
|
|
INTERLOCKED_ADD_ULONG(&AfpServerStatistics.stat_CurrNonPagedUsage,
|
|
(ULONG)(-(LONG)Size),
|
|
&AfpStatisticsLock);
|
|
INTERLOCKED_ADD_ULONG(&AfpServerStatistics.stat_NonPagedCount,
|
|
(ULONG)-1,
|
|
&AfpStatisticsLock);
|
|
}
|
|
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeS2);
|
|
#endif
|
|
|
|
// Free the pool and return.
|
|
ExFreePool(pBuffer);
|
|
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeE);
|
|
TimeD2.QuadPart = TimeE.QuadPart - TimeS2.QuadPart;
|
|
if (Paged)
|
|
{
|
|
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_ExFreeCountP);
|
|
|
|
INTERLOCKED_ADD_LARGE_INTGR(AfpServerProfile->perf_ExFreeTimeP,
|
|
TimeD2,
|
|
&AfpStatisticsLock);
|
|
TimeD1.QuadPart = TimeE.QuadPart - TimeS1.QuadPart;
|
|
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_AfpFreeCountP);
|
|
|
|
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_AfpFreeTimeP,
|
|
TimeD1,
|
|
&AfpStatisticsLock);
|
|
}
|
|
else
|
|
{
|
|
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_ExFreeCountN);
|
|
|
|
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_ExFreeTimeN,
|
|
TimeD2,
|
|
&AfpStatisticsLock);
|
|
TimeD1.QuadPart = TimeE.QuadPart - TimeS1.QuadPart;
|
|
INTERLOCKED_INCREMENT_LONG(&AfpServerProfile->perf_AfpFreeCountN);
|
|
|
|
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_AfpFreeTimeN,
|
|
TimeD1,
|
|
&AfpStatisticsLock);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
/*** AfpAllocPAMemory
|
|
*
|
|
* Similar to AfpAllocMemory, except that this allocates page-aligned/page-granular memory.
|
|
*/
|
|
PBYTE FASTCALL
|
|
AfpAllocPAMemory(
|
|
#ifdef TRACK_MEMORY_USAGE
|
|
IN LONG Size,
|
|
IN DWORD FileLine
|
|
#else
|
|
IN LONG Size
|
|
#endif
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
PBYTE Buffer;
|
|
DWORD OldMaxUsage;
|
|
POOL_TYPE PoolType;
|
|
PDWORD pCurUsage, pMaxUsage, pCount, pLimit;
|
|
BOOLEAN PageAligned;
|
|
#if DBG
|
|
DWORD Signature;
|
|
#endif
|
|
#ifdef PROFILING
|
|
TIME TimeS1, TimeS2, TimeE, TimeD;
|
|
|
|
AfpGetPerfCounter(&TimeS1);
|
|
#endif
|
|
|
|
ASSERT ((Size & ~MEMORY_TAG_MASK) < MAXIMUM_ALLOC_SIZE);
|
|
|
|
ASSERT (((Size & ~MEMORY_TAG_MASK) % PAGE_SIZE) == 0);
|
|
|
|
//
|
|
// Make sure that this allocation will not put us over the limit
|
|
// of paged/non-paged pool that we can allocate.
|
|
//
|
|
// Account for this allocation in the statistics database.
|
|
|
|
if (Size & NON_PAGED_MEMORY_TAG)
|
|
{
|
|
PoolType = NonPagedPool;
|
|
pCurUsage = &AfpServerStatistics.stat_CurrNonPagedUsage;
|
|
pMaxUsage = &AfpServerStatistics.stat_MaxNonPagedUsage;
|
|
pCount = &AfpServerStatistics.stat_NonPagedCount;
|
|
pLimit = &AfpNonPagedPoolLimit;
|
|
#if DBG
|
|
Signature = NONPAGED_BLOCK_SIGNATURE;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
ASSERT (Size & PAGED_MEMORY_TAG);
|
|
PoolType = PagedPool;
|
|
pCurUsage = &AfpServerStatistics.stat_CurrPagedUsage;
|
|
pMaxUsage = &AfpServerStatistics.stat_MaxPagedUsage;
|
|
pCount = &AfpServerStatistics.stat_PagedCount;
|
|
pLimit = &AfpPagedPoolLimit;
|
|
#if DBG
|
|
Signature = PAGED_BLOCK_SIGNATURE;
|
|
#endif
|
|
}
|
|
|
|
Size &= ~MEMORY_TAG_MASK;
|
|
|
|
ACQUIRE_SPIN_LOCK(&AfpStatisticsLock, &OldIrql);
|
|
|
|
*pCurUsage += Size;
|
|
(*pCount) ++;
|
|
|
|
// apparently very old code, added to track some problem: not needed anymore
|
|
#if 0
|
|
#if DBG
|
|
// Make sure that this allocation will not put us over the limit
|
|
// of paged pool that we can allocate. ONLY FOR CHECKED BUILDS NOW.
|
|
|
|
if (*pCurUsage > *pLimit)
|
|
{
|
|
*pCurUsage -= Size;
|
|
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_ERR,
|
|
("afpAllocMemory: %sPaged Allocation exceeds limits %lx/%lx\n",
|
|
(PoolType == NonPagedPool) ? "Non" : "", Size, *pLimit));
|
|
|
|
RELEASE_SPIN_LOCK(&AfpStatisticsLock, OldIrql);
|
|
|
|
DBGBRK(DBG_LEVEL_FATAL);
|
|
|
|
AFPLOG_DDERROR((PoolType == PagedPool) ?
|
|
AFPSRVMSG_PAGED_POOL : AFPSRVMSG_NONPAGED_POOL,
|
|
STATUS_NO_MEMORY,
|
|
NULL,
|
|
0,
|
|
NULL);
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
#endif // #if 0
|
|
|
|
OldMaxUsage = *pMaxUsage;
|
|
if (*pCurUsage > *pMaxUsage)
|
|
*pMaxUsage = *pCurUsage;
|
|
|
|
RELEASE_SPIN_LOCK(&AfpStatisticsLock, OldIrql);
|
|
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeS2);
|
|
#endif
|
|
|
|
// Do the actual memory allocation.
|
|
if ((Buffer = ExAllocatePoolWithTag(PoolType, Size, AFP_TAG)) == NULL)
|
|
{
|
|
ACQUIRE_SPIN_LOCK(&AfpStatisticsLock, &OldIrql);
|
|
|
|
*pCurUsage -= Size;
|
|
*pMaxUsage = OldMaxUsage;
|
|
|
|
RELEASE_SPIN_LOCK(&AfpStatisticsLock, OldIrql);
|
|
AFPLOG_DDERROR(AFPSRVMSG_PAGED_POOL, STATUS_NO_MEMORY, &Size,
|
|
sizeof(Size), NULL);
|
|
#if DBG
|
|
DbgBreakPoint();
|
|
#endif
|
|
return NULL;
|
|
}
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeE);
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS2.QuadPart;
|
|
INTERLOCKED_INCREMENT_LONG((PoolType == NonPagedPool) ?
|
|
&AfpServerProfile->perf_ExAllocCountN :
|
|
&AfpServerProfile->perf_ExAllocCountP);
|
|
|
|
INTERLOCKED_ADD_LARGE_INTGR((PoolType == NonPagedPool) ?
|
|
&AfpServerProfile->perf_ExAllocTimeN :
|
|
&AfpServerProfile->perf_ExAllocTimeP,
|
|
TimeD,
|
|
&AfpStatisticsLock);
|
|
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS1.QuadPart;
|
|
INTERLOCKED_INCREMENT_LONG((PoolType == NonPagedPool) ?
|
|
&AfpServerProfile->perf_AfpAllocCountN :
|
|
&AfpServerProfile->perf_AfpAllocCountP);
|
|
|
|
INTERLOCKED_ADD_LARGE_INTGR((PoolType == NonPagedPool) ?
|
|
&AfpServerProfile->perf_AfpAllocTimeN :
|
|
&AfpServerProfile->perf_AfpAllocTimeP,
|
|
TimeD,
|
|
&AfpStatisticsLock);
|
|
#endif
|
|
|
|
#ifdef TRACK_MEMORY_USAGE
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_INFO,
|
|
("AfpAllocMemory: %lx Allocated %lx bytes @%lx\n",
|
|
*(PDWORD)((PBYTE)(&Size) - sizeof(Size)), Size, Buffer));
|
|
AfpTrackMemoryUsage(Buffer, True, (BOOLEAN)(PoolType == PagedPool), FileLine);
|
|
#endif
|
|
|
|
return Buffer;
|
|
}
|
|
|
|
|
|
/*** AfpFreePAMemory
|
|
*
|
|
* Free the block of memory allocated via AfpAllocPAMemory.
|
|
*/
|
|
VOID FASTCALL
|
|
AfpFreePAMemory(
|
|
IN PVOID pBuffer,
|
|
IN DWORD Size
|
|
)
|
|
{
|
|
BOOLEAN Paged = True;
|
|
#ifdef PROFILING
|
|
TIME TimeS1, TimeS2, TimeE, TimeD;
|
|
|
|
AfpGetPerfCounter(&TimeS1);
|
|
#endif
|
|
|
|
ASSERT ((Size & ~MEMORY_TAG_MASK) < MAXIMUM_ALLOC_SIZE);
|
|
|
|
ASSERT (((Size & ~MEMORY_TAG_MASK) % PAGE_SIZE) == 0);
|
|
|
|
if (Size & NON_PAGED_MEMORY_TAG)
|
|
Paged = False;
|
|
|
|
#ifdef TRACK_MEMORY_USAGE
|
|
AfpTrackMemoryUsage(pBuffer, False, Paged, 0);
|
|
#endif
|
|
|
|
//
|
|
// Update the pool usage statistic.
|
|
//
|
|
Size &= ~(NON_PAGED_MEMORY_TAG | PAGED_MEMORY_TAG);
|
|
if (Paged)
|
|
{
|
|
INTERLOCKED_ADD_ULONG(&AfpServerStatistics.stat_CurrPagedUsage,
|
|
(ULONG)(-(LONG)Size),
|
|
&AfpStatisticsLock);
|
|
INTERLOCKED_ADD_ULONG(&AfpServerStatistics.stat_PagedCount,
|
|
(ULONG)-1,
|
|
&AfpStatisticsLock);
|
|
}
|
|
else
|
|
{
|
|
INTERLOCKED_ADD_ULONG(&AfpServerStatistics.stat_CurrNonPagedUsage,
|
|
(ULONG)(-(LONG)Size),
|
|
&AfpStatisticsLock);
|
|
INTERLOCKED_ADD_ULONG(&AfpServerStatistics.stat_NonPagedCount,
|
|
(ULONG)-1,
|
|
&AfpStatisticsLock);
|
|
}
|
|
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeS2);
|
|
#endif
|
|
|
|
// Free the pool and return.
|
|
ExFreePool(pBuffer);
|
|
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeE);
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS2.QuadPart;
|
|
INTERLOCKED_INCREMENT_LONG( Paged ?
|
|
&AfpServerProfile->perf_ExFreeCountP :
|
|
&AfpServerProfile->perf_ExFreeCountN);
|
|
|
|
INTERLOCKED_ADD_LARGE_INTGR(Paged ?
|
|
&AfpServerProfile->perf_ExFreeTimeP :
|
|
&AfpServerProfile->perf_ExFreeTimeN,
|
|
TimeD,
|
|
&AfpStatisticsLock);
|
|
TimeD1.QuadPart = TimeE.QuadPart - TimeS1.QuadPart;
|
|
INTERLOCKED_INCREMENT_LONG( Paged ?
|
|
&AfpServerProfile->perf_AfpFreeCountP :
|
|
&AfpServerProfile->perf_AfpFreeCountN);
|
|
|
|
INTERLOCKED_ADD_LARGE_INTGR(Paged ?
|
|
&AfpServerProfile->perf_AfpFreeTimeP :
|
|
&AfpServerProfile->perf_AfpFreeTimeN,
|
|
TimeD,
|
|
&AfpStatisticsLock);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*** AfpAllocIrp
|
|
*
|
|
* This is a wrapper over IoAllocateIrp. We also do some book-keeping.
|
|
*/
|
|
PIRP FASTCALL
|
|
AfpAllocIrp(
|
|
IN CCHAR StackSize
|
|
)
|
|
{
|
|
PIRP pIrp;
|
|
|
|
if ((pIrp = IoAllocateIrp(StackSize, False)) == NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_INFO,
|
|
("afpAllocIrp: Allocation failed\n"));
|
|
if (KeGetCurrentIrql() < DISPATCH_LEVEL)
|
|
{
|
|
AFPLOG_ERROR(AFPSRVMSG_ALLOC_IRP, STATUS_INSUFFICIENT_RESOURCES,
|
|
NULL, 0, NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef PROFILING
|
|
INTERLOCKED_INCREMENT_LONG((PLONG)&AfpServerProfile->perf_cAllocatedIrps);
|
|
#endif
|
|
AFP_DBG_INC_COUNT(AfpDbgIrpsAlloced);
|
|
}
|
|
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_INFO,
|
|
("AfAllocIrp: Allocated Irp %lx\n", pIrp));
|
|
return pIrp;
|
|
}
|
|
|
|
|
|
/*** AfpFreeIrp
|
|
*
|
|
* This is a wrapper over IoFreeIrp. We also do some book-keeping.
|
|
*/
|
|
VOID FASTCALL
|
|
AfpFreeIrp(
|
|
IN PIRP pIrp
|
|
)
|
|
{
|
|
ASSERT (pIrp != NULL);
|
|
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_INFO,
|
|
("AfFreeIrp: Freeing Irp %lx\n", pIrp));
|
|
IoFreeIrp(pIrp);
|
|
|
|
AFP_DBG_DEC_COUNT(AfpDbgIrpsAlloced);
|
|
|
|
#ifdef PROFILING
|
|
INTERLOCKED_DECREMENT_LONG((PLONG)&AfpServerProfile->perf_cAllocatedIrps);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*** AfpAllocMdl
|
|
*
|
|
* This is a wrapper over IoAllocateMdl. We also do some book-keeping.
|
|
*/
|
|
PMDL FASTCALL
|
|
AfpAllocMdl(
|
|
IN PVOID pBuffer,
|
|
IN DWORD Size,
|
|
IN PIRP pIrp
|
|
)
|
|
{
|
|
PMDL pMdl;
|
|
|
|
if ((pMdl = IoAllocateMdl(pBuffer, Size, False, False, pIrp)) == NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_INFO,
|
|
("AfpAllocMdl: Allocation failed\n"));
|
|
AFPLOG_ERROR(AFPSRVMSG_ALLOC_MDL, STATUS_INSUFFICIENT_RESOURCES,
|
|
NULL, 0, NULL);
|
|
}
|
|
else
|
|
{
|
|
#ifdef PROFILING
|
|
INTERLOCKED_INCREMENT_LONG((PLONG)&AfpServerProfile->perf_cAllocatedMdls);
|
|
#endif
|
|
AFP_DBG_INC_COUNT(AfpDbgMdlsAlloced);
|
|
MmBuildMdlForNonPagedPool(pMdl);
|
|
}
|
|
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_INFO,
|
|
("AfAllocMdl: Allocated Mdl %lx\n", pMdl));
|
|
return pMdl;
|
|
}
|
|
|
|
|
|
/*** AfpFreeMdl
|
|
*
|
|
* This is a wrapper over IoFreeMdl. We also do some book-keeping.
|
|
*/
|
|
VOID FASTCALL
|
|
AfpFreeMdl(
|
|
IN PMDL pMdl
|
|
)
|
|
{
|
|
ASSERT (pMdl != NULL);
|
|
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_INFO,
|
|
("AfFreeMdl: Freeing Mdl %lx\n", pMdl));
|
|
IoFreeMdl(pMdl);
|
|
AFP_DBG_DEC_COUNT(AfpDbgMdlsAlloced);
|
|
|
|
#ifdef PROFILING
|
|
INTERLOCKED_DECREMENT_LONG((PLONG)&AfpServerProfile->perf_cAllocatedMdls);
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
/*** AfpMdlChainSize
|
|
*
|
|
* This routine counts the bytes in the mdl chain
|
|
*/
|
|
DWORD FASTCALL
|
|
AfpMdlChainSize(
|
|
IN PMDL pMdl
|
|
)
|
|
{
|
|
DWORD dwSize=0;
|
|
|
|
while (pMdl)
|
|
{
|
|
dwSize += MmGetMdlByteCount(pMdl);
|
|
pMdl = pMdl->Next;
|
|
}
|
|
|
|
return (dwSize);
|
|
}
|
|
|
|
|
|
/*** AfpIOAllocBuffer
|
|
*
|
|
* Maintain a pool of I/O buffers and fork-locks. These are aged out when not in use.
|
|
*/
|
|
PVOID FASTCALL
|
|
AfpIOAllocBuffer(
|
|
IN DWORD BufSize
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
PIOPOOL pPool;
|
|
PIOPOOL_HDR pPoolHdr, *ppPoolHdr;
|
|
BOOLEAN Found = False;
|
|
PVOID pBuf = NULL;
|
|
#ifdef PROFILING
|
|
TIME TimeS, TimeE, TimeD;
|
|
|
|
INTERLOCKED_INCREMENT_LONG( &AfpServerProfile->perf_BPAllocCount );
|
|
|
|
AfpGetPerfCounter(&TimeS);
|
|
#endif
|
|
|
|
ASSERT (BufSize <= (DSI_SERVER_REQUEST_QUANTUM+DSI_HEADER_SIZE));
|
|
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_INFO,
|
|
("AfpIOAllocBuffer: Request for %d\n", BufSize));
|
|
|
|
//
|
|
// if a large block (over 4K on x86) is being allocated, we don't want to
|
|
// tie it in in the IoPool. Do a direct allocation, set flags etc. so we know
|
|
// how to free it when it's freed
|
|
//
|
|
if (BufSize > ASP_QUANTUM)
|
|
{
|
|
pPoolHdr = (PIOPOOL_HDR) ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
(BufSize + sizeof(IOPOOL_HDR)),
|
|
AFP_TAG);
|
|
if (pPoolHdr == NULL)
|
|
{
|
|
DBGPRINT(DBG_COMP_STACKIF, DBG_LEVEL_ERR,
|
|
("AfpIOAllocBuffer: big buf alloc (%d bytes) failed!\n",BufSize));
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
#if DBG
|
|
pPoolHdr->Signature = POOLHDR_SIGNATURE;
|
|
#endif
|
|
pPoolHdr->iph_Tag.tg_Tag = IO_POOL_TAG;
|
|
pPoolHdr->iph_Tag.tg_Flags = IO_POOL_HUGE_BUFFER;
|
|
|
|
// we only have 20 bits for tg_Size, so the size had better be less than that!
|
|
ASSERT(BufSize <= 0xFFFFF);
|
|
|
|
pPoolHdr->iph_Tag.tg_Size = BufSize;
|
|
|
|
return((PVOID)((PBYTE)pPoolHdr + sizeof(IOPOOL_HDR)));
|
|
}
|
|
|
|
|
|
ACQUIRE_SPIN_LOCK(&afpIoPoolLock, &OldIrql);
|
|
|
|
try_again:
|
|
for (pPool = afpIoPoolHead;
|
|
pPool != NULL;
|
|
pPool = pPool->iop_Next)
|
|
{
|
|
ASSERT(VALID_IOP(pPool));
|
|
|
|
if (BufSize > sizeof(FORKLOCK))
|
|
{
|
|
if (pPool->iop_NumFreeBufs > 0)
|
|
{
|
|
LONG i;
|
|
|
|
for (i = 0, ppPoolHdr = &pPool->iop_FreeBufHead;
|
|
(i < pPool->iop_NumFreeBufs);
|
|
ppPoolHdr = &pPoolHdr->iph_Next, i++)
|
|
{
|
|
pPoolHdr = *ppPoolHdr;
|
|
ASSERT(VALID_PLH(pPoolHdr));
|
|
|
|
if (pPoolHdr->iph_Tag.tg_Size >= BufSize)
|
|
{
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_INFO,
|
|
("AfpIOAllocBuffer: Found space (bufs) in pool %lx\n", pPool));
|
|
ASSERT (pPoolHdr->iph_Tag.tg_Flags == IO_POOL_NOT_IN_USE);
|
|
*ppPoolHdr = pPoolHdr->iph_Next;
|
|
INTERLOCKED_INCREMENT_LONG((PLONG)&AfpServerStatistics.stat_IoPoolHits);
|
|
|
|
Found = True;
|
|
break;
|
|
}
|
|
}
|
|
if (Found)
|
|
break;
|
|
}
|
|
}
|
|
else if (pPool->iop_NumFreeLocks > 0)
|
|
{
|
|
// Even IO buffers for size <= sizeof(FORKLOCK) are allocated out of the
|
|
// lock pool - hey why not !!!
|
|
pPoolHdr = pPool->iop_FreeLockHead;
|
|
ASSERT(VALID_PLH(pPoolHdr));
|
|
|
|
ASSERT(pPoolHdr->iph_Tag.tg_Flags == IO_POOL_NOT_IN_USE);
|
|
pPool->iop_FreeLockHead = pPoolHdr->iph_Next;
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_INFO,
|
|
("AfpIOAllocBuffer: Found space (locks) in pool %lx\n", pPool));
|
|
INTERLOCKED_INCREMENT_LONG((PLONG)&AfpServerStatistics.stat_IoPoolHits);
|
|
|
|
Found = True;
|
|
break;
|
|
}
|
|
|
|
// All empty pool blocks are the end.
|
|
if ((pPool->iop_NumFreeBufs == 0) && (pPool->iop_NumFreeLocks == 0))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!Found)
|
|
{
|
|
INTERLOCKED_INCREMENT_LONG((PLONG)&AfpServerStatistics.stat_IoPoolMisses);
|
|
|
|
// If we failed to find it, allocate a new pool chunk, initialize and
|
|
// link it in
|
|
pPool = (PIOPOOL)AfpAllocNonPagedMemory(POOL_ALLOC_SIZE);
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_INFO,
|
|
("AfpIOAllocBuffer: No free slot, allocated a new pool %lx\n", pPool));
|
|
|
|
if (pPool != NULL)
|
|
{
|
|
LONG i;
|
|
PBYTE p;
|
|
|
|
#if DBG
|
|
pPool->Signature = IOPOOL_SIGNATURE;
|
|
#endif
|
|
pPool->iop_NumFreeBufs = NUM_BUFS_IN_POOL;
|
|
pPool->iop_NumFreeLocks = (BYTE)NUM_LOCKS_IN_POOL;
|
|
AfpLinkDoubleAtHead(afpIoPoolHead, pPool, iop_Next, iop_Prev);
|
|
p = (PBYTE)pPool + sizeof(IOPOOL);
|
|
pPool->iop_FreeBufHead = (PIOPOOL_HDR)p;
|
|
|
|
// Initialize pool of buffers and locks
|
|
for (i = 0, pPoolHdr = pPool->iop_FreeBufHead;
|
|
i < (NUM_BUFS_IN_POOL + NUM_LOCKS_IN_POOL);
|
|
i++)
|
|
{
|
|
#if DBG
|
|
pPoolHdr->Signature = POOLHDR_SIGNATURE;
|
|
#endif
|
|
pPoolHdr->iph_Tag.tg_Flags = IO_POOL_NOT_IN_USE; // Mark it un-used
|
|
pPoolHdr->iph_Tag.tg_Tag = IO_POOL_TAG;
|
|
if (i < NUM_BUFS_IN_POOL)
|
|
{
|
|
p += sizeof(IOPOOL_HDR) + (pPoolHdr->iph_Tag.tg_Size = afpPoolAllocSizes[i]);
|
|
if (i == (NUM_BUFS_IN_POOL-1))
|
|
{
|
|
pPoolHdr->iph_Next = NULL;
|
|
pPoolHdr = pPool->iop_FreeLockHead = (PIOPOOL_HDR)p;
|
|
}
|
|
else
|
|
{
|
|
pPoolHdr->iph_Next = (PIOPOOL_HDR)p;
|
|
pPoolHdr = (PIOPOOL_HDR)p;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pPoolHdr->iph_Tag.tg_Size = sizeof(FORKLOCK);
|
|
p += (sizeof(IOPOOL_HDR) + sizeof(FORKLOCK));
|
|
if (i == (NUM_BUFS_IN_POOL+NUM_LOCKS_IN_POOL-1))
|
|
{
|
|
pPoolHdr->iph_Next = NULL;
|
|
|
|
}
|
|
else
|
|
{
|
|
pPoolHdr->iph_Next = (PIOPOOL_HDR)p;
|
|
pPoolHdr = (PIOPOOL_HDR)p;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
// Adjust this since we'll increment this again up above. This was
|
|
// really a miss and not a hit
|
|
INTERLOCKED_DECREMENT_LONG((PLONG)&AfpServerStatistics.stat_IoPoolHits);
|
|
|
|
goto try_again;
|
|
}
|
|
}
|
|
|
|
if (Found)
|
|
{
|
|
PIOPOOL pTmp;
|
|
|
|
ASSERT(VALID_IOP(pPool));
|
|
ASSERT(VALID_PLH(pPoolHdr));
|
|
|
|
pPoolHdr->iph_pPool = pPool;
|
|
pPoolHdr->iph_Tag.tg_Flags = IO_POOL_IN_USE; // Mark it used
|
|
pPool->iop_Age = 0;
|
|
pBuf = (PBYTE)pPoolHdr + sizeof(IOPOOL_HDR);
|
|
if (BufSize > sizeof(FORKLOCK))
|
|
{
|
|
pPool->iop_NumFreeBufs --;
|
|
}
|
|
else
|
|
{
|
|
pPool->iop_NumFreeLocks --;
|
|
}
|
|
|
|
// If the block is now empty, unlink it from here and move it
|
|
// to the first empty slot. We know that all blocks 'earlier' than
|
|
// this are non-empty.
|
|
if ((pPool->iop_NumFreeBufs == 0) &&
|
|
(pPool->iop_NumFreeLocks == 0) &&
|
|
((pTmp = pPool->iop_Next) != NULL) &&
|
|
((pTmp->iop_NumFreeBufs > 0) || (pTmp->iop_NumFreeLocks > 0)))
|
|
{
|
|
ASSERT(VALID_IOP(pTmp));
|
|
|
|
AfpUnlinkDouble(pPool, iop_Next, iop_Prev);
|
|
for (; pTmp != NULL; pTmp = pTmp->iop_Next)
|
|
{
|
|
if (pTmp->iop_NumFreeBufs == 0)
|
|
{
|
|
// Found a free one. Park it right here.
|
|
AfpInsertDoubleBefore(pPool, pTmp, iop_Next, iop_Prev);
|
|
break;
|
|
}
|
|
else if (pTmp->iop_Next == NULL) // We reached the end
|
|
{
|
|
AfpLinkDoubleAtEnd(pPool, pTmp, iop_Next, iop_Prev);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&afpIoPoolLock, OldIrql);
|
|
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeE);
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
|
|
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_BPAllocTime,
|
|
TimeD,
|
|
&AfpStatisticsLock);
|
|
#endif
|
|
|
|
return pBuf;
|
|
}
|
|
|
|
|
|
/*** AfpIOFreeBuffer
|
|
*
|
|
* Return the IO buffer to the pool. Reset its age to 0. Insert into the free list
|
|
* in ascending order of sizes for bufs and at the head for locks
|
|
*/
|
|
VOID FASTCALL
|
|
AfpIOFreeBuffer(
|
|
IN PVOID pBuf
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
PIOPOOL pPool;
|
|
PIOPOOL_HDR pPoolHdr, *ppPoolHdr;
|
|
#ifdef PROFILING
|
|
TIME TimeS, TimeE, TimeD;
|
|
|
|
INTERLOCKED_INCREMENT_LONG( &AfpServerProfile->perf_BPFreeCount);
|
|
|
|
AfpGetPerfCounter(&TimeS);
|
|
#endif
|
|
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_INFO,
|
|
("AfpIOFreeBuffer: Freeing %lx\n", pBuf));
|
|
|
|
pPoolHdr = (PIOPOOL_HDR)((PBYTE)pBuf - sizeof(IOPOOL_HDR));
|
|
ASSERT(VALID_PLH(pPoolHdr));
|
|
ASSERT (pPoolHdr->iph_Tag.tg_Flags != IO_POOL_NOT_IN_USE);
|
|
ASSERT (pPoolHdr->iph_Tag.tg_Tag == IO_POOL_TAG);
|
|
|
|
//
|
|
// if this is a huge buffer we allocated, free it here and return
|
|
//
|
|
if (pPoolHdr->iph_Tag.tg_Flags == IO_POOL_HUGE_BUFFER)
|
|
{
|
|
ASSERT(pPoolHdr->iph_Tag.tg_Size > ASP_QUANTUM);
|
|
|
|
ExFreePool((PVOID)pPoolHdr);
|
|
return;
|
|
}
|
|
|
|
pPool = pPoolHdr->iph_pPool;
|
|
ASSERT(VALID_IOP(pPool));
|
|
|
|
ACQUIRE_SPIN_LOCK(&afpIoPoolLock, &OldIrql);
|
|
|
|
if (pPoolHdr->iph_Tag.tg_Size > sizeof(FORKLOCK))
|
|
{
|
|
ASSERT (pPool->iop_NumFreeBufs < NUM_BUFS_IN_POOL);
|
|
|
|
for (ppPoolHdr = &pPool->iop_FreeBufHead;
|
|
(*ppPoolHdr) != NULL;
|
|
ppPoolHdr = &(*ppPoolHdr)->iph_Next)
|
|
{
|
|
ASSERT(VALID_PLH(*ppPoolHdr));
|
|
if ((*ppPoolHdr)->iph_Tag.tg_Size > pPoolHdr->iph_Tag.tg_Size)
|
|
{
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_INFO,
|
|
("AfpIOFreeBuffer: Found slot for %lx (%lx)\n",
|
|
pBuf, pPool));
|
|
break;
|
|
}
|
|
}
|
|
pPoolHdr->iph_Next = (*ppPoolHdr);
|
|
*ppPoolHdr = pPoolHdr;
|
|
pPool->iop_NumFreeBufs ++;
|
|
}
|
|
else
|
|
{
|
|
ASSERT (pPool->iop_NumFreeLocks < NUM_LOCKS_IN_POOL);
|
|
|
|
pPoolHdr->iph_Next = pPool->iop_FreeLockHead;
|
|
pPool->iop_FreeLockHead = pPoolHdr;
|
|
pPool->iop_NumFreeLocks ++;
|
|
}
|
|
|
|
pPoolHdr->iph_Tag.tg_Flags = IO_POOL_NOT_IN_USE; // Mark it un-used
|
|
|
|
// If this block's status is changing from a 'none available' to 'available'
|
|
// move him to the head of the list.
|
|
if ((pPool->iop_NumFreeBufs + pPool->iop_NumFreeLocks) == 1)
|
|
{
|
|
AfpUnlinkDouble(pPool, iop_Next, iop_Prev);
|
|
AfpLinkDoubleAtHead(afpIoPoolHead,
|
|
pPool,
|
|
iop_Next,
|
|
iop_Prev);
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&afpIoPoolLock, OldIrql);
|
|
|
|
#ifdef PROFILING
|
|
AfpGetPerfCounter(&TimeE);
|
|
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
|
|
INTERLOCKED_ADD_LARGE_INTGR(&AfpServerProfile->perf_BPFreeTime,
|
|
TimeD,
|
|
&AfpStatisticsLock);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*** afpIoPoolAge
|
|
*
|
|
* Scavenger worker for aging out the IO pool.
|
|
*/
|
|
LOCAL AFPSTATUS FASTCALL
|
|
afpIoPoolAge(
|
|
IN PVOID pContext
|
|
)
|
|
{
|
|
PIOPOOL pPool;
|
|
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_INFO,
|
|
("afpIOPoolAge: Entered\n"));
|
|
|
|
ACQUIRE_SPIN_LOCK_AT_DPC(&afpIoPoolLock);
|
|
|
|
for (pPool = afpIoPoolHead;
|
|
pPool != NULL;
|
|
NOTHING)
|
|
{
|
|
PIOPOOL pFree;
|
|
|
|
ASSERT(VALID_IOP(pPool));
|
|
|
|
pFree = pPool;
|
|
pPool = pPool->iop_Next;
|
|
|
|
// Since all blocks which are completely used up are at the tail end of
|
|
// the list, if we encounter one, we are done.
|
|
if ((pFree->iop_NumFreeBufs == 0) &&
|
|
(pFree->iop_NumFreeLocks == 0))
|
|
break;
|
|
|
|
if ((pFree->iop_NumFreeBufs == NUM_BUFS_IN_POOL) &&
|
|
(pFree->iop_NumFreeLocks == NUM_LOCKS_IN_POOL))
|
|
{
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_WARN,
|
|
("afpIOPoolAge: Aging pool %lx\n", pFree));
|
|
if (++(pFree->iop_Age) >= MAX_POOL_AGE)
|
|
{
|
|
#ifdef PROFILING
|
|
INTERLOCKED_INCREMENT_LONG( &AfpServerProfile->perf_BPAgeCount);
|
|
#endif
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_WARN,
|
|
("afpIOPoolAge: Freeing pool %lx\n", pFree));
|
|
AfpUnlinkDouble(pFree, iop_Next, iop_Prev);
|
|
AfpFreeMemory(pFree);
|
|
}
|
|
}
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK_FROM_DPC(&afpIoPoolLock);
|
|
|
|
return AFP_ERR_REQUEUE;
|
|
}
|
|
|
|
|
|
#ifdef TRACK_MEMORY_USAGE
|
|
|
|
#define MAX_PTR_COUNT 4*1024
|
|
#define MAX_MEM_USERS 256
|
|
LOCAL struct _MemPtr
|
|
{
|
|
PVOID mptr_Ptr;
|
|
DWORD mptr_FileLine;
|
|
} afpMemPtrs[MAX_PTR_COUNT] = { 0 };
|
|
|
|
typedef struct
|
|
{
|
|
ULONG mem_FL;
|
|
ULONG mem_Count;
|
|
} MEM_USAGE, *PMEM_USAGE;
|
|
|
|
LOCAL MEM_USAGE afpMemUsageNonPaged[MAX_MEM_USERS] = {0};
|
|
LOCAL MEM_USAGE afpMemUsagePaged[MAX_MEM_USERS] = {0};
|
|
|
|
LOCAL AFP_SPIN_LOCK afpMemTrackLock = {0};
|
|
|
|
/*** AfpTrackMemoryUsage
|
|
*
|
|
* Keep track of memory usage by storing and clearing away pointers as and
|
|
* when they are allocated or freed. This helps in keeping track of memory
|
|
* leaks.
|
|
*
|
|
* LOCKS: AfpMemTrackLock (SPIN)
|
|
*/
|
|
VOID
|
|
AfpTrackMemoryUsage(
|
|
IN PVOID pMem,
|
|
IN BOOLEAN Alloc,
|
|
IN BOOLEAN Paged,
|
|
IN ULONG FileLine
|
|
)
|
|
{
|
|
KIRQL OldIrql;
|
|
static int i = 0;
|
|
PMEM_USAGE pMemUsage;
|
|
int j, k;
|
|
|
|
pMemUsage = afpMemUsageNonPaged;
|
|
if (Paged)
|
|
pMemUsage = afpMemUsagePaged;
|
|
|
|
ACQUIRE_SPIN_LOCK(&afpMemTrackLock, &OldIrql);
|
|
|
|
if (Alloc)
|
|
{
|
|
for (j = 0; j < MAX_PTR_COUNT; i++, j++)
|
|
{
|
|
i = i & (MAX_PTR_COUNT-1);
|
|
if (afpMemPtrs[i].mptr_Ptr == NULL)
|
|
{
|
|
afpMemPtrs[i].mptr_FileLine = FileLine;
|
|
afpMemPtrs[i++].mptr_Ptr = pMem;
|
|
break;
|
|
}
|
|
}
|
|
for (k = 0; k < MAX_MEM_USERS; k++)
|
|
{
|
|
if (pMemUsage[k].mem_FL == FileLine)
|
|
{
|
|
pMemUsage[k].mem_Count ++;
|
|
break;
|
|
}
|
|
}
|
|
if (k == MAX_MEM_USERS)
|
|
{
|
|
for (k = 0; k < MAX_MEM_USERS; k++)
|
|
{
|
|
if (pMemUsage[k].mem_FL == 0)
|
|
{
|
|
pMemUsage[k].mem_FL = FileLine;
|
|
pMemUsage[k].mem_Count = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (k == MAX_MEM_USERS)
|
|
{
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_ERR,
|
|
("AfpTrackMemoryUsage: Out of space on afpMemUsage !!!\n"));
|
|
DBGBRK(DBG_LEVEL_FATAL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (j = 0, k = i; j < MAX_PTR_COUNT; j++, k--)
|
|
{
|
|
k = k & (MAX_PTR_COUNT-1);
|
|
if (afpMemPtrs[k].mptr_Ptr == pMem)
|
|
{
|
|
afpMemPtrs[k].mptr_Ptr = NULL;
|
|
afpMemPtrs[k].mptr_FileLine = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
RELEASE_SPIN_LOCK(&afpMemTrackLock, OldIrql);
|
|
|
|
if (j == MAX_PTR_COUNT)
|
|
{
|
|
DBGPRINT(DBG_COMP_MEMORY, DBG_LEVEL_ERR,
|
|
("AfpTrackMemoryUsage: (%lx) %s\n",
|
|
FileLine, Alloc ? "Table Full" : "Can't find"));
|
|
}
|
|
}
|
|
|
|
#endif // TRACK_MEMORY_USAGE
|
|
|
|
|