Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

547 lines
14 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
memory.c
Abstract:
This module contains routines for handling memory management within the SAC.
Currently the SAC allocates a chunk of memory up front and then does all local allocations
from this, growing it as necessary.
Author:
Sean Selitrennikoff (v-seans) - Jan 11, 1999
Revision History:
--*/
#include "sac.h"
//
// These are useful for finding memory leaks.
//
LONG TotalAllocations = 0;
LONG TotalFrees = 0;
LARGE_INTEGER TotalBytesAllocated;
LARGE_INTEGER TotalBytesFreed;
#define GLOBAL_MEMORY_SIGNATURE 0x44414548
#define LOCAL_MEMORY_SIGNATURE 0x5353454C
//
// Structure for holding all allocations from the system
//
typedef struct _GLOBAL_MEMORY_DESCRIPTOR {
#if DBG
ULONG Signature;
#endif
PVOID Memory;
ULONG Size;
struct _GLOBAL_MEMORY_DESCRIPTOR *NextDescriptor;
} GLOBAL_MEMORY_DESCRIPTOR, *PGLOBAL_MEMORY_DESCRIPTOR;
typedef struct _LOCAL_MEMORY_DESCRIPTOR {
#if DBG
#if defined (_IA64_)
//
// We must make sure that allocated memory falls on mod-8 boundaries.
// To do this, we must make sure that this structure is of size mod-8.
//
ULONG Filler;
#endif
ULONG Signature;
#endif
ULONG Tag;
ULONG Size;
} LOCAL_MEMORY_DESCRIPTOR, *PLOCAL_MEMORY_DESCRIPTOR;
//
// Variable for holding our memory together.
//
PGLOBAL_MEMORY_DESCRIPTOR GlobalMemoryList;
KSPIN_LOCK MemoryLock;
//
// Constants used to manipulate size growth
//
#define MEMORY_ALLOCATION_SIZE PAGE_SIZE
#define INITIAL_MEMORY_BLOCK_SIZE 0x100000
//
// Functions
//
BOOLEAN
InitializeMemoryManagement(
VOID
)
/*++
Routine Description:
This routine initializes the internal memory management system.
Arguments:
None.
Return Value:
TRUE if successful, else FALSE
--*/
{
PLOCAL_MEMORY_DESCRIPTOR LocalDescriptor;
IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC InitializeMem: Entering\n")));
GlobalMemoryList = (PGLOBAL_MEMORY_DESCRIPTOR)ExAllocatePoolWithTagPriority(NonPagedPool,
INITIAL_MEMORY_BLOCK_SIZE,
INITIAL_POOL_TAG,
HighPoolPriority
);
if (GlobalMemoryList == NULL) {
IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE,
KdPrint(("SAC InitializeMem: Exiting with FALSE. No pool.\n")));
return FALSE;
}
KeInitializeSpinLock(&MemoryLock);
#if DBG
GlobalMemoryList->Signature = GLOBAL_MEMORY_SIGNATURE;
#endif
GlobalMemoryList->Memory = (PVOID)(GlobalMemoryList + 1);
GlobalMemoryList->Size = INITIAL_MEMORY_BLOCK_SIZE - sizeof(GLOBAL_MEMORY_DESCRIPTOR);
GlobalMemoryList->NextDescriptor = NULL;
LocalDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(GlobalMemoryList->Memory);
#if DBG
LocalDescriptor->Signature = LOCAL_MEMORY_SIGNATURE;
#endif
LocalDescriptor->Tag = FREE_POOL_TAG;
LocalDescriptor->Size = GlobalMemoryList->Size - sizeof(LOCAL_MEMORY_DESCRIPTOR);
IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC InitializeMem: Exiting with TRUE.\n")));
return TRUE;
} // InitializeMemoryManagement
VOID
FreeMemoryManagement(
VOID
)
/*++
Routine Description:
This routine frees the internal memory management system.
Arguments:
None.
Return Value:
TRUE if successful, else FALSE
--*/
{
KIRQL OldIrql;
PGLOBAL_MEMORY_DESCRIPTOR NextDescriptor;
IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC FreeMem: Entering\n")));
KeAcquireSpinLock(&MemoryLock, &OldIrql);
//
// Check if the memory allocation fits in a current block anywhere
//
while (GlobalMemoryList != NULL) {
#if DBG
ASSERT( GlobalMemoryList->Signature == GLOBAL_MEMORY_SIGNATURE );
#endif
NextDescriptor = GlobalMemoryList->NextDescriptor;
KeReleaseSpinLock(&MemoryLock, OldIrql);
ExFreePool(GlobalMemoryList);
KeAcquireSpinLock(&MemoryLock, &OldIrql);
GlobalMemoryList = NextDescriptor;
}
KeReleaseSpinLock(&MemoryLock, OldIrql);
IF_SAC_DEBUG(SAC_DEBUG_FUNC_TRACE, KdPrint(("SAC FreeMem: Exiting\n")));
}
PVOID
MyAllocatePool(
IN SIZE_T NumberOfBytes,
IN ULONG Tag,
IN PCHAR FileName,
IN ULONG LineNumber
)
/*++
Routine Description:
This routine allocates memory from our internal structures, and if needed, gets more pool.
Arguments:
NumberOfBytes - Number of bytes the client wants.
Tag - A tag to place on the memory.
FileName - The file name this request is coming from.
LineNumber - Line number within the file that is making this request.
Return Value:
A pointer to the allocated block if successful, else NULL
--*/
{
KIRQL OldIrql;
PGLOBAL_MEMORY_DESCRIPTOR GlobalDescriptor;
PGLOBAL_MEMORY_DESCRIPTOR NewDescriptor;
PLOCAL_MEMORY_DESCRIPTOR LocalDescriptor;
PLOCAL_MEMORY_DESCRIPTOR NextDescriptor;
ULONG ThisBlockSize;
ULONG BytesToAllocate;
UNREFERENCED_PARAMETER(FileName);
UNREFERENCED_PARAMETER(LineNumber);
ASSERT(Tag != FREE_POOL_TAG);
IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyAllocPool: Entering.\n")));
KeAcquireSpinLock(&MemoryLock, &OldIrql);
//
// Always allocate on mod-8 boundaries
//
if( NumberOfBytes & 0x7 ) {
NumberOfBytes += 8 - (NumberOfBytes & 0x7);
}
//
// Check if the memory allocation fits in a current block anywhere
//
GlobalDescriptor = GlobalMemoryList;
while (GlobalDescriptor != NULL) {
#if DBG
ASSERT( GlobalDescriptor->Signature == GLOBAL_MEMORY_SIGNATURE );
#endif
LocalDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(GlobalDescriptor->Memory);
ThisBlockSize = GlobalDescriptor->Size;
while (ThisBlockSize != 0) {
#if DBG
ASSERT( LocalDescriptor->Signature == LOCAL_MEMORY_SIGNATURE );
#endif
if ((LocalDescriptor->Tag == FREE_POOL_TAG) &&
(LocalDescriptor->Size >= NumberOfBytes)) {
IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyAllocPool: Found a good sized block.\n")));
goto FoundBlock;
}
ThisBlockSize -= (LocalDescriptor->Size + sizeof(LOCAL_MEMORY_DESCRIPTOR));
LocalDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(((PUCHAR)LocalDescriptor) +
LocalDescriptor->Size +
sizeof(LOCAL_MEMORY_DESCRIPTOR)
);
}
GlobalDescriptor = GlobalDescriptor->NextDescriptor;
}
KeReleaseSpinLock(&MemoryLock, OldIrql);
//
// There is no memory block big enough to hold the request.
//
//
// Now check if the request is larger than the normal allocation unit we use.
//
if (NumberOfBytes >
(MEMORY_ALLOCATION_SIZE - sizeof(GLOBAL_MEMORY_DESCRIPTOR) - sizeof(LOCAL_MEMORY_DESCRIPTOR))) {
BytesToAllocate = (ULONG)(NumberOfBytes + sizeof(GLOBAL_MEMORY_DESCRIPTOR) + sizeof(LOCAL_MEMORY_DESCRIPTOR));
} else {
BytesToAllocate = MEMORY_ALLOCATION_SIZE;
}
IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyAllocPool: Allocating new space.\n")));
NewDescriptor = (PGLOBAL_MEMORY_DESCRIPTOR)ExAllocatePoolWithTagPriority(NonPagedPool,
BytesToAllocate,
ALLOC_POOL_TAG,
HighPoolPriority
);
if (NewDescriptor == NULL) {
IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyAllocPool: No more memory, returning NULL.\n")));
return NULL;
}
KeAcquireSpinLock(&MemoryLock, &OldIrql);
#if DBG
NewDescriptor->Signature = GLOBAL_MEMORY_SIGNATURE;
#endif
NewDescriptor->Memory = (PVOID)(NewDescriptor + 1);
NewDescriptor->Size = BytesToAllocate - sizeof(GLOBAL_MEMORY_DESCRIPTOR);
NewDescriptor->NextDescriptor = GlobalMemoryList;
GlobalMemoryList = NewDescriptor;
LocalDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(GlobalMemoryList->Memory);
#if DBG
LocalDescriptor->Signature = LOCAL_MEMORY_SIGNATURE;
#endif
LocalDescriptor->Tag = FREE_POOL_TAG;
LocalDescriptor->Size = GlobalMemoryList->Size - sizeof(LOCAL_MEMORY_DESCRIPTOR);
FoundBlock:
//
// Jump to here when a memory descriptor of the right size has been found. It is expected that
// LocalDescriptor points to the correct block.
//
ASSERT(LocalDescriptor != NULL);
ASSERT(LocalDescriptor->Tag == FREE_POOL_TAG);
#if DBG
ASSERT(LocalDescriptor->Signature == LOCAL_MEMORY_SIGNATURE );
#endif
if (LocalDescriptor->Size > NumberOfBytes + sizeof(LOCAL_MEMORY_DESCRIPTOR)) {
//
// Make a descriptor of the left over parts of this block.
//
NextDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(((PUCHAR)LocalDescriptor) +
sizeof(LOCAL_MEMORY_DESCRIPTOR) +
NumberOfBytes
);
#if DBG
NextDescriptor->Signature = LOCAL_MEMORY_SIGNATURE;
#endif
NextDescriptor->Tag = FREE_POOL_TAG;
NextDescriptor->Size = (ULONG)(LocalDescriptor->Size - NumberOfBytes - sizeof(LOCAL_MEMORY_DESCRIPTOR));
LocalDescriptor->Size = (ULONG)NumberOfBytes;
}
LocalDescriptor->Tag = Tag;
KeReleaseSpinLock(&MemoryLock, OldIrql);
InterlockedIncrement(
&TotalAllocations
);
ExInterlockedAddLargeStatistic(
&TotalBytesAllocated,
(CLONG)LocalDescriptor->Size // Sundown - FIX
);
IF_SAC_DEBUG(SAC_DEBUG_MEM,
KdPrint(("SAC MyAllocPool: Returning block 0x%X.\n", LocalDescriptor)));
RtlZeroMemory( (LocalDescriptor+1), NumberOfBytes );
return (PVOID)(LocalDescriptor + 1);
} // MyAllocatePool
VOID
MyFreePool(
IN PVOID *Pointer
)
/*++
Routine Description:
This routine frees a block previously allocated from the internal memory management system.
Arguments:
Pointer - A pointer to the pointer to free.
Return Value:
Pointer is set to NULL if successful, else it is left alone.
--*/
{
KIRQL OldIrql;
ULONG ThisBlockSize;
PGLOBAL_MEMORY_DESCRIPTOR GlobalDescriptor;
PLOCAL_MEMORY_DESCRIPTOR LocalDescriptor;
PLOCAL_MEMORY_DESCRIPTOR PrevDescriptor;
PLOCAL_MEMORY_DESCRIPTOR ThisDescriptor;
LocalDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(((PUCHAR)(*Pointer)) - sizeof(LOCAL_MEMORY_DESCRIPTOR));
IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyFreePool: Entering with block 0x%X.\n", LocalDescriptor)));
ASSERT (LocalDescriptor->Size > 0);
#if DBG
ASSERT (LocalDescriptor->Signature == LOCAL_MEMORY_SIGNATURE);
#endif
InterlockedIncrement(
&TotalFrees
);
ExInterlockedAddLargeStatistic(
&TotalBytesFreed,
(CLONG)LocalDescriptor->Size
);
//
// Find the memory block in the global list
//
KeAcquireSpinLock(&MemoryLock, &OldIrql);
GlobalDescriptor = GlobalMemoryList;
while (GlobalDescriptor != NULL) {
#if DBG
ASSERT(GlobalDescriptor->Signature == GLOBAL_MEMORY_SIGNATURE);
#endif
PrevDescriptor = NULL;
ThisDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(GlobalDescriptor->Memory);
ThisBlockSize = GlobalDescriptor->Size;
while (ThisBlockSize != 0) {
#if DBG
ASSERT (ThisDescriptor->Signature == LOCAL_MEMORY_SIGNATURE);
#endif
if (ThisDescriptor == LocalDescriptor) {
goto FoundBlock;
}
ThisBlockSize -= (ThisDescriptor->Size + sizeof(LOCAL_MEMORY_DESCRIPTOR));
PrevDescriptor = ThisDescriptor;
ThisDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(((PUCHAR)ThisDescriptor) +
ThisDescriptor->Size +
sizeof(LOCAL_MEMORY_DESCRIPTOR)
);
}
GlobalDescriptor = GlobalDescriptor->NextDescriptor;
}
KeReleaseSpinLock(&MemoryLock, OldIrql);
IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyFreePool: Could not find block.\n")));
ASSERT(FALSE);
return;
FoundBlock:
//
// Jump to here when the proper memory descriptor has been found.
//
#if DBG
ASSERT (ThisDescriptor->Signature == LOCAL_MEMORY_SIGNATURE);
#endif
if (LocalDescriptor->Tag == FREE_POOL_TAG) {
//
// Ouch! We tried to free something twice, skip it before bad things happen.
//
KeReleaseSpinLock(&MemoryLock, OldIrql);
IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyFreePool: Attempted to free something twice.\n")));
ASSERT(FALSE);
return;
}
LocalDescriptor->Tag = FREE_POOL_TAG;
//
// If possible, merge this memory block with the next one.
//
if (ThisBlockSize > (LocalDescriptor->Size + sizeof(LOCAL_MEMORY_DESCRIPTOR))) {
ThisDescriptor = (PLOCAL_MEMORY_DESCRIPTOR)(((PUCHAR)LocalDescriptor) +
LocalDescriptor->Size +
sizeof(LOCAL_MEMORY_DESCRIPTOR)
);
if (ThisDescriptor->Tag == FREE_POOL_TAG) {
ThisDescriptor->Tag = 0;
#if DBG
ThisDescriptor->Signature = 0;
#endif
LocalDescriptor->Size += ThisDescriptor->Size + sizeof(LOCAL_MEMORY_DESCRIPTOR);
}
}
//
// Now see if we can merge this block with a previous block.
//
if ((PrevDescriptor != NULL) && (PrevDescriptor->Tag == FREE_POOL_TAG)) {
#if DBG
LocalDescriptor->Signature = 0;
#endif
LocalDescriptor->Tag = 0;
PrevDescriptor->Size += LocalDescriptor->Size + sizeof(LOCAL_MEMORY_DESCRIPTOR);
}
KeReleaseSpinLock(&MemoryLock, OldIrql);
*Pointer = NULL;
IF_SAC_DEBUG(SAC_DEBUG_MEM, KdPrint(("SAC MyFreePool: exiting.\n")));
} // MyFreePool