diff mbox series

KVM: nVMX: Wake blocked vCPU in guest-mode if pending interrupt in virtual APICv

Message ID 20180904075652.127619-1-liran.alon@oracle.com (mailing list archive)
State New, archived
Headers show
Series KVM: nVMX: Wake blocked vCPU in guest-mode if pending interrupt in virtual APICv | expand

Commit Message

Liran Alon Sept. 4, 2018, 7:56 a.m. UTC
In case L1 do not intercept L2 HLT or enter L2 in HLT activity-state,
it is possible for a vCPU to be blocked while it is in guest-mode.

According to Intel SDM 26.6.5 Interrupt-Window Exiting and
Virtual-Interrupt Delivery: "These events wake the logical processor
if it just entered the HLT state because of a VM entry".
Therefore, if L1 enters L2 in HLT activity-state and L2 has a pending
deliverable interrupt in vmcs12->guest_intr_status.RVI, then the vCPU
should be waken from the HLT state and injected with the interrupt.

In addition, if while the vCPU is blocked (while it is in guest-mode),
it receives a nested posted-interrupt, then the vCPU should also be
waken and injected with the posted interrupt.

To handle these cases, this patch enhances kvm_vcpu_has_events() to also
check if there is a pending interrupt in L2 virtual APICv provided by
L1. That is, it evaluates if there is a pending virtual interrupt for L2
by checking RVI[7:4] > VPPR[7:4] as specified in Intel SDM 29.2.1
Evaluation of Pending Interrupts.

Note that this also handles the case of nested posted-interrupt by the
fact RVI is updated in vmx_complete_nested_posted_interrupt() which is
called from kvm_vcpu_check_block() -> kvm_arch_vcpu_runnable() ->
kvm_vcpu_running() -> vmx_check_nested_events() ->
vmx_complete_nested_posted_interrupt().

Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
Signed-off-by: Liran Alon <liran.alon@oracle.com>
---
 arch/x86/include/asm/kvm_host.h |  1 +
 arch/x86/kvm/vmx.c              | 22 ++++++++++++++++++++++
 arch/x86/kvm/x86.c              | 10 +++++++++-
 3 files changed, 32 insertions(+), 1 deletion(-)

Comments

