Index: trunk/src/VBox/VMM/PGMInternal.h
===================================================================
--- trunk/src/VBox/VMM/PGMInternal.h	(revision 22472)
+++ trunk/src/VBox/VMM/PGMInternal.h	(revision 22473)
@@ -65,4 +65,11 @@
  */
 #define PGM_SKIP_GLOBAL_PAGEDIRS_ON_NONGLOBAL_FLUSH
+
+/**
+ * Optimization for PAE page tables that are modified often
+ */
+#ifndef IN_RC
+////# define PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+#endif
 
 /**
@@ -1662,9 +1669,18 @@
      * It's a hack required because of REMR3NotifyHandlerPhysicalDeregister. */
     bool volatile       fReusedFlushPending;
+#ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+    /** Used to mark the page as dirty (write monitoring if temporarily off. */
+    bool                fDirty;
+#else
     bool                bPadding1;
+#endif
 
     /** Used to indicate that this page can't be flushed. Important for cr3 root pages or shadow pae pd pages). */
     uint32_t            cLocked;
+#ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+    uint32_t            idxDirty;
+#else
     uint32_t            bPadding2;
+#endif
 } PGMPOOLPAGE, *PPGMPOOLPAGE, **PPPGMPOOLPAGE;
 /** Pointer to a const pool page. */
@@ -1752,9 +1768,18 @@
     /** The access handler description (HC ptr). */
     R3PTRTYPE(const char *)         pszAccessHandler;
+# ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+    /* Next available slot. */
+    uint32_t                    idxFreeDirtyPage;
+    /* Number of active dirty pages. */
+    uint32_t                    cDirtyPages;
+    /* Array of current dirty pgm pool page indices. */
+    uint16_t                    aIdxDirtyPages[8];
+    uint64_t                    aDirtyPages[8][512];
+# endif /* PGMPOOL_WITH_OPTIMIZED_DIRTY_PT */
 #endif /* PGMPOOL_WITH_MONITORING */
     /** The number of pages currently in use. */
     uint16_t                    cUsedPages;
 #ifdef VBOX_WITH_STATISTICS
-    /** The high wather mark for cUsedPages. */
+    /** The high water mark for cUsedPages. */
     uint16_t                    cUsedPagesHigh;
     uint32_t                    Alignment1;         /**< Align the next member on a 64-bit boundrary. */
@@ -1831,4 +1856,9 @@
     /** The number of times we're called in an async thread an need to flush. */
     STAMCOUNTER                 StatMonitorR3Async;
+    /** Times we've called pgmPoolResetDirtyPages (and there were dirty page). */
+    STAMCOUNTER                 StatResetDirtyPages;
+    /** Times we've called pgmPoolAddDirtyPage. */
+    STAMCOUNTER                 StatDirtyPage;
+
     /** The high wather mark for cModifiedPages. */
     uint16_t                    cModifiedPagesHigh;
@@ -3023,4 +3053,5 @@
 uint16_t        pgmPoolTrackPhysExtAddref(PVM pVM, uint16_t u16, uint16_t iShwPT);
 void            pgmPoolTrackPhysExtDerefGCPhys(PPGMPOOL pPool, PPGMPOOLPAGE pPoolPage, PPGMPAGE pPhysPage);
+void            pgmPoolTracDerefGCPhysHint(PPGMPOOL pPool, PPGMPOOLPAGE pPage, RTHCPHYS HCPhys, RTGCPHYS GCPhysHint);
 #ifdef PGMPOOL_WITH_MONITORING
 void            pgmPoolMonitorChainChanging(PVMCPU pVCpu, PPGMPOOL pPool, PPGMPOOLPAGE pPage, RTGCPHYS GCPhysFault, CTXTYPE(RTGCPTR, RTHCPTR, RTGCPTR) pvAddress, PDISCPUSTATE pCpu);
@@ -3028,4 +3059,7 @@
 void            pgmPoolMonitorModifiedInsert(PPGMPOOL pPool, PPGMPOOLPAGE pPage);
 #endif
