On Tue, Jun 06, 2017 at 09:19:30PM +0300, Roman Kagan wrote:
> Hyper-V identifies vcpus by the virtual processor (VP) index which is
> normally queried by the guest via HV_X64_MSR_VP_INDEX msr.
> 
> It has to be owned by QEMU in order to preserve it across migration.
> 
> However, the current implementation in KVM doesn't allow to set this
> msr, and KVM uses its own notion of VP index.  Fortunately, the way
> vcpus are created in QEMU/KVM basically guarantees that the KVM value is
> equal to QEMU cpu_index.

This might not be true in the future.  cpu_index is not a good
identifier for CPUs, and we would like to remove it in the
future.

But it looks like we have no choice, see extra comments below:

> 
> So, for back and forward compatibility, attempt to set the msr at vcpu
> init time to cpu_index, but ignore the errors; then query the msr value
> from KVM and assert that it's equal to cpu_index.  On current kernels
> this will work by luck; future ones will accept the value from
> userspace.
> 
> Signed-off-by: Roman Kagan <rka...@virtuozzo.com>
> ---
>  target/i386/hyperv.h |  2 ++
>  target/i386/hyperv.c |  5 +++++
>  target/i386/kvm.c    | 26 ++++++++++++++++++++++++++
>  3 files changed, 33 insertions(+)
> 
> diff --git a/target/i386/hyperv.h b/target/i386/hyperv.h
> index 0c3b562..35da0b1 100644
> --- a/target/i386/hyperv.h
> +++ b/target/i386/hyperv.h
> @@ -39,4 +39,6 @@ void kvm_hv_sint_route_destroy(HvSintRoute *sint_route);
>  
>  int kvm_hv_sint_route_set_sint(HvSintRoute *sint_route);
>  
> +uint32_t hyperv_vp_index(X86CPU *cpu);
> +
>  #endif
> diff --git a/target/i386/hyperv.c b/target/i386/hyperv.c
> index 227185c..27de5bc 100644
> --- a/target/i386/hyperv.c
> +++ b/target/i386/hyperv.c
> @@ -16,6 +16,11 @@
>  #include "hyperv.h"
>  #include "hyperv_proto.h"
>  
> +uint32_t hyperv_vp_index(X86CPU *cpu)
> +{
> +    return CPU(cpu)->cpu_index;
> +}
> +

You are introducing a wrapper, but you are using cs->cpu_index
directly at hyperv_set_vp_index().  The knowledge that vp_index
== cpu_index is duplicated on two places, and the functions risk
getting out of sync.

I suggest using the wrapper inside hyperv_set_vp_index() too.


>  int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit)
>  {
>      CPUX86State *env = &cpu->env;
> diff --git a/target/i386/kvm.c b/target/i386/kvm.c
> index 251aa95..eb9cde4 100644
> --- a/target/i386/kvm.c
> +++ b/target/i386/kvm.c
> @@ -610,11 +610,37 @@ static int kvm_arch_set_tsc_khz(CPUState *cs)
>      return 0;
>  }
>  
> +static void hyperv_set_vp_index(CPUState *cs)
> +{
> +    struct {
> +        struct kvm_msrs info;
> +        struct kvm_msr_entry entries[1];
> +    } msr_data;
> +    int ret;
> +
> +    msr_data.info.nmsrs = 1;
> +    msr_data.entries[0].index = HV_X64_MSR_VP_INDEX;
> +
> +    /*
> +     * Some kernels don't support setting HV_X64_MSR_VP_INDEX.  However,
> +     * they use VP_INDEX equal to cpu_index due to the way vcpus are created.
> +     * So ignore the errors from SET_MSRS, but verify that GET_MSRS returns 
> the
> +     * expected value.
> +     */

Oh.  This sounds like a problem.  As reference for others, this
is the KVM code in kvm_hv_get_msr():

        case HV_X64_MSR_VP_INDEX: {
                int r;
                struct kvm_vcpu *v;

                kvm_for_each_vcpu(r, v, vcpu->kvm) {
                        if (v == vcpu) {
                                data = r;
                                break;
                        }
                }
                break;
        }

The problem with that is that it will break as soon as we create
VCPUs in a different order.  Unsolvable on hosts that don't allow
HV_X64_MSR_VP_INDEX to be set, however.

> +    msr_data.entries[0].data = cs->cpu_index;
> +    kvm_vcpu_ioctl(cs, KVM_SET_MSRS, &msr_data);
> +    ret = kvm_vcpu_ioctl(cs, KVM_GET_MSRS, &msr_data);
> +    assert(ret == 1);
> +    assert(msr_data.entries[0].data == cs->cpu_index);

If KVM already initializes the MSR to cpu_index and we will abort
if it was not set to anything except cpu_index here, why exactly
do we need the KVM_SET_MSRS call?

> +}
> +
>  static int hyperv_handle_properties(CPUState *cs)
>  {
>      X86CPU *cpu = X86_CPU(cs);
>      CPUX86State *env = &cpu->env;
>  
> +    hyperv_set_vp_index(cs);
> +

The purpose of hyperv_handle_properties() is just to initialize
cpu->features[] CPUID data based on the hyperv options.  I suggest
creating a new hyperv_vcpu_init() or x86_cpu_hyperv_init()
function for these initialization steps.

>      if (cpu->hyperv_time &&
>              kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV_TIME) <= 0) {
>          cpu->hyperv_time = false;
> -- 
> 2.9.4
> 

-- 
Eduardo

Reply via email to