[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH v2] x86/HVM: Avoid cache flush operations during hvm_load



An MTRR record is processed for each vCPU during hvm_load. Each MTRR
record sets several mtrrs, each of which flushes the cache on all pCPUs.
This can take some time and trip the watchdog for HVM guests with many
CPUs.

To fix this, introduce a flag which prevents flushing the cache on x86
while loading the restore records and instead does a single cache flush
at the end of hvm_load.

This reduces the time to restore an HVM guest with 32 vCPUs by about 5
seconds on an Intel Xeon CPU E7-2870.

Signed-off-by: Ross Lagerwall <ross.lagerwall@xxxxxxxxxx>
---

In v2: Code moved into arch hooks since it's x86 specific.

 xen/arch/x86/hvm/mtrr.c    |  5 +++++
 xen/arch/x86/hvm/save.c    | 10 ++++++++++
 xen/common/hvm/save.c      | 15 ++++++++++-----
 xen/include/asm-x86/mtrr.h |  9 +++++++++
 xen/include/xen/hvm/save.h |  1 +
 5 files changed, 35 insertions(+), 5 deletions(-)

diff --git a/xen/arch/x86/hvm/mtrr.c b/xen/arch/x86/hvm/mtrr.c
index a69ee62..f21b367 100644
--- a/xen/arch/x86/hvm/mtrr.c
+++ b/xen/arch/x86/hvm/mtrr.c
@@ -65,6 +65,8 @@ static const uint8_t 
mm_type_tbl[MTRR_NUM_TYPES][PAT_TYPE_NUMS] = {
 #undef RS
 };
 
+DEFINE_PER_CPU(bool_t, memory_type_changed_ignore);
+
 /*
  * Reverse lookup table, to find a pat type according to MTRR and effective
  * memory type. This table is dynamically generated.
@@ -789,6 +791,9 @@ HVM_REGISTER_SAVE_RESTORE(MTRR, hvm_save_mtrr_msr, 
hvm_load_mtrr_msr,
 
 void memory_type_changed(struct domain *d)
 {
+    if ( this_cpu(memory_type_changed_ignore) )
+        return;
+
     if ( need_iommu(d) && d->vcpu && d->vcpu[0] )
     {
         p2m_memory_type_changed(d);
diff --git a/xen/arch/x86/hvm/save.c b/xen/arch/x86/hvm/save.c
index 61f780d..b6325d0 100644
--- a/xen/arch/x86/hvm/save.c
+++ b/xen/arch/x86/hvm/save.c
@@ -76,9 +76,19 @@ int arch_hvm_load(struct domain *d, struct hvm_save_header 
*hdr)
     /* VGA state is not saved/restored, so we nobble the cache. */
     d->arch.hvm_domain.stdvga.cache = 0;
 
+    /* Prevent cache flushes until after all restore records. */
+    this_cpu(memory_type_changed_ignore) = 1;
+
     return 0;
 }
 
+void arch_hvm_load_post(struct domain *d)
+{
+    /* Re-enable cache flushes and flush the cache. */
+    this_cpu(memory_type_changed_ignore) = 0;
+    memory_type_changed(d);
+}
+
 /*
  * Local variables:
  * mode: C
diff --git a/xen/common/hvm/save.c b/xen/common/hvm/save.c
index da6e668..40ab9c0 100644
--- a/xen/common/hvm/save.c
+++ b/xen/common/hvm/save.c
@@ -206,6 +206,7 @@ int hvm_load(struct domain *d, hvm_domain_context_t *h)
     struct hvm_save_descriptor *desc;
     hvm_load_handler handler;
     struct vcpu *v;
+    int ret = 0;
     
     if ( d->is_dying )
         return -EINVAL;
@@ -230,13 +231,14 @@ int hvm_load(struct domain *d, hvm_domain_context_t *h)
             printk(XENLOG_G_ERR
                    "HVM%d restore: save did not end with a null entry\n",
                    d->domain_id);
-            return -1;
+            ret = -1;
+            break;
         }
         
         /* Read the typecode of the next entry  and check for the end-marker */
         desc = (struct hvm_save_descriptor *)(&h->data[h->cur]);
         if ( desc->typecode == 0 )
-            return 0; 
+            break;
         
         /* Find the handler for this entry */
         if ( (desc->typecode > HVM_SAVE_CODE_MAX) ||
@@ -244,7 +246,8 @@ int hvm_load(struct domain *d, hvm_domain_context_t *h)
         {
             printk(XENLOG_G_ERR "HVM%d restore: unknown entry typecode %u\n",
                    d->domain_id, desc->typecode);
-            return -1;
+            ret = -1;
+            break;
         }
 
         /* Load the entry */
@@ -254,11 +257,13 @@ int hvm_load(struct domain *d, hvm_domain_context_t *h)
         {
             printk(XENLOG_G_ERR "HVM%d restore: failed to load entry %u/%u\n",
                    d->domain_id, desc->typecode, desc->instance);
-            return -1;
+            ret = -1;
+            break;
         }
     }
 
-    /* Not reached */
+    arch_hvm_load_post(d);
+    return ret;
 }
 
 int _hvm_init_entry(struct hvm_domain_context *h,
diff --git a/xen/include/asm-x86/mtrr.h b/xen/include/asm-x86/mtrr.h
index 0569db6..9df87cd 100644
--- a/xen/include/asm-x86/mtrr.h
+++ b/xen/include/asm-x86/mtrr.h
@@ -60,6 +60,15 @@ struct mtrr_state {
 };
 extern struct mtrr_state mtrr_state;
 
+/*
+ * The purpose of the memory_type_changed_ignore cpu flag is to
+ * avoid unecessary cache flushes when doing multiple memory type
+ * operations that may flush the cache. Code can set this flag, do
+ * several memory type operations, clear the flag and then call
+ * memory_type_changed() to flush the cache at the end.
+ */
+DECLARE_PER_CPU(bool_t, memory_type_changed_ignore);
+
 extern void mtrr_save_fixed_ranges(void *);
 extern void mtrr_save_state(void);
 extern int mtrr_add(unsigned long base, unsigned long size,
diff --git a/xen/include/xen/hvm/save.h b/xen/include/xen/hvm/save.h
index ae6f0bb..815780b 100644
--- a/xen/include/xen/hvm/save.h
+++ b/xen/include/xen/hvm/save.h
@@ -134,5 +134,6 @@ int hvm_load(struct domain *d, hvm_domain_context_t *h);
 struct hvm_save_header;
 void arch_hvm_save(struct domain *d, struct hvm_save_header *hdr);
 int arch_hvm_load(struct domain *d, struct hvm_save_header *hdr);
+void arch_hvm_load_post(struct domain *d);
 
 #endif /* __XEN_HVM_SAVE_H__ */
-- 
2.1.0


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


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.