+
+void            pgmPoolAddDirtyPage(PVM pVM, PPGMPOOL pPool, PPGMPOOLPAGE pPage);
+void            pgmPoolResetDirtyPages(PVM pVM, bool fForceRemoval = false);
 
 int             pgmR3ExitShadowModeBeforePoolFlush(PVM pVM, PVMCPU pVCpu);
Index: trunk/src/VBox/VMM/PGMPool.cpp
===================================================================
--- trunk/src/VBox/VMM/PGMPool.cpp	(revision 22472)
+++ trunk/src/VBox/VMM/PGMPool.cpp	(revision 22473)
@@ -362,4 +362,7 @@
     STAM_REG(pVM, &pPool->cModifiedPages,               STAMTYPE_U16,       "/PGM/Pool/Monitor/cModifiedPages",     STAMUNIT_PAGES,          "The current cModifiedPages value.");
     STAM_REG(pVM, &pPool->cModifiedPagesHigh,           STAMTYPE_U16_RESET, "/PGM/Pool/Monitor/cModifiedPagesHigh", STAMUNIT_PAGES,          "The high watermark for cModifiedPages.");
+    STAM_REG(pVM, &pPool->StatResetDirtyPages,          STAMTYPE_COUNTER,   "/PGM/Pool/Monitor/Dirty/Resets",       STAMUNIT_OCCURENCES,     "Times we've called pgmPoolResetDirtyPages (and there were dirty page).");
+    STAM_REG(pVM, &pPool->StatDirtyPage,                STAMTYPE_COUNTER,   "/PGM/Pool/Monitor/Dirty/Pages",        STAMUNIT_OCCURENCES,     "Times we've called pgmPoolAddDirtyPage.");
+    
 # endif
 # ifdef PGMPOOL_WITH_CACHE
Index: trunk/src/VBox/VMM/VMMAll/PGMAll.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMAll/PGMAll.cpp	(revision 22472)
+++ trunk/src/VBox/VMM/VMMAll/PGMAll.cpp	(revision 22473)
@@ -403,5 +403,5 @@
     PVM pVM = pVCpu->CTX_SUFF(pVM);
 
-    LogFlow(("PGMTrap0eHandler: uErr=%RGu pvFault=%RGv eip=%04x:%RGv\n", uErr, pvFault, pRegFrame->cs, (RTGCPTR)pRegFrame->rip));
+    Log(("PGMTrap0eHandler: uErr=%RGu pvFault=%RGv eip=%04x:%RGv\n", uErr, pvFault, pRegFrame->cs, (RTGCPTR)pRegFrame->rip));
     STAM_PROFILE_START(&pVCpu->pgm.s.StatRZTrap0e, a);
     STAM_STATS({ pVCpu->pgm.s.CTX_SUFF(pStatTrap0eAttribution) = NULL; } );
