diff mbox

[qemu,V4,2/2] kvmclock: reduce kvmclock difference on migration

Message ID ccb8c6bf-f136-9a6c-fbdc-7e7d4924321e@redhat.com
State New
Headers show

Commit Message

Paolo Bonzini Dec. 16, 2016, 10:03 a.m. UTC
I'd like to make a few cleanups and add more documentation:

Comments

Eduardo Habkost Dec. 16, 2016, 1:41 p.m. UTC | #1
On Fri, Dec 16, 2016 at 11:03:33AM +0100, Paolo Bonzini wrote:
> I'd like to make a few cleanups and add more documentation:
> 

Looks good to me.

Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>

> diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c
> index eacc9dc..f767ea9 100644
> --- a/hw/i386/kvm/clock.c
> +++ b/hw/i386/kvm/clock.c
> @@ -37,7 +37,7 @@ typedef struct KVMClockState {
>      uint64_t clock;
>      bool clock_valid;
>  
> -    /* whether machine type supports reliable get clock */
> +    /* whether machine type supports reliable KVM_GET_CLOCK */
>      bool mach_use_reliable_get_clock;
>  
>      /* whether the 'clock' value was obtained in a host with
> @@ -88,7 +88,7 @@ static uint64_t kvmclock_current_nsec(KVMClockState *s)
>      return nsec + time.system_time;
>  }
>  
> -static uint64_t kvm_get_clock(void)
> +static void kvm_update_clock(void)
>  {
>      struct kvm_clock_data data;
>      int ret;
> @@ -98,7 +98,48 @@ 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;
> +
> +    /* If kvm_has_adjust_clock_stable() is false, KVM_GET_CLOCK returns
> +     * essentially CLOCK_MONOTONIC plus a guest-specific adjustment.  This
> +     * can drift from the TSC-based value that is computed by the guest,
> +     * so we need to go through kvmclock_current_nsec().  If
> +     * kvm_has_adjust_clock_stable() is true, and the flags contain
> +     * KVM_CLOCK_TSC_STABLE, then KVM_GET_CLOCK returns a TSC-based value
> +     * and kvmclock_current_nsec() is not necessary.
> +     *
> +     * Here, however, we need not check KVM_CLOCK_TSC_STABLE.  This is because:
> +     *
> +     * - if the host has disabled the kvmclock master clock, the guest already
> +     *   has protection against time going backwards.  This "safety net" is only
> +     *   absent when kvmclock is stable;
> +     *
> +     * - therefore, we can replace a check like
> +     *
> +     *       if last KVM_GET_CLOCK was not reliable
> +     *           read from memory
> +     *
> +     *   with
> +     *
> +     *       if last KVM_GET_CLOCK was not reliable && masterclock is enabled
> +     *           read from memory
> +     *
> +     * However:
> +     *
> +     * - if kvm_has_adjust_clock_stable() returns false, the left side is
> +     *   always true (KVM_GET_CLOCK is never reliable), and the right side is
> +     *   unknown (because we don't have data.flags).  We must assume it's true
> +     *   and read from memory.
> +     *
> +     * - if kvm_has_adjust_clock_stable() returns true, the result of the &&
> +     *   is always false (masterclock is enabled iff KVM_GET_CLOCK is reliable)
> +     *
> +     * So we can just use this instead:
> +     *
> +     *       if !kvm_has_adjust_clock_stable() then
> +     *           read from memory
> +     */
> +    s->clock_is_reliable = kvm_has_adjust_clock_stable();
>  }
>  
>  static void kvmclock_vm_state_change(void *opaque, int running,
> @@ -111,19 +153,17 @@ static void kvmclock_vm_state_change(void *opaque, int running,
>  
>      if (running) {
>          struct kvm_clock_data data = {};
> -        uint64_t pvclock_via_mem = 0;
>  
>          /*
>           * 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 saved clock value, just discard it */
> -        if (pvclock_via_mem) {
> -            s->clock = pvclock_via_mem;
> +            uint64_t pvclock_via_mem = kvmclock_current_nsec(s);
> +            /* 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;
> @@ -155,11 +195,7 @@ 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();
> +        kvm_update_clock();
>          /*
>           * If the VM is stopped, declare the clock state valid to
>           * avoid re-reading it on next vmsave (which would return
> @@ -173,9 +209,7 @@ static void kvmclock_realize(DeviceState *dev, Error **errp)
>  {
>      KVMClockState *s = KVM_CLOCK(dev);
>  
> -    if (kvm_has_adjust_clock_stable()) {
> -        s->clock_is_reliable = true;
> -    }
> +    kvm_update_clock();
>  
>      qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
>  }
> @@ -216,7 +250,7 @@ static void kvmclock_pre_save(void *opaque)
>  {
>      KVMClockState *s = opaque;
>  
> -    s->clock = kvm_get_clock();
> +    kvm_update_clock();
>  }
>  
>  static const VMStateDescription kvmclock_vmsd = {
Marcelo Tosatti Dec. 16, 2016, 3:59 p.m. UTC | #2
On Fri, Dec 16, 2016 at 11:41:36AM -0200, Eduardo Habkost wrote:
> On Fri, Dec 16, 2016 at 11:03:33AM +0100, Paolo Bonzini wrote:
> > I'd like to make a few cleanups and add more documentation:
> > 
> 
> Looks good to me.
> 
> Reviewed-by: Eduardo Habkost <ehabkost@redhat.com>

+1
diff mbox

Patch

diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c
index eacc9dc..f767ea9 100644
--- a/hw/i386/kvm/clock.c
+++ b/hw/i386/kvm/clock.c
@@ -37,7 +37,7 @@  typedef struct KVMClockState {
     uint64_t clock;
     bool clock_valid;
 
-    /* whether machine type supports reliable get clock */
+    /* whether machine type supports reliable KVM_GET_CLOCK */
     bool mach_use_reliable_get_clock;
 
     /* whether the 'clock' value was obtained in a host with
@@ -88,7 +88,7 @@  static uint64_t kvmclock_current_nsec(KVMClockState *s)
     return nsec + time.system_time;
 }
 
-static uint64_t kvm_get_clock(void)
+static void kvm_update_clock(void)
 {
     struct kvm_clock_data data;
     int ret;
@@ -98,7 +98,48 @@  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;
+
+    /* If kvm_has_adjust_clock_stable() is false, KVM_GET_CLOCK returns
+     * essentially CLOCK_MONOTONIC plus a guest-specific adjustment.  This
+     * can drift from the TSC-based value that is computed by the guest,
+     * so we need to go through kvmclock_current_nsec().  If
+     * kvm_has_adjust_clock_stable() is true, and the flags contain
+     * KVM_CLOCK_TSC_STABLE, then KVM_GET_CLOCK returns a TSC-based value
+     * and kvmclock_current_nsec() is not necessary.
+     *
+     * Here, however, we need not check KVM_CLOCK_TSC_STABLE.  This is because:
+     *
+     * - if the host has disabled the kvmclock master clock, the guest already
+     *   has protection against time going backwards.  This "safety net" is only
+     *   absent when kvmclock is stable;
+     *
+     * - therefore, we can replace a check like
+     *
+     *       if last KVM_GET_CLOCK was not reliable
+     *           read from memory
+     *
+     *   with
+     *
+     *       if last KVM_GET_CLOCK was not reliable && masterclock is enabled
+     *           read from memory
+     *
+     * However:
+     *
+     * - if kvm_has_adjust_clock_stable() returns false, the left side is
+     *   always true (KVM_GET_CLOCK is never reliable), and the right side is
+     *   unknown (because we don't have data.flags).  We must assume it's true
+     *   and read from memory.
+     *
+     * - if kvm_has_adjust_clock_stable() returns true, the result of the &&
+     *   is always false (masterclock is enabled iff KVM_GET_CLOCK is reliable)
+     *
+     * So we can just use this instead:
+     *
+     *       if !kvm_has_adjust_clock_stable() then
+     *           read from memory
+     */
+    s->clock_is_reliable = kvm_has_adjust_clock_stable();
 }
 
 static void kvmclock_vm_state_change(void *opaque, int running,
@@ -111,19 +153,17 @@  static void kvmclock_vm_state_change(void *opaque, int running,
 
     if (running) {
         struct kvm_clock_data data = {};
-        uint64_t pvclock_via_mem = 0;
 
         /*
          * 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 saved clock value, just discard it */
-        if (pvclock_via_mem) {
-            s->clock = pvclock_via_mem;
+            uint64_t pvclock_via_mem = kvmclock_current_nsec(s);
+            /* 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;
@@ -155,11 +195,7 @@  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();
+        kvm_update_clock();
         /*
          * If the VM is stopped, declare the clock state valid to
          * avoid re-reading it on next vmsave (which would return
@@ -173,9 +209,7 @@  static void kvmclock_realize(DeviceState *dev, Error **errp)
 {
     KVMClockState *s = KVM_CLOCK(dev);
 
-    if (kvm_has_adjust_clock_stable()) {
-        s->clock_is_reliable = true;
-    }
+    kvm_update_clock();
 
     qemu_add_vm_change_state_handler(kvmclock_vm_state_change, s);
 }
@@ -216,7 +250,7 @@  static void kvmclock_pre_save(void *opaque)
 {
     KVMClockState *s = opaque;
 
-    s->clock = kvm_get_clock();
+    kvm_update_clock();
 }
 
 static const VMStateDescription kvmclock_vmsd = {