diff --git a/reactos/ntoskrnl/mm/ARM3/miarm.h b/reactos/ntoskrnl/mm/ARM3/miarm.h index e3e889951e2..23271256c91 100644 --- a/reactos/ntoskrnl/mm/ARM3/miarm.h +++ b/reactos/ntoskrnl/mm/ARM3/miarm.h @@ -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( diff --git a/reactos/ntoskrnl/mm/ARM3/mminit.c b/reactos/ntoskrnl/mm/ARM3/mminit.c index 5b90e2e02a7..f595ece9a0f 100644 --- a/reactos/ntoskrnl/mm/ARM3/mminit.c +++ b/reactos/ntoskrnl/mm/ARM3/mminit.c @@ -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(); diff --git a/reactos/ntoskrnl/mm/ARM3/pagfault.c b/reactos/ntoskrnl/mm/ARM3/pagfault.c index eeb82a7712f..f4e00396ec3 100644 --- a/reactos/ntoskrnl/mm/ARM3/pagfault.c +++ b/reactos/ntoskrnl/mm/ARM3/pagfault.c @@ -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; } diff --git a/reactos/ntoskrnl/mm/ARM3/sysldr.c b/reactos/ntoskrnl/mm/ARM3/sysldr.c index 7706a9f16e3..b9fce2d56e9 100644 --- a/reactos/ntoskrnl/mm/ARM3/sysldr.c +++ b/reactos/ntoskrnl/mm/ARM3/sysldr.c @@ -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) diff --git a/reactos/ntoskrnl/mm/ARM3/virtual.c b/reactos/ntoskrnl/mm/ARM3/virtual.c index fd7f0e176be..c878c1542d4 100644 --- a/reactos/ntoskrnl/mm/ARM3/virtual.c +++ b/reactos/ntoskrnl/mm/ARM3/virtual.c @@ -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);