mirror of
https://github.com/reactos/reactos.git
synced 2024-11-20 06:15:26 +00:00
- Implement a simple nonpaged pool page allocator and deallocator:
- Not actually used yet! - No support for expansion nonpaged pool yet. - No major optimizations. - We simply do the sane thing of trying to combine the next free allocation with the one we're about to make, on free. - If possible, we'll also check if the previous allocation was a free block, and we'll merge ourselves (and in the best case, whoever is following us) into that block instead. - No debug checks/validation. - Write a couple lines of code to allocate and free up to 4096 pages (it will stop when it runs out). - This is to properly excercise this code on everyone's machines to make sure it works fine (it adds a little delay to booting, but only temporary). svn path=/trunk/; revision=41859
This commit is contained in:
parent
08df2017e3
commit
2aabdcc5fd
2 changed files with 411 additions and 0 deletions
|
@ -714,6 +714,26 @@ MmArmInitSystem(IN ULONG Phase,
|
||||||
// Initialize the nonpaged pool
|
// Initialize the nonpaged pool
|
||||||
//
|
//
|
||||||
InitializePool(NonPagedPool, 0);
|
InitializePool(NonPagedPool, 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Do a little test of the nonpaged pool allocator
|
||||||
|
//
|
||||||
|
if (1)
|
||||||
|
{
|
||||||
|
ULONG i = 0;
|
||||||
|
PVOID Buffers[4096];
|
||||||
|
while (TRUE)
|
||||||
|
{
|
||||||
|
Buffers[i] = MiAllocatePoolPages(NonPagedPool, PAGE_SIZE);
|
||||||
|
if (!Buffers[i]) break;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (i--)
|
||||||
|
{
|
||||||
|
MiFreePoolPages(Buffers[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -125,4 +125,395 @@ MiInitializeArmPool(VOID)
|
||||||
NonPagedPoolExpansion);
|
NonPagedPoolExpansion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PVOID
|
||||||
|
NTAPI
|
||||||
|
MiAllocatePoolPages(IN POOL_TYPE PoolType,
|
||||||
|
IN SIZE_T SizeInBytes)
|
||||||
|
{
|
||||||
|
PFN_NUMBER SizeInPages;
|
||||||
|
ULONG i;
|
||||||
|
KIRQL OldIrql;
|
||||||
|
PLIST_ENTRY NextEntry, NextHead, LastHead;
|
||||||
|
PMMPTE PointerPte;
|
||||||
|
PMMPFN Pfn1;
|
||||||
|
PVOID BaseVa;
|
||||||
|
PMMFREE_POOL_ENTRY FreeEntry;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Figure out how big the allocation is in pages
|
||||||
|
//
|
||||||
|
SizeInPages = BYTES_TO_PAGES(SizeInBytes);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Allocations of less than 4 pages go into their individual buckets
|
||||||
|
//
|
||||||
|
i = SizeInPages - 1;
|
||||||
|
if (i >= MI_MAX_FREE_PAGE_LISTS) i = MI_MAX_FREE_PAGE_LISTS - 1;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Loop through all the free page lists based on the page index
|
||||||
|
//
|
||||||
|
NextHead = &MmNonPagedPoolFreeListHead[i];
|
||||||
|
LastHead = &MmNonPagedPoolFreeListHead[MI_MAX_FREE_PAGE_LISTS];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Acquire the nonpaged pool lock
|
||||||
|
//
|
||||||
|
OldIrql = KeAcquireQueuedSpinLock(LockQueueMmNonPagedPoolLock);
|
||||||
|
do
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Now loop through all the free page entries in this given list
|
||||||
|
//
|
||||||
|
NextEntry = NextHead->Flink;
|
||||||
|
while (NextEntry != NextHead)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Grab the entry and see if it can handle our allocation
|
||||||
|
//
|
||||||
|
FreeEntry = CONTAINING_RECORD(NextEntry, MMFREE_POOL_ENTRY, List);
|
||||||
|
if (FreeEntry->Size >= SizeInPages)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// It does, so consume the pages from here
|
||||||
|
//
|
||||||
|
FreeEntry->Size -= SizeInPages;
|
||||||
|
|
||||||
|
//
|
||||||
|
// The allocation will begin in this free page area
|
||||||
|
//
|
||||||
|
BaseVa = (PVOID)((ULONG_PTR)FreeEntry +
|
||||||
|
(FreeEntry->Size << PAGE_SHIFT));
|
||||||
|
|
||||||
|
//
|
||||||
|
// This is not a free page segment anymore
|
||||||
|
//
|
||||||
|
RemoveEntryList(&FreeEntry->List);
|
||||||
|
|
||||||
|
//
|
||||||
|
// However, check if its' still got space left
|
||||||
|
//
|
||||||
|
if (FreeEntry->Size != 0)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Insert it back into a different list, based on its pages
|
||||||
|
//
|
||||||
|
i = FreeEntry->Size - 1;
|
||||||
|
if (i >= MI_MAX_FREE_PAGE_LISTS) i = MI_MAX_FREE_PAGE_LISTS - 1;
|
||||||
|
InsertTailList (&MmNonPagedPoolFreeListHead[i],
|
||||||
|
&FreeEntry->List);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Grab the PTE for this allocation
|
||||||
|
//
|
||||||
|
PointerPte = MiAddressToPte(BaseVa);
|
||||||
|
ASSERT(PointerPte->u.Hard.Valid == 1);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Grab the PFN NextEntry and index
|
||||||
|
//
|
||||||
|
Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Now mark it as the beginning of an allocation
|
||||||
|
//
|
||||||
|
ASSERT(Pfn1->u3.e1.StartOfAllocation == 0);
|
||||||
|
Pfn1->u3.e1.StartOfAllocation = 1;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check if the allocation is larger than one page
|
||||||
|
//
|
||||||
|
if (SizeInPages != 1)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Navigate to the last PFN entry and PTE
|
||||||
|
//
|
||||||
|
PointerPte += SizeInPages - 1;
|
||||||
|
ASSERT(PointerPte->u.Hard.Valid == 1);
|
||||||
|
Pfn1 = MiGetPfnEntry(PointerPte->u.Hard.PageFrameNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Mark this PFN as the last (might be the same as the first)
|
||||||
|
//
|
||||||
|
ASSERT(Pfn1->u3.e1.EndOfAllocation == 0);
|
||||||
|
Pfn1->u3.e1.EndOfAllocation = 1;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Release the nonpaged pool lock, and return the allocation
|
||||||
|
//
|
||||||
|
KeReleaseQueuedSpinLock(LockQueueMmNonPagedPoolLock, OldIrql);
|
||||||
|
return BaseVa;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Try the next free page entry
|
||||||
|
//
|
||||||
|
NextEntry = FreeEntry->List.Flink;
|
||||||
|
}
|
||||||
|
} while (++NextHead < LastHead);
|
||||||
|
|
||||||
|
//
|
||||||
|
// If we got here, we're out of space.
|
||||||
|
// Start by releasing the lock
|
||||||
|
//
|
||||||
|
KeReleaseQueuedSpinLock (LockQueueMmNonPagedPoolLock, OldIrql);
|
||||||
|
|
||||||
|
//
|
||||||
|
// We should now go into expansion nonpaged pool
|
||||||
|
//
|
||||||
|
DPRINT1("Out of NP Pool\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
ULONG
|
||||||
|
NTAPI
|
||||||
|
MiFreePoolPages(IN PVOID StartingVa)
|
||||||
|
{
|
||||||
|
PMMPTE PointerPte, StartPte;
|
||||||
|
PMMPFN Pfn1, StartPfn;
|
||||||
|
PFN_NUMBER FreePages, NumberOfPages;
|
||||||
|
KIRQL OldIrql;
|
||||||
|
PMMFREE_POOL_ENTRY FreeEntry, NextEntry, LastEntry;
|
||||||
|
ULONG i;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get the first PTE and its corresponding PFN entry
|
||||||
|
//
|
||||||
|
StartPte = PointerPte = MiAddressToPte(StartingVa);
|
||||||
|
StartPfn = Pfn1 = MiGetPfnEntry(PointerPte->u.Hard.PageFrameNumber);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Loop until we find the last PTE
|
||||||
|
//
|
||||||
|
while (Pfn1->u3.e1.EndOfAllocation == 0)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Keep going
|
||||||
|
//
|
||||||
|
PointerPte++;
|
||||||
|
Pfn1 = MiGetPfnEntry(PointerPte->u.Hard.PageFrameNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Now we know how many pages we have
|
||||||
|
//
|
||||||
|
NumberOfPages = PointerPte - StartPte + 1;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Acquire the nonpaged pool lock
|
||||||
|
//
|
||||||
|
OldIrql = KeAcquireQueuedSpinLock(LockQueueMmNonPagedPoolLock);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Mark the first and last PTEs as not part of an allocation anymore
|
||||||
|
//
|
||||||
|
StartPfn->u3.e1.StartOfAllocation = 0;
|
||||||
|
Pfn1->u3.e1.EndOfAllocation = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Assume we will free as many pages as the allocation was
|
||||||
|
//
|
||||||
|
FreePages = NumberOfPages;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Peek one page past the end of the allocation
|
||||||
|
//
|
||||||
|
PointerPte++;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Guard against going past initial nonpaged pool
|
||||||
|
//
|
||||||
|
if (MiGetPfnEntryIndex(Pfn1) == MiEndOfInitialPoolFrame)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// This page is on the outskirts of initial nonpaged pool, so ignore it
|
||||||
|
//
|
||||||
|
DPRINT1("End of initial frame\n");
|
||||||
|
Pfn1 = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Otherwise, our entire allocation must've fit within the initial non
|
||||||
|
// paged pool, or the expansion nonpaged pool, so get the PFN entry of
|
||||||
|
// the next allocation
|
||||||
|
//
|
||||||
|
ASSERT((ULONG_PTR)StartingVa + NumberOfPages <= (ULONG_PTR)MmNonPagedPoolEnd);
|
||||||
|
if (PointerPte->u.Hard.Valid == 1)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// It's either expansion or initial: get the PFN entry
|
||||||
|
//
|
||||||
|
Pfn1 = MiGetPfnEntry(PointerPte->u.Hard.PageFrameNumber);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// This means we've reached the guard page that protects the end of
|
||||||
|
// the expansion nonpaged pool
|
||||||
|
//
|
||||||
|
Pfn1 = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check if this allocation actually exists
|
||||||
|
//
|
||||||
|
if ((Pfn1) && (Pfn1->u3.e1.StartOfAllocation == 0))
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// It doesn't, so we should actually locate a free entry descriptor
|
||||||
|
//
|
||||||
|
FreeEntry = (PMMFREE_POOL_ENTRY)((ULONG_PTR)StartingVa +
|
||||||
|
(NumberOfPages << PAGE_SHIFT));
|
||||||
|
ASSERT(FreeEntry->Owner == FreeEntry);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Consume this entry's pages, and remove it from its free list
|
||||||
|
//
|
||||||
|
FreePages += FreeEntry->Size;
|
||||||
|
RemoveEntryList (&FreeEntry->List);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Now get the official free entry we'll create for the caller's allocation
|
||||||
|
//
|
||||||
|
FreeEntry = StartingVa;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check if the our allocation is the very first page
|
||||||
|
//
|
||||||
|
if (MiGetPfnEntryIndex(StartPfn) == MiStartOfInitialPoolFrame)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Then we can't do anything or we'll risk underflowing
|
||||||
|
//
|
||||||
|
DPRINT1("Start of of initial frame\n");
|
||||||
|
Pfn1 = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Otherwise, get the PTE for the page right before our allocation
|
||||||
|
//
|
||||||
|
PointerPte -= NumberOfPages + 1;
|
||||||
|
if (PointerPte->u.Hard.Valid == 1)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// It's either expansion or initial nonpaged pool, get the PFN entry
|
||||||
|
//
|
||||||
|
Pfn1 = MiGetPfnEntry(PointerPte->u.Hard.PageFrameNumber);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// We must've reached the guard page, so don't risk touching it
|
||||||
|
//
|
||||||
|
Pfn1 = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check if there is a valid PFN entry for the page before the allocation
|
||||||
|
// and then check if this page was actually the end of an allocation.
|
||||||
|
// If it wasn't, then we know for sure it's a free page
|
||||||
|
//
|
||||||
|
if ((Pfn1) && (Pfn1->u3.e1.EndOfAllocation == 0))
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Get the free entry descriptor for that given page range
|
||||||
|
//
|
||||||
|
FreeEntry = (PMMFREE_POOL_ENTRY)((ULONG_PTR)StartingVa - PAGE_SIZE);
|
||||||
|
FreeEntry = FreeEntry->Owner;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check if the entry is small enough to be indexed on a free list
|
||||||
|
// If it is, we'll want to re-insert it, since we're about to
|
||||||
|
// collapse our pages on top of it, which will change its count
|
||||||
|
//
|
||||||
|
if (FreeEntry->Size < (MI_MAX_FREE_PAGE_LISTS - 1))
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Remove the list from where it is now
|
||||||
|
//
|
||||||
|
RemoveEntryList(&FreeEntry->List);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Update its size
|
||||||
|
//
|
||||||
|
FreeEntry->Size += FreePages;
|
||||||
|
|
||||||
|
//
|
||||||
|
// And now find the new appropriate list to place it in
|
||||||
|
//
|
||||||
|
i = (ULONG)(FreeEntry->Size - 1);
|
||||||
|
if (i >= MI_MAX_FREE_PAGE_LISTS) i = MI_MAX_FREE_PAGE_LISTS - 1;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Do it
|
||||||
|
//
|
||||||
|
InsertTailList(&MmNonPagedPoolFreeListHead[i], &FreeEntry->List);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Otherwise, just combine our free pages into this entry
|
||||||
|
//
|
||||||
|
FreeEntry->Size += FreePages;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check if we were unable to do any compaction, and we'll stick with this
|
||||||
|
//
|
||||||
|
if (FreeEntry == StartingVa)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Well, now we are a free entry. At worse we just have our newly freed
|
||||||
|
// pages, at best we have our pages plus whatever entry came after us
|
||||||
|
//
|
||||||
|
FreeEntry->Size = FreePages;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Find the appropriate list we should be on
|
||||||
|
//
|
||||||
|
i = FreeEntry->Size - 1;
|
||||||
|
if (i >= MI_MAX_FREE_PAGE_LISTS) i = MI_MAX_FREE_PAGE_LISTS - 1;
|
||||||
|
|
||||||
|
//
|
||||||
|
// And insert us
|
||||||
|
//
|
||||||
|
InsertTailList (&MmNonPagedPoolFreeListHead[i], &FreeEntry->List);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Just a sanity check
|
||||||
|
//
|
||||||
|
ASSERT(FreePages != 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Get all the pages between our allocation and its end. These will all now
|
||||||
|
// become free page chunks.
|
||||||
|
//
|
||||||
|
NextEntry = StartingVa;
|
||||||
|
LastEntry = (PMMFREE_POOL_ENTRY)((ULONG_PTR)NextEntry + (FreePages << PAGE_SHIFT));
|
||||||
|
do
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Link back to the parent free entry, and keep going
|
||||||
|
//
|
||||||
|
NextEntry->Owner = FreeEntry;
|
||||||
|
NextEntry = (PMMFREE_POOL_ENTRY)((ULONG_PTR)NextEntry + PAGE_SIZE);
|
||||||
|
} while (NextEntry != LastEntry);
|
||||||
|
|
||||||
|
//
|
||||||
|
// We're done, release the lock and let the caller know how much we freed
|
||||||
|
//
|
||||||
|
KeReleaseQueuedSpinLock(LockQueueMmNonPagedPoolLock, OldIrql);
|
||||||
|
return NumberOfPages;
|
||||||
|
}
|
||||||
|
|
||||||
/* EOF */
|
/* EOF */
|
||||||
|
|
Loading…
Reference in a new issue