diff mbox

[v2,3/3] target-i386: load the migrated vcpu's TSC rate

Message ID 1445325774-7195-4-git-send-email-haozhong.zhang@intel.com (mailing list archive)
State New, archived
Headers show

Commit Message

Haozhong Zhang Oct. 20, 2015, 7:22 a.m. UTC
Set vcpu's TSC rate to the migrated value (if any). If KVM supports TSC
scaling, guest programs will observe TSC increasing in the migrated rate
other than the host TSC rate.

The loading is controlled by a new cpu option 'load-tsc-freq'. If it is
present, then the loading will be enabled and the migrated vcpu's TSC
rate will override the value specified by the cpu option
'tsc-freq'. Otherwise, the loading will be disabled.

The setting of vcpu's TSC rate in this patch duplicates the code in
kvm_arch_init_vcpu(), so we remove the latter one.

Signed-off-by: Haozhong Zhang <haozhong.zhang@intel.com>
---
 target-i386/cpu.c |  1 +
 target-i386/cpu.h |  1 +
 target-i386/kvm.c | 28 +++++++++++++++++++---------
 3 files changed, 21 insertions(+), 9 deletions(-)

Comments

Eduardo Habkost Oct. 22, 2015, 6:11 p.m. UTC | #1
On Tue, Oct 20, 2015 at 03:22:54PM +0800, Haozhong Zhang wrote:
> Set vcpu's TSC rate to the migrated value (if any). If KVM supports TSC
> scaling, guest programs will observe TSC increasing in the migrated rate
> other than the host TSC rate.
> 
> The loading is controlled by a new cpu option 'load-tsc-freq'. If it is
> present, then the loading will be enabled and the migrated vcpu's TSC
> rate will override the value specified by the cpu option
> 'tsc-freq'. Otherwise, the loading will be disabled.

Why do we need an option? Why can't we enable loading unconditionally?

> 
> The setting of vcpu's TSC rate in this patch duplicates the code in
> kvm_arch_init_vcpu(), so we remove the latter one.
> 
> Signed-off-by: Haozhong Zhang <haozhong.zhang@intel.com>
> ---
>  target-i386/cpu.c |  1 +
>  target-i386/cpu.h |  1 +
>  target-i386/kvm.c | 28 +++++++++++++++++++---------
>  3 files changed, 21 insertions(+), 9 deletions(-)
> 
> diff --git a/target-i386/cpu.c b/target-i386/cpu.c
> index b6bb457..763ba4b 100644
> --- a/target-i386/cpu.c
> +++ b/target-i386/cpu.c
> @@ -3144,6 +3144,7 @@ static Property x86_cpu_properties[] = {
>      DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
>      DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true),
>      DEFINE_PROP_BOOL("save-tsc-freq", X86CPU, env.save_tsc_khz, true),
> +    DEFINE_PROP_BOOL("load-tsc-freq", X86CPU, env.load_tsc_khz, false),
>      DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, 0),
>      DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, 0),
>      DEFINE_PROP_UINT32("xlevel2", X86CPU, env.cpuid_xlevel2, 0),
> diff --git a/target-i386/cpu.h b/target-i386/cpu.h
> index ba1a289..353f5fb 100644
> --- a/target-i386/cpu.h
> +++ b/target-i386/cpu.h
> @@ -968,6 +968,7 @@ typedef struct CPUX86State {
>      int64_t tsc_khz;
>      int64_t tsc_khz_incoming;
>      bool save_tsc_khz;
> +    bool load_tsc_khz;
>      void *kvm_xsave_buf;
>  
>      uint64_t mcg_cap;
> diff --git a/target-i386/kvm.c b/target-i386/kvm.c
> index 698524a..34616f5 100644
> --- a/target-i386/kvm.c
> +++ b/target-i386/kvm.c
> @@ -743,15 +743,6 @@ int kvm_arch_init_vcpu(CPUState *cs)
>          return r;
>      }
>  
> -    r = kvm_check_extension(cs->kvm_state, KVM_CAP_TSC_CONTROL);
> -    if (r && env->tsc_khz) {
> -        r = kvm_vcpu_ioctl(cs, KVM_SET_TSC_KHZ, env->tsc_khz);
> -        if (r < 0) {
> -            fprintf(stderr, "KVM_SET_TSC_KHZ failed\n");
> -            return r;
> -        }
> -    }
> -
>      if (kvm_has_xsave()) {
>          env->kvm_xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave));
>      }
> @@ -2223,6 +2214,25 @@ static int kvm_setup_tsc_khz(X86CPU *cpu, int level)
>          return 0;
>  
>      /*
> +     * If the cpu option 'load-tsc-freq' is present, the vcpu's TSC rate in the
> +     * migrated state will be used and the overrides the user-specified vcpu's
> +     * TSC rate (if any).
> +     */
> +    if (runstate_check(RUN_STATE_INMIGRATE) &&
> +        env->load_tsc_khz && env->tsc_khz_incoming) {
> +        env->tsc_khz = env->tsc_khz_incoming;
> +    }