@@ -1701,4 +1701,9 @@
     else
     {
+        pgmLock(pVM);
+# ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+        pgmPoolResetDirtyPages(pVM);
+# endif
+        pgmUnlock(pVM);
         /*
          * Check if we have a pending update of the CR3 monitoring.
Index: trunk/src/VBox/VMM/VMMAll/PGMAllBth.h
===================================================================
--- trunk/src/VBox/VMM/VMMAll/PGMAllBth.h	(revision 22472)
+++ trunk/src/VBox/VMM/VMMAll/PGMAllBth.h	(revision 22473)
@@ -967,4 +967,10 @@
 
     LogFlow(("InvalidatePage %RGv\n", GCPtrPage));
+
+# ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+    if (pPool->cDirtyPages)
+        pgmPoolResetDirtyPages(pVM);
+# endif
+
     /*
      * Get the shadow PD entry and skip out if this PD isn't present.
@@ -1470,4 +1476,16 @@
         PVM pVM = pVCpu->CTX_SUFF(pVM);
 
+# if    defined(PGMPOOL_WITH_OPTIMIZED_DIRTY_PT)                            \
+     && PGM_WITH_PAGING(PGM_GST_TYPE, PGM_SHW_TYPE)                         \
+     && (PGM_GST_TYPE == PGM_TYPE_PAE || PGM_GST_TYPE == PGM_TYPE_AMD64)
+        if (pShwPage->fDirty)
+        {
+            PPGMPOOL pPool = pVM->pgm.s.CTX_SUFF(pPool);
+            PX86PTPAE pGstPT;
+            
+            pGstPT = (PX86PTPAE)&pPool->aDirtyPages[pShwPage->idxDirty][0];
+            pGstPT->a[iPTDst].u = PteSrc.u;
+        }
+# endif
         /*
          * Find the ram range.
@@ -1711,4 +1729,5 @@
 
     PPGMPOOLPAGE    pShwPage = pgmPoolGetPage(pPool, PdeDst.u & SHW_PDE_PG_MASK);
+    Assert(pShwPage);
 
 # if PGM_GST_TYPE == PGM_TYPE_AMD64
@@ -1838,9 +1857,10 @@
                         const unsigned iPTDst = (GCPtrPage >> SHW_PT_SHIFT) & SHW_PT_MASK;
                         PGM_BTH_NAME(SyncPageWorker)(pVCpu, &pPTDst->a[iPTDst], PdeSrc, PteSrc, pShwPage, iPTDst);
-                        Log2(("SyncPage: 4K  %RGv PteSrc:{P=%d RW=%d U=%d raw=%08llx}%s\n",
+                        Log2(("SyncPage: 4K  %RGv PteSrc:{P=%d RW=%d U=%d raw=%08llx} PteDst=%08llx %s\n",
                               GCPtrPage, PteSrc.n.u1Present,
                               PteSrc.n.u1Write & PdeSrc.n.u1Write,
                               PteSrc.n.u1User & PdeSrc.n.u1User,
                               (uint64_t)PteSrc.u,
+                              (uint64_t)pPTDst->a[iPTDst].u,
                               pPTDst->a[iPTDst].u & PGM_PTFLAGS_TRACK_DIRTY ? " Track-Dirty" : ""));
                     }
@@ -3395,4 +3415,12 @@
 
 #if PGM_SHW_TYPE != PGM_TYPE_NESTED && PGM_SHW_TYPE != PGM_TYPE_EPT
+
+    pgmLock(pVM);
+# ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+    PPGMPOOL pPool = pVM->pgm.s.CTX_SUFF(pPool);
+    if (pPool->cDirtyPages)
+        pgmPoolResetDirtyPages(pVM);
+# endif
+
     /*
      * Update page access handlers.
@@ -3407,4 +3435,5 @@
     PGM_GST_NAME(HandlerVirtualUpdate)(pVM, cr4);
     STAM_PROFILE_STOP(&pVCpu->pgm.s.CTX_MID_Z(Stat,SyncCR3Handlers), h);
+    pgmUnlock(pVM);
 #endif
 
@@ -4379,4 +4408,9 @@
     pgmLock(pVM);
 
+# ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+    if (pPool->cDirtyPages)
+        pgmPoolResetDirtyPages(pVM);
+# endif
+
     Assert(!(GCPhysCR3 >> (PAGE_SHIFT + 32)));
     rc = pgmPoolAlloc(pVM, GCPhysCR3 & GST_CR3_PAGE_MASK, BTH_PGMPOOLKIND_ROOT, SHW_POOL_ROOT_IDX, GCPhysCR3 >> PAGE_SHIFT, &pNewShwPageCR3, true /* lock page */);
Index: trunk/src/VBox/VMM/VMMAll/PGMAllPool.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMAll/PGMAllPool.cpp	(revision 22472)
+++ trunk/src/VBox/VMM/VMMAll/PGMAllPool.cpp	(revision 22473)
@@ -777,5 +777,5 @@
  * Checks if a access could be a fork operation in progress.
  *
- * Meaning, that the guest is setuping up the parent process for Copy-On-Write.
+ * Meaning, that the guest is setting up the parent process for Copy-On-Write.
  *
  * @returns true if it's likly that we're forking, otherwise false.
@@ -830,5 +830,5 @@
 {
 #ifndef IN_RC
-    /** @todo could make this general, faulting close to rsp should be safe reuse heuristic. */
+    /** @todo could make this general, faulting close to rsp should be a safe reuse heuristic. */
     if (   HWACCMHasPendingIrq(pVM)
         && (pRegFrame->rsp - pvFault) < 32)
@@ -885,5 +885,6 @@
             return false;
     }
