The following issue was observed on Windows 10 21H2 x64+: when the domain state
is saved while all cores are executing the 'halt' instruction, and the memory
save takes a relatively long time (tens of seconds), the HPET counter may
overflow as follows:
counter  = 11243f3e4a
comparator = 910cb70f

In such cases, the fix implemented in commit
b144cf45d50b603c2909fc32c6abf7359f86f1aa does not work (because the 'diff' is
not negative), resulting in the guest VM becoming unresponsive for
approximately 30 seconds.

This patch adds an option to always adjust the HPET timer to fire immediately
after restore.
---
 docs/misc/xen-command-line.pandoc |  9 +++++++++
 xen/arch/x86/hvm/hpet.c           | 21 ++++++++++++++++-----
 2 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/docs/misc/xen-command-line.pandoc 
b/docs/misc/xen-command-line.pandoc
index a75b6c9301..b28610918f 100644
--- a/docs/misc/xen-command-line.pandoc
+++ b/docs/misc/xen-command-line.pandoc
@@ -1461,6 +1461,15 @@ HPET can be disabled by specifying `hpet=0`.
 
 Deprecated alternative of `hpet=broadcast`.
 
+### hpet_drift_fix (x86)
+> `= <boolean>`
+
+> Default: `false`
+
+Always set HPET timer to fire immediately after domain restore.
+This option can be used to fix unresponsive snapshots with modern x64 Windows
+systems (21H2+) which use non-periodic timers.
+
 ### hvm_debug (x86)
 > `= <integer>`
 
diff --git a/xen/arch/x86/hvm/hpet.c b/xen/arch/x86/hvm/hpet.c
index f0e5f877f4..5b9e38bd9b 100644
--- a/xen/arch/x86/hvm/hpet.c
+++ b/xen/arch/x86/hvm/hpet.c
@@ -11,6 +11,7 @@
 #include <asm/current.h>
 #include <asm/hpet.h>
 #include <asm/mc146818rtc.h>
+#include <xen/param.h>
 #include <xen/sched.h>
 #include <xen/event.h>
 #include <xen/trace.h>
@@ -222,6 +223,9 @@ static void cf_check hpet_timer_fired(struct vcpu *v, void 
*data)
  * 1/(2^10) second, namely, 0.9765625 milliseconds */
 #define  HPET_TINY_TIME_SPAN  ((h->stime_freq >> 10) / STIME_PER_HPET_TICK)
 
+bool hpet_drift_fix;
+boolean_param("hpet_drift_fix", hpet_drift_fix);
+
 static void hpet_set_timer(HPETState *h, unsigned int tn,
                            uint64_t guest_time)
 {
@@ -268,11 +272,18 @@ static void hpet_set_timer(HPETState *h, unsigned int tn,
      * are restoring after migrate, treat any wrap as past since the value
      * is unlikely to be 'small'.
      */
-    if ( (int64_t)diff < 0 )
-        diff = (timer_is_32bit(h, tn) &&
-                vhpet_domain(h)->creation_finished &&
-                (-diff > HPET_TINY_TIME_SPAN))
-            ? (uint32_t)diff : 0;
+    if (hpet_drift_fix && !vhpet_domain(h)->creation_finished)
+    {
+        diff = 0;
+    }
+    else
+    {
+        if ( (int64_t)diff < 0 )
+            diff = (timer_is_32bit(h, tn) &&
+                    vhpet_domain(h)->creation_finished &&
+                    (-diff > HPET_TINY_TIME_SPAN))
+                ? (uint32_t)diff : 0;
+    }
 
     destroy_periodic_time(&h->pt[tn]);
     if ( (tn <= 1) && (h->hpet.config & HPET_CFG_LEGACY) )
-- 
2.42.1


Reply via email to