|
|
/*++
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
|