-    if (    (pDis->param1.flags & USE_REG_GEN32)
+    if (    (    (pDis->param1.flags & USE_REG_GEN32) 
+             ||  (pDis->param1.flags & USE_REG_GEN64))
         &&  (pDis->param1.base.reg_gen == USE_REG_ESP))
     {
@@ -919,4 +920,5 @@
     /*
      * Emulate the instruction (xp/w2k problem, requires pc/cr2/sp detection).
+     * @todo: why is this necessary? an instruction restart would be sufficient, wouldn't it?
      */
     uint32_t cbWritten;
@@ -1117,4 +1119,5 @@
     PVMCPU          pVCpu = VMMGetCpu(pVM);
     unsigned        cMaxModifications;
+    bool            fForcedFlush = false;
 
     LogFlow(("pgmPoolAccessHandler: pvFault=%RGv pPage=%p:{.idx=%d} GCPhysFault=%RGp\n", pvFault, pPage, pPage->idx, GCPhysFault));
@@ -1144,4 +1147,7 @@
      */
     Assert(pPage->iMonitoredPrev == NIL_PGMPOOL_IDX);
+#ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+    Assert(!pPage->fDirty);
+#endif
 
     /* Maximum nr of modifications depends on the guest mode. */
@@ -1166,6 +1172,12 @@
         pPage->cLastAccessHandlerCount  = pVCpu->pgm.s.cPoolAccessHandler;
         if (pPage->cModifications > cMaxModifications)
+        {
             STAM_COUNTER_INC(&pPool->CTX_MID_Z(StatMonitor,FlushReinit));
-    }
+            fForcedFlush = true;
+        }
+    }
+
+    if (pPage->cModifications >= cMaxModifications)
+        Log(("Mod overflow %VGv cMods=%d (locked=%d type=%s)\n", pvFault, pPage->cModifications, pgmPoolIsPageLocked(&pVM->pgm.s, pPage), pgmPoolPoolKindToStr(pPage->enmKind)));
 
     /*
@@ -1266,4 +1278,37 @@
     }
 
+#ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+    /* E.g. Windows 7 x64 initializes page tables and touches some pages in the table during the process. This
+     * leads to pgm pool trashing and an excessive amount of write faults due to page monitoring.
+     */
+    if (    !fReused
+        &&  !fForcedFlush
+        &&  pPage->enmKind == PGMPOOLKIND_PAE_PT_FOR_PAE_PT
+        &&  pPage->cModifications >= cMaxModifications
+        &&  pPage->iModifiedNext == NIL_PGMPOOL_IDX
+        &&  pPage->iModifiedPrev == NIL_PGMPOOL_IDX)
+    {
+        Assert(!pgmPoolIsPageLocked(&pVM->pgm.s, pPage));
+        Assert(pPage->fDirty == false);
+
+        rc = PGMHandlerPhysicalPageTempOff(pVM, pPage->GCPhys, pPage->GCPhys);
+        if (rc == VINF_SUCCESS)
+        {
+            rc = PGMShwModifyPage(pVCpu, pvFault, 1, X86_PTE_RW, ~(uint64_t)X86_PTE_RW);
+            AssertMsg(rc == VINF_SUCCESS 
+                      /* In the SMP case the page table might be removed while we wait for the PGM lock in the trap handler. */
+                      ||  rc == VERR_PAGE_TABLE_NOT_PRESENT 
+                      ||  rc == VERR_PAGE_NOT_PRESENT, 
+                      ("PGMShwModifyPage -> GCPtr=%RGv rc=%d\n", pvFault, rc));
+
+            pgmPoolAddDirtyPage(pVM, pPool, pPage);
+ 
+            STAM_PROFILE_STOP(&pVM->pgm.s.CTX_SUFF(pPool)->CTX_SUFF_Z(StatMonitor), a);
+            pgmUnlock(pVM);
+            return rc;
+        }
+    }
+#endif /* PGMPOOL_WITH_OPTIMIZED_DIRTY_PT */
+
     /*
      * Not worth it, so flush it.
@@ -1273,5 +1318,5 @@
      * interpret then. This may be a bit risky, in which case
      * the reuse detection must be fixed.
-     */
+     */       
     rc = pgmPoolAccessHandlerFlush(pVM, pVCpu, pPool, pPage, pDis, pRegFrame, GCPhysFault, pvFault);
     if (rc == VINF_EM_RAW_EMULATE_INSTR && fReused)
