2 * PROJECT: ReactOS Kernel
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: ntoskrnl/mm/ARM3/pool.c
5 * PURPOSE: ARM Memory Manager Pool Allocator
6 * PROGRAMMERS: ReactOS Portable Systems Group
9 /* INCLUDES *******************************************************************/
15 #line 15 "ARMĀ³::POOL"
16 #define MODULE_INVOLVED_IN_ARM3
17 #include "../ARM3/miarm.h"
19 /* GLOBALS ********************************************************************/
21 LIST_ENTRY MmNonPagedPoolFreeListHead
[MI_MAX_FREE_PAGE_LISTS
];
22 PFN_NUMBER MmNumberOfFreeNonPagedPool
, MiExpansionPoolPagesInitialCharge
;
23 PVOID MmNonPagedPoolEnd0
;
24 PFN_NUMBER MiStartOfInitialPoolFrame
, MiEndOfInitialPoolFrame
;
26 MM_PAGED_POOL_INFO MmPagedPoolInfo
;
28 /* PRIVATE FUNCTIONS **********************************************************/
32 MiInitializeArmPool(VOID
)
36 PMMFREE_POOL_ENTRY FreeEntry
, FirstEntry
;
41 // We keep 4 lists of free pages (4 lists help avoid contention)
43 for (i
= 0; i
< MI_MAX_FREE_PAGE_LISTS
; i
++)
46 // Initialize each of them
48 InitializeListHead(&MmNonPagedPoolFreeListHead
[i
]);
52 // Calculate how many pages the initial nonpaged pool has
54 PoolPages
= BYTES_TO_PAGES(MmSizeOfNonPagedPoolInBytes
);
55 MmNumberOfFreeNonPagedPool
= PoolPages
;
58 // Initialize the first free entry
60 FreeEntry
= MmNonPagedPoolStart
;
61 FirstEntry
= FreeEntry
;
62 FreeEntry
->Size
= PoolPages
;
63 FreeEntry
->Owner
= FirstEntry
;
66 // Insert it into the last list
68 InsertHeadList(&MmNonPagedPoolFreeListHead
[MI_MAX_FREE_PAGE_LISTS
- 1],
72 // Now create free entries for every single other page
74 while (PoolPages
-- > 1)
77 // Link them all back to the original entry
79 FreeEntry
= (PMMFREE_POOL_ENTRY
)((ULONG_PTR
)FreeEntry
+ PAGE_SIZE
);
80 FreeEntry
->Owner
= FirstEntry
;
84 // Validate and remember first allocated pool page
86 PointerPte
= MiAddressToPte(MmNonPagedPoolStart
);
87 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
88 MiStartOfInitialPoolFrame
= PFN_FROM_PTE(PointerPte
);
91 // Keep track of where initial nonpaged pool ends
93 MmNonPagedPoolEnd0
= (PVOID
)((ULONG_PTR
)MmNonPagedPoolStart
+
94 MmSizeOfNonPagedPoolInBytes
);
97 // Validate and remember last allocated pool page
99 PointerPte
= MiAddressToPte((PVOID
)((ULONG_PTR
)MmNonPagedPoolEnd0
- 1));
100 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
101 MiEndOfInitialPoolFrame
= PFN_FROM_PTE(PointerPte
);
104 // Validate the first nonpaged pool expansion page (which is a guard page)
106 PointerPte
= MiAddressToPte(MmNonPagedPoolExpansionStart
);
107 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
110 // Calculate the size of the expansion region alone
112 MiExpansionPoolPagesInitialCharge
=
113 BYTES_TO_PAGES(MmMaximumNonPagedPoolInBytes
- MmSizeOfNonPagedPoolInBytes
);
116 // Remove 2 pages, since there's a guard page on top and on the bottom
118 MiExpansionPoolPagesInitialCharge
-= 2;
121 // Now initialize the nonpaged pool expansion PTE space. Remember there's a
122 // guard page on top so make sure to skip it. The bottom guard page will be
123 // guaranteed by the fact our size is off by one.
125 MiInitializeSystemPtes(PointerPte
+ 1,
126 MiExpansionPoolPagesInitialCharge
,
127 NonPagedPoolExpansion
);
132 MiAllocatePoolPages(IN POOL_TYPE PoolType
,
133 IN SIZE_T SizeInBytes
)
135 PFN_NUMBER SizeInPages
, PageFrameNumber
;
138 PLIST_ENTRY NextEntry
, NextHead
, LastHead
;
139 PMMPTE PointerPte
, StartPte
;
143 PMMFREE_POOL_ENTRY FreeEntry
;
146 // Figure out how big the allocation is in pages
148 SizeInPages
= BYTES_TO_PAGES(SizeInBytes
);
151 // Allocations of less than 4 pages go into their individual buckets
154 if (i
>= MI_MAX_FREE_PAGE_LISTS
) i
= MI_MAX_FREE_PAGE_LISTS
- 1;
157 // Loop through all the free page lists based on the page index
159 NextHead
= &MmNonPagedPoolFreeListHead
[i
];
160 LastHead
= &MmNonPagedPoolFreeListHead
[MI_MAX_FREE_PAGE_LISTS
];
163 // Acquire the nonpaged pool lock
165 OldIrql
= KeAcquireQueuedSpinLock(LockQueueMmNonPagedPoolLock
);
169 // Now loop through all the free page entries in this given list
171 NextEntry
= NextHead
->Flink
;
172 while (NextEntry
!= NextHead
)
175 // Grab the entry and see if it can handle our allocation
177 FreeEntry
= CONTAINING_RECORD(NextEntry
, MMFREE_POOL_ENTRY
, List
);
178 if (FreeEntry
->Size
>= SizeInPages
)
181 // It does, so consume the pages from here
183 FreeEntry
->Size
-= SizeInPages
;
186 // The allocation will begin in this free page area
188 BaseVa
= (PVOID
)((ULONG_PTR
)FreeEntry
+
189 (FreeEntry
->Size
<< PAGE_SHIFT
));
192 // This is not a free page segment anymore
194 RemoveEntryList(&FreeEntry
->List
);
197 // However, check if its' still got space left
199 if (FreeEntry
->Size
!= 0)
202 // Insert it back into a different list, based on its pages
204 i
= FreeEntry
->Size
- 1;
205 if (i
>= MI_MAX_FREE_PAGE_LISTS
) i
= MI_MAX_FREE_PAGE_LISTS
- 1;
206 InsertTailList (&MmNonPagedPoolFreeListHead
[i
],
211 // Grab the PTE for this allocation
213 PointerPte
= MiAddressToPte(BaseVa
);
214 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
217 // Grab the PFN NextEntry and index
219 Pfn1
= MiGetPfnEntry(PFN_FROM_PTE(PointerPte
));
222 // Now mark it as the beginning of an allocation
224 ASSERT(Pfn1
->u3
.e1
.StartOfAllocation
== 0);
225 Pfn1
->u3
.e1
.StartOfAllocation
= 1;
228 // Check if the allocation is larger than one page
230 if (SizeInPages
!= 1)
233 // Navigate to the last PFN entry and PTE
235 PointerPte
+= SizeInPages
- 1;
236 ASSERT(PointerPte
->u
.Hard
.Valid
== 1);
237 Pfn1
= MiGetPfnEntry(PointerPte
->u
.Hard
.PageFrameNumber
);
241 // Mark this PFN as the last (might be the same as the first)
243 ASSERT(Pfn1
->u3
.e1
.EndOfAllocation
== 0);
244 Pfn1
->u3
.e1
.EndOfAllocation
= 1;
247 // Release the nonpaged pool lock, and return the allocation
249 KeReleaseQueuedSpinLock(LockQueueMmNonPagedPoolLock
, OldIrql
);
254 // Try the next free page entry
256 NextEntry
= FreeEntry
->List
.Flink
;
258 } while (++NextHead
< LastHead
);
261 // If we got here, we're out of space.
262 // Start by releasing the lock
264 KeReleaseQueuedSpinLock(LockQueueMmNonPagedPoolLock
, OldIrql
);
267 // Allocate some system PTEs
269 StartPte
= MiReserveSystemPtes(SizeInPages
, NonPagedPoolExpansion
);
270 PointerPte
= StartPte
;
271 if (StartPte
== NULL
)
276 DPRINT1("Out of NP Expansion Pool\n");
281 // Acquire the pool lock now
283 OldIrql
= KeAcquireQueuedSpinLock(LockQueueMmNonPagedPoolLock
);
286 // Lock the PFN database too
288 //KeAcquireQueuedSpinLockAtDpcLevel(LockQueuePfnLock);
293 TempPte
= HyperTemplatePte
;
299 PageFrameNumber
= MmAllocPage(MC_NPPOOL
, 0);
302 // Get the PFN entry for it
304 Pfn1
= MiGetPfnEntry(PageFrameNumber
);
307 // Write the PTE for it
309 TempPte
.u
.Hard
.PageFrameNumber
= PageFrameNumber
;
310 ASSERT(PointerPte
->u
.Hard
.Valid
== 0);
311 ASSERT(TempPte
.u
.Hard
.Valid
== 1);
312 *PointerPte
++ = TempPte
;
313 } while (--SizeInPages
> 0);
316 // This is the last page
318 Pfn1
->u3
.e1
.EndOfAllocation
= 1;
321 // Get the first page and mark it as such
323 Pfn1
= MiGetPfnEntry(StartPte
->u
.Hard
.PageFrameNumber
);
324 Pfn1
->u3
.e1
.StartOfAllocation
= 1;
327 // Release the PFN and nonpaged pool lock
329 //KeReleaseQueuedSpinLockFromDpcLevel(LockQueuePfnLock);
330 KeReleaseQueuedSpinLock(LockQueueMmNonPagedPoolLock
, OldIrql
);
333 // Return the address
335 return MiPteToAddress(StartPte
);
340 MiFreePoolPages(IN PVOID StartingVa
)
342 PMMPTE PointerPte
, StartPte
;
343 PMMPFN Pfn1
, StartPfn
;
344 PFN_NUMBER FreePages
, NumberOfPages
;
346 PMMFREE_POOL_ENTRY FreeEntry
, NextEntry
, LastEntry
;
350 // Get the first PTE and its corresponding PFN entry
352 StartPte
= PointerPte
= MiAddressToPte(StartingVa
);
353 StartPfn
= Pfn1
= MiGetPfnEntry(PointerPte
->u
.Hard
.PageFrameNumber
);
356 // Loop until we find the last PTE
358 while (Pfn1
->u3
.e1
.EndOfAllocation
== 0)
364 Pfn1
= MiGetPfnEntry(PointerPte
->u
.Hard
.PageFrameNumber
);
368 // Now we know how many pages we have
370 NumberOfPages
= PointerPte
- StartPte
+ 1;
373 // Acquire the nonpaged pool lock
375 OldIrql
= KeAcquireQueuedSpinLock(LockQueueMmNonPagedPoolLock
);
378 // Mark the first and last PTEs as not part of an allocation anymore
380 StartPfn
->u3
.e1
.StartOfAllocation
= 0;
381 Pfn1
->u3
.e1
.EndOfAllocation
= 0;
384 // Assume we will free as many pages as the allocation was
386 FreePages
= NumberOfPages
;
389 // Peek one page past the end of the allocation
394 // Guard against going past initial nonpaged pool
396 if (MiGetPfnEntryIndex(Pfn1
) == MiEndOfInitialPoolFrame
)
399 // This page is on the outskirts of initial nonpaged pool, so ignore it
406 // Otherwise, our entire allocation must've fit within the initial non
407 // paged pool, or the expansion nonpaged pool, so get the PFN entry of
408 // the next allocation
410 ASSERT((ULONG_PTR
)StartingVa
+ NumberOfPages
<= (ULONG_PTR
)MmNonPagedPoolEnd
);
411 if (PointerPte
->u
.Hard
.Valid
== 1)
414 // It's either expansion or initial: get the PFN entry
416 Pfn1
= MiGetPfnEntry(PointerPte
->u
.Hard
.PageFrameNumber
);
421 // This means we've reached the guard page that protects the end of
422 // the expansion nonpaged pool
430 // Check if this allocation actually exists
432 if ((Pfn1
) && (Pfn1
->u3
.e1
.StartOfAllocation
== 0))
435 // It doesn't, so we should actually locate a free entry descriptor
437 FreeEntry
= (PMMFREE_POOL_ENTRY
)((ULONG_PTR
)StartingVa
+
438 (NumberOfPages
<< PAGE_SHIFT
));
439 ASSERT(FreeEntry
->Owner
== FreeEntry
);
442 // Consume this entry's pages, and remove it from its free list
444 FreePages
+= FreeEntry
->Size
;
445 RemoveEntryList (&FreeEntry
->List
);
449 // Now get the official free entry we'll create for the caller's allocation
451 FreeEntry
= StartingVa
;
454 // Check if the our allocation is the very first page
456 if (MiGetPfnEntryIndex(StartPfn
) == MiStartOfInitialPoolFrame
)
459 // Then we can't do anything or we'll risk underflowing
466 // Otherwise, get the PTE for the page right before our allocation
468 PointerPte
-= NumberOfPages
+ 1;
469 if (PointerPte
->u
.Hard
.Valid
== 1)
472 // It's either expansion or initial nonpaged pool, get the PFN entry
474 Pfn1
= MiGetPfnEntry(PointerPte
->u
.Hard
.PageFrameNumber
);
479 // We must've reached the guard page, so don't risk touching it
486 // Check if there is a valid PFN entry for the page before the allocation
487 // and then check if this page was actually the end of an allocation.
488 // If it wasn't, then we know for sure it's a free page
490 if ((Pfn1
) && (Pfn1
->u3
.e1
.EndOfAllocation
== 0))
493 // Get the free entry descriptor for that given page range
495 FreeEntry
= (PMMFREE_POOL_ENTRY
)((ULONG_PTR
)StartingVa
- PAGE_SIZE
);
496 FreeEntry
= FreeEntry
->Owner
;
499 // Check if the entry is small enough to be indexed on a free list
500 // If it is, we'll want to re-insert it, since we're about to
501 // collapse our pages on top of it, which will change its count
503 if (FreeEntry
->Size
< (MI_MAX_FREE_PAGE_LISTS
- 1))
506 // Remove the list from where it is now
508 RemoveEntryList(&FreeEntry
->List
);
513 FreeEntry
->Size
+= FreePages
;
516 // And now find the new appropriate list to place it in
518 i
= (ULONG
)(FreeEntry
->Size
- 1);
519 if (i
>= MI_MAX_FREE_PAGE_LISTS
) i
= MI_MAX_FREE_PAGE_LISTS
- 1;
524 InsertTailList(&MmNonPagedPoolFreeListHead
[i
], &FreeEntry
->List
);
529 // Otherwise, just combine our free pages into this entry
531 FreeEntry
->Size
+= FreePages
;
536 // Check if we were unable to do any compaction, and we'll stick with this
538 if (FreeEntry
== StartingVa
)
541 // Well, now we are a free entry. At worse we just have our newly freed
542 // pages, at best we have our pages plus whatever entry came after us
544 FreeEntry
->Size
= FreePages
;
547 // Find the appropriate list we should be on
549 i
= FreeEntry
->Size
- 1;
550 if (i
>= MI_MAX_FREE_PAGE_LISTS
) i
= MI_MAX_FREE_PAGE_LISTS
- 1;
555 InsertTailList (&MmNonPagedPoolFreeListHead
[i
], &FreeEntry
->List
);
559 // Just a sanity check
561 ASSERT(FreePages
!= 0);
564 // Get all the pages between our allocation and its end. These will all now
565 // become free page chunks.
567 NextEntry
= StartingVa
;
568 LastEntry
= (PMMFREE_POOL_ENTRY
)((ULONG_PTR
)NextEntry
+ (FreePages
<< PAGE_SHIFT
));
572 // Link back to the parent free entry, and keep going
574 NextEntry
->Owner
= FreeEntry
;
575 NextEntry
= (PMMFREE_POOL_ENTRY
)((ULONG_PTR
)NextEntry
+ PAGE_SIZE
);
576 } while (NextEntry
!= LastEntry
);
579 // We're done, release the lock and let the caller know how much we freed
581 KeReleaseQueuedSpinLock(LockQueueMmNonPagedPoolLock
, OldIrql
);
582 return NumberOfPages
;