diff options
author | Avi Kivity <avi@redhat.com> | 2011-09-20 06:43:14 -0400 |
---|---|---|
committer | Avi Kivity <avi@redhat.com> | 2011-09-25 12:52:59 -0400 |
commit | 7460fb4a340033107530df19e7e125bd0969bfb2 (patch) | |
tree | 02f2aedd48af0a2e316b28d6a5b21a7a96442ac6 /arch | |
parent | 1cd196ea42c526549ded4fd29809c3fdaa4a7f41 (diff) |
KVM: Fix simultaneous NMIs
If simultaneous NMIs happen, we're supposed to queue the second
and next (collapsing them), but currently we sometimes collapse
the second into the first.
Fix by using a counter for pending NMIs instead of a bool; since
the counter limit depends on whether the processor is currently
in an NMI handler, which can only be checked in vcpu context
(via the NMI mask), we add a new KVM_REQ_NMI to request recalculation
of the counter.
Signed-off-by: Avi Kivity <avi@redhat.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/x86/include/asm/kvm_host.h | 5 | ||||
-rw-r--r-- | arch/x86/kvm/x86.c | 48 |
2 files changed, 34 insertions, 19 deletions
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 6ab4241c27cb..ab62711ccb78 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h | |||
@@ -413,8 +413,9 @@ struct kvm_vcpu_arch { | |||
413 | u32 tsc_catchup_mult; | 413 | u32 tsc_catchup_mult; |
414 | s8 tsc_catchup_shift; | 414 | s8 tsc_catchup_shift; |
415 | 415 | ||
416 | bool nmi_pending; | 416 | atomic_t nmi_queued; /* unprocessed asynchronous NMIs */ |
417 | bool nmi_injected; | 417 | unsigned nmi_pending; /* NMI queued after currently running handler */ |
418 | bool nmi_injected; /* Trying to inject an NMI this entry */ | ||
418 | 419 | ||
419 | struct mtrr_state_type mtrr_state; | 420 | struct mtrr_state_type mtrr_state; |
420 | u32 pat; | 421 | u32 pat; |
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 6b37f18a1663..d51e40733fcb 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c | |||
@@ -83,6 +83,7 @@ static u64 __read_mostly efer_reserved_bits = ~((u64)EFER_SCE); | |||
83 | static void update_cr8_intercept(struct kvm_vcpu *vcpu); | 83 | static void update_cr8_intercept(struct kvm_vcpu *vcpu); |
84 | static int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid, | 84 | static int kvm_dev_ioctl_get_supported_cpuid(struct kvm_cpuid2 *cpuid, |
85 | struct kvm_cpuid_entry2 __user *entries); | 85 | struct kvm_cpuid_entry2 __user *entries); |
86 | static void process_nmi(struct kvm_vcpu *vcpu); | ||
86 | 87 | ||
87 | struct kvm_x86_ops *kvm_x86_ops; | 88 | struct kvm_x86_ops *kvm_x86_ops; |
88 | EXPORT_SYMBOL_GPL(kvm_x86_ops); | 89 | EXPORT_SYMBOL_GPL(kvm_x86_ops); |
@@ -359,8 +360,8 @@ void kvm_propagate_fault(struct kvm_vcpu *vcpu, struct x86_exception *fault) | |||
359 | 360 | ||
360 | void kvm_inject_nmi(struct kvm_vcpu *vcpu) | 361 | void kvm_inject_nmi(struct kvm_vcpu *vcpu) |
361 | { | 362 | { |
362 | kvm_make_request(KVM_REQ_EVENT, vcpu); | 363 | atomic_inc(&vcpu->arch.nmi_queued); |
363 | vcpu->arch.nmi_pending = 1; | 364 | kvm_make_request(KVM_REQ_NMI, vcpu); |
364 | } | 365 | } |
365 | EXPORT_SYMBOL_GPL(kvm_inject_nmi); | 366 | EXPORT_SYMBOL_GPL(kvm_inject_nmi); |
366 | 367 | ||
@@ -2827,6 +2828,7 @@ static int kvm_vcpu_ioctl_x86_set_mce(struct kvm_vcpu *vcpu, | |||
2827 | static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, | 2828 | static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, |
2828 | struct kvm_vcpu_events *events) | 2829 | struct kvm_vcpu_events *events) |
2829 | { | 2830 | { |
2831 | process_nmi(vcpu); | ||
2830 | events->exception.injected = | 2832 | events->exception.injected = |
2831 | vcpu->arch.exception.pending && | 2833 | vcpu->arch.exception.pending && |
2832 | !kvm_exception_is_soft(vcpu->arch.exception.nr); | 2834 | !kvm_exception_is_soft(vcpu->arch.exception.nr); |
@@ -2844,7 +2846,7 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, | |||
2844 | KVM_X86_SHADOW_INT_MOV_SS | KVM_X86_SHADOW_INT_STI); | 2846 | KVM_X86_SHADOW_INT_MOV_SS | KVM_X86_SHADOW_INT_STI); |
2845 | 2847 | ||
2846 | events->nmi.injected = vcpu->arch.nmi_injected; | 2848 | events->nmi.injected = vcpu->arch.nmi_injected; |
2847 | events->nmi.pending = vcpu->arch.nmi_pending; | 2849 | events->nmi.pending = vcpu->arch.nmi_pending != 0; |
2848 | events->nmi.masked = kvm_x86_ops->get_nmi_mask(vcpu); | 2850 | events->nmi.masked = kvm_x86_ops->get_nmi_mask(vcpu); |
2849 | events->nmi.pad = 0; | 2851 | events->nmi.pad = 0; |
2850 | 2852 | ||
@@ -2864,6 +2866,7 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, | |||
2864 | | KVM_VCPUEVENT_VALID_SHADOW)) | 2866 | | KVM_VCPUEVENT_VALID_SHADOW)) |
2865 | return -EINVAL; | 2867 | return -EINVAL; |
2866 | 2868 | ||
2869 | process_nmi(vcpu); | ||
2867 | vcpu->arch.exception.pending = events->exception.injected; | 2870 | vcpu->arch.exception.pending = events->exception.injected; |
2868 | vcpu->arch.exception.nr = events->exception.nr; | 2871 | vcpu->arch.exception.nr = events->exception.nr; |
2869 | vcpu->arch.exception.has_error_code = events->exception.has_error_code; | 2872 | vcpu->arch.exception.has_error_code = events->exception.has_error_code; |
@@ -4763,7 +4766,7 @@ int kvm_inject_realmode_interrupt(struct kvm_vcpu *vcpu, int irq, int inc_eip) | |||
4763 | kvm_set_rflags(vcpu, ctxt->eflags); | 4766 | kvm_set_rflags(vcpu, ctxt->eflags); |
4764 | 4767 | ||
4765 | if (irq == NMI_VECTOR) | 4768 | if (irq == NMI_VECTOR) |
4766 | vcpu->arch.nmi_pending = false; | 4769 | vcpu->arch.nmi_pending = 0; |
4767 | else | 4770 | else |
4768 | vcpu->arch.interrupt.pending = false; | 4771 | vcpu->arch.interrupt.pending = false; |
4769 | 4772 | ||
@@ -5572,7 +5575,7 @@ static void inject_pending_event(struct kvm_vcpu *vcpu) | |||
5572 | /* try to inject new event if pending */ | 5575 | /* try to inject new event if pending */ |
5573 | if (vcpu->arch.nmi_pending) { | 5576 | if (vcpu->arch.nmi_pending) { |
5574 | if (kvm_x86_ops->nmi_allowed(vcpu)) { | 5577 | if (kvm_x86_ops->nmi_allowed(vcpu)) { |
5575 | vcpu->arch.nmi_pending = false; | 5578 | --vcpu->arch.nmi_pending; |
5576 | vcpu->arch.nmi_injected = true; | 5579 | vcpu->arch.nmi_injected = true; |
5577 | kvm_x86_ops->set_nmi(vcpu); | 5580 | kvm_x86_ops->set_nmi(vcpu); |
5578 | } | 5581 | } |
@@ -5604,10 +5607,26 @@ static void kvm_put_guest_xcr0(struct kvm_vcpu *vcpu) | |||
5604 | } | 5607 | } |
5605 | } | 5608 | } |
5606 | 5609 | ||
5610 | static void process_nmi(struct kvm_vcpu *vcpu) | ||
5611 | { | ||
5612 | unsigned limit = 2; | ||
5613 | |||
5614 | /* | ||
5615 | * x86 is limited to one NMI running, and one NMI pending after it. | ||
5616 | * If an NMI is already in progress, limit further NMIs to just one. | ||
5617 | * Otherwise, allow two (and we'll inject the first one immediately). | ||
5618 | */ | ||
5619 | if (kvm_x86_ops->get_nmi_mask(vcpu) || vcpu->arch.nmi_injected) | ||
5620 | limit = 1; | ||
5621 | |||
5622 | vcpu->arch.nmi_pending += atomic_xchg(&vcpu->arch.nmi_queued, 0); | ||
5623 | vcpu->arch.nmi_pending = min(vcpu->arch.nmi_pending, limit); | ||
5624 | kvm_make_request(KVM_REQ_EVENT, vcpu); | ||
5625 | } | ||
5626 | |||
5607 | static int vcpu_enter_guest(struct kvm_vcpu *vcpu) | 5627 | static int vcpu_enter_guest(struct kvm_vcpu *vcpu) |
5608 | { | 5628 | { |
5609 | int r; | 5629 | int r; |
5610 | bool nmi_pending; | ||
5611 | bool req_int_win = !irqchip_in_kernel(vcpu->kvm) && | 5630 | bool req_int_win = !irqchip_in_kernel(vcpu->kvm) && |
5612 | vcpu->run->request_interrupt_window; | 5631 | vcpu->run->request_interrupt_window; |
5613 | 5632 | ||
@@ -5647,6 +5666,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) | |||
5647 | } | 5666 | } |
5648 | if (kvm_check_request(KVM_REQ_STEAL_UPDATE, vcpu)) | 5667 | if (kvm_check_request(KVM_REQ_STEAL_UPDATE, vcpu)) |
5649 | record_steal_time(vcpu); | 5668 | record_steal_time(vcpu); |
5669 | if (kvm_check_request(KVM_REQ_NMI, vcpu)) | ||
5670 | process_nmi(vcpu); | ||
5650 | 5671 | ||
5651 | } | 5672 | } |
5652 | 5673 | ||
@@ -5654,19 +5675,11 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) | |||
5654 | if (unlikely(r)) | 5675 | if (unlikely(r)) |
5655 | goto out; | 5676 | goto out; |
5656 | 5677 | ||
5657 | /* | ||
5658 | * An NMI can be injected between local nmi_pending read and | ||
5659 | * vcpu->arch.nmi_pending read inside inject_pending_event(). | ||
5660 | * But in that case, KVM_REQ_EVENT will be set, which makes | ||
5661 | * the race described above benign. | ||
5662 | */ | ||
5663 | nmi_pending = ACCESS_ONCE(vcpu->arch.nmi_pending); | ||
5664 | |||
5665 | if (kvm_check_request(KVM_REQ_EVENT, vcpu) || req_int_win) { | 5678 | if (kvm_check_request(KVM_REQ_EVENT, vcpu) || req_int_win) { |
5666 | inject_pending_event(vcpu); | 5679 | inject_pending_event(vcpu); |
5667 | 5680 | ||
5668 | /* enable NMI/IRQ window open exits if needed */ | 5681 | /* enable NMI/IRQ window open exits if needed */ |
5669 | if (nmi_pending) | 5682 | if (vcpu->arch.nmi_pending) |
5670 | kvm_x86_ops->enable_nmi_window(vcpu); | 5683 | kvm_x86_ops->enable_nmi_window(vcpu); |
5671 | else if (kvm_cpu_has_interrupt(vcpu) || req_int_win) | 5684 | else if (kvm_cpu_has_interrupt(vcpu) || req_int_win) |
5672 | kvm_x86_ops->enable_irq_window(vcpu); | 5685 | kvm_x86_ops->enable_irq_window(vcpu); |
@@ -6374,7 +6387,8 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) | |||
6374 | 6387 | ||
6375 | int kvm_arch_vcpu_reset(struct kvm_vcpu *vcpu) | 6388 | int kvm_arch_vcpu_reset(struct kvm_vcpu *vcpu) |
6376 | { | 6389 | { |
6377 | vcpu->arch.nmi_pending = false; | 6390 | atomic_set(&vcpu->arch.nmi_queued, 0); |
6391 | vcpu->arch.nmi_pending = 0; | ||
6378 | vcpu->arch.nmi_injected = false; | 6392 | vcpu->arch.nmi_injected = false; |
6379 | 6393 | ||
6380 | vcpu->arch.switch_db_regs = 0; | 6394 | vcpu->arch.switch_db_regs = 0; |
@@ -6649,7 +6663,7 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) | |||
6649 | !vcpu->arch.apf.halted) | 6663 | !vcpu->arch.apf.halted) |
6650 | || !list_empty_careful(&vcpu->async_pf.done) | 6664 | || !list_empty_careful(&vcpu->async_pf.done) |
6651 | || vcpu->arch.mp_state == KVM_MP_STATE_SIPI_RECEIVED | 6665 | || vcpu->arch.mp_state == KVM_MP_STATE_SIPI_RECEIVED |
6652 | || vcpu->arch.nmi_pending || | 6666 | || atomic_read(&vcpu->arch.nmi_queued) || |
6653 | (kvm_arch_interrupt_allowed(vcpu) && | 6667 | (kvm_arch_interrupt_allowed(vcpu) && |
6654 | kvm_cpu_has_interrupt(vcpu)); | 6668 | kvm_cpu_has_interrupt(vcpu)); |
6655 | } | 6669 | } |