@@ -1283,4 +1328,213 @@
 
 # endif /* !IN_RING3 */
+
+# ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+/**
+ * Clear references to guest physical memory in a PAE / PAE page table.
+ *
+ * @returns nr of changed PTEs
+ * @param   pPool       The pool.
+ * @param   pPage       The page.
+ * @param   pShwPT      The shadow page table (mapping of the page).
+ * @param   pGstPT      The guest page table.
+ * @param   pGstPT      The old cached guest page table.
+ */
+DECLINLINE(unsigned) pgmPoolTrackFlushPTPaePae(PPGMPOOL pPool, PPGMPOOLPAGE pPage, PX86PTPAE pShwPT, PCX86PTPAE pGstPT, PCX86PTPAE pOldGstPT)
+{
+    unsigned cChanged = 0;
+
+    for (unsigned i = 0; i < RT_ELEMENTS(pShwPT->a); i++)
+    {
+        if (pShwPT->a[i].n.u1Present)
+        {
+            /* The the old cached PTE is identical, then there's no need to flush the shadow copy. */
+            if ((pGstPT->a[i].u & X86_PTE_PAE_PG_MASK) == (pOldGstPT->a[i].u & X86_PTE_PAE_PG_MASK))
+            {
+#ifdef VBOX_STRICT
+                RTHCPHYS HCPhys = -1;
+                int rc = PGMPhysGCPhys2HCPhys(pPool->CTX_SUFF(pVM), pGstPT->a[i].u & X86_PTE_PAE_PG_MASK, &HCPhys);
+                AssertMsg(rc == VINF_SUCCESS && (pShwPT->a[i].u & X86_PTE_PAE_PG_MASK) == HCPhys, ("rc=%d guest %RGp %RHp vs %RHp\n", rc, pGstPT->a[i].u & X86_PTE_PAE_PG_MASK, (pShwPT->a[i].u & X86_PTE_PAE_PG_MASK), HCPhys));
+#endif
+                uint64_t uHostAttr  = pShwPT->a[i].u & (X86_PTE_P | X86_PTE_US | X86_PTE_A | X86_PTE_D | X86_PTE_G | X86_PTE_PAE_NX);
+                bool     fHostRW    = !!(pShwPT->a[i].u & X86_PTE_RW);
+                uint64_t uGuestAttr = pGstPT->a[i].u & (X86_PTE_P | X86_PTE_US | X86_PTE_A | X86_PTE_D | X86_PTE_G | X86_PTE_PAE_NX);
+                bool     fGuestRW   = !!(pGstPT->a[i].u & X86_PTE_RW);
+
+                if (    uHostAttr == uGuestAttr
+                    &&  fHostRW <= fGuestRW)
+                    continue;
+            }
+            cChanged++;
+            /* Something was changed, so flush it. */
+            Log4(("pgmPoolTrackDerefPTPaePae: i=%d pte=%RX32 hint=%RX32\n",
+                  i, pShwPT->a[i].u & X86_PTE_PAE_PG_MASK, pGstPT->a[i].u & X86_PTE_PAE_PG_MASK));
+            pgmPoolTracDerefGCPhysHint(pPool, pPage, pShwPT->a[i].u & X86_PTE_PAE_PG_MASK, pOldGstPT->a[i].u & X86_PTE_PAE_PG_MASK);
+            ASMAtomicWriteSize(&pShwPT->a[i].u, 0);
+        }
+    }
+    return cChanged;
+}
+
+
+/**
+ * Flush a dirty page
+ *
+ * @param   pVM             VM Handle.
+ * @param   pPool           The pool.
+ * @param   idxSlot         Dirty array slot index
+ * @param   fForceRemoval   Force removal from the dirty page list
+ */
+static void pgmPoolFlushDirtyPage(PVM pVM, PPGMPOOL pPool, unsigned idxSlot, bool fForceRemoval = false)
+{
+    PPGMPOOLPAGE pPage;
+    unsigned     idxPage;
+
+    Assert(idxSlot < RT_ELEMENTS(pPool->aIdxDirtyPages));
+    if (pPool->aIdxDirtyPages[idxSlot] == NIL_PGMPOOL_IDX)
+        return;
+
+    idxPage = pPool->aIdxDirtyPages[idxSlot];
+    AssertRelease(idxPage != NIL_PGMPOOL_IDX);
+    pPage = &pPool->aPages[idxPage];
+    Assert(pPage->idx == idxPage);
+    Assert(pPage->iModifiedNext == NIL_PGMPOOL_IDX && pPage->iModifiedPrev == NIL_PGMPOOL_IDX);
+
+    AssertMsg(pPage->fDirty, ("Page %RGp (slot=%d) not marked dirty!", pPage->GCPhys, idxSlot));
+    Log(("Flush dirty page %RGp cMods=%d\n", pPage->GCPhys, pPage->cModifications));
+
+    /* Flush those PTEs that have changed. */
+    STAM_PROFILE_START(&pPool->StatTrackDeref,a);
+    void *pvShw = PGMPOOL_PAGE_2_LOCKED_PTR(pPool->CTX_SUFF(pVM), pPage);
+    void *pvGst;
+    int rc = PGM_GCPHYS_2_PTR(pPool->CTX_SUFF(pVM), pPage->GCPhys, &pvGst); AssertReleaseRC(rc);
+    unsigned cChanges = pgmPoolTrackFlushPTPaePae(pPool, pPage, (PX86PTPAE)pvShw, (PCX86PTPAE)pvGst, (PCX86PTPAE)&pPool->aDirtyPages[idxSlot][0]);
+    STAM_PROFILE_STOP(&pPool->StatTrackDeref,a);
+
+    /** Note: we might want to consider keeping the dirty page active in case there were many changes. */
+
+    /* Write protect the page again to catch all write accesses. */
+    rc = PGMHandlerPhysicalReset(pVM, pPage->GCPhys);
+    Assert(rc == VINF_SUCCESS);
+    pPage->fDirty         = false;
+    pPage->fZeroed        = true;
+
+    /* This page is likely to be modified again, so reduce the nr of modifications just a bit here. */
+    Assert(pPage->cModifications);
+    if (cChanges < 4)
+        pPage->cModifications = 1;      /* must use > 0 here */
+    else
+        pPage->cModifications = RT_MAX(1, pPage->cModifications / 2);
+
+    STAM_COUNTER_INC(&pPool->StatResetDirtyPages);
+    if (pPool->cDirtyPages == RT_ELEMENTS(pPool->aIdxDirtyPages))
+        pPool->idxFreeDirtyPage = idxSlot;
+
+    pPool->cDirtyPages--;
+    pPool->aIdxDirtyPages[idxSlot] = NIL_PGMPOOL_IDX;
+    Assert(pPool->cDirtyPages <= RT_ELEMENTS(pPool->aIdxDirtyPages));
+    Log(("Removed dirty page %RGp cMods=%d\n", pPage->GCPhys, pPage->cModifications));
+}
+
+/**
+ * Add a new dirty page
+ *
+ * @param   pVM         VM Handle.
+ * @param   pPool       The pool.
+ * @param   pPage       The page.
+ */
+void pgmPoolAddDirtyPage(PVM pVM, PPGMPOOL pPool, PPGMPOOLPAGE pPage)
+{
+    unsigned idxFree;
+
+    Assert(PGMIsLocked(pVM));
+    AssertCompile(RT_ELEMENTS(pPool->aIdxDirtyPages) == 8 || RT_ELEMENTS(pPool->aIdxDirtyPages) == 16);
+
+    if (pPage->fDirty)
+        return;
+
+    idxFree = pPool->idxFreeDirtyPage;
+    Assert(idxFree < RT_ELEMENTS(pPool->aIdxDirtyPages));
+    Assert(pPage->iModifiedNext == NIL_PGMPOOL_IDX && pPage->iModifiedPrev == NIL_PGMPOOL_IDX);
+
+    if (pPool->cDirtyPages >= RT_ELEMENTS(pPool->aIdxDirtyPages))
+        pgmPoolFlushDirtyPage(pVM, pPool, idxFree, true /* force removal */);
+    Assert(pPool->cDirtyPages < RT_ELEMENTS(pPool->aIdxDirtyPages));
+    AssertMsg(pPool->aIdxDirtyPages[idxFree] == NIL_PGMPOOL_IDX, ("idxFree=%d cDirtyPages=%d\n", idxFree, pPool->cDirtyPages));
+
+    /* Make a copy of the guest page table as we require valid GCPhys addresses when removing
+     * references to physical pages. (the HCPhys linear lookup is *extremely* expensive!)
+     */
+    void *pvGst;
+    int rc = PGM_GCPHYS_2_PTR(pPool->CTX_SUFF(pVM), pPage->GCPhys, &pvGst); AssertReleaseRC(rc);
+    memcpy(&pPool->aDirtyPages[idxFree][0], pvGst, PAGE_SIZE);
+
+    STAM_COUNTER_INC(&pPool->StatDirtyPage);
+    Log(("Mark dirty page %RGp (slot=%d)\n", pPage->GCPhys, idxFree));
+    pPage->fDirty                  = true;
+    pPage->idxDirty                = idxFree;
+    pPool->aIdxDirtyPages[idxFree] = pPage->idx;
+    pPool->cDirtyPages++;
+
+    pPool->idxFreeDirtyPage        = (pPool->idxFreeDirtyPage + 1) & (RT_ELEMENTS(pPool->aIdxDirtyPages) - 1);
+    if (    pPool->cDirtyPages < RT_ELEMENTS(pPool->aIdxDirtyPages)
+        &&  pPool->aIdxDirtyPages[pPool->idxFreeDirtyPage] != NIL_PGMPOOL_IDX)
+    {
+        unsigned i;
+        for (i = 1; i < RT_ELEMENTS(pPool->aIdxDirtyPages); i++)
+        {
+            idxFree = (pPool->idxFreeDirtyPage + i) & (RT_ELEMENTS(pPool->aIdxDirtyPages) - 1);
+            if (pPool->aIdxDirtyPages[idxFree] == NIL_PGMPOOL_IDX)
+            {
+                pPool->idxFreeDirtyPage = idxFree;
+                break;
+            }
+        }
+        Assert(i != RT_ELEMENTS(pPool->aIdxDirtyPages));
+    }
+
+    Assert(pPool->cDirtyPages == RT_ELEMENTS(pPool->aIdxDirtyPages) || pPool->aIdxDirtyPages[pPool->idxFreeDirtyPage] == NIL_PGMPOOL_IDX);
+    return;
+}
+
+
+/**
+ * Reset all dirty pages by reinstating page monitoring.
+ *
+ * @param   pVM             VM Handle.
+ * @param   fForceRemoval   Force removal of all dirty pages
+ */
+void pgmPoolResetDirtyPages(PVM pVM, bool fForceRemoval)
+{
+    PPGMPOOL pPool = pVM->pgm.s.CTX_SUFF(pPool);
+    Assert(PGMIsLocked(pVM));
+    Assert(pPool->cDirtyPages <= RT_ELEMENTS(pPool->aIdxDirtyPages));
+
+    if (!pPool->cDirtyPages)
+        return;
+
+    Log(("pgmPoolResetDirtyPages\n"));
+    for (unsigned i = 0; i < RT_ELEMENTS(pPool->aIdxDirtyPages); i++)
+        pgmPoolFlushDirtyPage(pVM, pPool, i, fForceRemoval);
+
+    pPool->idxFreeDirtyPage = 0;
+    if (    pPool->cDirtyPages != RT_ELEMENTS(pPool->aIdxDirtyPages)
+        &&  pPool->aIdxDirtyPages[pPool->idxFreeDirtyPage] != NIL_PGMPOOL_IDX)
+    {
+        unsigned i;
+        for (i = 1; i < RT_ELEMENTS(pPool->aIdxDirtyPages); i++)
+        {
+            if (pPool->aIdxDirtyPages[i] == NIL_PGMPOOL_IDX)
+            {
+                pPool->idxFreeDirtyPage = i;
+                break;
+            }
+        }
+        AssertMsg(i != RT_ELEMENTS(pPool->aIdxDirtyPages), ("cDirtyPages %d", pPool->cDirtyPages));
+    }
+
+    Assert(pPool->aIdxDirtyPages[pPool->idxFreeDirtyPage] == NIL_PGMPOOL_IDX || pPool->cDirtyPages == RT_ELEMENTS(pPool->aIdxDirtyPages));
+    return;
+}
+# endif /* PGMPOOL_WITH_OPTIMIZED_DIRTY_PT */
 #endif  /* PGMPOOL_WITH_MONITORING */
 
