ULONG MmSizeOfNonPagedPoolInBytes;
ULONG MmMaximumNonPagedPoolInBytes;
+/* Some of the same values, in pages */
+PFN_NUMBER MmMaximumNonPagedPoolInPages;
+
//
// These numbers describe the discrete equation components of the nonpaged
// pool sizing algorithm.
C_ASSERT(FreePageList == 1);
PMMCOLOR_TABLES MmFreePagesByColor[FreePageList + 1];
+/* An event used in Phase 0 before the rest of the system is ready to go */
+KEVENT MiTempEvent;
+
+/* All the events used for memory threshold notifications */
+PKEVENT MiLowMemoryEvent;
+PKEVENT MiHighMemoryEvent;
+PKEVENT MiLowPagedPoolEvent;
+PKEVENT MiHighPagedPoolEvent;
+PKEVENT MiLowNonPagedPoolEvent;
+PKEVENT MiHighNonPagedPoolEvent;
+
+/* The actual thresholds themselves, in page numbers */
+PFN_NUMBER MmLowMemoryThreshold;
+PFN_NUMBER MmHighMemoryThreshold;
+PFN_NUMBER MiLowPagedPoolThreshold;
+PFN_NUMBER MiHighPagedPoolThreshold;
+PFN_NUMBER MiLowNonPagedPoolThreshold;
+PFN_NUMBER MiHighNonPagedPoolThreshold;
+
+/*
+ * This number determines how many free pages must exist, at minimum, until we
+ * start trimming working sets and flushing modified pages to obtain more free
+ * pages.
+ *
+ * This number changes if the system detects that this is a server product
+ */
+PFN_NUMBER MmMinimumFreePages = 26;
+
+/*
+ * This number indicates how many pages we consider to be a low limit of having
+ * "plenty" of free memory.
+ *
+ * It is doubled on systems that have more than 63MB of memory
+ */
+PFN_NUMBER MmPlentyFreePages = 400;
+
+/* These values store the type of system this is (small, med, large) and if server */
+ULONG MmProductType;
+MM_SYSTEMSIZE MmSystemSize;
+
/* PRIVATE FUNCTIONS **********************************************************/
//
MiBuildPfnDatabaseSelf();
}
+VOID
+NTAPI
+MiAdjustWorkingSetManagerParameters(IN BOOLEAN Client)
+{
+ /* This function needs to do more work, for now, we tune page minimums */
+
+ /* Check for a system with around 64MB RAM or more */
+ if (MmNumberOfPhysicalPages >= (63 * _1MB) / PAGE_SIZE)
+ {
+ /* Double the minimum amount of pages we consider for a "plenty free" scenario */
+ MmPlentyFreePages *= 2;
+ }
+}
+
+VOID
+NTAPI
+MiNotifyMemoryEvents(VOID)
+{
+ /* Are we in a low-memory situation? */
+ if (MmAvailablePages < MmLowMemoryThreshold)
+ {
+ /* Clear high, set low */
+ if (KeReadStateEvent(MiHighMemoryEvent)) KeClearEvent(MiHighMemoryEvent);
+ if (!KeReadStateEvent(MiLowMemoryEvent)) KeSetEvent(MiLowMemoryEvent, 0, FALSE);
+ }
+ else if (MmAvailablePages < MmHighMemoryThreshold)
+ {
+ /* We are in between, clear both */
+ if (KeReadStateEvent(MiHighMemoryEvent)) KeClearEvent(MiHighMemoryEvent);
+ if (KeReadStateEvent(MiLowMemoryEvent)) KeClearEvent(MiLowMemoryEvent);
+ }
+ else
+ {
+ /* Clear low, set high */
+ if (KeReadStateEvent(MiLowMemoryEvent)) KeClearEvent(MiLowMemoryEvent);
+ if (!KeReadStateEvent(MiHighMemoryEvent)) KeSetEvent(MiHighMemoryEvent, 0, FALSE);
+ }
+}
+
+NTSTATUS
+NTAPI
+MiCreateMemoryEvent(IN PUNICODE_STRING Name,
+ OUT PKEVENT *Event)
+{
+ PACL Dacl;
+ HANDLE EventHandle;
+ ULONG DaclLength;
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ SECURITY_DESCRIPTOR SecurityDescriptor;
+
+ /* Create the SD */
+ Status = RtlCreateSecurityDescriptor(&SecurityDescriptor,
+ SECURITY_DESCRIPTOR_REVISION);
+ if (!NT_SUCCESS(Status)) return Status;
+
+ /* One ACL with 3 ACEs, containing each one SID */
+ DaclLength = sizeof(ACL) +
+ 3 * sizeof(ACCESS_ALLOWED_ACE) +
+ RtlLengthSid(SeLocalSystemSid) +
+ RtlLengthSid(SeAliasAdminsSid) +
+ RtlLengthSid(SeWorldSid);
+
+ /* Allocate space for the DACL */
+ Dacl = ExAllocatePoolWithTag(PagedPool, DaclLength, 'lcaD');
+ if (!Dacl) return STATUS_INSUFFICIENT_RESOURCES;
+
+ /* Setup the ACL inside it */
+ Status = RtlCreateAcl(Dacl, DaclLength, ACL_REVISION);
+ if (!NT_SUCCESS(Status)) goto CleanUp;
+
+ /* Add query rights for everyone */
+ Status = RtlAddAccessAllowedAce(Dacl,
+ ACL_REVISION,
+ SYNCHRONIZE | EVENT_QUERY_STATE | READ_CONTROL,
+ SeWorldSid);
+ if (!NT_SUCCESS(Status)) goto CleanUp;
+
+ /* Full rights for the admin */
+ Status = RtlAddAccessAllowedAce(Dacl,
+ ACL_REVISION,
+ EVENT_ALL_ACCESS,
+ SeAliasAdminsSid);
+ if (!NT_SUCCESS(Status)) goto CleanUp;
+
+ /* As well as full rights for the system */
+ Status = RtlAddAccessAllowedAce(Dacl,
+ ACL_REVISION,
+ EVENT_ALL_ACCESS,
+ SeLocalSystemSid);
+ if (!NT_SUCCESS(Status)) goto CleanUp;
+
+ /* Set this DACL inside the SD */
+ Status = RtlSetDaclSecurityDescriptor(&SecurityDescriptor,
+ TRUE,
+ Dacl,
+ FALSE);
+ if (!NT_SUCCESS(Status)) goto CleanUp;
+
+ /* Setup the event attributes, making sure it's a permanent one */
+ InitializeObjectAttributes(&ObjectAttributes,
+ Name,
+ OBJ_KERNEL_HANDLE | OBJ_PERMANENT,
+ NULL,
+ &SecurityDescriptor);
+
+ /* Create the event */
+ Status = ZwCreateEvent(&EventHandle,
+ EVENT_ALL_ACCESS,
+ &ObjectAttributes,
+ NotificationEvent,
+ FALSE);
+CleanUp:
+ /* Free the DACL */
+ ExFreePool(Dacl);
+
+ /* Check if this is the success path */
+ if (NT_SUCCESS(Status))
+ {
+ /* Add a reference to the object, then close the handle we had */
+ Status = ObReferenceObjectByHandle(EventHandle,
+ EVENT_MODIFY_STATE,
+ ExEventObjectType,
+ KernelMode,
+ (PVOID*)Event,
+ NULL);
+ ZwClose (EventHandle);
+ }
+
+ /* Return status */
+ return Status;
+}
+
+BOOLEAN
+NTAPI
+MiInitializeMemoryEvents(VOID)
+{
+ UNICODE_STRING LowString = RTL_CONSTANT_STRING(L"\\KernelObjects\\LowMemoryCondition");
+ UNICODE_STRING HighString = RTL_CONSTANT_STRING(L"\\KernelObjects\\HighMemoryCondition");
+ UNICODE_STRING LowPagedPoolString = RTL_CONSTANT_STRING(L"\\KernelObjects\\LowPagedPoolCondition");
+ UNICODE_STRING HighPagedPoolString = RTL_CONSTANT_STRING(L"\\KernelObjects\\HighPagedPoolCondition");
+ UNICODE_STRING LowNonPagedPoolString = RTL_CONSTANT_STRING(L"\\KernelObjects\\LowNonPagedPoolCondition");
+ UNICODE_STRING HighNonPagedPoolString = RTL_CONSTANT_STRING(L"\\KernelObjects\\HighNonPagedPoolCondition");
+ NTSTATUS Status;
+
+ /* Check if we have a registry setting */
+ if (MmLowMemoryThreshold)
+ {
+ /* Convert it to pages */
+ MmLowMemoryThreshold *= (_1MB / PAGE_SIZE);
+ }
+ else
+ {
+ /* The low memory threshold is hit when we don't consider that we have "plenty" of free pages anymore */
+ MmLowMemoryThreshold = MmPlentyFreePages;
+
+ /* More than one GB of memory? */
+ if (MmNumberOfPhysicalPages > 0x40000)
+ {
+ /* Start at 32MB, and add another 16MB for each GB */
+ MmLowMemoryThreshold = (32 * _1MB) / PAGE_SIZE;
+ MmLowMemoryThreshold += ((MmNumberOfPhysicalPages - 0x40000) >> 7);
+ }
+ else if (MmNumberOfPhysicalPages > 0x8000)
+ {
+ /* For systems with > 128MB RAM, add another 4MB for each 128MB */
+ MmLowMemoryThreshold += ((MmNumberOfPhysicalPages - 0x8000) >> 5);
+ }
+
+ /* Don't let the minimum threshold go past 64MB */
+ MmLowMemoryThreshold = min(MmLowMemoryThreshold, (64 * _1MB) / PAGE_SIZE);
+ }
+
+ /* Check if we have a registry setting */
+ if (MmHighMemoryThreshold)
+ {
+ /* Convert it into pages */
+ MmHighMemoryThreshold *= (_1MB / PAGE_SIZE);
+ }
+ else
+ {
+ /* Otherwise, the default is three times the low memory threshold */
+ MmHighMemoryThreshold = 3 * MmLowMemoryThreshold;
+ ASSERT(MmHighMemoryThreshold > MmLowMemoryThreshold);
+ }
+
+ /* Make sure high threshold is actually higher than the low */
+ MmHighMemoryThreshold = max(MmHighMemoryThreshold, MmLowMemoryThreshold);
+
+ /* Create the memory events for all the thresholds */
+ Status = MiCreateMemoryEvent(&LowString, &MiLowMemoryEvent);
+ if (!NT_SUCCESS(Status)) return FALSE;
+ Status = MiCreateMemoryEvent(&HighString, &MiHighMemoryEvent);
+ if (!NT_SUCCESS(Status)) return FALSE;
+ Status = MiCreateMemoryEvent(&LowPagedPoolString, &MiLowPagedPoolEvent);
+ if (!NT_SUCCESS(Status)) return FALSE;
+ Status = MiCreateMemoryEvent(&HighPagedPoolString, &MiHighPagedPoolEvent);
+ if (!NT_SUCCESS(Status)) return FALSE;
+ Status = MiCreateMemoryEvent(&LowNonPagedPoolString, &MiLowNonPagedPoolEvent);
+ if (!NT_SUCCESS(Status)) return FALSE;
+ Status = MiCreateMemoryEvent(&HighNonPagedPoolString, &MiHighNonPagedPoolEvent);
+ if (!NT_SUCCESS(Status)) return FALSE;
+
+ /* Now setup the pool events */
+ MiInitializePoolEvents();
+
+ /* Set the initial event state */
+ MiNotifyMemoryEvents();
+ return TRUE;
+}
+
VOID
NTAPI
MmDumpArmPfnDatabase(VOID)
//
InitializePool(PagedPool, 0);
- //
- // Initialize the paged pool mutex
- //
- KeInitializeGuardedMutex(&MmPagedPoolMutex);
+ /* Default low threshold of 30MB or one fifth of paged pool */
+ MiLowPagedPoolThreshold = (30 * _1MB) >> PAGE_SHIFT;
+ MiLowPagedPoolThreshold = min(MiLowPagedPoolThreshold, Size / 5);
+
+ /* Default high threshold of 60MB or 25% */
+ MiHighPagedPoolThreshold = (60 * _1MB) >> PAGE_SHIFT;
+ MiHighPagedPoolThreshold = min(MiHighPagedPoolThreshold, (Size * 2) / 5);
+ ASSERT(MiLowPagedPoolThreshold < MiHighPagedPoolThreshold);
}
NTSTATUS
IncludeType[LoaderBBTMemory] = FALSE;
if (Phase == 0)
{
+ /* Initialize the phase 0 temporary event */
+ KeInitializeEvent(&MiTempEvent, NotificationEvent, FALSE);
+
+ /* Set all the events to use the temporary event for now */
+ MiLowMemoryEvent = &MiTempEvent;
+ MiHighMemoryEvent = &MiTempEvent;
+ MiLowPagedPoolEvent = &MiTempEvent;
+ MiHighPagedPoolEvent = &MiTempEvent;
+ MiLowNonPagedPoolEvent = &MiTempEvent;
+ MiHighNonPagedPoolEvent = &MiTempEvent;
+
//
// Define the basic user vs. kernel address space separation
//
MiSystemViewStart = (PVOID)((ULONG_PTR)MmSessionBase -
MmSystemViewSize);
+
+ /* Initialize the user mode image list */
+ InitializeListHead(&MmLoadedUserImageList);
+
+ /* Initialize the paged pool mutex */
+ KeInitializeGuardedMutex(&MmPagedPoolMutex);
+
+ /* Initialize the Loader Lock */
+ KeInitializeMutant(&MmSystemLoadLock, FALSE);
+
//
// Count physical pages on the system
//
// Size up paged pool and build the shadow system page directory
//
MiBuildPagedPool();
+
+ /* Check how many pages the system has */
+ if (MmNumberOfPhysicalPages <= (13 * _1MB))
+ {
+ /* Set small system */
+ MmSystemSize = MmSmallSystem;
+ }
+ else if (MmNumberOfPhysicalPages <= (19 * _1MB))
+ {
+ /* Set small system */
+ MmSystemSize = MmSmallSystem;
+ }
+ else
+ {
+ /* Set medium system */
+ MmSystemSize = MmMediumSystem;
+ }
+
+ /* Check for more than 32MB */
+ if (MmNumberOfPhysicalPages >= ((32 * _1MB) / PAGE_SIZE))
+ {
+ /* Check for product type being "Wi" for WinNT */
+ if (MmProductType == '\0i\0W')
+ {
+ /* Then this is a large system */
+ MmSystemSize = MmLargeSystem;
+ }
+ else
+ {
+ /* For servers, we need 64MB to consider this as being large */
+ if (MmNumberOfPhysicalPages >= ((64 * _1MB) / PAGE_SIZE))
+ {
+ /* Set it as large */
+ MmSystemSize = MmLargeSystem;
+ }
+ }
+ }
+
+ /* Now setup the shared user data fields */
+ ASSERT(SharedUserData->NumberOfPhysicalPages == 0);
+ SharedUserData->NumberOfPhysicalPages = MmNumberOfPhysicalPages;
+ SharedUserData->LargePageMinimum = 0;
+
+ /* Check for workstation (Wi for WinNT) */
+ if (MmProductType == '\0i\0W')
+ {
+ /* Set Windows NT Workstation product type */
+ SharedUserData->NtProductType = NtProductWinNt;
+ MmProductType = 0;
+ }
+ else
+ {
+ /* Check for LanMan server */
+ if (MmProductType == '\0a\0L')
+ {
+ /* This is a domain controller */
+ SharedUserData->NtProductType = NtProductLanManNt;
+ }
+ else
+ {
+ /* Otherwise it must be a normal server */
+ SharedUserData->NtProductType = NtProductServer;
+ }
+
+ /* Set the product type, and make the system more aggressive with low memory */
+ MmProductType = 1;
+ MmMinimumFreePages = 81;
+ }
+
+ /* Update working set tuning parameters */
+ MiAdjustWorkingSetManagerParameters(!MmProductType);
}
//
/* GLOBALS ********************************************************************/
-MMPFNLIST MmZeroedPageListHead;
-MMPFNLIST MmFreePageListHead;
-MMPFNLIST MmStandbyPageListHead;
-MMPFNLIST MmModifiedPageListHead;
-MMPFNLIST MmModifiedNoWritePageListHead;
+MMPFNLIST MmZeroedPageListHead = {0, ZeroedPageList, LIST_HEAD, LIST_HEAD};
+MMPFNLIST MmFreePageListHead = {0, FreePageList, LIST_HEAD, LIST_HEAD};
+MMPFNLIST MmStandbyPageListHead = {0, StandbyPageList, LIST_HEAD, LIST_HEAD};
+MMPFNLIST MmModifiedPageListHead = {0, ModifiedPageList, LIST_HEAD, LIST_HEAD};
+MMPFNLIST MmModifiedNoWritePageListHead = {0, ModifiedNoWritePageList, LIST_HEAD, LIST_HEAD};
+MMPFNLIST MmBadPageListHead = {0, BadPageList, LIST_HEAD, LIST_HEAD};
+MMPFNLIST MmRomPageListHead = {0, StandbyPageList, LIST_HEAD, LIST_HEAD};
+PMMPFNLIST MmPageLocationList[] =
+{
+ &MmZeroedPageListHead,
+ &MmFreePageListHead,
+ &MmStandbyPageListHead,
+ &MmModifiedPageListHead,
+ &MmModifiedNoWritePageListHead,
+ &MmBadPageListHead,
+ NULL,
+ NULL
+};
/* FUNCTIONS ******************************************************************/
VOID
VOID
NTAPI
-MiRemoveFromList(IN PMMPFN Entry)
+MiUnlinkFreeOrZeroedPage(IN PMMPFN Entry)
{
PFN_NUMBER OldFlink, OldBlink;
PMMPFNLIST ListHead;
+ MMLISTS ListName;
- /* Find the list for this */
- if (Entry->u3.e1.PageLocation == ZeroedPageList)
- {
- ListHead = &MmZeroedPageListHead;
- }
- else if (Entry->u3.e1.PageLocation == FreePageList)
- {
- ListHead = &MmFreePageListHead;
- }
- else
- {
- ListHead = NULL;
- ASSERT(ListHead != NULL);
- }
+ /* Make sure the PFN lock is held */
+ ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
+
+ /* Make sure the PFN entry isn't in-use */
+ ASSERT(Entry->u3.e1.WriteInProgress == 0);
+ ASSERT(Entry->u3.e1.ReadInProgress == 0);
+
+ /* Find the list for this entry, make sure it's the free or zero list */
+ ListHead = MmPageLocationList[Entry->u3.e1.PageLocation];
+ ListName = ListHead->ListName;
+ ASSERT(ListHead != NULL);
+ ASSERT(ListName <= FreePageList);
+
+ /* Remove one count */
+ ASSERT(ListHead->Total != 0);
+ ListHead->Total--;
/* Get the forward and back pointers */
OldFlink = Entry->u1.Flink;
}
/* We are not on a list anymore */
- ListHead->Total--;
Entry->u1.Flink = Entry->u2.Blink = 0;
+
+ /* FIXME: Deal with color list */
+
+ /* See if we hit any thresholds */
+ if (MmAvailablePages == MmHighMemoryThreshold)
+ {
+ /* Clear the high memory event */
+ KeClearEvent(MiHighMemoryEvent);
+ }
+ else if (MmAvailablePages == MmLowMemoryThreshold)
+ {
+ /* Signal the low memory event */
+ KeSetEvent(MiLowMemoryEvent, 0, FALSE);
+ }
+
+ /* One less page */
+ if (--MmAvailablePages < MmMinimumFreePages)
+ {
+ /* FIXME: Should wake up the MPW and working set manager, if we had one */
+ }
}
PMMPFN
VOID
NTAPI
-MiInitializeArmPool(VOID)
+MiInitializeNonPagedPoolThresholds(VOID)
+{
+ PFN_NUMBER Size = MmMaximumNonPagedPoolInPages;
+
+ /* Default low threshold of 8MB or one third of nonpaged pool */
+ MiLowNonPagedPoolThreshold = (8 * _1MB) >> PAGE_SHIFT;
+ MiLowNonPagedPoolThreshold = min(MiLowNonPagedPoolThreshold, Size / 3);
+
+ /* Default high threshold of 20MB or 50% */
+ MiHighNonPagedPoolThreshold = (20 * _1MB) >> PAGE_SHIFT;
+ MiHighNonPagedPoolThreshold = min(MiHighNonPagedPoolThreshold, Size / 2);
+ ASSERT(MiLowNonPagedPoolThreshold < MiHighNonPagedPoolThreshold);
+}
+
+VOID
+NTAPI
+MiInitializePoolEvents(VOID)
+{
+ KIRQL OldIrql;
+ PFN_NUMBER FreePoolInPages;
+
+ /* Lock paged pool */
+ KeAcquireGuardedMutex(&MmPagedPoolMutex);
+
+ /* Total size of the paged pool minus the allocated size, is free */
+ FreePoolInPages = MmSizeOfPagedPoolInPages - MmPagedPoolInfo.AllocatedPagedPool;
+
+ /* Check the initial state high state */
+ if (FreePoolInPages >= MiHighPagedPoolThreshold)
+ {
+ /* We have plenty of pool */
+ KeSetEvent(MiHighPagedPoolEvent, 0, FALSE);
+ }
+ else
+ {
+ /* We don't */
+ KeClearEvent(MiHighPagedPoolEvent);
+ }
+
+ /* Check the initial low state */
+ if (FreePoolInPages <= MiLowPagedPoolThreshold)
+ {
+ /* We're very low in free pool memory */
+ KeSetEvent(MiLowPagedPoolEvent, 0, FALSE);
+ }
+ else
+ {
+ /* We're not */
+ KeClearEvent(MiLowPagedPoolEvent);
+ }
+
+ /* Release the paged pool lock */
+ KeReleaseGuardedMutex(&MmPagedPoolMutex);
+
+ /* Now it's time for the nonpaged pool lock */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueueMmNonPagedPoolLock);
+
+ /* Free pages are the maximum minus what's been allocated */
+ FreePoolInPages = MmMaximumNonPagedPoolInPages - MmAllocatedNonPagedPool;
+
+ /* Check if we have plenty */
+ if (FreePoolInPages >= MiHighNonPagedPoolThreshold)
+ {
+ /* We do, set the event */
+ KeSetEvent(MiHighNonPagedPoolEvent, 0, FALSE);
+ }
+ else
+ {
+ /* We don't, clear the event */
+ KeClearEvent(MiHighNonPagedPoolEvent);
+ }
+
+ /* Check if we have very little */
+ if (FreePoolInPages <= MiLowNonPagedPoolThreshold)
+ {
+ /* We do, set the event */
+ KeSetEvent(MiLowNonPagedPoolEvent, 0, FALSE);
+ }
+ else
+ {
+ /* We don't, clear it */
+ KeClearEvent(MiLowNonPagedPoolEvent);
+ }
+
+ /* We're done, release the nonpaged pool lock */
+ KeReleaseQueuedSpinLock(LockQueueMmNonPagedPoolLock, OldIrql);
+}
+
+VOID
+NTAPI
+MiInitializeNonPagedPool(VOID)
{
ULONG i;
PFN_NUMBER PoolPages;