KVM: SVM: Move INTR vmexit out of atomic code
The nested SVM code emulates a #vmexit caused by a request to open the irq window right in the request function. This is a bug because the request function runs with preemption and interrupts disabled but the #vmexit emulation might sleep. This can cause a schedule()-while-atomic bug and is fixed with this patch. Signed-off-by: Joerg Roedel <joerg.roedel@amd.com> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
This commit is contained in:
@@ -85,6 +85,9 @@ struct nested_state {
|
|||||||
/* gpa pointers to the real vectors */
|
/* gpa pointers to the real vectors */
|
||||||
u64 vmcb_msrpm;
|
u64 vmcb_msrpm;
|
||||||
|
|
||||||
|
/* A VMEXIT is required but not yet emulated */
|
||||||
|
bool exit_required;
|
||||||
|
|
||||||
/* cache for intercepts of the guest */
|
/* cache for intercepts of the guest */
|
||||||
u16 intercept_cr_read;
|
u16 intercept_cr_read;
|
||||||
u16 intercept_cr_write;
|
u16 intercept_cr_write;
|
||||||
@@ -1379,7 +1382,14 @@ static inline int nested_svm_intr(struct vcpu_svm *svm)
|
|||||||
|
|
||||||
svm->vmcb->control.exit_code = SVM_EXIT_INTR;
|
svm->vmcb->control.exit_code = SVM_EXIT_INTR;
|
||||||
|
|
||||||
if (nested_svm_exit_handled(svm)) {
|
if (svm->nested.intercept & 1ULL) {
|
||||||
|
/*
|
||||||
|
* The #vmexit can't be emulated here directly because this
|
||||||
|
* code path runs with irqs and preemtion disabled. A
|
||||||
|
* #vmexit emulation might sleep. Only signal request for
|
||||||
|
* the #vmexit here.
|
||||||
|
*/
|
||||||
|
svm->nested.exit_required = true;
|
||||||
nsvm_printk("VMexit -> INTR\n");
|
nsvm_printk("VMexit -> INTR\n");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -2340,6 +2350,13 @@ static int handle_exit(struct kvm_vcpu *vcpu)
|
|||||||
|
|
||||||
trace_kvm_exit(exit_code, svm->vmcb->save.rip);
|
trace_kvm_exit(exit_code, svm->vmcb->save.rip);
|
||||||
|
|
||||||
|
if (unlikely(svm->nested.exit_required)) {
|
||||||
|
nested_svm_vmexit(svm);
|
||||||
|
svm->nested.exit_required = false;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_nested(svm)) {
|
if (is_nested(svm)) {
|
||||||
int vmexit;
|
int vmexit;
|
||||||
|
|
||||||
@@ -2615,6 +2632,13 @@ static void svm_vcpu_run(struct kvm_vcpu *vcpu)
|
|||||||
u16 gs_selector;
|
u16 gs_selector;
|
||||||
u16 ldt_selector;
|
u16 ldt_selector;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A vmexit emulation is required before the vcpu can be executed
|
||||||
|
* again.
|
||||||
|
*/
|
||||||
|
if (unlikely(svm->nested.exit_required))
|
||||||
|
return;
|
||||||
|
|
||||||
svm->vmcb->save.rax = vcpu->arch.regs[VCPU_REGS_RAX];
|
svm->vmcb->save.rax = vcpu->arch.regs[VCPU_REGS_RAX];
|
||||||
svm->vmcb->save.rsp = vcpu->arch.regs[VCPU_REGS_RSP];
|
svm->vmcb->save.rsp = vcpu->arch.regs[VCPU_REGS_RSP];
|
||||||
svm->vmcb->save.rip = vcpu->arch.regs[VCPU_REGS_RIP];
|
svm->vmcb->save.rip = vcpu->arch.regs[VCPU_REGS_RIP];
|
||||||
|
Reference in New Issue
Block a user