aboutsummaryrefslogtreecommitdiff
path: root/target
diff options
context:
space:
mode:
authorRoman Kagan <rkagan@virtuozzo.com>2018-09-21 11:22:10 +0300
committerPaolo Bonzini <pbonzini@redhat.com>2018-10-19 13:44:14 +0200
commit9b4cf107b09d18ac30f46fd1c4de8585ccba030c (patch)
treeeaf76223e82c1cf57f08526ec6e23b77cd2f8c08 /target
parent606c34bfd57a0ecda67b395bea022bb307a5384e (diff)
downloadqemu-9b4cf107b09d18ac30f46fd1c4de8585ccba030c.zip
qemu-9b4cf107b09d18ac30f46fd1c4de8585ccba030c.tar.gz
qemu-9b4cf107b09d18ac30f46fd1c4de8585ccba030c.tar.bz2
hyperv: only add SynIC in compatible configurations
Certain configurations do not allow SynIC to be used in QEMU. In particular, - when hyperv_vpindex is off, SINT routes can't be used as they refer to the destination vCPU by vp_index - older KVM (which doesn't expose KVM_CAP_HYPERV_SYNIC2) zeroes out SynIC message and event pages on every msr load, breaking migration OTOH in-KVM users of SynIC -- SynIC timers -- do work in those configurations, and we shouldn't stop the guest from using them. To cover both scenarios, introduce an X86CPU property that makes CPU init code to skip creation of the SynIC object (and thus disables any SynIC use in QEMU) but keeps the KVM part of the SynIC working. The property is clear by default but is set via compat logic for older machine types. As a result, when hv_synic and a modern machine type are specified, QEMU will refuse to run unless vp_index is on and the kernel is recent enough. OTOH with an older machine type QEMU will run fine with hv_synic=on against an older kernel and/or without vp_index enabled but will disallow the in-QEMU uses of SynIC (in e.g. VMBus). Signed-off-by: Roman Kagan <rkagan@virtuozzo.com> Message-Id: <20180921082217.29481-4-rkagan@virtuozzo.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'target')
-rw-r--r--target/i386/cpu.c2
-rw-r--r--target/i386/cpu.h1
-rw-r--r--target/i386/kvm.c30
3 files changed, 25 insertions, 8 deletions
diff --git a/target/i386/cpu.c b/target/i386/cpu.c
index 32ea041..f0d9f7c 100644
--- a/target/i386/cpu.c
+++ b/target/i386/cpu.c
@@ -5607,6 +5607,8 @@ static Property x86_cpu_properties[] = {
* to the specific Windows version being used."
*/
DEFINE_PROP_INT32("x-hv-max-vps", X86CPU, hv_max_vps, -1),
+ DEFINE_PROP_BOOL("x-hv-synic-kvm-only", X86CPU, hyperv_synic_kvm_only,
+ false),
DEFINE_PROP_END_OF_LIST()
};
diff --git a/target/i386/cpu.h b/target/i386/cpu.h
index caa1544..663f3a5 100644
--- a/target/i386/cpu.h
+++ b/target/i386/cpu.h
@@ -1378,6 +1378,7 @@ struct X86CPU {
bool hyperv_vpindex;
bool hyperv_runtime;
bool hyperv_synic;
+ bool hyperv_synic_kvm_only;
bool hyperv_stimer;
bool hyperv_frequencies;
bool hyperv_reenlightenment;
diff --git a/target/i386/kvm.c b/target/i386/kvm.c
index cf6270a..7b7a565 100644
--- a/target/i386/kvm.c
+++ b/target/i386/kvm.c
@@ -734,8 +734,18 @@ static int hyperv_handle_properties(CPUState *cs)
env->features[FEAT_HYPERV_EAX] |= HV_VP_RUNTIME_AVAILABLE;
}
if (cpu->hyperv_synic) {
- if (!has_msr_hv_synic ||
- !kvm_check_extension(cs->kvm_state, KVM_CAP_HYPERV_SYNIC)) {
+ unsigned int cap = KVM_CAP_HYPERV_SYNIC;
+ if (!cpu->hyperv_synic_kvm_only) {
+ if (!cpu->hyperv_vpindex) {
+ fprintf(stderr, "Hyper-V SynIC "
+ "(requested by 'hv-synic' cpu flag) "
+ "requires Hyper-V VP_INDEX ('hv-vpindex')\n");
+ return -ENOSYS;
+ }
+ cap = KVM_CAP_HYPERV_SYNIC2;
+ }
+
+ if (!has_msr_hv_synic || !kvm_check_extension(cs->kvm_state, cap)) {
fprintf(stderr, "Hyper-V SynIC (requested by 'hv-synic' cpu flag) "
"is not supported by kernel\n");
return -ENOSYS;
@@ -784,18 +794,22 @@ static int hyperv_init_vcpu(X86CPU *cpu)
}
if (cpu->hyperv_synic) {
- ret = kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_SYNIC, 0);
+ uint32_t synic_cap = cpu->hyperv_synic_kvm_only ?
+ KVM_CAP_HYPERV_SYNIC : KVM_CAP_HYPERV_SYNIC2;
+ ret = kvm_vcpu_enable_cap(cs, synic_cap, 0);
if (ret < 0) {
error_report("failed to turn on HyperV SynIC in KVM: %s",
strerror(-ret));
return ret;
}
- ret = hyperv_x86_synic_add(cpu);
- if (ret < 0) {
- error_report("failed to create HyperV SynIC: %s",
- strerror(-ret));
- return ret;
+ if (!cpu->hyperv_synic_kvm_only) {
+ ret = hyperv_x86_synic_add(cpu);
+ if (ret < 0) {
+ error_report("failed to create HyperV SynIC: %s",
+ strerror(-ret));
+ return ret;
+ }
}
}