Skip to content

Commit e40cc57

Browse files
committed
KVM: arm/arm64: vgic: Support level-triggered mapped interrupts
Level-triggered mapped IRQs are special because we only observe rising edges as input to the VGIC, and we don't set the EOI flag and therefore are not told when the level goes down, so that we can re-queue a new interrupt when the level goes up. One way to solve this problem is to side-step the logic of the VGIC and special case the validation in the injection path, but it has the unfortunate drawback of having to peak into the physical GIC state whenever we want to know if the interrupt is pending on the virtual distributor. Instead, we can maintain the current semantics of a level triggered interrupt by sort of treating it as an edge-triggered interrupt, following from the fact that we only observe an asserting edge. This requires us to be a bit careful when populating the LRs and when folding the state back in though: * We lower the line level when populating the LR, so that when subsequently observing an asserting edge, the VGIC will do the right thing. * If the guest never acked the interrupt while running (for example if it had masked interrupts at the CPU level while running), we have to preserve the pending state of the LR and move it back to the line_level field of the struct irq when folding LR state. If the guest never acked the interrupt while running, but changed the device state and lowered the line (again with interrupts masked) then we need to observe this change in the line_level. Both of the above situations are solved by sampling the physical line and set the line level when folding the LR back. * Finally, if the guest never acked the interrupt while running and sampling the line reveals that the device state has changed and the line has been lowered, we must clear the physical active state, since we will otherwise never be told when the interrupt becomes asserted again. This has the added benefit of making the timer optimization patches (https://lists.cs.columbia.edu/pipermail/kvmarm/2017-July/026343.html) a bit simpler, because the timer code doesn't have to clear the active state on the sync anymore. It also potentially improves the performance of the timer implementation because the GIC knows the state or the LR and only needs to clear the active state when the pending bit in the LR is still set, where the timer has to always clear it when returning from running the guest with an injected timer interrupt. Reviewed-by: Marc Zyngier <[email protected]> Reviewed-by: Eric Auger <[email protected]> Signed-off-by: Christoffer Dall <[email protected]>
1 parent 70450a9 commit e40cc57

File tree

4 files changed

+88
-0
lines changed

4 files changed

+88
-0
lines changed

virt/kvm/arm/vgic/vgic-v2.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,26 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
105105
irq->pending_latch = false;
106106
}
107107

108+
/*
109+
* Level-triggered mapped IRQs are special because we only
110+
* observe rising edges as input to the VGIC.
111+
*
112+
* If the guest never acked the interrupt we have to sample
113+
* the physical line and set the line level, because the
114+
* device state could have changed or we simply need to
115+
* process the still pending interrupt later.
116+
*
117+
* If this causes us to lower the level, we have to also clear
118+
* the physical active state, since we will otherwise never be
119+
* told when the interrupt becomes asserted again.
120+
*/
121+
if (vgic_irq_is_mapped_level(irq) && (val & GICH_LR_PENDING_BIT)) {
122+
irq->line_level = vgic_get_phys_line_level(irq);
123+
124+
if (!irq->line_level)
125+
vgic_irq_set_phys_active(irq, false);
126+
}
127+
108128
spin_unlock_irqrestore(&irq->irq_lock, flags);
109129
vgic_put_irq(vcpu->kvm, irq);
110130
}
@@ -162,6 +182,15 @@ void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
162182
val |= GICH_LR_EOI;
163183
}
164184

185+
/*
186+
* Level-triggered mapped IRQs are special because we only observe
187+
* rising edges as input to the VGIC. We therefore lower the line
188+
* level here, so that we can take new virtual IRQs. See
189+
* vgic_v2_fold_lr_state for more info.
190+
*/
191+
if (vgic_irq_is_mapped_level(irq) && (val & GICH_LR_PENDING_BIT))
192+
irq->line_level = false;
193+
165194
/* The GICv2 LR only holds five bits of priority. */
166195
val |= (irq->priority >> 3) << GICH_LR_PRIORITY_SHIFT;
167196

