Author: jgardou
Date: Fri Aug 19 17:24:53 2016
New Revision: 72395
URL:
http://svn.reactos.org/svn/reactos?rev=72395&view=rev
Log:
[NTOS/MM]
- Implement copy on write support in ARM3 page fault handler
CORE-8541 #resolve #comment committed in r72395
Modified:
trunk/reactos/ntoskrnl/mm/ARM3/miarm.h
trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c
trunk/reactos/ntoskrnl/mm/balance.c
trunk/reactos/ntoskrnl/mm/marea.c
Modified: trunk/reactos/ntoskrnl/mm/ARM3/miarm.h
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/miarm.h?r…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/ARM3/miarm.h [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/ARM3/miarm.h [iso-8859-1] Fri Aug 19 17:24:53 2016
@@ -880,6 +880,18 @@
#endif
+FORCEINLINE
+VOID
+MI_MAKE_TRANSITION_PTE(_Out_ PMMPTE NewPte,
+ _In_ PFN_NUMBER Page,
+ _In_ ULONG Protection)
+{
+ NewPte->u.Long = 0;
+ NewPte->u.Trans.Transition = 1;
+ NewPte->u.Trans.Protection = Protection;
+ NewPte->u.Trans.PageFrameNumber = Page;
+}
+
//
// Returns if the page is physically resident (ie: a large page)
// FIXFIX: CISC/x86 only?
@@ -2169,6 +2181,15 @@
IN PMMVAD Vad
);
+VOID
+NTAPI
+MiDeletePte(
+ IN PMMPTE PointerPte,
+ IN PVOID VirtualAddress,
+ IN PEPROCESS CurrentProcess,
+ IN PMMPTE PrototypePte
+);
+
ULONG
NTAPI
MiMakeSystemAddressValid(
Modified: trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/ARM3/pagfault.…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/ARM3/pagfault.c [iso-8859-1] Fri Aug 19 17:24:53 2016
@@ -519,6 +519,71 @@
/* Now get rid of it */
MiReleaseSystemPtes(ZeroPte, 1, SystemPteSpace);
+}
+
+VOID
+NTAPI
+MiCopyPfn(
+ _In_ PFN_NUMBER DestPage,
+ _In_ PFN_NUMBER SrcPage)
+{
+ PMMPTE SysPtes;
+ MMPTE TempPte;
+ PMMPFN DestPfn, SrcPfn;
+ PVOID DestAddress;
+ const VOID* SrcAddress;
+
+ /* Get the PFNs */
+ DestPfn = MiGetPfnEntry(DestPage);
+ ASSERT(DestPfn);
+ SrcPfn = MiGetPfnEntry(SrcPage);
+ ASSERT(SrcPfn);
+
+ /* Grab 2 system PTEs */
+ SysPtes = MiReserveSystemPtes(2, SystemPteSpace);
+ ASSERT(SysPtes);
+
+ /* Initialize the destination PTE */
+ TempPte = ValidKernelPte;
+ TempPte.u.Hard.PageFrameNumber = DestPage;
+
+ /* Setup caching */
+ if (DestPfn->u3.e1.CacheAttribute == MiWriteCombined)
+ {
+ /* Write combining, no caching */
+ MI_PAGE_DISABLE_CACHE(&TempPte);
+ MI_PAGE_WRITE_COMBINED(&TempPte);
+ }
+ else if (DestPfn->u3.e1.CacheAttribute == MiNonCached)
+ {
+ /* Write through, no caching */
+ MI_PAGE_DISABLE_CACHE(&TempPte);
+ MI_PAGE_WRITE_THROUGH(&TempPte);
+ }
+
+ /* Make the system PTE valid with our PFN */
+ MI_WRITE_VALID_PTE(&SysPtes[0], TempPte);
+
+ /* Initialize the source PTE */
+ TempPte = ValidKernelPte;
+ TempPte.u.Hard.PageFrameNumber = SrcPage;
+
+ /* Setup caching */
+ if (SrcPfn->u3.e1.CacheAttribute == MiNonCached)
+ {
+ MI_PAGE_DISABLE_CACHE(&TempPte);
+ }
+
+ /* Make the system PTE valid with our PFN */
+ MI_WRITE_VALID_PTE(&SysPtes[1], TempPte);
+
+ /* Get the addresses and perform the copy */
+ DestAddress = MiPteToAddress(&SysPtes[0]);
+ SrcAddress = MiPteToAddress(&SysPtes[1]);
+ RtlCopyMemory(DestAddress, SrcAddress, PAGE_SIZE);
+
+ /* Now get rid of it */
+ MiReleaseSystemPtes(SysPtes, 2, SystemPteSpace);
}
NTSTATUS
@@ -1043,6 +1108,7 @@
PFN_NUMBER PageFrameIndex;
NTSTATUS Status;
PVOID InPageBlock = NULL;
+ ULONG Protection;
/* Must be called with an invalid, prototype PTE, with the PFN lock held */
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
@@ -1088,31 +1154,95 @@
{
if (!PteContents.u.Proto.ReadOnly)
{
- /* Check for page acess in software */
- Status = MiAccessCheck(PointerProtoPte,
- StoreInstruction,
- KernelMode,
- TempPte.u.Soft.Protection,
- TrapInformation,
- TRUE);
- ASSERT(Status == STATUS_SUCCESS);
-
- /* Check for copy on write page */
- if ((TempPte.u.Soft.Protection & MM_WRITECOPY) == MM_WRITECOPY)
- {
- /* Not yet supported */
- ASSERT(FALSE);
- }
- }
+ Protection = TempPte.u.Soft.Protection;
+ }
+ else
+ {
+ Protection = MM_READONLY;
+ }
+ /* Check for page acess in software */
+ Status = MiAccessCheck(PointerProtoPte,
+ StoreInstruction,
+ KernelMode,
+ TempPte.u.Soft.Protection,
+ TrapInformation,
+ TRUE);
+ ASSERT(Status == STATUS_SUCCESS);
}
else
{
- /* Check for copy on write page */
- if ((PteContents.u.Soft.Protection & MM_WRITECOPY) == MM_WRITECOPY)
- {
- /* Not yet supported */
- ASSERT(FALSE);
- }
+ Protection = PteContents.u.Soft.Protection;
+ }
+
+ /* Check for writing copy on write page */
+ if (((Protection & MM_WRITECOPY) == MM_WRITECOPY) && StoreInstruction)
+ {
+ PFN_NUMBER PageFrameIndex, ProtoPageFrameIndex;
+ ULONG Color;
+
+ /* Resolve the proto fault as if it was a read operation */
+ Status = MiResolveProtoPteFault(FALSE,
+ Address,
+ PointerPte,
+ PointerProtoPte,
+ OutPfn,
+ PageFileData,
+ PteValue,
+ Process,
+ OldIrql,
+ TrapInformation);
+
+ if (!NT_SUCCESS(Status))
+ {
+ return Status;
+ }
+
+ /* Lock again the PFN lock, MiResolveProtoPteFault unlocked it */
+ OldIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+ /* And re-read the proto PTE */
+ TempPte = *PointerProtoPte;
+ ASSERT(TempPte.u.Hard.Valid == 1);
+ ProtoPageFrameIndex = PFN_FROM_PTE(&TempPte);
+
+ /* Get a new page for the private copy */
+ if (Process > HYDRA_PROCESS)
+ Color = MI_GET_NEXT_PROCESS_COLOR(Process);
+ else
+ Color = MI_GET_NEXT_COLOR();
+
+ PageFrameIndex = MiRemoveAnyPage(Color);
+
+ /* Perform the copy */
+ MiCopyPfn(PageFrameIndex, ProtoPageFrameIndex);
+
+ /* This will drop everything MiResolveProtoPteFault referenced */
+ MiDeletePte(PointerPte, Address, Process, PointerProtoPte);
+
+ /* Because now we use this */
+ Pfn1 = MI_PFN_ELEMENT(PageFrameIndex);
+ MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
+
+ /* Fix the protection */
+ Protection &= ~MM_WRITECOPY;
+ Protection |= MM_READWRITE;
+ if (Address < MmSystemRangeStart)
+ {
+ /* Build the user PTE */
+ MI_MAKE_HARDWARE_PTE_USER(&PteContents, PointerPte, Protection,
PageFrameIndex);
+ }
+ else
+ {
+ /* Build the kernel PTE */
+ MI_MAKE_HARDWARE_PTE(&PteContents, PointerPte, Protection,
PageFrameIndex);
+ }
+
+ /* And finally, write the valid PTE */
+ MI_WRITE_VALID_PTE(PointerPte, PteContents);
+
+ /* The caller expects us to release the PFN lock */
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, OldIrql);
+ return Status;
}
/* Check for clone PTEs */
@@ -2002,8 +2132,39 @@
/* Is this a copy on write PTE? */
if (MI_IS_PAGE_COPY_ON_WRITE(&TempPte))
{
- /* Not supported yet */
- ASSERT(FALSE);
+ PFN_NUMBER PageFrameIndex, OldPageFrameIndex;
+ PMMPFN Pfn1;
+
+ LockIrql = KeAcquireQueuedSpinLock(LockQueuePfnLock);
+
+ ASSERT(MmAvailablePages > 0);
+
+ /* Allocate a new page and copy it */
+ PageFrameIndex =
MiRemoveAnyPage(MI_GET_NEXT_PROCESS_COLOR(CurrentProcess));
+ OldPageFrameIndex = PFN_FROM_PTE(&TempPte);
+
+ MiCopyPfn(PageFrameIndex, OldPageFrameIndex);
+
+ /* Dereference whatever this PTE is referencing */
+ Pfn1 = MI_PFN_ELEMENT(OldPageFrameIndex);
+ ASSERT(Pfn1->u3.e1.PrototypePte == 1);
+ ASSERT(!MI_IS_PFN_DELETED(Pfn1));
+ ProtoPte = Pfn1->PteAddress;
+ MiDeletePte(PointerPte, Address, CurrentProcess, ProtoPte);
+
+ /* And make a new shiny one with our page */
+ MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
+ TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
+ TempPte.u.Hard.Write = 1;
+ TempPte.u.Hard.CopyOnWrite = 0;
+
+ MI_WRITE_VALID_PTE(PointerPte, TempPte);
+
+ KeReleaseQueuedSpinLock(LockQueuePfnLock, LockIrql);
+
+ /* Return the status */
+ MiUnlockProcessWorkingSet(CurrentProcess, CurrentThread);
+ return STATUS_PAGE_FAULT_COPY_ON_WRITE;
}
/* Is this a read-only PTE? */
Modified: trunk/reactos/ntoskrnl/mm/balance.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/balance.c?rev=…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/balance.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/balance.c [iso-8859-1] Fri Aug 19 17:24:53 2016
@@ -228,13 +228,6 @@
return (MiBalancerThreadHandle != NULL) &&
(PsGetCurrentThreadId() == MiBalancerThreadId.UniqueThread);
}
-
-VOID
-NTAPI
-MiDeletePte(IN PMMPTE PointerPte,
- IN PVOID VirtualAddress,
- IN PEPROCESS CurrentProcess,
- IN PMMPTE PrototypePte);
VOID
NTAPI
Modified: trunk/reactos/ntoskrnl/mm/marea.c
URL:
http://svn.reactos.org/svn/reactos/trunk/reactos/ntoskrnl/mm/marea.c?rev=72…
==============================================================================
--- trunk/reactos/ntoskrnl/mm/marea.c [iso-8859-1] (original)
+++ trunk/reactos/ntoskrnl/mm/marea.c [iso-8859-1] Fri Aug 19 17:24:53 2016
@@ -275,12 +275,6 @@
*
* @remarks Lock the address space before calling this function.
*/
-VOID
-NTAPI
-MiDeletePte(IN PMMPTE PointerPte,
- IN PVOID VirtualAddress,
- IN PEPROCESS CurrentProcess,
- IN PMMPTE PrototypePte);
NTSTATUS NTAPI
MmFreeMemoryArea(