Please don't make the results of the function depend on global QEMU
runstate, as it makes it harder to reason about it, and easy to
introduce subtle bugs if we change initialization order. Can't we just
ensure tsc_khz gets set to the right value before the function is
called, inside the code that loads migration data?

> +
> +    r = kvm_check_extension(cs->kvm_state, KVM_CAP_TSC_CONTROL);
> +    if (r && env->tsc_khz) {
> +        r = kvm_vcpu_ioctl(cs, KVM_SET_TSC_KHZ, env->tsc_khz);
> +        if (r < 0) {
> +            fprintf(stderr, "KVM_SET_TSC_KHZ failed\n");
> +            return r;
> +        }
> +    }

So, the final result here does not depend on the configuration, but also
on host capabilities. That means nobody can possibly know if the
tsc-freq option really works, until they enable it, run the VM, and
check the results from inside the VM. Not a good idea.

(This doesn't apply just to the new code, the existing code is already
broken this way.)
Haozhong Zhang Oct. 23, 2015, 3:04 a.m. UTC | #2
On Thu, Oct 22, 2015 at 04:11:37PM -0200, Eduardo Habkost wrote:
> On Tue, Oct 20, 2015 at 03:22:54PM +0800, Haozhong Zhang wrote:
> > Set vcpu's TSC rate to the migrated value (if any). If KVM supports TSC
> > scaling, guest programs will observe TSC increasing in the migrated rate
> > other than the host TSC rate.
> > 
> > The loading is controlled by a new cpu option 'load-tsc-freq'. If it is
> > present, then the loading will be enabled and the migrated vcpu's TSC
> > rate will override the value specified by the cpu option
> > 'tsc-freq'. Otherwise, the loading will be disabled.
> 
> Why do we need an option? Why can't we enable loading unconditionally?
>
> > 
> > The setting of vcpu's TSC rate in this patch duplicates the code in
> > kvm_arch_init_vcpu(), so we remove the latter one.
> > 
> > Signed-off-by: Haozhong Zhang <haozhong.zhang@intel.com>
> > ---
> >  target-i386/cpu.c |  1 +
> >  target-i386/cpu.h |  1 +
> >  target-i386/kvm.c | 28 +++++++++++++++++++---------
> >  3 files changed, 21 insertions(+), 9 deletions(-)
> > 
> > diff --git a/target-i386/cpu.c b/target-i386/cpu.c
> > index b6bb457..763ba4b 100644
> > --- a/target-i386/cpu.c
> > +++ b/target-i386/cpu.c
> > @@ -3144,6 +3144,7 @@ static Property x86_cpu_properties[] = {
> >      DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
> >      DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true),
> >      DEFINE_PROP_BOOL("save-tsc-freq", X86CPU, env.save_tsc_khz, true),
> > +    DEFINE_PROP_BOOL("load-tsc-freq", X86CPU, env.load_tsc_khz, false),
> >      DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, 0),
> >      DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, 0),
> >      DEFINE_PROP_UINT32("xlevel2", X86CPU, env.cpuid_xlevel2, 0),
> > diff --git a/target-i386/cpu.h b/target-i386/cpu.h
> > index ba1a289..353f5fb 100644
> > --- a/target-i386/cpu.h
> > +++ b/target-i386/cpu.h
> > @@ -968,6 +968,7 @@ typedef struct CPUX86State {
> >      int64_t tsc_khz;
> >      int64_t tsc_khz_incoming;
> >      bool save_tsc_khz;
> > +    bool load_tsc_khz;
> >      void *kvm_xsave_buf;
> >  
> >      uint64_t mcg_cap;
> > diff --git a/target-i386/kvm.c b/target-i386/kvm.c
> > index 698524a..34616f5 100644
> > --- a/target-i386/kvm.c
> > +++ b/target-i386/kvm.c
> > @@ -743,15 +743,6 @@ int kvm_arch_init_vcpu(CPUState *cs)
> >          return r;
> >      }
> >  
> > -    r = kvm_check_extension(cs->kvm_state, KVM_CAP_TSC_CONTROL);
> > -    if (r && env->tsc_khz) {
> > -        r = kvm_vcpu_ioctl(cs, KVM_SET_TSC_KHZ, env->tsc_khz);
> > -        if (r < 0) {
> > -            fprintf(stderr, "KVM_SET_TSC_KHZ failed\n");
> > -            return r;
> > -        }
> > -    }
> > -
> >      if (kvm_has_xsave()) {
> >          env->kvm_xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave));
> >      }
> > @@ -2223,6 +2214,25 @@ static int kvm_setup_tsc_khz(X86CPU *cpu, int level)
> >          return 0;
> >  
> >      /*
> > +     * If the cpu option 'load-tsc-freq' is present, the vcpu's TSC rate in the
> > +     * migrated state will be used and the overrides the user-specified vcpu's
> > +     * TSC rate (if any).
> > +     */
> > +    if (runstate_check(RUN_STATE_INMIGRATE) &&
> > +        env->load_tsc_khz && env->tsc_khz_incoming) {
> > +        env->tsc_khz = env->tsc_khz_incoming;
> > +    }
> 
> Please don't make the results of the function depend on global QEMU
> runstate, as it makes it harder to reason about it, and easy to
> introduce subtle bugs if we change initialization order. Can't we just
> ensure tsc_khz gets set to the right value before the function is
> called, inside the code that loads migration data?
>

