Skip to contentSkip to conversation

improve kvmclock difference on migration

PATCH Superseded Marcelo Tosatti 3 participants 18 messages Nov 21, 2016 – Dec 6, 2016 KVM

v3 of 4 Superseded Latest is v4 →

lore
Share & tools
lore
#0 lore raw

v3 cover improve kvmclock difference on migration

See individual patches for details. This patchset depends on kernels "[PATCH] kvm: kvmclock: let KVM_GET_CLOCK return whether the master clock is in use" from Paolo.

#1 lore raw

PATCH v3 1/2 kvm: sync linux headers

Import KVM_CLOCK_TSC_STABLE.

include/standard-headers/linux/input.h +1 -0
index 7361a16..b472b85 100644
@@ -245,6 +245,7 @@ struct input_mask {
 #define BUS_SPI			0x1C
 #define BUS_RMI			0x1D
 #define BUS_CEC			0x1E
+#define BUS_INTEL_ISHTP		0x1F
 
 /*
  * MT_TOOL types
include/standard-headers/linux/pci_regs.h +14 -1
index 4040951..e5a2e68 100644
@@ -612,6 +612,8 @@
  */
 #define PCI_EXP_DEVCAP2		36	/* Device Capabilities 2 */
 #define  PCI_EXP_DEVCAP2_ARI		0x00000020 /* Alternative Routing-ID */
+#define  PCI_EXP_DEVCAP2_ATOMIC_ROUTE	0x00000040 /* Atomic Op routing */
+#define PCI_EXP_DEVCAP2_ATOMIC_COMP64	0x00000100 /* Atomic 64-bit compare */
 #define  PCI_EXP_DEVCAP2_LTR		0x00000800 /* Latency tolerance reporting */
 #define  PCI_EXP_DEVCAP2_OBFF_MASK	0x000c0000 /* OBFF support mechanism */
 #define  PCI_EXP_DEVCAP2_OBFF_MSG	0x00040000 /* New message signaling */
@@ -619,6 +621,7 @@
 #define PCI_EXP_DEVCTL2		40	/* Device Control 2 */
 #define  PCI_EXP_DEVCTL2_COMP_TIMEOUT	0x000f	/* Completion Timeout Value */
 #define  PCI_EXP_DEVCTL2_ARI		0x0020	/* Alternative Routing-ID */
+#define PCI_EXP_DEVCTL2_ATOMIC_REQ	0x0040	/* Set Atomic requests */
 #define  PCI_EXP_DEVCTL2_IDO_REQ_EN	0x0100	/* Allow IDO for requests */
 #define  PCI_EXP_DEVCTL2_IDO_CMP_EN	0x0200	/* Allow IDO for completions */
 #define  PCI_EXP_DEVCTL2_LTR_EN		0x0400	/* Enable LTR mechanism */
@@ -671,7 +674,8 @@
 #define PCI_EXT_CAP_ID_PMUX	0x1A	/* Protocol Multiplexing */
 #define PCI_EXT_CAP_ID_PASID	0x1B	/* Process Address Space ID */
 #define PCI_EXT_CAP_ID_DPC	0x1D	/* Downstream Port Containment */
-#define PCI_EXT_CAP_ID_MAX	PCI_EXT_CAP_ID_DPC
+#define PCI_EXT_CAP_ID_PTM	0x1F	/* Precision Time Measurement */
+#define PCI_EXT_CAP_ID_MAX	PCI_EXT_CAP_ID_PTM
 
 #define PCI_EXT_CAP_DSN_SIZEOF	12
 #define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40
@@ -964,4 +968,13 @@
 
 #define PCI_EXP_DPC_SOURCE_ID		10	/* DPC Source Identifier */
 
+/* Precision Time Measurement */
+#define PCI_PTM_CAP			0x04	    /* PTM Capability */
+#define  PCI_PTM_CAP_REQ		0x00000001  /* Requester capable */
+#define  PCI_PTM_CAP_ROOT		0x00000004  /* Root capable */
+#define  PCI_PTM_GRANULARITY_MASK	0x0000FF00  /* Clock granularity */
+#define PCI_PTM_CTRL			0x08	    /* PTM Control */
+#define  PCI_PTM_CTRL_ENABLE		0x00000001  /* PTM enable */
+#define  PCI_PTM_CTRL_ROOT		0x00000002  /* Root select */
+
 #endif /* LINUX_PCI_REGS_H */
linux-headers/asm-arm/kvm.h +7 -0
index 541268c..2fb7859 100644
@@ -84,6 +84,13 @@ struct kvm_regs {
 #define KVM_VGIC_V2_DIST_SIZE		0x1000
 #define KVM_VGIC_V2_CPU_SIZE		0x2000
 
+/* Supported VGICv3 address types  */
+#define KVM_VGIC_V3_ADDR_TYPE_DIST	2
+#define KVM_VGIC_V3_ADDR_TYPE_REDIST	3
+
+#define KVM_VGIC_V3_DIST_SIZE		SZ_64K
+#define KVM_VGIC_V3_REDIST_SIZE		(2 * SZ_64K)
+
 #define KVM_ARM_VCPU_POWER_OFF		0 /* CPU is started in OFF state */
 #define KVM_ARM_VCPU_PSCI_0_2		1 /* CPU uses PSCI v0.2 */
 
linux-headers/asm-x86/unistd_32.h +3 -0
index abeaf40..d45ea28 100644
@@ -377,5 +377,8 @@
 #define __NR_copy_file_range 377
 #define __NR_preadv2 378
 #define __NR_pwritev2 379
+#define __NR_pkey_mprotect 380
+#define __NR_pkey_alloc 381
+#define __NR_pkey_free 382
 
 #endif /* _ASM_X86_UNISTD_32_H */
linux-headers/asm-x86/unistd_64.h +3 -0
index 73c3d1f..e22db91 100644
@@ -330,5 +330,8 @@
 #define __NR_copy_file_range 326
 #define __NR_preadv2 327
 #define __NR_pwritev2 328
+#define __NR_pkey_mprotect 329
+#define __NR_pkey_alloc 330
+#define __NR_pkey_free 331
 
 #endif /* _ASM_X86_UNISTD_64_H */
linux-headers/asm-x86/unistd_x32.h +3 -0
index e5aea76..84e58b2 100644
@@ -283,6 +283,9 @@
 #define __NR_membarrier (__X32_SYSCALL_BIT + 324)
 #define __NR_mlock2 (__X32_SYSCALL_BIT + 325)
 #define __NR_copy_file_range (__X32_SYSCALL_BIT + 326)
+#define __NR_pkey_mprotect (__X32_SYSCALL_BIT + 329)
+#define __NR_pkey_alloc (__X32_SYSCALL_BIT + 330)
+#define __NR_pkey_free (__X32_SYSCALL_BIT + 331)
 #define __NR_rt_sigaction (__X32_SYSCALL_BIT + 512)
 #define __NR_rt_sigreturn (__X32_SYSCALL_BIT + 513)
 #define __NR_ioctl (__X32_SYSCALL_BIT + 514)
linux-headers/linux/kvm.h +7 -0
index 4806e06..bb0ed71 100644
@@ -972,12 +972,19 @@ struct kvm_irqfd {
 	__u8  pad[16];
 };
 
+/* For KVM_CAP_ADJUST_CLOCK */
+
+/* Do not use 1, KVM_CHECK_EXTENSION returned it before we had flags.  */
+#define KVM_CLOCK_TSC_STABLE		2
+
 struct kvm_clock_data {
 	__u64 clock;
 	__u32 flags;
 	__u32 pad[9];
 };
 
+/* For KVM_CAP_SW_TLB */
+
 #define KVM_MMU_FSL_BOOKE_NOHV		0
 #define KVM_MMU_FSL_BOOKE_HV		1
 
#2 lore raw

PATCH v3 2/2 kvmclock: reduce kvmclock difference on migration

In reply to Marcelo Tosatti

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |  107 ++++++++++++++++++++++++++++++++++++++++++-------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 106 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

hw/i386/kvm/clock.c +93 -14
Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================
@@ -36,6 +36,12 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine supports reliable KVM_GET_CLOCK */
+    bool mach_use_reliable_get_clock;
+
+    /* whether source host supported reliable KVM_GET_CLOCK */
+    bool src_use_reliable_get_clock;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +87,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +110,36 @@
 
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /* local (running VM) restore */
+        if (s->clock_valid) {
+            /*
+             * if host does not support reliable KVM_GET_CLOCK,
+             * read kvmclock value from memory
+             */
+            if (!kvm_has_adjust_clock_stable()) {
+                pvclock_via_mem = kvmclock_current_nsec(s);
+            }
+        /* migration/savevm/init restore */
+        } else {
+            /*
+             * use s->clock in case machine uses reliable
+             * get clock and source host supported
+             * reliable get clock
+             */
+            if (!s->src_use_reliable_get_clock) {
+                pvclock_via_mem = kvmclock_current_nsec(s);
+            }
+        }
 
-        /* We can't rely on the migrated clock value, just discard it */
-        if (time_at_migration) {
-            s->clock = time_at_migration;
+        /* We can't rely on the saved clock value, just discard it */
+        if (pvclock_via_mem) {
+            s->clock = pvclock_via_mem;
         }
 
+        s->clock_valid = false;
+
         data.clock = s->clock;
         ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
         if (ret < 0) {
@@ -120,8 +160,6 @@
             }
         }
     } else {
-        struct kvm_clock_data data;
-        int ret;
 
         if (s->clock_valid) {
             return;
@@ -129,13 +167,7 @@
 
         kvm_synchronize_all_tsc();
 
-        ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
-        if (ret < 0) {
-            fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
-            abort();
-        }
-        s->clock = data.clock;
-
+        s->clock = kvm_get_clock();
         /*
          * If the VM is stopped, declare the clock state valid to
          * avoid re-reading it on next vmsave (which would return
@@ -149,25 +181,72 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
+    /*
+     * On machine types that support reliable KVM_GET_CLOCK,
+     * if host kernel does provide reliable KVM_GET_CLOCK,
+     * set src_use_reliable_get_clock=true so that destination
+     * avoids reading kvmclock from memory.
+     */
+    if (s->mach_use_reliable_get_clock && kvm_has_adjust_clock_stable()) {
+        s->src_use_reliable_get_clock = true;
+    }
+
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
+static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    return s->mach_use_reliable_get_clock;
+}
+
+static const VMStateDescription kvmclock_reliable_get_clock = {
+    .name = "kvmclock/src_use_reliable_get_clock",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = kvmclock_src_use_reliable_get_clock,
+    .fields = (VMStateField[]) {
+        VMSTATE_BOOL(src_use_reliable_get_clock, KVMClockState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void kvmclock_pre_save(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    s->clock = kvm_get_clock();
+}
+
 static const VMStateDescription kvmclock_vmsd = {
     .name = "kvmclock",
     .version_id = 1,
     .minimum_version_id = 1,
+    .pre_save = kvmclock_pre_save,
     .fields = (VMStateField[]) {
         VMSTATE_UINT64(clock, KVMClockState),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * []) {
+        &kvmclock_reliable_get_clock,
+        NULL
     }
 };
 
+static Property kvmclock_properties[] = {
+    DEFINE_PROP_BOOL("x-mach-use-reliable-get-clock", KVMClockState,
+                      mach_use_reliable_get_clock, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void kvmclock_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->realize = kvmclock_realize;
     dc->vmsd = &kvmclock_vmsd;
+    dc->props = kvmclock_properties;
 }
 
 static const TypeInfo kvmclock_info = {
include/hw/i386/pc.h +5 -0
Index: qemu-mig-advance-clock/include/hw/i386/pc.h
===================================================================
@@ -370,6 +370,11 @@
 #define PC_COMPAT_2_7 \
     HW_COMPAT_2_7 \
     {\
+        .driver   = "kvmclock",\
+        .property = "x-mach-use-reliable-get-clock",\
+        .value    = "off",\
+    },\
+    {\
         .driver   = TYPE_X86_CPU,\
         .property = "l3-cache",\
         .value    = "off",\
target-i386/kvm.c +7 -0
Index: qemu-mig-advance-clock/target-i386/kvm.c
===================================================================
@@ -117,6 +117,13 @@
     return kvm_check_extension(kvm_state, KVM_CAP_X86_SMM);
 }
 
+bool kvm_has_adjust_clock_stable(void)
+{
+    int ret = kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK);
+
+    return (ret == KVM_CLOCK_TSC_STABLE);
+}
+
 bool kvm_allows_irq0_override(void)
 {
     return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing();
target-i386/kvm_i386.h +1 -0
Index: qemu-mig-advance-clock/target-i386/kvm_i386.h
===================================================================
@@ -17,6 +17,7 @@
 
 bool kvm_allows_irq0_override(void);
 bool kvm_has_smm(void);
+bool kvm_has_adjust_clock_stable(void);
 void kvm_synchronize_all_tsc(void);
 void kvm_arch_reset_vcpu(X86CPU *cs);
 void kvm_arch_do_init_vcpu(X86CPU *cs);
#3 lore raw

Re: [qemu patch V3 2/2] kvmclock: reduce kvmclock difference on migration

Sorry for not noticing the following issues on the previous reviews. I was only paying attention to the vmstate and machine code:

hw/i386/kvm/clock.c

Quoted 66 lines · Marcelo Tosatti

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |  107 ++++++++++++++++++++++++++++++++++++++++++-------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 106 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +19 -0
@@ -36,6 +36,12 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine supports reliable KVM_GET_CLOCK */
+    bool mach_use_reliable_get_clock;
+
+    /* whether source host supported reliable KVM_GET_CLOCK */
+    bool src_use_reliable_get_clock;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +87,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +110,36 @@

Can you please use "diff -p" on your patches?

Quoted 4 lines
hw/i386/kvm/clock.c +0 -1
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);

hw/i386/kvm/clock.c line 121

Quoted 10 lines
hw/i386/kvm/clock.c +8 -1
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /* local (running VM) restore */
+        if (s->clock_valid) {
+            /*
+             * if host does not support reliable KVM_GET_CLOCK,
+             * read kvmclock value from memory
+             */
+            if (!kvm_has_adjust_clock_stable()) {

Why not use src_use_reliable_get_clock here, too? (Maybe rename it to simply clock_is_reliable?)

You can change kvm_get_clock() to kvm_get_clock(KVMClockState*), and make it update both s->clock and s->clock_is_reliable. With this, s->clock and s->clock_is_reliable will be always in sync, and have a single code path for both local restore and migration:

    /*
     * If host that set s->clock did not support reliable
     * KVM_GET_CLOCK, read kvmclock value from memory
     */
    if (!s->clock_is_reliable) {
        pvclock_via_mem = kvmclock_current_nsec(s);
    }

Updating the table from my previous message, the result would be:

--------+------------+---------------+-----------+---------+

        | kvm_has_adj_clock_stable() | clock_is_reliable   |
machine | src        | dst           | src       | dst (*) |
--------+------------+---------------+-----------+---------+
pc-2.8  | false      | false         | false     | false   |
pc-2.8  | false      | true          | false     | false   |
pc-2.8  | true       | false         | true      | true    |
pc-2.8  | true       | true          | true      | true    |
--------+------------+---------------+-----------+---------+
pc-2.7  | false      | (any)         | false     | false   |
pc-2.7  | true       | (any)         | true (**) | false   |

--------+------------+---------------+-----------+---------+

(*) More precisely: in the first kvmclock_vm_state_change() call
just after migration. It will get updated the next time s->clock is set.

(**) On purpose, so the code I suggest above would work for local
restore, too.

Quoted 81 lines
hw/i386/kvm/clock.c +42 -12
+                pvclock_via_mem = kvmclock_current_nsec(s);
+            }
+        /* migration/savevm/init restore */
+        } else {
+            /*
+             * use s->clock in case machine uses reliable
+             * get clock and source host supported
+             * reliable get clock
+             */
+            if (!s->src_use_reliable_get_clock) {
+                pvclock_via_mem = kvmclock_current_nsec(s);
+            }
+        }
 
-        /* We can't rely on the migrated clock value, just discard it */
-        if (time_at_migration) {
-            s->clock = time_at_migration;
+        /* We can't rely on the saved clock value, just discard it */
+        if (pvclock_via_mem) {
+            s->clock = pvclock_via_mem;
         }
 
+        s->clock_valid = false;
+
         data.clock = s->clock;
         ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
         if (ret < 0) {
@@ -120,8 +160,6 @@
             }
         }
     } else {
-        struct kvm_clock_data data;
-        int ret;
 
         if (s->clock_valid) {
             return;
@@ -129,13 +167,7 @@
 
         kvm_synchronize_all_tsc();
 
-        ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
-        if (ret < 0) {
-            fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
-            abort();
-        }
-        s->clock = data.clock;
-
+        s->clock = kvm_get_clock();
         /*
          * If the VM is stopped, declare the clock state valid to
          * avoid re-reading it on next vmsave (which would return
@@ -149,25 +181,72 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
+    /*
+     * On machine types that support reliable KVM_GET_CLOCK,
+     * if host kernel does provide reliable KVM_GET_CLOCK,
+     * set src_use_reliable_get_clock=true so that destination
+     * avoids reading kvmclock from memory.
+     */
+    if (s->mach_use_reliable_get_clock && kvm_has_adjust_clock_stable()) {
+        s->src_use_reliable_get_clock = true;
+    }
+
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
+static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    return s->mach_use_reliable_get_clock;
+}
+
+static const VMStateDescription kvmclock_reliable_get_clock = {
+    .name = "kvmclock/src_use_reliable_get_clock",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = kvmclock_src_use_reliable_get_clock,
+    .fields = (VMStateField[]) {

hw/i386/kvm/clock.c line 219

Quoted 10 lines
hw/i386/kvm/clock.c +10 -0
+        VMSTATE_BOOL(src_use_reliable_get_clock, KVMClockState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void kvmclock_pre_save(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    s->clock = kvm_get_clock();

I can't tell if s->clock_valid here is a bug, or a feature. In either case, we seem to be contradicting the comment at kvmclock_vm_state_change():

    /*
     * If the VM is stopped, declare the clock state valid to
     * avoid re-reading it on next vmsave (which would return
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     * a different value). Will be reset when the VM is continued.
     */
    s->clock_valid = true;
Quoted 81 lines
hw/i386/kvm/clock.c +14 -0
+}
+
 static const VMStateDescription kvmclock_vmsd = {
     .name = "kvmclock",
     .version_id = 1,
     .minimum_version_id = 1,
+    .pre_save = kvmclock_pre_save,
     .fields = (VMStateField[]) {
         VMSTATE_UINT64(clock, KVMClockState),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * []) {
+        &kvmclock_reliable_get_clock,
+        NULL
     }
 };
 
+static Property kvmclock_properties[] = {
+    DEFINE_PROP_BOOL("x-mach-use-reliable-get-clock", KVMClockState,
+                      mach_use_reliable_get_clock, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void kvmclock_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->realize = kvmclock_realize;
     dc->vmsd = &kvmclock_vmsd;
+    dc->props = kvmclock_properties;
 }
 
 static const TypeInfo kvmclock_info = {

Index: qemu-mig-advance-clock/include/hw/i386/pc.h
===================================================================

include/hw/i386/pc.h +5 -0
@@ -370,6 +370,11 @@
 #define PC_COMPAT_2_7 \
     HW_COMPAT_2_7 \
     {\
+        .driver   = "kvmclock",\
+        .property = "x-mach-use-reliable-get-clock",\
+        .value    = "off",\
+    },\
+    {\
         .driver   = TYPE_X86_CPU,\
         .property = "l3-cache",\
         .value    = "off",\

Index: qemu-mig-advance-clock/target-i386/kvm.c
===================================================================

target-i386/kvm.c +7 -0
@@ -117,6 +117,13 @@
     return kvm_check_extension(kvm_state, KVM_CAP_X86_SMM);
 }
 
+bool kvm_has_adjust_clock_stable(void)
+{
+    int ret = kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK);
+
+    return (ret == KVM_CLOCK_TSC_STABLE);
+}
+
 bool kvm_allows_irq0_override(void)
 {
     return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing();

Index: qemu-mig-advance-clock/target-i386/kvm_i386.h
===================================================================

target-i386/kvm_i386.h +1 -0
@@ -17,6 +17,7 @@
 
 bool kvm_allows_irq0_override(void);
 bool kvm_has_smm(void);
+bool kvm_has_adjust_clock_stable(void);
 void kvm_synchronize_all_tsc(void);
 void kvm_arch_reset_vcpu(X86CPU *cs);
 void kvm_arch_do_init_vcpu(X86CPU *cs);
Signature · 1 line
Eduardo
#4 lore raw

Re: [qemu patch V3 2/2] kvmclock: reduce kvmclock difference on migration

Quoted 89 lines · Eduardo Habkost

Sorry for not noticing the following issues on the previous reviews. I was only paying attention to the vmstate and machine code:

hw/i386/kvm/clock.c

Quoted 66 lines · Marcelo Tosatti

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |  107 ++++++++++++++++++++++++++++++++++++++++++-------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 106 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +19 -0
@@ -36,6 +36,12 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine supports reliable KVM_GET_CLOCK */
+    bool mach_use_reliable_get_clock;
+
+    /* whether source host supported reliable KVM_GET_CLOCK */
+    bool src_use_reliable_get_clock;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +87,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +110,36 @@

Can you please use "diff -p" on your patches?

Quoted 4 lines
hw/i386/kvm/clock.c +0 -1
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);

hw/i386/kvm/clock.c line 121

Quoted 10 lines
hw/i386/kvm/clock.c +8 -1
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /* local (running VM) restore */
+        if (s->clock_valid) {
+            /*
+             * if host does not support reliable KVM_GET_CLOCK,
+             * read kvmclock value from memory
+             */
+            if (!kvm_has_adjust_clock_stable()) {
Quoted 2 lines

Why not use src_use_reliable_get_clock here, too? (Maybe rename it to simply clock_is_reliable?)

Because you end up mixing two mental ideas (one: whether the source has KVM_GET_CLOCK, second: whether the destination has KVM_GET_CLOCK into one variable). I find it more confusing than not. Maybe its just my limited brain but i find it
confusing.

Quoted 4 lines

You can change kvm_get_clock() to kvm_get_clock(KVMClockState*), and make it update both s->clock and s->clock_is_reliable. With this, s->clock and s->clock_is_reliable will be always in sync, and have a single code path for both local restore and migration:

There should not be a single path for migration/runtime cases:
on migration, we care whether the source used reliable KVM_GET_CLOCK.

On runtime, we care whether the destination uses reliable KVM_GET_CLOCK.

Quoted 127 lines
    /*
     * If host that set s->clock did not support reliable
     * KVM_GET_CLOCK, read kvmclock value from memory
     */
    if (!s->clock_is_reliable) {
        pvclock_via_mem = kvmclock_current_nsec(s);
    }

Updating the table from my previous message, the result would be:

--------+------------+---------------+-----------+---------+

        | kvm_has_adj_clock_stable() | clock_is_reliable   |
machine | src        | dst           | src       | dst (*) |
--------+------------+---------------+-----------+---------+
pc-2.8  | false      | false         | false     | false   |
pc-2.8  | false      | true          | false     | false   |
pc-2.8  | true       | false         | true      | true    |
pc-2.8  | true       | true          | true      | true    |
--------+------------+---------------+-----------+---------+
pc-2.7  | false      | (any)         | false     | false   |
pc-2.7  | true       | (any)         | true (**) | false   |

--------+------------+---------------+-----------+---------+

(*) More precisely: in the first kvmclock_vm_state_change() call
just after migration. It will get updated the next time s->clock is set.

(**) On purpose, so the code I suggest above would work for local
restore, too.

Quoted 81 lines
hw/i386/kvm/clock.c +42 -12
+                pvclock_via_mem = kvmclock_current_nsec(s);
+            }
+        /* migration/savevm/init restore */
+        } else {
+            /*
+             * use s->clock in case machine uses reliable
+             * get clock and source host supported
+             * reliable get clock
+             */
+            if (!s->src_use_reliable_get_clock) {
+                pvclock_via_mem = kvmclock_current_nsec(s);
+            }
+        }
 
-        /* We can't rely on the migrated clock value, just discard it */
-        if (time_at_migration) {
-            s->clock = time_at_migration;
+        /* We can't rely on the saved clock value, just discard it */
+        if (pvclock_via_mem) {
+            s->clock = pvclock_via_mem;
         }
 
+        s->clock_valid = false;
+
         data.clock = s->clock;
         ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
         if (ret < 0) {
@@ -120,8 +160,6 @@
             }
         }
     } else {
-        struct kvm_clock_data data;
-        int ret;
 
         if (s->clock_valid) {
             return;
@@ -129,13 +167,7 @@
 
         kvm_synchronize_all_tsc();
 
-        ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
-        if (ret < 0) {
-            fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
-            abort();
-        }
-        s->clock = data.clock;
-
+        s->clock = kvm_get_clock();
         /*
          * If the VM is stopped, declare the clock state valid to
          * avoid re-reading it on next vmsave (which would return
@@ -149,25 +181,72 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
+    /*
+     * On machine types that support reliable KVM_GET_CLOCK,
+     * if host kernel does provide reliable KVM_GET_CLOCK,
+     * set src_use_reliable_get_clock=true so that destination
+     * avoids reading kvmclock from memory.
+     */
+    if (s->mach_use_reliable_get_clock && kvm_has_adjust_clock_stable()) {
+        s->src_use_reliable_get_clock = true;
+    }
+
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
+static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    return s->mach_use_reliable_get_clock;
+}
+
+static const VMStateDescription kvmclock_reliable_get_clock = {
+    .name = "kvmclock/src_use_reliable_get_clock",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = kvmclock_src_use_reliable_get_clock,
+    .fields = (VMStateField[]) {

hw/i386/kvm/clock.c line 219

Quoted 10 lines
hw/i386/kvm/clock.c +10 -0
+        VMSTATE_BOOL(src_use_reliable_get_clock, KVMClockState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void kvmclock_pre_save(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    s->clock = kvm_get_clock();

I can't tell if s->clock_valid here is a bug, or a feature. In either case, we seem to be contradicting the comment at kvmclock_vm_state_change():

Quoted 7 lines
    /*
     * If the VM is stopped, declare the clock state valid to
     * avoid re-reading it on next vmsave (which would return
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     * a different value). Will be reset when the VM is continued.
     */
    s->clock_valid = true;

Sorry, what is the problem??

Quoted 85 lines
Quoted 82 lines
hw/i386/kvm/clock.c +14 -0
+}
+
 static const VMStateDescription kvmclock_vmsd = {
     .name = "kvmclock",
     .version_id = 1,
     .minimum_version_id = 1,
+    .pre_save = kvmclock_pre_save,
     .fields = (VMStateField[]) {
         VMSTATE_UINT64(clock, KVMClockState),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * []) {
+        &kvmclock_reliable_get_clock,
+        NULL
     }
 };
 
+static Property kvmclock_properties[] = {
+    DEFINE_PROP_BOOL("x-mach-use-reliable-get-clock", KVMClockState,
+                      mach_use_reliable_get_clock, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void kvmclock_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->realize = kvmclock_realize;
     dc->vmsd = &kvmclock_vmsd;
+    dc->props = kvmclock_properties;
 }
 
 static const TypeInfo kvmclock_info = {

Index: qemu-mig-advance-clock/include/hw/i386/pc.h
===================================================================

include/hw/i386/pc.h +5 -0
@@ -370,6 +370,11 @@
 #define PC_COMPAT_2_7 \
     HW_COMPAT_2_7 \
     {\
+        .driver   = "kvmclock",\
+        .property = "x-mach-use-reliable-get-clock",\
+        .value    = "off",\
+    },\
+    {\
         .driver   = TYPE_X86_CPU,\
         .property = "l3-cache",\
         .value    = "off",\

Index: qemu-mig-advance-clock/target-i386/kvm.c
===================================================================

target-i386/kvm.c +7 -0
@@ -117,6 +117,13 @@
     return kvm_check_extension(kvm_state, KVM_CAP_X86_SMM);
 }
 
+bool kvm_has_adjust_clock_stable(void)
+{
+    int ret = kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK);
+
+    return (ret == KVM_CLOCK_TSC_STABLE);
+}
+
 bool kvm_allows_irq0_override(void)
 {
     return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing();

Index: qemu-mig-advance-clock/target-i386/kvm_i386.h
===================================================================

target-i386/kvm_i386.h +1 -0
@@ -17,6 +17,7 @@
 
 bool kvm_allows_irq0_override(void);
 bool kvm_has_smm(void);
+bool kvm_has_adjust_clock_stable(void);
 void kvm_synchronize_all_tsc(void);
 void kvm_arch_reset_vcpu(X86CPU *cs);
 void kvm_arch_do_init_vcpu(X86CPU *cs);
Signature · 1 line
Eduardo
#5 lore raw

Re: [qemu patch V3 2/2] kvmclock: reduce kvmclock difference on migration

Quoted 90 lines · Marcelo Tosatti
Quoted 89 lines · Eduardo Habkost

Sorry for not noticing the following issues on the previous reviews. I was only paying attention to the vmstate and machine code:

hw/i386/kvm/clock.c

Quoted 66 lines · Marcelo Tosatti

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |  107 ++++++++++++++++++++++++++++++++++++++++++-------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 106 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +19 -0
@@ -36,6 +36,12 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine supports reliable KVM_GET_CLOCK */
+    bool mach_use_reliable_get_clock;
+
+    /* whether source host supported reliable KVM_GET_CLOCK */
+    bool src_use_reliable_get_clock;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +87,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +110,36 @@

Can you please use "diff -p" on your patches?

Quoted 14 lines
hw/i386/kvm/clock.c +8 -2
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /* local (running VM) restore */
+        if (s->clock_valid) {
+            /*
+             * if host does not support reliable KVM_GET_CLOCK,
+             * read kvmclock value from memory
+             */
+            if (!kvm_has_adjust_clock_stable()) {
Quoted 8 lines
Quoted 2 lines

Why not use src_use_reliable_get_clock here, too? (Maybe rename it to simply clock_is_reliable?)

Because you end up mixing two mental ideas (one: whether the source has KVM_GET_CLOCK, second: whether the destination has KVM_GET_CLOCK into one variable). I find it more confusing than not. Maybe its just my limited brain but i find it
confusing.

I find it simpler and easier to reason about.

Quoted 10 lines
Quoted 5 lines

You can change kvm_get_clock() to kvm_get_clock(KVMClockState*), and make it update both s->clock and s->clock_is_reliable. With this, s->clock and s->clock_is_reliable will be always in sync, and have a single code path for both local restore and migration:

There should not be a single path for migration/runtime cases:
on migration, we care whether the source used reliable KVM_GET_CLOCK.

On runtime, we care whether the destination uses reliable KVM_GET_CLOCK.

The conditions may be different, but both can be translated to a "should we trust the value in s->clock" flag, to simplify the code (just like Paolo's suggestion on his reply to v1 today).

Quoted 136 lines
Quoted 128 lines
    /*
     * If host that set s->clock did not support reliable
     * KVM_GET_CLOCK, read kvmclock value from memory
     */
    if (!s->clock_is_reliable) {
        pvclock_via_mem = kvmclock_current_nsec(s);
    }

Updating the table from my previous message, the result would be:

--------+------------+---------------+-----------+---------+

        | kvm_has_adj_clock_stable() | clock_is_reliable   |
machine | src        | dst           | src       | dst (*) |
--------+------------+---------------+-----------+---------+
pc-2.8  | false      | false         | false     | false   |
pc-2.8  | false      | true          | false     | false   |
pc-2.8  | true       | false         | true      | true    |
pc-2.8  | true       | true          | true      | true    |
--------+------------+---------------+-----------+---------+
pc-2.7  | false      | (any)         | false     | false   |
pc-2.7  | true       | (any)         | true (**) | false   |

--------+------------+---------------+-----------+---------+

(*) More precisely: in the first kvmclock_vm_state_change() call
just after migration. It will get updated the next time s->clock is set.

(**) On purpose, so the code I suggest above would work for local
restore, too.

Quoted 81 lines
hw/i386/kvm/clock.c +42 -12
+                pvclock_via_mem = kvmclock_current_nsec(s);
+            }
+        /* migration/savevm/init restore */
+        } else {
+            /*
+             * use s->clock in case machine uses reliable
+             * get clock and source host supported
+             * reliable get clock
+             */
+            if (!s->src_use_reliable_get_clock) {
+                pvclock_via_mem = kvmclock_current_nsec(s);
+            }
+        }
 
-        /* We can't rely on the migrated clock value, just discard it */
-        if (time_at_migration) {
-            s->clock = time_at_migration;
+        /* We can't rely on the saved clock value, just discard it */
+        if (pvclock_via_mem) {
+            s->clock = pvclock_via_mem;
         }
 
+        s->clock_valid = false;
+
         data.clock = s->clock;
         ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
         if (ret < 0) {
@@ -120,8 +160,6 @@
             }
         }
     } else {
-        struct kvm_clock_data data;
-        int ret;
 
         if (s->clock_valid) {
             return;
@@ -129,13 +167,7 @@
 
         kvm_synchronize_all_tsc();
 
-        ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
-        if (ret < 0) {
-            fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
-            abort();
-        }
-        s->clock = data.clock;
-
+        s->clock = kvm_get_clock();
         /*
          * If the VM is stopped, declare the clock state valid to
          * avoid re-reading it on next vmsave (which would return
@@ -149,25 +181,72 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
+    /*
+     * On machine types that support reliable KVM_GET_CLOCK,
+     * if host kernel does provide reliable KVM_GET_CLOCK,
+     * set src_use_reliable_get_clock=true so that destination
+     * avoids reading kvmclock from memory.
+     */
+    if (s->mach_use_reliable_get_clock && kvm_has_adjust_clock_stable()) {
+        s->src_use_reliable_get_clock = true;
+    }
+
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
+static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    return s->mach_use_reliable_get_clock;
+}
+
+static const VMStateDescription kvmclock_reliable_get_clock = {
+    .name = "kvmclock/src_use_reliable_get_clock",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = kvmclock_src_use_reliable_get_clock,
+    .fields = (VMStateField[]) {

hw/i386/kvm/clock.c line 219

Quoted 10 lines
hw/i386/kvm/clock.c +10 -0
+        VMSTATE_BOOL(src_use_reliable_get_clock, KVMClockState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void kvmclock_pre_save(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    s->clock = kvm_get_clock();

I can't tell if s->clock_valid here is a bug, or a feature. In either case, we seem to be contradicting the comment at kvmclock_vm_state_change():

Quoted 7 lines
    /*
     * If the VM is stopped, declare the clock state valid to
     * avoid re-reading it on next vmsave (which would return
       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     * a different value). Will be reset when the VM is continued.
     */
    s->clock_valid = true;
Quoted 1 line

Sorry, what is the problem??

The comment says clock_valid will avoid re-reading the clock, but kvmclock_pre_save() ignores clock_valid and will re-read it unconditionally. Either the comment is incorrect, or kvmclock_pre_save() is incorrect (I don't know which is the case).

Signature · 1 line
Eduardo
maintainer
#6 lore raw

Re: [qemu patch V3 2/2] kvmclock: reduce kvmclock difference on migration

Quoted 10 lines · Eduardo Habkost
Quoted 9 lines
Quoted 9 lines
Quoted 3 lines
Quoted 3 lines

Why not use src_use_reliable_get_clock here, too? (Maybe rename it to simply clock_is_reliable?)

Because you end up mixing two mental ideas (one: whether the source has KVM_GET_CLOCK, second: whether the destination has KVM_GET_CLOCK into one variable). I find it more confusing than not. Maybe its just my limited brain but i find it
confusing.

I find it simpler and easier to reason about.

Me too---note that it's not "whether X had reliable KVM_GET_CLOCK" but rather "whether the last read came from a reliable KVM_GET_CLOCK". However...

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
Quoted 4 lines

You can change kvm_get_clock() to kvm_get_clock(KVMClockState*), and make it update both s->clock and s->clock_is_reliable. With this, s->clock and s->clock_is_reliable will be always in sync, and have a single code path for both local restore and migration:

Quoted 7 lines
Quoted 4 lines
Quoted 4 lines

There should not be a single path for migration/runtime cases:
on migration, we care whether the source used reliable KVM_GET_CLOCK.

On runtime, we care whether the destination uses reliable KVM_GET_CLOCK.

The conditions may be different, but both can be translated to a "should we trust the value in s->clock" flag, to simplify the code (just like Paolo's suggestion on his reply to v1 today).

... I think I understand now: Marcelo is approaching the problem differently. For him it's not an issue of "should we trust the value" but rather it's "is the value going to mess up the guest with backwards time". Which is fine, it just requires some more comments because it's subtle and it looks a lot like the kernel API is being misused (while it's fine).

Paolo

#7 lore raw

Re: [qemu patch V3 2/2] kvmclock: reduce kvmclock difference on migration

In reply to Eduardo Habkost
Quoted 94 lines · Eduardo Habkost
Quoted 93 lines · Marcelo Tosatti
Quoted 91 lines · Eduardo Habkost

Sorry for not noticing the following issues on the previous reviews. I was only paying attention to the vmstate and machine code:

hw/i386/kvm/clock.c

Quoted 66 lines · Marcelo Tosatti

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |  107 ++++++++++++++++++++++++++++++++++++++++++-------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 106 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +19 -0
@@ -36,6 +36,12 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine supports reliable KVM_GET_CLOCK */
+    bool mach_use_reliable_get_clock;
+
+    /* whether source host supported reliable KVM_GET_CLOCK */
+    bool src_use_reliable_get_clock;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +87,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +110,36 @@

Can you please use "diff -p" on your patches?

Quoted 4 lines
hw/i386/kvm/clock.c +0 -1
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);

hw/i386/kvm/clock.c line 120

Quoted 10 lines
hw/i386/kvm/clock.c +8 -1
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /* local (running VM) restore */
+        if (s->clock_valid) {
+            /*
+             * if host does not support reliable KVM_GET_CLOCK,
+             * read kvmclock value from memory
+             */
+            if (!kvm_has_adjust_clock_stable()) {

Why not use src_use_reliable_get_clock here, too? (Maybe rename it to simply clock_is_reliable?)

Quoted 7 lines
Quoted 5 lines

Because you end up mixing two mental ideas (one: whether the source has KVM_GET_CLOCK, second: whether the destination has KVM_GET_CLOCK into one variable). I find it more confusing than not. Maybe its just my limited brain but i find it
confusing.

I find it simpler and easier to reason about.

I disagree, but i don't mind. Is this what you refer to:

hw/i386/kvm/clock.c +20 -34
Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================
@@ -37,11 +37,11 @@
     uint64_t clock;
     bool clock_valid;
 
-    /* whether machine supports reliable KVM_GET_CLOCK */
+    /* whether machine type supports reliable get clock */
     bool mach_use_reliable_get_clock;
 
-    /* whether source host supported reliable KVM_GET_CLOCK */
-    bool src_use_reliable_get_clock;
+    /* whether clock is reliable */
+    bool clock_is_reliable;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -112,25 +112,17 @@
         struct kvm_clock_data data = {};
         uint64_t pvclock_via_mem = 0;
 
-        /* local (running VM) restore */
-        if (s->clock_valid) {
-            /*
-             * if host does not support reliable KVM_GET_CLOCK,
-             * read kvmclock value from memory
-             */
-            if (!kvm_has_adjust_clock_stable()) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
-        /* migration/savevm/init restore */
-        } else {
-            /*
-             * use s->clock in case machine uses reliable
-             * get clock and source host supported
-             * reliable get clock
-             */
-            if (!s->src_use_reliable_get_clock) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
+
+        if (!s->clock_is_reliable) {
+            pvclock_via_mem = kvmclock_current_nsec(s);
+        }
+
+        /* migration/savevm/init restore
+         * update clock_is_reliable to match local
+         * host capabilities.
+         */
+        if (s->clock_valid == false) {
+            s->clock_is_reliable = kvm_has_adjust_clock_stable();
         }
 
         /* We can't rely on the saved clock value, just discard it */
@@ -181,20 +173,14 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
-    /*
-     * On machine types that support reliable KVM_GET_CLOCK,
-     * if host kernel does provide reliable KVM_GET_CLOCK,
-     * set src_use_reliable_get_clock=true so that destination
-     * avoids reading kvmclock from memory.
-     */
-    if (s->mach_use_reliable_get_clock && kvm_has_adjust_clock_stable()) {
-        s->src_use_reliable_get_clock = true;
+    if (kvm_has_adjust_clock_stable()) {
+        s->clock_is_reliable = true;
     }
 
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
-static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+static bool kvmclock_clock_is_reliable_needed(void *opaque)
 {
     KVMClockState *s = opaque;
 
@@ -202,12 +188,12 @@
 }
 
 static const VMStateDescription kvmclock_reliable_get_clock = {
-    .name = "kvmclock/src_use_reliable_get_clock",
+    .name = "kvmclock/clock_is_reliable",
     .version_id = 1,
     .minimum_version_id = 1,
-    .needed = kvmclock_src_use_reliable_get_clock,
+    .needed = kvmclock_clock_is_reliable_needed,
     .fields = (VMStateField[]) {
-        VMSTATE_BOOL(src_use_reliable_get_clock, KVMClockState),
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
         VMSTATE_END_OF_LIST()
     }
 };
#8 lore raw

Re: [qemu patch V3 2/2] kvmclock: reduce kvmclock difference on migration

Quoted 110 lines · Marcelo Tosatti
Quoted 94 lines · Eduardo Habkost
Quoted 93 lines · Marcelo Tosatti
Quoted 91 lines · Eduardo Habkost

Sorry for not noticing the following issues on the previous reviews. I was only paying attention to the vmstate and machine code:

hw/i386/kvm/clock.c

Quoted 66 lines · Marcelo Tosatti

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |  107 ++++++++++++++++++++++++++++++++++++++++++-------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 106 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +19 -0
@@ -36,6 +36,12 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine supports reliable KVM_GET_CLOCK */
+    bool mach_use_reliable_get_clock;
+
+    /* whether source host supported reliable KVM_GET_CLOCK */
+    bool src_use_reliable_get_clock;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +87,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +110,36 @@

Can you please use "diff -p" on your patches?

Quoted 4 lines
hw/i386/kvm/clock.c +0 -1
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);

hw/i386/kvm/clock.c line 120

Quoted 10 lines
hw/i386/kvm/clock.c +8 -1
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /* local (running VM) restore */
+        if (s->clock_valid) {
+            /*
+             * if host does not support reliable KVM_GET_CLOCK,
+             * read kvmclock value from memory
+             */
+            if (!kvm_has_adjust_clock_stable()) {

Why not use src_use_reliable_get_clock here, too? (Maybe rename it to simply clock_is_reliable?)

Quoted 7 lines
Quoted 5 lines

Because you end up mixing two mental ideas (one: whether the source has KVM_GET_CLOCK, second: whether the destination has KVM_GET_CLOCK into one variable). I find it more confusing than not. Maybe its just my limited brain but i find it
confusing.

I find it simpler and easier to reason about.

I disagree, but i don't mind. Is this what you refer to:

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c line 43

Quoted 10 lines
hw/i386/kvm/clock.c +2 -3
@@ -37,11 +37,11 @@
     uint64_t clock;
     bool clock_valid;
 
-    /* whether machine supports reliable KVM_GET_CLOCK */
+    /* whether machine type supports reliable get clock */
     bool mach_use_reliable_get_clock;
 
-    /* whether source host supported reliable KVM_GET_CLOCK */
-    bool src_use_reliable_get_clock;
+    /* whether clock is reliable */

I would make the comment more explicit about its meaning:
/* whether the 'clock' value was obtained in a host with
* reliable KVM_GET_CLOCK */

Quoted 18 lines
hw/i386/kvm/clock.c +1 -10
+    bool clock_is_reliable;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -112,25 +112,17 @@
         struct kvm_clock_data data = {};
         uint64_t pvclock_via_mem = 0;
 
-        /* local (running VM) restore */
-        if (s->clock_valid) {
-            /*
-             * if host does not support reliable KVM_GET_CLOCK,
-             * read kvmclock value from memory
-             */
-            if (!kvm_has_adjust_clock_stable()) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
-        /* migration/savevm/init restore */

hw/i386/kvm/clock.c line 115

Quoted 10 lines
hw/i386/kvm/clock.c +1 -9
-        } else {
-            /*
-             * use s->clock in case machine uses reliable
-             * get clock and source host supported
-             * reliable get clock
-             */
-            if (!s->src_use_reliable_get_clock) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
+

A comment here would be welcome. Something like: "If the host where s->clock was read did not support reliable KVM_GET_CLOCK, read kvmclock value from memory".

hw/i386/kvm/clock.c line 124

Quoted 9 lines
hw/i386/kvm/clock.c +9 -0
+        if (!s->clock_is_reliable) {
+            pvclock_via_mem = kvmclock_current_nsec(s);
+        }
+
+        /* migration/savevm/init restore
+         * update clock_is_reliable to match local
+         * host capabilities.
+         */
+        if (s->clock_valid == false) {

Why the s->clock_valid check? It seems unnecessary.

(If this is an optimization to avoid extra ioctl() calls, you can cache it inside kvm_has_adjust_clock_stable()).

hw/i386/kvm/clock.c line 126

Quoted 2 lines
hw/i386/kvm/clock.c +1 -0
+            s->clock_is_reliable = kvm_has_adjust_clock_stable();
         }

This should go to kvm_get_clock(), so s->clock and s->clock_is_reliable are guaranteed to be always in sync.

Quoted 42 lines
hw/i386/kvm/clock.c +6 -12
         /* We can't rely on the saved clock value, just discard it */
@@ -181,20 +173,14 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
-    /*
-     * On machine types that support reliable KVM_GET_CLOCK,
-     * if host kernel does provide reliable KVM_GET_CLOCK,
-     * set src_use_reliable_get_clock=true so that destination
-     * avoids reading kvmclock from memory.
-     */
-    if (s->mach_use_reliable_get_clock && kvm_has_adjust_clock_stable()) {
-        s->src_use_reliable_get_clock = true;
+    if (kvm_has_adjust_clock_stable()) {
+        s->clock_is_reliable = true;
     }
 
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
-static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+static bool kvmclock_clock_is_reliable_needed(void *opaque)
 {
     KVMClockState *s = opaque;
 
@@ -202,12 +188,12 @@
 }
 
 static const VMStateDescription kvmclock_reliable_get_clock = {
-    .name = "kvmclock/src_use_reliable_get_clock",
+    .name = "kvmclock/clock_is_reliable",
     .version_id = 1,
     .minimum_version_id = 1,
-    .needed = kvmclock_src_use_reliable_get_clock,
+    .needed = kvmclock_clock_is_reliable_needed,
     .fields = (VMStateField[]) {
-        VMSTATE_BOOL(src_use_reliable_get_clock, KVMClockState),
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
         VMSTATE_END_OF_LIST()
     }
 };
Signature · 1 line
Eduardo
#9 lore raw

Re: [qemu patch V3 2/2] kvmclock: reduce kvmclock difference on migration

Quoted 118 lines · Eduardo Habkost
Quoted 117 lines · Marcelo Tosatti
Quoted 94 lines · Eduardo Habkost
Quoted 93 lines · Marcelo Tosatti
Quoted 91 lines · Eduardo Habkost

Sorry for not noticing the following issues on the previous reviews. I was only paying attention to the vmstate and machine code:

hw/i386/kvm/clock.c

Quoted 66 lines · Marcelo Tosatti

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |  107 ++++++++++++++++++++++++++++++++++++++++++-------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 106 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +19 -0
@@ -36,6 +36,12 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine supports reliable KVM_GET_CLOCK */
+    bool mach_use_reliable_get_clock;
+
+    /* whether source host supported reliable KVM_GET_CLOCK */
+    bool src_use_reliable_get_clock;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +87,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +110,36 @@

Can you please use "diff -p" on your patches?

Quoted 4 lines
hw/i386/kvm/clock.c +0 -1
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);

hw/i386/kvm/clock.c line 120

Quoted 10 lines
hw/i386/kvm/clock.c +8 -1
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /* local (running VM) restore */
+        if (s->clock_valid) {
+            /*
+             * if host does not support reliable KVM_GET_CLOCK,
+             * read kvmclock value from memory
+             */
+            if (!kvm_has_adjust_clock_stable()) {

Why not use src_use_reliable_get_clock here, too? (Maybe rename it to simply clock_is_reliable?)

Quoted 7 lines
Quoted 5 lines

Because you end up mixing two mental ideas (one: whether the source has KVM_GET_CLOCK, second: whether the destination has KVM_GET_CLOCK into one variable). I find it more confusing than not. Maybe its just my limited brain but i find it
confusing.

I find it simpler and easier to reason about.

I disagree, but i don't mind. Is this what you refer to:

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +1 -1
@@ -37,11 +37,11 @@
     uint64_t clock;
     bool clock_valid;
 
-    /* whether machine supports reliable KVM_GET_CLOCK */
+    /* whether machine type supports reliable get clock */
     bool mach_use_reliable_get_clock;
 
Quoted 7 lines

hw/i386/kvm/clock.c line 43

Quoted 3 lines
hw/i386/kvm/clock.c +1 -2
-    /* whether source host supported reliable KVM_GET_CLOCK */
-    bool src_use_reliable_get_clock;
+    /* whether clock is reliable */

I would make the comment more explicit about its meaning:
/* whether the 'clock' value was obtained in a host with
* reliable KVM_GET_CLOCK */

Done.

Quoted 25 lines

hw/i386/kvm/clock.c line 44

Quoted 1 line
hw/i386/kvm/clock.c +1 -0
+    bool clock_is_reliable;

} KVMClockState;

Quoted 22 lines
hw/i386/kvm/clock.c +0 -16
 struct pvclock_vcpu_time_info {
@@ -112,25 +112,17 @@
         struct kvm_clock_data data = {};
         uint64_t pvclock_via_mem = 0;
 
-        /* local (running VM) restore */
-        if (s->clock_valid) {
-            /*
-             * if host does not support reliable KVM_GET_CLOCK,
-             * read kvmclock value from memory
-             */
-            if (!kvm_has_adjust_clock_stable()) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
-        /* migration/savevm/init restore */
-        } else {
-            /*
-             * use s->clock in case machine uses reliable
-             * get clock and source host supported
-             * reliable get clock
-             */
Quoted 8 lines

hw/i386/kvm/clock.c line 115

Quoted 4 lines
hw/i386/kvm/clock.c +1 -3
-            if (!s->src_use_reliable_get_clock) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
+

A comment here would be welcome. Something like: "If the host where s->clock was read did not support reliable KVM_GET_CLOCK, read kvmclock value from memory".

Done.

Quoted 11 lines

hw/i386/kvm/clock.c line 124

Quoted 9 lines
hw/i386/kvm/clock.c +9 -0
+        if (!s->clock_is_reliable) {
+            pvclock_via_mem = kvmclock_current_nsec(s);
+        }
+
+        /* migration/savevm/init restore
+         * update clock_is_reliable to match local
+         * host capabilities.
+         */
+        if (s->clock_valid == false) {

Why the s->clock_valid check? It seems unnecessary.

Because if s->clock_valid == false (from migration), kvm_has_adjust_clock_stable() from the local host is irrelevant.

If s->clock_valid == true, kvm_has_adjust_clock_stable() is what decides whether to use KVM_GET_CLOCK or not.

Quoted 2 lines

(If this is an optimization to avoid extra ioctl() calls, you can cache it inside kvm_has_adjust_clock_stable()).

Its not.

Quoted 5 lines

hw/i386/kvm/clock.c line 126

Quoted 2 lines
hw/i386/kvm/clock.c +1 -0
+            s->clock_is_reliable = kvm_has_adjust_clock_stable();
         }

This should go to kvm_get_clock(), so s->clock and s->clock_is_reliable are guaranteed to be always in sync.

No: the point to change clock_is_reliable is the following: whether its the first read of the clock (migration), or subsequent reads (normal machine operation).

Now kvm_get_clock() just reads the clock, so i don't see it as the right place to change the variable.

Agreed?

Quoted 44 lines
Quoted 41 lines
hw/i386/kvm/clock.c +6 -12
         /* We can't rely on the saved clock value, just discard it */
@@ -181,20 +173,14 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
-    /*
-     * On machine types that support reliable KVM_GET_CLOCK,
-     * if host kernel does provide reliable KVM_GET_CLOCK,
-     * set src_use_reliable_get_clock=true so that destination
-     * avoids reading kvmclock from memory.
-     */
-    if (s->mach_use_reliable_get_clock && kvm_has_adjust_clock_stable()) {
-        s->src_use_reliable_get_clock = true;
+    if (kvm_has_adjust_clock_stable()) {
+        s->clock_is_reliable = true;
     }
 
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
-static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+static bool kvmclock_clock_is_reliable_needed(void *opaque)
 {
     KVMClockState *s = opaque;
 
@@ -202,12 +188,12 @@
 }
 
 static const VMStateDescription kvmclock_reliable_get_clock = {
-    .name = "kvmclock/src_use_reliable_get_clock",
+    .name = "kvmclock/clock_is_reliable",
     .version_id = 1,
     .minimum_version_id = 1,
-    .needed = kvmclock_src_use_reliable_get_clock,
+    .needed = kvmclock_clock_is_reliable_needed,
     .fields = (VMStateField[]) {
-        VMSTATE_BOOL(src_use_reliable_get_clock, KVMClockState),
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
         VMSTATE_END_OF_LIST()
     }
 };
Signature · 1 line
Eduardo
#10 lore raw

Re: [qemu patch V3 2/2] kvmclock: reduce kvmclock difference on migration

Quoted 175 lines · Marcelo Tosatti
Quoted 118 lines · Eduardo Habkost
Quoted 117 lines · Marcelo Tosatti
Quoted 94 lines · Eduardo Habkost
Quoted 93 lines · Marcelo Tosatti
Quoted 91 lines · Eduardo Habkost

Sorry for not noticing the following issues on the previous reviews. I was only paying attention to the vmstate and machine code:

hw/i386/kvm/clock.c

Quoted 66 lines · Marcelo Tosatti

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |  107 ++++++++++++++++++++++++++++++++++++++++++-------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 106 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +19 -0
@@ -36,6 +36,12 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine supports reliable KVM_GET_CLOCK */
+    bool mach_use_reliable_get_clock;
+
+    /* whether source host supported reliable KVM_GET_CLOCK */
+    bool src_use_reliable_get_clock;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +87,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +110,36 @@

Can you please use "diff -p" on your patches?

Quoted 4 lines
hw/i386/kvm/clock.c +0 -1
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);

hw/i386/kvm/clock.c line 120

Quoted 10 lines
hw/i386/kvm/clock.c +8 -1
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /* local (running VM) restore */
+        if (s->clock_valid) {
+            /*
+             * if host does not support reliable KVM_GET_CLOCK,
+             * read kvmclock value from memory
+             */
+            if (!kvm_has_adjust_clock_stable()) {

Why not use src_use_reliable_get_clock here, too? (Maybe rename it to simply clock_is_reliable?)

Quoted 7 lines
Quoted 5 lines

Because you end up mixing two mental ideas (one: whether the source has KVM_GET_CLOCK, second: whether the destination has KVM_GET_CLOCK into one variable). I find it more confusing than not. Maybe its just my limited brain but i find it
confusing.

I find it simpler and easier to reason about.

I disagree, but i don't mind. Is this what you refer to:

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +1 -1
@@ -37,11 +37,11 @@
     uint64_t clock;
     bool clock_valid;
 
-    /* whether machine supports reliable KVM_GET_CLOCK */
+    /* whether machine type supports reliable get clock */
     bool mach_use_reliable_get_clock;
 
Quoted 7 lines

hw/i386/kvm/clock.c line 43

Quoted 3 lines
hw/i386/kvm/clock.c +1 -2
-    /* whether source host supported reliable KVM_GET_CLOCK */
-    bool src_use_reliable_get_clock;
+    /* whether clock is reliable */

I would make the comment more explicit about its meaning:
/* whether the 'clock' value was obtained in a host with
* reliable KVM_GET_CLOCK */

Done.

Quoted 25 lines

hw/i386/kvm/clock.c line 44

Quoted 1 line
hw/i386/kvm/clock.c +1 -0
+    bool clock_is_reliable;

} KVMClockState;

Quoted 22 lines
hw/i386/kvm/clock.c +0 -16
 struct pvclock_vcpu_time_info {
@@ -112,25 +112,17 @@
         struct kvm_clock_data data = {};
         uint64_t pvclock_via_mem = 0;
 
-        /* local (running VM) restore */
-        if (s->clock_valid) {
-            /*
-             * if host does not support reliable KVM_GET_CLOCK,
-             * read kvmclock value from memory
-             */
-            if (!kvm_has_adjust_clock_stable()) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
-        /* migration/savevm/init restore */
-        } else {
-            /*
-             * use s->clock in case machine uses reliable
-             * get clock and source host supported
-             * reliable get clock
-             */
Quoted 8 lines

hw/i386/kvm/clock.c line 115

Quoted 4 lines
hw/i386/kvm/clock.c +1 -3
-            if (!s->src_use_reliable_get_clock) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
+

A comment here would be welcome. Something like: "If the host where s->clock was read did not support reliable KVM_GET_CLOCK, read kvmclock value from memory".

Done.

Quoted 10 lines
Quoted 9 lines
hw/i386/kvm/clock.c +9 -0
+        if (!s->clock_is_reliable) {
+            pvclock_via_mem = kvmclock_current_nsec(s);
+        }
+
+        /* migration/savevm/init restore
+         * update clock_is_reliable to match local
+         * host capabilities.
+         */
+        if (s->clock_valid == false) {
Quoted 4 lines
Quoted 1 line

Why the s->clock_valid check? It seems unnecessary.

Because if s->clock_valid == false (from migration), kvm_has_adjust_clock_stable() from the local host is irrelevant.

That's true, and that's why updating it on kvm_get_clock() looks like a more logical place.

Quoted 15 lines

If s->clock_valid == true, kvm_has_adjust_clock_stable() is what decides whether to use KVM_GET_CLOCK or not.

Quoted 2 lines

(If this is an optimization to avoid extra ioctl() calls, you can cache it inside kvm_has_adjust_clock_stable()).

Its not.

Quoted 5 lines

hw/i386/kvm/clock.c line 126

Quoted 2 lines
hw/i386/kvm/clock.c +1 -0
+            s->clock_is_reliable = kvm_has_adjust_clock_stable();
         }

This should go to kvm_get_clock(), so s->clock and s->clock_is_reliable are guaranteed to be always in sync.

Quoted 8 lines

No: the point to change clock_is_reliable is the following: whether its the first read of the clock (migration), or subsequent reads (normal machine operation).

Now kvm_get_clock() just reads the clock, so i don't see it as the right place to change the variable.

Agreed?

I don't agree. clock_is_reliable is information about the current value in s->clock, I don't see why we need to change s->clock and s->clock_is_reliable at different places in the code.

Your code might work too, but I can't tell without reviewing very carefully the state transitions KVMClockState could go through. I don't see the point of making the state transitions in this code even more complex, if we can simply stick to a simple "s->clock and s->clock_is_reliable are always in sync" rule.

Quoted 45 lines
Quoted 45 lines
Quoted 42 lines
hw/i386/kvm/clock.c +6 -12
         /* We can't rely on the saved clock value, just discard it */
@@ -181,20 +173,14 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
-    /*
-     * On machine types that support reliable KVM_GET_CLOCK,
-     * if host kernel does provide reliable KVM_GET_CLOCK,
-     * set src_use_reliable_get_clock=true so that destination
-     * avoids reading kvmclock from memory.
-     */
-    if (s->mach_use_reliable_get_clock && kvm_has_adjust_clock_stable()) {
-        s->src_use_reliable_get_clock = true;
+    if (kvm_has_adjust_clock_stable()) {
+        s->clock_is_reliable = true;
     }
 
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
-static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+static bool kvmclock_clock_is_reliable_needed(void *opaque)
 {
     KVMClockState *s = opaque;
 
@@ -202,12 +188,12 @@
 }
 
 static const VMStateDescription kvmclock_reliable_get_clock = {
-    .name = "kvmclock/src_use_reliable_get_clock",
+    .name = "kvmclock/clock_is_reliable",
     .version_id = 1,
     .minimum_version_id = 1,
-    .needed = kvmclock_src_use_reliable_get_clock,
+    .needed = kvmclock_clock_is_reliable_needed,
     .fields = (VMStateField[]) {
-        VMSTATE_BOOL(src_use_reliable_get_clock, KVMClockState),
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
         VMSTATE_END_OF_LIST()
     }
 };
Signature · 1 line
Eduardo
Signature · 1 line
Eduardo
#11 lore raw

Re: [qemu patch V3 2/2] kvmclock: reduce kvmclock difference on migration

Quoted 176 lines · Eduardo Habkost
Quoted 175 lines · Marcelo Tosatti
Quoted 118 lines · Eduardo Habkost
Quoted 117 lines · Marcelo Tosatti
Quoted 94 lines · Eduardo Habkost
Quoted 93 lines · Marcelo Tosatti
Quoted 91 lines · Eduardo Habkost

Sorry for not noticing the following issues on the previous reviews. I was only paying attention to the vmstate and machine code:

hw/i386/kvm/clock.c

Quoted 66 lines · Marcelo Tosatti

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |  107 ++++++++++++++++++++++++++++++++++++++++++-------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 106 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +19 -0
@@ -36,6 +36,12 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine supports reliable KVM_GET_CLOCK */
+    bool mach_use_reliable_get_clock;
+
+    /* whether source host supported reliable KVM_GET_CLOCK */
+    bool src_use_reliable_get_clock;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +87,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +110,36 @@

Can you please use "diff -p" on your patches?

Quoted 4 lines
hw/i386/kvm/clock.c +0 -1
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);

hw/i386/kvm/clock.c line 120

Quoted 10 lines
hw/i386/kvm/clock.c +8 -1
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /* local (running VM) restore */
+        if (s->clock_valid) {
+            /*
+             * if host does not support reliable KVM_GET_CLOCK,
+             * read kvmclock value from memory
+             */
+            if (!kvm_has_adjust_clock_stable()) {

Why not use src_use_reliable_get_clock here, too? (Maybe rename it to simply clock_is_reliable?)

Quoted 7 lines
Quoted 5 lines

Because you end up mixing two mental ideas (one: whether the source has KVM_GET_CLOCK, second: whether the destination has KVM_GET_CLOCK into one variable). I find it more confusing than not. Maybe its just my limited brain but i find it
confusing.

I find it simpler and easier to reason about.

I disagree, but i don't mind. Is this what you refer to:

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +1 -1
@@ -37,11 +37,11 @@
     uint64_t clock;
     bool clock_valid;
 
-    /* whether machine supports reliable KVM_GET_CLOCK */
+    /* whether machine type supports reliable get clock */
     bool mach_use_reliable_get_clock;
 
Quoted 7 lines

hw/i386/kvm/clock.c line 43

Quoted 3 lines
hw/i386/kvm/clock.c +1 -2
-    /* whether source host supported reliable KVM_GET_CLOCK */
-    bool src_use_reliable_get_clock;
+    /* whether clock is reliable */

I would make the comment more explicit about its meaning:
/* whether the 'clock' value was obtained in a host with
* reliable KVM_GET_CLOCK */

Done.

Quoted 25 lines

hw/i386/kvm/clock.c line 44

Quoted 1 line
hw/i386/kvm/clock.c +1 -0
+    bool clock_is_reliable;

} KVMClockState;

Quoted 22 lines
hw/i386/kvm/clock.c +0 -16
 struct pvclock_vcpu_time_info {
@@ -112,25 +112,17 @@
         struct kvm_clock_data data = {};
         uint64_t pvclock_via_mem = 0;
 
-        /* local (running VM) restore */
-        if (s->clock_valid) {
-            /*
-             * if host does not support reliable KVM_GET_CLOCK,
-             * read kvmclock value from memory
-             */
-            if (!kvm_has_adjust_clock_stable()) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
-        /* migration/savevm/init restore */
-        } else {
-            /*
-             * use s->clock in case machine uses reliable
-             * get clock and source host supported
-             * reliable get clock
-             */
Quoted 8 lines

hw/i386/kvm/clock.c line 115

Quoted 4 lines
hw/i386/kvm/clock.c +1 -3
-            if (!s->src_use_reliable_get_clock) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
+

A comment here would be welcome. Something like: "If the host where s->clock was read did not support reliable KVM_GET_CLOCK, read kvmclock value from memory".

Done.

Quoted 10 lines
Quoted 9 lines
hw/i386/kvm/clock.c +9 -0
+        if (!s->clock_is_reliable) {
+            pvclock_via_mem = kvmclock_current_nsec(s);
+        }
+
+        /* migration/savevm/init restore
+         * update clock_is_reliable to match local
+         * host capabilities.
+         */
+        if (s->clock_valid == false) {
Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Why the s->clock_valid check? It seems unnecessary.

Because if s->clock_valid == false (from migration), kvm_has_adjust_clock_stable() from the local host is irrelevant.

That's true, and that's why updating it on kvm_get_clock() looks like a more logical place.

But the current patch updates
s->clock_is_reliable at the "if (running)"
case of kvmclock_vm_state_change().

There, both s->clock_is_reliable and s->clock are
updated in sync:

        if (s->clock_valid == false) {
            s->clock_is_reliable = kvm_has_adjust_clock_stable();
        }

        /* We can't rely on the saved clock value, just discard it */
        if (pvclock_via_mem) {
            s->clock = pvclock_via_mem;
        }

However, there is no need to call kvm_get_clock() there.

kvm_get_clock() is called at the "} else {" case.

Are you suggesting to

* move s->clock_is_reliable assignment to kvm_get_clock()
* move s->clock assignment to kvm_get_clock()
* call kvm_get_clock() from the "if (running)" because the assignments are in that function now.

But you don't need KVM_GET_CLOCK to be called there.

Quoted 29 lines
Quoted 16 lines

If s->clock_valid == true, kvm_has_adjust_clock_stable() is what decides whether to use KVM_GET_CLOCK or not.

Quoted 2 lines

(If this is an optimization to avoid extra ioctl() calls, you can cache it inside kvm_has_adjust_clock_stable()).

Its not.

Quoted 5 lines

hw/i386/kvm/clock.c line 126

Quoted 2 lines
hw/i386/kvm/clock.c +1 -0
+            s->clock_is_reliable = kvm_has_adjust_clock_stable();
         }

This should go to kvm_get_clock(), so s->clock and s->clock_is_reliable are guaranteed to be always in sync.

Quoted 8 lines

No: the point to change clock_is_reliable is the following: whether its the first read of the clock (migration), or subsequent reads (normal machine operation).

Now kvm_get_clock() just reads the clock, so i don't see it as the right place to change the variable.

Agreed?

I don't agree. clock_is_reliable is information about the current value in s->clock, I don't see why we need to change s->clock and s->clock_is_reliable at different places in the code.

Quoted 5 lines

Your code might work too, but I can't tell without reviewing very carefully the state transitions KVMClockState could go through. I don't see the point of making the state transitions in this code even more complex, if we can simply stick to a simple "s->clock and s->clock_is_reliable are always in sync" rule.

It is updating from the same place:

        /* migration/savevm/init restore
         * update clock_is_reliable to match local
         * host capabilities.
         */
        if (s->clock_valid == false) {
            s->clock_is_reliable = kvm_has_adjust_clock_stable();
        }

        /* We can't rely on the saved clock value, just discard it */
        if (pvclock_via_mem) {
            s->clock = pvclock_via_mem;
        }
Quoted 49 lines
Quoted 46 lines
Quoted 46 lines
Quoted 43 lines
hw/i386/kvm/clock.c +6 -12
         /* We can't rely on the saved clock value, just discard it */
@@ -181,20 +173,14 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
-    /*
-     * On machine types that support reliable KVM_GET_CLOCK,
-     * if host kernel does provide reliable KVM_GET_CLOCK,
-     * set src_use_reliable_get_clock=true so that destination
-     * avoids reading kvmclock from memory.
-     */
-    if (s->mach_use_reliable_get_clock && kvm_has_adjust_clock_stable()) {
-        s->src_use_reliable_get_clock = true;
+    if (kvm_has_adjust_clock_stable()) {
+        s->clock_is_reliable = true;
     }
 
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
-static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+static bool kvmclock_clock_is_reliable_needed(void *opaque)
 {
     KVMClockState *s = opaque;
 
@@ -202,12 +188,12 @@
 }
 
 static const VMStateDescription kvmclock_reliable_get_clock = {
-    .name = "kvmclock/src_use_reliable_get_clock",
+    .name = "kvmclock/clock_is_reliable",
     .version_id = 1,
     .minimum_version_id = 1,
-    .needed = kvmclock_src_use_reliable_get_clock,
+    .needed = kvmclock_clock_is_reliable_needed,
     .fields = (VMStateField[]) {
-        VMSTATE_BOOL(src_use_reliable_get_clock, KVMClockState),
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
         VMSTATE_END_OF_LIST()
     }
 };
Signature · 1 line
Eduardo
Signature · 1 line
Eduardo
#12 lore raw

Re: [qemu patch V3 2/2] kvmclock: reduce kvmclock difference on migration

Quoted 192 lines · Marcelo Tosatti
Quoted 176 lines · Eduardo Habkost
Quoted 175 lines · Marcelo Tosatti
Quoted 118 lines · Eduardo Habkost
Quoted 117 lines · Marcelo Tosatti
Quoted 94 lines · Eduardo Habkost
Quoted 93 lines · Marcelo Tosatti
Quoted 91 lines · Eduardo Habkost

Sorry for not noticing the following issues on the previous reviews. I was only paying attention to the vmstate and machine code:

hw/i386/kvm/clock.c

Quoted 66 lines · Marcelo Tosatti

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |  107 ++++++++++++++++++++++++++++++++++++++++++-------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 106 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +19 -0
@@ -36,6 +36,12 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine supports reliable KVM_GET_CLOCK */
+    bool mach_use_reliable_get_clock;
+
+    /* whether source host supported reliable KVM_GET_CLOCK */
+    bool src_use_reliable_get_clock;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +87,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +110,36 @@

Can you please use "diff -p" on your patches?

Quoted 4 lines
hw/i386/kvm/clock.c +0 -1
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);

hw/i386/kvm/clock.c line 120

Quoted 10 lines
hw/i386/kvm/clock.c +8 -1
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /* local (running VM) restore */
+        if (s->clock_valid) {
+            /*
+             * if host does not support reliable KVM_GET_CLOCK,
+             * read kvmclock value from memory
+             */
+            if (!kvm_has_adjust_clock_stable()) {

Why not use src_use_reliable_get_clock here, too? (Maybe rename it to simply clock_is_reliable?)

Quoted 7 lines
Quoted 5 lines

Because you end up mixing two mental ideas (one: whether the source has KVM_GET_CLOCK, second: whether the destination has KVM_GET_CLOCK into one variable). I find it more confusing than not. Maybe its just my limited brain but i find it
confusing.

I find it simpler and easier to reason about.

I disagree, but i don't mind. Is this what you refer to:

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +1 -1
@@ -37,11 +37,11 @@
     uint64_t clock;
     bool clock_valid;
 
-    /* whether machine supports reliable KVM_GET_CLOCK */
+    /* whether machine type supports reliable get clock */
     bool mach_use_reliable_get_clock;
 
Quoted 7 lines

hw/i386/kvm/clock.c line 43

Quoted 3 lines
hw/i386/kvm/clock.c +1 -2
-    /* whether source host supported reliable KVM_GET_CLOCK */
-    bool src_use_reliable_get_clock;
+    /* whether clock is reliable */

I would make the comment more explicit about its meaning:
/* whether the 'clock' value was obtained in a host with
* reliable KVM_GET_CLOCK */

Done.

Quoted 25 lines

hw/i386/kvm/clock.c line 44

Quoted 1 line
hw/i386/kvm/clock.c +1 -0
+    bool clock_is_reliable;

} KVMClockState;

Quoted 22 lines
hw/i386/kvm/clock.c +0 -16
 struct pvclock_vcpu_time_info {
@@ -112,25 +112,17 @@
         struct kvm_clock_data data = {};
         uint64_t pvclock_via_mem = 0;
 
-        /* local (running VM) restore */
-        if (s->clock_valid) {
-            /*
-             * if host does not support reliable KVM_GET_CLOCK,
-             * read kvmclock value from memory
-             */
-            if (!kvm_has_adjust_clock_stable()) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
-        /* migration/savevm/init restore */
-        } else {
-            /*
-             * use s->clock in case machine uses reliable
-             * get clock and source host supported
-             * reliable get clock
-             */
Quoted 8 lines

hw/i386/kvm/clock.c line 115

Quoted 4 lines
hw/i386/kvm/clock.c +1 -3
-            if (!s->src_use_reliable_get_clock) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
+

A comment here would be welcome. Something like: "If the host where s->clock was read did not support reliable KVM_GET_CLOCK, read kvmclock value from memory".

Done.

Quoted 10 lines
Quoted 9 lines
hw/i386/kvm/clock.c +9 -0
+        if (!s->clock_is_reliable) {
+            pvclock_via_mem = kvmclock_current_nsec(s);
+        }
+
+        /* migration/savevm/init restore
+         * update clock_is_reliable to match local
+         * host capabilities.
+         */
+        if (s->clock_valid == false) {
Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Why the s->clock_valid check? It seems unnecessary.

Because if s->clock_valid == false (from migration), kvm_has_adjust_clock_stable() from the local host is irrelevant.

That's true, and that's why updating it on kvm_get_clock() looks like a more logical place.

But the current patch updates
s->clock_is_reliable at the "if (running)"
case of kvmclock_vm_state_change().

There, both s->clock_is_reliable and s->clock are
updated in sync:

Quoted 9 lines
        if (s->clock_valid == false) {
            s->clock_is_reliable = kvm_has_adjust_clock_stable();
        }

        /* We can't rely on the saved clock value, just discard it */
        if (pvclock_via_mem) {
            s->clock = pvclock_via_mem;
        }

Here s->clock is updated only to be used to set data.clock in the next line (and never used again). I'm talking about every other s->clock assignment in the code.

Quoted 1 line

However, there is no need to call kvm_get_clock() there.

I'm talking about the existing kvm_get_clock() calls.

Quoted 9 lines

kvm_get_clock() is called at the "} else {" case.

Are you suggesting to

* move s->clock_is_reliable assignment to kvm_get_clock()
* move s->clock assignment to kvm_get_clock()
* call kvm_get_clock() from the "if (running)" because the assignments are in that function now.

No.

Quoted 2 lines

But you don't need KVM_GET_CLOCK to be called there.

This is what I'm suggesting:

[ehabkost: simplified migration logic]
---
 hw/i386/kvm/clock.c    | 95 ++++++++++++++++++++++++++++++++++++++++++--------
 include/hw/i386/pc.h   |  5 +++
 target-i386/kvm.c      |  7 ++++
 target-i386/kvm_i386.h |  1 +
 4 files changed, 94 insertions(+), 14 deletions(-)
hw/i386/kvm/clock.c +81 -14
index 0f75dd3..df07ec7 100644
@@ -35,6 +35,13 @@ typedef struct KVMClockState {
     /*< public >*/
 
     uint64_t clock;
+    /* whether the value in 'clock' is reliable (i.e. was set by a host
+     * with reliable KVM_GET_CLOCK) */
+    bool clock_is_reliable;
+    /* Enable migration of clock_is_reliable. Some machine-types
+     * will disable it for compatibility with older QEMU versions.
+     */
+    bool migrate_reliable_flag;
     bool clock_valid;
 } KVMClockState;
 
@@ -81,6 +88,20 @@ static uint64_t kvmclock_current_nsec(KVMClockState *s)
     return nsec + time.system_time;
 }
 
+static void kvm_get_clock(KVMClockState *s)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+        abort();
+    }
+    s->clock = data.clock;
+    s->clock_is_reliable = kvm_has_adjust_clock_stable();
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,16 +112,18 @@ static void kvmclock_vm_state_change(void *opaque, int running,
 
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);
 
         s->clock_valid = false;
 
-        /* We can't rely on the migrated clock value, just discard it */
-        if (time_at_migration) {
-            s->clock = time_at_migration;
+        /* if host that set s->clock did not support reliable KVM_GET_CLOCK,
+         * read kvmclock value from memory
+         */
+        if (!s->clock_is_reliable) {
+            data.clock = kvmclock_current_nsec(s);
+        } else {
+            data.clock = s->clock;
         }
 
-        data.clock = s->clock;
         ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
         if (ret < 0) {
             fprintf(stderr, "KVM_SET_CLOCK failed: %s\n", strerror(ret));
@@ -120,22 +143,23 @@ static void kvmclock_vm_state_change(void *opaque, int running,
             }
         }
     } else {
-        struct kvm_clock_data data;
-        int ret;
 
+        /*FIXME: is this really possible to happen?
+         * Can kvmclock_vm_state_change(s, 0) be called twice without
+         * a kvmclock_vm_state_change(s, 1) call in between?
+         */
         if (s->clock_valid) {
             return;
         }
 
         kvm_synchronize_all_tsc();
 
-        ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
-        if (ret < 0) {
-            fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
-            abort();
-        }
-        s->clock = data.clock;
-
+        kvm_get_clock(s);
+        /* FIXME: the comment below doesn't match what is done at
+         * kvmclock_pre_save(). The comment must be updated, or
+         * kvmclock_pre_save() must be changed. I don't know what's
+         * the right thing to do.
+         */
         /*
          * If the VM is stopped, declare the clock state valid to
          * avoid re-reading it on next vmsave (which would return
@@ -152,22 +176,65 @@ static void kvmclock_realize(DeviceState *dev, Error **errp)
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
+static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    return s->migrate_reliable_flag;
+}
+
+static const VMStateDescription kvmclock_reliable_get_clock = {
+    .name = "kvmclock/clock_is_reliable",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = kvmclock_src_use_reliable_get_clock,
+    .fields = (VMStateField[]) {
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void kvmclock_pre_save(void *opaque)
+{
+    KVMClockState *s = opaque;
+    /*TODO: add comment explaining why this is here:
+     * "re-read clock value here because [...]"" */
+
+    /*XXX: is it correct to call kvm_get_clock() here
+     * if s->clock_is_valid is true? (meaning the kvmclock was
+     * already stopped)
+     */
+    kvm_get_clock(s);
+}
+
 static const VMStateDescription kvmclock_vmsd = {
     .name = "kvmclock",
     .version_id = 1,
     .minimum_version_id = 1,
+    .pre_save = kvmclock_pre_save,
     .fields = (VMStateField[]) {
         VMSTATE_UINT64(clock, KVMClockState),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * []) {
+        &kvmclock_reliable_get_clock,
+        NULL
     }
 };
 
+static Property kvmclock_properties[] = {
+    DEFINE_PROP_BOOL("x-migrate-reliable-flag", KVMClockState,
+                      migrate_reliable_flag, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void kvmclock_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->realize = kvmclock_realize;
     dc->vmsd = &kvmclock_vmsd;
+    dc->props = kvmclock_properties;
 }
 
 static const TypeInfo kvmclock_info = {
include/hw/i386/pc.h +5 -0
index 4b74130..fb80d69 100644
@@ -372,6 +372,11 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
 #define PC_COMPAT_2_7 \
     HW_COMPAT_2_7 \
     {\
+        .driver   = "kvmclock",\
+        .property = "x-migrate-reliable-flag",\
+        .value    = "off",\
+    },\
+    {\
         .driver   = TYPE_X86_CPU,\
         .property = "l3-cache",\
         .value    = "off",\
target-i386/kvm.c +7 -0
index f62264a..10a9cd8 100644
@@ -117,6 +117,13 @@ bool kvm_has_smm(void)
     return kvm_check_extension(kvm_state, KVM_CAP_X86_SMM);
 }
 
+bool kvm_has_adjust_clock_stable(void)
+{
+    int ret = kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK);
+
+    return (ret == KVM_CLOCK_TSC_STABLE);
+}
+
 bool kvm_allows_irq0_override(void)
 {
     return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing();
target-i386/kvm_i386.h +1 -0
index 7607929..bfce427 100644
@@ -17,6 +17,7 @@
 
 bool kvm_allows_irq0_override(void);
 bool kvm_has_smm(void);
+bool kvm_has_adjust_clock_stable(void);
 void kvm_synchronize_all_tsc(void);
 void kvm_arch_reset_vcpu(X86CPU *cs);
 void kvm_arch_do_init_vcpu(X86CPU *cs);
Signature · 1 line
2.7.4
Quoted 39 lines
Quoted 31 lines
Quoted 18 lines

If s->clock_valid == true, kvm_has_adjust_clock_stable() is what decides whether to use KVM_GET_CLOCK or not.

Quoted 2 lines

(If this is an optimization to avoid extra ioctl() calls, you can cache it inside kvm_has_adjust_clock_stable()).

Its not.

Quoted 5 lines

hw/i386/kvm/clock.c line 126

Quoted 2 lines
hw/i386/kvm/clock.c +1 -0
+            s->clock_is_reliable = kvm_has_adjust_clock_stable();
         }

This should go to kvm_get_clock(), so s->clock and s->clock_is_reliable are guaranteed to be always in sync.

Quoted 8 lines

No: the point to change clock_is_reliable is the following: whether its the first read of the clock (migration), or subsequent reads (normal machine operation).

Now kvm_get_clock() just reads the clock, so i don't see it as the right place to change the variable.

Agreed?

I don't agree. clock_is_reliable is information about the current value in s->clock, I don't see why we need to change s->clock and s->clock_is_reliable at different places in the code.

Quoted 5 lines

Your code might work too, but I can't tell without reviewing very carefully the state transitions KVMClockState could go through. I don't see the point of making the state transitions in this code even more complex, if we can simply stick to a simple "s->clock and s->clock_is_reliable are always in sync" rule.

It is updating from the same place:

Quoted 12 lines
        /* migration/savevm/init restore
         * update clock_is_reliable to match local
         * host capabilities.
         */
        if (s->clock_valid == false) {
            s->clock_is_reliable = kvm_has_adjust_clock_stable();
        }

        /* We can't rely on the saved clock value, just discard it */
        if (pvclock_via_mem) {
            s->clock = pvclock_via_mem;
        }

I'm talking about the other s->clock assignments. This one doesn't even seem to be necessary.

Quoted 51 lines
Quoted 51 lines
Quoted 48 lines
Quoted 48 lines
Quoted 45 lines
hw/i386/kvm/clock.c +6 -12
         /* We can't rely on the saved clock value, just discard it */
@@ -181,20 +173,14 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
-    /*
-     * On machine types that support reliable KVM_GET_CLOCK,
-     * if host kernel does provide reliable KVM_GET_CLOCK,
-     * set src_use_reliable_get_clock=true so that destination
-     * avoids reading kvmclock from memory.
-     */
-    if (s->mach_use_reliable_get_clock && kvm_has_adjust_clock_stable()) {
-        s->src_use_reliable_get_clock = true;
+    if (kvm_has_adjust_clock_stable()) {
+        s->clock_is_reliable = true;
     }
 
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
-static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+static bool kvmclock_clock_is_reliable_needed(void *opaque)
 {
     KVMClockState *s = opaque;
 
@@ -202,12 +188,12 @@
 }
 
 static const VMStateDescription kvmclock_reliable_get_clock = {
-    .name = "kvmclock/src_use_reliable_get_clock",
+    .name = "kvmclock/clock_is_reliable",
     .version_id = 1,
     .minimum_version_id = 1,
-    .needed = kvmclock_src_use_reliable_get_clock,
+    .needed = kvmclock_clock_is_reliable_needed,
     .fields = (VMStateField[]) {
-        VMSTATE_BOOL(src_use_reliable_get_clock, KVMClockState),
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
         VMSTATE_END_OF_LIST()
     }
 };
Signature · 1 line
Eduardo
Signature · 1 line
Eduardo
Signature · 1 line
Eduardo
#13 lore raw

Re: [qemu patch V3 2/2] kvmclock: reduce kvmclock difference on migration

Quoted 264 lines · Eduardo Habkost
Quoted 192 lines · Marcelo Tosatti
Quoted 176 lines · Eduardo Habkost
Quoted 175 lines · Marcelo Tosatti
Quoted 118 lines · Eduardo Habkost
Quoted 117 lines · Marcelo Tosatti
Quoted 94 lines · Eduardo Habkost
Quoted 93 lines · Marcelo Tosatti
Quoted 91 lines · Eduardo Habkost

Sorry for not noticing the following issues on the previous reviews. I was only paying attention to the vmstate and machine code:

hw/i386/kvm/clock.c

Quoted 66 lines · Marcelo Tosatti

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |  107 ++++++++++++++++++++++++++++++++++++++++++-------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 106 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +19 -0
@@ -36,6 +36,12 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine supports reliable KVM_GET_CLOCK */
+    bool mach_use_reliable_get_clock;
+
+    /* whether source host supported reliable KVM_GET_CLOCK */
+    bool src_use_reliable_get_clock;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +87,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +110,36 @@

Can you please use "diff -p" on your patches?

Quoted 4 lines
hw/i386/kvm/clock.c +0 -1
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);

hw/i386/kvm/clock.c line 120

Quoted 10 lines
hw/i386/kvm/clock.c +8 -1
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /* local (running VM) restore */
+        if (s->clock_valid) {
+            /*
+             * if host does not support reliable KVM_GET_CLOCK,
+             * read kvmclock value from memory
+             */
+            if (!kvm_has_adjust_clock_stable()) {

Why not use src_use_reliable_get_clock here, too? (Maybe rename it to simply clock_is_reliable?)

Quoted 7 lines
Quoted 5 lines

Because you end up mixing two mental ideas (one: whether the source has KVM_GET_CLOCK, second: whether the destination has KVM_GET_CLOCK into one variable). I find it more confusing than not. Maybe its just my limited brain but i find it
confusing.

I find it simpler and easier to reason about.

I disagree, but i don't mind. Is this what you refer to:

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +1 -1
@@ -37,11 +37,11 @@
     uint64_t clock;
     bool clock_valid;
 
-    /* whether machine supports reliable KVM_GET_CLOCK */
+    /* whether machine type supports reliable get clock */
     bool mach_use_reliable_get_clock;
 
Quoted 7 lines

hw/i386/kvm/clock.c line 43

Quoted 3 lines
hw/i386/kvm/clock.c +1 -2
-    /* whether source host supported reliable KVM_GET_CLOCK */
-    bool src_use_reliable_get_clock;
+    /* whether clock is reliable */

I would make the comment more explicit about its meaning:
/* whether the 'clock' value was obtained in a host with
* reliable KVM_GET_CLOCK */

Done.

Quoted 25 lines

hw/i386/kvm/clock.c line 44

Quoted 1 line
hw/i386/kvm/clock.c +1 -0
+    bool clock_is_reliable;

} KVMClockState;

Quoted 22 lines
hw/i386/kvm/clock.c +0 -16
 struct pvclock_vcpu_time_info {
@@ -112,25 +112,17 @@
         struct kvm_clock_data data = {};
         uint64_t pvclock_via_mem = 0;
 
-        /* local (running VM) restore */
-        if (s->clock_valid) {
-            /*
-             * if host does not support reliable KVM_GET_CLOCK,
-             * read kvmclock value from memory
-             */
-            if (!kvm_has_adjust_clock_stable()) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
-        /* migration/savevm/init restore */
-        } else {
-            /*
-             * use s->clock in case machine uses reliable
-             * get clock and source host supported
-             * reliable get clock
-             */
Quoted 8 lines

hw/i386/kvm/clock.c line 115

Quoted 4 lines
hw/i386/kvm/clock.c +1 -3
-            if (!s->src_use_reliable_get_clock) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
+

A comment here would be welcome. Something like: "If the host where s->clock was read did not support reliable KVM_GET_CLOCK, read kvmclock value from memory".

Done.

Quoted 10 lines
Quoted 9 lines
hw/i386/kvm/clock.c +9 -0
+        if (!s->clock_is_reliable) {
+            pvclock_via_mem = kvmclock_current_nsec(s);
+        }
+
+        /* migration/savevm/init restore
+         * update clock_is_reliable to match local
+         * host capabilities.
+         */
+        if (s->clock_valid == false) {
Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Why the s->clock_valid check? It seems unnecessary.

Because if s->clock_valid == false (from migration), kvm_has_adjust_clock_stable() from the local host is irrelevant.

That's true, and that's why updating it on kvm_get_clock() looks like a more logical place.

But the current patch updates
s->clock_is_reliable at the "if (running)"
case of kvmclock_vm_state_change().

There, both s->clock_is_reliable and s->clock are
updated in sync:

Quoted 9 lines
        if (s->clock_valid == false) {
            s->clock_is_reliable = kvm_has_adjust_clock_stable();
        }

        /* We can't rely on the saved clock value, just discard it */
        if (pvclock_via_mem) {
            s->clock = pvclock_via_mem;
        }

Here s->clock is updated only to be used to set data.clock in the next line (and never used again). I'm talking about every other s->clock assignment in the code.

Quoted 1 line

However, there is no need to call kvm_get_clock() there.

I'm talking about the existing kvm_get_clock() calls.

Quoted 9 lines

kvm_get_clock() is called at the "} else {" case.

Are you suggesting to

* move s->clock_is_reliable assignment to kvm_get_clock()
* move s->clock assignment to kvm_get_clock()
* call kvm_get_clock() from the "if (running)" because the assignments are in that function now.

No.

Quoted 2 lines

But you don't need KVM_GET_CLOCK to be called there.

This is what I'm suggesting:

[ehabkost: simplified migration logic]
---
 hw/i386/kvm/clock.c    | 95 ++++++++++++++++++++++++++++++++++++++++++--------
 include/hw/i386/pc.h   |  5 +++
 target-i386/kvm.c      |  7 ++++
 target-i386/kvm_i386.h |  1 +
 4 files changed, 94 insertions(+), 14 deletions(-)
hw/i386/kvm/clock.c +11 -0
index 0f75dd3..df07ec7 100644
@@ -35,6 +35,13 @@ typedef struct KVMClockState {
     /*< public >*/
 
     uint64_t clock;
+    /* whether the value in 'clock' is reliable (i.e. was set by a host
+     * with reliable KVM_GET_CLOCK) */
+    bool clock_is_reliable;
+    /* Enable migration of clock_is_reliable. Some machine-types
+     * will disable it for compatibility with older QEMU versions.
+     */
+    bool migrate_reliable_flag;
     bool clock_valid;
 } KVMClockState;
 
@@ -81,6 +88,20 @@ static uint64_t kvmclock_current_nsec(KVMClockState *s)
     return nsec + time.system_time;
 }
 
+static void kvm_get_clock(KVMClockState *s)
+{
+    struct kvm_clock_data data;
+    int ret;

hw/i386/kvm/clock.c static uint64_t kvmclock_current_nsec(KVMClockState *s) · line 104

Quoted 10 lines
hw/i386/kvm/clock.c +10 -0
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+        abort();
+    }
+    s->clock = data.clock;
+    s->clock_is_reliable = kvm_has_adjust_clock_stable();
+}
+

s->clock_is_reliable has to updated at migration, after the first time KVM_SET_CLOCK is called. There is no other place where it should be updated. This is the behaviour desired, according to this table:

index:
mig->c means use s->clock value from migration
mig->m means read pvclock value from memory
(both at migration, only the first time kvmclock_change_vmstate is called).

runt->c means use KVM_GET_CLOCK/KVM_SET_CLOCK at runtime runt->m means read pvclock value from memory
(both at runtime, after the first time kvmclock_change_vmstate is called).

SOURCE                DESTINATION             BEHAVIOUR
get_clock_reliable    get_clock_reliable      mig->c,runt->c  1
get_clock_reliable    ~get_clock_reliable     mig->c,runt->m  2
~get_clock_reliable   get_clock_reliable      mig->m,runt->c  3
~get_clock_reliable   ~get_clock_reliable     mig->m,runt->m  4

So looking at your patch below, and thinking of kvm_get_clock() updating s->clock_is_reliable from !running case (that is RUNNING -> ~RUNNING transition) makes no sense at all. That should not happen.

s->clock_is_reliable should be updated after the first time ~RUNNING -> RUNNING migration is done, which is my patch does.

I think your s->clock,s->clock_is_reliable should be in sync suggestion comes from a misunderstanding of how they are used.

s->clock is not read if s->clock_is_reliable is set.

Look at my attached patch below (still untested, will test and then resubmit) and check whether it matches the behaviour described in the table above.

Quoted 29 lines
hw/i386/kvm/clock.c +7 -5
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,16 +112,18 @@ static void kvmclock_vm_state_change(void *opaque, int running,
 
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);
 
         s->clock_valid = false;
 
-        /* We can't rely on the migrated clock value, just discard it */
-        if (time_at_migration) {
-            s->clock = time_at_migration;
+        /* if host that set s->clock did not support reliable KVM_GET_CLOCK,
+         * read kvmclock value from memory
+         */
+        if (!s->clock_is_reliable) {
+            data.clock = kvmclock_current_nsec(s);
+        } else {
+            data.clock = s->clock;
         }
 
-        data.clock = s->clock;
         ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
         if (ret < 0) {
             fprintf(stderr, "KVM_SET_CLOCK failed: %s\n", strerror(ret));
@@ -120,22 +143,23 @@ static void kvmclock_vm_state_change(void *opaque, int running,
 

hw/i386/kvm/clock.c static void kvmclock_vm_state_change(void *opaque, int running, · line 151

Quoted 10 lines
hw/i386/kvm/clock.c +4 -2
             }
         }
     } else {
-        struct kvm_clock_data data;
-        int ret;
 
+        /*FIXME: is this really possible to happen?
+         * Can kvmclock_vm_state_change(s, 0) be called twice without
+         * a kvmclock_vm_state_change(s, 1) call in between?
+         */

It can't: check the code where notifiers are called.

Quoted 9 lines
hw/i386/kvm/clock.c +0 -3
         if (s->clock_valid) {
             return;
         }
 
         kvm_synchronize_all_tsc();
 
-        ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
-        if (ret < 0) {
-            fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));

hw/i386/kvm/clock.c static void kvmclock_vm_state_change(void *opaque, int running, · line 163

Quoted 10 lines
hw/i386/kvm/clock.c +6 -4
-            abort();
-        }
-        s->clock = data.clock;
-
+        kvm_get_clock(s);
+        /* FIXME: the comment below doesn't match what is done at
+         * kvmclock_pre_save(). The comment must be updated, or
+         * kvmclock_pre_save() must be changed. I don't know what's
+         * the right thing to do.
+         */

This fails for all cases (1,2,3,4) in the table above.

Quoted 173 lines
hw/i386/kvm/clock.c +0 -0
         /*
          * If the VM is stopped, declare the clock state valid to

* avoid re-reading it on next vmsave (which would return

hw/i386/kvm/clock.c +43 -0
@@ -152,22 +176,65 @@ static void kvmclock_realize(DeviceState *dev, Error **errp)
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
+static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    return s->migrate_reliable_flag;
+}
+
+static const VMStateDescription kvmclock_reliable_get_clock = {
+    .name = "kvmclock/clock_is_reliable",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = kvmclock_src_use_reliable_get_clock,
+    .fields = (VMStateField[]) {
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void kvmclock_pre_save(void *opaque)
+{
+    KVMClockState *s = opaque;
+    /*TODO: add comment explaining why this is here:
+     * "re-read clock value here because [...]"" */
+
+    /*XXX: is it correct to call kvm_get_clock() here
+     * if s->clock_is_valid is true? (meaning the kvmclock was
+     * already stopped)
+     */
+    kvm_get_clock(s);
+}
+
 static const VMStateDescription kvmclock_vmsd = {
     .name = "kvmclock",
     .version_id = 1,
     .minimum_version_id = 1,
+    .pre_save = kvmclock_pre_save,
     .fields = (VMStateField[]) {
         VMSTATE_UINT64(clock, KVMClockState),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * []) {
+        &kvmclock_reliable_get_clock,
+        NULL
     }
 };
 
+static Property kvmclock_properties[] = {
+    DEFINE_PROP_BOOL("x-migrate-reliable-flag", KVMClockState,
+                      migrate_reliable_flag, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void kvmclock_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->realize = kvmclock_realize;
     dc->vmsd = &kvmclock_vmsd;
+    dc->props = kvmclock_properties;
 }
 
 static const TypeInfo kvmclock_info = {
include/hw/i386/pc.h +5 -0
index 4b74130..fb80d69 100644
@@ -372,6 +372,11 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
 #define PC_COMPAT_2_7 \
     HW_COMPAT_2_7 \
     {\
+        .driver   = "kvmclock",\
+        .property = "x-migrate-reliable-flag",\
+        .value    = "off",\
+    },\
+    {\
         .driver   = TYPE_X86_CPU,\
         .property = "l3-cache",\
         .value    = "off",\
target-i386/kvm.c +7 -0
index f62264a..10a9cd8 100644
@@ -117,6 +117,13 @@ bool kvm_has_smm(void)
     return kvm_check_extension(kvm_state, KVM_CAP_X86_SMM);
 }
 
+bool kvm_has_adjust_clock_stable(void)
+{
+    int ret = kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK);
+
+    return (ret == KVM_CLOCK_TSC_STABLE);
+}
+
 bool kvm_allows_irq0_override(void)
 {
     return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing();
target-i386/kvm_i386.h +1 -0
index 7607929..bfce427 100644
@@ -17,6 +17,7 @@
 
 bool kvm_allows_irq0_override(void);
 bool kvm_has_smm(void);
+bool kvm_has_adjust_clock_stable(void);
 void kvm_synchronize_all_tsc(void);
 void kvm_arch_reset_vcpu(X86CPU *cs);
 void kvm_arch_do_init_vcpu(X86CPU *cs);
Signature · 1 line
2.7.4
Quoted 39 lines
Quoted 31 lines
Quoted 18 lines

If s->clock_valid == true, kvm_has_adjust_clock_stable() is what decides whether to use KVM_GET_CLOCK or not.

Quoted 2 lines

(If this is an optimization to avoid extra ioctl() calls, you can cache it inside kvm_has_adjust_clock_stable()).

Its not.

Quoted 5 lines

hw/i386/kvm/clock.c line 126

Quoted 2 lines
hw/i386/kvm/clock.c +1 -0
+            s->clock_is_reliable = kvm_has_adjust_clock_stable();
         }

This should go to kvm_get_clock(), so s->clock and s->clock_is_reliable are guaranteed to be always in sync.

Quoted 8 lines

No: the point to change clock_is_reliable is the following: whether its the first read of the clock (migration), or subsequent reads (normal machine operation).

Now kvm_get_clock() just reads the clock, so i don't see it as the right place to change the variable.

Agreed?

I don't agree. clock_is_reliable is information about the current value in s->clock, I don't see why we need to change s->clock and s->clock_is_reliable at different places in the code.

Quoted 5 lines

Your code might work too, but I can't tell without reviewing very carefully the state transitions KVMClockState could go through. I don't see the point of making the state transitions in this code even more complex, if we can simply stick to a simple "s->clock and s->clock_is_reliable are always in sync" rule.

It is updating from the same place:

Quoted 12 lines
        /* migration/savevm/init restore
         * update clock_is_reliable to match local
         * host capabilities.
         */
        if (s->clock_valid == false) {
            s->clock_is_reliable = kvm_has_adjust_clock_stable();
        }

        /* We can't rely on the saved clock value, just discard it */
        if (pvclock_via_mem) {
            s->clock = pvclock_via_mem;
        }

I'm talking about the other s->clock assignments. This one doesn't even seem to be necessary.

It is necessary: this is how you read pvclock from memory.

Quoted 49 lines
Quoted 49 lines
Quoted 49 lines
Quoted 48 lines
Quoted 48 lines
Quoted 45 lines
hw/i386/kvm/clock.c +6 -12
         /* We can't rely on the saved clock value, just discard it */
@@ -181,20 +173,14 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
-    /*
-     * On machine types that support reliable KVM_GET_CLOCK,
-     * if host kernel does provide reliable KVM_GET_CLOCK,
-     * set src_use_reliable_get_clock=true so that destination
-     * avoids reading kvmclock from memory.
-     */
-    if (s->mach_use_reliable_get_clock && kvm_has_adjust_clock_stable()) {
-        s->src_use_reliable_get_clock = true;
+    if (kvm_has_adjust_clock_stable()) {
+        s->clock_is_reliable = true;
     }
 
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
-static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+static bool kvmclock_clock_is_reliable_needed(void *opaque)
 {
     KVMClockState *s = opaque;
 
@@ -202,12 +188,12 @@
 }
 
 static const VMStateDescription kvmclock_reliable_get_clock = {
-    .name = "kvmclock/src_use_reliable_get_clock",
+    .name = "kvmclock/clock_is_reliable",
     .version_id = 1,
     .minimum_version_id = 1,
-    .needed = kvmclock_src_use_reliable_get_clock,
+    .needed = kvmclock_clock_is_reliable_needed,
     .fields = (VMStateField[]) {
-        VMSTATE_BOOL(src_use_reliable_get_clock, KVMClockState),
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
         VMSTATE_END_OF_LIST()
     }
 };
Signature · 1 line
Eduardo
Quoted 5 lines
Quoted 2 lines
Quoted 2 lines
Signature · 1 line
Eduardo
Signature · 1 line
Eduardo

kvmclock: reduce kvmclock difference on migration

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |   97 +++++++++++++++++++++++++++++++++++++++++--------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 96 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

hw/i386/kvm/clock.c +83 -14
Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================
@@ -36,6 +36,13 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine type supports reliable get clock */
+    bool mach_use_reliable_get_clock;
+
+    /* whether the 'clock' value was obtained in a host with
+     * reliable KVM_GET_CLOCK */
+    bool clock_is_reliable;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +88,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +111,31 @@
 
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /*
+         * If the host where s->clock was read did not support reliable
+         * KVM_GET_CLOCK, read kvmclock value from memory.
+         */
+        if (!s->clock_is_reliable) {
+            pvclock_via_mem = kvmclock_current_nsec(s);
+        }
 
-        /* We can't rely on the migrated clock value, just discard it */
-        if (time_at_migration) {
-            s->clock = time_at_migration;
+        /* migration/savevm/init restore
+         * update clock_is_reliable to match local
+         * host capabilities.
+         */
+        if (s->clock_valid == false) {
+            s->clock_is_reliable = kvm_has_adjust_clock_stable();
         }
 
+        /* We can't rely on the saved clock value, just discard it */
+        if (pvclock_via_mem) {
+            s->clock = pvclock_via_mem;
+        }
+
+        s->clock_valid = false;
+
         data.clock = s->clock;
         ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
         if (ret < 0) {
@@ -120,8 +156,6 @@
             }
         }
     } else {
-        struct kvm_clock_data data;
-        int ret;
 
         if (s->clock_valid) {
             return;
@@ -129,13 +163,7 @@
 
         kvm_synchronize_all_tsc();
 
-        ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
-        if (ret < 0) {
-            fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
-            abort();
-        }
-        s->clock = data.clock;
-
+        s->clock = kvm_get_clock();
         /*
          * If the VM is stopped, declare the clock state valid to
          * avoid re-reading it on next vmsave (which would return
@@ -149,25 +177,66 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
+    if (kvm_has_adjust_clock_stable()) {
+        s->clock_is_reliable = true;
+    }
+
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
+static bool kvmclock_clock_is_reliable_needed(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    return s->mach_use_reliable_get_clock;
+}
+
+static const VMStateDescription kvmclock_reliable_get_clock = {
+    .name = "kvmclock/clock_is_reliable",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = kvmclock_clock_is_reliable_needed,
+    .fields = (VMStateField[]) {
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void kvmclock_pre_save(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    s->clock = kvm_get_clock();
+}
+
 static const VMStateDescription kvmclock_vmsd = {
     .name = "kvmclock",
     .version_id = 1,
     .minimum_version_id = 1,
+    .pre_save = kvmclock_pre_save,
     .fields = (VMStateField[]) {
         VMSTATE_UINT64(clock, KVMClockState),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * []) {
+        &kvmclock_reliable_get_clock,
+        NULL
     }
 };
 
+static Property kvmclock_properties[] = {
+    DEFINE_PROP_BOOL("x-mach-use-reliable-get-clock", KVMClockState,
+                      mach_use_reliable_get_clock, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void kvmclock_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->realize = kvmclock_realize;
     dc->vmsd = &kvmclock_vmsd;
+    dc->props = kvmclock_properties;
 }
 
 static const TypeInfo kvmclock_info = {
include/hw/i386/pc.h +5 -0
Index: qemu-mig-advance-clock/include/hw/i386/pc.h
===================================================================
@@ -370,6 +370,11 @@
 #define PC_COMPAT_2_7 \
     HW_COMPAT_2_7 \
     {\
+        .driver   = "kvmclock",\
+        .property = "x-mach-use-reliable-get-clock",\
+        .value    = "off",\
+    },\
+    {\
         .driver   = TYPE_X86_CPU,\
         .property = "l3-cache",\
         .value    = "off",\
target-i386/kvm.c +7 -0
Index: qemu-mig-advance-clock/target-i386/kvm.c
===================================================================
@@ -117,6 +117,13 @@
     return kvm_check_extension(kvm_state, KVM_CAP_X86_SMM);
 }
 
+bool kvm_has_adjust_clock_stable(void)
+{
+    int ret = kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK);
+
+    return (ret == KVM_CLOCK_TSC_STABLE);
+}
+
 bool kvm_allows_irq0_override(void)
 {
     return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing();
target-i386/kvm_i386.h +1 -0
Index: qemu-mig-advance-clock/target-i386/kvm_i386.h
===================================================================
@@ -17,6 +17,7 @@
 
 bool kvm_allows_irq0_override(void);
 bool kvm_has_smm(void);
+bool kvm_has_adjust_clock_stable(void);
 void kvm_synchronize_all_tsc(void);
 void kvm_arch_reset_vcpu(X86CPU *cs);
 void kvm_arch_do_init_vcpu(X86CPU *cs);
#14 lore raw

Re: [qemu patch V3 2/2] kvmclock: reduce kvmclock difference on migration

Quoted 271 lines · Marcelo Tosatti
Quoted 270 lines · Eduardo Habkost
Quoted 192 lines · Marcelo Tosatti
Quoted 176 lines · Eduardo Habkost
Quoted 175 lines · Marcelo Tosatti
Quoted 118 lines · Eduardo Habkost
Quoted 117 lines · Marcelo Tosatti
Quoted 94 lines · Eduardo Habkost
Quoted 93 lines · Marcelo Tosatti
Quoted 91 lines · Eduardo Habkost

Sorry for not noticing the following issues on the previous reviews. I was only paying attention to the vmstate and machine code:

hw/i386/kvm/clock.c

Quoted 66 lines · Marcelo Tosatti

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |  107 ++++++++++++++++++++++++++++++++++++++++++-------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 106 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +19 -0
@@ -36,6 +36,12 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine supports reliable KVM_GET_CLOCK */
+    bool mach_use_reliable_get_clock;
+
+    /* whether source host supported reliable KVM_GET_CLOCK */
+    bool src_use_reliable_get_clock;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +87,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +110,36 @@

Can you please use "diff -p" on your patches?

Quoted 4 lines
hw/i386/kvm/clock.c +0 -1
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);

hw/i386/kvm/clock.c line 120

Quoted 10 lines
hw/i386/kvm/clock.c +8 -1
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /* local (running VM) restore */
+        if (s->clock_valid) {
+            /*
+             * if host does not support reliable KVM_GET_CLOCK,
+             * read kvmclock value from memory
+             */
+            if (!kvm_has_adjust_clock_stable()) {

Why not use src_use_reliable_get_clock here, too? (Maybe rename it to simply clock_is_reliable?)

Quoted 7 lines
Quoted 5 lines

Because you end up mixing two mental ideas (one: whether the source has KVM_GET_CLOCK, second: whether the destination has KVM_GET_CLOCK into one variable). I find it more confusing than not. Maybe its just my limited brain but i find it
confusing.

I find it simpler and easier to reason about.

I disagree, but i don't mind. Is this what you refer to:

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +1 -1
@@ -37,11 +37,11 @@
     uint64_t clock;
     bool clock_valid;
 
-    /* whether machine supports reliable KVM_GET_CLOCK */
+    /* whether machine type supports reliable get clock */
     bool mach_use_reliable_get_clock;
 
Quoted 7 lines

hw/i386/kvm/clock.c line 43

Quoted 3 lines
hw/i386/kvm/clock.c +1 -2
-    /* whether source host supported reliable KVM_GET_CLOCK */
-    bool src_use_reliable_get_clock;
+    /* whether clock is reliable */

I would make the comment more explicit about its meaning:
/* whether the 'clock' value was obtained in a host with
* reliable KVM_GET_CLOCK */

Done.

Quoted 25 lines

hw/i386/kvm/clock.c line 44

Quoted 1 line
hw/i386/kvm/clock.c +1 -0
+    bool clock_is_reliable;

} KVMClockState;

Quoted 22 lines
hw/i386/kvm/clock.c +0 -16
 struct pvclock_vcpu_time_info {
@@ -112,25 +112,17 @@
         struct kvm_clock_data data = {};
         uint64_t pvclock_via_mem = 0;
 
-        /* local (running VM) restore */
-        if (s->clock_valid) {
-            /*
-             * if host does not support reliable KVM_GET_CLOCK,
-             * read kvmclock value from memory
-             */
-            if (!kvm_has_adjust_clock_stable()) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
-        /* migration/savevm/init restore */
-        } else {
-            /*
-             * use s->clock in case machine uses reliable
-             * get clock and source host supported
-             * reliable get clock
-             */
Quoted 8 lines

hw/i386/kvm/clock.c line 115

Quoted 4 lines
hw/i386/kvm/clock.c +1 -3
-            if (!s->src_use_reliable_get_clock) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
+

A comment here would be welcome. Something like: "If the host where s->clock was read did not support reliable KVM_GET_CLOCK, read kvmclock value from memory".

Done.

Quoted 10 lines
Quoted 9 lines
hw/i386/kvm/clock.c +9 -0
+        if (!s->clock_is_reliable) {
+            pvclock_via_mem = kvmclock_current_nsec(s);
+        }
+
+        /* migration/savevm/init restore
+         * update clock_is_reliable to match local
+         * host capabilities.
+         */
+        if (s->clock_valid == false) {
Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Why the s->clock_valid check? It seems unnecessary.

Because if s->clock_valid == false (from migration), kvm_has_adjust_clock_stable() from the local host is irrelevant.

That's true, and that's why updating it on kvm_get_clock() looks like a more logical place.

But the current patch updates
s->clock_is_reliable at the "if (running)"
case of kvmclock_vm_state_change().

There, both s->clock_is_reliable and s->clock are
updated in sync:

Quoted 9 lines
        if (s->clock_valid == false) {
            s->clock_is_reliable = kvm_has_adjust_clock_stable();
        }

        /* We can't rely on the saved clock value, just discard it */
        if (pvclock_via_mem) {
            s->clock = pvclock_via_mem;
        }

Here s->clock is updated only to be used to set data.clock in the next line (and never used again). I'm talking about every other s->clock assignment in the code.

Quoted 1 line

However, there is no need to call kvm_get_clock() there.

I'm talking about the existing kvm_get_clock() calls.

Quoted 9 lines

kvm_get_clock() is called at the "} else {" case.

Are you suggesting to

* move s->clock_is_reliable assignment to kvm_get_clock()
* move s->clock assignment to kvm_get_clock()
* call kvm_get_clock() from the "if (running)" because the assignments are in that function now.

No.

Quoted 2 lines

But you don't need KVM_GET_CLOCK to be called there.

This is what I'm suggesting:

[ehabkost: simplified migration logic]
---
 hw/i386/kvm/clock.c    | 95 ++++++++++++++++++++++++++++++++++++++++++--------
 include/hw/i386/pc.h   |  5 +++
 target-i386/kvm.c      |  7 ++++
 target-i386/kvm_i386.h |  1 +
 4 files changed, 94 insertions(+), 14 deletions(-)
hw/i386/kvm/clock.c +17 -0
index 0f75dd3..df07ec7 100644
@@ -35,6 +35,13 @@ typedef struct KVMClockState {
     /*< public >*/
 
     uint64_t clock;
+    /* whether the value in 'clock' is reliable (i.e. was set by a host
+     * with reliable KVM_GET_CLOCK) */
+    bool clock_is_reliable;
+    /* Enable migration of clock_is_reliable. Some machine-types
+     * will disable it for compatibility with older QEMU versions.
+     */
+    bool migrate_reliable_flag;
     bool clock_valid;
 } KVMClockState;
 
@@ -81,6 +88,20 @@ static uint64_t kvmclock_current_nsec(KVMClockState *s)
     return nsec + time.system_time;
 }
 
+static void kvm_get_clock(KVMClockState *s)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+        abort();
+    }
Quoted 8 lines

hw/i386/kvm/clock.c static uint64_t kvmclock_current_nsec(KVMClockState *s) · line 104

Quoted 4 lines
hw/i386/kvm/clock.c +4 -0
+    s->clock = data.clock;
+    s->clock_is_reliable = kvm_has_adjust_clock_stable();
+}
+

s->clock_is_reliable has to updated at migration, after the first time KVM_SET_CLOCK is called. There is no other place where it should be updated.

I don't see why you claim that. We can simply update s->clock_is_reliable the next time s->clock is updated. The only code that reads s->clock checks s->clock_is_reliable first. s->clock_is_reliable is a flag _about_ the value in s->clock. I don't see why you want to update them at different times, if it means more complex logic.

Quoted 20 lines

This is the behaviour
desired, according to this table:

index:
mig->c means use s->clock value from migration
mig->m means read pvclock value from memory
(both at migration, only the first time kvmclock_change_vmstate is called).

runt->c means use KVM_GET_CLOCK/KVM_SET_CLOCK at runtime runt->m means read pvclock value from memory
(both at runtime, after the first time kvmclock_change_vmstate is called).

SOURCE                DESTINATION             BEHAVIOUR
get_clock_reliable    get_clock_reliable      mig->c,runt->c  1
get_clock_reliable    ~get_clock_reliable     mig->c,runt->m  2
~get_clock_reliable   get_clock_reliable      mig->m,runt->c  3
~get_clock_reliable   ~get_clock_reliable     mig->m,runt->m  4
Quoted 4 lines

So looking at your patch below, and thinking of kvm_get_clock() updating s->clock_is_reliable from !running case (that is RUNNING -> ~RUNNING transition) makes no sense at all. That should not happen.

Why not?

Quoted 4 lines

s->clock_is_reliable should be updated after the first time ~RUNNING -> RUNNING migration is done, which is my patch does.

I don't see why. All we need is that s->clock_is_reliable get updated before any code try to read s->clock again.

Quoted 5 lines

I think your s->clock,s->clock_is_reliable should be in sync suggestion comes from a misunderstanding of how they are used.

s->clock is not read if s->clock_is_reliable is set.

Did you mean "s->clock is not read if s->clock_is_reliable is false"? You are right (except when kvmclock_current_nsec()==0?). But this also means that the only code that reads s->clock also checks s->clock_is_reliable first.

Quoted 4 lines

Look at my attached patch below (still untested, will test and then resubmit) and check whether it matches the behaviour described in the table above.

It seems to match. But I don't see why my patch doesn't match the table above.

Quoted 36 lines
Quoted 36 lines
hw/i386/kvm/clock.c +7 -7
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,16 +112,18 @@ static void kvmclock_vm_state_change(void *opaque, int running,
 
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);
 
         s->clock_valid = false;
 
-        /* We can't rely on the migrated clock value, just discard it */
-        if (time_at_migration) {
-            s->clock = time_at_migration;
+        /* if host that set s->clock did not support reliable KVM_GET_CLOCK,
+         * read kvmclock value from memory
+         */
+        if (!s->clock_is_reliable) {
+            data.clock = kvmclock_current_nsec(s);
+        } else {
+            data.clock = s->clock;
         }
 
-        data.clock = s->clock;
         ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
         if (ret < 0) {
             fprintf(stderr, "KVM_SET_CLOCK failed: %s\n", strerror(ret));
@@ -120,22 +143,23 @@ static void kvmclock_vm_state_change(void *opaque, int running,
 
             }
         }
     } else {
-        struct kvm_clock_data data;
-        int ret;
 
Quoted 6 lines

hw/i386/kvm/clock.c static void kvmclock_vm_state_change(void *opaque, int running, · line 151

Quoted 4 lines
hw/i386/kvm/clock.c +4 -0
+        /*FIXME: is this really possible to happen?
+         * Can kvmclock_vm_state_change(s, 0) be called twice without
+         * a kvmclock_vm_state_change(s, 1) call in between?
+         */

It can't: check the code where notifiers are called.

Yet another reason to simplify the code: if we don't need different code paths for local stop/resume vs migration, we can remove s->clock_valid completely.

Quoted 7 lines
Quoted 7 lines
hw/i386/kvm/clock.c +0 -0
         if (s->clock_valid) {
             return;
         }
 
         kvm_synchronize_all_tsc();
 
Quoted 13 lines

hw/i386/kvm/clock.c static void kvmclock_vm_state_change(void *opaque, int running, · line 163

Quoted 13 lines
hw/i386/kvm/clock.c +6 -7
-        ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
-        if (ret < 0) {
-            fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
-            abort();
-        }
-        s->clock = data.clock;
-
+        kvm_get_clock(s);
+        /* FIXME: the comment below doesn't match what is done at
+         * kvmclock_pre_save(). The comment must be updated, or
+         * kvmclock_pre_save() must be changed. I don't know what's
+         * the right thing to do.
+         */

We still need to address this comment. Your patch keeps the comment that contradicts kvmclock_pre_save().

Quoted 2 lines

This fails for all cases (1,2,3,4) in the table above.

I can't see why it fails. The ordering of events from migration followed by vm stop/resume should be:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-SRC
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-SRC
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
--- | MIGRATION                   | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
dst |   KVM_SET_CLOCK             | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-DEST
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-DEST
dst |   KVM_SET_CLOCK             | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-DEST

Specific cases from your table are below. See the KVM_SET_CLOCK rows.

Case 1:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | 1
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | 1
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | 1
--- | MIGRATION                   | TIME-AT-PRE-SAVE | 1
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | 1
dst |   KVM_SET_CLOCK(s->clock)   | TIME-AT-PRE-SAVE | 1
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | 1
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | 1
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | 1
dst |   KVM_SET_CLOCK(s->clock)   | TIME-AT-VM-STOP  | 1

Case 2:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | 1
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | 1
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | 1
--- | MIGRATION                   | TIME-AT-PRE-SAVE | 1
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | 1
dst |   KVM_SET_CLOCK(s->clock)   | TIME-AT-PRE-SAVE | 1
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | 1
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | 0
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | 0
dst |   KVM_SET_CLOCK(from_mem)   | TIME-AT-VM-STOP  | 0

Case 3:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | 0
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | 0
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | 0
--- | MIGRATION                   | TIME-AT-PRE-SAVE | 0
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | 0
dst |   KVM_SET_CLOCK(from_mem)   | TIME-AT-PRE-SAVE | 0
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | 0
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | 1
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | 1
dst |   KVM_SET_CLOCK(s->clock)   | TIME-AT-VM-STOP  | 1

Case 4:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | 0
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | 0
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | 0
--- | MIGRATION                   | TIME-AT-PRE-SAVE | 0
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | 0
dst |   KVM_SET_CLOCK(from_mem)   | TIME-AT-PRE-SAVE | 0
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | 0
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | 0
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | 0
dst |   KVM_SET_CLOCK(from_mem)   | TIME-AT-VM-STOP  | 0
Quoted 172 lines
Quoted 172 lines
hw/i386/kvm/clock.c +0 -0
         /*
          * If the VM is stopped, declare the clock state valid to

* avoid re-reading it on next vmsave (which would return

hw/i386/kvm/clock.c +43 -0
@@ -152,22 +176,65 @@ static void kvmclock_realize(DeviceState *dev, Error **errp)
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
+static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    return s->migrate_reliable_flag;
+}
+
+static const VMStateDescription kvmclock_reliable_get_clock = {
+    .name = "kvmclock/clock_is_reliable",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = kvmclock_src_use_reliable_get_clock,
+    .fields = (VMStateField[]) {
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void kvmclock_pre_save(void *opaque)
+{
+    KVMClockState *s = opaque;
+    /*TODO: add comment explaining why this is here:
+     * "re-read clock value here because [...]"" */
+
+    /*XXX: is it correct to call kvm_get_clock() here
+     * if s->clock_is_valid is true? (meaning the kvmclock was
+     * already stopped)
+     */
+    kvm_get_clock(s);
+}
+
 static const VMStateDescription kvmclock_vmsd = {
     .name = "kvmclock",
     .version_id = 1,
     .minimum_version_id = 1,
+    .pre_save = kvmclock_pre_save,
     .fields = (VMStateField[]) {
         VMSTATE_UINT64(clock, KVMClockState),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * []) {
+        &kvmclock_reliable_get_clock,
+        NULL
     }
 };
 
+static Property kvmclock_properties[] = {
+    DEFINE_PROP_BOOL("x-migrate-reliable-flag", KVMClockState,
+                      migrate_reliable_flag, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void kvmclock_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->realize = kvmclock_realize;
     dc->vmsd = &kvmclock_vmsd;
+    dc->props = kvmclock_properties;
 }
 
 static const TypeInfo kvmclock_info = {
include/hw/i386/pc.h +5 -0
index 4b74130..fb80d69 100644
@@ -372,6 +372,11 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
 #define PC_COMPAT_2_7 \
     HW_COMPAT_2_7 \
     {\
+        .driver   = "kvmclock",\
+        .property = "x-migrate-reliable-flag",\
+        .value    = "off",\
+    },\
+    {\
         .driver   = TYPE_X86_CPU,\
         .property = "l3-cache",\
         .value    = "off",\
target-i386/kvm.c +7 -0
index f62264a..10a9cd8 100644
@@ -117,6 +117,13 @@ bool kvm_has_smm(void)
     return kvm_check_extension(kvm_state, KVM_CAP_X86_SMM);
 }
 
+bool kvm_has_adjust_clock_stable(void)
+{
+    int ret = kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK);
+
+    return (ret == KVM_CLOCK_TSC_STABLE);
+}
+
 bool kvm_allows_irq0_override(void)
 {
     return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing();
target-i386/kvm_i386.h +1 -0
index 7607929..bfce427 100644
@@ -17,6 +17,7 @@
 
 bool kvm_allows_irq0_override(void);
 bool kvm_has_smm(void);
+bool kvm_has_adjust_clock_stable(void);
 void kvm_synchronize_all_tsc(void);
 void kvm_arch_reset_vcpu(X86CPU *cs);
 void kvm_arch_do_init_vcpu(X86CPU *cs);
Signature · 1 line
2.7.4
Quoted 51 lines
Quoted 31 lines
Quoted 18 lines

If s->clock_valid == true, kvm_has_adjust_clock_stable() is what decides whether to use KVM_GET_CLOCK or not.

Quoted 2 lines

(If this is an optimization to avoid extra ioctl() calls, you can cache it inside kvm_has_adjust_clock_stable()).

Its not.

Quoted 5 lines

hw/i386/kvm/clock.c line 126

Quoted 2 lines
hw/i386/kvm/clock.c +1 -0
+            s->clock_is_reliable = kvm_has_adjust_clock_stable();
         }

This should go to kvm_get_clock(), so s->clock and s->clock_is_reliable are guaranteed to be always in sync.

Quoted 8 lines

No: the point to change clock_is_reliable is the following: whether its the first read of the clock (migration), or subsequent reads (normal machine operation).

Now kvm_get_clock() just reads the clock, so i don't see it as the right place to change the variable.

Agreed?

I don't agree. clock_is_reliable is information about the current value in s->clock, I don't see why we need to change s->clock and s->clock_is_reliable at different places in the code.

Quoted 5 lines

Your code might work too, but I can't tell without reviewing very carefully the state transitions KVMClockState could go through. I don't see the point of making the state transitions in this code even more complex, if we can simply stick to a simple "s->clock and s->clock_is_reliable are always in sync" rule.

It is updating from the same place:

        /* migration/savevm/init restore
         * update clock_is_reliable to match local
         * host capabilities.
         */
        if (s->clock_valid == false) {
            s->clock_is_reliable = kvm_has_adjust_clock_stable();
        }

        /* We can't rely on the saved clock value, just discard it */
        if (pvclock_via_mem) {
            s->clock = pvclock_via_mem;
        }
Quoted 4 lines
Quoted 2 lines

I'm talking about the other s->clock assignments. This one doesn't even seem to be necessary.

It is necessary: this is how you read pvclock from memory.

It is not necessary if you set data.clock directly.

But this doesn't matter for this discussion. My new patch below keeps that line, just to make the diff smaller.

Quoted 142 lines
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
Quoted 49 lines
Quoted 49 lines
Quoted 46 lines
hw/i386/kvm/clock.c +6 -12
         /* We can't rely on the saved clock value, just discard it */
@@ -181,20 +173,14 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
-    /*
-     * On machine types that support reliable KVM_GET_CLOCK,
-     * if host kernel does provide reliable KVM_GET_CLOCK,
-     * set src_use_reliable_get_clock=true so that destination
-     * avoids reading kvmclock from memory.
-     */
-    if (s->mach_use_reliable_get_clock && kvm_has_adjust_clock_stable()) {
-        s->src_use_reliable_get_clock = true;
+    if (kvm_has_adjust_clock_stable()) {
+        s->clock_is_reliable = true;
     }
 
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
-static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+static bool kvmclock_clock_is_reliable_needed(void *opaque)
 {
     KVMClockState *s = opaque;
 
@@ -202,12 +188,12 @@
 }
 
 static const VMStateDescription kvmclock_reliable_get_clock = {
-    .name = "kvmclock/src_use_reliable_get_clock",
+    .name = "kvmclock/clock_is_reliable",
     .version_id = 1,
     .minimum_version_id = 1,
-    .needed = kvmclock_src_use_reliable_get_clock,
+    .needed = kvmclock_clock_is_reliable_needed,
     .fields = (VMStateField[]) {
-        VMSTATE_BOOL(src_use_reliable_get_clock, KVMClockState),
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
         VMSTATE_END_OF_LIST()
     }
 };
Signature · 1 line
Eduardo
Quoted 5 lines
Quoted 2 lines
Quoted 2 lines
Signature · 1 line
Eduardo
Signature · 1 line
Eduardo

kvmclock: reduce kvmclock difference on migration

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |   97 +++++++++++++++++++++++++++++++++++++++++--------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 96 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +28 -3
@@ -36,6 +36,13 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine type supports reliable get clock */
+    bool mach_use_reliable_get_clock;
+
+    /* whether the 'clock' value was obtained in a host with
+     * reliable KVM_GET_CLOCK */
+    bool clock_is_reliable;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +88,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +111,31 @@
 
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /*
+         * If the host where s->clock was read did not support reliable
+         * KVM_GET_CLOCK, read kvmclock value from memory.
+         */
+        if (!s->clock_is_reliable) {
+            pvclock_via_mem = kvmclock_current_nsec(s);
+        }
 
-        /* We can't rely on the migrated clock value, just discard it */

hw/i386/kvm/clock.c line 131

Quoted 10 lines
hw/i386/kvm/clock.c +6 -2
-        if (time_at_migration) {
-            s->clock = time_at_migration;
+        /* migration/savevm/init restore
+         * update clock_is_reliable to match local
+         * host capabilities.
+         */
+        if (s->clock_valid == false) {
+            s->clock_is_reliable = kvm_has_adjust_clock_stable();
         }
 

If we are reusing clock_valid for a different purpose, I suggest we rename it to something clearer, like "clock_is_local" or "clock_is_stopped".

But I still don't see the need for this convoluted logic, if we can simply set s->clock_is_reliable and s->clock at the same time. This even allow us to remove s->clock_valid completely.

hw/i386/kvm/clock.c line 138

Quoted 7 lines
hw/i386/kvm/clock.c +7 -0
+        /* We can't rely on the saved clock value, just discard it */
+        if (pvclock_via_mem) {
+            s->clock = pvclock_via_mem;
+        }
+
+        s->clock_valid = false;
+

I suggest the following on top of your patch. It removes 27 unnecessary lines from the code.

But, really, I'm tired of this discussion. If you want to keep the complex logic you suggest and others are happy with it, go ahead. You just won't get my Reviewed-by.

But in addition to this discussion about migration_is_safe, I have a question below about kvmclock_pre_save(), which seems to be a real problem. See below.

---
 hw/i386/kvm/clock.c | 37 +++++--------------------------------
 1 file changed, 5 insertions(+), 32 deletions(-)
hw/i386/kvm/clock.c +5 -32
index e179862..359abce 100644
@@ -35,8 +35,6 @@ typedef struct KVMClockState {
     /*< public >*/
 
     uint64_t clock;
-    bool clock_valid;
-
     /* whether machine type supports reliable get clock */
     bool mach_use_reliable_get_clock;
 
@@ -88,7 +86,7 @@ static uint64_t kvmclock_current_nsec(KVMClockState *s)
     return nsec + time.system_time;
 }
 
-static uint64_t kvm_get_clock(void)
+static void kvm_get_clock(KVMClockState *s)
 {
     struct kvm_clock_data data;
     int ret;
@@ -98,7 +96,8 @@ static uint64_t kvm_get_clock(void)
         fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
                 abort();
     }
-    return data.clock;
+    s->clock = data.clock;
+    s->clock_is_reliable = kvm_has_adjust_clock_stable();
 }
 
 static void kvmclock_vm_state_change(void *opaque, int running,
@@ -121,21 +120,11 @@ static void kvmclock_vm_state_change(void *opaque, int running,
             pvclock_via_mem = kvmclock_current_nsec(s);
         }
 
-        /* migration/savevm/init restore
-         * update clock_is_reliable to match local
-         * host capabilities.
-         */
-        if (s->clock_valid == false) {
-            s->clock_is_reliable = kvm_has_adjust_clock_stable();
-        }
-
         /* We can't rely on the saved clock value, just discard it */
         if (pvclock_via_mem) {
             s->clock = pvclock_via_mem;
         }
 
-        s->clock_valid = false;
-
         data.clock = s->clock;
         ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
         if (ret < 0) {
@@ -156,20 +145,8 @@ static void kvmclock_vm_state_change(void *opaque, int running,
             }
         }
     } else {
-
-        if (s->clock_valid) {
-            return;
-        }
-
         kvm_synchronize_all_tsc();
-
-        s->clock = kvm_get_clock();
-        /*
-         * If the VM is stopped, declare the clock state valid to
-         * avoid re-reading it on next vmsave (which would return
-         * a different value). Will be reset when the VM is continued.
-         */
-        s->clock_valid = true;
+        kvm_get_clock(s);
     }
 }
 
@@ -177,10 +154,6 @@ static void kvmclock_realize(DeviceState *dev, Error **errp)
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
-    if (kvm_has_adjust_clock_stable()) {
-        s->clock_is_reliable = true;
-    }
-
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
@@ -206,7 +179,7 @@ static void kvmclock_pre_save(void *opaque)
 {
     KVMClockState *s = opaque;
 
-    s->clock = kvm_get_clock();
+    kvm_get_clock(s);
 }
 
 static const VMStateDescription kvmclock_vmsd = {
Quoted 51 lines
hw/i386/kvm/clock.c +18 -9
+
         data.clock = s->clock;
         ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
         if (ret < 0) {
@@ -120,8 +156,6 @@
             }
         }
     } else {
-        struct kvm_clock_data data;
-        int ret;
 
         if (s->clock_valid) {
             return;
@@ -129,13 +163,7 @@
 
         kvm_synchronize_all_tsc();
 
-        ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
-        if (ret < 0) {
-            fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
-            abort();
-        }
-        s->clock = data.clock;
-
+        s->clock = kvm_get_clock();
         /*
          * If the VM is stopped, declare the clock state valid to
          * avoid re-reading it on next vmsave (which would return
@@ -149,25 +177,66 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
+    if (kvm_has_adjust_clock_stable()) {
+        s->clock_is_reliable = true;
+    }
+
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
+static bool kvmclock_clock_is_reliable_needed(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    return s->mach_use_reliable_get_clock;
+}
+
+static const VMStateDescription kvmclock_reliable_get_clock = {
+    .name = "kvmclock/clock_is_reliable",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = kvmclock_clock_is_reliable_needed,

hw/i386/kvm/clock.c line 208

Quoted 10 lines
hw/i386/kvm/clock.c +10 -0
+    .fields = (VMStateField[]) {
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void kvmclock_pre_save(void *opaque)
+{
+    KVMClockState *s = opaque;
+

We still need a comment here explaining why we need to re-read the clock on pre_save if s->clock was already set on kvmclock_vm_state_change().

Also, what if we are migrating a VM that was already paused 10 minutes ago? Should we migrate the s->clock value from 10 minutes ago, or the one read at pre_save?

Quoted 80 lines
hw/i386/kvm/clock.c +15 -0
+    s->clock = kvm_get_clock();
+}
+
 static const VMStateDescription kvmclock_vmsd = {
     .name = "kvmclock",
     .version_id = 1,
     .minimum_version_id = 1,
+    .pre_save = kvmclock_pre_save,
     .fields = (VMStateField[]) {
         VMSTATE_UINT64(clock, KVMClockState),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * []) {
+        &kvmclock_reliable_get_clock,
+        NULL
     }
 };
 
+static Property kvmclock_properties[] = {
+    DEFINE_PROP_BOOL("x-mach-use-reliable-get-clock", KVMClockState,
+                      mach_use_reliable_get_clock, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void kvmclock_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->realize = kvmclock_realize;
     dc->vmsd = &kvmclock_vmsd;
+    dc->props = kvmclock_properties;
 }
 
 static const TypeInfo kvmclock_info = {

Index: qemu-mig-advance-clock/include/hw/i386/pc.h
===================================================================

include/hw/i386/pc.h +5 -0
@@ -370,6 +370,11 @@
 #define PC_COMPAT_2_7 \
     HW_COMPAT_2_7 \
     {\
+        .driver   = "kvmclock",\
+        .property = "x-mach-use-reliable-get-clock",\
+        .value    = "off",\
+    },\
+    {\
         .driver   = TYPE_X86_CPU,\
         .property = "l3-cache",\
         .value    = "off",\

Index: qemu-mig-advance-clock/target-i386/kvm.c
===================================================================

target-i386/kvm.c +7 -0
@@ -117,6 +117,13 @@
     return kvm_check_extension(kvm_state, KVM_CAP_X86_SMM);
 }
 
+bool kvm_has_adjust_clock_stable(void)
+{
+    int ret = kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK);
+
+    return (ret == KVM_CLOCK_TSC_STABLE);
+}
+
 bool kvm_allows_irq0_override(void)
 {
     return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing();

Index: qemu-mig-advance-clock/target-i386/kvm_i386.h
===================================================================

target-i386/kvm_i386.h +1 -0
@@ -17,6 +17,7 @@
 
 bool kvm_allows_irq0_override(void);
 bool kvm_has_smm(void);
+bool kvm_has_adjust_clock_stable(void);
 void kvm_synchronize_all_tsc(void);
 void kvm_arch_reset_vcpu(X86CPU *cs);
 void kvm_arch_do_init_vcpu(X86CPU *cs);
Signature · 1 line
Eduardo
#15 lore raw

Re: [qemu patch V3 2/2] kvmclock: reduce kvmclock difference on migration

Quoted 281 lines · Eduardo Habkost
Quoted 271 lines · Marcelo Tosatti
Quoted 270 lines · Eduardo Habkost
Quoted 192 lines · Marcelo Tosatti
Quoted 176 lines · Eduardo Habkost
Quoted 175 lines · Marcelo Tosatti
Quoted 118 lines · Eduardo Habkost
Quoted 117 lines · Marcelo Tosatti
Quoted 94 lines · Eduardo Habkost
Quoted 93 lines · Marcelo Tosatti
Quoted 91 lines · Eduardo Habkost

Sorry for not noticing the following issues on the previous reviews. I was only paying attention to the vmstate and machine code:

hw/i386/kvm/clock.c

Quoted 66 lines · Marcelo Tosatti

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |  107 ++++++++++++++++++++++++++++++++++++++++++-------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 106 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +19 -0
@@ -36,6 +36,12 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine supports reliable KVM_GET_CLOCK */
+    bool mach_use_reliable_get_clock;
+
+    /* whether source host supported reliable KVM_GET_CLOCK */
+    bool src_use_reliable_get_clock;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +87,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +110,36 @@

Can you please use "diff -p" on your patches?

Quoted 4 lines
hw/i386/kvm/clock.c +0 -1
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);

hw/i386/kvm/clock.c line 120

Quoted 10 lines
hw/i386/kvm/clock.c +8 -1
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /* local (running VM) restore */
+        if (s->clock_valid) {
+            /*
+             * if host does not support reliable KVM_GET_CLOCK,
+             * read kvmclock value from memory
+             */
+            if (!kvm_has_adjust_clock_stable()) {

Why not use src_use_reliable_get_clock here, too? (Maybe rename it to simply clock_is_reliable?)

Quoted 7 lines
Quoted 5 lines

Because you end up mixing two mental ideas (one: whether the source has KVM_GET_CLOCK, second: whether the destination has KVM_GET_CLOCK into one variable). I find it more confusing than not. Maybe its just my limited brain but i find it
confusing.

I find it simpler and easier to reason about.

I disagree, but i don't mind. Is this what you refer to:

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +1 -1
@@ -37,11 +37,11 @@
     uint64_t clock;
     bool clock_valid;
 
-    /* whether machine supports reliable KVM_GET_CLOCK */
+    /* whether machine type supports reliable get clock */
     bool mach_use_reliable_get_clock;
 
Quoted 7 lines

hw/i386/kvm/clock.c line 43

Quoted 3 lines
hw/i386/kvm/clock.c +1 -2
-    /* whether source host supported reliable KVM_GET_CLOCK */
-    bool src_use_reliable_get_clock;
+    /* whether clock is reliable */

I would make the comment more explicit about its meaning:
/* whether the 'clock' value was obtained in a host with
* reliable KVM_GET_CLOCK */

Done.

Quoted 25 lines

hw/i386/kvm/clock.c line 44

Quoted 1 line
hw/i386/kvm/clock.c +1 -0
+    bool clock_is_reliable;

} KVMClockState;

Quoted 22 lines
hw/i386/kvm/clock.c +0 -16
 struct pvclock_vcpu_time_info {
@@ -112,25 +112,17 @@
         struct kvm_clock_data data = {};
         uint64_t pvclock_via_mem = 0;
 
-        /* local (running VM) restore */
-        if (s->clock_valid) {
-            /*
-             * if host does not support reliable KVM_GET_CLOCK,
-             * read kvmclock value from memory
-             */
-            if (!kvm_has_adjust_clock_stable()) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
-        /* migration/savevm/init restore */
-        } else {
-            /*
-             * use s->clock in case machine uses reliable
-             * get clock and source host supported
-             * reliable get clock
-             */
Quoted 8 lines

hw/i386/kvm/clock.c line 115

Quoted 4 lines
hw/i386/kvm/clock.c +1 -3
-            if (!s->src_use_reliable_get_clock) {
-                pvclock_via_mem = kvmclock_current_nsec(s);
-            }
+

A comment here would be welcome. Something like: "If the host where s->clock was read did not support reliable KVM_GET_CLOCK, read kvmclock value from memory".

Done.

Quoted 10 lines
Quoted 9 lines
hw/i386/kvm/clock.c +9 -0
+        if (!s->clock_is_reliable) {
+            pvclock_via_mem = kvmclock_current_nsec(s);
+        }
+
+        /* migration/savevm/init restore
+         * update clock_is_reliable to match local
+         * host capabilities.
+         */
+        if (s->clock_valid == false) {
Quoted 7 lines
Quoted 4 lines
Quoted 1 line

Why the s->clock_valid check? It seems unnecessary.

Because if s->clock_valid == false (from migration), kvm_has_adjust_clock_stable() from the local host is irrelevant.

That's true, and that's why updating it on kvm_get_clock() looks like a more logical place.

But the current patch updates
s->clock_is_reliable at the "if (running)"
case of kvmclock_vm_state_change().

There, both s->clock_is_reliable and s->clock are
updated in sync:

Quoted 9 lines
        if (s->clock_valid == false) {
            s->clock_is_reliable = kvm_has_adjust_clock_stable();
        }

        /* We can't rely on the saved clock value, just discard it */
        if (pvclock_via_mem) {
            s->clock = pvclock_via_mem;
        }

Here s->clock is updated only to be used to set data.clock in the next line (and never used again). I'm talking about every other s->clock assignment in the code.

Quoted 1 line

However, there is no need to call kvm_get_clock() there.

I'm talking about the existing kvm_get_clock() calls.

Quoted 9 lines

kvm_get_clock() is called at the "} else {" case.

Are you suggesting to

* move s->clock_is_reliable assignment to kvm_get_clock()
* move s->clock assignment to kvm_get_clock()
* call kvm_get_clock() from the "if (running)" because the assignments are in that function now.

No.

Quoted 2 lines

But you don't need KVM_GET_CLOCK to be called there.

This is what I'm suggesting:

[ehabkost: simplified migration logic]
---
 hw/i386/kvm/clock.c    | 95 ++++++++++++++++++++++++++++++++++++++++++--------
 include/hw/i386/pc.h   |  5 +++
 target-i386/kvm.c      |  7 ++++
 target-i386/kvm_i386.h |  1 +
 4 files changed, 94 insertions(+), 14 deletions(-)
hw/i386/kvm/clock.c +17 -0
index 0f75dd3..df07ec7 100644
@@ -35,6 +35,13 @@ typedef struct KVMClockState {
     /*< public >*/
 
     uint64_t clock;
+    /* whether the value in 'clock' is reliable (i.e. was set by a host
+     * with reliable KVM_GET_CLOCK) */
+    bool clock_is_reliable;
+    /* Enable migration of clock_is_reliable. Some machine-types
+     * will disable it for compatibility with older QEMU versions.
+     */
+    bool migrate_reliable_flag;
     bool clock_valid;
 } KVMClockState;
 
@@ -81,6 +88,20 @@ static uint64_t kvmclock_current_nsec(KVMClockState *s)
     return nsec + time.system_time;
 }
 
+static void kvm_get_clock(KVMClockState *s)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+        abort();
+    }
Quoted 8 lines

hw/i386/kvm/clock.c static uint64_t kvm_get_clock(void)

Quoted 4 lines
hw/i386/kvm/clock.c +4 -0
+    s->clock = data.clock;
+    s->clock_is_reliable = kvm_has_adjust_clock_stable();
+}
+

s->clock_is_reliable has to updated at migration, after the first time KVM_SET_CLOCK is called. There is no other place where it should be updated.

Quoted 6 lines

I don't see why you claim that. We can simply update s->clock_is_reliable the next time s->clock is updated. The only code that reads s->clock checks s->clock_is_reliable first. s->clock_is_reliable is a flag _about_ the value in s->clock. I don't see why you want to update them at different times, if it means more complex logic.

Because the table of (wanted) behaviour dictates that.

Quoted 20 lines
Quoted 20 lines

This is the behaviour
desired, according to this table:

index:
mig->c means use s->clock value from migration
mig->m means read pvclock value from memory
(both at migration, only the first time kvmclock_change_vmstate is called).

runt->c means use KVM_GET_CLOCK/KVM_SET_CLOCK at runtime runt->m means read pvclock value from memory
(both at runtime, after the first time kvmclock_change_vmstate is called).

SOURCE                DESTINATION             BEHAVIOUR
get_clock_reliable    get_clock_reliable      mig->c,runt->c  1
get_clock_reliable    ~get_clock_reliable     mig->c,runt->m  2
~get_clock_reliable   get_clock_reliable      mig->m,runt->c  3
~get_clock_reliable   ~get_clock_reliable     mig->m,runt->m  4
Quoted 6 lines
Quoted 4 lines

So looking at your patch below, and thinking of kvm_get_clock() updating s->clock_is_reliable from !running case (that is RUNNING -> ~RUNNING transition) makes no sense at all. That should not happen.

Why not?

Because we want the behaviour of the table above as follows:

First execution of ~RUNNING -> RUNNING transition:

* If source host supports reliable KVM_GET_CLOCK, then its not necessary to read from guest memory (because there is no danger of KVM_GET_CLOCK returning a value which will cause the guest to crash).

* If source host does NOT support reliable KVM_GET_CLOCK, then it is necessary to read from guest memory (because there is danger of KVM_GET_CLOCK returning a value which will cause the guest to crash).

Second and subsequent executions of ~RUNNING -> RUNNING transition:
(say if you pause the guest and continue it):

* If host supports reliable KVM_GET_CLOCK, then
its not necessary to read from guest memory (because there is no danger of KVM_GET_CLOCK returning a value which will cause the guest to crash).

* If host does NOT support reliable KVM_GET_CLOCK, then it is necessary to read from guest memory (because there is danger of KVM_GET_CLOCK returning a value which will cause the guest to crash).

Does that make sense?

Quoted 18 lines
Quoted 3 lines

s->clock_is_reliable should be updated after the first time ~RUNNING -> RUNNING migration is done, which is my patch does.

I don't see why. All we need is that s->clock_is_reliable get updated before any code try to read s->clock again.

Quoted 4 lines

I think your s->clock,s->clock_is_reliable should be in sync suggestion comes from a misunderstanding of how they are used.

s->clock is not read if s->clock_is_reliable is set.

Did you mean "s->clock is not read if s->clock_is_reliable is false"? You are right (except when kvmclock_current_nsec()==0?). But this also means that the only code that reads s->clock also checks s->clock_is_reliable first.

Quoted 1 line
Quoted 6 lines
Quoted 3 lines

Look at my attached patch below (still untested, will test and then resubmit) and check whether it matches the behaviour described in the table above.

It seems to match. But I don't see why my patch doesn't match the table above.

Its just fragile Eduardo: if anyone comes and changes the code as follows.

Source: Does not support reliable KVM_GET_CLOCK.
Destination: Does support reliable KVM_GET_CLOCK.

* Migration from remote host that does not support reliable KVM_GET_CLOCK, to a host that supports it.

* Some modification is done so that:

1) Incoming migration sets s->clock_is_reliable=false from source.
2) _Before_ vm_start(), new code decides to issue kvm_get_clock().

Your code sets s->clock_reliable = true (because local host supports it), and failure. Do you see the problem?

(*)

Quoted 40 lines
Quoted 40 lines
Quoted 39 lines
hw/i386/kvm/clock.c +11 -7
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,16 +112,18 @@ static void kvmclock_vm_state_change(void *opaque, int running,
 
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);
 
         s->clock_valid = false;
 
-        /* We can't rely on the migrated clock value, just discard it */
-        if (time_at_migration) {
-            s->clock = time_at_migration;
+        /* if host that set s->clock did not support reliable KVM_GET_CLOCK,
+         * read kvmclock value from memory
+         */
+        if (!s->clock_is_reliable) {
+            data.clock = kvmclock_current_nsec(s);
+        } else {
+            data.clock = s->clock;
         }
 
-        data.clock = s->clock;
         ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
         if (ret < 0) {
             fprintf(stderr, "KVM_SET_CLOCK failed: %s\n", strerror(ret));
@@ -120,22 +143,23 @@ static void kvmclock_vm_state_change(void *opaque, int running,
 
             }
         }
     } else {
-        struct kvm_clock_data data;
-        int ret;
 
+        /*FIXME: is this really possible to happen?
+         * Can kvmclock_vm_state_change(s, 0) be called twice without
+         * a kvmclock_vm_state_change(s, 1) call in between?
+         */
Quoted 5 lines
Quoted 1 line

It can't: check the code where notifiers are called.

Yet another reason to simplify the code: if we don't need different code paths for local stop/resume vs migration, we can remove s->clock_valid completely.

Yes it can be removed... But i'm just trying to fix a bug, not rewrite code while at it.

Quoted 20 lines
Quoted 6 lines
Quoted 6 lines
hw/i386/kvm/clock.c +0 -0
         if (s->clock_valid) {
             return;
         }
 
         kvm_synchronize_all_tsc();
 
Quoted 13 lines

hw/i386/kvm/clock.c static void kvmclock_vm_state_change(void *opaque, int running, · line 163

Quoted 13 lines
hw/i386/kvm/clock.c +6 -7
-        ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
-        if (ret < 0) {
-            fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
-            abort();
-        }
-        s->clock = data.clock;
-
+        kvm_get_clock(s);
+        /* FIXME: the comment below doesn't match what is done at
+         * kvmclock_pre_save(). The comment must be updated, or
+         * kvmclock_pre_save() must be changed. I don't know what's
+         * the right thing to do.
+         */
Quoted 2 lines

We still need to address this comment. Your patch keeps the comment that contradicts kvmclock_pre_save().

The comment refers to the fact that you should not do:

Event stop_vm()
saved_clock1 = KVM_GET_CLOCK.

Then again with the VM stopped, overwrite saved_clock:
saved_clock2 = KVM_GET_CLOCK.

You should always use saved_clock1. Thats what its about.

Updated it.

Quoted 439 lines
Quoted 3 lines

This fails for all cases (1,2,3,4) in the table above.

I can't see why it fails. The ordering of events from migration followed by vm stop/resume should be:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-SRC
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-SRC
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
--- | MIGRATION                   | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
dst |   KVM_SET_CLOCK             | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-DEST
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-DEST
dst |   KVM_SET_CLOCK             | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-DEST

Specific cases from your table are below. See the KVM_SET_CLOCK rows.

Case 1:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | 1
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | 1
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | 1
--- | MIGRATION                   | TIME-AT-PRE-SAVE | 1
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | 1
dst |   KVM_SET_CLOCK(s->clock)   | TIME-AT-PRE-SAVE | 1
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | 1
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | 1
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | 1
dst |   KVM_SET_CLOCK(s->clock)   | TIME-AT-VM-STOP  | 1

Case 2:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | 1
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | 1
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | 1
--- | MIGRATION                   | TIME-AT-PRE-SAVE | 1
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | 1
dst |   KVM_SET_CLOCK(s->clock)   | TIME-AT-PRE-SAVE | 1
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | 1
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | 0
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | 0
dst |   KVM_SET_CLOCK(from_mem)   | TIME-AT-VM-STOP  | 0

Case 3:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | 0
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | 0
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | 0
--- | MIGRATION                   | TIME-AT-PRE-SAVE | 0
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | 0
dst |   KVM_SET_CLOCK(from_mem)   | TIME-AT-PRE-SAVE | 0
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | 0
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | 1
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | 1
dst |   KVM_SET_CLOCK(s->clock)   | TIME-AT-VM-STOP  | 1

Case 4:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | 0
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | 0
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | 0
--- | MIGRATION                   | TIME-AT-PRE-SAVE | 0
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | 0
dst |   KVM_SET_CLOCK(from_mem)   | TIME-AT-PRE-SAVE | 0
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | 0
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | 0
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | 0
dst |   KVM_SET_CLOCK(from_mem)   | TIME-AT-VM-STOP  | 0
Quoted 172 lines
Quoted 172 lines
hw/i386/kvm/clock.c +0 -0
         /*
          * If the VM is stopped, declare the clock state valid to

* avoid re-reading it on next vmsave (which would return

hw/i386/kvm/clock.c +43 -0
@@ -152,22 +176,65 @@ static void kvmclock_realize(DeviceState *dev, Error **errp)
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
+static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    return s->migrate_reliable_flag;
+}
+
+static const VMStateDescription kvmclock_reliable_get_clock = {
+    .name = "kvmclock/clock_is_reliable",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = kvmclock_src_use_reliable_get_clock,
+    .fields = (VMStateField[]) {
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+static void kvmclock_pre_save(void *opaque)
+{
+    KVMClockState *s = opaque;
+    /*TODO: add comment explaining why this is here:
+     * "re-read clock value here because [...]"" */
+
+    /*XXX: is it correct to call kvm_get_clock() here
+     * if s->clock_is_valid is true? (meaning the kvmclock was
+     * already stopped)
+     */
+    kvm_get_clock(s);
+}
+
 static const VMStateDescription kvmclock_vmsd = {
     .name = "kvmclock",
     .version_id = 1,
     .minimum_version_id = 1,
+    .pre_save = kvmclock_pre_save,
     .fields = (VMStateField[]) {
         VMSTATE_UINT64(clock, KVMClockState),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * []) {
+        &kvmclock_reliable_get_clock,
+        NULL
     }
 };
 
+static Property kvmclock_properties[] = {
+    DEFINE_PROP_BOOL("x-migrate-reliable-flag", KVMClockState,
+                      migrate_reliable_flag, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void kvmclock_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->realize = kvmclock_realize;
     dc->vmsd = &kvmclock_vmsd;
+    dc->props = kvmclock_properties;
 }
 
 static const TypeInfo kvmclock_info = {
include/hw/i386/pc.h +5 -0
index 4b74130..fb80d69 100644
@@ -372,6 +372,11 @@ bool e820_get_entry(int, uint32_t, uint64_t *, uint64_t *);
 #define PC_COMPAT_2_7 \
     HW_COMPAT_2_7 \
     {\
+        .driver   = "kvmclock",\
+        .property = "x-migrate-reliable-flag",\
+        .value    = "off",\
+    },\
+    {\
         .driver   = TYPE_X86_CPU,\
         .property = "l3-cache",\
         .value    = "off",\
target-i386/kvm.c +7 -0
index f62264a..10a9cd8 100644
@@ -117,6 +117,13 @@ bool kvm_has_smm(void)
     return kvm_check_extension(kvm_state, KVM_CAP_X86_SMM);
 }
 
+bool kvm_has_adjust_clock_stable(void)
+{
+    int ret = kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK);
+
+    return (ret == KVM_CLOCK_TSC_STABLE);
+}
+
 bool kvm_allows_irq0_override(void)
 {
     return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing();
target-i386/kvm_i386.h +1 -0
index 7607929..bfce427 100644
@@ -17,6 +17,7 @@
 
 bool kvm_allows_irq0_override(void);
 bool kvm_has_smm(void);
+bool kvm_has_adjust_clock_stable(void);
 void kvm_synchronize_all_tsc(void);
 void kvm_arch_reset_vcpu(X86CPU *cs);
 void kvm_arch_do_init_vcpu(X86CPU *cs);
Signature · 1 line
2.7.4
Quoted 51 lines
Quoted 31 lines
Quoted 18 lines

If s->clock_valid == true, kvm_has_adjust_clock_stable() is what decides whether to use KVM_GET_CLOCK or not.

Quoted 2 lines

(If this is an optimization to avoid extra ioctl() calls, you can cache it inside kvm_has_adjust_clock_stable()).

Its not.

Quoted 5 lines

hw/i386/kvm/clock.c static uint64_t kvm_get_clock(void) · line 101

Quoted 2 lines
hw/i386/kvm/clock.c +1 -0
+            s->clock_is_reliable = kvm_has_adjust_clock_stable();
         }

This should go to kvm_get_clock(), so s->clock and s->clock_is_reliable are guaranteed to be always in sync.

Quoted 8 lines

No: the point to change clock_is_reliable is the following: whether its the first read of the clock (migration), or subsequent reads (normal machine operation).

Now kvm_get_clock() just reads the clock, so i don't see it as the right place to change the variable.

Agreed?

I don't agree. clock_is_reliable is information about the current value in s->clock, I don't see why we need to change s->clock and s->clock_is_reliable at different places in the code.

Quoted 5 lines

Your code might work too, but I can't tell without reviewing very carefully the state transitions KVMClockState could go through. I don't see the point of making the state transitions in this code even more complex, if we can simply stick to a simple "s->clock and s->clock_is_reliable are always in sync" rule.

It is updating from the same place:

        /* migration/savevm/init restore
         * update clock_is_reliable to match local
         * host capabilities.
         */
        if (s->clock_valid == false) {
            s->clock_is_reliable = kvm_has_adjust_clock_stable();
        }

        /* We can't rely on the saved clock value, just discard it */
        if (pvclock_via_mem) {
            s->clock = pvclock_via_mem;
        }
Quoted 4 lines
Quoted 2 lines

I'm talking about the other s->clock assignments. This one doesn't even seem to be necessary.

It is necessary: this is how you read pvclock from memory.

It is not necessary if you set data.clock directly.

But this doesn't matter for this discussion. My new patch below keeps that line, just to make the diff smaller.

Quoted 142 lines
Quoted 50 lines
Quoted 50 lines
Quoted 50 lines
Quoted 49 lines
Quoted 49 lines
Quoted 46 lines
hw/i386/kvm/clock.c +6 -12
         /* We can't rely on the saved clock value, just discard it */
@@ -181,20 +173,14 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
-    /*
-     * On machine types that support reliable KVM_GET_CLOCK,
-     * if host kernel does provide reliable KVM_GET_CLOCK,
-     * set src_use_reliable_get_clock=true so that destination
-     * avoids reading kvmclock from memory.
-     */
-    if (s->mach_use_reliable_get_clock && kvm_has_adjust_clock_stable()) {
-        s->src_use_reliable_get_clock = true;
+    if (kvm_has_adjust_clock_stable()) {
+        s->clock_is_reliable = true;
     }
 
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
-static bool kvmclock_src_use_reliable_get_clock(void *opaque)
+static bool kvmclock_clock_is_reliable_needed(void *opaque)
 {
     KVMClockState *s = opaque;
 
@@ -202,12 +188,12 @@
 }
 
 static const VMStateDescription kvmclock_reliable_get_clock = {
-    .name = "kvmclock/src_use_reliable_get_clock",
+    .name = "kvmclock/clock_is_reliable",
     .version_id = 1,
     .minimum_version_id = 1,
-    .needed = kvmclock_src_use_reliable_get_clock,
+    .needed = kvmclock_clock_is_reliable_needed,
     .fields = (VMStateField[]) {
-        VMSTATE_BOOL(src_use_reliable_get_clock, KVMClockState),
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
         VMSTATE_END_OF_LIST()
     }
 };
Signature · 1 line
Eduardo
Quoted 5 lines
Quoted 2 lines
Quoted 2 lines
Signature · 1 line
Eduardo
Signature · 1 line
Eduardo

kvmclock: reduce kvmclock difference on migration

Check for KVM_CAP_ADJUST_CLOCK capability KVM_CLOCK_TSC_STABLE, which indicates that KVM_GET_CLOCK returns a value as seen by the guest at that moment.

For new machine types, use this value rather than reading from guest memory.

This reduces kvmclock difference on migration from 5s to 0.1s (when max_downtime == 5s).

---
 hw/i386/kvm/clock.c    |   97 +++++++++++++++++++++++++++++++++++++++++--------
 include/hw/i386/pc.h   |    5 ++
 target-i386/kvm.c      |    7 +++
 target-i386/kvm_i386.h |    1 
 4 files changed, 96 insertions(+), 14 deletions(-)

v2:
- improve variable names (Juan)
- consolidate code on kvm_get_clock function (Paolo)
- return mach_use_reliable_get_clock from needed function (Paolo) v3:
- simplify check for src_use_reliable_get_clock (Eduardo)
- move src_use_reliable_get_clock initialization to realize (Eduardo)

Index: qemu-mig-advance-clock/hw/i386/kvm/clock.c
===================================================================

hw/i386/kvm/clock.c +28 -3
@@ -36,6 +36,13 @@
 
     uint64_t clock;
     bool clock_valid;
+
+    /* whether machine type supports reliable get clock */
+    bool mach_use_reliable_get_clock;
+
+    /* whether the 'clock' value was obtained in a host with
+     * reliable KVM_GET_CLOCK */
+    bool clock_is_reliable;
 } KVMClockState;
 
 struct pvclock_vcpu_time_info {
@@ -81,6 +88,19 @@
     return nsec + time.system_time;
 }
 
+static uint64_t kvm_get_clock(void)
+{
+    struct kvm_clock_data data;
+    int ret;
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+                abort();
+    }
+    return data.clock;
+}
+
 static void kvmclock_vm_state_change(void *opaque, int running,
                                      RunState state)
 {
@@ -91,15 +111,31 @@
 
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /*
+         * If the host where s->clock was read did not support reliable
+         * KVM_GET_CLOCK, read kvmclock value from memory.
+         */
+        if (!s->clock_is_reliable) {
+            pvclock_via_mem = kvmclock_current_nsec(s);
+        }
 
-        /* We can't rely on the migrated clock value, just discard it */

hw/i386/kvm/clock.c line 131

Quoted 10 lines
hw/i386/kvm/clock.c +6 -2
-        if (time_at_migration) {
-            s->clock = time_at_migration;
+        /* migration/savevm/init restore
+         * update clock_is_reliable to match local
+         * host capabilities.
+         */
+        if (s->clock_valid == false) {
+            s->clock_is_reliable = kvm_has_adjust_clock_stable();
         }
 

If we are reusing clock_valid for a different purpose, I suggest we rename it to something clearer, like "clock_is_local" or "clock_is_stopped".

But I still don't see the need for this convoluted logic, if we can simply set s->clock_is_reliable and s->clock at the same time. This even allow us to remove s->clock_valid completely.

hw/i386/kvm/clock.c line 138

Quoted 7 lines
hw/i386/kvm/clock.c +7 -0
+        /* We can't rely on the saved clock value, just discard it */
+        if (pvclock_via_mem) {
+            s->clock = pvclock_via_mem;
+        }
+
+        s->clock_valid = false;
+

I suggest the following on top of your patch. It removes 27 unnecessary lines from the code.

Quoted 3 lines

But, really, I'm tired of this discussion. If you want to keep the complex logic you suggest and others are happy with it, go ahead. You just won't get my Reviewed-by.

I have been addressing all the comments you made so far Eduardo, and have a point at (*) which seems valid to me.
This is the reason the patch is the way it is now.

But sure, i will include your next patch in the series, test it, and if someone does kvm_get_clock() in between those two points in the code, then it'll break. I'll add a comment to that effect to warn users.

Quoted 163 lines

But in addition to this discussion about migration_is_safe, I have a question below about kvmclock_pre_save(), which seems to be a real problem. See below.

---
 hw/i386/kvm/clock.c | 37 +++++--------------------------------
 1 file changed, 5 insertions(+), 32 deletions(-)
hw/i386/kvm/clock.c +5 -31
index e179862..359abce 100644
@@ -35,8 +35,6 @@ typedef struct KVMClockState {
     /*< public >*/
 
     uint64_t clock;
-    bool clock_valid;
-
     /* whether machine type supports reliable get clock */
     bool mach_use_reliable_get_clock;
 
@@ -88,7 +86,7 @@ static uint64_t kvmclock_current_nsec(KVMClockState *s)
     return nsec + time.system_time;
 }
 
-static uint64_t kvm_get_clock(void)
+static void kvm_get_clock(KVMClockState *s)
 {
     struct kvm_clock_data data;
     int ret;
@@ -98,7 +96,8 @@ static uint64_t kvm_get_clock(void)
         fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
                 abort();
     }
-    return data.clock;
+    s->clock = data.clock;
+    s->clock_is_reliable = kvm_has_adjust_clock_stable();
 }
 
 static void kvmclock_vm_state_change(void *opaque, int running,
@@ -121,21 +120,11 @@ static void kvmclock_vm_state_change(void *opaque, int running,
             pvclock_via_mem = kvmclock_current_nsec(s);
         }
 
-        /* migration/savevm/init restore
-         * update clock_is_reliable to match local
-         * host capabilities.
-         */
-        if (s->clock_valid == false) {
-            s->clock_is_reliable = kvm_has_adjust_clock_stable();
-        }
         /* We can't rely on the saved clock value, just discard it */
         if (pvclock_via_mem) {
             s->clock = pvclock_via_mem;
         }
 
-        s->clock_valid = false;
-
         data.clock = s->clock;
         ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
         if (ret < 0) {
@@ -156,20 +145,8 @@ static void kvmclock_vm_state_change(void *opaque, int running,
             }
         }
     } else {
-
-        if (s->clock_valid) {
-            return;
-        }
-
         kvm_synchronize_all_tsc();
-
-        s->clock = kvm_get_clock();
-        /*
-         * If the VM is stopped, declare the clock state valid to
-         * avoid re-reading it on next vmsave (which would return
-         * a different value). Will be reset when the VM is continued.
-         */
-        s->clock_valid = true;
+        kvm_get_clock(s);
     }
 }
 
@@ -177,10 +154,6 @@ static void kvmclock_realize(DeviceState *dev, Error **errp)
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
-    if (kvm_has_adjust_clock_stable()) {
-        s->clock_is_reliable = true;
-    }
-
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
@@ -206,7 +179,7 @@ static void kvmclock_pre_save(void *opaque)
 {
     KVMClockState *s = opaque;
 
-    s->clock = kvm_get_clock();
+    kvm_get_clock(s);
 }
 
 static const VMStateDescription kvmclock_vmsd = {
Quoted 59 lines
hw/i386/kvm/clock.c +24 -9
+
         data.clock = s->clock;
         ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
         if (ret < 0) {
@@ -120,8 +156,6 @@
             }
         }
     } else {
-        struct kvm_clock_data data;
-        int ret;
 
         if (s->clock_valid) {
             return;
@@ -129,13 +163,7 @@
 
         kvm_synchronize_all_tsc();
 
-        ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
-        if (ret < 0) {
-            fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
-            abort();
-        }
-        s->clock = data.clock;
-
+        s->clock = kvm_get_clock();
         /*
          * If the VM is stopped, declare the clock state valid to
          * avoid re-reading it on next vmsave (which would return
@@ -149,25 +177,66 @@
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
+    if (kvm_has_adjust_clock_stable()) {
+        s->clock_is_reliable = true;
+    }
+
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
+static bool kvmclock_clock_is_reliable_needed(void *opaque)
+{
+    KVMClockState *s = opaque;
+
+    return s->mach_use_reliable_get_clock;
+}
+
+static const VMStateDescription kvmclock_reliable_get_clock = {
+    .name = "kvmclock/clock_is_reliable",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .needed = kvmclock_clock_is_reliable_needed,
+    .fields = (VMStateField[]) {
+        VMSTATE_BOOL(clock_is_reliable, KVMClockState),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
Quoted 8 lines

hw/i386/kvm/clock.c line 208

Quoted 4 lines
hw/i386/kvm/clock.c +4 -0
+static void kvmclock_pre_save(void *opaque)
+{
+    KVMClockState *s = opaque;
+

We still need a comment here explaining why we need to re-read the clock on pre_save if s->clock was already set on kvmclock_vm_state_change().

OK.

Quoted 3 lines

Also, what if we are migrating a VM that was already paused 10 minutes ago? Should we migrate the s->clock value from 10 minutes ago, or the one read at pre_save?

The one read at pre_save. I'll add a comment.

Quoted 83 lines
Quoted 80 lines
hw/i386/kvm/clock.c +15 -0
+    s->clock = kvm_get_clock();
+}
+
 static const VMStateDescription kvmclock_vmsd = {
     .name = "kvmclock",
     .version_id = 1,
     .minimum_version_id = 1,
+    .pre_save = kvmclock_pre_save,
     .fields = (VMStateField[]) {
         VMSTATE_UINT64(clock, KVMClockState),
         VMSTATE_END_OF_LIST()
+    },
+    .subsections = (const VMStateDescription * []) {
+        &kvmclock_reliable_get_clock,
+        NULL
     }
 };
 
+static Property kvmclock_properties[] = {
+    DEFINE_PROP_BOOL("x-mach-use-reliable-get-clock", KVMClockState,
+                      mach_use_reliable_get_clock, true),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
 static void kvmclock_class_init(ObjectClass *klass, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(klass);
 
     dc->realize = kvmclock_realize;
     dc->vmsd = &kvmclock_vmsd;
+    dc->props = kvmclock_properties;
 }
 
 static const TypeInfo kvmclock_info = {

Index: qemu-mig-advance-clock/include/hw/i386/pc.h
===================================================================

include/hw/i386/pc.h +5 -0
@@ -370,6 +370,11 @@
 #define PC_COMPAT_2_7 \
     HW_COMPAT_2_7 \
     {\
+        .driver   = "kvmclock",\
+        .property = "x-mach-use-reliable-get-clock",\
+        .value    = "off",\
+    },\
+    {\
         .driver   = TYPE_X86_CPU,\
         .property = "l3-cache",\
         .value    = "off",\

Index: qemu-mig-advance-clock/target-i386/kvm.c
===================================================================

target-i386/kvm.c +7 -0
@@ -117,6 +117,13 @@
     return kvm_check_extension(kvm_state, KVM_CAP_X86_SMM);
 }
 
+bool kvm_has_adjust_clock_stable(void)
+{
+    int ret = kvm_check_extension(kvm_state, KVM_CAP_ADJUST_CLOCK);
+
+    return (ret == KVM_CLOCK_TSC_STABLE);
+}
+
 bool kvm_allows_irq0_override(void)
 {
     return !kvm_irqchip_in_kernel() || kvm_has_gsi_routing();

Index: qemu-mig-advance-clock/target-i386/kvm_i386.h
===================================================================

target-i386/kvm_i386.h +1 -0
@@ -17,6 +17,7 @@
 
 bool kvm_allows_irq0_override(void);
 bool kvm_has_smm(void);
+bool kvm_has_adjust_clock_stable(void);
 void kvm_synchronize_all_tsc(void);
 void kvm_arch_reset_vcpu(X86CPU *cs);
 void kvm_arch_do_init_vcpu(X86CPU *cs);
Signature · 1 line
Eduardo
#16 lore raw

Re: [qemu patch V3 2/2] kvmclock: reduce kvmclock difference on migration

Quoted 1 line · Marcelo Tosatti

On Mon, Dec 05, 2016 at 01:17:42PM -0200, Eduardo Habkost wrote:

[...]

Quoted 20 lines
Quoted 20 lines
Quoted 19 lines
Quoted 5 lines
(quoted diff) +4 -0
+static void kvm_get_clock(KVMClockState *s)
+{
+    struct kvm_clock_data data;
+    int ret;

Review

Quoted 10 lines
(quoted diff) +10 -0
+
+    ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
+    if (ret < 0) {
+        fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
+        abort();
+    }
+    s->clock = data.clock;
+    s->clock_is_reliable = kvm_has_adjust_clock_stable();
+}
+

s->clock_is_reliable has to updated at migration, after the first time KVM_SET_CLOCK is called. There is no other place where it should be updated.

Quoted 8 lines
Quoted 6 lines

I don't see why you claim that. We can simply update s->clock_is_reliable the next time s->clock is updated. The only code that reads s->clock checks s->clock_is_reliable first. s->clock_is_reliable is a flag _about_ the value in s->clock. I don't see why you want to update them at different times, if it means more complex logic.

Because the table of (wanted) behaviour dictates that.

My suggestion also follows the table strictly.

Quoted 28 lines
Quoted 21 lines
Quoted 21 lines

This is the behaviour
desired, according to this table:

index:
mig->c means use s->clock value from migration
mig->m means read pvclock value from memory
(both at migration, only the first time kvmclock_change_vmstate is called).

runt->c means use KVM_GET_CLOCK/KVM_SET_CLOCK at runtime runt->m means read pvclock value from memory
(both at runtime, after the first time kvmclock_change_vmstate is called).

SOURCE                DESTINATION             BEHAVIOUR
get_clock_reliable    get_clock_reliable      mig->c,runt->c  1
get_clock_reliable    ~get_clock_reliable     mig->c,runt->m  2
~get_clock_reliable   get_clock_reliable      mig->m,runt->c  3
~get_clock_reliable   ~get_clock_reliable     mig->m,runt->m  4
Quoted 6 lines
Quoted 4 lines

So looking at your patch below, and thinking of kvm_get_clock() updating s->clock_is_reliable from !running case (that is RUNNING -> ~RUNNING transition) makes no sense at all. That should not happen.

Why not?

Quoted 8 lines

Because we want the behaviour of the table above as follows:

First execution of ~RUNNING -> RUNNING transition:

* If source host supports reliable KVM_GET_CLOCK, then its not necessary to read from guest memory (because there is no danger of KVM_GET_CLOCK returning a value which will cause the guest to crash).

Correct. And this can be generalized to:

"If the host that set s->clock support reliable KVM_GET_CLOCK, then its not necessary to read from guest memory [...]".

Quoted 5 lines

* If source host does NOT support reliable KVM_GET_CLOCK, then it is necessary to read from guest memory (because there is danger of KVM_GET_CLOCK returning a value which will cause the guest to crash).

Correct. And this can be generalized to:

"If host that set s->clock do NOT support reliable KVM_GET_CLOCK, then it is necessary to read from guest memory [...]"

Quoted 9 lines

Second and subsequent executions of ~RUNNING -> RUNNING transition:
(say if you pause the guest and continue it):

* If host supports reliable KVM_GET_CLOCK, then
its not necessary to read from guest memory (because there is no danger of KVM_GET_CLOCK returning a value which will cause the guest to crash).

Correct. And this can be generalized to:

"If the host that set s->clock support reliable KVM_GET_CLOCK, then its not necessary to [...]".

Quoted 5 lines

* If host does NOT support reliable KVM_GET_CLOCK, then it is necessary to read from guest memory (because there is danger of KVM_GET_CLOCK returning a value which will cause the guest to crash).

Correct. And this can be generalized to:

"If host that set s->clock do NOT support reliable KVM_GET_CLOCK, then it is necessary to read from guest memory [...]"

Quoted 1 line

Does that make sense?

Yes. And settting s->clock_is_reliable and s->clock at the same time guarantees that.

Quoted 35 lines
Quoted 19 lines
Quoted 4 lines

s->clock_is_reliable should be updated after the first time ~RUNNING -> RUNNING migration is done, which is my patch does.

I don't see why. All we need is that s->clock_is_reliable get updated before any code try to read s->clock again.

Quoted 4 lines

I think your s->clock,s->clock_is_reliable should be in sync suggestion comes from a misunderstanding of how they are used.

s->clock is not read if s->clock_is_reliable is set.

Did you mean "s->clock is not read if s->clock_is_reliable is false"? You are right (except when kvmclock_current_nsec()==0?). But this also means that the only code that reads s->clock also checks s->clock_is_reliable first.

Quoted 1 line
Quoted 6 lines
Quoted 3 lines

Look at my attached patch below (still untested, will test and then resubmit) and check whether it matches the behaviour described in the table above.

It seems to match. But I don't see why my patch doesn't match the table above.

Its just fragile Eduardo: if anyone comes and changes the code as follows.

Source: Does not support reliable KVM_GET_CLOCK.
Destination: Does support reliable KVM_GET_CLOCK.

* Migration from remote host that does not support reliable KVM_GET_CLOCK, to a host that supports it.

Quoted 7 lines

* Some modification is done so that:

1) Incoming migration sets s->clock_is_reliable=false from source.
2) _Before_ vm_start(), new code decides to issue kvm_get_clock().

Your code sets s->clock_reliable = true (because local host supports it), and failure. Do you see the problem?

The whole point of (my version of) kvm_get_clock() is to set s->clock. If any code wants to change s->clock before vm_start(), we are already screwed. I really don't think this is a realistic scenario.

If you also want a function that returns KVM_GET_CLOCK but do not change s->clock or s->clock_is_reliable, you can write one and I won't complain.

Quoted 43 lines

(*)

Quoted 40 lines
Quoted 40 lines
Quoted 39 lines

static void kvmclock_vm_state_change(void *opaque, int running,
RunState state)
{

(quoted diff) +11 -7
@@ -91,16 +112,18 @@ static void kvmclock_vm_state_change(void *opaque, int running,
 
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);
 
         s->clock_valid = false;
 
-        /* We can't rely on the migrated clock value, just discard it */
-        if (time_at_migration) {
-            s->clock = time_at_migration;
+        /* if host that set s->clock did not support reliable KVM_GET_CLOCK,
+         * read kvmclock value from memory
+         */
+        if (!s->clock_is_reliable) {
+            data.clock = kvmclock_current_nsec(s);
+        } else {
+            data.clock = s->clock;
         }
 
-        data.clock = s->clock;
         ret = kvm_vm_ioctl(kvm_state, KVM_SET_CLOCK, &data);
         if (ret < 0) {
             fprintf(stderr, "KVM_SET_CLOCK failed: %s\n", strerror(ret));
@@ -120,22 +143,23 @@ static void kvmclock_vm_state_change(void *opaque, int running,
 
             }
         }
     } else {
-        struct kvm_clock_data data;
-        int ret;
 
+        /*FIXME: is this really possible to happen?
+         * Can kvmclock_vm_state_change(s, 0) be called twice without
+         * a kvmclock_vm_state_change(s, 1) call in between?
+         */
Quoted 8 lines
Quoted 5 lines
Quoted 1 line

It can't: check the code where notifiers are called.

Yet another reason to simplify the code: if we don't need different code paths for local stop/resume vs migration, we can remove s->clock_valid completely.

Yes it can be removed... But i'm just trying to fix a bug, not rewrite code while at it.

No need to remove it right now. But if the logic can make the extra field unnecessary, that's another reason to simplify it. I normally don't care about 2 or 3 extra lines of code, but in this case I really think the complexity you are adding is unnecessary.

Quoted 26 lines
Quoted 21 lines
Quoted 7 lines
Quoted 7 lines
(quoted diff) +0 -0
         if (s->clock_valid) {
             return;
         }
 
         kvm_synchronize_all_tsc();
 
Quoted 13 lines

Review static void kvmclock_vm_state_change(void *opaque, int running, · line 163

Quoted 13 lines
(quoted diff) +6 -7
-        ret = kvm_vm_ioctl(kvm_state, KVM_GET_CLOCK, &data);
-        if (ret < 0) {
-            fprintf(stderr, "KVM_GET_CLOCK failed: %s\n", strerror(ret));
-            abort();
-        }
-        s->clock = data.clock;
-
+        kvm_get_clock(s);
+        /* FIXME: the comment below doesn't match what is done at
+         * kvmclock_pre_save(). The comment must be updated, or
+         * kvmclock_pre_save() must be changed. I don't know what's
+         * the right thing to do.
+         */
Quoted 2 lines

We still need to address this comment. Your patch keeps the comment that contradicts kvmclock_pre_save().

The comment refers to the fact that you should not do:

Quoted 7 lines

Event stop_vm()
saved_clock1 = KVM_GET_CLOCK.

Then again with the VM stopped, overwrite saved_clock:
saved_clock2 = KVM_GET_CLOCK.

You should always use saved_clock1. Thats what its about.

I am not sure I follow. pre_save runs after stop_vm() and overwrites saved_clock1 (s->clock) with a new value, doesn't it? So is it incorrect?

Quoted 88 lines

Updated it.

Quoted 85 lines
Quoted 3 lines

This fails for all cases (1,2,3,4) in the table above.

I can't see why it fails. The ordering of events from migration followed by vm stop/resume should be:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-SRC
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-SRC
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
--- | MIGRATION                   | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
dst |   KVM_SET_CLOCK             | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | CAP_ADJUST_CLOCK-on-SRC
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-DEST
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-DEST
dst |   KVM_SET_CLOCK             | TIME-AT-VM-STOP  | CAP_ADJUST_CLOCK-on-DEST

Specific cases from your table are below. See the KVM_SET_CLOCK rows.

Case 1:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | 1
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | 1
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | 1
--- | MIGRATION                   | TIME-AT-PRE-SAVE | 1
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | 1
dst |   KVM_SET_CLOCK(s->clock)   | TIME-AT-PRE-SAVE | 1
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | 1
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | 1
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | 1
dst |   KVM_SET_CLOCK(s->clock)   | TIME-AT-VM-STOP  | 1

Case 2:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | 1
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | 1
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | 1
--- | MIGRATION                   | TIME-AT-PRE-SAVE | 1
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | 1
dst |   KVM_SET_CLOCK(s->clock)   | TIME-AT-PRE-SAVE | 1
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | 1
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | 0
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | 0
dst |   KVM_SET_CLOCK(from_mem)   | TIME-AT-VM-STOP  | 0

Case 3:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | 0
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | 0
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | 0
--- | MIGRATION                   | TIME-AT-PRE-SAVE | 0
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | 0
dst |   KVM_SET_CLOCK(from_mem)   | TIME-AT-PRE-SAVE | 0
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | 0
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | 1
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | 1
dst |   KVM_SET_CLOCK(s->clock)   | TIME-AT-VM-STOP  | 1

Case 4:

    | Event                       | s->clock         | s->clock_is_reliable
src | kvmclock_vm_state_change(0) | -                | -
src |   kvm_get_clock()           | TIME-AT-VM-STOP  | 0
src | kvmclock_pre_save()         | TIME-AT-VM-STOP  | 0
src |   kvm_get_clock()           | TIME-AT-PRE-SAVE | 0
--- | MIGRATION                   | TIME-AT-PRE-SAVE | 0
dst | kvmclock_vm_state_change(1) | TIME-AT-PRE-SAVE | 0
dst |   KVM_SET_CLOCK(from_mem)   | TIME-AT-PRE-SAVE | 0
dst | kvmclock_vm_state_change(0) | TIME-AT-PRE-SAVE | 0
dst |   kvm_get_clock()           | TIME-AT-VM-STOP  | 0
dst | kvmclock_vm_state_change(1) | TIME-AT-VM-STOP  | 0
dst |   KVM_SET_CLOCK(from_mem)   | TIME-AT-VM-STOP  | 0

[...]

Quoted 54 lines
Quoted 50 lines
Quoted 20 lines

static void kvmclock_vm_state_change(void *opaque, int running,
RunState state)
{

(quoted diff) +8 -3
@@ -91,15 +111,31 @@
 
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t time_at_migration = kvmclock_current_nsec(s);
+        uint64_t pvclock_via_mem = 0;
 
-        s->clock_valid = false;
+        /*
+         * If the host where s->clock was read did not support reliable
+         * KVM_GET_CLOCK, read kvmclock value from memory.
+         */
+        if (!s->clock_is_reliable) {
+            pvclock_via_mem = kvmclock_current_nsec(s);
+        }
 
-        /* We can't rely on the migrated clock value, just discard it */

Review line 131

Quoted 10 lines
(quoted diff) +6 -2
-        if (time_at_migration) {
-            s->clock = time_at_migration;
+        /* migration/savevm/init restore
+         * update clock_is_reliable to match local
+         * host capabilities.
+         */
+        if (s->clock_valid == false) {
+            s->clock_is_reliable = kvm_has_adjust_clock_stable();
         }
 

If we are reusing clock_valid for a different purpose, I suggest we rename it to something clearer, like "clock_is_local" or "clock_is_stopped".

But I still don't see the need for this convoluted logic, if we can simply set s->clock_is_reliable and s->clock at the same time. This even allow us to remove s->clock_valid completely.

Review line 138

Quoted 7 lines
(quoted diff) +7 -0
+        /* We can't rely on the saved clock value, just discard it */
+        if (pvclock_via_mem) {
+            s->clock = pvclock_via_mem;
+        }
+
+        s->clock_valid = false;
+

I suggest the following on top of your patch. It removes 27 unnecessary lines from the code.

Quoted 3 lines

But, really, I'm tired of this discussion. If you want to keep the complex logic you suggest and others are happy with it, go ahead. You just won't get my Reviewed-by.

Quoted 8 lines

I have been addressing all the comments you made so far Eduardo, and have a point at (*) which seems valid to me.
This is the reason the patch is the way it is now.

But sure, i will include your next patch in the series, test it, and if someone does kvm_get_clock() in between those two points in the code, then it'll break. I'll add a comment to that effect to warn users.

kvm_get_clock() changes s->clock too. If you set s->clock at the wrong moment you'll have bigger problems than clock_is_reliable being incorrect. But if your worry is kvm_get_clock(), then we can just do the following. Would that be OK?

Then I can send a patch to remove clock_is_valid later, as a follow-up.

(I have one additional comment about pre_save(), below)

---

 hw/i386/kvm/clock.c | 16 ++++------------
 1 file changed, 4 insertions(+), 12 deletions(-)
hw/i386/kvm/clock.c +4 -12
index e179862..e2256a6 100644
@@ -121,14 +121,6 @@ static void kvmclock_vm_state_change(void *opaque, int running,
             pvclock_via_mem = kvmclock_current_nsec(s);
         }
 
-        /* migration/savevm/init restore
-         * update clock_is_reliable to match local
-         * host capabilities.
-         */
-        if (s->clock_valid == false) {
-            s->clock_is_reliable = kvm_has_adjust_clock_stable();
-        }
-
         /* We can't rely on the saved clock value, just discard it */
         if (pvclock_via_mem) {
             s->clock = pvclock_via_mem;
@@ -164,6 +156,10 @@ static void kvmclock_vm_state_change(void *opaque, int running,
         kvm_synchronize_all_tsc();
 
         s->clock = kvm_get_clock();
+        /* any code that sets s->clock needs to ensure clock_is_reliable
+         * is correctly set.
+         */
+        s->clock_is_reliable = kvm_has_adjust_clock_stable();
         /*
          * If the VM is stopped, declare the clock state valid to
          * avoid re-reading it on next vmsave (which would return
@@ -177,10 +173,6 @@ static void kvmclock_realize(DeviceState *dev, Error **errp)
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
-    if (kvm_has_adjust_clock_stable()) {
-        s->clock_is_reliable = true;
-    }
-
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
 
Quoted 1 line

[...]

Quoted 10 lines
Quoted 9 lines

Review

Quoted 5 lines
(quoted diff) +5 -0
+
+static void kvmclock_pre_save(void *opaque)
+{
+    KVMClockState *s = opaque;
+

We still need a comment here explaining why we need to re-read the clock on pre_save if s->clock was already set on kvmclock_vm_state_change().

Quoted 7 lines

OK.

Quoted 3 lines

Also, what if we are migrating a VM that was already paused 10 minutes ago? Should we migrate the s->clock value from 10 minutes ago, or the one read at pre_save?

The one read at pre_save. I'll add a comment.

Is it really valid to make the clock move on an already-paused VM, only because it was migrated?

Quoted 4 lines
Quoted 4 lines
Quoted 4 lines
(quoted diff) +3 -0
+    s->clock = kvm_get_clock();
+}
+
Signature · 1 line
Eduardo
#17 lore raw
In reply to Marcelo Tosatti
Quoted 3 lines · Marcelo Tosatti

See individual patches for details. This patchset depends on kernels "[PATCH] kvm: kvmclock: let KVM_GET_CLOCK return whether the master clock is in use" from Paolo.

Ping?