- Implement a new Hyperspace Mapping Interface:

- The new interface is portable and much faster than before. For example, unmapping a hyperpage is almost a one-line operation.
    - The new interface is also thread-safe and uses the EPROCESS hyperspace spinlock.
    - However, in order to isolate from React Mm internals, the Hyper IRQL and Process are stored as globals, so this will not work on SMP.
    - For now, mapping vs. zero PTEs are not treated differently, but the two interfaces have been separated pending future work.
    - Performance tests with _rdtsc resulted in an improvement of over 300% compared to the old interface.
    - Hyperspace mappings are frequent, so the improvement is noticeable during startup (3/10ths of a second).
    - This also fixes incorrect initializtion of hyperspace -- pages were zeroed out (which requires hyperspace) before hyperspace was created.


svn path=/trunk/; revision=39925
This commit is contained in:
ReactOS Portable Systems Group 2009-03-10 00:31:14 +00:00
parent 468100e628
commit 115377a581
7 changed files with 259 additions and 88 deletions

View file

@ -20,6 +20,8 @@ PULONG MmGetPageDirectory(VOID);
((PMMPTE)(((((ULONG)(x)) >> 22) << 2) + PAGEDIRECTORY_MAP))
#define MiAddressToPte(x) \
((PMMPTE)(((((ULONG)(x)) >> 12) << 2) + PAGETABLE_MAP))
#define MiAddressToPteOffset(x) \
((((ULONG)(x)) << 10) >> 22)
#define ADDR_TO_PAGE_TABLE(v) (((ULONG)(v)) / (1024 * PAGE_SIZE))
#define ADDR_TO_PDE_OFFSET(v) ((((ULONG)(v)) / (1024 * PAGE_SIZE)))

View file

@ -59,6 +59,13 @@ typedef ULONG PFN_TYPE, *PPFN_TYPE;
/* Number of list heads to use */
#define MI_FREE_POOL_LISTS 4
#define HYPER_SPACE (0xC0400000)
#define MI_HYPERSPACE_PTES (256 - 1)
#define MI_MAPPING_RANGE_START (ULONG)HYPER_SPACE
#define MI_MAPPING_RANGE_END (MI_MAPPING_RANGE_START + \
MI_HYPERSPACE_PTES * PAGE_SIZE)
/* Signature of free pool blocks */
#define MM_FREE_POOL_TAG TAG('F', 'r', 'p', 'l')
@ -1060,15 +1067,45 @@ MmZeroPageThreadMain(
PVOID Context
);
/* i386/page.c *********************************************************/
/* hypermap.c *****************************************************************/
extern PEPROCESS HyperProcess;
extern KIRQL HyperIrql;
PVOID
NTAPI
MmCreateHyperspaceMapping(PFN_TYPE Page);
MiMapPageInHyperSpace(IN PEPROCESS Process,
IN PFN_NUMBER Page,
IN PKIRQL OldIrql);
PFN_TYPE
VOID
NTAPI
MmDeleteHyperspaceMapping(PVOID Address);
MiUnmapPageInHyperSpace(IN PEPROCESS Process,
IN PVOID Address,
IN KIRQL OldIrql);
PVOID
NTAPI
MiMapPagesToZeroInHyperSpace(IN PFN_NUMBER Page);
VOID
NTAPI
MiUnmapPagesInZeroSpace(IN PVOID Address);
//
// ReactOS Compatibility Layer
//
PVOID
FORCEINLINE
MmCreateHyperspaceMapping(IN PFN_NUMBER Page)
{
HyperProcess = (PEPROCESS)KeGetCurrentThread()->ApcState.Process;
return MiMapPageInHyperSpace(HyperProcess, Page, &HyperIrql);
}
#define MmDeleteHyperspaceMapping(x) MiUnmapPageInHyperSpace(HyperProcess, x, HyperIrql);
/* i386/page.c *********************************************************/
NTSTATUS
NTAPI

View file

