1 /* $Id: npool.c,v 1.74 2003/08/19 23:52:36 dwelch Exp $
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/mm/npool.c
6 * PURPOSE: Implements the kernel memory pool
7 * PROGRAMMER: David Welch (welch@cwcom.net)
10 * 10/06/98: Bug fixes by Iwan Fatahi (i_fatahi@hotmail.com)
11 * in take_block (if current bigger than required)
12 * in remove_from_used_list
14 * 23/08/98: Fixes from Robert Bergkvist (fragdance@hotmail.com)
17 /* INCLUDES ****************************************************************/
19 #include <ddk/ntddk.h>
20 #include <internal/mm.h>
21 #include <internal/ntoskrnl.h>
22 #include <internal/pool.h>
25 #include <internal/debug.h>
27 /* Enable strict checking of the nonpaged pool on every allocation */
28 //#define ENABLE_VALIDATE_POOL
30 /* Enable tracking of statistics about the tagged blocks in the pool */
31 #define TAG_STATISTICS_TRACKING
34 * Put each block in its own range of pages and position the block at the
35 * end of the range so any accesses beyond the end of block are to invalid
38 /*#define WHOLE_PAGE_ALLOCATIONS*/
40 #ifdef ENABLE_VALIDATE_POOL
41 #define VALIDATE_POOL validate_kernel_pool()
47 #define POOL_TRACE(args...) do { DbgPrint(args); } while(0);
49 #define POOL_TRACE(args...)
52 /* avl types ****************************************************************/
55 * This declarations should be moved into a separate header file.
60 struct _NODE
* link
[2];
65 /* TYPES *******************************************************************/
67 #define BLOCK_HDR_USED_MAGIC (0xdeadbeef)
68 #define BLOCK_HDR_FREE_MAGIC (0xceadbeef)
71 * fields present at the start of a block (this is for internal use only)
73 typedef struct _BLOCK_HDR
78 LIST_ENTRY AddressList
;
79 LIST_ENTRY TagListEntry
;
87 ExAllocateWholePageBlock(ULONG Size
);
89 ExFreeWholePageBlock(PVOID Addr
);
91 /* GLOBALS *****************************************************************/
93 extern PVOID MiNonPagedPoolStart
;
94 extern ULONG MiNonPagedPoolLength
;
95 static ULONG MiCurrentNonPagedPoolLength
= 0;
98 * Head of the list of free blocks
100 static PNODE FreeBlockListRoot
= NULL
;
103 * Head of the list of in use block
105 static LIST_ENTRY UsedBlockListHead
;
107 static LIST_ENTRY AddressListHead
;
109 #ifndef WHOLE_PAGE_ALLOCATIONS
111 * Count of free blocks
113 static ULONG EiNrFreeBlocks
= 0;
116 * Count of used blocks
118 static ULONG EiNrUsedBlocks
= 0;
122 * Lock that protects the non-paged pool data structures
124 static KSPIN_LOCK MmNpoolLock
;
127 * Total memory used for free nonpaged pool blocks
129 ULONG EiFreeNonPagedPool
= 0;
132 * Total memory used for nonpaged pool blocks
134 ULONG EiUsedNonPagedPool
= 0;
137 * Allocate a range of memory in the nonpaged pool
140 MiAllocNonPagedPoolRegion(unsigned int nr_pages
);
143 MiFreeNonPagedPoolRegion(PVOID Addr
, ULONG Count
, BOOLEAN Free
);
145 #ifdef TAG_STATISTICS_TRACKING
146 #define TAG_HASH_TABLE_SIZE (1024)
147 static LIST_ENTRY tag_hash_table
[TAG_HASH_TABLE_SIZE
];
148 #endif /* TAG_STATISTICS_TRACKING */
150 #ifdef WHOLE_PAGE_ALLOCATIONS
151 static UCHAR NonPagedPoolAllocMapBuffer
[ROUND_UP(MM_NONPAGED_POOL_SIZE
/ PAGE_SIZE
, 32) / 8];
152 static RTL_BITMAP NonPagedPoolAllocMap
;
153 static ULONG NonPagedPoolAllocMapHint
;
154 #endif /* WHOLE_PAGE_ALLOCATIONS */
156 /* avl helper functions ****************************************************/
158 void DumpFreeBlockNode(PNODE p
)
160 static int count
= 0;
167 DumpFreeBlockNode(p
->link
[0]);
168 blk
= CONTAINING_RECORD(p
, BLOCK_HDR
, Node
);
169 DbgPrint("%08x %8d (%d)\n", blk
, blk
->Size
, count
);
170 DumpFreeBlockNode(p
->link
[1]);
175 void DumpFreeBlockTree(void)
177 DbgPrint("--- Begin tree ------------------\n");
178 DbgPrint("%08x\n", CONTAINING_RECORD(FreeBlockListRoot
, BLOCK_HDR
, Node
));
179 DumpFreeBlockNode(FreeBlockListRoot
);
180 DbgPrint("--- End tree --------------------\n");
183 int compare_node(PNODE p1
, PNODE p2
)
185 BLOCK_HDR
* blk1
= CONTAINING_RECORD(p1
, BLOCK_HDR
, Node
);
186 BLOCK_HDR
* blk2
= CONTAINING_RECORD(p2
, BLOCK_HDR
, Node
);
188 if (blk1
->Size
== blk2
->Size
)
201 if (blk1
->Size
< blk2
->Size
)
205 if (blk1
->Size
> blk2
->Size
)
214 int compare_value(PVOID value
, PNODE p
)
216 BLOCK_HDR
* blk
= CONTAINING_RECORD(p
, BLOCK_HDR
, Node
);
217 ULONG v
= *(PULONG
)value
;
230 /* avl functions **********************************************************/
233 * The avl functions should be moved into a separate file.
236 /* The avl_insert and avl_remove are based on libavl (library for manipulation of binary trees). */
238 void avl_insert (PNODE
* root
, PNODE n
, int (*compare
)(PNODE
, PNODE
))
240 PNODE y
; /* Top node to update balance factor, and parent. */
241 PNODE p
, q
; /* Iterator, and parent. */
242 PNODE w
; /* New root of rebalanced subtree. */
243 int dir
; /* Direction to descend. */
245 n
->link
[0] = n
->link
[1] = n
->parent
= NULL
;
249 for (q
= NULL
, p
= *root
; p
!= NULL
; q
= p
, p
= p
->link
[dir
])
251 dir
= compare(n
, p
) > 0;
273 for (p
= n
; p
!= y
; p
= q
)
276 dir
= q
->link
[0] != p
;
287 if (y
->balance
== -2)
289 PNODE x
= y
->link
[0];
290 if (x
->balance
== -1)
293 y
->link
[0] = x
->link
[1];
295 x
->balance
= y
->balance
= 0;
296 x
->parent
= y
->parent
;
298 if (y
->link
[0] != NULL
)
300 y
->link
[0]->parent
= y
;
305 assert (x
->balance
== +1);
307 x
->link
[1] = w
->link
[0];
309 y
->link
[0] = w
->link
[1];
311 if (w
->balance
== -1)
316 else if (w
->balance
== 0)
318 x
->balance
= y
->balance
= 0;
320 else /* |w->pavl_balance == +1| */
326 w
->parent
= y
->parent
;
327 x
->parent
= y
->parent
= w
;
328 if (x
->link
[1] != NULL
)
330 x
->link
[1]->parent
= x
;
332 if (y
->link
[0] != NULL
)
334 y
->link
[0]->parent
= y
;
338 else if (y
->balance
== +2)
340 PNODE x
= y
->link
[1];
341 if (x
->balance
== +1)
344 y
->link
[1] = x
->link
[0];
346 x
->balance
= y
->balance
= 0;
347 x
->parent
= y
->parent
;
349 if (y
->link
[1] != NULL
)
351 y
->link
[1]->parent
= y
;
356 assert (x
->balance
== -1);
358 x
->link
[0] = w
->link
[1];
360 y
->link
[1] = w
->link
[0];
367 else if (w
->balance
== 0)
369 x
->balance
= y
->balance
= 0;
371 else /* |w->pavl_balance == -1| */
377 w
->parent
= y
->parent
;
378 x
->parent
= y
->parent
= w
;
379 if (x
->link
[0] != NULL
)
381 x
->link
[0]->parent
= x
;
383 if (y
->link
[1] != NULL
)
385 y
->link
[1]->parent
= y
;
393 if (w
->parent
!= NULL
)
395 w
->parent
->link
[y
!= w
->parent
->link
[0]] = w
;
405 void avl_remove (PNODE
*root
, PNODE item
, int (*compare
)(PNODE
, PNODE
))
407 PNODE p
; /* Traverses tree to find node to delete. */
408 PNODE q
; /* Parent of |p|. */
409 int dir
; /* Side of |q| on which |p| is linked. */
411 if (root
== NULL
|| *root
== NULL
)
425 dir
= compare(p
, q
) > 0;
428 if (p
->link
[1] == NULL
)
430 q
->link
[dir
] = p
->link
[0];
431 if (q
->link
[dir
] != NULL
)
433 q
->link
[dir
]->parent
= p
->parent
;
438 PNODE r
= p
->link
[1];
439 if (r
->link
[0] == NULL
)
441 r
->link
[0] = p
->link
[0];
443 r
->parent
= p
->parent
;
444 if (r
->link
[0] != NULL
)
446 r
->link
[0]->parent
= r
;
448 r
->balance
= p
->balance
;
454 PNODE s
= r
->link
[0];
455 while (s
->link
[0] != NULL
)
460 r
->link
[0] = s
->link
[1];
461 s
->link
[0] = p
->link
[0];
462 s
->link
[1] = p
->link
[1];
464 if (s
->link
[0] != NULL
)
466 s
->link
[0]->parent
= s
;
468 s
->link
[1]->parent
= s
;
469 s
->parent
= p
->parent
;
470 if (r
->link
[0] != NULL
)
472 r
->link
[0]->parent
= r
;
474 s
->balance
= p
->balance
;
480 item
->link
[0] = item
->link
[1] = item
->parent
= NULL
;
483 while (q
!= (PNODE
) root
)
487 if (y
->parent
!= NULL
)
498 dir
= q
->link
[0] != y
;
500 if (y
->balance
== +1)
504 else if (y
->balance
== +2)
506 PNODE x
= y
->link
[1];
507 if (x
->balance
== -1)
511 assert (x
->balance
== -1);
513 x
->link
[0] = w
->link
[1];
515 y
->link
[1] = w
->link
[0];
517 if (w
->balance
== +1)
522 else if (w
->balance
== 0)
524 x
->balance
= y
->balance
= 0;
526 else /* |w->pavl_balance == -1| */
532 w
->parent
= y
->parent
;
533 x
->parent
= y
->parent
= w
;
534 if (x
->link
[0] != NULL
)
536 x
->link
[0]->parent
= x
;
538 if (y
->link
[1] != NULL
)
540 y
->link
[1]->parent
= y
;
546 y
->link
[1] = x
->link
[0];
548 x
->parent
= y
->parent
;
550 if (y
->link
[1] != NULL
)
552 y
->link
[1]->parent
= y
;
563 x
->balance
= y
->balance
= 0;
571 dir
= q
->link
[0] != y
;
573 if (y
->balance
== -1)
577 else if (y
->balance
== -2)
579 PNODE x
= y
->link
[0];
580 if (x
->balance
== +1)
583 assert (x
->balance
== +1);
585 x
->link
[1] = w
->link
[0];
587 y
->link
[0] = w
->link
[1];
589 if (w
->balance
== -1)
594 else if (w
->balance
== 0)
596 x
->balance
= y
->balance
= 0;
598 else /* |w->pavl_balance == +1| */
604 w
->parent
= y
->parent
;
605 x
->parent
= y
->parent
= w
;
606 if (x
->link
[1] != NULL
)
608 x
->link
[1]->parent
= x
;
610 if (y
->link
[0] != NULL
)
612 y
->link
[0]->parent
= y
;
618 y
->link
[0] = x
->link
[1];
620 x
->parent
= y
->parent
;
622 if (y
->link
[0] != NULL
)
624 y
->link
[0]->parent
= y
;
635 x
->balance
= y
->balance
= 0;
645 PNODE
avl_get_next(PNODE root
, PNODE p
)
660 while (q
&& q
->link
[1] == p
)
676 PNODE
avl_find_equal_or_greater(PNODE root
, ULONG size
, int (compare
)(PVOID
, PNODE
))
682 for (p
= root
; p
!= NULL
;)
684 cmp
= compare((PVOID
)&size
, p
);
698 cmp
= compare((PVOID
)&size
, p
->link
[0]);
711 /* non paged pool functions ************************************************/
713 #ifdef TAG_STATISTICS_TRACKING
715 MiRemoveFromTagHashTable(BLOCK_HDR
* block
)
717 * Remove a block from the tag hash table
725 RemoveEntryList(&block
->TagListEntry
);
729 MiAddToTagHashTable(BLOCK_HDR
* block
)
731 * Add a block to the tag hash table
741 hash
= block
->Tag
% TAG_HASH_TABLE_SIZE
;
743 InsertHeadList(&tag_hash_table
[hash
], &block
->TagListEntry
);
745 #endif /* TAG_STATISTICS_TRACKING */
748 MiInitializeNonPagedPool(VOID
)
750 #ifdef TAG_STATISTICS_TRACKING
752 for (i
= 0; i
< TAG_HASH_TABLE_SIZE
; i
++)
754 InitializeListHead(&tag_hash_table
[i
]);
757 MiCurrentNonPagedPoolLength
= 0;
758 KeInitializeSpinLock(&MmNpoolLock
);
759 InitializeListHead(&UsedBlockListHead
);
760 InitializeListHead(&AddressListHead
);
761 FreeBlockListRoot
= NULL
;
762 #ifdef WHOLE_PAGE_ALLOCATIONS
763 RtlInitializeBitMap(&NonPagedPoolAllocMap
, (PVOID
)&NonPagedPoolAllocMapBuffer
, MM_NONPAGED_POOL_SIZE
/ PAGE_SIZE
);
764 RtlClearAllBits(&NonPagedPoolAllocMap
);
765 NonPagedPoolAllocMapHint
= 0;
769 #if defined(TAG_STATISTICS_TRACKING) && !defined(WHOLE_PAGE_ALLOCATIONS)
771 MiDumpTagStats(ULONG CurrentTag
, ULONG CurrentNrBlocks
, ULONG CurrentSize
)
775 c1
= (CurrentTag
>> 24) & 0xFF;
776 c2
= (CurrentTag
>> 16) & 0xFF;
777 c3
= (CurrentTag
>> 8) & 0xFF;
778 c4
= CurrentTag
& 0xFF;
780 if (isprint(c1
) && isprint(c2
) && isprint(c3
) && isprint(c4
))
782 DbgPrint("Tag %x (%c%c%c%c) Blocks %d Total Size %d Average Size %d\n",
783 CurrentTag
, c4
, c3
, c2
, c1
, CurrentNrBlocks
,
784 CurrentSize
, CurrentSize
/ CurrentNrBlocks
);
788 DbgPrint("Tag %x Blocks %d Total Size %d Average Size %d\n",
789 CurrentTag
, CurrentNrBlocks
, CurrentSize
,
790 CurrentSize
/ CurrentNrBlocks
);
793 #endif /* defined(TAG_STATISTICS_TRACKING) && !defined(WHOLE_PAGE_ALLOCATIONS); */
796 MiDebugDumpNonPagedPoolStats(BOOLEAN NewOnly
)
798 #if defined(TAG_STATISTICS_TRACKING) && !defined(WHOLE_PAGE_ALLOCATIONS)
802 ULONG CurrentNrBlocks
;
806 LIST_ENTRY tmpListHead
;
807 PLIST_ENTRY current_entry
;
809 DbgPrint("******* Dumping non paging pool stats ******\n");
812 for (i
= 0; i
< TAG_HASH_TABLE_SIZE
; i
++)
814 InitializeListHead(&tmpListHead
);
816 while (!IsListEmpty(&tag_hash_table
[i
]))
820 current_entry
= tag_hash_table
[i
].Flink
;
821 while (current_entry
!= &tag_hash_table
[i
])
823 current
= CONTAINING_RECORD(current_entry
, BLOCK_HDR
, TagListEntry
);
824 current_entry
= current_entry
->Flink
;
827 CurrentTag
= current
->Tag
;
831 if (current
->Tag
== CurrentTag
)
833 RemoveEntryList(¤t
->TagListEntry
);
834 InsertHeadList(&tmpListHead
, ¤t
->TagListEntry
);
835 if (!NewOnly
|| !current
->Dumped
)
839 CurrentSize
+= current
->Size
;
840 TotalSize
+= current
->Size
;
841 current
->Dumped
= TRUE
;
845 if (CurrentTag
!= 0 && CurrentNrBlocks
!= 0)
847 MiDumpTagStats(CurrentTag
, CurrentNrBlocks
, CurrentSize
);
850 if (!IsListEmpty(&tmpListHead
))
852 tag_hash_table
[i
].Flink
= tmpListHead
.Flink
;
853 tag_hash_table
[i
].Flink
->Blink
= &tag_hash_table
[i
];
854 tag_hash_table
[i
].Blink
= tmpListHead
.Blink
;
855 tag_hash_table
[i
].Blink
->Flink
= &tag_hash_table
[i
];
858 if (TotalBlocks
!= 0)
860 DbgPrint("TotalBlocks %d TotalSize %d AverageSize %d\n",
861 TotalBlocks
, TotalSize
, TotalSize
/ TotalBlocks
);
865 DbgPrint("TotalBlocks %d TotalSize %d\n",
866 TotalBlocks
, TotalSize
);
868 DbgPrint("Freeblocks %d TotalFreeSize %d AverageFreeSize %d\n",
869 EiNrFreeBlocks
, EiFreeNonPagedPool
, EiNrFreeBlocks
? EiFreeNonPagedPool
/ EiNrFreeBlocks
: 0);
870 DbgPrint("***************** Dump Complete ***************\n");
871 #endif /* defined(TAG_STATISTICS_TRACKING) && !defined(WHOLE_PAGE_ALLOCATIONS) */
875 MiDebugDumpNonPagedPool(BOOLEAN NewOnly
)
878 PLIST_ENTRY current_entry
;
881 KeAcquireSpinLock(&MmNpoolLock
, &oldIrql
);
883 DbgPrint("******* Dumping non paging pool contents ******\n");
884 current_entry
= UsedBlockListHead
.Flink
;
885 while (current_entry
!= &UsedBlockListHead
)
887 current
= CONTAINING_RECORD(current_entry
, BLOCK_HDR
, ListEntry
);
888 if (!NewOnly
|| !current
->Dumped
)
892 c1
= (current
->Tag
>> 24) & 0xFF;
893 c2
= (current
->Tag
>> 16) & 0xFF;
894 c3
= (current
->Tag
>> 8) & 0xFF;
895 c4
= current
->Tag
& 0xFF;
897 if (isprint(c1
) && isprint(c2
) && isprint(c3
) && isprint(c4
))
899 DbgPrint("Size 0x%x Tag 0x%x (%c%c%c%c) Allocator 0x%x\n",
900 current
->Size
, current
->Tag
, c4
, c3
, c2
, c1
,
905 DbgPrint("Size 0x%x Tag 0x%x Allocator 0x%x\n",
906 current
->Size
, current
->Tag
, current
->Caller
);
908 current
->Dumped
= TRUE
;
910 current_entry
= current_entry
->Flink
;
912 DbgPrint("***************** Dump Complete ***************\n");
913 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
916 #ifndef WHOLE_PAGE_ALLOCATIONS
918 #ifdef ENABLE_VALIDATE_POOL
919 static void validate_free_list(void)
921 * FUNCTION: Validate the integrity of the list of free blocks
925 PLIST_ENTRY current_entry
;
926 unsigned int blocks_seen
=0;
928 current_entry
= MiFreeBlockListHead
.Flink
;
929 while (current_entry
!= &MiFreeBlockListHead
)
933 current
= CONTAINING_RECORD(current_entry
, BLOCK_HDR
, ListEntry
);
934 base_addr
= (PVOID
)current
;
936 if (current
->Magic
!= BLOCK_HDR_FREE_MAGIC
)
938 DbgPrint("Bad block magic (probable pool corruption) at %x\n",
940 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
943 if (base_addr
< MiNonPagedPoolStart
||
944 MiNonPagedPoolStart
+ current
->Size
> MiNonPagedPoolStart
+ MiCurrentNonPagedPoolLength
)
946 DbgPrint("Block %x found outside pool area\n",current
);
947 DbgPrint("Size %d\n",current
->Size
);
948 DbgPrint("Limits are %x %x\n",MiNonPagedPoolStart
,
949 MiNonPagedPoolStart
+MiCurrentNonPagedPoolLength
);
950 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
953 if (blocks_seen
> MiNrFreeBlocks
)
955 DbgPrint("Too many blocks on free list\n");
956 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
958 if (current
->ListEntry
.Flink
!= &MiFreeBlockListHead
&&
959 current
->ListEntry
.Flink
->Blink
!= ¤t
->ListEntry
)
961 DbgPrint("%s:%d:Break in list (current %x next %x "
962 "current->next->previous %x)\n",
963 __FILE__
,__LINE__
,current
, current
->ListEntry
.Flink
,
964 current
->ListEntry
.Flink
->Blink
);
965 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
968 current_entry
= current_entry
->Flink
;
972 static void validate_used_list(void)
974 * FUNCTION: Validate the integrity of the list of used blocks
978 PLIST_ENTRY current_entry
;
979 unsigned int blocks_seen
=0;
981 current_entry
= UsedBlockListHead
.Flink
;
982 while (current_entry
!= &UsedBlockListHead
)
986 current
= CONTAINING_RECORD(current_entry
, BLOCK_HDR
, ListEntry
);
987 base_addr
= (PVOID
)current
;
989 if (current
->Magic
!= BLOCK_HDR_USED_MAGIC
)
991 DbgPrint("Bad block magic (probable pool corruption) at %x\n",
993 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
995 if (base_addr
< MiNonPagedPoolStart
||
996 (base_addr
+current
->Size
) >
997 MiNonPagedPoolStart
+MiCurrentNonPagedPoolLength
)
999 DbgPrint("Block %x found outside pool area\n",current
);
1003 if (blocks_seen
> EiNrUsedBlocks
)
1005 DbgPrint("Too many blocks on used list\n");
1008 if (current
->ListEntry
.Flink
!= &UsedBlockListHead
&&
1009 current
->ListEntry
.Flink
->Blink
!= ¤t
->ListEntry
)
1011 DbgPrint("Break in list (current %x next %x)\n",
1012 current
, current
->ListEntry
.Flink
);
1016 current_entry
= current_entry
->Flink
;
1020 static void check_duplicates(BLOCK_HDR
* blk
)
1022 * FUNCTION: Check a block has no duplicates
1024 * blk = block to check
1025 * NOTE: Bug checks if duplicates are found
1028 char* base
= (char*)blk
;
1029 char* last
= ((char*)blk
) + +sizeof(BLOCK_HDR
) + blk
->Size
;
1031 PLIST_ENTRY current_entry
;
1033 current_entry
= MiFreeBlockListHead
.Flink
;
1034 while (current_entry
!= &MiFreeBlockListHead
)
1036 current
= CONTAINING_RECORD(current_entry
, BLOCK_HDR
, ListEntry
);
1038 if (current
->Magic
!= BLOCK_HDR_FREE_MAGIC
)
1040 DbgPrint("Bad block magic (probable pool corruption) at %x\n",
1042 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
1045 if ( (char*)current
> base
&& (char*)current
< last
)
1047 DbgPrint("intersecting blocks on list\n");
1050 if ( (char*)current
< base
&&
1051 ((char*)current
+ current
->Size
+ sizeof(BLOCK_HDR
))
1054 DbgPrint("intersecting blocks on list\n");
1058 current_entry
= current_entry
->Flink
;
1061 current_entry
= UsedBlockListHead
.Flink
;
1062 while (current_entry
!= &UsedBlockListHead
)
1064 current
= CONTAINING_RECORD(current_entry
, BLOCK_HDR
, ListEntry
);
1066 if ( (char*)current
> base
&& (char*)current
< last
)
1068 DbgPrint("intersecting blocks on list\n");
1071 if ( (char*)current
< base
&&
1072 ((char*)current
+ current
->Size
+ sizeof(BLOCK_HDR
))
1075 DbgPrint("intersecting blocks on list\n");
1079 current_entry
= current_entry
->Flink
;
1084 static void validate_kernel_pool(void)
1086 * FUNCTION: Checks the integrity of the kernel memory heap
1090 PLIST_ENTRY current_entry
;
1092 validate_free_list();
1093 validate_used_list();
1095 current_entry
= MiFreeBlockListHead
.Flink
;
1096 while (current_entry
!= &MiFreeBlockListHead
)
1098 current
= CONTAINING_RECORD(current_entry
, BLOCK_HDR
, ListEntry
);
1099 check_duplicates(current
);
1100 current_entry
= current_entry
->Flink
;
1102 current_entry
= UsedBlockListHead
.Flink
;
1103 while (current_entry
!= &UsedBlockListHead
)
1105 current
= CONTAINING_RECORD(current_entry
, BLOCK_HDR
, ListEntry
);
1106 check_duplicates(current
);
1107 current_entry
= current_entry
->Flink
;
1114 free_pages(BLOCK_HDR
* blk
)
1121 end
= (ULONG
)blk
+ sizeof(BLOCK_HDR
) + blk
->Size
;
1124 * If the block doesn't contain a whole page then there is nothing to do
1126 if (PAGE_ROUND_UP(start
) >= PAGE_ROUND_DOWN(end
))
1133 static void remove_from_used_list(BLOCK_HDR
* current
)
1135 RemoveEntryList(¤t
->ListEntry
);
1136 EiUsedNonPagedPool
-= current
->Size
;
1140 static void remove_from_free_list(BLOCK_HDR
* current
)
1142 DPRINT("remove_from_free_list %d\n", current
->Size
);
1144 avl_remove(&FreeBlockListRoot
, ¤t
->Node
, compare_node
);
1146 EiFreeNonPagedPool
-= current
->Size
;
1148 DPRINT("remove_from_free_list done\n");
1150 DumpFreeBlockTree();
1155 add_to_free_list(BLOCK_HDR
* blk
)
1157 * FUNCTION: add the block to the free list (internal)
1162 DPRINT("add_to_free_list (%d)\n", blk
->Size
);
1166 if (blk
->AddressList
.Blink
!= &AddressListHead
)
1168 current
= CONTAINING_RECORD(blk
->AddressList
.Blink
, BLOCK_HDR
, AddressList
);
1169 if (current
->Magic
== BLOCK_HDR_FREE_MAGIC
&&
1170 (PVOID
)current
+ current
->Size
+ sizeof(BLOCK_HDR
) == (PVOID
)blk
)
1173 remove_from_free_list(current
);
1174 RemoveEntryList(&blk
->AddressList
);
1175 current
->Size
= current
->Size
+ sizeof(BLOCK_HDR
) + blk
->Size
;
1176 current
->Magic
= BLOCK_HDR_USED_MAGIC
;
1177 memset(blk
, 0xcc, sizeof(BLOCK_HDR
));
1182 if (blk
->AddressList
.Flink
!= &AddressListHead
)
1184 current
= CONTAINING_RECORD(blk
->AddressList
.Flink
, BLOCK_HDR
, AddressList
);
1185 if (current
->Magic
== BLOCK_HDR_FREE_MAGIC
&&
1186 (PVOID
)blk
+ blk
->Size
+ sizeof(BLOCK_HDR
) == (PVOID
)current
)
1189 remove_from_free_list(current
);
1190 RemoveEntryList(¤t
->AddressList
);
1191 blk
->Size
= blk
->Size
+ sizeof(BLOCK_HDR
) + current
->Size
;
1192 memset(current
, 0xcc, sizeof(BLOCK_HDR
));
1196 DPRINT("%d\n", blk
->Size
);
1197 blk
->Magic
= BLOCK_HDR_FREE_MAGIC
;
1198 EiFreeNonPagedPool
+= blk
->Size
;
1199 avl_insert(&FreeBlockListRoot
, &blk
->Node
, compare_node
);
1201 DPRINT("add_to_free_list done\n");
1203 DumpFreeBlockTree();
1207 static void add_to_used_list(BLOCK_HDR
* blk
)
1209 * FUNCTION: add the block to the used list (internal)
1212 InsertHeadList(&UsedBlockListHead
, &blk
->ListEntry
);
1213 EiUsedNonPagedPool
+= blk
->Size
;
1217 inline static void* block_to_address(BLOCK_HDR
* blk
)
1219 * FUNCTION: Translate a block header address to the corresponding block
1220 * address (internal)
1223 return ( (void *) ((char*)blk
+ sizeof(BLOCK_HDR
)) );
1226 inline static BLOCK_HDR
* address_to_block(void* addr
)
1228 return (BLOCK_HDR
*)
1229 ( ((char*)addr
) - sizeof(BLOCK_HDR
) );
1232 static BLOCK_HDR
* lookup_block(unsigned int size
)
1235 BLOCK_HDR
* best
= NULL
;
1237 PVOID block
, block_boundary
;
1240 DPRINT("lookup_block %d\n", size
);
1242 if (size
< PAGE_SIZE
)
1244 p
= avl_find_equal_or_greater(FreeBlockListRoot
, size
, compare_value
);
1247 best
= CONTAINING_RECORD(p
, BLOCK_HDR
, Node
);
1252 p
= avl_find_equal_or_greater(FreeBlockListRoot
, size
, compare_value
);
1256 current
= CONTAINING_RECORD(p
, BLOCK_HDR
, Node
);
1257 block
= block_to_address(current
);
1258 block_boundary
= (PVOID
)PAGE_ROUND_UP((ULONG
)block
);
1259 new_size
= (ULONG
)block_boundary
- (ULONG
)block
+ size
;
1260 if (new_size
!= size
&& (ULONG
)block_boundary
- (ULONG
)block
< sizeof(BLOCK_HDR
))
1262 new_size
+= PAGE_SIZE
;
1264 if (current
->Size
>= new_size
&&
1265 (best
== NULL
|| current
->Size
< best
->Size
))
1269 if (best
&& current
->Size
>= size
+ PAGE_SIZE
+ 2 * sizeof(BLOCK_HDR
))
1273 p
= avl_get_next(FreeBlockListRoot
, p
);
1277 DPRINT("lookup_block done %d\n", best
? best
->Size
: 0);
1281 static void* take_block(BLOCK_HDR
* current
, unsigned int size
,
1282 ULONG Tag
, PVOID Caller
)
1284 * FUNCTION: Allocate a used block of least 'size' from the specified
1286 * RETURNS: The address of the created memory block
1290 BOOL Removed
= FALSE
;
1292 DPRINT("take_block\n");
1294 if (size
>= PAGE_SIZE
)
1296 blk
= address_to_block((PVOID
)PAGE_ROUND_UP(block_to_address (current
)));
1299 if ((ULONG
)blk
- (ULONG
)current
< sizeof(BLOCK_HDR
))
1301 (ULONG
)blk
+= PAGE_SIZE
;
1303 assert((ULONG
)blk
- (ULONG
)current
+ size
<= current
->Size
&& (ULONG
)blk
- (ULONG
)current
>= sizeof(BLOCK_HDR
));
1305 memset(blk
, 0, sizeof(BLOCK_HDR
));
1306 blk
->Magic
= BLOCK_HDR_USED_MAGIC
;
1307 blk
->Size
= current
->Size
- ((ULONG
)blk
- (ULONG
)current
);
1308 remove_from_free_list(current
);
1309 current
->Size
-= (blk
->Size
+ sizeof(BLOCK_HDR
));
1310 blk
->AddressList
.Flink
= current
->AddressList
.Flink
;
1311 blk
->AddressList
.Flink
->Blink
= &blk
->AddressList
;
1312 blk
->AddressList
.Blink
= ¤t
->AddressList
;
1313 current
->AddressList
.Flink
= &blk
->AddressList
;
1314 add_to_free_list(current
);
1319 if (Removed
== FALSE
)
1321 remove_from_free_list(current
);
1325 * If the block is much bigger than required then split it and
1326 * return a pointer to the allocated section. If the difference
1327 * between the sizes is marginal it makes no sense to have the
1330 if (current
->Size
> size
+ sizeof(BLOCK_HDR
))
1332 BLOCK_HDR
* free_blk
;
1335 * Replace the bigger block with a smaller block in the
1336 * same position in the list
1338 free_blk
= (BLOCK_HDR
*)(((char*)current
)
1339 + sizeof(BLOCK_HDR
) + size
);
1341 free_blk
->Size
= current
->Size
- (sizeof(BLOCK_HDR
) + size
);
1343 free_blk
->AddressList
.Flink
= current
->AddressList
.Flink
;
1344 free_blk
->AddressList
.Flink
->Blink
= &free_blk
->AddressList
;
1345 free_blk
->AddressList
.Blink
= ¤t
->AddressList
;
1346 current
->AddressList
.Flink
= &free_blk
->AddressList
;
1347 current
->Magic
= BLOCK_HDR_USED_MAGIC
;
1348 free_blk
->Magic
= BLOCK_HDR_FREE_MAGIC
;
1349 add_to_free_list(free_blk
);
1350 add_to_used_list(current
);
1352 current
->Caller
= Caller
;
1353 current
->Dumped
= FALSE
;
1354 #ifdef TAG_STATISTICS_TRACKING
1355 MiAddToTagHashTable(current
);
1356 #endif /* TAG_STATISTICS_TRACKING */
1358 return(block_to_address(current
));
1362 * Otherwise allocate the whole block
1365 current
->Magic
= BLOCK_HDR_USED_MAGIC
;
1367 current
->Caller
= Caller
;
1368 current
->Dumped
= FALSE
;
1369 add_to_used_list(current
);
1370 #ifdef TAG_STATISTICS_TRACKING
1371 MiAddToTagHashTable(current
);
1372 #endif /* TAG_STATISTICS_TRACKING */
1375 return(block_to_address(current
));
1378 static void* grow_kernel_pool(unsigned int size
, ULONG Tag
, PVOID Caller
)
1380 * FUNCTION: Grow the executive heap to accomodate a block of at least 'size'
1384 ULONG nr_pages
= PAGE_ROUND_UP(size
+ sizeof(BLOCK_HDR
)) / PAGE_SIZE
;
1386 BLOCK_HDR
* blk
=NULL
;
1392 PLIST_ENTRY current_entry
;
1394 if (size
>= PAGE_SIZE
)
1399 KeAcquireSpinLock(&MmNpoolLock
, &oldIrql
);
1400 start
= (ULONG
)MiNonPagedPoolStart
+ MiCurrentNonPagedPoolLength
;
1401 if (MiCurrentNonPagedPoolLength
+ nr_pages
* PAGE_SIZE
> MiNonPagedPoolLength
)
1403 DbgPrint("CRITICAL: Out of non-paged pool space\n");
1406 MiCurrentNonPagedPoolLength
+= nr_pages
* PAGE_SIZE
;
1407 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1409 DPRINT("growing heap for block size %d, ",size
);
1410 DPRINT("start %x\n",start
);
1412 for (i
=0;i
<nr_pages
;i
++)
1414 PHYSICAL_ADDRESS Page
;
1415 /* FIXME: Check whether we can really wait here. */
1416 Status
= MmRequestPageMemoryConsumer(MC_NPPOOL
, TRUE
, &Page
);
1417 if (!NT_SUCCESS(Status
))
1422 Status
= MmCreateVirtualMapping(NULL
,
1423 (PVOID
)(start
+ (i
*PAGE_SIZE
)),
1424 PAGE_READWRITE
|PAGE_SYSTEM
,
1427 if (!NT_SUCCESS(Status
))
1429 DbgPrint("Unable to create virtual mapping\n");
1434 blk
= (struct _BLOCK_HDR
*)start
;
1435 memset(blk
, 0, sizeof(BLOCK_HDR
));
1436 blk
->Size
= (nr_pages
* PAGE_SIZE
) - sizeof(BLOCK_HDR
);
1437 memset(block_to_address(blk
), 0xcc, blk
->Size
);
1439 KeAcquireSpinLock(&MmNpoolLock
, &oldIrql
);
1440 current_entry
= AddressListHead
.Blink
;
1441 while (current_entry
!= &AddressListHead
)
1443 current
= CONTAINING_RECORD(current_entry
, BLOCK_HDR
, AddressList
);
1444 if ((PVOID
)current
+ current
->Size
< (PVOID
)blk
)
1446 InsertHeadList(current_entry
, &blk
->AddressList
);
1449 current_entry
= current_entry
->Blink
;
1451 if (current_entry
== &AddressListHead
)
1453 InsertHeadList(&AddressListHead
, &blk
->AddressList
);
1455 blk
->Magic
= BLOCK_HDR_FREE_MAGIC
;
1456 add_to_free_list(blk
);
1457 blk
= lookup_block(size
);
1460 block
= take_block(blk
, size
, Tag
, Caller
);
1463 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1467 #endif /* not WHOLE_PAGE_ALLOCATIONS */
1469 VOID STDCALL
ExFreeNonPagedPool (PVOID block
)
1471 * FUNCTION: Releases previously allocated memory
1473 * block = block to free
1476 #ifdef WHOLE_PAGE_ALLOCATIONS /* WHOLE_PAGE_ALLOCATIONS */
1484 DPRINT("freeing block %x\n",blk
);
1486 POOL_TRACE("ExFreePool(block %x), size %d, caller %x\n",block
,blk
->size
,
1487 ((PULONG
)&block
)[-1]);
1489 KeAcquireSpinLock(&MmNpoolLock
, &oldIrql
);
1491 ExFreeWholePageBlock(block
);
1492 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1494 #else /* not WHOLE_PAGE_ALLOCATIONS */
1496 BLOCK_HDR
* blk
=address_to_block(block
);
1504 DPRINT("freeing block %x\n",blk
);
1506 POOL_TRACE("ExFreePool(block %x), size %d, caller %x\n",block
,blk
->size
,
1507 ((PULONG
)&block
)[-1]);
1509 KeAcquireSpinLock(&MmNpoolLock
, &oldIrql
);
1513 if (blk
->Magic
!= BLOCK_HDR_USED_MAGIC
)
1515 if (blk
->Magic
== BLOCK_HDR_FREE_MAGIC
)
1517 DbgPrint("ExFreePool of already freed address %x\n", block
);
1521 DbgPrint("ExFreePool of non-allocated address %x (magic %x)\n",
1528 memset(block
, 0xcc, blk
->Size
);
1530 #ifdef TAG_STATISTICS_TRACKING
1531 MiRemoveFromTagHashTable(blk
);
1532 #endif /* TAG_STATISTICS_TRACKING */
1533 remove_from_used_list(blk
);
1536 blk
->TagListEntry
.Flink
= blk
->TagListEntry
.Blink
= NULL
;
1537 blk
->Magic
= BLOCK_HDR_FREE_MAGIC
;
1538 add_to_free_list(blk
);
1540 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1542 #endif /* WHOLE_PAGE_ALLOCATIONS */
1546 ExAllocateNonPagedPoolWithTag(ULONG Type
, ULONG Size
, ULONG Tag
, PVOID Caller
)
1548 #ifdef WHOLE_PAGE_ALLOCATIONS
1552 POOL_TRACE("ExAllocatePool(NumberOfBytes %d) caller %x ",
1555 KeAcquireSpinLock(&MmNpoolLock
, &oldIrql
);
1558 * accomodate this useful idiom
1562 POOL_TRACE("= NULL\n");
1563 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1567 block
= ExAllocateWholePageBlock(Size
);
1568 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1571 #else /* not WHOLE_PAGE_ALLOCATIONS */
1573 BLOCK_HDR
* best
= NULL
;
1576 POOL_TRACE("ExAllocatePool(NumberOfBytes %d) caller %x ",
1579 KeAcquireSpinLock(&MmNpoolLock
, &oldIrql
);
1584 /* after some allocations print the npaged pool stats */
1585 #ifdef TAG_STATISTICS_TRACKING
1587 static ULONG counter
= 0;
1588 if (counter
++ % 100000 == 0)
1590 MiDebugDumpNonPagedPoolStats(FALSE
);
1596 * accomodate this useful idiom
1600 POOL_TRACE("= NULL\n");
1601 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1604 /* Make the size dword alligned, this makes the block dword alligned */
1605 Size
= ROUND_UP(Size
, 4);
1607 * Look for an already created block of sufficent size
1609 best
= lookup_block(Size
);
1612 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1613 block
= grow_kernel_pool(Size
, Tag
, Caller
);
1616 DPRINT1("%d\n", Size
);
1617 DumpFreeBlockTree();
1619 assert(block
!= NULL
);
1620 memset(block
,0,Size
);
1624 block
=take_block(best
, Size
, Tag
, Caller
);
1626 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1627 memset(block
,0,Size
);
1630 #endif /* WHOLE_PAGE_ALLOCATIONS */
1633 #ifdef WHOLE_PAGE_ALLOCATIONS
1636 ExAllocateWholePageBlock(ULONG Size
)
1639 PHYSICAL_ADDRESS Page
;
1644 NrPages
= ROUND_UP(Size
, PAGE_SIZE
) / PAGE_SIZE
;
1646 Base
= RtlFindClearBitsAndSet(&NonPagedPoolAllocMap
, NrPages
+ 1, NonPagedPoolAllocMapHint
);
1647 if (NonPagedPoolAllocMapHint
== Base
)
1649 NonPagedPoolAllocMapHint
+= (NrPages
+ 1);
1651 Address
= MiNonPagedPoolStart
+ Base
* PAGE_SIZE
;
1653 for (i
= 0; i
< NrPages
; i
++)
1655 Page
= MmAllocPage(MC_NPPOOL
, 0);
1656 if (Page
.QuadPart
== 0LL)
1660 MmCreateVirtualMapping(NULL
,
1661 Address
+ (i
* PAGE_SIZE
),
1662 PAGE_READWRITE
| PAGE_SYSTEM
,
1667 MiCurrentNonPagedPoolLength
= max(MiCurrentNonPagedPoolLength
, (Base
+ NrPages
) * PAGE_SIZE
);
1668 return((PVOID
)((PUCHAR
)Address
+ (NrPages
* PAGE_SIZE
) - Size
));
1672 ExFreeWholePageBlock(PVOID Addr
)
1676 if (Addr
< MiNonPagedPoolStart
||
1677 Addr
>= (MiNonPagedPoolStart
+ MiCurrentNonPagedPoolLength
))
1679 DbgPrint("Block %x found outside pool area\n", Addr
);
1682 Base
= (Addr
- MiNonPagedPoolStart
) / PAGE_SIZE
;
1683 NonPagedPoolAllocMapHint
= min(NonPagedPoolAllocMapHint
, Base
);
1684 while (MmIsPagePresent(NULL
, Addr
))
1686 MmDeleteVirtualMapping(NULL
,
1691 RtlClearBits(&NonPagedPoolAllocMap
, Base
, 1);
1697 #endif /* WHOLE_PAGE_ALLOCATIONS */