[NTOS]: Fix a very stupid bug which made any machine with less than 52GB of RAM appear as a "Small memory machine" (instead of 13MB).

[NTOS]: Initialize MmTotalCommitLimit, MmTotalCommitLimitMaximum for consistency.
[NTOS]: Initialize and honor MmAllocationFragment value from registry, or from defaults.
[NTOS]: Initialize the system cache working set pushlock.
[NTOS]: Implement Mi(Un)Lock(Process)WorkingSet inlines to handle the various scenarios. Replace broken/hacky code that was in the system loader, fault handler, and VM deletion paths with the correct macros. No locking is done yet as ReactOS' MAREA implementation overloads the Vm field in EPROCESS, but the correct APC blockings and state transitions are done.
[NTOS]: Add another helper from symbols, MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS, and use it instead of the math-by-hand.
[NTOS]: Add MM_IS_ANY_LOCK_HELD and MI_WS_OWNER to help out the new working let locking inlines (names taken from symbols).
[NTOS]: Add _1GB helper for large-memory checks. Also define _1KB as "1024u". The "u" makes GCC not complain when sizes reach >= 2GB, because GCC will interpret the integer literals as "signed", even if the final calculated value is in an unsigned variable.

svn path=/trunk/; revision=48187
This commit is contained in:
Sir Richard 2010-07-22 18:26:04 +00:00
parent 4ae7219e5c
commit 23c3e742d9
5 changed files with 294 additions and 72 deletions

View file