@ -0,0 +1,197 @@
/*
* PROJECT: ReactOS Kernel
* LICENSE: BSD - See COPYING.ARM in the top level directory
* FILE: ntoskrnl/mm/hypermap.c
* PURPOSE: Hyperspace Mapping Functionality
* PROGRAMMERS: ReactOS Portable Systems Group
*/
/* INCLUDES *******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS ********************************************************************/
PMMPTE MmFirstReservedMappingPte;
PMMPTE MmLastReservedMappingPte;
MMPTE HyperTemplatePte;
PEPROCESS HyperProcess;
KIRQL HyperIrql;
/* PRIVATE FUNCTIONS **********************************************************/
VOID
NTAPI
MiInitHyperSpace(VOID)
{
PMMPTE PointerPte;
//
// Get the hyperspace PTE and zero out the page table
//
PointerPte = MiAddressToPte(HYPER_SPACE);
RtlZeroMemory(PointerPte, PAGE_SIZE);
//
// Setup mapping PTEs
//
MmFirstReservedMappingPte = MiAddressToPte(MI_MAPPING_RANGE_START);
MmLastReservedMappingPte = MiAddressToPte(MI_MAPPING_RANGE_END);
MmFirstReservedMappingPte->u.Hard.PageFrameNumber = MI_HYPERSPACE_PTES;
}
PVOID
NTAPI
MiMapPageInHyperSpace(IN PEPROCESS Process,
IN PFN_NUMBER Page,
IN PKIRQL OldIrql)
{
MMPTE TempPte;
PMMPTE PointerPte;
PFN_NUMBER Offset;
PVOID Address;
//
// Never accept page 0
//
ASSERT(Page != 0);
//
// Build the PTE
//
TempPte = HyperTemplatePte;
TempPte.u.Hard.PageFrameNumber = Page;
//
// Pick the first hyperspace PTE
//
PointerPte = MmFirstReservedMappingPte;
//
// Acquire the hyperlock
//
ASSERT(Process == PsGetCurrentProcess());
KeAcquireSpinLock(&Process->HyperSpaceLock, OldIrql);
//
// Now get the first free PTE
//
Offset = PFN_FROM_PTE(PointerPte);
if (!Offset)
{
//
// Reset the PTEs
//
Offset = MI_HYPERSPACE_PTES;
KeFlushProcessTb();
}
//
// Prepare the next PTE
//
PointerPte->u.Hard.PageFrameNumber = Offset - 1;
//
// Write the current PTE
//
PointerPte += Offset;
ASSERT(PointerPte->u.Hard.Valid == 0);
ASSERT(TempPte.u.Hard.Valid == 1);
*PointerPte = TempPte;
//
// Return the address
//
Address = (PVOID)((ULONG_PTR)PointerPte << 10);
return Address;
}
VOID
NTAPI
MiUnmapPageInHyperSpace(IN PEPROCESS Process,
IN PVOID Address,
IN KIRQL OldIrql)
{
ASSERT(Process == PsGetCurrentProcess());
//
// Blow away the mapping
//
MiAddressToPte(Address)->u.Long = 0;
//
// Release the hyperlock
//
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
KeReleaseSpinLock(&Process->HyperSpaceLock, OldIrql);
}
PVOID
NTAPI
MiMapPagesToZeroInHyperSpace(IN PFN_NUMBER Page)
{
MMPTE TempPte;
PMMPTE PointerPte;
PFN_NUMBER Offset;
PVOID Address;
//
// Never accept page 0
//
ASSERT(Page != 0);
//
// Build the PTE
//
TempPte = HyperTemplatePte;
TempPte.u.Hard.PageFrameNumber = Page;
//
// Pick the first hyperspace PTE
//
PointerPte = MmFirstReservedMappingPte;
//
// Now get the first free PTE
//
Offset = PFN_FROM_PTE(PointerPte);
if (!Offset)
{
//
// Reset the PTEs
//
Offset = MI_HYPERSPACE_PTES;
KeFlushProcessTb();
}
//
// Prepare the next PTE
//
PointerPte->u.Hard.PageFrameNumber = Offset - 1;
//
// Write the current PTE
//
PointerPte += Offset;
ASSERT(PointerPte->u.Hard.Valid == 0);
ASSERT(TempPte.u.Hard.Valid == 1);
*PointerPte = TempPte;
//
// Return the address
//
Address = (PVOID)((ULONG_PTR)PointerPte << 10);
return Address;
}
VOID
NTAPI
MiUnmapPagesInZeroSpace(IN PVOID Address)
{
//
// Blow away the mapping
//
MiAddressToPte(Address)->u.Long = 0;
}

View file

