Skip to content

Commit ab4e41e

Browse files
Maxim Levitskysean-jc
authored andcommitted
KVM: x86: Don't clear async #PF queue when CR0.PG is disabled (e.g. on #SMI)
Fix an interaction between SMM and PV asynchronous #PFs where an #SMI can cause KVM to drop an async #PF ready event, and thus result in guest tasks becoming permanently stuck due to the task that encountered the #PF never being resumed. Specifically, don't clear the completion queue when paging is disabled, and re-check for completed async #PFs if/when paging is enabled. Prior to commit 2635b5c ("KVM: x86: interrupt based APF 'page ready' event delivery"), flushing the APF queue without notifying the guest of completed APF requests when paging is disabled was "necessary", in that delivering a #PF to the guest when paging is disabled would likely confuse and/or crash the guest. And presumably the original async #PF development assumed that a guest would only disable paging when there was no intent to ever re-enable paging. That assumption fails in several scenarios, most visibly on an emulated SMI, as entering SMM always disables CR0.PG (i.e. initially runs with paging disabled). When the SMM handler eventually executes RSM, the interrupted paging-enabled is restored, and the async #PF event is lost. Similarly, invoking firmware, e.g. via EFI runtime calls, might require a transition through paging modes and thus also disable paging with valid entries in the competion queue. To avoid dropping completion events, drop the "clear" entirely, and handle paging-enable transitions in the same way KVM already handles APIC enable/disable events: if a vCPU's APIC is disabled, APF completion events are not kept pending and not injected while APIC is disabled. Once a vCPU's APIC is re-enabled, KVM raises KVM_REQ_APF_READY so that the vCPU recognizes any pending pending #APF ready events. Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com> Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20251015033258.50974-4-mlevitsk@redhat.com [sean: rework changelog to call out #PF injection, drop "real mode" references, expand the code comment] Signed-off-by: Sean Christopherson <seanjc@google.com>
1 parent 68c35f8 commit ab4e41e

File tree

1 file changed

+15
-10
lines changed

1 file changed

+15
-10
lines changed

arch/x86/kvm/x86.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,6 +1045,13 @@ bool kvm_require_dr(struct kvm_vcpu *vcpu, int dr)
10451045
}
10461046
EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_require_dr);
10471047

1048+
static bool kvm_pv_async_pf_enabled(struct kvm_vcpu *vcpu)
1049+
{
1050+
u64 mask = KVM_ASYNC_PF_ENABLED | KVM_ASYNC_PF_DELIVERY_AS_INT;
1051+
1052+
return (vcpu->arch.apf.msr_en_val & mask) == mask;
1053+
}
1054+
10481055
static inline u64 pdptr_rsvd_bits(struct kvm_vcpu *vcpu)
10491056
{
10501057
return vcpu->arch.reserved_gpa_bits | rsvd_bits(5, 8) | rsvd_bits(1, 2);
@@ -1137,15 +1144,20 @@ void kvm_post_set_cr0(struct kvm_vcpu *vcpu, unsigned long old_cr0, unsigned lon
11371144
}
11381145

11391146
if ((cr0 ^ old_cr0) & X86_CR0_PG) {
1140-
kvm_clear_async_pf_completion_queue(vcpu);
1141-
kvm_async_pf_hash_reset(vcpu);
1142-
11431147
/*
11441148
* Clearing CR0.PG is defined to flush the TLB from the guest's
11451149
* perspective.
11461150
*/
11471151
if (!(cr0 & X86_CR0_PG))
11481152
kvm_make_request(KVM_REQ_TLB_FLUSH_GUEST, vcpu);
1153+
/*
1154+
* Check for async #PF completion events when enabling paging,
1155+
* as the vCPU may have previously encountered async #PFs (it's
1156+
* entirely legal for the guest to toggle paging on/off without
1157+
* waiting for the async #PF queue to drain).
1158+
*/
1159+
else if (kvm_pv_async_pf_enabled(vcpu))
1160+
kvm_make_request(KVM_REQ_APF_READY, vcpu);
11491161
}
11501162

11511163
if ((cr0 ^ old_cr0) & KVM_MMU_CR0_ROLE_BITS)
@@ -3650,13 +3662,6 @@ static int set_msr_mce(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
36503662
return 0;
36513663
}
36523664

3653-
static inline bool kvm_pv_async_pf_enabled(struct kvm_vcpu *vcpu)
3654-
{
3655-
u64 mask = KVM_ASYNC_PF_ENABLED | KVM_ASYNC_PF_DELIVERY_AS_INT;
3656-
3657-
return (vcpu->arch.apf.msr_en_val & mask) == mask;
3658-
}
3659-
36603665
static int kvm_pv_enable_async_pf(struct kvm_vcpu *vcpu, u64 data)
36613666
{
36623667
gpa_t gpa = data & ~0x3f;

0 commit comments

Comments
 (0)