[RHEL7,COMMIT] ms/KVM: nVMX: Do not load EOI-exitmap while running L2

Submitted by Konstantin Khorenko on April 30, 2019, 10:49 a.m.

Details

Message ID 201904301049.x3UAnsFm020522@finist-ce7.sw.ru
State New
Series "ms/KVM: nVMX: Do not load EOI-exitmap while running L2"
Headers show

Commit Message

Konstantin Khorenko April 30, 2019, 10:49 a.m.
The commit is pushed to "branch-rh7-3.10.0-957.12.1.vz7.95.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-957.12.1.vz7.95.2
------>
commit 1a6ef78b6f9cf97662c8ab3c5784aebaec9cfba6
Author: Liran Alon <liran.alon@oracle.com>
Date:   Tue Apr 30 13:49:54 2019 +0300

    ms/KVM: nVMX: Do not load EOI-exitmap while running L2
    
    When L1 IOAPIC redirection-table is written, a request of
    KVM_REQ_SCAN_IOAPIC is set on all vCPUs. This is done such that
    all vCPUs will now recalc their IOAPIC handled vectors and load
    it to their EOI-exitmap.
    
    However, it could be that one of the vCPUs is currently running
    L2. In this case, load_eoi_exitmap() will be called which would
    write to vmcs02->eoi_exit_bitmap, which is wrong because
    vmcs02->eoi_exit_bitmap should always be equal to
    vmcs12->eoi_exit_bitmap. Furthermore, at this point
    KVM_REQ_SCAN_IOAPIC was already consumed and therefore we will
    never update vmcs01->eoi_exit_bitmap. This could lead to remote_irr
    of some IOAPIC level-triggered entry to remain set forever.
    
    Fix this issue by delaying the load of EOI-exitmap to when vCPU
    is running L1.
    
    One may wonder why not just delay entire KVM_REQ_SCAN_IOAPIC
    processing to when vCPU is running L1. This is done in order to handle
    correctly the case where LAPIC & IO-APIC of L1 is pass-throughed into
    L2. In this case, vmcs12->virtual_interrupt_delivery should be 0. In
    current nVMX implementation, that results in
    vmcs02->virtual_interrupt_delivery to also be 0. Thus,
    vmcs02->eoi_exit_bitmap is not used. Therefore, every L2 EOI cause
    a #VMExit into L0 (either on MSR_WRITE to x2APIC MSR or
    APIC_ACCESS/APIC_WRITE/EPT_MISCONFIG to APIC MMIO page).
    In order for such L2 EOI to be broadcasted, if needed, from LAPIC
    to IO-APIC, vcpu->arch.ioapic_handled_vectors must be updated
    while L2 is running. Therefore, patch makes sure to delay only the
    loading of EOI-exitmap but not the update of
    vcpu->arch.ioapic_handled_vectors.
    
    Reviewed-by: Arbel Moshe <arbel.moshe@oracle.com>
    Reviewed-by: Krish Sadhukhan <krish.sadhukhan@oracle.com>
    Signed-off-by: Liran Alon <liran.alon@oracle.com>
    Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
    
    https://jira.sw.ru/browse/PSBM-93945
    (cherry picked from commit e40ff1d6608dd9a5e07d7bc3079c64d9d676fe15)
    Signed-off-by: Jan Dakinevich <jan.dakinevich@virtuozzo.com>
---
 arch/x86/include/asm/kvm_host.h |  1 +
 arch/x86/kvm/kvm_cache_regs.h   |  5 +++++
 arch/x86/kvm/x86.c              | 18 ++++++++++++++++--
 include/linux/kvm_host.h        |  1 +
 4 files changed, 23 insertions(+), 2 deletions(-)

Patch hide | download patch | download mbox

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index fb1075221916..969d4bc54258 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -468,6 +468,7 @@  struct kvm_vcpu_arch {
 	u64 apic_base;
 	struct kvm_lapic *apic;    /* kernel irqchip context */
 	bool apicv_active;
+	bool load_eoi_exitmap_pending;
 	DECLARE_BITMAP(ioapic_handled_vectors, 256);
 	unsigned long apic_attention;
 	int32_t apic_arb_prio;
diff --git a/arch/x86/kvm/kvm_cache_regs.h b/arch/x86/kvm/kvm_cache_regs.h
index e1e89ee4af75..ac7bb1429491 100644
--- a/arch/x86/kvm/kvm_cache_regs.h
+++ b/arch/x86/kvm/kvm_cache_regs.h
@@ -92,6 +92,11 @@  static inline void enter_guest_mode(struct kvm_vcpu *vcpu)
 static inline void leave_guest_mode(struct kvm_vcpu *vcpu)
 {
 	vcpu->arch.hflags &= ~HF_GUEST_MASK;
+
+	if (vcpu->arch.load_eoi_exitmap_pending) {
+		vcpu->arch.load_eoi_exitmap_pending = false;
+		kvm_make_request(KVM_REQ_LOAD_EOI_EXITMAP, vcpu);
+	}
 }
 
 static inline bool is_guest_mode(struct kvm_vcpu *vcpu)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index c075dfe5c015..f1230a251add 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -6640,8 +6640,6 @@  static void process_smi(struct kvm_vcpu *vcpu)
 
 static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
 {
-	u64 eoi_exit_bitmap[4];
-
 	if (!kvm_apic_hw_enabled(vcpu->arch.apic))
 		return;
 
@@ -6655,6 +6653,20 @@  static void vcpu_scan_ioapic(struct kvm_vcpu *vcpu)
 		if (ioapic_in_kernel(vcpu->kvm))
 			kvm_ioapic_scan_entry(vcpu, vcpu->arch.ioapic_handled_vectors);
 	}
+
+	if (is_guest_mode(vcpu))
+		vcpu->arch.load_eoi_exitmap_pending = true;
+	else
+		kvm_make_request(KVM_REQ_LOAD_EOI_EXITMAP, vcpu);
+}
+
+static void vcpu_load_eoi_exitmap(struct kvm_vcpu *vcpu)
+{
+	u64 eoi_exit_bitmap[4];
+
+	if (!kvm_apic_hw_enabled(vcpu->arch.apic))
+		return;
+
 	bitmap_or((ulong *)eoi_exit_bitmap, vcpu->arch.ioapic_handled_vectors,
 		  vcpu_to_synic(vcpu)->vec_bitmap, 256);
 	kvm_x86_ops->load_eoi_exitmap(vcpu, eoi_exit_bitmap);
@@ -6776,6 +6788,8 @@  static int vcpu_enter_guest(struct kvm_vcpu *vcpu)
 		}
 		if (kvm_check_request(KVM_REQ_SCAN_IOAPIC, vcpu))
 			vcpu_scan_ioapic(vcpu);
+		if (kvm_check_request(KVM_REQ_LOAD_EOI_EXITMAP, vcpu))
+			vcpu_load_eoi_exitmap(vcpu);
 		if (kvm_check_request(KVM_REQ_APIC_PAGE_RELOAD, vcpu))
 			kvm_vcpu_reload_apic_access_page(vcpu);
 		if (kvm_check_request(KVM_REQ_HV_CRASH, vcpu)) {
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 5fb0e2efa4b0..ff50020dc91f 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -152,6 +152,7 @@  static inline bool is_error_page(struct page *page)
 #define KVM_REQ_HV_RESET          29
 #define KVM_REQ_HV_EXIT           30
 #define KVM_REQ_HV_STIMER         31
+#define KVM_REQ_LOAD_EOI_EXITMAP  32
 
 #define KVM_USERSPACE_IRQ_SOURCE_ID		0
 #define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID	1