diff mbox series

KVM: x86: Properly handle APF vs disabled LAPIC situation

Message ID 20210422092948.568327-1-vkuznets@redhat.com (mailing list archive)
State New, archived
Headers show
Series KVM: x86: Properly handle APF vs disabled LAPIC situation | expand

Commit Message

Vitaly Kuznetsov April 22, 2021, 9:29 a.m. UTC
Async PF 'page ready' event may happen when LAPIC is (temporary) disabled.
In particular, Sebastien reports that when Linux kernel is directly booted
by Cloud Hypervisor, LAPIC is 'software disabled' when APF mechanism is
initialized. On initialization KVM tries to inject 'wakeup all' event and
puts the corresponding token to the slot. It is, however, failing to inject
an interrupt (kvm_apic_set_irq() -> __apic_accept_irq() -> !apic_enabled())
so the guest never gets notified and the whole APF mechanism gets stuck.
The same issue is likely to happen if the guest temporary disables LAPIC
and a previously unavailable page becomes available.

Do two things to resolve the issue:
- Avoid dequeuing 'page ready' events from APF queue when LAPIC is
  disabled.
- Trigger an attempt to deliver pending 'page ready' events when LAPIC
  becomes enabled (SPIV or MSR_IA32_APICBASE).

Reported-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
---
 arch/x86/kvm/lapic.c | 6 ++++++
 arch/x86/kvm/x86.c   | 2 +-
 2 files changed, 7 insertions(+), 1 deletion(-)

Comments

Paolo Bonzini April 22, 2021, 9:40 a.m. UTC | #1
On 22/04/21 11:29, Vitaly Kuznetsov wrote:
> Async PF 'page ready' event may happen when LAPIC is (temporary) disabled.
> In particular, Sebastien reports that when Linux kernel is directly booted
> by Cloud Hypervisor, LAPIC is 'software disabled' when APF mechanism is
> initialized. On initialization KVM tries to inject 'wakeup all' event and
> puts the corresponding token to the slot. It is, however, failing to inject
> an interrupt (kvm_apic_set_irq() -> __apic_accept_irq() -> !apic_enabled())
> so the guest never gets notified and the whole APF mechanism gets stuck.
> The same issue is likely to happen if the guest temporary disables LAPIC
> and a previously unavailable page becomes available.
> 
> Do two things to resolve the issue:
> - Avoid dequeuing 'page ready' events from APF queue when LAPIC is
>    disabled.
> - Trigger an attempt to deliver pending 'page ready' events when LAPIC
>    becomes enabled (SPIV or MSR_IA32_APICBASE).
> 
> Reported-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
> Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
> ---
>   arch/x86/kvm/lapic.c | 6 ++++++
>   arch/x86/kvm/x86.c   | 2 +-
>   2 files changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
> index cc369b9ad8f1..49a839d0567a 100644
> --- a/arch/x86/kvm/lapic.c
> +++ b/arch/x86/kvm/lapic.c
> @@ -296,6 +296,10 @@ static inline void apic_set_spiv(struct kvm_lapic *apic, u32 val)
>   
>   		atomic_set_release(&apic->vcpu->kvm->arch.apic_map_dirty, DIRTY);
>   	}
> +
> +	/* Check if there are APF page ready requests pending */
> +	if (enabled)
> +		kvm_make_request(KVM_REQ_APF_READY, apic->vcpu);
>   }
>   
>   static inline void kvm_apic_set_xapic_id(struct kvm_lapic *apic, u8 id)
> @@ -2261,6 +2265,8 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value)
>   		if (value & MSR_IA32_APICBASE_ENABLE) {
>   			kvm_apic_set_xapic_id(apic, vcpu->vcpu_id);
>   			static_branch_slow_dec_deferred(&apic_hw_disabled);
> +			/* Check if there are APF page ready requests pending */
> +			kvm_make_request(KVM_REQ_APF_READY, vcpu);
>   		} else {
>   			static_branch_inc(&apic_hw_disabled.key);
>   			atomic_set_release(&apic->vcpu->kvm->arch.apic_map_dirty, DIRTY);
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index a06a6f48386d..001c6a445eaf 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -11296,7 +11296,7 @@ bool kvm_arch_can_dequeue_async_page_present(struct kvm_vcpu *vcpu)
>   	if (!kvm_pv_async_pf_enabled(vcpu))
>   		return true;
>   	else
> -		return apf_pageready_slot_free(vcpu);
> +		return kvm_lapic_enabled(vcpu) && apf_pageready_slot_free(vcpu);
>   }
>   
>   void kvm_arch_start_assignment(struct kvm *kvm)
> 

Queued, with

Cc: stable@vger.kernel.org

Paolo
diff mbox series

Patch

diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c
index cc369b9ad8f1..49a839d0567a 100644
--- a/arch/x86/kvm/lapic.c
+++ b/arch/x86/kvm/lapic.c
@@ -296,6 +296,10 @@  static inline void apic_set_spiv(struct kvm_lapic *apic, u32 val)
 
 		atomic_set_release(&apic->vcpu->kvm->arch.apic_map_dirty, DIRTY);
 	}
+
+	/* Check if there are APF page ready requests pending */
+	if (enabled)
+		kvm_make_request(KVM_REQ_APF_READY, apic->vcpu);
 }
 
 static inline void kvm_apic_set_xapic_id(struct kvm_lapic *apic, u8 id)
@@ -2261,6 +2265,8 @@  void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value)
 		if (value & MSR_IA32_APICBASE_ENABLE) {
 			kvm_apic_set_xapic_id(apic, vcpu->vcpu_id);
 			static_branch_slow_dec_deferred(&apic_hw_disabled);
+			/* Check if there are APF page ready requests pending */
+			kvm_make_request(KVM_REQ_APF_READY, vcpu);
 		} else {
 			static_branch_inc(&apic_hw_disabled.key);
 			atomic_set_release(&apic->vcpu->kvm->arch.apic_map_dirty, DIRTY);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index a06a6f48386d..001c6a445eaf 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -11296,7 +11296,7 @@  bool kvm_arch_can_dequeue_async_page_present(struct kvm_vcpu *vcpu)
 	if (!kvm_pv_async_pf_enabled(vcpu))
 		return true;
 	else
-		return apf_pageready_slot_free(vcpu);
+		return kvm_lapic_enabled(vcpu) && apf_pageready_slot_free(vcpu);
 }
 
 void kvm_arch_start_assignment(struct kvm *kvm)