All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] accel/mshv: freeze time during TSC MSR write
@ 2026-11-06 14:39 Magnus Kulke
  2026-11-09 16:22 ` Doru Blânzeanu
  0 siblings, 1 reply; 3+ messages in thread
From: Magnus Kulke @ 2026-11-06 14:39 UTC (permalink / raw)
  To: qemu-devel
  Cc: Michael S. Tsirkin, Paolo Bonzini, Doru Blânzeanu,
	Magnus Kulke, Wei Liu, Wei Liu, Feng Liang, Richard Henderson,
	Doru Blânzeanu

A regression was introduced when the HV_X64_REGISTER_TSC MSR state is
being written to the hypervisor as part of the migration framework. If
the MSR is written without the time being frozen, the tsc_page (a
hyper-v enlightenment) is zeroed out and cannot be used by a guest,
which results in a performance regression, because the guest falls back
to costly reads of HV_X64_MSR_TIME_REF_COUNT.

An implementation of pre_resume_vm hook was added to handle the clock
lifecycle, relying on the dirty_vcpu state to avoid redundant state
writes.

Fixes: d540dcc5e28c (target/i386/mshv: migrate MSRs)

Signed-off-by: Magnus Kulke <magnuskulke@linux.microsoft.com>
---
 accel/mshv/mshv-all.c |  33 ++++++++++++-
 hw/i386/mshv/clock.c  | 111 +++++++++++++++++++++++++++++-------------
 include/system/mshv.h |   4 +-
 3 files changed, 113 insertions(+), 35 deletions(-)

diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index d820a67b95..ae7577d407 100644
--- a/accel/mshv/mshv-all.c
+++ b/accel/mshv/mshv-all.c
@@ -34,6 +34,7 @@
 #include "accel/accel-cpu-ops.h"
 #include "exec/cpu-common.h"
 #include "system/cpus.h"
+#include "system/hw_accel.h"
 #include "system/runstate.h"
 #include "system/accel-blocker.h"
 #include "system/address-spaces.h"
@@ -578,7 +579,10 @@ static int mshv_init(AccelState *as, MachineState *ms)
 
     register_savevm_live("mshv", 0, 1, &savevm_mshv, s);
 
-    mshv_clock_init();
+    ret = mshv_clock_init(vm_fd);
+    if (ret < 0) {
+        return -1;
+    }
 
     return 0;
 }
@@ -927,6 +931,32 @@ static void mshv_cpu_synchronize_pre_loadvm(CPUState *cpu)
     run_on_cpu(cpu, do_mshv_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL);
 }
 
+static void mshv_pre_resume_vm(AccelState *as, bool step_pending)
+{
+    MshvState *s = MSHV_STATE(as);
+    CPUState *cpu;
+    int ret, vm_fd = s->vm;
+
+    ret = mshv_clock_prepare_resume(vm_fd);
+    if (ret < 0) {
+        error_report("Failed to prepare clock for resume");
+        abort();
+    }
+
+    CPU_FOREACH(cpu) {
+        if (!cpu->vcpu_dirty) {
+            continue;
+        }
+        cpu_synchronize_post_init(cpu);
+    }
+
+    ret = mshv_clock_finish_resume(vm_fd);
+    if (ret < 0) {
+        error_report("Failed to finish clock for resume");
+        abort();
+    }
+}
+
 static void do_mshv_cpu_synchronize(CPUState *cpu, run_on_cpu_data arg)
 {
     if (!cpu->vcpu_dirty) {
@@ -962,6 +992,7 @@ static void mshv_accel_class_init(ObjectClass *oc, const void *data)
     ac->name = "MSHV";
     ac->init_machine = mshv_init;
     ac->allowed = &mshv_allowed;
+    ac->pre_resume_vm = mshv_pre_resume_vm;
 }
 
 static void mshv_accel_instance_init(Object *obj)
diff --git a/hw/i386/mshv/clock.c b/hw/i386/mshv/clock.c
index 02c586503a..2a03d3cef9 100644
--- a/hw/i386/mshv/clock.c
+++ b/hw/i386/mshv/clock.c
@@ -29,6 +29,7 @@
 typedef struct MshvClockState {
     uint64_t ref_time;
     bool ref_time_pending;
+    bool time_frozen;
 } MshvClockState;
 
 static MshvClockState mshv_clock;
@@ -110,52 +111,85 @@ static int mshv_set_time_freeze(int vm_fd, int freeze)
     return 0;
 }
 
-static void mshv_clock_vm_state_change(void *opaque, bool running,
-                                       RunState state)
-{
-    MshvClockState *s = opaque;
-    int vm_fd = mshv_state->vm;
+static int ensure_time_frozen(int vm_fd) {
     int ret;
 
-    if (running) {
-        /* Skip if we have nothing to restore, e.g. on initial boot. */
-        if (!s->ref_time_pending) {
-            return;
-        }
+    /* Ensure time is frozen */
+    if (mshv_clock.time_frozen) {
+        return 0;
+    }
 
-        ret = mshv_set_time_freeze(vm_fd, 1);
-        if (ret < 0) {
-            error_report("Failed to freeze partition time on resume");
-            abort();
-        }
+    ret = mshv_set_time_freeze(vm_fd, 1);
+    if (ret < 0) {
+        error_report("Failed to freeze partition");
+        return -1;
+    }
+    mshv_clock.time_frozen = true;
 
+    return 0;
+}
+
+int mshv_clock_prepare_resume(int vm_fd) {
+    return ensure_time_frozen(vm_fd);
+}
+
+int mshv_clock_finish_resume(int vm_fd) {
+    int ret;
+    MshvClockState *s = &mshv_clock;
+
+    assert(mshv_clock.time_frozen);
+
+    /* Skip if we have nothing to restore, e.g. on initial boot. */
+    if (s->ref_time_pending) {
         /* INVARIANT: reference time can only be written if time is frozen. */
         ret = mshv_set_reference_time(vm_fd, s->ref_time);
         if (ret < 0) {
             error_report("Failed to restore reference time on resume");
-            abort();
+            return -1;
         }
+        s->ref_time_pending = false;
+    }
 
-        if (mshv_set_time_freeze(vm_fd, 0) < 0) {
-            error_report("Failed to unfreeze partition time on resume");
-            abort();
-        }
+    ret = mshv_set_time_freeze(vm_fd, 0);
+    if (ret < 0) {
+        error_report("Failed to thaw partition");
+        return -1;
+    }
+    mshv_clock.time_frozen = false;
 
-        s->ref_time_pending = false;
-    } else {
-        /* Skip if we already have a to-be-set ref time */
-        if (s->ref_time_pending) {
-            return;
-        }
+    return 0;
+}
 
-        ret = mshv_get_reference_time(vm_fd, &s->ref_time);
-        if (ret < 0) {
-            error_report("Failed to capture reference time on stop");
-            abort();
-        }
+static void mshv_clock_vm_state_change(void *opaque, bool running,
+                                       RunState state)
+{
+    MshvClockState *s = opaque;
+    int vm_fd = mshv_state->vm;
+    int ret;
 
-        s->ref_time_pending = true;
+    if (running) {
+        return;
     }
+
+    /* Ensure the time if frozen before we capture the reference time. */
+    ret = ensure_time_frozen(vm_fd);
+    if (ret < 0) {
+        error_report("Failed to freeze partition time on stop");
+        abort();
+    }
+
+    /* Skip if we already have a to-be-set ref time */
+    if (s->ref_time_pending) {
+        return;
+    }
+
+    ret = mshv_get_reference_time(vm_fd, &s->ref_time);
+    if (ret < 0) {
+        error_report("Failed to capture reference time on stop");
+        abort();
+    }
+
+    s->ref_time_pending = true;
 }
 
 /*
@@ -182,8 +216,19 @@ static const VMStateDescription vmstate_mshv_clock = {
     },
 };
 
-void mshv_clock_init(void)
+int mshv_clock_init(int vm_fd)
 {
+    int ret;
+
+    ret = mshv_set_time_freeze(vm_fd, 1);
+    if (ret < 0) {
+        error_report("Failed to freeze partition time on init");
+        return -1;
+    }
+    mshv_clock.time_frozen = true;
+
     vmstate_register(NULL, 0, &vmstate_mshv_clock, &mshv_clock);
     qemu_add_vm_change_state_handler(mshv_clock_vm_state_change, &mshv_clock);
+
+    return 0;
 }
diff --git a/include/system/mshv.h b/include/system/mshv.h
index f98ddff9b5..864f2aa34d 100644
--- a/include/system/mshv.h
+++ b/include/system/mshv.h
@@ -58,7 +58,9 @@ DECLARE_INSTANCE_CHECKER(MshvState, MSHV_STATE,
 extern MshvState *mshv_state;
 
 /* clock (partition reference time) */
-void mshv_clock_init(void);
+int mshv_clock_init(int vm_fd);
+int mshv_clock_prepare_resume(int vm_fd);
+int mshv_clock_finish_resume(int vm_fd);
 
 /* interrupt */
 int mshv_request_interrupt(MshvState *mshv_state, uint32_t interrupt_type, uint32_t vector,
-- 
2.34.1



^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-11-11 19:37 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-06 14:39 [PATCH] accel/mshv: freeze time during TSC MSR write Magnus Kulke
2026-11-09 16:22 ` Doru Blânzeanu
2026-11-11 19:36   ` Magnus Kulke

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.