Jim Mattson March 14, 2019, 9:08 p.m. UTC | #1
On Tue, Sep 4, 2018 at 12:57 AM Liran Alon <liran.alon@oracle.com> wrote:
>
> In case L1 do not intercept L2 HLT or enter L2 in HLT activity-state,
> it is possible for a vCPU to be blocked while it is in guest-mode.
>
> According to Intel SDM 26.6.5 Interrupt-Window Exiting and
> Virtual-Interrupt Delivery: "These events wake the logical processor
> if it just entered the HLT state because of a VM entry".
> Therefore, if L1 enters L2 in HLT activity-state and L2 has a pending
> deliverable interrupt in vmcs12->guest_intr_status.RVI, then the vCPU
> should be waken from the HLT state and injected with the interrupt.
>
> In addition, if while the vCPU is blocked (while it is in guest-mode),
> it receives a nested posted-interrupt, then the vCPU should also be
> waken and injected with the posted interrupt.
>
> To handle these cases, this patch enhances kvm_vcpu_has_events() to also
> check if there is a pending interrupt in L2 virtual APICv provided by
> L1. That is, it evaluates if there is a pending virtual interrupt for L2
> by checking RVI[7:4] > VPPR[7:4] as specified in Intel SDM 29.2.1
> Evaluation of Pending Interrupts.
>
> Note that this also handles the case of nested posted-interrupt by the
> fact RVI is updated in vmx_complete_nested_posted_interrupt() which is
> called from kvm_vcpu_check_block() -> kvm_arch_vcpu_runnable() ->
> kvm_vcpu_running() -> vmx_check_nested_events() ->
> vmx_complete_nested_posted_interrupt().
>
> Reviewed-by: Nikita Leshenko <nikita.leshchenko@oracle.com>
> Reviewed-by: Darren Kenny <darren.kenny@oracle.com>
> Signed-off-by: Liran Alon <liran.alon@oracle.com>
> ---
>  arch/x86/include/asm/kvm_host.h |  1 +
>  arch/x86/kvm/vmx.c              | 22 ++++++++++++++++++++++
>  arch/x86/kvm/x86.c              | 10 +++++++++-
>  3 files changed, 32 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index 00ddb0c9e612..456a3f9aa544 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -1022,6 +1022,7 @@ struct kvm_x86_ops {
>         void (*refresh_apicv_exec_ctrl)(struct kvm_vcpu *vcpu);
>         void (*hwapic_irr_update)(struct kvm_vcpu *vcpu, int max_irr);
>         void (*hwapic_isr_update)(struct kvm_vcpu *vcpu, int isr);
> +       bool (*guest_apic_has_interrupt)(struct kvm_vcpu *vcpu);
>         void (*load_eoi_exitmap)(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap);
>         void (*set_virtual_apic_mode)(struct kvm_vcpu *vcpu);
>         void (*set_apic_access_page_addr)(struct kvm_vcpu *vcpu, hpa_t hpa);
> diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
> index d687c20e7634..bed75118111c 100644
> --- a/arch/x86/kvm/vmx.c
> +++ b/arch/x86/kvm/vmx.c
> @@ -6183,6 +6183,27 @@ static void vmx_complete_nested_posted_interrupt(struct kvm_vcpu *vcpu)
>         nested_mark_vmcs12_pages_dirty(vcpu);
>  }
>
> +static bool vmx_guest_apic_has_interrupt(struct kvm_vcpu *vcpu)
> +{
> +       struct vcpu_vmx *vmx = to_vmx(vcpu);
> +       void *vapic_page;
> +       u32 vppr;
> +       int rvi;
> +
> +       if (WARN_ON_ONCE(!is_guest_mode(vcpu)) ||
> +               !nested_cpu_has_vid(get_vmcs12(vcpu)) ||
> +               WARN_ON_ONCE(!vmx->nested.virtual_apic_page))
> +               return false;

Here I am, late to the party as usual...

Immediately after a live migration, is it possible that this code
might be called while KVM_REQ_GET_VMCS12_PAGES is still pending, and
vmx->nested.virtual_apic_page is NULL?
diff mbox series

Patch

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 00ddb0c9e612..456a3f9aa544 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1022,6 +1022,7 @@  struct kvm_x86_ops {
 	void (*refresh_apicv_exec_ctrl)(struct kvm_vcpu *vcpu);
 	void (*hwapic_irr_update)(struct kvm_vcpu *vcpu, int max_irr);
 	void (*hwapic_isr_update)(struct kvm_vcpu *vcpu, int isr);
+	bool (*guest_apic_has_interrupt)(struct kvm_vcpu *vcpu);
 	void (*load_eoi_exitmap)(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap);
 	void (*set_virtual_apic_mode)(struct kvm_vcpu *vcpu);
 	void (*set_apic_access_page_addr)(struct kvm_vcpu *vcpu, hpa_t hpa);
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index d687c20e7634..bed75118111c 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -6183,6 +6183,27 @@  static void vmx_complete_nested_posted_interrupt(struct kvm_vcpu *vcpu)
 	nested_mark_vmcs12_pages_dirty(vcpu);
 }
 
+static bool vmx_guest_apic_has_interrupt(struct kvm_vcpu *vcpu)
+{
+	struct vcpu_vmx *vmx = to_vmx(vcpu);
+	void *vapic_page;
+	u32 vppr;
+	int rvi;
+
+	if (WARN_ON_ONCE(!is_guest_mode(vcpu)) ||
+		!nested_cpu_has_vid(get_vmcs12(vcpu)) ||
+		WARN_ON_ONCE(!vmx->nested.virtual_apic_page))
+		return false;
+
+	rvi = vmcs_read16(GUEST_INTR_STATUS) & 0xff;
+
+	vapic_page = kmap(vmx->nested.virtual_apic_page);
+	vppr = *((u32 *)(vapic_page + APIC_PROCPRI));
+	kunmap(vmx->nested.virtual_apic_page);
+
+	return ((rvi & 0xf0) > (vppr & 0xf0));
+}
+
 static inline bool kvm_vcpu_trigger_posted_interrupt(struct kvm_vcpu *vcpu,
 						     bool nested)
 {
@@ -14100,6 +14121,7 @@  static struct kvm_x86_ops vmx_x86_ops __ro_after_init = {
 	.apicv_post_state_restore = vmx_apicv_post_state_restore,
 	.hwapic_irr_update = vmx_hwapic_irr_update,
 	.hwapic_isr_update = vmx_hwapic_isr_update,
+	.guest_apic_has_interrupt = vmx_guest_apic_has_interrupt,
 	.sync_pir_to_irr = vmx_sync_pir_to_irr,
 	.deliver_posted_interrupt = vmx_deliver_posted_interrupt,
 
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 506bd2b4b8bb..805c1df7c77e 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -9182,6 +9182,13 @@  void kvm_arch_flush_shadow_memslot(struct kvm *kvm,
 	kvm_page_track_flush_slot(kvm, slot);
 }
 
+static inline bool kvm_guest_apic_has_interrupt(struct kvm_vcpu *vcpu)
+{
+	return (is_guest_mode(vcpu) &&
+			kvm_x86_ops->guest_apic_has_interrupt &&
+			kvm_x86_ops->guest_apic_has_interrupt(vcpu));
+}
+
 static inline bool kvm_vcpu_has_events(struct kvm_vcpu *vcpu)
 {
 	if (!list_empty_careful(&vcpu->async_pf.done))
@@ -9206,7 +9213,8 @@  static inline bool kvm_vcpu_has_events(struct kvm_vcpu *vcpu)
 		return true;
 
 	if (kvm_arch_interrupt_allowed(vcpu) &&
-	    kvm_cpu_has_interrupt(vcpu))
+	    (kvm_cpu_has_interrupt(vcpu) ||
+	    kvm_guest_apic_has_interrupt(vcpu)))
 		return true;
 
 	if (kvm_hv_has_stimer_pending(vcpu))