I can make kvm_setup_tsc_khz() called in
do_kvm_cpu_synchronize_post_init() and also make empty stubs for other
targets.

> > +
> > +    r = kvm_check_extension(cs->kvm_state, KVM_CAP_TSC_CONTROL);
> > +    if (r && env->tsc_khz) {
> > +        r = kvm_vcpu_ioctl(cs, KVM_SET_TSC_KHZ, env->tsc_khz);
> > +        if (r < 0) {
> > +            fprintf(stderr, "KVM_SET_TSC_KHZ failed\n");
> > +            return r;
> > +        }
> > +    }
> 
> So, the final result here does not depend on the configuration, but also
> on host capabilities. That means nobody can possibly know if the
> tsc-freq option really works, until they enable it, run the VM, and
> check the results from inside the VM. Not a good idea.
> 
> (This doesn't apply just to the new code, the existing code is already
> broken this way.)
>

Really should abort QEMU here for both tsc-freq and migration.

> -- 
> Eduardo
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Haozhong Zhang Oct. 23, 2015, 3:14 a.m. UTC | #3
On Thu, Oct 22, 2015 at 04:11:37PM -0200, Eduardo Habkost wrote:
> On Tue, Oct 20, 2015 at 03:22:54PM +0800, Haozhong Zhang wrote:
> > Set vcpu's TSC rate to the migrated value (if any). If KVM supports TSC
> > scaling, guest programs will observe TSC increasing in the migrated rate
> > other than the host TSC rate.
> > 
> > The loading is controlled by a new cpu option 'load-tsc-freq'. If it is
> > present, then the loading will be enabled and the migrated vcpu's TSC
> > rate will override the value specified by the cpu option
> > 'tsc-freq'. Otherwise, the loading will be disabled.
> 
> Why do we need an option? Why can't we enable loading unconditionally?
>

If TSC scaling is not supported by KVM and CPU, unconditionally
enabling this loading will not take effect which would be different
from users' expectation. 'load-tsc-freq' is introduced to allow users
to enable the loading of migrated TSC frequency if they do know the
underlying KVM and CPU have TSC scaling support.

