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.
 
 
 
 
 
 

2441 lines
60 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
llcmem.c
Abstract:
Functions for allocating & freeing memory. Split out from llclib.c. The
reasons this module created are to isolate the memory allocators and to
convert from using the Zone package to just using non-paged pool for all
of DLC's memory requirements.
Functions in this module are used by both DLC & LLC. These functions must
go into a statically-linked library if DLC is ever divorced from LLC
We use pools to avoid the overhead of calling the system allocation & free
functions (although in practice, we end up allocating additional memory
because the packet count in the pool is usually insufficient). The downside
is that we may allocate memory that in the majority of situations is not
used, but the packets in pools tend to be small and few in number
To aid in tracking memory resources, DLC/LLC now defines the following
memory categories:
Memory
- arbitrary sized blocks allocated out of non-paged pool using
ExAllocatePool(NonPagedPool, ...)
ZeroMemory
- arbitrary sized blocks allocated out of non-paged pool using
ExAllocatePool(NonPagedPool, ...) and initialized to zeroes
Pool
- small sets of (relatively) small packets are allocated in one
block from Memory or ZeroMemory as a Pool and then subdivided
into packets (CreatePacketPool, DeletePacketPool, AllocatePacket,
DeallocatePacket)
Object
- structures which may be packets allocated from Pool which have
a known size and initialization values. Pseudo-category mainly
for debugging purposes
Contents:
InitializeMemoryPackage (DEBUG)
PullEntryList (DEBUG)
LinkMemoryUsage (DEBUG)
UnlinkMemoryUsage (DEBUG)
ChargeNonPagedPoolUsage (DEBUG)
RefundNonPagedPoolUsage (DEBUG)
AllocateMemory (DEBUG)
AllocateZeroMemory
DeallocateMemory (DEBUG)
AllocateObject (DEBUG)
FreeObject (DEBUG)
ValidateObject (DEBUG)
GetObjectSignature (DEBUG)
GetObjectBaseSize (DEBUG)
CreatePacketPool
DeletePacketPool
AllocatePacket
DeallocatePacket
CreateObjectPool (DEBUG)
AllocatePoolObject (DEBUG)
DeallocatePoolObject (DEBUG)
DeleteObjectPool (DEBUG)
CheckMemoryReturned (DEBUG)
CheckDriverMemoryUsage (DEBUG)
MemoryAllocationError (DEBUG)
UpdateCounter (DEBUG)
MemoryCounterOverflow (DEBUG)
DumpMemoryMetrics (DEBUG)
DumpPoolStats (DEBUG)
MapObjectId (DEBUG)
DumpPool (DEBUG)
DumpPoolList (DEBUG)
DumpPacketHead (DEBUG)
DumpMemoryUsageList (DEBUG)
DumpMemoryUsage (DEBUG)
x86SleazeCallersAddress (DEBUG)
CollectReturnAddresses (DEBUG)
GetLastReturnAddress (DEBUG)
VerifyElementOnList (DEBUG)
CheckList (DEBUG)
CheckEntryOnList (DEBUG)
DumpPrivateMemoryHeader (DEBUG)
ReportSwitchSettings (DEBUG)
Author:
Richard L Firth (rfirth) 10-Mar-1993
Environment:
kernel mode only.
Notes:
In non-debug version, DeallocateMemory is replaced with a macro which calls
ExFreePool(...) and AllocateMemory is replaced by a macro which calls
ExAllocatePool(NonPagedPool, ...)
Revision History:
09-Mar-1993 RFirth
Created
--*/
#ifndef i386
#define LLC_PRIVATE_PROTOTYPES
#endif
#include <ntddk.h>
#include <ndis.h>
#define APIENTRY
#include <dlcapi.h>
#include <dlcio.h>
#include "llcapi.h"
#include "dlcdef.h"
#include "dlcreg.h"
#include "dlctyp.h"
#include "llcdef.h"
#include "llcmem.h"
#include "llctyp.h"
#define DWORD_ROUNDUP(d) (((d) + 3) & ~3)
#define YES_NO(thing) ((thing) ? "Yes" : "No")
#if DBG
//
// some variables to keep track of memory allocations from non-paged pool. These
// are the cumulative totals for all of DLC's non-paged pool memory usage
//
KSPIN_LOCK MemoryCountersLock;
KIRQL MemoryCountersIrql;
ULONG GoodNonPagedPoolAllocs = 0;
ULONG BadNonPagedPoolAllocs = 0;
ULONG GoodNonPagedPoolFrees = 0;
ULONG BadNonPagedPoolFrees = 0;
ULONG NonPagedPoolRequested = 0;
ULONG NonPagedPoolAllocated = 0;
ULONG TotalNonPagedPoolRequested = 0;
ULONG TotalNonPagedPoolAllocated = 0;
ULONG TotalNonPagedPoolFreed = 0;
KSPIN_LOCK MemoryAllocatorLock;
ULONG InMemoryAllocator = 0;
KSPIN_LOCK PoolCreatorLock;
ULONG InPoolCreator = 0;
//
// MemoryUsageList - linked list of all MEMORY_USAGE structures in driver. If we
// allocate something that has a MEMORY_USAGE structure (& what doesn't?) then
// don't delete it, we can later scan this list to find out what is still allocated
//
PMEMORY_USAGE MemoryUsageList = NULL;
KSPIN_LOCK MemoryUsageLock;
//
// flags to aid in debugging - change states via debugger
//
//BOOLEAN DebugDump = TRUE;
BOOLEAN DebugDump = FALSE;
//BOOLEAN DeleteBusyListAnyway = TRUE;
BOOLEAN DeleteBusyListAnyway = FALSE;
BOOLEAN MemoryCheckNotify = TRUE;
//BOOLEAN MemoryCheckNotify = FALSE;
BOOLEAN MemoryCheckStop = TRUE;
//BOOLEAN MemoryCheckStop = FALSE;
BOOLEAN MaintainPrivateLists = TRUE;
//BOOLEAN MaintainPrivateLists = FALSE;
BOOLEAN MaintainGlobalLists = TRUE;
//BOOLEAN MaintainGlobalLists = FALSE;
BOOLEAN ZapDeallocatedPackets = TRUE;
//BOOLEAN ZapDeallocatedPackets = FALSE;
BOOLEAN ZapFreedMemory = TRUE;
//BOOLEAN ZapFreedMemory = FALSE;
//
// DlcGlobalMemoryList - every block that is allocated is linked to this list
// and removed when deleted. Helps us keep track of who allocated which block
//
KSPIN_LOCK DlcGlobalMemoryListLock;
LIST_ENTRY DlcGlobalMemoryList;
ULONG DlcGlobalMemoryListCount = 0;
//
// local function prototypes
//
VOID MemoryAllocationError(PCHAR, PVOID);
VOID UpdateCounter(PULONG, LONG);
VOID MemoryCounterOverflow(PULONG, LONG);
VOID DumpMemoryMetrics(VOID);
VOID DumpPoolStats(PCHAR, PPACKET_POOL);
PCHAR MapObjectId(DLC_OBJECT_TYPE);
VOID DumpPool(PPACKET_POOL);
VOID DumpPoolList(PCHAR, PSINGLE_LIST_ENTRY);
VOID DumpPacketHead(PPACKET_HEAD, ULONG);
VOID DumpMemoryUsageList(VOID);
VOID DumpMemoryUsage(PMEMORY_USAGE, BOOLEAN);
VOID CollectReturnAddresses(PVOID*, ULONG, ULONG);
PVOID* GetLastReturnAddress(PVOID**);
VOID x86SleazeCallersAddress(PVOID*, PVOID*);
BOOLEAN VerifyElementOnList(PSINGLE_LIST_ENTRY, PSINGLE_LIST_ENTRY);
VOID CheckList(PSINGLE_LIST_ENTRY, ULONG);
VOID CheckEntryOnList(PLIST_ENTRY, PLIST_ENTRY, BOOLEAN);
VOID DumpPrivateMemoryHeader(PPRIVATE_NON_PAGED_POOL_HEAD);
VOID ReportSwitchSettings(PSTR);
#define GRAB_SPINLOCK() KeAcquireSpinLock(&MemoryCountersLock, &MemoryCountersIrql)
#define FREE_SPINLOCK() KeReleaseSpinLock(&MemoryCountersLock, MemoryCountersIrql)
#ifdef i386
#define GET_CALLERS_ADDRESS x86SleazeCallersAddress
#else
#define GET_CALLERS_ADDRESS RtlGetCallersAddress
#endif
//
// private prototypes
//
ULONG
GetObjectSignature(
IN DLC_OBJECT_TYPE ObjectType
);
ULONG
GetObjectBaseSize(
IN DLC_OBJECT_TYPE ObjectType
);
#else
#define GRAB_SPINLOCK()
#define FREE_SPINLOCK()
#endif
//
// functions
//
#if DBG
VOID
InitializeMemoryPackage(
VOID
)
/*++
Routine Description:
Performs initialization for memory allocation functions
Arguments:
None.
Return Value:
None.
--*/
{
KeInitializeSpinLock(&MemoryCountersLock);
KeInitializeSpinLock(&MemoryAllocatorLock);
KeInitializeSpinLock(&PoolCreatorLock);
KeInitializeSpinLock(&MemoryUsageLock);
KeInitializeSpinLock(&DlcGlobalMemoryListLock);
DriverMemoryUsage.OwnerObjectId = DlcDriverObject;
DriverMemoryUsage.OwnerInstance = 0x4D454D; // 'MEM'
InitializeListHead(&DriverMemoryUsage.PrivateList);
LinkMemoryUsage(&DriverMemoryUsage);
DriverStringUsage.OwnerObjectId = DlcDriverObject;
DriverStringUsage.OwnerInstance = 0x535452; // 'STR'
InitializeListHead(&DriverStringUsage.PrivateList);
LinkMemoryUsage(&DriverStringUsage);
InitializeListHead(&DlcGlobalMemoryList);
ReportSwitchSettings("DLC.InitializeMemoryPackage (DEBUG version only)");
}
PSINGLE_LIST_ENTRY
PullEntryList(
IN PSINGLE_LIST_ENTRY List,
IN PSINGLE_LIST_ENTRY Element
)
/*++
Routine Description:
The missing SINGLE_LIST_ENTRY function. Removes an entry from a single-linked
list. The entry can be anywhere on the list. Reduces size of list elements
by one pointer, at expense of increased time to traverse list.
This function SHOULD NOT return NULL: if it does then the code is broken
since it assumes that an element is on a list, when it ain't
Arguments:
List - pointer to singly-linked list anchor. This MUST be the address of
the pointer to the list, not the first element in the list
Element - pointer to element to remove from List
Return Value:
PSINGLE_LIST_ENTRY
Success - Element
Failure - NULL
--*/
{
PSINGLE_LIST_ENTRY prev = List;
ASSERT(List);
ASSERT(Element);
while (List = List->Next) {
if (List == Element) {
prev->Next = Element->Next;
return Element;
}
prev = List;
}
return NULL;
}
VOID
LinkMemoryUsage(
IN PMEMORY_USAGE pMemoryUsage
)
/*++
Routine Description:
Add pMemoryUsage to linked list of MEMORY_USAGE structures
Arguments:
pMemoryUsage - pointer to MEMORY_USAGE structure to add
Return Value:
None.
--*/
{
KIRQL irql;
KeAcquireSpinLock(&MemoryUsageLock, &irql);
PushEntryList((PSINGLE_LIST_ENTRY)&MemoryUsageList, (PSINGLE_LIST_ENTRY)pMemoryUsage);
KeReleaseSpinLock(&MemoryUsageLock, irql);
}
VOID
UnlinkMemoryUsage(
IN PMEMORY_USAGE pMemoryUsage
)
/*++
Routine Description:
Remove pMemoryUsage from linked list of MEMORY_USAGE structures
Arguments:
pMemoryUsage - pointer to MEMORY_USAGE structure to remove
Return Value:
None.
--*/
{
KIRQL irql;
ASSERT(pMemoryUsage);
CheckMemoryReturned(pMemoryUsage);
KeAcquireSpinLock(&MemoryUsageLock, &irql);
ASSERT(PullEntryList((PSINGLE_LIST_ENTRY)&MemoryUsageList, (PSINGLE_LIST_ENTRY)pMemoryUsage));
KeReleaseSpinLock(&MemoryUsageLock, irql);
}
VOID
ChargeNonPagedPoolUsage(
IN PMEMORY_USAGE pMemoryUsage,
IN ULONG Size,
IN PPRIVATE_NON_PAGED_POOL_HEAD Block
)
/*++
Routine Description:
Charges this non-paged pool allocation to a specific memory user
Arguments:
pMemoryUsage - pointer to structure recording memory usage
Size - size of block allocated
Block - pointer to private header of allocated block
Return Value:
None.
--*/
{
KIRQL irql;
KeAcquireSpinLock(&pMemoryUsage->SpinLock, &irql);
if (pMemoryUsage->NonPagedPoolAllocated + Size < pMemoryUsage->NonPagedPoolAllocated) {
if (MemoryCheckNotify) {
DbgPrint("DLC.ChargeNonPagedPoolUsage: Overcharged? Usage @ %08x\n", pMemoryUsage);
}
if (MemoryCheckStop) {
DumpMemoryUsage(pMemoryUsage, TRUE);
DbgBreakPoint();
}
}
pMemoryUsage->NonPagedPoolAllocated += Size;
++pMemoryUsage->AllocateCount;
//
// link this block to the memory usage private list
//
if (MaintainPrivateLists) {
if (pMemoryUsage->PrivateList.Flink == NULL) {
//
// slight hack to make initializing MEMORY_USAGEs easier...
//
InitializeListHead(&pMemoryUsage->PrivateList);
}
InsertTailList(&pMemoryUsage->PrivateList, &Block->PrivateList);
}
KeReleaseSpinLock(&pMemoryUsage->SpinLock, irql);
}
VOID
RefundNonPagedPoolUsage(
IN PMEMORY_USAGE pMemoryUsage,
IN ULONG Size,
IN PPRIVATE_NON_PAGED_POOL_HEAD Block
)
/*++
Routine Description:
Refunds a non-paged pool allocation to a specific memory user
Arguments:
pMemoryUsage - pointer to structure recording memory usage
Size - size of block allocated
Block - pointer to private header of allocated block
Return Value:
None.
--*/
{
KIRQL irql;
KeAcquireSpinLock(&pMemoryUsage->SpinLock, &irql);
if (pMemoryUsage->NonPagedPoolAllocated - Size > pMemoryUsage->NonPagedPoolAllocated) {
if (MemoryCheckNotify) {
DbgPrint("DLC.RefundNonPagedPoolUsage: Error: Freeing unallocated memory? Usage @ %08x, %d\n",
pMemoryUsage,
Size
);
}
if (MemoryCheckStop) {
DumpMemoryUsage(pMemoryUsage, TRUE);
DbgBreakPoint();
}
}
//
// unlink this block from the memory usage private list
//
if (MaintainPrivateLists) {
CheckEntryOnList(&Block->PrivateList, &pMemoryUsage->PrivateList, TRUE);
RemoveEntryList(&Block->PrivateList);
}
pMemoryUsage->NonPagedPoolAllocated -= Size;
++pMemoryUsage->FreeCount;
KeReleaseSpinLock(&pMemoryUsage->SpinLock, irql);
}
PVOID
AllocateMemory(
IN PMEMORY_USAGE pMemoryUsage,
IN ULONG Size
)
/*++
Routine Description:
Allocates memory out of non-paged pool. For the debug version, we round up
the requested size to the next 4-byte boundary and we add header and tail
sections which contain a signature to check for over-write, and in-use and
size information
In the non-debug version, this function is replaced by a call to
ExAllocatePool(NonPagedPool, ...)
Arguments:
pMemoryUsage - pointer to MEMORY_USAGE structure for charging mem usage
Size - number of bytes to allocate
Return Value:
PVOID
Success - pointer to allocated memory
Failure - NULL
--*/
{
PVOID pMem;
ULONG OriginalSize = Size;
PUCHAR pMemEnd;
/*
KIRQL irql;
KeAcquireSpinLock(&MemoryAllocatorLock, &irql);
if (InMemoryAllocator) {
DbgPrint("DLC.AllocateMemory: Error: Memory allocator clash on entry. Count = %d\n",
InMemoryAllocator
);
// DbgBreakPoint();
}
++InMemoryAllocator;
KeReleaseSpinLock(&MemoryAllocatorLock, irql);
*/
Size = DWORD_ROUNDUP(Size)
+ sizeof(PRIVATE_NON_PAGED_POOL_HEAD)
+ sizeof(PRIVATE_NON_PAGED_POOL_TAIL);
pMem = ExAllocatePoolWithTag(NonPagedPool, (ULONG)Size, DLC_POOL_TAG);
if (pMem) {
((PPRIVATE_NON_PAGED_POOL_HEAD)pMem)->Size = Size;
((PPRIVATE_NON_PAGED_POOL_HEAD)pMem)->OriginalSize = OriginalSize;
((PPRIVATE_NON_PAGED_POOL_HEAD)pMem)->Flags = MEM_FLAGS_IN_USE;
((PPRIVATE_NON_PAGED_POOL_HEAD)pMem)->Signature = SIGNATURE1;
pMemEnd = (PUCHAR)pMem
+ DWORD_ROUNDUP(OriginalSize)
+ sizeof(PRIVATE_NON_PAGED_POOL_HEAD);
((PPRIVATE_NON_PAGED_POOL_TAIL)pMemEnd)->Size = Size;
((PPRIVATE_NON_PAGED_POOL_TAIL)pMemEnd)->Signature = SIGNATURE2;
((PPRIVATE_NON_PAGED_POOL_TAIL)pMemEnd)->Pattern1 = PATTERN1;
((PPRIVATE_NON_PAGED_POOL_TAIL)pMemEnd)->Pattern2 = PATTERN2;
GRAB_SPINLOCK();
UpdateCounter(&GoodNonPagedPoolAllocs, 1);
UpdateCounter(&NonPagedPoolAllocated, (LONG)Size);
UpdateCounter(&NonPagedPoolRequested, (LONG)OriginalSize);
UpdateCounter(&TotalNonPagedPoolRequested, (LONG)OriginalSize);
UpdateCounter(&TotalNonPagedPoolAllocated, (LONG)Size);
FREE_SPINLOCK();
if (MaintainGlobalLists) {
KIRQL irql;
//
// record the caller and add this block to the global list
//
GET_CALLERS_ADDRESS(&((PPRIVATE_NON_PAGED_POOL_HEAD)pMem)->Stack[0],
&((PPRIVATE_NON_PAGED_POOL_HEAD)pMem)->Stack[1]
);
KeAcquireSpinLock(&DlcGlobalMemoryListLock, &irql);
InsertTailList(&DlcGlobalMemoryList,
&((PPRIVATE_NON_PAGED_POOL_HEAD)pMem)->GlobalList
);
++DlcGlobalMemoryListCount;
KeReleaseSpinLock(&DlcGlobalMemoryListLock, irql);
}
ChargeNonPagedPoolUsage(pMemoryUsage, Size, (PPRIVATE_NON_PAGED_POOL_HEAD)pMem);
pMem = (PVOID)((PUCHAR)pMem + sizeof(PRIVATE_NON_PAGED_POOL_HEAD));
} else {
GRAB_SPINLOCK();
UpdateCounter(&BadNonPagedPoolAllocs, 1);
FREE_SPINLOCK();
}
/*
KeAcquireSpinLock(&MemoryAllocatorLock, &irql);
--InMemoryAllocator;
if (InMemoryAllocator) {
DbgPrint("DLC.AllocateMemory: Error: Memory allocator clash on exit. Count = %d\n",
InMemoryAllocator
);
// DbgBreakPoint();
}
KeReleaseSpinLock(&MemoryAllocatorLock, irql);
*/
return pMem;
}
VOID
DeallocateMemory(
IN PMEMORY_USAGE pMemoryUsage,
IN PVOID Pointer
)
/*++
Routine Description:
frees memory to non-paged pool
Arguments:
pMemoryUsage - pointer to MEMORY_USAGE structure for charging mem usage
Pointer - pointer to previously allocated non-paged pool memory
Return Value:
None.
--*/
{
PPRIVATE_NON_PAGED_POOL_HEAD pHead;
PPRIVATE_NON_PAGED_POOL_TAIL pTail;
/*
KIRQL irql;
KeAcquireSpinLock(&MemoryAllocatorLock, &irql);
if (InMemoryAllocator) {
DbgPrint("DLC.DeallocateMemory: Error: Memory allocator clash on entry. Count = %d\n",
InMemoryAllocator
);
// DbgBreakPoint();
}
++InMemoryAllocator;
KeReleaseSpinLock(&MemoryAllocatorLock, irql);
*/
pHead = (PPRIVATE_NON_PAGED_POOL_HEAD)((PUCHAR)Pointer - sizeof(PRIVATE_NON_PAGED_POOL_HEAD));
pTail = (PPRIVATE_NON_PAGED_POOL_TAIL)((PUCHAR)pHead + pHead->Size - sizeof(PRIVATE_NON_PAGED_POOL_TAIL));
if (MaintainGlobalLists) {
CheckEntryOnList(&pHead->GlobalList, &DlcGlobalMemoryList, TRUE);
if (pHead->GlobalList.Flink == NULL
|| pHead->GlobalList.Blink == NULL) {
if (MemoryCheckNotify) {
DbgPrint("DLC.DeallocateMemory: Error: Block already globally freed: %08x\n", pHead);
}
if (MemoryCheckStop) {
DbgBreakPoint();
}
}
}
if (pHead->Signature != SIGNATURE1
|| !(pHead->Flags & MEM_FLAGS_IN_USE)
|| pTail->Size != pHead->Size
|| pTail->Signature != SIGNATURE2
|| pTail->Pattern1 != PATTERN1
|| pTail->Pattern2 != PATTERN2) {
if (MemoryCheckNotify || MemoryCheckStop) {
MemoryAllocationError("DeallocateMemory", (PVOID)pHead);
}
GRAB_SPINLOCK();
UpdateCounter(&BadNonPagedPoolFrees, 1);
FREE_SPINLOCK();
} else {
GRAB_SPINLOCK();
UpdateCounter(&GoodNonPagedPoolFrees, 1);
FREE_SPINLOCK();
}
GRAB_SPINLOCK();
UpdateCounter(&NonPagedPoolRequested, -(LONG)pHead->OriginalSize);
UpdateCounter(&NonPagedPoolAllocated, -(LONG)pHead->Size);
UpdateCounter(&TotalNonPagedPoolFreed, (LONG)pHead->Size);
FREE_SPINLOCK();
//
// access Size field before ExFreePool zaps it/somebody else allocates memory
//
RefundNonPagedPoolUsage(pMemoryUsage, pHead->Size, pHead);
if (MaintainGlobalLists) {
//
// remove this block from the global list
//
RemoveEntryList(&pHead->GlobalList);
--DlcGlobalMemoryListCount;
pHead->GlobalList.Flink = pHead->GlobalList.Flink = NULL;
}
if (ZapFreedMemory) {
RtlFillMemory(pHead + 1,
DWORD_ROUNDUP(pHead->OriginalSize),
ZAP_EX_FREE_VALUE
);
}
ExFreePool((PVOID)pHead);
/*
KeAcquireSpinLock(&MemoryAllocatorLock, &irql);
--InMemoryAllocator;
if (InMemoryAllocator) {
DbgPrint("DLC.DeallocateMemory: Error: Memory allocator clash on exit. Count = %d\n",
InMemoryAllocator
);
// DbgBreakPoint();
}
KeReleaseSpinLock(&MemoryAllocatorLock, irql);
*/
}
PVOID
AllocateObject(
IN PMEMORY_USAGE pMemoryUsage,
IN DLC_OBJECT_TYPE ObjectType,
IN ULONG ObjectSize
)
/*++
Routine Description:
Allocates a pseudo-object
Arguments:
ObjectType - type of object to allocate
ObjectSize - size of object; mainly because some objects have variable size
pMemoryUsage - pointer to MEMORY_USAGE structure for charging mem usage
Return Value:
PVOID
Success - pointer to object allocated from non-paged pool
Failure - NULL
--*/
{
POBJECT_ID pObject;
ULONG signature;
ULONG baseSize;
signature = GetObjectSignature(ObjectType);
baseSize = GetObjectBaseSize(ObjectType);
if (baseSize < ObjectSize) {
DbgPrint("DLC.AllocateObject: Error: Invalid size %d for ObjectType %08x (should be >= %d)\n",
ObjectSize,
ObjectType,
baseSize
);
DbgBreakPoint();
}
pObject = (POBJECT_ID)AllocateZeroMemory(pMemoryUsage, ObjectSize);
if (pObject) {
pObject->Signature = signature;
pObject->Type = ObjectType;
pObject->Size = baseSize;
pObject->Extra = ObjectSize - baseSize;
}
return (PVOID)pObject;
}
VOID
FreeObject(
IN PMEMORY_USAGE pMemoryUsage,
IN PVOID pObject,
IN DLC_OBJECT_TYPE ObjectType
)
/*++
Routine Description:
Deallocates a pseudo-object
Arguments:
pMemoryUsage - pointer to MEMORY_USAGE structure for charging mem usage
pObject - pointer to object allocated with AllocateObject
ObjectType - type of object pObject supposed to be
Return Value:
None.
--*/
{
ValidateObject(pObject, ObjectType);
DeallocateMemory(pMemoryUsage, pObject);
}
VOID
ValidateObject(
IN POBJECT_ID pObject,
IN DLC_OBJECT_TYPE ObjectType
)
/*++
Routine Description:
Checks that an object is what its supposed to be
Arguments:
pObject - pointer to object to check
ObjectType - type of object pObject supposed to point to
Return Value:
None.
--*/
{
ULONG signature = GetObjectSignature(ObjectType);
ULONG baseSize = GetObjectBaseSize(ObjectType);
if (pObject->Signature != signature
|| pObject->Type != ObjectType
|| pObject->Size != baseSize) {
DbgPrint("DLC.ValidateObject: Error: InvalidObject %08x, Type=%08x\n",
pObject,
ObjectType
);
DbgBreakPoint();
}
}
ULONG
GetObjectSignature(
IN DLC_OBJECT_TYPE ObjectType
)
/*++
Routine Description:
returns the signature for an object type
Arguments:
ObjectType - type of object to return signature for
Return Value:
ULONG
--*/
{
switch (ObjectType) {
case FileContextObject:
return SIGNATURE_FILE;
case AdapterContextObject:
return SIGNATURE_ADAPTER;
case BindingContextObject:
return SIGNATURE_BINDING;
case DlcSapObject:
case DlcGroupSapObject:
return SIGNATURE_DLC_SAP;
case DlcLinkObject:
return SIGNATURE_DLC_LINK;
case DlcDixObject:
return SIGNATURE_DIX;
case LlcDataLinkObject:
return SIGNATURE_LLC_LINK;
case LlcSapObject:
case LlcGroupSapObject:
return SIGNATURE_LLC_SAP;
default:
DbgPrint("DLC.GetObjectSignature: Error: unknown object type %08x\n", ObjectType);
DbgBreakPoint();
return 0;
}
}
ULONG
GetObjectBaseSize(
IN DLC_OBJECT_TYPE ObjectType
)
/*++
Routine Description:
returns the base size for an object
Arguments:
ObjectType - type of object to return base size for
Return Value:
ULONG
--*/
{
switch (ObjectType) {
case FileContextObject:
return sizeof(DLC_FILE_CONTEXT);
case AdapterContextObject:
return sizeof(ADAPTER_CONTEXT);
case BindingContextObject:
return sizeof(BINDING_CONTEXT);
case DlcSapObject:
case DlcGroupSapObject:
return sizeof(DLC_OBJECT);
case DlcLinkObject:
return sizeof(DLC_OBJECT);
case DlcDixObject:
return sizeof(DLC_OBJECT);
case LlcDataLinkObject:
return sizeof(DATA_LINK);
case LlcSapObject:
case LlcGroupSapObject:
return sizeof(LLC_OBJECT);
default:
DbgPrint("DLC.GetObjectBaseSize: Error: unknown object type %08x\n", ObjectType);
DbgBreakPoint();
return 0;
}
}
#endif
PVOID
AllocateZeroMemory(
#if DBG
IN PMEMORY_USAGE pMemoryUsage,
#endif
IN ULONG Size
)
/*++
Routine Description:
Allocates memory out of non-paged pool. For the debug version, we round up
the requested size to the next 4-byte boundary and we add header and tail
sections which contain a signature to check for over-write, and in-use and
size information
The memory is zeroed before being returned to the caller
Arguments:
pMemoryUsage - pointer to MEMORY_USAGE structure for charging mem usage
Size - number of bytes to allocate
Return Value:
PVOID
Success - pointer to allocated memory
Failure - NULL
--*/
{
PVOID pMem;
#if DBG
ULONG OriginalSize = Size;
PUCHAR pMemEnd;
/*
KIRQL irql;
KeAcquireSpinLock(&MemoryAllocatorLock, &irql);
if (InMemoryAllocator) {
DbgPrint("DLC.AllocateZeroMemory: Error: Memory allocator clash on entry. Count = %d\n",
InMemoryAllocator
);
// DbgBreakPoint();
}
++InMemoryAllocator;
KeReleaseSpinLock(&MemoryAllocatorLock, irql);
*/
Size = DWORD_ROUNDUP(Size)
+ sizeof(PRIVATE_NON_PAGED_POOL_HEAD)
+ sizeof(PRIVATE_NON_PAGED_POOL_TAIL);
#endif
pMem = ExAllocatePoolWithTag(NonPagedPool, (ULONG)Size, DLC_POOL_TAG);
if (pMem) {
LlcZeroMem(pMem, Size);
#if DBG
((PPRIVATE_NON_PAGED_POOL_HEAD)pMem)->Size = Size;
((PPRIVATE_NON_PAGED_POOL_HEAD)pMem)->OriginalSize = OriginalSize;
((PPRIVATE_NON_PAGED_POOL_HEAD)pMem)->Flags = MEM_FLAGS_IN_USE;
((PPRIVATE_NON_PAGED_POOL_HEAD)pMem)->Signature = SIGNATURE1;
pMemEnd = (PUCHAR)pMem
+ DWORD_ROUNDUP(OriginalSize)
+ sizeof(PRIVATE_NON_PAGED_POOL_HEAD);
((PPRIVATE_NON_PAGED_POOL_TAIL)pMemEnd)->Size = Size;
((PPRIVATE_NON_PAGED_POOL_TAIL)pMemEnd)->Signature = SIGNATURE2;
((PPRIVATE_NON_PAGED_POOL_TAIL)pMemEnd)->Pattern1 = PATTERN1;
((PPRIVATE_NON_PAGED_POOL_TAIL)pMemEnd)->Pattern2 = PATTERN2;
GRAB_SPINLOCK();
UpdateCounter(&GoodNonPagedPoolAllocs, 1);
UpdateCounter(&NonPagedPoolAllocated, (LONG)Size);
UpdateCounter(&NonPagedPoolRequested, (LONG)OriginalSize);
UpdateCounter(&TotalNonPagedPoolRequested, (LONG)OriginalSize);
UpdateCounter(&TotalNonPagedPoolAllocated, (LONG)Size);
FREE_SPINLOCK();
if (MaintainGlobalLists) {
KIRQL irql;
//
// record the caller and add this block to the global list
//
GET_CALLERS_ADDRESS(&((PPRIVATE_NON_PAGED_POOL_HEAD)pMem)->Stack[0],
&((PPRIVATE_NON_PAGED_POOL_HEAD)pMem)->Stack[1]
);
KeAcquireSpinLock(&DlcGlobalMemoryListLock, &irql);
InsertTailList(&DlcGlobalMemoryList,
&((PPRIVATE_NON_PAGED_POOL_HEAD)pMem)->GlobalList
);
++DlcGlobalMemoryListCount;
KeReleaseSpinLock(&DlcGlobalMemoryListLock, irql);
}
ChargeNonPagedPoolUsage(pMemoryUsage, Size, (PPRIVATE_NON_PAGED_POOL_HEAD)pMem);
pMem = (PVOID)((PUCHAR)pMem + sizeof(PRIVATE_NON_PAGED_POOL_HEAD));
} else {
GRAB_SPINLOCK();
UpdateCounter(&BadNonPagedPoolAllocs, 1);
FREE_SPINLOCK();
}
/*
KeAcquireSpinLock(&MemoryAllocatorLock, &irql);
--InMemoryAllocator;
if (InMemoryAllocator) {
DbgPrint("DLC.AllocateZeroMemory: Error: Memory allocator clash on exit. Count = %d\n",
InMemoryAllocator
);
// DbgBreakPoint();
}
KeReleaseSpinLock(&MemoryAllocatorLock, irql);
*/
#else
}
#endif
return pMem;
}
PPACKET_POOL
CreatePacketPool(
#if DBG
IN PMEMORY_USAGE pMemoryUsage,
IN PVOID pOwner,
IN DLC_OBJECT_TYPE ObjectType,
#endif
IN ULONG PacketSize,
IN ULONG NumberOfPackets
)
/*++
Routine Description:
creates a packet pool. A packet pool is a collection of same-sized packets
Arguments:
pMemoryUsage - pointer to MEMORY_USAGE structure for charging mem usage
pOwner - pointer to owner object
ObjectType - type of object for owner
PacketSize - size of packet in bytes
NumberOfPackets - initial number of packets in pool
Return Value:
PPACKET_POOL
Success - pointer to PACKET_POOL structure allocated from non-paged pool
Failure - NULL
--*/
{
PPACKET_POOL pPacketPool;
PPACKET_HEAD pPacketHead;
#if DBG
/*
// DbgPrint("DLC.CreatePacketPool(%d, %d)\n", PacketSize, NumberOfPackets);
if (InPoolCreator) {
DbgPrint("DLC.CreatePacketPool: Error: Pool Creator clash on entry. Count = %d\n",
InPoolCreator
);
// DbgBreakPoint();
}
++InPoolCreator;
*/
pPacketPool = AllocateZeroMemory(pMemoryUsage, sizeof(PACKET_POOL));
#else
pPacketPool = AllocateZeroMemory(sizeof(PACKET_POOL));
#endif
if (pPacketPool) {
#if DBG
pPacketPool->OriginalPacketCount = NumberOfPackets;
pPacketPool->MemoryUsage.Owner = pPacketPool;
pPacketPool->MemoryUsage.OwnerObjectId = ObjectType;
#endif
while (NumberOfPackets--) {
#if DBG
//
// charge memory for individual packets to the pool
//
pPacketHead = (PPACKET_HEAD)AllocateZeroMemory(&pPacketPool->MemoryUsage,
sizeof(PACKET_HEAD) + PacketSize
);
#else
pPacketHead = (PPACKET_HEAD)AllocateZeroMemory(sizeof(PACKET_HEAD) + PacketSize);
#endif
if (pPacketHead) {
#if DBG
pPacketHead->Signature = PACKET_HEAD_SIGNATURE;
pPacketHead->pPacketPool = pPacketPool;
++pPacketPool->FreeCount;
#endif
pPacketHead->Flags = PACKET_FLAGS_FREE;
PushEntryList(&pPacketPool->FreeList, (PSINGLE_LIST_ENTRY)pPacketHead);
} else {
while (pPacketPool->FreeList.Next) {
PVOID ptr = (PVOID)PopEntryList(&pPacketPool->FreeList);
#if DBG
DeallocateMemory(&pPacketPool->MemoryUsage, ptr);
#else
DeallocateMemory(ptr);
#endif
}
#if DBG
DbgPrint("DLC.CreatePacketPool: Error: couldn't allocate %d packets\n",
pPacketPool->OriginalPacketCount
);
DeallocateMemory(pMemoryUsage, pPacketPool);
/*
--InPoolCreator;
if (InPoolCreator) {
DbgPrint("DLC.CreatePacketPool: Error: Pool Creator clash on exit. Count = %d\n",
InPoolCreator
);
// DbgBreakPoint();
}
*/
#else
DeallocateMemory(pPacketPool);
#endif
return NULL;
}
}
KeInitializeSpinLock(&pPacketPool->PoolLock);
pPacketPool->PacketSize = PacketSize;
#if DBG
pPacketPool->Signature = PACKET_POOL_SIGNATURE;
pPacketPool->Viable = TRUE;
pPacketPool->CurrentPacketCount = pPacketPool->OriginalPacketCount;
pPacketPool->Flags = POOL_FLAGS_IN_USE;
pPacketPool->pMemoryUsage = pMemoryUsage;
//
// add the memory usage structure for this pool to the memory usage
// list
//
LinkMemoryUsage(&pPacketPool->MemoryUsage);
if (DebugDump) {
DbgPrint("DLC.CreatePacketPool: %08x\n", pPacketPool);
DumpPool(pPacketPool);
}
} else {
DbgPrint("DLC.CreatePacketPool: Error: couldn't allocate memory for PACKET_POOL\n");
}
//
// debug counters in PACKET_POOL structure are already zero thanks to
// AllocateZeroMemory automatically zeroing all memory allocated from
// non-paged pool
//
/*
--InPoolCreator;
if (InPoolCreator) {
DbgPrint("DLC.CreatePacketPool: Error: Pool Creator clash on exit. Count = %d\n",
InPoolCreator
);
// DbgBreakPoint();
}
*/
#else
}
#endif
return pPacketPool;
}
VOID
DeletePacketPool(
#if DBG
IN PMEMORY_USAGE pMemoryUsage,
#endif
IN PPACKET_POOL* ppPacketPool
)
/*++
Routine Description:
frees a previously created packet pool
Arguments:
pMemoryUsage - pointer to MEMORY_USAGE structure for charging mem usage
ppPacketPool - pointer to pointer to PACKET_POOL structure. Zero on return
Return Value:
None.
--*/
{
KIRQL irql;
PPACKET_HEAD pPacketHead;
PPACKET_POOL pPacketPool = *ppPacketPool;
#if DBG
ULONG packetCount;
#endif
//
// for various reasons, we can receive a NULL pointer. No action in this case
//
if (pPacketPool == NULL) {
#if DBG
PVOID callerAddress, callersCaller;
GET_CALLERS_ADDRESS(&callerAddress, &callersCaller);
DbgPrint("DLC.DeletePacketPool: NULL pointer. Caller = %x (caller's caller = %x)\n",
callerAddress,
callersCaller
);
#endif
return;
}
#if DBG
// DbgPrint("DLC.DeletePacketPool(%08x)\n", pPacketPool);
// DumpPool(pPacketPool);
if (pPacketPool->ClashCount) {
DbgPrint("DLC.DeletePacketPool: Error: Memory allocator clash on entry: Pool %08x\n", pPacketPool);
DbgBreakPoint();
}
++pPacketPool->ClashCount;
if (pPacketPool->Signature != PACKET_POOL_SIGNATURE) {
DbgPrint("DLC.DeletePacketPool: Error: Invalid Pool Handle %08x\n", pPacketPool);
DbgBreakPoint();
}
if (!pPacketPool->Viable) {
DbgPrint("DLC.DeletePacketPool: Error: Unviable Packet Pool %08x\n", pPacketPool);
DbgBreakPoint();
}
#endif
KeAcquireSpinLock(&pPacketPool->PoolLock, &irql);
#if DBG
//
// mark the packet pool structure as unviable: if anybody tries to allocate
// or deallocate while we are destroying the pool, we will break into debugger
//
pPacketPool->Viable = FALSE;
pPacketPool->Signature = 0xFFFFFFFF;
//
// assert that the busy list is empty
//
if (pPacketPool->BusyList.Next != NULL) {
DbgPrint("DLC.DeletePacketPool: Error: %d packets busy. Pool = %08x\n",
pPacketPool->BusyCount,
pPacketPool
);
if (!DeleteBusyListAnyway) {
DumpPool(pPacketPool);
DbgBreakPoint();
} else {
DbgPrint("DLC.DeletePacketPool: Deleting BusyList anyway\n");
}
}
packetCount = 0;
#endif
while (pPacketPool->FreeList.Next != NULL) {
pPacketHead = (PPACKET_HEAD)PopEntryList(&pPacketPool->FreeList);
#if DBG
if (pPacketHead->Signature != PACKET_HEAD_SIGNATURE
|| pPacketHead->pPacketPool != pPacketPool
|| (pPacketHead->Flags & PACKET_FLAGS_BUSY)
|| !(pPacketHead->Flags & PACKET_FLAGS_FREE)) {
DbgPrint("DLC.DeletePacketPool: Error: Bad packet %08x. Pool = %08x\n",
pPacketHead,
pPacketPool
);
DbgBreakPoint();
}
++packetCount;
DeallocateMemory(&pPacketPool->MemoryUsage, pPacketHead);
#else
DeallocateMemory(pPacketHead);
#endif
}
#if DBG
if (DeleteBusyListAnyway) {
while (pPacketPool->BusyList.Next != NULL) {
pPacketHead = (PPACKET_HEAD)PopEntryList(&pPacketPool->BusyList);
if (pPacketHead->Signature != PACKET_HEAD_SIGNATURE
|| pPacketHead->pPacketPool != pPacketPool
|| (pPacketHead->Flags & PACKET_FLAGS_FREE)
|| !(pPacketHead->Flags & PACKET_FLAGS_BUSY)) {
DbgPrint("DLC.DeletePacketPool: Error: Bad packet %08x. Pool = %08x\n",
pPacketHead,
pPacketPool
);
DbgBreakPoint();
}
++packetCount;
DeallocateMemory(&pPacketPool->MemoryUsage, pPacketHead);
}
}
//
// did any packets get unwittingly added or removed?
//
if (packetCount != pPacketPool->CurrentPacketCount) {
DbgPrint("DLC.DeletePacketPool: Error: PacketCount (%d) != PoolCount (%d)\n",
packetCount,
pPacketPool->CurrentPacketCount
);
DumpPool(pPacketPool);
DbgBreakPoint();
}
//
// ensure we returned all the memory allocated to this pool
//
CheckMemoryReturned(&pPacketPool->MemoryUsage);
//
// dump the counters every time we delete a pool
//
// DumpPoolStats("DeletePacketPool", pPacketPool);
//
// remove the pool's memory usage structure - all memory allocated has been
// freed, so we're in the clear for this one
//
UnlinkMemoryUsage(&pPacketPool->MemoryUsage);
#endif
KeReleaseSpinLock(&pPacketPool->PoolLock, irql);
#if DBG
DeallocateMemory(pMemoryUsage, pPacketPool);
#else
DeallocateMemory(pPacketPool);
#endif
*ppPacketPool = NULL;
}
PVOID
AllocatePacket(
IN PPACKET_POOL pPacketPool
)
/*++
Routine Description:
allocates a packet from a packet pool. We expect that we can always get a
packet from the previously allocated pool. However, if all packets are
currently in use, allocate another from non-paged pool
Arguments:
pPacketPool - pointer to PACKET_POOL structure
Return Value:
PVOID
Success - pointer to allocated packet
Failure - NULL
--*/
{
KIRQL irql;
PPACKET_HEAD pPacketHead;
#if DBG
if (pPacketPool->ClashCount) {
DbgPrint("DLC.AllocatePacket: Error: Memory allocator clash on entry: Pool %08x, Count %d\n",
pPacketPool,
pPacketPool->ClashCount
);
// DbgBreakPoint();
}
++pPacketPool->ClashCount;
#endif
KeAcquireSpinLock(&pPacketPool->PoolLock, &irql);
#if DBG
if (pPacketPool->Signature != PACKET_POOL_SIGNATURE) {
DbgPrint("DLC.AllocatePacket: Error: Invalid Pool Handle %08x\n", pPacketPool);
DbgBreakPoint();
}
if (!pPacketPool->Viable) {
DbgPrint("DLC.AllocatePacket: Error: Unviable Packet Pool %08x\n", pPacketPool);
DbgBreakPoint();
}
#endif
if (pPacketPool->FreeList.Next != NULL) {
pPacketHead = (PPACKET_HEAD)PopEntryList(&pPacketPool->FreeList);
#if DBG
--pPacketPool->FreeCount;
if (pPacketHead->Flags & PACKET_FLAGS_BUSY
|| !(pPacketHead->Flags & PACKET_FLAGS_FREE)) {
DbgPrint("DLC.AllocatePacket: Error: BUSY packet %08x on FreeList; Pool=%08x\n",
pPacketHead,
pPacketPool
);
DumpPacketHead(pPacketHead, 0);
DbgBreakPoint();
}
#endif
} else {
//
// Miscalculated pool usage
//
#if DBG
pPacketHead = (PPACKET_HEAD)AllocateZeroMemory(&pPacketPool->MemoryUsage,
sizeof(PACKET_HEAD) + pPacketPool->PacketSize
);
#else
pPacketHead = (PPACKET_HEAD)AllocateZeroMemory(sizeof(PACKET_HEAD) + pPacketPool->PacketSize);
#endif
if (pPacketHead) {
//
// mark this packet as allocated after the pool was created - this
// means our initial estimation of packet requirement for this
// pool was inadequate
//
pPacketHead->Flags = PACKET_FLAGS_POST_ALLOC | PACKET_FLAGS_FREE;
}
#if DBG
++pPacketPool->NoneFreeCount;
if (pPacketHead) {
PVOID caller;
PVOID callersCaller;
GET_CALLERS_ADDRESS(&caller, &callersCaller);
if (DebugDump) {
DbgPrint("DLC.AllocatePacket: Adding new packet %08x to pool %08x. ret=%08x,%08x\n",
pPacketHead,
pPacketPool,
caller,
callersCaller
);
}
pPacketHead->Signature = PACKET_HEAD_SIGNATURE;
pPacketHead->pPacketPool = pPacketPool;
++pPacketPool->CurrentPacketCount;
DumpPoolStats("AllocatePacket", pPacketPool);
} else {
DbgPrint("DLC.AllocatePacket: Error: couldn't allocate packet for Pool %08x\n",
pPacketPool
);
}
#endif
}
if (pPacketHead) {
//
// turn on BUSY flag, turn off FREE flag
//
pPacketHead->Flags ^= (PACKET_FLAGS_FREE | PACKET_FLAGS_BUSY);
//
// zero the contents of the packet!
//
LlcZeroMem((PVOID)(pPacketHead + 1), pPacketPool->PacketSize);
PushEntryList(&pPacketPool->BusyList, (PSINGLE_LIST_ENTRY)pPacketHead);
#if DBG
GET_CALLERS_ADDRESS(&pPacketHead->CallersAddress_A,
&pPacketHead->CallersCaller_A
);
++pPacketPool->BusyCount;
++pPacketPool->Allocations;
++pPacketPool->MaxInUse;
#endif
}
KeReleaseSpinLock(&pPacketPool->PoolLock, irql);
#if DBG
--pPacketPool->ClashCount;
if (pPacketPool->ClashCount) {
DbgPrint("DLC.AllocatePacket: Error: Memory allocator clash on exit: Pool %08x\n", pPacketPool);
DbgBreakPoint();
}
#endif
//
// return pointer to packet body, not packet header
//
return pPacketHead ? (PVOID)(pPacketHead + 1) : (PVOID)pPacketHead;
}
VOID
DeallocatePacket(
IN PPACKET_POOL pPacketPool,
IN PVOID pPacket
)
/*++
Routine Description:
Returns a packet to its pool
Arguments:
pPacketPool - pointer to PACKET_POOL structure describing this pool
pPacket - pointer to previously allocated packet
Return Value:
None.
--*/
{
KIRQL irql;
PPACKET_HEAD pPacketHead = ((PPACKET_HEAD)pPacket) - 1;
PSINGLE_LIST_ENTRY p;
PSINGLE_LIST_ENTRY prev;
#if DBG
if (pPacketPool->ClashCount) {
DbgPrint("DLC.DeallocatePacket: Error: Memory allocator clash on entry: Pool %08x\n", pPacketPool);
DbgBreakPoint();
}
++pPacketPool->ClashCount;
#endif
KeAcquireSpinLock(&pPacketPool->PoolLock, &irql);
#if DBG
if (pPacketPool->Signature != PACKET_POOL_SIGNATURE) {
DbgPrint("DLC.DeallocatePacket: Error: Invalid Pool Handle %08x\n", pPacketPool);
DbgBreakPoint();
}
if (!pPacketPool->Viable) {
DbgPrint("DLC.DeallocatePacket: Error: Unviable Packet Pool %08x\n", pPacketPool);
DbgBreakPoint();
}
if (pPacketHead->Signature != PACKET_HEAD_SIGNATURE
|| pPacketHead->pPacketPool != pPacketPool
|| !(pPacketHead->Flags & PACKET_FLAGS_BUSY)
|| pPacketHead->Flags & PACKET_FLAGS_FREE) {
DbgPrint("DLC.DeallocatePacket: Error: Invalid Packet Header %08x, Pool = %08x\n",
pPacketHead,
pPacketPool
);
DbgBreakPoint();
}
#endif
//
// remove this packet from single linked list on BusyList
//
prev = (PSINGLE_LIST_ENTRY)&pPacketPool->BusyList;
for (p = prev->Next; p; p = p->Next) {
if (p == (PSINGLE_LIST_ENTRY)pPacketHead) {
break;
} else {
prev = p;
}
}
#if DBG
if (!p) {
DbgPrint("DLC.DeallocatePacket: Error: packet %08x not on BusyList of pool %08x\n",
pPacketHead,
pPacketPool
);
DumpPool(pPacketPool);
DbgBreakPoint();
}
#endif
prev->Next = pPacketHead->List.Next;
#if DBG
if (ZapDeallocatedPackets) {
//
// fill the deallocated packet with 'Z's. This will quickly tell us if
// the packet is still being used after it is deallocated
//
RtlFillMemory(pPacketHead + 1, pPacketPool->PacketSize, ZAP_DEALLOC_VALUE);
}
#endif
PushEntryList(&pPacketPool->FreeList, (PSINGLE_LIST_ENTRY)pPacketHead);
//
// turn off BUSY flag, turn on FREE flag
//
pPacketHead->Flags ^= (PACKET_FLAGS_BUSY | PACKET_FLAGS_FREE);
#if DBG
++pPacketPool->FreeCount;
--pPacketPool->BusyCount;
++pPacketPool->Frees;
--pPacketPool->MaxInUse;
// pPacketHead->CallersAddress_A = (PVOID)-1;
// pPacketHead->CallersCaller_A = (PVOID)-1;
GET_CALLERS_ADDRESS(&pPacketHead->CallersAddress_D,
&pPacketHead->CallersCaller_D
);
#endif
KeReleaseSpinLock(&pPacketPool->PoolLock, irql);
#if DBG
--pPacketPool->ClashCount;
if (pPacketPool->ClashCount) {
DbgPrint("DLC.DeallocatePacket: Error: Memory allocator clash on exit: Pool %08x\n", pPacketPool);
DbgBreakPoint();
}
#endif
}
#if DBG
#ifdef TRACK_DLC_OBJECTS
POBJECT_POOL
CreateObjectPool(
IN PMEMORY_USAGE pMemoryUsage,
IN DLC_OBJECT_TYPE ObjectType,
IN ULONG SizeOfObject,
IN ULONG NumberOfObjects
)
/*++
Routine Description:
description-of-function.
Arguments:
pMemoryUsage -
ObjectType -
SizeOfObject -
NumberOfObjects -
Return Value:
POBJECT_POOL
--*/
{
}
VOID
DeleteObjectPool(
IN PMEMORY_USAGE pMemoryUsage,
IN DLC_OBJECT_TYPE ObjectType,
IN POBJECT_POOL pObjectPool
)
/*++
Routine Description:
description-of-function.
Arguments:
pMemoryUsage -
ObjectType -
pObjectPool -
Return Value:
None.
--*/
{
}
POBJECT_HEAD
AllocatePoolObject(
IN POBJECT_POOL pObjectPool
)
/*++
Routine Description:
description-of-function.
Arguments:
pObjectPool -
Return Value:
POBJECT_HEAD
--*/
{
}
POBJECT_POOL
FreePoolObject(
IN DLC_OBJECT_TYPE ObjectType,
IN POBJECT_HEAD pObjectHead
)
/*++
Routine Description:
description-of-function.
Arguments:
ObjectType -
pObjectHead -
Return Value:
POBJECT_POOL
--*/
{
}
#endif // TRACK_DLC_OBJECTS
VOID
CheckMemoryReturned(
IN PMEMORY_USAGE pMemoryUsage
)
/*++
Routine Description:
Called when a 'handle' which owns a MEMORY_USAGE structure is being closed.
Checks that all memory has been returned and that number of allocations is
the same as number of frees
Arguments:
pMemoryUsage - pointer to MEMORY_USAGE structure to check
Return Value:
None.
--*/
{
if (pMemoryUsage->AllocateCount != pMemoryUsage->FreeCount || pMemoryUsage->NonPagedPoolAllocated) {
if (MemoryCheckNotify) {
if (pMemoryUsage->AllocateCount != pMemoryUsage->FreeCount) {
DbgPrint("DLC.CheckMemoryReturned: Error: AllocateCount != FreeCount. Usage @ %08x\n",
pMemoryUsage
);
} else {
DbgPrint("DLC.CheckMemoryReturned: Error: NonPagedPoolAllocated != 0. Usage @ %08x\n",
pMemoryUsage
);
}
}
if (MemoryCheckStop) {
DumpMemoryUsage(pMemoryUsage, TRUE);
DbgBreakPoint();
}
}
}
VOID
CheckDriverMemoryUsage(
IN BOOLEAN Break
)
/*++
Routine Description:
Checks if the driver has allocated memory & dumps usage to debugger
Arguments:
Break - if true && driver has memory, breaks into debugger
Return Value:
None.
--*/
{
DbgPrint("DLC.CheckDriverMemoryUsage\n");
DumpMemoryMetrics();
if (Break && NonPagedPoolAllocated) {
if (MemoryCheckNotify) {
DbgPrint("DLC.CheckDriverMemoryUsage: Error: Driver still has memory allocated\n");
}
if (MemoryCheckStop) {
DbgBreakPoint();
}
}
}
VOID MemoryAllocationError(PCHAR Routine, PVOID Address) {
DbgPrint("DLC.%s: Error: Memory Allocation error in block @ %08x\n", Routine, Address);
DumpMemoryMetrics();
DbgBreakPoint();
}
VOID UpdateCounter(PULONG pCounter, LONG Value) {
if (Value > 0) {
if (*pCounter + Value < *pCounter) {
MemoryCounterOverflow(pCounter, Value);
}
} else {
if (*pCounter + Value > *pCounter) {
MemoryCounterOverflow(pCounter, Value);
}
}
*pCounter += Value;
}
VOID MemoryCounterOverflow(PULONG pCounter, LONG Value) {
DbgPrint("DLC: Memory Counter Overflow: &Counter=%08x, Count=%d, Value=%d\n",
pCounter,
*pCounter,
Value
);
DumpMemoryMetrics();
}
VOID DumpMemoryMetrics() {
DbgPrint("DLC Device Driver Non-Paged Pool Usage:\n"
"\tNumber Of Good Non-Paged Pool Allocations. : %d\n"
"\tNumber Of Bad Non-Paged Pool Allocations. : %d\n"
"\tNumber Of Good Non-Paged Pool Frees. . . . : %d\n"
"\tNumber Of Bad Non-Paged Pool Frees. . . . : %d\n",
GoodNonPagedPoolAllocs,
BadNonPagedPoolAllocs,
GoodNonPagedPoolFrees,
BadNonPagedPoolFrees
);
DbgPrint("\tTotal Non-Paged Pool Currently Requested . : %d\n"
"\tTotal Non-Paged Pool Currently Allocated . : %d\n"
"\tCumulative Total Non-Paged Pool Requested. : %d\n"
"\tCumulative Total Non-Paged Pool Allocated. : %d\n"
"\tCumulative Total Non-Paged Pool Freed. . . : %d\n"
"\n",
NonPagedPoolRequested,
NonPagedPoolAllocated,
TotalNonPagedPoolRequested,
TotalNonPagedPoolAllocated,
TotalNonPagedPoolFreed
);
DumpMemoryUsageList();
}
VOID DumpPoolStats(PCHAR Routine, PPACKET_POOL pPacketPool) {
if (!DebugDump) {
return;
}
DbgPrint("DLC.%s: Stats For Pool %08x:\n"
"\tPool Owner . . . . . . . . . . . . . . . . . . . : %08x\n"
"\tPool Owner Object ID . . . . . . . . . . . . . . : %08x [%s]\n",
Routine,
pPacketPool,
pPacketPool->pMemoryUsage->Owner,
pPacketPool->pMemoryUsage->OwnerObjectId,
MapObjectId(pPacketPool->pMemoryUsage->OwnerObjectId)
);
DbgPrint("\tFree List. . . . . . . . . . . . . . . . . . . . : %08x\n"
"\tBusy List. . . . . . . . . . . . . . . . . . . . : %08x\n",
pPacketPool->FreeList,
pPacketPool->BusyList
);
DbgPrint("\tPacket Size. . . . . . . . . . . . . . . . . . . : %d\n"
"\tOriginal Number Of Packets In Pool . . . . . . . : %d\n"
"\tCurrent Number Of Packets In Pool. . . . . . . . : %d\n"
"\tNumber Of Allocations From Pool. . . . . . . . . : %d\n"
"\tNumber Of Deallocations To Pool. . . . . . . . . : %d\n"
"\tNumber Of Times No Available Packets On Allocate : %d\n"
"\tMax. Number Of Packets Allocated At Any One Time : %d\n"
"\tNumber Of Packets On Free List . . . . . . . . . : %d\n"
"\tNumber Of Packets On Busy List . . . . . . . . . : %d\n"
"\n",
pPacketPool->PacketSize,
pPacketPool->OriginalPacketCount,
pPacketPool->CurrentPacketCount,
pPacketPool->Allocations,
pPacketPool->Frees,
pPacketPool->NoneFreeCount,
pPacketPool->MaxInUse,
pPacketPool->FreeCount,
pPacketPool->BusyCount
);
DumpMemoryUsage(&pPacketPool->MemoryUsage, FALSE);
}
PCHAR MapObjectId(DLC_OBJECT_TYPE ObjectType) {
switch (ObjectType) {
case DlcDriverObject:
return "DlcDriverObject";
case FileContextObject:
return "FileContextObject";
case AdapterContextObject:
return "AdapterContextObject";
case BindingContextObject:
return "BindingContextObject";
case DlcSapObject:
return "DlcSapObject";
case DlcGroupSapObject:
return "DlcGroupSapObject";
case DlcLinkObject:
return "DlcLinkObject";
case DlcDixObject:
return "DlcDixObject";
case LlcDataLinkObject:
return "LlcDataLinkObject";
case LLcDirectObject:
return "LLcDirectObject";
case LlcSapObject:
return "LlcSapObject";
case LlcGroupSapObject:
return "LlcGroupSapObject";
case DlcBufferPoolObject:
return "DlcBufferPoolObject";
case DlcLinkPoolObject:
return "DlcLinkPoolObject";
case DlcPacketPoolObject:
return "DlcPacketPoolObject";
case LlcLinkPoolObject:
return "LlcLinkPoolObject";
case LlcPacketPoolObject:
return "LlcPacketPoolObject";
default:
return "*** UNKNOWN OBJECT TYPE ***";
}
}
VOID DumpPool(PPACKET_POOL pPacketPool) {
if (!DebugDump) {
return;
}
DumpPoolStats("DumpPool", pPacketPool);
DumpPoolList("Free", &pPacketPool->FreeList);
DumpPoolList("Busy", &pPacketPool->BusyList);
}
VOID DumpPoolList(PCHAR Name, PSINGLE_LIST_ENTRY List) {
ULONG count = 0;
if (List->Next) {
DbgPrint("\n%s List @ %08x:\n", Name, List);
while (List->Next) {
List = List->Next;
DumpPacketHead((PPACKET_HEAD)List, ++count);
}
} else {
DbgPrint("%s List is EMPTY\n\n", Name);
}
}
VOID DumpPacketHead(PPACKET_HEAD pPacketHead, ULONG Number) {
CHAR numbuf[5];
if (!DebugDump) {
return;
}
if (Number) {
int i;
ULONG div = 1000; // 1000 packets in a pool?
while (!(Number / div)) {
div /= 10;
}
for (i = 0; Number; ++i) {
numbuf[i] = (CHAR)('0' + Number / div);
Number %= div;
div /= 10;
}
numbuf[i] = 0;
Number = 1; // flag
}
DbgPrint("%s\tPACKET_HEAD @ %08x:\n"
"\tList . . . . . . . . : %08x\n"
"\tFlags. . . . . . . . : %08x\n"
"\tSignature. . . . . . : %08x\n"
"\tpPacketPool. . . . . : %08x\n"
"\tCallers Address (A). : %08x\n"
"\tCallers Caller (A) . : %08x\n"
"\tCallers Address (D). : %08x\n"
"\tCallers Caller (D) . : %08x\n"
"\n",
Number ? numbuf : "",
pPacketHead,
pPacketHead->List,
pPacketHead->Flags,
pPacketHead->Signature,
pPacketHead->pPacketPool,
pPacketHead->CallersAddress_A,
pPacketHead->CallersCaller_A,
pPacketHead->CallersAddress_D,
pPacketHead->CallersCaller_D
);
}
VOID DumpMemoryUsageList() {
PMEMORY_USAGE pMemoryUsage;
KIRQL irql;
BOOLEAN allocatedMemoryFound = FALSE;
KeAcquireSpinLock(&MemoryUsageLock, &irql);
for (pMemoryUsage = MemoryUsageList; pMemoryUsage; pMemoryUsage = pMemoryUsage->List) {
if (pMemoryUsage->NonPagedPoolAllocated) {
allocatedMemoryFound = TRUE;
DbgPrint("DLC.DumpMemoryUsageList: %08x: %d bytes memory allocated\n",
pMemoryUsage,
pMemoryUsage->NonPagedPoolAllocated
);
DumpMemoryUsage(pMemoryUsage, FALSE);
}
}
KeReleaseSpinLock(&MemoryUsageLock, irql);
if (!allocatedMemoryFound) {
DbgPrint("DLC.DumpMemoryUsageList: No allocated memory found\n");
}
}
VOID DumpMemoryUsage(PMEMORY_USAGE pMemoryUsage, BOOLEAN Override) {
if (!DebugDump && !Override) {
return;
}
DbgPrint("MEMORY_USAGE @ %08x:\n"
"\tOwner. . . . . . . . . . . . . : %08x\n"
"\tOwner Object ID. . . . . . . . : %08x [%s]\n"
"\tOwner Instance . . . . . . . . : %x\n",
pMemoryUsage,
pMemoryUsage->Owner,
pMemoryUsage->OwnerObjectId,
MapObjectId(pMemoryUsage->OwnerObjectId),
pMemoryUsage->OwnerInstance
);
DbgPrint("\tNon Paged Pool Allocated . . . : %d\n"
"\tNumber Of Allocations. . . . . : %d\n"
"\tNumber Of Frees. . . . . . . . : %d\n"
"\tPrivate Allocation List Flink. : %08x\n"
"\tPrivate Allocation List Blink. : %08x\n"
"\n",
pMemoryUsage->NonPagedPoolAllocated,
pMemoryUsage->AllocateCount,
pMemoryUsage->FreeCount,
pMemoryUsage->PrivateList.Flink,
pMemoryUsage->PrivateList.Blink
);
}
VOID
CollectReturnAddresses(
OUT PVOID* ReturnAddresses,
IN ULONG AddressesToCollect,
IN ULONG AddressesToSkip
)
{
PVOID* ebp = (PVOID*)*(PVOID**)&ReturnAddresses - 2;
while (AddressesToSkip--) {
GetLastReturnAddress(&ebp);
}
while (AddressesToCollect--) {
*ReturnAddresses++ = GetLastReturnAddress(&ebp);
}
}
PVOID* GetLastReturnAddress(PVOID** pEbp) {
PVOID* returnAddress = *(*pEbp + 1);
*pEbp = **pEbp;
return returnAddress;
}
#ifdef i386
VOID x86SleazeCallersAddress(PVOID* pCaller, PVOID* pCallerCaller) {
//
// this only works on x86 and only if not fpo functions!
//
PVOID* ebp;
ebp = (PVOID*)&pCaller - 2; // told you it was sleazy
ebp = (PVOID*)*(PVOID*)ebp;
*pCaller = *(ebp + 1);
ebp = (PVOID*)*(PVOID*)ebp;
*pCallerCaller = *(ebp + 1);
}
#endif
BOOLEAN VerifyElementOnList(PSINGLE_LIST_ENTRY List, PSINGLE_LIST_ENTRY Element) {
while (List) {
if (List == Element) {
return TRUE;
}
List = List->Next;
}
return FALSE;
}
VOID CheckList(PSINGLE_LIST_ENTRY List, ULONG NumberOfElements) {
PSINGLE_LIST_ENTRY originalList = List;
while (NumberOfElements--) {
if (List->Next == NULL) {
DbgPrint("DLC.CheckList: Error: too few entries on list %08x\n", originalList);
DbgBreakPoint();
} else {
List = List->Next;
}
}
if (List->Next != NULL) {
DbgPrint("DLC.CheckList: Error: too many entries on list %08x\n", originalList);
DbgBreakPoint();
}
}
VOID CheckEntryOnList(PLIST_ENTRY Entry, PLIST_ENTRY List, BOOLEAN Sense) {
BOOLEAN found = FALSE;
PLIST_ENTRY p;
if (!IsListEmpty(List)) {
for (p = List->Flink; p != List; p = p->Flink) {
if (p == Entry) {
found = TRUE;
break;
}
}
}
if (found != Sense) {
if (found) {
DbgPrint("DLC.CheckEntryOnList: Error: Entry %08x found on list %08x. Not supposed to be there\n",
Entry,
List
);
} else {
DbgPrint("DLC.CheckEntryOnList: Error: Entry %08x not found on list %08x\n",
Entry,
List
);
}
if (MemoryCheckStop) {
DbgBreakPoint();
}
}
}
VOID DumpPrivateMemoryHeader(PPRIVATE_NON_PAGED_POOL_HEAD pHead) {
DbgPrint("Private Non Paged Pool Header @ %08x:\n"
"\tSize . . . . . . . : %d\n"
"\tOriginal Size. . . : %d\n"
"\tFlags. . . . . . . : %08x\n"
"\tSignature. . . . . : %08x\n"
"\tGlobalList.Flink . : %08x\n"
"\tGlobalList.Blink . : %08x\n"
"\tPrivateList.Flink. : %08x\n"
"\tPrivateList.Blink. : %08x\n"
"\tStack[0] . . . . . : %08x\n"
"\tStack[1] . . . . . : %08x\n"
"\tStack[2] . . . . . : %08x\n"
"\tStack[3] . . . . . : %08x\n"
"\n",
pHead->Size,
pHead->OriginalSize,
pHead->Flags,
pHead->Signature,
pHead->GlobalList.Flink,
pHead->GlobalList.Blink,
pHead->PrivateList.Flink,
pHead->PrivateList.Blink,
pHead->Stack[0],
pHead->Stack[1],
pHead->Stack[2],
pHead->Stack[3]
);
}
VOID ReportSwitchSettings(PSTR str) {
DbgPrint("%s: LLCMEM Switches:\n"
"\tDebugDump . . . . . . : %s\n"
"\tDeleteBusyListAnyway. : %s\n"
"\tMemoryCheckNotify . . : %s\n"
"\tMemoryCheckStop . . . : %s\n"
"\tMaintainGlobalLists . : %s\n"
"\tMaintainPrivateLists. : %s\n"
"\tZapDeallocatedPackets : %s\n"
"\tZapFreedMemory. . . . : %s\n"
"\n",
str,
YES_NO(DebugDump),
YES_NO(DeleteBusyListAnyway),
YES_NO(MemoryCheckNotify),
YES_NO(MemoryCheckStop),
YES_NO(MaintainGlobalLists),
YES_NO(MaintainPrivateLists),
YES_NO(ZapDeallocatedPackets),
YES_NO(ZapFreedMemory)
);
}
#endif