aboutsummaryrefslogtreecommitdiffstats
path: root/virt
diff options
context:
space:
mode:
authorChristoffer Dall <christoffer.dall@linaro.org>2015-10-17 13:05:27 -0400
committerChristoffer Dall <christoffer.dall@linaro.org>2015-10-20 12:09:13 -0400
commit0d997491f814c87310a6ad7be30a9049c7150489 (patch)
treee368ad59cc46060514c092f8121bd62f7311e0c4 /virt
parent544c572e03174438b6656ed24a4516b9a9d5f14a (diff)
arm/arm64: KVM: Fix disabled distributor operation
We currently do a single update of the vgic state when the distributor enable/disable control register is accessed and then bypass updating the state for as long as the distributor remains disabled. This is incorrect, because updating the state does not consider the distributor enable bit, and this you can end up in a situation where an interrupt is marked as pending on the CPU interface, but not pending on the distributor, which is an impossible state to be in, and triggers a warning. Consider for example the following sequence of events: 1. An interrupt is marked as pending on the distributor - the interrupt is also forwarded to the CPU interface 2. The guest turns off the distributor (it's about to do a reboot) - we stop updating the CPU interface state from now on 3. The guest disables the pending interrupt - we remove the pending state from the distributor, but don't touch the CPU interface, see point 2. Since the distributor disable bit really means that no interrupts should be forwarded to the CPU interface, we modify the code to keep updating the internal VGIC state, but always set the CPU interface pending bits to zero when the distributor is disabled. Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Diffstat (limited to 'virt')
-rw-r--r--virt/kvm/arm/vgic.c11
1 files changed, 6 insertions, 5 deletions
diff --git a/virt/kvm/arm/vgic.c b/virt/kvm/arm/vgic.c
index 58b125676785..66c66165e712 100644
--- a/virt/kvm/arm/vgic.c
+++ b/virt/kvm/arm/vgic.c
@@ -1012,6 +1012,12 @@ static int compute_pending_for_cpu(struct kvm_vcpu *vcpu)
1012 pend_percpu = vcpu->arch.vgic_cpu.pending_percpu; 1012 pend_percpu = vcpu->arch.vgic_cpu.pending_percpu;
1013 pend_shared = vcpu->arch.vgic_cpu.pending_shared; 1013 pend_shared = vcpu->arch.vgic_cpu.pending_shared;
1014 1014
1015 if (!dist->enabled) {
1016 bitmap_zero(pend_percpu, VGIC_NR_PRIVATE_IRQS);
1017 bitmap_zero(pend_shared, nr_shared);
1018 return 0;
1019 }
1020
1015 pending = vgic_bitmap_get_cpu_map(&dist->irq_pending, vcpu_id); 1021 pending = vgic_bitmap_get_cpu_map(&dist->irq_pending, vcpu_id);
1016 enabled = vgic_bitmap_get_cpu_map(&dist->irq_enabled, vcpu_id); 1022 enabled = vgic_bitmap_get_cpu_map(&dist->irq_enabled, vcpu_id);
1017 bitmap_and(pend_percpu, pending, enabled, VGIC_NR_PRIVATE_IRQS); 1023 bitmap_and(pend_percpu, pending, enabled, VGIC_NR_PRIVATE_IRQS);
@@ -1039,11 +1045,6 @@ void vgic_update_state(struct kvm *kvm)
1039 struct kvm_vcpu *vcpu; 1045 struct kvm_vcpu *vcpu;
1040 int c; 1046 int c;
1041 1047
1042 if (!dist->enabled) {
1043 set_bit(0, dist->irq_pending_on_cpu);
1044 return;
1045 }
1046
1047 kvm_for_each_vcpu(c, vcpu, kvm) { 1048 kvm_for_each_vcpu(c, vcpu, kvm) {
1048 if (compute_pending_for_cpu(vcpu)) 1049 if (compute_pending_for_cpu(vcpu))
1049 set_bit(c, dist->irq_pending_on_cpu); 1050 set_bit(c, dist->irq_pending_on_cpu);