> > 
> > The setting of vcpu's TSC rate in this patch duplicates the code in
> > kvm_arch_init_vcpu(), so we remove the latter one.
> > 
> > Signed-off-by: Haozhong Zhang <haozhong.zhang@intel.com>
> > ---
> >  target-i386/cpu.c |  1 +
> >  target-i386/cpu.h |  1 +
> >  target-i386/kvm.c | 28 +++++++++++++++++++---------
> >  3 files changed, 21 insertions(+), 9 deletions(-)
> > 
> > diff --git a/target-i386/cpu.c b/target-i386/cpu.c
> > index b6bb457..763ba4b 100644
> > --- a/target-i386/cpu.c
> > +++ b/target-i386/cpu.c
> > @@ -3144,6 +3144,7 @@ static Property x86_cpu_properties[] = {
> >      DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
> >      DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true),
> >      DEFINE_PROP_BOOL("save-tsc-freq", X86CPU, env.save_tsc_khz, true),
> > +    DEFINE_PROP_BOOL("load-tsc-freq", X86CPU, env.load_tsc_khz, false),
> >      DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, 0),
> >      DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, 0),
> >      DEFINE_PROP_UINT32("xlevel2", X86CPU, env.cpuid_xlevel2, 0),
> > diff --git a/target-i386/cpu.h b/target-i386/cpu.h
> > index ba1a289..353f5fb 100644
> > --- a/target-i386/cpu.h
> > +++ b/target-i386/cpu.h
> > @@ -968,6 +968,7 @@ typedef struct CPUX86State {
> >      int64_t tsc_khz;
> >      int64_t tsc_khz_incoming;
> >      bool save_tsc_khz;
> > +    bool load_tsc_khz;
> >      void *kvm_xsave_buf;
> >  
> >      uint64_t mcg_cap;
> > diff --git a/target-i386/kvm.c b/target-i386/kvm.c
> > index 698524a..34616f5 100644
> > --- a/target-i386/kvm.c
> > +++ b/target-i386/kvm.c
> > @@ -743,15 +743,6 @@ int kvm_arch_init_vcpu(CPUState *cs)
> >          return r;
> >      }
> >  
> > -    r = kvm_check_extension(cs->kvm_state, KVM_CAP_TSC_CONTROL);
> > -    if (r && env->tsc_khz) {
> > -        r = kvm_vcpu_ioctl(cs, KVM_SET_TSC_KHZ, env->tsc_khz);
> > -        if (r < 0) {
> > -            fprintf(stderr, "KVM_SET_TSC_KHZ failed\n");
> > -            return r;
> > -        }
> > -    }
> > -
> >      if (kvm_has_xsave()) {
> >          env->kvm_xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave));
> >      }
> > @@ -2223,6 +2214,25 @@ static int kvm_setup_tsc_khz(X86CPU *cpu, int level)
> >          return 0;
> >  
> >      /*
> > +     * If the cpu option 'load-tsc-freq' is present, the vcpu's TSC rate in the
> > +     * migrated state will be used and the overrides the user-specified vcpu's
> > +     * TSC rate (if any).
> > +     */
> > +    if (runstate_check(RUN_STATE_INMIGRATE) &&
> > +        env->load_tsc_khz && env->tsc_khz_incoming) {
> > +        env->tsc_khz = env->tsc_khz_incoming;
> > +    }
> 
> Please don't make the results of the function depend on global QEMU
> runstate, as it makes it harder to reason about it, and easy to
> introduce subtle bugs if we change initialization order. Can't we just
> ensure tsc_khz gets set to the right value before the function is
> called, inside the code that loads migration data?
> 
> > +
> > +    r = kvm_check_extension(cs->kvm_state, KVM_CAP_TSC_CONTROL);
> > +    if (r && env->tsc_khz) {
> > +        r = kvm_vcpu_ioctl(cs, KVM_SET_TSC_KHZ, env->tsc_khz);
> > +        if (r < 0) {
> > +            fprintf(stderr, "KVM_SET_TSC_KHZ failed\n");
> > +            return r;
> > +        }
> > +    }
> 
> So, the final result here does not depend on the configuration, but also
> on host capabilities. That means nobody can possibly know if the
> tsc-freq option really works, until they enable it, run the VM, and
> check the results from inside the VM. Not a good idea.
> 
> (This doesn't apply just to the new code, the existing code is already
> broken this way.)
> 
> -- 
> Eduardo
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Eduardo Habkost Oct. 23, 2015, 2:58 p.m. UTC | #4
On Fri, Oct 23, 2015 at 11:14:48AM +0800, Haozhong Zhang wrote:
> On Thu, Oct 22, 2015 at 04:11:37PM -0200, Eduardo Habkost wrote:
> > On Tue, Oct 20, 2015 at 03:22:54PM +0800, Haozhong Zhang wrote:
> > > Set vcpu's TSC rate to the migrated value (if any). If KVM supports TSC
> > > scaling, guest programs will observe TSC increasing in the migrated rate
> > > other than the host TSC rate.
> > > 
> > > The loading is controlled by a new cpu option 'load-tsc-freq'. If it is
> > > present, then the loading will be enabled and the migrated vcpu's TSC
> > > rate will override the value specified by the cpu option
> > > 'tsc-freq'. Otherwise, the loading will be disabled.
> > 
> > Why do we need an option? Why can't we enable loading unconditionally?
> >
> 
> If TSC scaling is not supported by KVM and CPU, unconditionally
> enabling this loading will not take effect which would be different
> from users' expectation. 'load-tsc-freq' is introduced to allow users
> to enable the loading of migrated TSC frequency if they do know the
> underlying KVM and CPU have TSC scaling support.
> 

