This patch adds PML support in p2m-ept for log-dirty.

In case of PML is used, we just need to clear EPT entry's D-bit in order to log
that GFN instead of setting EPT entry to read-only. And for partial log-dirty,
we also set D-bit for guest memory in normal mode to avoid unnecessary GPA
logging, as PML works globally at entire valid EPT table.

Signed-off-by: Kai Huang <kai.hu...@linux.intel.com>
---
 xen/arch/x86/mm/hap/hap.c | 15 ++++++++--
 xen/arch/x86/mm/p2m-ept.c | 73 +++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 80 insertions(+), 8 deletions(-)

diff --git a/xen/arch/x86/mm/hap/hap.c b/xen/arch/x86/mm/hap/hap.c
index 25f2f58..15fb5de 100644
--- a/xen/arch/x86/mm/hap/hap.c
+++ b/xen/arch/x86/mm/hap/hap.c
@@ -198,7 +198,10 @@ static int hap_enable_log_dirty(struct domain *d, bool_t 
log_global)
 
     if ( log_global )
     {
-        /* set l1e entries of P2M table to be read-only. */
+        /*
+         * switch to log dirty mode. either set l1e entries of P2M table to be
+         * read-only, or enable log dirty in hardware-assisted way.
+         */
         p2m_change_entry_type_global(d, p2m_ram_rw, p2m_ram_logdirty);
         flush_tlb_mask(d->domain_dirty_cpumask);
     }
@@ -213,14 +216,20 @@ static int hap_disable_log_dirty(struct domain *d)
 
     p2m_disable_hardware_log_dirty(d);
 
-    /* set l1e entries of P2M table with normal mode */
+    /*
+     * switch back to normal mode. either set l1e entries of P2M table with
+     * normal mode, or disable log dirty in hardware-assisted way.
+     */
     p2m_change_entry_type_global(d, p2m_ram_logdirty, p2m_ram_rw);
     return 0;
 }
 
 static void hap_clean_dirty_bitmap(struct domain *d)
 {
-    /* set l1e entries of P2M table to be read-only. */
+    /*
+     * switch to log dirty mode. either set l1e entries of P2M table to be
+     * read-only, or enable log dirty in hardware-assisted way.
+     */
     p2m_change_entry_type_global(d, p2m_ram_rw, p2m_ram_logdirty);
     flush_tlb_mask(d->domain_dirty_cpumask);
 }
diff --git a/xen/arch/x86/mm/p2m-ept.c b/xen/arch/x86/mm/p2m-ept.c
index 8650092..9a719f6 100644
--- a/xen/arch/x86/mm/p2m-ept.c
+++ b/xen/arch/x86/mm/p2m-ept.c
@@ -102,7 +102,8 @@ static int atomic_write_ept_entry(ept_entry_t *entryptr, 
ept_entry_t new,
     return rc;
 }
 
