https://git.reactos.org/?p=reactos.git;a=commitdiff;h=41475dfcd71275f2c704c…
commit 41475dfcd71275f2c704cc3e8cfbcb8bf35aa242
Author: Jérôme Gardou <jerome.gardou(a)reactos.org>
AuthorDate: Thu Jan 28 15:42:12 2021 +0100
Commit: Jérôme Gardou <jerome.gardou(a)reactos.org>
CommitDate: Wed Feb 3 09:41:23 2021 +0100
[NTOS:CC] Performance improvements
Do not ditch the pages as soon as the section are unmapped
Improve MmBalancer "algorithm" (or whatever you call that)
Various needed fixes to get this going.
---
ntoskrnl/cache/section/newmm.h | 2 -
ntoskrnl/include/internal/mm.h | 10 +++
ntoskrnl/mm/balance.c | 197 +++++++++++++++++++++--------------------
ntoskrnl/mm/freelist.c | 9 +-
ntoskrnl/mm/i386/page.c | 40 +++++++++
ntoskrnl/mm/rmap.c | 111 ++++++++++++++---------
ntoskrnl/mm/section.c | 137 +++++++++++++++++-----------
7 files changed, 309 insertions(+), 197 deletions(-)
diff --git a/ntoskrnl/cache/section/newmm.h b/ntoskrnl/cache/section/newmm.h
index 3fb731c6ace..2efcd9cf7d5 100644
--- a/ntoskrnl/cache/section/newmm.h
+++ b/ntoskrnl/cache/section/newmm.h
@@ -5,8 +5,6 @@
/* TYPES *********************************************************************/
#define MM_SEGMENT_FINALIZE (0x40000000)
-#define RMAP_SEGMENT_MASK ~((ULONG_PTR)0xff)
-#define RMAP_IS_SEGMENT(x) (((ULONG_PTR)(x) & RMAP_SEGMENT_MASK) ==
RMAP_SEGMENT_MASK)
#define MIN(x,y) (((x)<(y))?(x):(y))
#define MAX(x,y) (((x)>(y))?(x):(y))
diff --git a/ntoskrnl/include/internal/mm.h b/ntoskrnl/include/internal/mm.h
index 86ed4dfb23a..0243ac55177 100644
--- a/ntoskrnl/include/internal/mm.h
+++ b/ntoskrnl/include/internal/mm.h
@@ -830,6 +830,8 @@ NTAPI
MmRebalanceMemoryConsumers(VOID);
/* rmap.c **************************************************************/
+#define RMAP_SEGMENT_MASK ~((ULONG_PTR)0xff)
+#define RMAP_IS_SEGMENT(x) (((ULONG_PTR)(x) & RMAP_SEGMENT_MASK) ==
RMAP_SEGMENT_MASK)
VOID
NTAPI
@@ -1189,6 +1191,14 @@ MmIsDirtyPage(
PVOID Address
);
+VOID
+NTAPI
+MmClearPageAccessedBit(PEPROCESS Process, PVOID Address);
+
+BOOLEAN
+NTAPI
+MmIsPageAccessed(PEPROCESS Process, PVOID Address);
+
/* wset.c ********************************************************************/
NTSTATUS
diff --git a/ntoskrnl/mm/balance.c b/ntoskrnl/mm/balance.c
index f9067c58a13..04231e86951 100644
--- a/ntoskrnl/mm/balance.c
+++ b/ntoskrnl/mm/balance.c
@@ -113,14 +113,13 @@ MiTrimMemoryConsumer(ULONG Consumer, ULONG InitialTarget)
Target = (ULONG)max(Target, MiMinimumAvailablePages - MmAvailablePages);
}
- /* Don't be too greedy if we're not in a hurry */
- if (MmAvailablePages > MiMinimumAvailablePages)
- Target = min(Target, 256);
+ /* Don't be too greedy in one run */
+ Target = min(Target, 256);
if (Target)
{
/* Now swap the pages out */
- Status = MiMemoryConsumers[Consumer].Trim(Target, 0, &NrFreedPages);
+ Status = MiMemoryConsumers[Consumer].Trim(Target, MmAvailablePages <
MiMinimumAvailablePages, &NrFreedPages);
DPRINT("Trimming consumer %lu: Freed %lu pages with a target of %lu
pages\n", Consumer, NrFreedPages, Target);
@@ -142,15 +141,104 @@ MmTrimUserMemory(ULONG Target, ULONG Priority, PULONG
NrFreedPages)
(*NrFreedPages) = 0;
+ DPRINT1("MM BALANCER: %s\n", Priority ? "Paging out!" :
"Removing access bit!");
+
CurrentPage = MmGetLRUFirstUserPage();
while (CurrentPage != 0 && Target > 0)
{
- Status = MmPageOutPhysicalAddress(CurrentPage);
- if (NT_SUCCESS(Status))
+ if (Priority)
+ {
+ Status = MmPageOutPhysicalAddress(CurrentPage);
+ if (NT_SUCCESS(Status))
+ {
+ DPRINT("Succeeded\n");
+ Target--;
+ (*NrFreedPages)++;
+ }
+ }
+ else
{
- DPRINT("Succeeded\n");
+ /* When not paging-out agressively, just reset the accessed bit */
+ PEPROCESS Process = NULL;
+ PVOID Address = NULL;
+ BOOLEAN Accessed = FALSE;
+
+ /*
+ * We have a lock-ordering problem here. We cant lock the PFN DB before the
Process address space.
+ * So we must use circonvoluted loops.
+ * Well...
+ */
+ while (TRUE)
+ {
+ KAPC_STATE ApcState;
+ KIRQL OldIrql = MiAcquirePfnLock();
+ PMM_RMAP_ENTRY Entry = MmGetRmapListHeadPage(CurrentPage);
+ while (Entry)
+ {
+ if (RMAP_IS_SEGMENT(Entry->Address))
+ {
+ Entry = Entry->Next;
+ continue;
+ }
+
+ /* Check that we didn't treat this entry before */
+ if (Entry->Address < Address)
+ {
+ Entry = Entry->Next;
+ continue;
+ }
+
+ if ((Entry->Address == Address) && (Entry->Process
<= Process))
+ {
+ Entry = Entry->Next;
+ continue;
+ }
+
+ break;
+ }
+
+ if (!Entry)
+ {
+ MiReleasePfnLock(OldIrql);
+ break;
+ }
+
+ Process = Entry->Process;
+ Address = Entry->Address;
+
+ MiReleasePfnLock(OldIrql);
+
+ KeStackAttachProcess(&Process->Pcb, &ApcState);
+
+ MmLockAddressSpace(&Process->Vm);
+
+ /* Be sure this is still valid. */
+ PMMPTE Pte = MiAddressToPte(Address);
+ if (Pte->u.Hard.Valid)
+ {
+ Accessed = Accessed || Pte->u.Hard.Accessed;
+ Pte->u.Hard.Accessed = 0;
+
+ /* There is no need to invalidate, the balancer thread is never on a
user process */
+ //KeInvalidateTlbEntry(Address);
+ }
+
+ MmUnlockAddressSpace(&Process->Vm);
+
+ KeUnstackDetachProcess(&ApcState);
+ }
+
+ if (!Accessed)
+ {
+ /* Nobody accessed this page since the last time we check. Time to clean
up */
+
+ Status = MmPageOutPhysicalAddress(CurrentPage);
+ // DPRINT1("Paged-out one page: %s\n", NT_SUCCESS(Status) ?
"Yes" : "No");
+ (void)Status;
+ }
+
+ /* Done for this page. */
Target--;
- (*NrFreedPages)++;
}
CurrentPage = MmGetLRUNextUserPage(CurrentPage, TRUE);
@@ -189,78 +277,10 @@ NTAPI
MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait,
PPFN_NUMBER AllocatedPage)
{
- ULONG PagesUsed;
PFN_NUMBER Page;
- /*
- * Make sure we don't exceed our individual target.
- */
- PagesUsed = InterlockedIncrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
- if (PagesUsed > MiMemoryConsumers[Consumer].PagesTarget &&
- !MiIsBalancerThread())
- {
- MmRebalanceMemoryConsumers();
- }
-
- /*
- * Allocate always memory for the non paged pool and for the pager thread.
- */
- if (Consumer == MC_SYSTEM)
- {
- Page = MmAllocPage(Consumer);
- if (Page == 0)
- {
- KeBugCheck(NO_PAGES_AVAILABLE);
- }
- *AllocatedPage = Page;
- if (MmAvailablePages < MiMinimumAvailablePages)
- MmRebalanceMemoryConsumers();
- return(STATUS_SUCCESS);
- }
-
- /*
- * Make sure we don't exceed global targets.
- */
- if (((MmAvailablePages < MiMinimumAvailablePages) &&
!MiIsBalancerThread())
- || (MmAvailablePages < (MiMinimumAvailablePages / 2)))
- {
- MM_ALLOCATION_REQUEST Request;
-
- if (!CanWait)
- {
- (void)InterlockedDecrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
- MmRebalanceMemoryConsumers();
- return(STATUS_NO_MEMORY);
- }
-
- /* Insert an allocation request. */
- Request.Page = 0;
- KeInitializeEvent(&Request.Event, NotificationEvent, FALSE);
-
- ExInterlockedInsertTailList(&AllocationListHead, &Request.ListEntry,
&AllocationListLock);
- MmRebalanceMemoryConsumers();
-
- KeWaitForSingleObject(&Request.Event,
- 0,
- KernelMode,
- FALSE,
- NULL);
-
- Page = Request.Page;
- if (Page == 0)
- {
- KeBugCheck(NO_PAGES_AVAILABLE);
- }
-
- *AllocatedPage = Page;
-
- if (MmAvailablePages < MiMinimumAvailablePages)
- {
- MmRebalanceMemoryConsumers();
- }
-
- return(STATUS_SUCCESS);
- }
+ /* Update the target */
+ InterlockedIncrementUL(&MiMemoryConsumers[Consumer].PagesUsed);
/*
* Actually allocate the page.
@@ -272,11 +292,6 @@ MmRequestPageMemoryConsumer(ULONG Consumer, BOOLEAN CanWait,
}
*AllocatedPage = Page;
- if (MmAvailablePages < MiMinimumAvailablePages)
- {
- MmRebalanceMemoryConsumers();
- }
-
return(STATUS_SUCCESS);
}
@@ -407,22 +422,14 @@ MiInitBalancerThread(VOID)
{
KPRIORITY Priority;
NTSTATUS Status;
-#if !defined(__GNUC__)
-
- LARGE_INTEGER dummyJunkNeeded;
- dummyJunkNeeded.QuadPart = -20000000; /* 2 sec */
- ;
-#endif
-
+ LARGE_INTEGER Timeout;
KeInitializeEvent(&MiBalancerEvent, SynchronizationEvent, FALSE);
KeInitializeTimerEx(&MiBalancerTimer, SynchronizationTimer);
+
+ Timeout.QuadPart = -20000000; /* 2 sec */
KeSetTimerEx(&MiBalancerTimer,
-#if defined(__GNUC__)
- (LARGE_INTEGER)(LONGLONG)-20000000LL, /* 2 sec */
-#else
- dummyJunkNeeded,
-#endif
+ Timeout,
2000, /* 2 sec */
NULL);
diff --git a/ntoskrnl/mm/freelist.c b/ntoskrnl/mm/freelist.c
index 35c6336dcc6..4aa15a12582 100644
--- a/ntoskrnl/mm/freelist.c
+++ b/ntoskrnl/mm/freelist.c
@@ -139,7 +139,7 @@ MmGetLRUNextUserPage(PFN_NUMBER PreviousPage, BOOLEAN MoveToLast)
* If it's not, then it means it is still hanging in some process address space.
* This avoids paging-out e.g. ntdll early just because it's mapped first time.
*/
- if (MoveToLast)
+ if ((MoveToLast) && (MmGetReferenceCountPage(PreviousPage) > 1))
{
MmRemoveLRUUserPage(PreviousPage);
MmInsertLRULastUserPage(PreviousPage);
@@ -424,10 +424,11 @@ VOID
NTAPI
MmSetRmapListHeadPage(PFN_NUMBER Pfn, PMM_RMAP_ENTRY ListHead)
{
- KIRQL oldIrql;
PMMPFN Pfn1;
- oldIrql = MiAcquirePfnLock();
+ /* PFN database must be locked */
+ MI_ASSERT_PFN_LOCK_HELD();
+
Pfn1 = MiGetPfnEntry(Pfn);
ASSERT(Pfn1);
ASSERT_IS_ROS_PFN(Pfn1);
@@ -450,8 +451,6 @@ MmSetRmapListHeadPage(PFN_NUMBER Pfn, PMM_RMAP_ENTRY ListHead)
/* ReactOS semantics will now release the page, which will make it free and enter
a colored list */
}
-
- MiReleasePfnLock(oldIrql);
}
PMM_RMAP_ENTRY
diff --git a/ntoskrnl/mm/i386/page.c b/ntoskrnl/mm/i386/page.c
index bcc2be911b1..b541b5d4752 100644
--- a/ntoskrnl/mm/i386/page.c
+++ b/ntoskrnl/mm/i386/page.c
@@ -585,6 +585,46 @@ MmSetDirtyPage(PEPROCESS Process, PVOID Address)
}
}
+VOID
+NTAPI
+MmClearPageAccessedBit(PEPROCESS Process, PVOID Address)
+{
+ PULONG Pt;
+ LONG Pte;
+ KIRQL OldIrql;
+
+ if (Address < MmSystemRangeStart && Process == NULL)
+ {
+ DPRINT1("MmClearPageAccessedBit is called for user space without a
process.\n");
+ KeBugCheck(MEMORY_MANAGEMENT);
+ }
+
+ Pt = MmGetPageTableForProcess(Process, Address, FALSE, &OldIrql);
+ if (Pt == NULL)
+ {
+ KeBugCheck(MEMORY_MANAGEMENT);
+ }
+
+ do
+ {
+ Pte = *Pt;
+ } while (Pte != InterlockedCompareExchangePte(Pt, Pte & ~PA_ACCESSED, Pte));
+
+ if (!(Pte & PA_PRESENT))
+ {
+ KeBugCheck(MEMORY_MANAGEMENT);
+ }
+
+ MiFlushTlb(Pt, Address, OldIrql);
+}
+
+BOOLEAN
+NTAPI
+MmIsPageAccessed(PEPROCESS Process, PVOID Address)
+{
+ return BooleanFlagOn(MmGetPageEntryForProcess(Process, Address), PA_ACCESSED);
+}
+
BOOLEAN
NTAPI
MmIsPagePresent(PEPROCESS Process, PVOID Address)
diff --git a/ntoskrnl/mm/rmap.c b/ntoskrnl/mm/rmap.c
index 5b7ffe3a3da..f926afb8471 100644
--- a/ntoskrnl/mm/rmap.c
+++ b/ntoskrnl/mm/rmap.c
@@ -53,13 +53,14 @@ MmPageOutPhysicalAddress(PFN_NUMBER Page)
PMM_RMAP_ENTRY entry;
PMEMORY_AREA MemoryArea;
PMMSUPPORT AddressSpace;
- PVOID Address;
- PEPROCESS Process;
+ PVOID Address = NULL;
+ PEPROCESS Process = NULL;
NTSTATUS Status = STATUS_SUCCESS;
PMM_SECTION_SEGMENT Segment;
LARGE_INTEGER SegmentOffset;
KIRQL OldIrql;
+GetEntry:
OldIrql = MiAcquirePfnLock();
entry = MmGetRmapListHeadPage(Page);
@@ -67,6 +68,16 @@ MmPageOutPhysicalAddress(PFN_NUMBER Page)
while (entry && RMAP_IS_SEGMENT(entry->Address))
entry = entry->Next;
+ /* See if we are retrying because the page is actively used */
+ while (entry && ((entry->Address < Address) ||
RMAP_IS_SEGMENT(entry->Address)))
+ entry = entry->Next;
+
+ if (entry && (entry->Address == Address))
+ {
+ while (entry && ((entry->Process <= Process) ||
RMAP_IS_SEGMENT(entry->Address)))
+ entry = entry->Next;
+ }
+
if (entry == NULL)
{
MiReleasePfnLock(OldIrql);
@@ -81,41 +92,42 @@ MmPageOutPhysicalAddress(PFN_NUMBER Page)
KeBugCheck(MEMORY_MANAGEMENT);
}
- if (Address < MmSystemRangeStart)
- {
- if (!ExAcquireRundownProtection(&Process->RundownProtect))
- {
- MiReleasePfnLock(OldIrql);
- return STATUS_PROCESS_IS_TERMINATING;
- }
+ /* This is for user-mode address only */
+ ASSERT(Address < MmSystemRangeStart);
- Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL,
KernelMode);
+ if (!ExAcquireRundownProtection(&Process->RundownProtect))
+ {
MiReleasePfnLock(OldIrql);
- if (!NT_SUCCESS(Status))
- {
- ExReleaseRundownProtection(&Process->RundownProtect);
- return Status;
- }
- AddressSpace = &Process->Vm;
+ return STATUS_PROCESS_IS_TERMINATING;
}
- else
+
+ Status = ObReferenceObjectByPointer(Process, PROCESS_ALL_ACCESS, NULL, KernelMode);
+ MiReleasePfnLock(OldIrql);
+ if (!NT_SUCCESS(Status))
{
- MiReleasePfnLock(OldIrql);
- AddressSpace = MmGetKernelAddressSpace();
+ ExReleaseRundownProtection(&Process->RundownProtect);
+ return Status;
}
+ AddressSpace = &Process->Vm;
MmLockAddressSpace(AddressSpace);
+ if ((MmGetPfnForProcess(Process, Address) != Page) || MmIsPageAccessed(Process,
Address))
+ {
+ /* This changed in the short window where we didn't have any locks */
+ MmUnlockAddressSpace(AddressSpace);
+ ExReleaseRundownProtection(&Process->RundownProtect);
+ ObDereferenceObject(Process);
+ goto GetEntry;
+ }
+
MemoryArea = MmLocateMemoryAreaByAddress(AddressSpace, Address);
if (MemoryArea == NULL || MemoryArea->DeleteInProgress)
{
MmUnlockAddressSpace(AddressSpace);
- if (Address < MmSystemRangeStart)
- {
- ExReleaseRundownProtection(&Process->RundownProtect);
- ObDereferenceObject(Process);
- }
- return(STATUS_UNSUCCESSFUL);
+ ExReleaseRundownProtection(&Process->RundownProtect);
+ ObDereferenceObject(Process);
+ goto GetEntry;
}
if (MemoryArea->Type == MEMORY_AREA_SECTION_VIEW)
@@ -269,7 +281,7 @@ MmPageOutPhysicalAddress(PFN_NUMBER Page)
MmDeleteRmap(Page, Process, Address);
/* One less mapping referencing this segment */
- Released = MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset,
Dirty, FALSE, NULL);
+ Released = MmUnsharePageEntrySectionSegment(MemoryArea, Segment, &Offset,
Dirty, TRUE, NULL);
MmUnlockSectionSegment(Segment);
MmUnlockAddressSpace(AddressSpace);
@@ -396,11 +408,7 @@ MmInsertRmap(PFN_NUMBER Page, PEPROCESS Process,
new_entry->Address = Address;
new_entry->Process = (PEPROCESS)Process;
#if DBG
-#ifdef __GNUC__
- new_entry->Caller = __builtin_return_address(0);
-#else
new_entry->Caller = _ReturnAddress();
-#endif
#endif
if (
@@ -417,24 +425,39 @@ MmInsertRmap(PFN_NUMBER Page, PEPROCESS Process,
OldIrql = MiAcquirePfnLock();
current_entry = MmGetRmapListHeadPage(Page);
- new_entry->Next = current_entry;
-#if DBG
- while (current_entry)
+
+ PMM_RMAP_ENTRY previous_entry = NULL;
+ /* Keep the list sorted */
+ while (current_entry && (current_entry->Address < Address))
+ {
+ previous_entry = current_entry;
+ current_entry = current_entry->Next;
+ }
+
+ /* In case of clash in the address, sort by process */
+ if (current_entry && (current_entry->Address == Address))
{
- if (current_entry->Address == new_entry->Address &&
current_entry->Process == new_entry->Process)
+ while (current_entry && (current_entry->Process < Process))
{
- DbgPrint("MmInsertRmap tries to add a second rmap entry for address %p\n
current caller ",
- current_entry->Address);
- DbgPrint("%p", new_entry->Caller);
- DbgPrint("\n previous caller ");
- DbgPrint("%p", current_entry->Caller);
- DbgPrint("\n");
- KeBugCheck(MEMORY_MANAGEMENT);
+ previous_entry = current_entry;
+ current_entry = current_entry->Next;
}
- current_entry = current_entry->Next;
}
-#endif
- MmSetRmapListHeadPage(Page, new_entry);
+
+ if (current_entry && (current_entry->Address == Address) &&
(current_entry->Process == Process))
+ {
+ DbgPrint("MmInsertRmap tries to add a second rmap entry for address
%p\n", current_entry->Address);
+ DbgPrint(" current caller %p\n", new_entry->Caller);
+ DbgPrint(" previous caller %p\n", current_entry->Caller);
+ KeBugCheck(MEMORY_MANAGEMENT);
+ }
+
+ new_entry->Next = current_entry;
+ if (previous_entry)
+ previous_entry->Next = new_entry;
+ else
+ MmSetRmapListHeadPage(Page, new_entry);
+
MiReleasePfnLock(OldIrql);
if (!RMAP_IS_SEGMENT(Address))
diff --git a/ntoskrnl/mm/section.c b/ntoskrnl/mm/section.c
index ff7d9438baa..94ea1844d2d 100644
--- a/ntoskrnl/mm/section.c
+++ b/ntoskrnl/mm/section.c
@@ -1091,8 +1091,7 @@ MmUnsharePageEntrySectionSegment(PMEMORY_AREA MemoryArea,
{
ULONG_PTR Entry = InEntry ? *InEntry : MmGetPageEntrySectionSegment(Segment,
Offset);
PFN_NUMBER Page = PFN_FROM_SSE(Entry);
- ULONG_PTR NewEntry = 0;
- SWAPENTRY SwapEntry;
+ BOOLEAN IsDataMap = BooleanFlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT);
if (Entry == 0)
{
@@ -1111,64 +1110,53 @@ MmUnsharePageEntrySectionSegment(PMEMORY_AREA MemoryArea,
Entry = DECREF_SSE(Entry);
if (Dirty) Entry = DIRTY_SSE(Entry);
- if (SHARE_COUNT_FROM_SSE(Entry) > 0)
+ /* If we are paging-out, pruning the page for real will be taken care of in
MmCheckDirtySegment */
+ if ((SHARE_COUNT_FROM_SSE(Entry) > 0) || PageOut)
{
/* Update the page mapping in the segment and we're done */
- if (InEntry)
- *InEntry = Entry;
- else
- MmSetPageEntrySectionSegment(Segment, Offset, Entry);
+ MmSetPageEntrySectionSegment(Segment, Offset, Entry);
return FALSE;
}
- if (IS_DIRTY_SSE(Entry) && (MemoryArea->VadNode.u.VadFlags.VadType !=
VadImageMap))
- {
- ASSERT(!Segment->WriteCopy);
- ASSERT(MmGetSavedSwapEntryPage(Page) == 0);
+ /* We are pruning the last mapping on this page. See if we can keep it a bit more.
*/
+ ASSERT(!PageOut);
- /* The entry must be written back to the disk, so let this in the segment, the
page-out thread will take care of this */
+ if (IsDataMap)
+ {
+ /* We can always keep memory in for data maps */
MmSetPageEntrySectionSegment(Segment, Offset, Entry);
return FALSE;
}
- /* Only valid case for shared dirty pages is shared image section */
- ASSERT(!IS_DIRTY_SSE(Entry) || (Segment->Image.Characteristics &
IMAGE_SCN_MEM_SHARED));
-
- SwapEntry = MmGetSavedSwapEntryPage(Page);
- if (IS_DIRTY_SSE(Entry) && !SwapEntry)
+ if (!BooleanFlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED))
{
- SwapEntry = MmAllocSwapPage();
- if (!SwapEntry)
- {
- /* We can't have a swap entry for this page. Let the segment keep it */
- MmSetPageEntrySectionSegment(Segment, Offset, Entry);
- return FALSE;
- }
+ /* So this must have been a read-only page. Keep it ! */
+ ASSERT(Segment->WriteCopy);
+ ASSERT(!IS_DIRTY_SSE(Entry));
+ ASSERT(MmGetSavedSwapEntryPage(Page) == 0);
+ MmSetPageEntrySectionSegment(Segment, Offset, Entry);
+ return FALSE;
}
- if (IS_DIRTY_SSE(Entry))
+ /*
+ * So this is a page for a shared section of a DLL.
+ * We can keep it if it is not dirty.
+ */
+ SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
+ if ((SwapEntry == 0) && !IS_DIRTY_SSE(Entry))
{
- NTSTATUS Status = MmWriteToSwapPage(SwapEntry, Page);
- if (!NT_SUCCESS(Status))
- {
- /* We failed. Clean up */
- MmSetSavedSwapEntryPage(Page, 0);
- MmFreeSwapPage(SwapEntry);
- MmSetPageEntrySectionSegment(Segment, Offset, Entry);
- return FALSE;
- }
+ MmSetPageEntrySectionSegment(Segment, Offset, Entry);
+ return FALSE;
}
+ /* No more processes are referencing this shared dirty page. Ditch it. */
if (SwapEntry)
{
- NewEntry = MAKE_SWAP_SSE(SwapEntry);
MmSetSavedSwapEntryPage(Page, 0);
+ MmFreeSwapPage(SwapEntry);
}
-
- /* We can let this go */
- MmSetPageEntrySectionSegment(Segment, Offset, NewEntry);
+ MmSetPageEntrySectionSegment(Segment, Offset, 0);
MmReleasePageMemoryConsumer(MC_USER, Page);
- MiSetPageEvent(NULL, NULL);
return TRUE;
}
@@ -4849,9 +4837,12 @@ MmCheckDirtySegment(
{
BOOLEAN DirtyAgain;
- /* We got a dirty entry. Is this segment copy on write */
+ /*
+ * We got a dirty entry. This path is for the shared data,
+ * be-it regular file maps or shared sections of DLLs
+ */
ASSERT(!Segment->WriteCopy);
- ASSERT(Segment->SegFlags & MM_DATAFILE_SEGMENT);
+ ASSERT(FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT) ||
FlagOn(Segment->Image.Characteristics, IMAGE_SCN_MEM_SHARED));
/* Insert the cleaned entry back. Mark it as write in progress, and clear the
dirty bit. */
Entry = MAKE_SSE(PAGE_FROM_SSE(Entry), SHARE_COUNT_FROM_SSE(Entry) + 1);
@@ -4863,17 +4854,52 @@ MmCheckDirtySegment(
MmUnlockSectionSegment(Segment);
- /* Tell the FS driver who we are */
- if (PageOut)
- IoSetTopLevelIrp((PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP);
+ if (FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
+ {
+ /* We have to write it back to the file. Tell the FS driver who we are */
+ if (PageOut)
+ IoSetTopLevelIrp((PIRP)FSRTL_MOD_WRITE_TOP_LEVEL_IRP);
+
+ /* Go ahead and write the page */
+ DPRINT("Writing page at offset %I64d for file %wZ, Pageout: %s\n",
+ Offset->QuadPart, &Segment->FileObject->FileName,
PageOut ? "TRUE" : "FALSE");
+ Status = MiWritePage(Segment, Offset->QuadPart, Page);
- /* Go ahead and write the page */
- DPRINT("Writing page at offset %I64d for file %wZ, Pageout: %s\n",
- Offset->QuadPart, &Segment->FileObject->FileName, PageOut ?
"TRUE" : "FALSE");
- Status = MiWritePage(Segment, Offset->QuadPart, Page);
+ if (PageOut)
+ IoSetTopLevelIrp(NULL);
+ }
+ else
+ {
+ /* This must only be called by the page-out path */
+ ASSERT(PageOut);
- if (PageOut)
- IoSetTopLevelIrp(NULL);
+ /* And this must be for a shared section in a DLL */
+ ASSERT(Segment->Image.Characteristics & IMAGE_SCN_MEM_SHARED);
+
+ SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
+ if (!SwapEntry)
+ {
+ SwapEntry = MmAllocSwapPage();
+ }
+
+ if (SwapEntry)
+ {
+ Status = MmWriteToSwapPage(SwapEntry, Page);
+ if (NT_SUCCESS(Status))
+ {
+ MmSetSavedSwapEntryPage(Page, SwapEntry);
+ }
+ else
+ {
+ MmFreeSwapPage(SwapEntry);
+ }
+ }
+ else
+ {
+ DPRINT1("Failed to allocate a swap page!\n");
+ Status = STATUS_INSUFFICIENT_RESOURCES;
+ }
+ }
MmLockSectionSegment(Segment);
@@ -4905,8 +4931,17 @@ MmCheckDirtySegment(
/* Were this page hanging there just for the sake of being present ? */
if (!IS_DIRTY_SSE(Entry) && (SHARE_COUNT_FROM_SSE(Entry) == 0) &&
PageOut)
{
+ ULONG_PTR NewEntry = 0;
+ /* Restore the swap entry here */
+ if (!FlagOn(*Segment->Flags, MM_DATAFILE_SEGMENT))
+ {
+ SWAPENTRY SwapEntry = MmGetSavedSwapEntryPage(Page);
+ if (SwapEntry)
+ NewEntry = MAKE_SWAP_SSE(SwapEntry);
+ }
+
/* Yes. Release it */
- MmSetPageEntrySectionSegment(Segment, Offset, 0);
+ MmSetPageEntrySectionSegment(Segment, Offset, NewEntry);
MmReleasePageMemoryConsumer(MC_USER, Page);
/* Tell the caller we released the page */
return TRUE;