@ -8,35 +8,39 @@
#ifndef _M_AMD64
#define MI_MIN_PAGES_FOR_NONPAGED_POOL_TUNING ((255*1024*1024) >> PAGE_SHIFT)
#define MI_MIN_PAGES_FOR_SYSPTE_TUNING ((19*1024*1024) >> PAGE_SHIFT)
#define MI_MIN_PAGES_FOR_SYSPTE_BOOST ((32*1024*1024) >> PAGE_SHIFT)
#define MI_MAX_INIT_NONPAGED_POOL_SIZE (128 * 1024 * 1024)
#define MI_MAX_NONPAGED_POOL_SIZE (128 * 1024 * 1024)
#define MI_MAX_FREE_PAGE_LISTS 4
#define MI_MIN_PAGES_FOR_NONPAGED_POOL_TUNING ((255 * _1MB) >> PAGE_SHIFT)
#define MI_MIN_PAGES_FOR_SYSPTE_TUNING ((19 * _1MB) >> PAGE_SHIFT)
#define MI_MIN_PAGES_FOR_SYSPTE_BOOST ((32 * _1MB) >> PAGE_SHIFT)
#define MI_MAX_INIT_NONPAGED_POOL_SIZE (128 * _1MB)
#define MI_MAX_NONPAGED_POOL_SIZE (128 * _1MB)
#define MI_MAX_FREE_PAGE_LISTS 4
#define MI_MIN_INIT_PAGED_POOLSIZE (32 * 1024 * 1024)
#define MI_MIN_INIT_PAGED_POOLSIZE (32 * _1MB)
#define MI_SESSION_VIEW_SIZE (20 * 1024 * 1024)
#define MI_SESSION_POOL_SIZE (16 * 1024 * 1024)
#define MI_SESSION_IMAGE_SIZE (8 * 1024 * 1024)
#define MI_SESSION_WORKING_SET_SIZE (4 * 1024 * 1024)
#define MI_SESSION_SIZE (MI_SESSION_VIEW_SIZE + \
MI_SESSION_POOL_SIZE + \
MI_SESSION_IMAGE_SIZE + \
MI_SESSION_WORKING_SET_SIZE)
#define MI_SESSION_VIEW_SIZE (20 * _1MB)
#define MI_SESSION_POOL_SIZE (16 * _1MB)
#define MI_SESSION_IMAGE_SIZE (8 * _1MB)
#define MI_SESSION_WORKING_SET_SIZE (4 * _1MB)
#define MI_SESSION_SIZE (MI_SESSION_VIEW_SIZE + \
MI_SESSION_POOL_SIZE + \
MI_SESSION_IMAGE_SIZE + \
MI_SESSION_WORKING_SET_SIZE)
#define MI_SYSTEM_VIEW_SIZE (16 * 1024 * 1024)
#define MI_SYSTEM_VIEW_SIZE (16 * _1MB)
#define MI_SYSTEM_CACHE_WS_START (PVOID)0xC0C00000
#define MI_PAGED_POOL_START (PVOID)0xE1000000
#define MI_NONPAGED_POOL_END (PVOID)0xFFBE0000
#define MI_DEBUG_MAPPING (PVOID)0xFFBFF000
#define MI_SYSTEM_CACHE_WS_START (PVOID)0xC0C00000
#define MI_PAGED_POOL_START (PVOID)0xE1000000
#define MI_NONPAGED_POOL_END (PVOID)0xFFBE0000
#define MI_DEBUG_MAPPING (PVOID)0xFFBFF000
#define MI_MIN_SECONDARY_COLORS 8
#define MI_SECONDARY_COLORS 64
#define MI_MAX_SECONDARY_COLORS 1024
#define MI_MIN_ALLOCATION_FRAGMENT (4 * _1KB)
#define MI_ALLOCATION_FRAGMENT (64 * _1KB)
#define MI_MAX_ALLOCATION_FRAGMENT (2 * _1MB)
#define MM_HIGHEST_VAD_ADDRESS \
(PVOID)((ULONG_PTR)MM_HIGHEST_USER_ADDRESS - (16 * PAGE_SIZE))
@ -46,8 +50,9 @@
#endif /* !_M_AMD64 */
/* Make the code cleaner with some definitions for size multiples */
#define _1KB (1024)
#define _1KB (1024u)
#define _1MB (1024 * _1KB)
#define _1GB (1024 * _1MB)
/* Area mapped by a PDE */
#define PDE_MAPPED_VA (PTE_COUNT * PAGE_SIZE)
@ -199,6 +204,9 @@ MmProtectToPteMask[32] =
#define MI_IS_SYSTEM_PAGE_TABLE_ADDRESS(Address) \
(((Address) >= (PVOID)MiAddressToPte(MmSystemRangeStart)) && ((Address) <= (PVOID)PTE_TOP))
#define MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address) \
(((PVOID)(Address) >= (PVOID)PTE_BASE) && ((PVOID)(Address) <= (PVOID)MmHyperSpaceEnd))
//
// Corresponds to MMPTE_SOFTWARE.Protection
//
@ -608,6 +616,165 @@ MI_WRITE_INVALID_PTE(IN PMMPTE PointerPte,
*PointerPte = InvalidPte;
}
//
// Checks if the thread already owns a working set
//
FORCEINLINE
BOOLEAN
MM_ANY_WS_LOCK_HELD(IN PETHREAD Thread)
{
/* If any of these are held, return TRUE */
return ((Thread->OwnsProcessWorkingSetExclusive) ||
(Thread->OwnsProcessWorkingSetShared) ||
(Thread->OwnsSystemWorkingSetExclusive) ||
(Thread->OwnsSystemWorkingSetShared) ||
(Thread->OwnsSessionWorkingSetExclusive) ||
(Thread->OwnsSessionWorkingSetShared));
}
//
// Checks if the process owns the working set lock
//
FORCEINLINE
BOOLEAN
MI_WS_OWNER(IN PEPROCESS Process)
{
/* Check if this process is the owner, and that the thread owns the WS */
return ((KeGetCurrentThread()->ApcState.Process == &Process->Pcb) &&
((PsGetCurrentThread()->OwnsProcessWorkingSetExclusive) ||
(PsGetCurrentThread()->OwnsProcessWorkingSetShared)));
}
//
// Locks the working set for the given process
//
FORCEINLINE
VOID
MiLockProcessWorkingSet(IN PEPROCESS Process,
IN PETHREAD Thread)
{
/* Shouldn't already be owning the process working set */
ASSERT(Thread->OwnsProcessWorkingSetShared == FALSE);
ASSERT(Thread->OwnsProcessWorkingSetExclusive == FALSE);
/* Block APCs, make sure that still nothing is already held */
KeEnterGuardedRegion();
ASSERT(!MM_ANY_WS_LOCK_HELD(Thread));
/* FIXME: Actually lock it (we can't because Vm is used by MAREAs) */
/* FIXME: This also can't be checked because Vm is used by MAREAs) */
//ASSERT(Process->Vm.Flags.AcquiredUnsafe == 0);
/* Okay, now we can own it exclusively */
ASSERT(Thread->OwnsProcessWorkingSetExclusive == FALSE);
Thread->OwnsProcessWorkingSetExclusive = TRUE;
}
//
// Unlocks the working set for the given process
//
FORCEINLINE
VOID
MiUnlockProcessWorkingSet(IN PEPROCESS Process,
IN PETHREAD Thread)
{
/* Make sure this process really is owner, and it was a safe acquisition */
ASSERT(MI_WS_OWNER(Process));
/* This can't be checked because Vm is used by MAREAs) */
//ASSERT(Process->Vm.Flags.AcquiredUnsafe == 0);
/* The thread doesn't own it anymore */
ASSERT(Thread->OwnsProcessWorkingSetExclusive == TRUE);
Thread->OwnsProcessWorkingSetExclusive = FALSE;
/* FIXME: Actually release it (we can't because Vm is used by MAREAs) */
/* Unblock APCs */
KeLeaveGuardedRegion();
}
//
// Locks the working set
//
FORCEINLINE
VOID
MiLockWorkingSet(IN PETHREAD Thread,
IN PMMSUPPORT WorkingSet)
{
/* Block APCs */
KeEnterGuardedRegion();
/* Working set should be in global memory */
ASSERT(MI_IS_SESSION_ADDRESS((PVOID)WorkingSet) == FALSE);
/* Thread shouldn't already be owning something */
ASSERT(!MM_ANY_WS_LOCK_HELD(Thread));
/* FIXME: Actually lock it (we can't because Vm is used by MAREAs) */
/* Which working set is this? */
if (WorkingSet == &MmSystemCacheWs)
{
/* Own the system working set */
ASSERT((Thread->OwnsSystemWorkingSetExclusive == FALSE) &&
(Thread->OwnsSystemWorkingSetShared == FALSE));
Thread->OwnsSystemWorkingSetExclusive = TRUE;
}
else if (WorkingSet->Flags.SessionSpace)
{
/* We don't implement this yet */
UNIMPLEMENTED;
while (TRUE);
}
else
{
/* Own the process working set */
ASSERT((Thread->OwnsProcessWorkingSetExclusive == FALSE) &&
(Thread->OwnsProcessWorkingSetShared == FALSE));
Thread->OwnsProcessWorkingSetExclusive = TRUE;
}
}
//
// Unlocks the working set
//
FORCEINLINE
VOID
MiUnlockWorkingSet(IN PETHREAD Thread,
IN PMMSUPPORT WorkingSet)
{
/* Working set should be in global memory */
ASSERT(MI_IS_SESSION_ADDRESS((PVOID)WorkingSet) == FALSE);
/* Which working set is this? */
if (WorkingSet == &MmSystemCacheWs)
{
/* Release the system working set */
ASSERT((Thread->OwnsSystemWorkingSetExclusive == TRUE) ||
(Thread->OwnsSystemWorkingSetShared == TRUE));
Thread->OwnsSystemWorkingSetExclusive = FALSE;
}
else if (WorkingSet->Flags.SessionSpace)
{
/* We don't implement this yet */
UNIMPLEMENTED;
while (TRUE);
}
else
{
/* Release the process working set */
ASSERT((Thread->OwnsProcessWorkingSetExclusive) ||
(Thread->OwnsProcessWorkingSetShared));
Thread->OwnsProcessWorkingSetExclusive = FALSE;
}
/* FIXME: Actually release it (we can't because Vm is used by MAREAs) */
/* Unblock APCs */
KeLeaveGuardedRegion();
}
NTSTATUS
NTAPI
MmArmInitSystem(

View file

@ -328,6 +328,26 @@ PFN_NUMBER MmSystemCacheWsMaximum = 350;
/* FIXME: Move to cache/working set code later */
BOOLEAN MmLargeSystemCache;
/*
* This value determines in how many fragments/chunks the subsection prototype
* PTEs should be allocated when mapping a section object. It is configurable in
* the registry through the MapAllocationFragment parameter.
*
* The default is 64KB on systems with more than 1GB of RAM, 32KB on systems with
* more than 256MB of RAM, and 16KB on systems with less than 256MB of RAM.
*
* The maximum it can be set to is 2MB, and the minimum is 4KB.
*/
SIZE_T MmAllocationFragment;
/*
* These two values track how much virtual memory can be committed, and when
* expansion should happen.
*/
// FIXME: They should be moved elsewhere since it's not an "init" setting?
SIZE_T MmTotalCommitLimit;
SIZE_T MmTotalCommitLimitMaximum;
/* PRIVATE FUNCTIONS **********************************************************/
//
@ -1799,6 +1819,44 @@ MmArmInitSystem(IN ULONG Phase,
DPRINT("System PTE count has been tuned to %d (%d bytes)\n",
MmNumberOfSystemPtes, MmNumberOfSystemPtes * PAGE_SIZE);
/* Initialize the working set lock */
ExInitializePushLock((PULONG_PTR)&MmSystemCacheWs.WorkingSetMutex);
/* Set commit limit */
MmTotalCommitLimit = 2 * _1GB;
MmTotalCommitLimitMaximum = MmTotalCommitLimit;
/* Has the allocation fragment been setup? */
if (!MmAllocationFragment)
{
/* Use the default value */
MmAllocationFragment = MI_ALLOCATION_FRAGMENT;
if (PageCount < ((256 * _1MB) / PAGE_SIZE))
{
/* On memory systems with less than 256MB, divide by 4 */
MmAllocationFragment = MI_ALLOCATION_FRAGMENT / 4;
}
else if (PageCount < (_1GB / PAGE_SIZE))
{
/* On systems with less than 1GB, divide by 2 */
MmAllocationFragment = MI_ALLOCATION_FRAGMENT / 2;
}
}
else
{
/* Convert from 1KB fragments to pages */
MmAllocationFragment *= _1KB;
MmAllocationFragment = ROUND_TO_PAGES(MmAllocationFragment);
/* Don't let it past the maximum */
MmAllocationFragment = min(MmAllocationFragment,
MI_MAX_ALLOCATION_FRAGMENT);
/* Don't let it too small either */
MmAllocationFragment = max(MmAllocationFragment,
MI_MIN_ALLOCATION_FRAGMENT);
}
/* Initialize the platform-specific parts */
MiInitMachineDependent(LoaderBlock);
@ -1889,12 +1947,12 @@ MmArmInitSystem(IN ULONG Phase,
/* FIXME: Call out into Driver Verifier for initialization */
/* Check how many pages the system has */
if (MmNumberOfPhysicalPages <= (13 * _1MB))
if (MmNumberOfPhysicalPages <= ((13 * _1MB) / PAGE_SIZE))
{
/* Set small system */
MmSystemSize = MmSmallSystem;
}
else if (MmNumberOfPhysicalPages <= (19 * _1MB))
else if (MmNumberOfPhysicalPages <= ((19 * _1MB) / PAGE_SIZE))
{
/* Set small system and add 100 pages for the cache */
MmSystemSize = MmSmallSystem;
@ -1986,6 +2044,14 @@ MmArmInitSystem(IN ULONG Phase,
return FALSE;
}
/* Initialize the system cache */
//MiInitializeSystemCache(MmSystemCacheWsMinimum, MmAvailablePages);
/* Update the commit limit */
MmTotalCommitLimit = MmAvailablePages;
if (MmTotalCommitLimit > 1024) MmTotalCommitLimit -= 1024;
MmTotalCommitLimitMaximum = MmTotalCommitLimit;
/* Size up paged pool and build the shadow system page directory */
MiBuildPagedPool();

View file

@ -297,7 +297,9 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
PMMPDE PointerPde;
MMPTE TempPte;
PETHREAD CurrentThread;
PEPROCESS CurrentProcess;
NTSTATUS Status;
PMMSUPPORT WorkingSet;
DPRINT("ARM3 FAULT AT: %p\n", Address);
//
@ -392,7 +394,7 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
//
// Check for a fault on the page table or hyperspace itself
//
if ((Address >= (PVOID)PTE_BASE) && (Address <= MmHyperSpaceEnd))
if (MI_IS_PAGE_TABLE_OR_HYPER_ADDRESS(Address))
{
//
// This might happen...not sure yet
@ -415,17 +417,13 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
return STATUS_ACCESS_VIOLATION;
}
//
// Now we must raise to APC_LEVEL and mark the thread as owner
// We don't actually implement a working set pushlock, so this is only
// for internal consistency (and blocking APCs)
//
KeRaiseIrql(APC_LEVEL, &LockIrql);
/* In this path, we are using the system working set */
CurrentThread = PsGetCurrentThread();
KeEnterGuardedRegion();
ASSERT((CurrentThread->OwnsSystemWorkingSetExclusive == 0) &&
(CurrentThread->OwnsSystemWorkingSetShared == 0));
CurrentThread->OwnsSystemWorkingSetExclusive = 1;
WorkingSet = &MmSystemCacheWs;
/* Acquire it */
KeRaiseIrql(APC_LEVEL, &LockIrql);
MiLockWorkingSet(CurrentThread, WorkingSet);
//
// Re-read PTE now that the IRQL has been raised
@ -445,6 +443,10 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
return STATUS_ACCESS_VIOLATION;
}
/* Release the working set */
MiUnlockWorkingSet(CurrentThread, WorkingSet);
KeLowerIrql(LockIrql);
//
// Otherwise, the PDE was probably invalid, and all is good now
//
@ -472,13 +474,10 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
NULL,
TrapInformation,
NULL);
//
// Re-enable APCs
//
/* Release the working set */
ASSERT(KeAreAllApcsDisabled() == TRUE);
CurrentThread->OwnsSystemWorkingSetExclusive = 0;
KeLeaveGuardedRegion();
MiUnlockWorkingSet(CurrentThread, WorkingSet);
KeLowerIrql(LockIrql);
//
@ -488,9 +487,17 @@ MmArmAccessFault(IN BOOLEAN StoreInstruction,
return Status;
}
//
// DIE DIE DIE
//
/* This is a user fault */
CurrentThread = PsGetCurrentThread();
CurrentProcess = PsGetCurrentProcess();
/* Lock the working set */
MiLockProcessWorkingSet(CurrentProcess, CurrentThread);
/* Do something */
/* Release the working set */
MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
DPRINT1("WARNING: USER MODE FAULT IN ARM3???\n");
return STATUS_ACCESS_VIOLATION;
}

View file

@ -2230,7 +2230,7 @@ MiSetPagingOfDriver(IN PMMPTE PointerPte,
IN PMMPTE LastPte)
{
PVOID ImageBase;
PETHREAD CurrentThread;
PETHREAD CurrentThread = PsGetCurrentThread();
PFN_NUMBER PageCount = 0, PageFrameIndex;
PMMPFN Pfn1;
PAGED_CODE();
@ -2242,12 +2242,8 @@ MiSetPagingOfDriver(IN PMMPTE PointerPte,
/* If this is a large page, it's stuck in physical memory */
if (MI_IS_PHYSICAL_ADDRESS(ImageBase)) return;
/* We should lock the system working set -- we don't have one yet, so just be consistent */
CurrentThread = PsGetCurrentThread();
KeEnterGuardedRegion();
ASSERT((CurrentThread->OwnsSystemWorkingSetExclusive == 0) &&
(CurrentThread->OwnsSystemWorkingSetShared == 0));
CurrentThread->OwnsSystemWorkingSetExclusive = 1;
/* Lock the working set */
MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);
/* Loop the PTEs */
while (PointerPte <= LastPte)
@ -2267,10 +2263,8 @@ MiSetPagingOfDriver(IN PMMPTE PointerPte,
PointerPte++;
}
/* Release the working set "lock" */
ASSERT(KeAreAllApcsDisabled() == TRUE);
CurrentThread->OwnsSystemWorkingSetExclusive = 0;
KeLeaveGuardedRegion();
/* Release the working set */
MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);
/* Do we have any driver pages? */
if (PageCount)

View file

@ -37,23 +37,14 @@ MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
OUT PPFN_NUMBER ValidPages)
{
PFN_NUMBER ActualPages = 0;
PETHREAD CurrentThread;
PETHREAD CurrentThread = PsGetCurrentThread();
PMMPFN Pfn1, Pfn2;
PFN_NUMBER PageFrameIndex, PageTableIndex;
KIRQL OldIrql, LockIrql;
KIRQL OldIrql;
ASSERT(KeGetCurrentIrql() <= APC_LEVEL);
/*
* Now we must raise to APC_LEVEL and mark the thread as owner
* We don't actually implement a working set pushlock, so this is only
* for internal consistency (and blocking APCs)
*/
KeRaiseIrql(APC_LEVEL, &LockIrql);
CurrentThread = PsGetCurrentThread();
KeEnterGuardedRegion();
ASSERT((CurrentThread->OwnsSystemWorkingSetExclusive == 0) &&
(CurrentThread->OwnsSystemWorkingSetShared == 0));
CurrentThread->OwnsSystemWorkingSetExclusive = 1;
/* Lock the system working set */
MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);
/* Loop all pages */
while (PageCount)
@ -124,11 +115,8 @@ MiDeleteSystemPageableVm(IN PMMPTE PointerPte,
PageCount--;
}
/* Re-enable APCs */
ASSERT(KeAreAllApcsDisabled() == TRUE);
CurrentThread->OwnsSystemWorkingSetExclusive = 0;
KeLeaveGuardedRegion();
KeLowerIrql(LockIrql);
/* Release the working set */
MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);
/* Flush the entire TLB */
KeFlushEntireTb(TRUE, TRUE);