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
15 KiB
547 lines
15 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
|
|
|