-static void ept_p2m_type_to_flags(ept_entry_t *entry, p2m_type_t type, 
p2m_access_t access)
+static void ept_p2m_type_to_flags(struct p2m_domain *p2m, ept_entry_t *entry,
+                                  p2m_type_t type, p2m_access_t access)
 {
     /* First apply type permissions */
     switch(type)
@@ -118,6 +119,12 @@ static void ept_p2m_type_to_flags(ept_entry_t *entry, 
p2m_type_t type, p2m_acces
             break;
         case p2m_ram_rw:
             entry->r = entry->w = entry->x = 1;
+            /*
+             * This is about to avoid unnecessary GPA logging in PML buffer,
+             * such as normal memory in partial log-dirty
+             */
+            if ( vmx_domain_pml_enabled(p2m->domain) )
+                entry->d = 1;
             break;
         case p2m_mmio_direct:
             entry->r = entry->x = 1;
@@ -125,6 +132,26 @@ static void ept_p2m_type_to_flags(ept_entry_t *entry, 
p2m_type_t type, p2m_acces
                                                     entry->mfn);
             break;
         case p2m_ram_logdirty:
+            entry->r = entry->x = 1;
+            if ( vmx_domain_pml_enabled(p2m->domain) )
+            {
+                /*
+                 * In case of PML, we don't have to write protect 4K page, but
+                 * only need to clear D-bit for it. Note we still need to write
+                 * protect super page in order to split it to 4K pages in EPT
+                 * violation.
+                 */
+                if ( !is_epte_superpage(entry) )
+                {
+                    entry->w = 1;
+                    entry->d = 0;
+                }
+                else
+                    entry->w = 0;
+            }
+            else
+                entry->w = 0;
+            break;
         case p2m_ram_ro:
         case p2m_ram_shared:
             entry->r = entry->x = 1;
@@ -250,7 +277,7 @@ static int ept_split_super_page(struct p2m_domain *p2m, 
ept_entry_t *ept_entry,
         /* A/D bits are inherited from superpage */
         ASSERT(!epte->avail3);
 
-        ept_p2m_type_to_flags(epte, epte->sa_p2mt, epte->access);
+        ept_p2m_type_to_flags(p2m, epte, epte->sa_p2mt, epte->access);
 
         if ( (level - 1) == target )
             continue;
@@ -492,7 +519,7 @@ static int resolve_misconfig(struct p2m_domain *p2m, 
unsigned long gfn)
                     {
                          e.sa_p2mt = p2m_is_logdirty_range(p2m, gfn + i, gfn + 
i)
                                      ? p2m_ram_logdirty : p2m_ram_rw;
-                         ept_p2m_type_to_flags(&e, e.sa_p2mt, e.access);
+                         ept_p2m_type_to_flags(p2m, &e, e.sa_p2mt, e.access);
                     }
                     e.recalc = 0;
                     wrc = atomic_write_ept_entry(&epte[i], e, level);
@@ -544,7 +571,7 @@ static int resolve_misconfig(struct p2m_domain *p2m, 
unsigned long gfn)
                 e.ipat = ipat;
                 e.recalc = 0;
                 if ( recalc && p2m_is_changeable(e.sa_p2mt) )
-                    ept_p2m_type_to_flags(&e, e.sa_p2mt, e.access);
+                    ept_p2m_type_to_flags(p2m, &e, e.sa_p2mt, e.access);
                 wrc = atomic_write_ept_entry(&epte[i], e, level);
                 ASSERT(wrc == 0);
             }
@@ -755,7 +782,7 @@ ept_set_entry(struct p2m_domain *p2m, unsigned long gfn, 
mfn_t mfn,
         if ( ept_entry->mfn == new_entry.mfn )
              need_modify_vtd_table = 0;
 
-        ept_p2m_type_to_flags(&new_entry, p2mt, p2ma);
+        ept_p2m_type_to_flags(p2m, &new_entry, p2mt, p2ma);
     }
 
     rc = atomic_write_ept_entry(ept_entry, new_entry, target);
@@ -1057,6 +1084,35 @@ void ept_sync_domain(struct p2m_domain *p2m)
                      __ept_sync_domain, p2m, 1);
 }
 
+static void ept_enable_pml(struct p2m_domain *p2m)
+{
+    /*
+     * No need to check if vmx_domain_enable_pml has succeeded or not, as
+     * ept_p2m_type_to_flags will check if PML has been enabled or not, and
+     * traditional write protection will be used if PML has not been enabled.
+     */
+    if ( vmx_domain_pml_enabled(p2m->domain) )
+        return;
+
+    vmx_domain_enable_pml(p2m->domain);
+}
+
+static void ept_disable_pml(struct p2m_domain *p2m)
+{
+    if ( !vmx_domain_pml_enabled(p2m->domain) )
+        return;
+
+    vmx_domain_disable_pml(p2m->domain);
+}
+
+static void ept_flush_pml_buffers(struct p2m_domain *p2m)
+{
+    if ( !vmx_domain_pml_enabled(p2m->domain) )
+        return;
+
+    vmx_domain_flush_pml_buffers(p2m->domain);
+}
+
 int ept_p2m_init(struct p2m_domain *p2m)
 {
     struct ept_data *ept = &p2m->ept;
@@ -1077,6 +1133,13 @@ int ept_p2m_init(struct p2m_domain *p2m)
     /* Enable EPT A/D bit if it's supported by hardware */
     ept->ept_ad = cpu_has_vmx_ept_ad_bit ? 1 : 0;
 
+    if ( cpu_has_vmx_pml )
+    {
+        p2m->enable_hardware_log_dirty = ept_enable_pml;
+        p2m->disable_hardware_log_dirty = ept_disable_pml;
+        p2m->flush_hardware_cached_dirty = ept_flush_pml_buffers;
+    }
+
     if ( !zalloc_cpumask_var(&ept->synced_mask) )
         return -ENOMEM;
 
-- 
2.1.0


_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
http://lists.xen.org/xen-devel

Reply via email to