I don't get your argument about user expectations. We can't read the
user's mind, but let's enumerate all possible scenarios:

* Host has TSC scaling, user expect TSC frequency to be set:
  * We set it. The user is happy.
* Host has TSC scaling, user doesn't expect TSC frequency to be
  set:
  * We still set it. VM behaves better, guest doesn't see changing TSC
    frequency. User didn't expect it but won't be unhappy.
* No TSC scaling, user expect TSC frequency to be set:
  * We won't set it, user will be unhappy. But I believe we all agree
    we shouldn't make QEMU abort migration by default on all hosts that
    don't support TSC scaling.
* No TSC scaling, user doesn't expect TSC frequency to be set:
  * We don't set it. User is happy.

Could you clarify on which items you disagree above, exactly?
Haozhong Zhang Oct. 26, 2015, 2:09 a.m. UTC | #5
On Fri, Oct 23, 2015 at 12:58:02PM -0200, Eduardo Habkost wrote:
> On Fri, Oct 23, 2015 at 11:14:48AM +0800, Haozhong Zhang wrote:
> > On Thu, Oct 22, 2015 at 04:11:37PM -0200, Eduardo Habkost wrote:
> > > On Tue, Oct 20, 2015 at 03:22:54PM +0800, Haozhong Zhang wrote:
> > > > Set vcpu's TSC rate to the migrated value (if any). If KVM supports TSC
> > > > scaling, guest programs will observe TSC increasing in the migrated rate
> > > > other than the host TSC rate.
> > > > 
> > > > The loading is controlled by a new cpu option 'load-tsc-freq'. If it is
> > > > present, then the loading will be enabled and the migrated vcpu's TSC
> > > > rate will override the value specified by the cpu option
> > > > 'tsc-freq'. Otherwise, the loading will be disabled.
> > > 
> > > Why do we need an option? Why can't we enable loading unconditionally?
> > >
> > 
> > If TSC scaling is not supported by KVM and CPU, unconditionally
> > enabling this loading will not take effect which would be different
> > from users' expectation. 'load-tsc-freq' is introduced to allow users
> > to enable the loading of migrated TSC frequency if they do know the
> > underlying KVM and CPU have TSC scaling support.
> > 
>

Sorry for the confusion, I changed my mind. The semantics of
'load-tsc-freq' is really confusing and we should not need it at all.

Now, what I want to implement is to migrate the TSC frequency as much
as possible. If it could not, QEMU does not abort the migration.

> I don't get your argument about user expectations. We can't read the
> user's mind, but let's enumerate all possible scenarios:
>
> * Host has TSC scaling, user expect TSC frequency to be set:
>   * We set it. The user is happy.
Agree.

