summary refs log tree commit diff
path: root/virt
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2014-03-28 20:41:51 +0100
committerPaolo Bonzini <pbonzini@redhat.com>2014-04-04 17:17:24 +0200
commit4009b2499eee26c7f413073300d124a37765dfca (patch)
tree05b431aec59a1d17fbbd9d29c46fd1e51831e260 /virt
parent5678de3f15010b9022ee45673f33bcfc71d47b60 (diff)
downloadlinux-4009b2499eee26c7f413073300d124a37765dfca.tar.gz
KVM: ioapic: try to recover if pending_eoi goes out of range
The RTC tracking code tracks the cardinality of rtc_status.dest_map
into rtc_status.pending_eoi.  It has some WARN_ONs that trigger if
pending_eoi ever becomes negative; however, these do not do anything
to recover, and it bad things will happen soon after they trigger.

When the next RTC interrupt is triggered, rtc_check_coalesced() will
return false, but ioapic_service will find pending_eoi != 0 and
do a BUG_ON.  To avoid this, should pending_eoi ever be nonzero,
call kvm_rtc_eoi_tracking_restore_all to recompute a correct
dest_map and pending_eoi.

Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'virt')
-rw-r--r--virt/kvm/ioapic.c23
1 files changed, 18 insertions, 5 deletions
diff --git a/virt/kvm/ioapic.c b/virt/kvm/ioapic.c
index d98d107efb05..2458a1dc2ba9 100644
--- a/virt/kvm/ioapic.c
+++ b/virt/kvm/ioapic.c
@@ -97,6 +97,14 @@ static void rtc_irq_eoi_tracking_reset(struct kvm_ioapic *ioapic)
 	bitmap_zero(ioapic->rtc_status.dest_map, KVM_MAX_VCPUS);
 }
 
+static void kvm_rtc_eoi_tracking_restore_all(struct kvm_ioapic *ioapic);
+
+static void rtc_status_pending_eoi_check_valid(struct kvm_ioapic *ioapic)
+{
+	if (WARN_ON(ioapic->rtc_status.pending_eoi < 0))
+		kvm_rtc_eoi_tracking_restore_all(ioapic);
+}
+
 static void __rtc_irq_eoi_tracking_restore_one(struct kvm_vcpu *vcpu)
 {
 	bool new_val, old_val;
@@ -120,9 +128,8 @@ static void __rtc_irq_eoi_tracking_restore_one(struct kvm_vcpu *vcpu)
 	} else {
 		__clear_bit(vcpu->vcpu_id, ioapic->rtc_status.dest_map);
 		ioapic->rtc_status.pending_eoi--;
+		rtc_status_pending_eoi_check_valid(ioapic);
 	}
-
-	WARN_ON(ioapic->rtc_status.pending_eoi < 0);
 }
 
 void kvm_rtc_eoi_tracking_restore_one(struct kvm_vcpu *vcpu)
@@ -149,10 +156,10 @@ static void kvm_rtc_eoi_tracking_restore_all(struct kvm_ioapic *ioapic)
 
 static void rtc_irq_eoi(struct kvm_ioapic *ioapic, struct kvm_vcpu *vcpu)
 {
-	if (test_and_clear_bit(vcpu->vcpu_id, ioapic->rtc_status.dest_map))
+	if (test_and_clear_bit(vcpu->vcpu_id, ioapic->rtc_status.dest_map)) {
 		--ioapic->rtc_status.pending_eoi;
-
-	WARN_ON(ioapic->rtc_status.pending_eoi < 0);
+		rtc_status_pending_eoi_check_valid(ioapic);
+	}
 }
 
 static bool rtc_irq_check_coalesced(struct kvm_ioapic *ioapic)
@@ -353,6 +360,12 @@ static int ioapic_service(struct kvm_ioapic *ioapic, int irq, bool line_status)
 		ioapic->irr &= ~(1 << irq);
 
 	if (irq == RTC_GSI && line_status) {
+		/*
+		 * pending_eoi cannot ever become negative (see
+		 * rtc_status_pending_eoi_check_valid) and the caller
+		 * ensures that it is only called if it is >= zero, namely
+		 * if rtc_irq_check_coalesced returns false).
+		 */
 		BUG_ON(ioapic->rtc_status.pending_eoi != 0);
 		ret = kvm_irq_delivery_to_apic(ioapic->kvm, NULL, &irqe,
 				ioapic->rtc_status.dest_map);