@ -1069,88 +1069,6 @@ MmGetPhysicalAddress(PVOID vaddr)
return p;
}
PVOID
NTAPI
MmCreateHyperspaceMapping(PFN_TYPE Page)
{
PVOID Address;
ULONG i;
ULONG Entry;
PULONG Pte;
Entry = PFN_TO_PTE(Page) | PA_PRESENT | PA_READWRITE;
Pte = (PULONG)MiAddressToPte(HYPERSPACE) + Page % 1024;
if (Page & 1024)
{
for (i = Page % 1024; i < 1024; i++, Pte++)
{
if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0))
{
break;
}
}
if (i >= 1024)
{
Pte = (PULONG)MiAddressToPte(HYPERSPACE);
for (i = 0; i < Page % 1024; i++, Pte++)
{
if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0))
{
break;
}
}
if (i >= Page % 1024)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
}
}
else
{
for (i = Page % 1024; (LONG)i >= 0; i--, Pte--)
{
if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0))
{
break;
}
}
if ((LONG)i < 0)
{
Pte = (PULONG)MiAddressToPte(HYPERSPACE) + 1023;
for (i = 1023; i > Page % 1024; i--, Pte--)
{
if (0 == InterlockedCompareExchange((PLONG)Pte, (LONG)Entry, 0))
{
break;
}
}
if (i <= Page % 1024)
{
KeBugCheck(MEMORY_MANAGEMENT);
}
}
}
Address = (PVOID)((ULONG_PTR)HYPERSPACE + i * PAGE_SIZE);
__invlpg(Address);
return Address;
}
PFN_TYPE
NTAPI
MmDeleteHyperspaceMapping(PVOID Address)
{
PFN_TYPE Pfn;
ULONG Entry;
ASSERT (IS_HYPERSPACE(Address));
Entry = InterlockedExchangePte(MiAddressToPte(Address), 0);
Pfn = PTE_TO_PFN(Entry);
__invlpg(Address);
return Pfn;
}
VOID
NTAPI
MmUpdatePageDir(PEPROCESS Process, PVOID Address, ULONG Size)
@ -1187,6 +1105,8 @@ MmUpdatePageDir(PEPROCESS Process, PVOID Address, ULONG Size)
}
}
extern MMPTE HyperTemplatePte;
VOID
INIT_FUNCTION
NTAPI
@ -1197,6 +1117,12 @@ MmInitGlobalKernelPageDirectory(VOID)
DPRINT("MmInitGlobalKernelPageDirectory()\n");
//
// Setup template
//
HyperTemplatePte.u.Long = (PA_PRESENT | PA_READWRITE | PA_DIRTY | PA_ACCESSED);
if (Ke386GlobalPagesEnabled) HyperTemplatePte.u.Long |= PA_BIT_GLOBAL;
for (i = ADDR_TO_PDE_OFFSET(MmSystemRangeStart); i < 1024; i++)
{
if (i != ADDR_TO_PDE_OFFSET(PAGETABLE_MAP) &&

View file

@ -22,13 +22,13 @@ MiZeroPage(PFN_TYPE Page)
{
PVOID TempAddress;
TempAddress = MmCreateHyperspaceMapping(Page);
TempAddress = MiMapPagesToZeroInHyperSpace(Page);
if (TempAddress == NULL)
{
return(STATUS_NO_MEMORY);
}
memset(TempAddress, 0, PAGE_SIZE);
MmDeleteHyperspaceMapping(TempAddress);
MiUnmapPagesInZeroSpace(TempAddress);
return(STATUS_SUCCESS);
}

View file

@ -351,6 +351,11 @@ MiGetLastKernelAddress(VOID)
return LastKrnlPhysAddr << PAGE_SHIFT;
}
VOID
NTAPI
MiInitHyperSpace(VOID);
VOID
INIT_FUNCTION
NTAPI
@ -434,6 +439,9 @@ MmInit1(VOID)
/* Unmap low memory */
MmDeletePageTable(NULL, 0);
/* Initialize hyperspace */
MiInitHyperSpace();
/* Intialize memory areas */
MmInitVirtualMemory();

View file

@ -365,6 +365,7 @@
<file>cont.c</file>
<file>drvlck.c</file>
<file>freelist.c</file>
<file>hypermap.c</file>
<file>iospace.c</file>
<file>kmap.c</file>
<file>marea.c</file>