> * Host has TSC scaling, user doesn't expect TSC frequency to be
>   set:
>   * We still set it. VM behaves better, guest doesn't see changing TSC
>     frequency. User didn't expect it but won't be unhappy.
Agree.

> * No TSC scaling, user expect TSC frequency to be set:
>   * We won't set it, user will be unhappy. But I believe we all agree
>     we shouldn't make QEMU abort migration by default on all hosts that
>     don't support TSC scaling.
Agree and display warning messages.

> * No TSC scaling, user doesn't expect TSC frequency to be set:
>   * We don't set it. User is happy.
Agree. This is the current QEMU's behavior, so it's still acceptable.

Thanks,
Haozhong

> 
> Could you clarify on which items you disagree above, exactly?
> 
> -- 
> Eduardo
> --
> To unsubscribe from this list: send the line "unsubscribe kvm" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/target-i386/cpu.c b/target-i386/cpu.c
index b6bb457..763ba4b 100644
--- a/target-i386/cpu.c
+++ b/target-i386/cpu.c
@@ -3144,6 +3144,7 @@  static Property x86_cpu_properties[] = {
     DEFINE_PROP_BOOL("enforce", X86CPU, enforce_cpuid, false),
     DEFINE_PROP_BOOL("kvm", X86CPU, expose_kvm, true),
     DEFINE_PROP_BOOL("save-tsc-freq", X86CPU, env.save_tsc_khz, true),
+    DEFINE_PROP_BOOL("load-tsc-freq", X86CPU, env.load_tsc_khz, false),
     DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, 0),
     DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, 0),
     DEFINE_PROP_UINT32("xlevel2", X86CPU, env.cpuid_xlevel2, 0),
diff --git a/target-i386/cpu.h b/target-i386/cpu.h
index ba1a289..353f5fb 100644
--- a/target-i386/cpu.h
+++ b/target-i386/cpu.h
@@ -968,6 +968,7 @@  typedef struct CPUX86State {
     int64_t tsc_khz;
     int64_t tsc_khz_incoming;
     bool save_tsc_khz;
+    bool load_tsc_khz;
     void *kvm_xsave_buf;
 
     uint64_t mcg_cap;
diff --git a/target-i386/kvm.c b/target-i386/kvm.c
index 698524a..34616f5 100644
--- a/target-i386/kvm.c
+++ b/target-i386/kvm.c
@@ -743,15 +743,6 @@  int kvm_arch_init_vcpu(CPUState *cs)
         return r;
     }
 
-    r = kvm_check_extension(cs->kvm_state, KVM_CAP_TSC_CONTROL);
-    if (r && env->tsc_khz) {
-        r = kvm_vcpu_ioctl(cs, KVM_SET_TSC_KHZ, env->tsc_khz);
-        if (r < 0) {
-            fprintf(stderr, "KVM_SET_TSC_KHZ failed\n");
-            return r;
-        }
-    }
-
     if (kvm_has_xsave()) {
         env->kvm_xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave));
     }
@@ -2223,6 +2214,25 @@  static int kvm_setup_tsc_khz(X86CPU *cpu, int level)
         return 0;
 
     /*
+     * If the cpu option 'load-tsc-freq' is present, the vcpu's TSC rate in the
+     * migrated state will be used and the overrides the user-specified vcpu's
+     * TSC rate (if any).
+     */
+    if (runstate_check(RUN_STATE_INMIGRATE) &&
+        env->load_tsc_khz && env->tsc_khz_incoming) {
+        env->tsc_khz = env->tsc_khz_incoming;
+    }
+
+    r = kvm_check_extension(cs->kvm_state, KVM_CAP_TSC_CONTROL);
+    if (r && env->tsc_khz) {
+        r = kvm_vcpu_ioctl(cs, KVM_SET_TSC_KHZ, env->tsc_khz);
+        if (r < 0) {
+            fprintf(stderr, "KVM_SET_TSC_KHZ failed\n");
+            return r;
+        }
+    }
+
+    /*
      * Prepare the vcpu's TSC rate (ie. env->tsc_khz_incoming) to be migrated.
      * 1. If no user-specified value is provided, we will use the value from
      *    KVM;