Commit faf92218ec for qemu.org
commit faf92218ec4b643b63e1ebbdc11b201afee1eee2
Author: Magnus Kulke <magnuskulke@linux.microsoft.com>
Date: Fri Nov 6 15:39:28 2026 +0100
accel/mshv: freeze time during TSC MSR write
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>
Reviewed-by: Doru Blânzeanu <dblanzeanu@linux.microsoft.com>
diff --git a/accel/mshv/mshv-all.c b/accel/mshv/mshv-all.c
index be3abb0824..5519768cc2 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"
@@ -579,7 +580,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;
}
@@ -928,6 +932,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) {
@@ -963,6 +993,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..50c9aa6417 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,88 @@ 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)
+static int ensure_time_frozen(int vm_fd)
{
- MshvClockState *s = opaque;
- int vm_fd = mshv_state->vm;
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 +219,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,