@@ -1818,4 +2072,10 @@
         Assert(pPageHead != pPage); Assert(pPageHead->iMonitoredNext != pPage->idx);
         Assert(pPageHead->iMonitoredPrev != pPage->idx);
+
+#ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+        if (pPageHead->fDirty)
+            pgmPoolFlushDirtyPage(pPool->CTX_SUFF(pVM), pPool, pPageHead->idxDirty, true /* force removal */);
+#endif
+
         pPage->iMonitoredPrev = pPageHead->idx;
         pPage->iMonitoredNext = pPageHead->iMonitoredNext;
@@ -2024,5 +2284,5 @@
  * @param   pVM     The VM handle.
  */
-void pgmPoolMonitorModifiedClearAll(PVM pVM)
+static void pgmPoolMonitorModifiedClearAll(PVM pVM)
 {
     pgmLock(pVM);
@@ -2031,4 +2291,9 @@
 
     unsigned cPages = 0; NOREF(cPages);
+
+#ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+    pgmPoolResetDirtyPages(pVM, true /* force removal. */);
+#endif
+
     uint16_t idx = pPool->iModifiedHead;
     pPool->iModifiedHead = NIL_PGMPOOL_IDX;
@@ -2171,4 +2436,12 @@
     }
     paPhysExts[cMaxPhysExts - 1].iNext = NIL_PGMPOOL_PHYSEXT_INDEX;
