548 lines
14 KiB
C
548 lines
14 KiB
C
/*++
|
||
|
||
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
|
||
|