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 ****************************************************************/
21 #include <internal/debug.h>
23 #ifdef ENABLE_VALIDATE_POOL
24 #define VALIDATE_POOL validate_kernel_pool()
30 #define POOL_TRACE(args...) do { DbgPrint(args); } while(0);
33 #define POOL_TRACE(args...)
39 /* avl types ****************************************************************/
42 * This declarations should be moved into a separate header file.
47 struct _NODE
* link
[2];
53 /* TYPES *******************************************************************/
55 #define BLOCK_HDR_USED_MAGIC (0xdeadbeef)
56 #define BLOCK_HDR_FREE_MAGIC (0xceadbeef)
59 * fields present at the start of a block (this is for internal use only)
61 typedef struct _BLOCK_HDR
65 struct _BLOCK_HDR
* previous
;
73 LIST_ENTRY TagListEntry
;
86 #define BLOCK_HDR_SIZE ROUND_UP(sizeof(BLOCK_HDR), MM_POOL_ALIGNMENT)
89 ExAllocateWholePageBlock(ULONG Size
);
91 ExFreeWholePageBlock(PVOID Addr
);
93 /* GLOBALS *****************************************************************/
95 extern PVOID MiNonPagedPoolStart
;
96 extern ULONG MiNonPagedPoolLength
;
99 * Head of the list of free blocks
101 static PNODE FreeBlockListRoot
= NULL
;
104 * Head of the list of in use block
106 static LIST_ENTRY UsedBlockListHead
;
108 static LIST_ENTRY AddressListHead
;
110 #ifndef WHOLE_PAGE_ALLOCATIONS
112 * Count of free blocks
114 static ULONG EiNrFreeBlocks
= 0;
117 * Count of used blocks
119 static ULONG EiNrUsedBlocks
= 0;
123 * Lock that protects the non-paged pool data structures
125 static KSPIN_LOCK MmNpoolLock
;
128 * Total memory used for free nonpaged pool blocks
130 ULONG EiFreeNonPagedPool
= 0;
133 * Total memory used for nonpaged pool blocks
135 ULONG EiUsedNonPagedPool
= 0;
137 /* Total quota for Non Paged Pool */
138 ULONG MmTotalNonPagedPoolQuota
= 0;
141 * Allocate a range of memory in the nonpaged pool
144 MiAllocNonPagedPoolRegion(unsigned int nr_pages
);
147 MiFreeNonPagedPoolRegion(PVOID Addr
, ULONG Count
, BOOLEAN Free
);
149 #ifdef TAG_STATISTICS_TRACKING
150 #define TAG_HASH_TABLE_SIZE (1024)
151 static LIST_ENTRY tag_hash_table
[TAG_HASH_TABLE_SIZE
];
152 #endif /* TAG_STATISTICS_TRACKING */
154 #ifdef WHOLE_PAGE_ALLOCATIONS
155 static RTL_BITMAP NonPagedPoolAllocMap
;
156 static ULONG NonPagedPoolAllocMapHint
;
157 static ULONG MiCurrentNonPagedPoolLength
= 0;
159 static PULONG MiNonPagedPoolAllocMap
;
160 static ULONG MiNonPagedPoolNrOfPages
;
161 #endif /* WHOLE_PAGE_ALLOCATIONS */
163 /* avl helper functions ****************************************************/
165 void DumpFreeBlockNode(PNODE p
)
167 static int count
= 0;
174 DumpFreeBlockNode(p
->link
[0]);
175 blk
= CONTAINING_RECORD(p
, BLOCK_HDR
, Free
.Node
);
176 DbgPrint("%08x %8d (%d)\n", blk
, blk
->Size
, count
);
177 DumpFreeBlockNode(p
->link
[1]);
182 void DumpFreeBlockTree(void)
184 DbgPrint("--- Begin tree ------------------\n");
185 DbgPrint("%08x\n", CONTAINING_RECORD(FreeBlockListRoot
, BLOCK_HDR
, Free
.Node
));
186 DumpFreeBlockNode(FreeBlockListRoot
);
187 DbgPrint("--- End tree --------------------\n");
190 int compare_node(PNODE p1
, PNODE p2
)
192 BLOCK_HDR
* blk1
= CONTAINING_RECORD(p1
, BLOCK_HDR
, Free
.Node
);
193 BLOCK_HDR
* blk2
= CONTAINING_RECORD(p2
, BLOCK_HDR
, Free
.Node
);
195 if (blk1
->Size
== blk2
->Size
)
208 if (blk1
->Size
< blk2
->Size
)
212 if (blk1
->Size
> blk2
->Size
)
221 int compare_value(PVOID value
, PNODE p
)
223 BLOCK_HDR
* blk
= CONTAINING_RECORD(p
, BLOCK_HDR
, Free
.Node
);
224 ULONG v
= *(PULONG
)value
;
237 /* avl functions **********************************************************/
240 * The avl functions should be moved into a separate file.
243 /* The avl_insert and avl_remove are based on libavl (library for manipulation of binary trees). */
245 void avl_insert (PNODE
* root
, PNODE n
, int (*compare
)(PNODE
, PNODE
))
247 PNODE y
; /* Top node to update balance factor, and parent. */
248 PNODE p
, q
; /* Iterator, and parent. */
249 PNODE w
; /* New root of rebalanced subtree. */
250 int dir
= 0; /* Direction to descend. */
252 n
->link
[0] = n
->link
[1] = n
->parent
= NULL
;
256 for (q
= NULL
, p
= *root
; p
!= NULL
; q
= p
, p
= p
->link
[dir
])
258 dir
= compare(n
, p
) > 0;
280 for (p
= n
; p
!= y
; p
= q
)
283 dir
= q
->link
[0] != p
;
294 if (y
->balance
== -2)
296 PNODE x
= y
->link
[0];
297 if (x
->balance
== -1)
300 y
->link
[0] = x
->link
[1];
302 x
->balance
= y
->balance
= 0;
303 x
->parent
= y
->parent
;
305 if (y
->link
[0] != NULL
)
307 y
->link
[0]->parent
= y
;
312 ASSERT(x
->balance
== +1);
314 x
->link
[1] = w
->link
[0];
316 y
->link
[0] = w
->link
[1];
318 if (w
->balance
== -1)
323 else if (w
->balance
== 0)
325 x
->balance
= y
->balance
= 0;
327 else /* |w->pavl_balance == +1| */
333 w
->parent
= y
->parent
;
334 x
->parent
= y
->parent
= w
;
335 if (x
->link
[1] != NULL
)
337 x
->link
[1]->parent
= x
;
339 if (y
->link
[0] != NULL
)
341 y
->link
[0]->parent
= y
;
345 else if (y
->balance
== +2)
347 PNODE x
= y
->link
[1];
348 if (x
->balance
== +1)
351 y
->link
[1] = x
->link
[0];
353 x
->balance
= y
->balance
= 0;
354 x
->parent
= y
->parent
;
356 if (y
->link
[1] != NULL
)
358 y
->link
[1]->parent
= y
;
363 ASSERT(x
->balance
== -1);
365 x
->link
[0] = w
->link
[1];
367 y
->link
[1] = w
->link
[0];
374 else if (w
->balance
== 0)
376 x
->balance
= y
->balance
= 0;
378 else /* |w->pavl_balance == -1| */
384 w
->parent
= y
->parent
;
385 x
->parent
= y
->parent
= w
;
386 if (x
->link
[0] != NULL
)
388 x
->link
[0]->parent
= x
;
390 if (y
->link
[1] != NULL
)
392 y
->link
[1]->parent
= y
;
400 if (w
->parent
!= NULL
)
402 w
->parent
->link
[y
!= w
->parent
->link
[0]] = w
;
412 void avl_remove (PNODE
*root
, PNODE item
, int (*compare
)(PNODE
, PNODE
))
414 PNODE p
; /* Traverses tree to find node to delete. */
415 PNODE q
; /* Parent of |p|. */
416 int dir
; /* Side of |q| on which |p| is linked. */
418 if (root
== NULL
|| *root
== NULL
)
432 dir
= compare(p
, q
) > 0;
435 if (p
->link
[1] == NULL
)
437 q
->link
[dir
] = p
->link
[0];
438 if (q
->link
[dir
] != NULL
)
440 q
->link
[dir
]->parent
= p
->parent
;
445 PNODE r
= p
->link
[1];
446 if (r
->link
[0] == NULL
)
448 r
->link
[0] = p
->link
[0];
450 r
->parent
= p
->parent
;
451 if (r
->link
[0] != NULL
)
453 r
->link
[0]->parent
= r
;
455 r
->balance
= p
->balance
;
461 PNODE s
= r
->link
[0];
462 while (s
->link
[0] != NULL
)
467 r
->link
[0] = s
->link
[1];
468 s
->link
[0] = p
->link
[0];
469 s
->link
[1] = p
->link
[1];
471 if (s
->link
[0] != NULL
)
473 s
->link
[0]->parent
= s
;
475 s
->link
[1]->parent
= s
;
476 s
->parent
= p
->parent
;
477 if (r
->link
[0] != NULL
)
479 r
->link
[0]->parent
= r
;
481 s
->balance
= p
->balance
;
487 item
->link
[0] = item
->link
[1] = item
->parent
= NULL
;
490 while (q
!= (PNODE
) root
)
494 if (y
->parent
!= NULL
)
505 dir
= q
->link
[0] != y
;
507 if (y
->balance
== +1)
511 else if (y
->balance
== +2)
513 PNODE x
= y
->link
[1];
514 if (x
->balance
== -1)
518 ASSERT(x
->balance
== -1);
520 x
->link
[0] = w
->link
[1];
522 y
->link
[1] = w
->link
[0];
524 if (w
->balance
== +1)
529 else if (w
->balance
== 0)
531 x
->balance
= y
->balance
= 0;
533 else /* |w->pavl_balance == -1| */
539 w
->parent
= y
->parent
;
540 x
->parent
= y
->parent
= w
;
541 if (x
->link
[0] != NULL
)
543 x
->link
[0]->parent
= x
;
545 if (y
->link
[1] != NULL
)
547 y
->link
[1]->parent
= y
;
553 y
->link
[1] = x
->link
[0];
555 x
->parent
= y
->parent
;
557 if (y
->link
[1] != NULL
)
559 y
->link
[1]->parent
= y
;
570 x
->balance
= y
->balance
= 0;
578 dir
= q
->link
[0] != y
;
580 if (y
->balance
== -1)
584 else if (y
->balance
== -2)
586 PNODE x
= y
->link
[0];
587 if (x
->balance
== +1)
590 ASSERT(x
->balance
== +1);
592 x
->link
[1] = w
->link
[0];
594 y
->link
[0] = w
->link
[1];
596 if (w
->balance
== -1)
601 else if (w
->balance
== 0)
603 x
->balance
= y
->balance
= 0;
605 else /* |w->pavl_balance == +1| */
611 w
->parent
= y
->parent
;
612 x
->parent
= y
->parent
= w
;
613 if (x
->link
[1] != NULL
)
615 x
->link
[1]->parent
= x
;
617 if (y
->link
[0] != NULL
)
619 y
->link
[0]->parent
= y
;
625 y
->link
[0] = x
->link
[1];
627 x
->parent
= y
->parent
;
629 if (y
->link
[0] != NULL
)
631 y
->link
[0]->parent
= y
;
642 x
->balance
= y
->balance
= 0;
652 PNODE _cdecl
avl_get_first(PNODE root
)
667 PNODE
avl_get_next(PNODE root
, PNODE p
)
682 while (q
&& q
->link
[1] == p
)
698 PNODE
avl_find_equal_or_greater(PNODE root
, ULONG size
, int (compare
)(PVOID
, PNODE
))
704 for (p
= root
; p
!= NULL
;)
706 cmp
= compare((PVOID
)&size
, p
);
720 cmp
= compare((PVOID
)&size
, p
->link
[0]);
733 /* non paged pool functions ************************************************/
735 #ifdef TAG_STATISTICS_TRACKING
737 MiRemoveFromTagHashTable(BLOCK_HDR
* block
)
739 * Remove a block from the tag hash table
742 if (block
->Used
.Tag
== 0)
747 RemoveEntryList(&block
->Used
.TagListEntry
);
751 MiAddToTagHashTable(BLOCK_HDR
* block
)
753 * Add a block to the tag hash table
758 if (block
->Used
.Tag
== 0)
763 hash
= block
->Used
.Tag
% TAG_HASH_TABLE_SIZE
;
765 InsertHeadList(&tag_hash_table
[hash
], &block
->Used
.TagListEntry
);
767 #endif /* TAG_STATISTICS_TRACKING */
769 #if defined(TAG_STATISTICS_TRACKING) && !defined(WHOLE_PAGE_ALLOCATIONS)
771 MiDumpTagStats(ULONG CurrentTag
, ULONG CurrentNrBlocks
, ULONG CurrentSize
)
775 c1
= (CHAR
)((CurrentTag
>> 24) & 0xFF);
776 c2
= (CHAR
)((CurrentTag
>> 16) & 0xFF);
777 c3
= (CHAR
)((CurrentTag
>> 8) & 0xFF);
778 c4
= (CHAR
)(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
= 0;
803 ULONG CurrentSize
= 0;
807 LIST_ENTRY tmpListHead
;
808 PLIST_ENTRY current_entry
;
810 DbgPrint("******* Dumping non paging pool stats ******\n");
813 for (i
= 0; i
< TAG_HASH_TABLE_SIZE
; i
++)
815 InitializeListHead(&tmpListHead
);
817 while (!IsListEmpty(&tag_hash_table
[i
]))
821 current_entry
= tag_hash_table
[i
].Flink
;
822 while (current_entry
!= &tag_hash_table
[i
])
824 current
= CONTAINING_RECORD(current_entry
, BLOCK_HDR
, Used
.TagListEntry
);
825 current_entry
= current_entry
->Flink
;
828 CurrentTag
= current
->Used
.Tag
;
832 if (current
->Used
.Tag
== CurrentTag
)
834 RemoveEntryList(¤t
->Used
.TagListEntry
);
835 InsertHeadList(&tmpListHead
, ¤t
->Used
.TagListEntry
);
836 if (!NewOnly
|| !current
->Used
.Dumped
)
840 CurrentSize
+= current
->Size
;
841 TotalSize
+= current
->Size
;
842 current
->Used
.Dumped
= TRUE
;
846 if (CurrentTag
!= 0 && CurrentNrBlocks
!= 0)
848 MiDumpTagStats(CurrentTag
, CurrentNrBlocks
, CurrentSize
);
851 if (!IsListEmpty(&tmpListHead
))
853 tag_hash_table
[i
].Flink
= tmpListHead
.Flink
;
854 tag_hash_table
[i
].Flink
->Blink
= &tag_hash_table
[i
];
855 tag_hash_table
[i
].Blink
= tmpListHead
.Blink
;
856 tag_hash_table
[i
].Blink
->Flink
= &tag_hash_table
[i
];
859 if (TotalBlocks
!= 0)
861 DbgPrint("TotalBlocks %d TotalSize %d AverageSize %d\n",
862 TotalBlocks
, TotalSize
, TotalSize
/ TotalBlocks
);
866 DbgPrint("TotalBlocks %d TotalSize %d\n",
867 TotalBlocks
, TotalSize
);
869 Size
= EiFreeNonPagedPool
- (MiNonPagedPoolLength
- MiNonPagedPoolNrOfPages
* PAGE_SIZE
);
870 DbgPrint("Freeblocks %d TotalFreeSize %d AverageFreeSize %d\n",
871 EiNrFreeBlocks
, Size
, EiNrFreeBlocks
? Size
/ EiNrFreeBlocks
: 0);
872 DbgPrint("***************** Dump Complete ***************\n");
873 #endif /* defined(TAG_STATISTICS_TRACKING) && !defined(WHOLE_PAGE_ALLOCATIONS) */
877 MiDebugDumpNonPagedPool(BOOLEAN NewOnly
)
879 #ifndef WHOLE_PAGE_ALLOCATIONS
881 PLIST_ENTRY current_entry
;
884 KeAcquireSpinLock(&MmNpoolLock
, &oldIrql
);
886 DbgPrint("******* Dumping non paging pool contents ******\n");
887 current_entry
= UsedBlockListHead
.Flink
;
888 while (current_entry
!= &UsedBlockListHead
)
890 current
= CONTAINING_RECORD(current_entry
, BLOCK_HDR
, Used
.ListEntry
);
891 if (!NewOnly
|| !current
->Used
.Dumped
)
895 c1
= (CHAR
)((current
->Used
.Tag
>> 24) & 0xFF);
896 c2
= (CHAR
)((current
->Used
.Tag
>> 16) & 0xFF);
897 c3
= (CHAR
)((current
->Used
.Tag
>> 8) & 0xFF);
898 c4
= (CHAR
)(current
->Used
.Tag
& 0xFF);
900 if (isprint(c1
) && isprint(c2
) && isprint(c3
) && isprint(c4
))
902 DbgPrint("Size 0x%x Tag 0x%x (%c%c%c%c) Allocator 0x%x\n",
903 current
->Size
, current
->Used
.Tag
, c4
, c3
, c2
, c1
,
904 current
->Used
.Caller
);
908 DbgPrint("Size 0x%x Tag 0x%x Allocator 0x%x\n",
909 current
->Size
, current
->Used
.Tag
, current
->Used
.Caller
);
911 current
->Used
.Dumped
= TRUE
;
913 current_entry
= current_entry
->Flink
;
915 DbgPrint("***************** Dump Complete ***************\n");
916 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
917 #endif /* not WHOLE_PAGE_ALLOCATIONS */
920 #ifndef WHOLE_PAGE_ALLOCATIONS
922 #ifdef ENABLE_VALIDATE_POOL
923 static void validate_free_list(void)
925 * FUNCTION: Validate the integrity of the list of free blocks
929 unsigned int blocks_seen
=0;
932 p
= avl_get_first(FreeBlockListRoot
);
938 current
= CONTAINING_RECORD(p
, BLOCK_HDR
, Free
.Node
);
939 base_addr
= (PVOID
)current
;
941 if (current
->Magic
!= BLOCK_HDR_FREE_MAGIC
)
943 DbgPrint("Bad block magic (probable pool corruption) at %x\n",
945 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
948 if (base_addr
< MiNonPagedPoolStart
||
949 base_addr
+ BLOCK_HDR_SIZE
+ current
->Size
> MiNonPagedPoolStart
+ MiNonPagedPoolLength
)
951 DbgPrint("Block %x found outside pool area\n",current
);
952 DbgPrint("Size %d\n",current
->Size
);
953 DbgPrint("Limits are %x %x\n",MiNonPagedPoolStart
,
954 MiNonPagedPoolStart
+MiNonPagedPoolLength
);
955 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
958 if (blocks_seen
> EiNrFreeBlocks
)
960 DbgPrint("Too many blocks on free list\n");
961 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
963 p
= avl_get_next(FreeBlockListRoot
, p
);
967 static void validate_used_list(void)
969 * FUNCTION: Validate the integrity of the list of used blocks
973 PLIST_ENTRY current_entry
;
974 unsigned int blocks_seen
=0;
976 current_entry
= UsedBlockListHead
.Flink
;
977 while (current_entry
!= &UsedBlockListHead
)
981 current
= CONTAINING_RECORD(current_entry
, BLOCK_HDR
, Used
.ListEntry
);
982 base_addr
= (PVOID
)current
;
984 if (current
->Magic
!= BLOCK_HDR_USED_MAGIC
)
986 DbgPrint("Bad block magic (probable pool corruption) at %x\n",
988 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
990 if (base_addr
< MiNonPagedPoolStart
||
991 (base_addr
+BLOCK_HDR_SIZE
+current
->Size
) >
992 MiNonPagedPoolStart
+MiNonPagedPoolLength
)
994 DbgPrint("Block %x found outside pool area\n",current
);
995 DbgPrint("Size %d\n",current
->Size
);
996 DbgPrint("Limits are %x %x\n",MiNonPagedPoolStart
,
997 MiNonPagedPoolStart
+MiNonPagedPoolLength
);
998 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
1001 if (blocks_seen
> EiNrUsedBlocks
)
1003 DbgPrint("Too many blocks on used list\n");
1004 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
1006 if (current
->Used
.ListEntry
.Flink
!= &UsedBlockListHead
&&
1007 current
->Used
.ListEntry
.Flink
->Blink
!= ¤t
->Used
.ListEntry
)
1009 DbgPrint("%s:%d:Break in list (current %x next %x "
1010 "current->next->previous %x)\n",
1011 __FILE__
,__LINE__
,current
, current
->Used
.ListEntry
.Flink
,
1012 current
->Used
.ListEntry
.Flink
->Blink
);
1013 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
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
) + BLOCK_HDR_SIZE
+ blk
->Size
;
1031 PLIST_ENTRY current_entry
;
1034 p
= avl_get_first(FreeBlockListRoot
);
1038 current
= CONTAINING_RECORD(p
, BLOCK_HDR
, Free
.Node
);
1040 if (current
->Magic
!= BLOCK_HDR_FREE_MAGIC
)
1042 DbgPrint("Bad block magic (probable pool corruption) at %x\n",
1044 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
1047 if ( (char*)current
> base
&& (char*)current
< last
)
1049 DbgPrint("intersecting blocks on list\n");
1050 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
1052 if ( (char*)current
< base
&&
1053 ((char*)current
+ current
->Size
+ BLOCK_HDR_SIZE
)
1056 DbgPrint("intersecting blocks on list\n");
1057 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
1059 p
= avl_get_next(FreeBlockListRoot
, p
);
1062 current_entry
= UsedBlockListHead
.Flink
;
1063 while (current_entry
!= &UsedBlockListHead
)
1065 current
= CONTAINING_RECORD(current_entry
, BLOCK_HDR
, Used
.ListEntry
);
1067 if ( (char*)current
> base
&& (char*)current
< last
)
1069 DbgPrint("intersecting blocks on list\n");
1070 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
1072 if ( (char*)current
< base
&&
1073 ((char*)current
+ current
->Size
+ BLOCK_HDR_SIZE
)
1076 DbgPrint("intersecting blocks on list\n");
1077 KEBUGCHECK(/*KBUG_POOL_FREE_LIST_CORRUPT*/0);
1080 current_entry
= current_entry
->Flink
;
1085 static void validate_kernel_pool(void)
1087 * FUNCTION: Checks the integrity of the kernel memory heap
1091 PLIST_ENTRY current_entry
;
1094 validate_free_list();
1095 validate_used_list();
1097 p
= avl_get_first(FreeBlockListRoot
);
1100 current
= CONTAINING_RECORD(p
, BLOCK_HDR
, Free
.Node
);
1101 check_duplicates(current
);
1102 p
= avl_get_next(FreeBlockListRoot
, p
);
1104 current_entry
= UsedBlockListHead
.Flink
;
1105 while (current_entry
!= &UsedBlockListHead
)
1107 current
= CONTAINING_RECORD(current_entry
, BLOCK_HDR
, Used
.ListEntry
);
1108 check_duplicates(current
);
1109 current_entry
= current_entry
->Flink
;
1116 free_pages(BLOCK_HDR
* blk
)
1123 end
= (ULONG
)blk
+ BLOCK_HDR_SIZE
+ blk
->Size
;
1126 * If the block doesn't contain a whole page then there is nothing to do
1128 if (PAGE_ROUND_UP(start
) >= PAGE_ROUND_DOWN(end
))
1135 static void remove_from_used_list(BLOCK_HDR
* current
)
1137 RemoveEntryList(¤t
->Used
.ListEntry
);
1138 EiUsedNonPagedPool
-= current
->Size
;
1142 static void remove_from_free_list(BLOCK_HDR
* current
)
1144 DPRINT("remove_from_free_list %d\n", current
->Size
);
1146 avl_remove(&FreeBlockListRoot
, ¤t
->Free
.Node
, compare_node
);
1148 EiFreeNonPagedPool
-= current
->Size
;
1150 DPRINT("remove_from_free_list done\n");
1153 DumpFreeBlockTree();
1158 add_to_free_list(BLOCK_HDR
* blk
)
1160 * FUNCTION: add the block to the free list (internal)
1164 BOOL UpdatePrevPtr
= FALSE
;
1166 DPRINT("add_to_free_list (%d)\n", blk
->Size
);
1170 current
= blk
->previous
;
1171 if (current
&& current
->Magic
== BLOCK_HDR_FREE_MAGIC
)
1173 remove_from_free_list(current
);
1174 current
->Size
= current
->Size
+ BLOCK_HDR_SIZE
+ blk
->Size
;
1175 current
->Magic
= BLOCK_HDR_USED_MAGIC
;
1176 memset(blk
, 0xcc, BLOCK_HDR_SIZE
);
1178 UpdatePrevPtr
= TRUE
;
1181 current
= (BLOCK_HDR
*)((char*)blk
+ BLOCK_HDR_SIZE
+ blk
->Size
);
1182 if ((char*)current
< (char*)MiNonPagedPoolStart
+ MiNonPagedPoolLength
&&
1183 current
->Magic
== BLOCK_HDR_FREE_MAGIC
)
1185 remove_from_free_list(current
);
1186 blk
->Size
+= BLOCK_HDR_SIZE
+ current
->Size
;
1187 memset(current
, 0xcc, BLOCK_HDR_SIZE
);
1188 UpdatePrevPtr
= TRUE
;
1189 current
= (BLOCK_HDR
*)((char*)blk
+ BLOCK_HDR_SIZE
+ blk
->Size
);
1191 if (UpdatePrevPtr
&&
1192 (char*)current
< (char*)MiNonPagedPoolStart
+ MiNonPagedPoolLength
)
1194 current
->previous
= blk
;
1196 DPRINT("%d\n", blk
->Size
);
1197 blk
->Magic
= BLOCK_HDR_FREE_MAGIC
;
1198 EiFreeNonPagedPool
+= blk
->Size
;
1199 avl_insert(&FreeBlockListRoot
, &blk
->Free
.Node
, compare_node
);
1201 DPRINT("add_to_free_list done\n");
1204 DumpFreeBlockTree();
1208 static void add_to_used_list(BLOCK_HDR
* blk
)
1210 * FUNCTION: add the block to the used list (internal)
1213 InsertHeadList(&UsedBlockListHead
, &blk
->Used
.ListEntry
);
1214 EiUsedNonPagedPool
+= blk
->Size
;
1218 inline static void* block_to_address(BLOCK_HDR
* blk
)
1220 * FUNCTION: Translate a block header address to the corresponding block
1221 * address (internal)
1224 return ( (void *) ((char*)blk
+ BLOCK_HDR_SIZE
));
1227 inline static BLOCK_HDR
* address_to_block(void* addr
)
1229 return (BLOCK_HDR
*)
1230 ( ((char*)addr
) - BLOCK_HDR_SIZE
);
1234 grow_block(BLOCK_HDR
* blk
, PVOID end
)
1238 ULONG_PTR StartIndex
, EndIndex
;
1241 StartIndex
= (ULONG_PTR
)(PAGE_ROUND_UP((ULONG_PTR
)blk
+ BLOCK_HDR_SIZE
- (ULONG_PTR
)MiNonPagedPoolStart
)) / PAGE_SIZE
;
1242 EndIndex
= ((ULONG_PTR
)PAGE_ROUND_UP(end
) - (ULONG_PTR
)MiNonPagedPoolStart
) / PAGE_SIZE
;
1245 for (i
= StartIndex
; i
< EndIndex
; i
++)
1247 if (!(MiNonPagedPoolAllocMap
[i
/ 32] & (1 << (i
% 32))))
1249 for (j
= i
+ 1; j
< EndIndex
&& j
- i
< 32; j
++)
1251 if (MiNonPagedPoolAllocMap
[j
/ 32] & (1 << (j
% 32)))
1256 for (k
= 0; k
< j
- i
; k
++)
1258 Status
= MmRequestPageMemoryConsumer(MC_NPPOOL
, FALSE
, &Page
[k
]);
1259 if (!NT_SUCCESS(Status
))
1261 for (i
= 0; i
< k
; i
++)
1263 MmReleasePageMemoryConsumer(MC_NPPOOL
, Page
[i
]);
1268 Status
= MmCreateVirtualMapping(NULL
,
1269 (PVOID
)((ULONG_PTR
)MiNonPagedPoolStart
+ i
* PAGE_SIZE
),
1270 PAGE_READWRITE
|PAGE_SYSTEM
,
1273 if (!NT_SUCCESS(Status
))
1275 for (i
= 0; i
< k
; i
++)
1277 MmReleasePageMemoryConsumer(MC_NPPOOL
, Page
[i
]);
1281 for (j
= i
; j
< k
+ i
; j
++)
1283 MiNonPagedPoolAllocMap
[j
/ 32] |= (1 << (j
% 32));
1285 MiNonPagedPoolNrOfPages
+= k
;
1292 static BLOCK_HDR
* get_block(unsigned int size
, unsigned long alignment
)
1294 BLOCK_HDR
*blk
, *current
, *previous
= NULL
, *next
= NULL
, *best
= NULL
;
1295 ULONG previous_size
= 0, current_size
, next_size
= 0, new_size
;
1297 PVOID addr
, aligned_addr
;
1300 DPRINT("get_block %d\n", size
);
1304 p
= avl_find_equal_or_greater(FreeBlockListRoot
, size
, compare_value
);
1307 best
= CONTAINING_RECORD(p
, BLOCK_HDR
, Free
.Node
);
1308 addr
= block_to_address(best
);
1313 p
= avl_find_equal_or_greater(FreeBlockListRoot
, size
, compare_value
);
1317 current
= CONTAINING_RECORD(p
, BLOCK_HDR
, Free
.Node
);
1318 addr
= block_to_address(current
);
1319 /* calculate first aligned address available within this block */
1320 aligned_addr
= MM_ROUND_UP(addr
, alignment
);
1321 /* check to see if this address is already aligned */
1322 if (addr
== aligned_addr
)
1324 if (current
->Size
>= size
&&
1325 (best
== NULL
|| current
->Size
< best
->Size
))
1332 /* make sure there's enough room to make a free block by the space skipped
1333 * from alignment. If not, calculate forward to the next alignment
1334 * and see if we allocate there...
1336 new_size
= (ULONG_PTR
)aligned_addr
- (ULONG_PTR
)addr
+ size
;
1337 if ((ULONG_PTR
)aligned_addr
- (ULONG_PTR
)addr
< BLOCK_HDR_SIZE
)
1339 /* not enough room for a free block header, add some more bytes */
1340 aligned_addr
= MM_ROUND_UP(block_to_address((BLOCK_HDR
*)((char*)current
+ BLOCK_HDR_SIZE
)), alignment
);
1341 new_size
= (ULONG_PTR
)aligned_addr
- (ULONG_PTR
)addr
+ size
;
1343 if (current
->Size
>= new_size
&&
1344 (best
== NULL
|| current
->Size
< best
->Size
))
1349 if (best
&& current
->Size
>= size
+ alignment
+ 2 * BLOCK_HDR_SIZE
)
1353 p
= avl_get_next(FreeBlockListRoot
, p
);
1359 * We didn't find anything suitable at all.
1367 current_size
= current
->Size
;
1371 addr
= block_to_address(current
);
1372 aligned_addr
= MM_ROUND_UP(addr
, alignment
);
1373 if (addr
!= aligned_addr
)
1375 blk
= address_to_block(aligned_addr
);
1376 if ((char*)blk
< (char*)current
+ BLOCK_HDR_SIZE
)
1378 aligned_addr
= MM_ROUND_UP(block_to_address((BLOCK_HDR
*)((char*)current
+ BLOCK_HDR_SIZE
)), alignment
);
1379 blk
= address_to_block(aligned_addr
);
1382 * if size-aligned, break off the preceding bytes into their own block...
1385 previous_size
= (ULONG_PTR
)blk
- (ULONG_PTR
)previous
- BLOCK_HDR_SIZE
;
1387 current_size
-= ((ULONG_PTR
)current
- (ULONG_PTR
)previous
);
1391 end
= (char*)current
+ BLOCK_HDR_SIZE
+ size
;
1393 if (current_size
>= size
+ BLOCK_HDR_SIZE
+ MM_POOL_ALIGNMENT
)
1395 /* create a new free block after our block, if the memory size is >= 4 byte for this block */
1396 next
= (BLOCK_HDR
*)((ULONG_PTR
)current
+ size
+ BLOCK_HDR_SIZE
);
1397 next_size
= current_size
- size
- BLOCK_HDR_SIZE
;
1398 current_size
= size
;
1399 end
= (char*)next
+ BLOCK_HDR_SIZE
;
1404 remove_from_free_list(previous
);
1405 if (!grow_block(previous
, end
))
1407 add_to_free_list(previous
);
1410 memset(current
, 0, BLOCK_HDR_SIZE
);
1411 current
->Size
= current_size
;
1412 current
->Magic
= BLOCK_HDR_USED_MAGIC
;
1413 current
->previous
= previous
;
1414 previous
->Size
= previous_size
;
1417 blk
= (BLOCK_HDR
*)((char*)current
+ BLOCK_HDR_SIZE
+ current
->Size
);
1418 if ((char*)blk
< (char*)MiNonPagedPoolStart
+ MiNonPagedPoolLength
)
1420 blk
->previous
= current
;
1424 add_to_free_list(previous
);
1428 remove_from_free_list(current
);
1430 if (!grow_block(current
, end
))
1432 add_to_free_list(current
);
1436 current
->Magic
= BLOCK_HDR_USED_MAGIC
;
1439 current
->Size
= current_size
;
1445 memset(next
, 0, BLOCK_HDR_SIZE
);
1447 next
->Size
= next_size
;
1448 next
->Magic
= BLOCK_HDR_FREE_MAGIC
;
1449 next
->previous
= current
;
1450 blk
= (BLOCK_HDR
*)((char*)next
+ BLOCK_HDR_SIZE
+ next
->Size
);
1451 if ((char*)blk
< (char*)MiNonPagedPoolStart
+ MiNonPagedPoolLength
)
1453 blk
->previous
= next
;
1455 add_to_free_list(next
);
1458 add_to_used_list(current
);
1463 #endif /* not WHOLE_PAGE_ALLOCATIONS */
1465 VOID STDCALL
ExFreeNonPagedPool (PVOID block
)
1467 * FUNCTION: Releases previously allocated memory
1469 * block = block to free
1472 #ifdef WHOLE_PAGE_ALLOCATIONS /* WHOLE_PAGE_ALLOCATIONS */
1480 DPRINT("freeing block %x\n",block
);
1482 POOL_TRACE("ExFreePool(block %x), size %d, caller %x\n",block
,blk
->size
,
1483 ((PULONG
)&block
)[-1]);
1485 KeAcquireSpinLock(&MmNpoolLock
, &oldIrql
);
1487 ExFreeWholePageBlock(block
);
1488 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1490 #else /* not WHOLE_PAGE_ALLOCATIONS */
1492 BLOCK_HDR
* blk
=address_to_block(block
);
1500 DPRINT("freeing block %x\n",blk
);
1502 POOL_TRACE("ExFreePool(block %x), size %d, caller %x\n",block
,blk
->Size
,
1503 ((PULONG
)&block
)[-1]);
1505 KeAcquireSpinLock(&MmNpoolLock
, &oldIrql
);
1509 if (blk
->Magic
!= BLOCK_HDR_USED_MAGIC
)
1511 if (blk
->Magic
== BLOCK_HDR_FREE_MAGIC
)
1513 DbgPrint("ExFreePool of already freed address %x\n", block
);
1517 DbgPrint("ExFreePool of non-allocated address %x (magic %x)\n",
1524 memset(block
, 0xcc, blk
->Size
);
1525 #ifdef TAG_STATISTICS_TRACKING
1527 MiRemoveFromTagHashTable(blk
);
1530 remove_from_used_list(blk
);
1531 blk
->Magic
= BLOCK_HDR_FREE_MAGIC
;
1532 add_to_free_list(blk
);
1534 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1536 #endif /* WHOLE_PAGE_ALLOCATIONS */
1540 ExAllocateNonPagedPoolWithTag(POOL_TYPE Type
, ULONG Size
, ULONG Tag
, PVOID Caller
)
1542 #ifdef WHOLE_PAGE_ALLOCATIONS
1546 ASSERT_IRQL(DISPATCH_LEVEL
);
1548 POOL_TRACE("ExAllocatePool(NumberOfBytes %d) caller %x ",
1551 KeAcquireSpinLock(&MmNpoolLock
, &oldIrql
);
1554 * accomodate this useful idiom
1558 POOL_TRACE("= NULL\n");
1559 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1563 block
= ExAllocateWholePageBlock(Size
);
1564 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1567 DPRINT1("Trying to allocate %lu bytes from nonpaged pool - nothing suitable found, returning NULL\n",
1572 #else /* not WHOLE_PAGE_ALLOCATIONS */
1575 BLOCK_HDR
* best
= NULL
;
1579 POOL_TRACE("ExAllocatePool(NumberOfBytes %d) caller %x ",
1582 KeAcquireSpinLock(&MmNpoolLock
, &oldIrql
);
1587 /* after some allocations print the npaged pool stats */
1588 #ifdef TAG_STATISTICS_TRACKING
1591 static ULONG counter
= 0;
1592 if (counter
++ % 100000 == 0)
1594 MiDebugDumpNonPagedPoolStats(FALSE
);
1600 * accomodate this useful idiom
1604 POOL_TRACE("= NULL\n");
1605 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1608 /* Make the size dword alligned, this makes the block dword alligned */
1609 Size
= ROUND_UP(Size
, MM_POOL_ALIGNMENT
);
1611 if (Size
>= PAGE_SIZE
)
1613 alignment
= PAGE_SIZE
;
1615 else if (Type
== NonPagedPoolCacheAligned
||
1616 Type
== NonPagedPoolCacheAlignedMustS
)
1618 alignment
= MM_CACHE_LINE_SIZE
;
1625 best
= get_block(Size
, alignment
);
1628 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1629 DPRINT1("Trying to allocate %lu bytes from nonpaged pool - nothing suitable found, returning NULL\n",
1633 best
->Used
.Tag
= Tag
;
1634 best
->Used
.Caller
= Caller
;
1635 best
->Used
.Dumped
= FALSE
;
1636 best
->Used
.TagListEntry
.Flink
= best
->Used
.TagListEntry
.Blink
= NULL
;
1637 #ifdef TAG_STATISTICS_TRACKING
1639 MiAddToTagHashTable(best
);
1642 KeReleaseSpinLock(&MmNpoolLock
, oldIrql
);
1643 block
= block_to_address(best
);
1644 /* RtlZeroMemory(block, Size);*/
1646 #endif /* WHOLE_PAGE_ALLOCATIONS */
1649 #ifdef WHOLE_PAGE_ALLOCATIONS
1652 ExAllocateWholePageBlock(ULONG Size
)
1660 NrPages
= ROUND_UP(Size
, PAGE_SIZE
) / PAGE_SIZE
;
1662 Base
= RtlFindClearBitsAndSet(&NonPagedPoolAllocMap
, NrPages
+ 1, NonPagedPoolAllocMapHint
);
1663 if (Base
== 0xffffffff)
1665 DbgPrint("Out of non paged pool space.\n");
1668 if (NonPagedPoolAllocMapHint
== Base
)
1670 NonPagedPoolAllocMapHint
+= (NrPages
+ 1);
1673 Address
= MiNonPagedPoolStart
+ Base
* PAGE_SIZE
;
1675 for (i
= 0; i
< NrPages
; i
++)
1677 Page
= MmAllocPage(MC_NPPOOL
, 0);
1682 MmCreateVirtualMapping(NULL
,
1683 Address
+ (i
* PAGE_SIZE
),
1684 PAGE_READWRITE
| PAGE_SYSTEM
,
1689 MiCurrentNonPagedPoolLength
= max(MiCurrentNonPagedPoolLength
, (Base
+ NrPages
) * PAGE_SIZE
);
1690 Size
= (Size
+ 7) & ~7;
1691 Address
= ((PVOID
)((PUCHAR
)Address
+ (NrPages
* PAGE_SIZE
) - Size
));
1693 DPRINT("WPALLOC: %x (%d)\n", Address
, Size
);
1699 ExFreeWholePageBlock(PVOID Addr
)
1703 if (Addr
< MiNonPagedPoolStart
||
1704 Addr
>= (MiNonPagedPoolStart
+ MiCurrentNonPagedPoolLength
))
1706 DbgPrint("Block %x found outside pool area\n", Addr
);
1709 Base
= (Addr
- MiNonPagedPoolStart
) / PAGE_SIZE
;
1710 NonPagedPoolAllocMapHint
= min(NonPagedPoolAllocMapHint
, Base
);
1711 while (MmIsPagePresent(NULL
, Addr
))
1713 MmDeleteVirtualMapping(NULL
,
1718 RtlClearBits(&NonPagedPoolAllocMap
, Base
, 1);
1724 #endif /* WHOLE_PAGE_ALLOCATIONS */
1726 /* Whole Page Allocations note:
1728 * We need enough pages for these things:
1736 MiInitializeNonPagedPool(VOID
)
1742 #ifdef WHOLE_PAGE_ALLOCATIONS
1747 #ifdef TAG_STATISTICS_TRACKING
1749 for (i
= 0; i
< TAG_HASH_TABLE_SIZE
; i
++)
1751 InitializeListHead(&tag_hash_table
[i
]);
1754 KeInitializeSpinLock(&MmNpoolLock
);
1755 InitializeListHead(&UsedBlockListHead
);
1756 InitializeListHead(&AddressListHead
);
1757 FreeBlockListRoot
= NULL
;
1758 #ifdef WHOLE_PAGE_ALLOCATIONS
1760 NonPagedPoolAllocMapHint
= PAGE_ROUND_UP(MiNonPagedPoolLength
/ PAGE_SIZE
/ 8) / PAGE_SIZE
; /* Pages of bitmap buffer */
1761 MiCurrentNonPagedPoolLength
= NonPagedPoolAllocMapHint
* PAGE_SIZE
;
1762 Address
= MiNonPagedPoolStart
;
1763 for (i
= 0; i
< NonPagedPoolAllocMapHint
; i
++)
1765 Status
= MmRequestPageMemoryConsumer(MC_NPPOOL
, FALSE
, &Page
);
1766 if (!NT_SUCCESS(Status
))
1768 DbgPrint("Unable to allocate a page\n");
1771 Status
= MmCreateVirtualMapping(NULL
,
1773 PAGE_READWRITE
|PAGE_SYSTEM
,
1776 if (!NT_SUCCESS(Status
))
1778 DbgPrint("Unable to create virtual mapping\n");
1781 Address
+= PAGE_SIZE
;
1783 RtlInitializeBitMap(&NonPagedPoolAllocMap
, MiNonPagedPoolStart
,
1784 MiNonPagedPoolLength
/ PAGE_SIZE
);
1785 RtlClearAllBits(&NonPagedPoolAllocMap
);
1786 RtlSetBits(&NonPagedPoolAllocMap
, 0, NonPagedPoolAllocMapHint
);
1789 MiNonPagedPoolAllocMap
= block_to_address((BLOCK_HDR
*)MiNonPagedPoolStart
);
1790 MiNonPagedPoolNrOfPages
= PAGE_ROUND_UP(ROUND_UP(MiNonPagedPoolLength
/ PAGE_SIZE
, 32) / 8 + 2 * BLOCK_HDR_SIZE
);
1791 MiNonPagedPoolNrOfPages
/= PAGE_SIZE
;
1792 Address
= MiNonPagedPoolStart
;
1793 for (i
= 0; i
< MiNonPagedPoolNrOfPages
; i
++)
1795 Status
= MmRequestPageMemoryConsumer(MC_NPPOOL
, FALSE
, &Page
);
1796 if (!NT_SUCCESS(Status
))
1798 DbgPrint("Unable to allocate a page\n");
1801 Status
= MmCreateVirtualMapping(NULL
,
1803 PAGE_READWRITE
|PAGE_SYSTEM
,
1806 if (!NT_SUCCESS(Status
))
1808 DbgPrint("Unable to create virtual mapping\n");
1811 MiNonPagedPoolAllocMap
[i
/ 32] |= (1 << (i
% 32));
1812 Address
= (PVOID
)((ULONG_PTR
)Address
+ PAGE_SIZE
);
1814 /* the first block contains the non paged pool bitmap */
1815 blk
= (BLOCK_HDR
*)MiNonPagedPoolStart
;
1816 memset(blk
, 0, BLOCK_HDR_SIZE
);
1817 blk
->Magic
= BLOCK_HDR_USED_MAGIC
;
1818 blk
->Size
= ROUND_UP(MiNonPagedPoolLength
/ PAGE_SIZE
, 32) / 8;
1819 blk
->previous
= NULL
;
1820 blk
->Used
.Tag
= 0xffffffff;
1821 blk
->Used
.Caller
= 0;
1822 blk
->Used
.Dumped
= FALSE
;
1823 add_to_used_list(blk
);
1824 #ifdef TAG_STATISTICS_TRACKING
1826 MiAddToTagHashTable(blk
);
1828 /* the second block is the first free block */
1829 blk
= (BLOCK_HDR
*)((char*)blk
+ BLOCK_HDR_SIZE
+ blk
->Size
);
1830 memset(blk
, 0, BLOCK_HDR_SIZE
);
1831 memset((char*)blk
+ BLOCK_HDR_SIZE
, 0x0cc, MiNonPagedPoolNrOfPages
* PAGE_SIZE
- ((ULONG_PTR
)blk
+ BLOCK_HDR_SIZE
- (ULONG_PTR
)MiNonPagedPoolStart
));
1832 blk
->Magic
= BLOCK_HDR_FREE_MAGIC
;
1833 blk
->Size
= MiNonPagedPoolLength
- ((ULONG_PTR
)blk
+ BLOCK_HDR_SIZE
- (ULONG_PTR
)MiNonPagedPoolStart
);
1834 blk
->previous
= (BLOCK_HDR
*)MiNonPagedPoolStart
;
1835 add_to_free_list(blk
);
1841 MiAllocateSpecialPool (IN POOL_TYPE PoolType
,
1842 IN SIZE_T NumberOfBytes
,
1847 /* FIXME: Special Pools not Supported */
1848 DbgPrint("Special Pools not supported\n");