+#endif
+
+#ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+    /* Clear all dirty pages. */
+    pPool->idxFreeDirtyPage = 0;
+    pPool->cDirtyPages      = 0;
+    for (unsigned i = 0; i < RT_ELEMENTS(pPool->aIdxDirtyPages); i++)
+        pPool->aIdxDirtyPages[i] = NIL_PGMPOOL_IDX;
 #endif
 
@@ -3987,4 +4260,9 @@
     pPage->fZeroed = false;
 
+#ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+    if (pPage->fDirty)
+        pgmPoolFlushDirtyPage(pVM, pPool, pPage->idxDirty, true /* force removal */);
+#endif
+
 #ifdef PGMPOOL_WITH_USER_TRACKING
     /*
@@ -4211,4 +4489,7 @@
     pPage->fMonitored = false;
     pPage->fCached = false;
+#ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+    pPage->fDirty = false;
+#endif
     pPage->fReusedFlushPending = false;
 #ifdef PGMPOOL_WITH_MONITORING
@@ -4373,4 +4654,7 @@
         pPage->fSeenNonGlobal = false;
         pPage->fMonitored = false;
+#ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+        pPage->fDirty     = false;
+#endif
         pPage->fCached    = false;
         pPage->fReusedFlushPending = false;
@@ -4449,4 +4733,12 @@
     pPool->iAgeHead = NIL_PGMPOOL_IDX;
     pPool->iAgeTail = NIL_PGMPOOL_IDX;
+#endif
+
+#ifdef PGMPOOL_WITH_OPTIMIZED_DIRTY_PT
+    /* Clear all dirty pages. */
+    pPool->idxFreeDirtyPage = 0;
+    pPool->cDirtyPages      = 0;
+    for (unsigned i = 0; i < RT_ELEMENTS(pPool->aIdxDirtyPages); i++)
+        pPool->aIdxDirtyPages[i] = NIL_PGMPOOL_IDX;
 #endif
 
