From: Zenghui Yu yuzenghui@huawei.com
virt inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I4BLL0 CVE: NA
--------------------------------
As per ARM DDI 0487G.a, seting the HCR_EL2.FB (Force broadcast) bit causes a given set of TLBI and IC instructions to be broadcast within the Inner Shareable domain when executed from EL1 (if HCR_EL2.TGE is 0).
And people complain that this leads to bad performance when running guests on Kunpeng920 which has 128 physical CPUs in the IS domain, especially in the case where vcpus are pinned to physical CPUs, where we indeed don't need broadcast invalidations.
Introduce a new cmdline parameter "kvm-arm.hcr_nofb" for users and setting it at boot time allows all vcpus running without HCR_EL2.FB. Note that we now have to nuke the whole vcpu context in the general case (when vcpu is loaded on to the new physical CPU).
Co-developed-by: Nianyao Tang tangnianyao@huawei.com Signed-off-by: Nianyao Tang tangnianyao@huawei.com Signed-off-by: Zenghui Yu yuzenghui@huawei.com Reviewed-by: Cheng Jian cj.chengjian@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- arch/arm64/include/asm/kvm_emulate.h | 5 +++++ virt/kvm/arm/arm.c | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+)
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index 6106a85ae0be7..9ee37c0e763b2 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -52,6 +52,8 @@ static inline bool vcpu_el1_is_32bit(struct kvm_vcpu *vcpu) return !(vcpu->arch.hcr_el2 & HCR_RW); }
+extern bool kvm_hcr_nofb; + static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu) { vcpu->arch.hcr_el2 = HCR_GUEST_FLAGS; @@ -76,6 +78,9 @@ static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu) */ if (!vcpu_el1_is_32bit(vcpu)) vcpu->arch.hcr_el2 |= HCR_TID3; + + if (unlikely(kvm_hcr_nofb)) + vcpu->arch.hcr_el2 &= ~HCR_FB; }
static inline unsigned long *vcpu_hcr(struct kvm_vcpu *vcpu) diff --git a/virt/kvm/arm/arm.c b/virt/kvm/arm/arm.c index 7cf274e6966e6..1271779873d36 100644 --- a/virt/kvm/arm/arm.c +++ b/virt/kvm/arm/arm.c @@ -74,6 +74,14 @@ static bool vgic_present; enum hisi_cpu_type hi_cpu_type = UNKNOWN_HI_TYPE; bool kvm_ncsnp_support;
+bool kvm_hcr_nofb; + +static int __init early_hcr_nofb_cfg(char *buf) +{ + return strtobool(buf, &kvm_hcr_nofb); +} +early_param("kvm-arm.hcr_nofb", early_hcr_nofb_cfg); + static DEFINE_PER_CPU(unsigned char, kvm_arm_hardware_enabled);
static void kvm_arm_set_running_vcpu(struct kvm_vcpu *vcpu) @@ -419,6 +427,7 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { int *last_ran; kvm_host_data_t *cpu_data; + bool flushed = false;
last_ran = this_cpu_ptr(vcpu->kvm->arch.last_vcpu_ran); cpu_data = this_cpu_ptr(&kvm_host_data); @@ -435,8 +444,17 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) if (*last_ran != vcpu->vcpu_id) { kvm_call_hyp(__kvm_flush_cpu_context, vcpu); *last_ran = vcpu->vcpu_id; + flushed = true; }
+ /* + * If FB (Force broadcast) is cleared, we have to nuke the + * vcpu context as well in case it is loaded on to the new + * physical CPU. + */ + if (unlikely(kvm_hcr_nofb) && vcpu->pre_pcpu != cpu && !flushed) + kvm_call_hyp(__kvm_flush_cpu_context, vcpu); + vcpu->cpu = cpu; vcpu->arch.host_cpu_context = &cpu_data->host_ctxt;