virt/kvm/arm/vgic/vgic-v3.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,26 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
9696
irq->pending_latch = false;
9797
}
9898

99+
/*
100+
* Level-triggered mapped IRQs are special because we only
101+
* observe rising edges as input to the VGIC.
102+
*
103+
* If the guest never acked the interrupt we have to sample
104+
* the physical line and set the line level, because the
105+
* device state could have changed or we simply need to
106+
* process the still pending interrupt later.
107+
*
108+
* If this causes us to lower the level, we have to also clear
109+
* the physical active state, since we will otherwise never be
110+
* told when the interrupt becomes asserted again.
111+
*/
112+
if (vgic_irq_is_mapped_level(irq) && (val & ICH_LR_PENDING_BIT)) {
113+
irq->line_level = vgic_get_phys_line_level(irq);
114+
115+
if (!irq->line_level)
116+
vgic_irq_set_phys_active(irq, false);
117+
}
118+
99119
spin_unlock_irqrestore(&irq->irq_lock, flags);
100120
vgic_put_irq(vcpu->kvm, irq);
101121
}
@@ -145,6 +165,15 @@ void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr)
145165
val |= ICH_LR_EOI;
146166
}
147167

168+
/*
169+
* Level-triggered mapped IRQs are special because we only observe
170+
* rising edges as input to the VGIC. We therefore lower the line
171+
* level here, so that we can take new virtual IRQs. See
172+
* vgic_v3_fold_lr_state for more info.
173+
*/
174+
if (vgic_irq_is_mapped_level(irq) && (val & ICH_LR_PENDING_BIT))
175+
irq->line_level = false;
176+
148177
/*
149178
* We currently only support Group1 interrupts, which is a
150179
* known defect. This needs to be addressed at some point.

virt/kvm/arm/vgic/vgic.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,29 @@ void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
144144
kfree(irq);
145145
}
146146

147+
/* Get the input level of a mapped IRQ directly from the physical GIC */
148+
bool vgic_get_phys_line_level(struct vgic_irq *irq)
149+
{
150+
bool line_level;
151+
152+
BUG_ON(!irq->hw);
153+
154+
WARN_ON(irq_get_irqchip_state(irq->host_irq,
155+
IRQCHIP_STATE_PENDING,
156+
&line_level));
157+
return line_level;
158+
}
159+
160+
/* Set/Clear the physical active state */
161+
void vgic_irq_set_phys_active(struct vgic_irq *irq, bool active)
162+
{
163+
164+
BUG_ON(!irq->hw);
165+
WARN_ON(irq_set_irqchip_state(irq->host_irq,
166+
IRQCHIP_STATE_ACTIVE,
167+
active));
168+
}
169+
147170
/**
148171
* kvm_vgic_target_oracle - compute the target vcpu for an irq
149172
*

virt/kvm/arm/vgic/vgic.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ static inline bool irq_is_pending(struct vgic_irq *irq)
104104
return irq->pending_latch || irq->line_level;
105105
}
106106

107+
static inline bool vgic_irq_is_mapped_level(struct vgic_irq *irq)
108+
{
109+
return irq->config == VGIC_CONFIG_LEVEL && irq->hw;
110+
}
111+
107112
/*
108113
* This struct provides an intermediate representation of the fields contained
109114
* in the GICH_VMCR and ICH_VMCR registers, such that code exporting the GIC
@@ -140,6 +145,8 @@ vgic_get_mmio_region(struct kvm_vcpu *vcpu, struct vgic_io_device *iodev,
140145
struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
141146
u32 intid);
142147
void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq);
148+
bool vgic_get_phys_line_level(struct vgic_irq *irq);
149+
void vgic_irq_set_phys_active(struct vgic_irq *irq, bool active);
143150
bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq,
144151
unsigned long flags);
145152
void vgic_kick_vcpus(struct kvm *kvm);

0 commit comments

Comments
 (0)