Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: arm64: vgic: Implement SW-driven deactivation

In order to deal with these systems that do not offer HW-based
deactivation of interrupts, let implement a SW-based approach:

- When the irq is queued into a LR, treat it as a pure virtual
interrupt and set the EOI flag in the LR.

- When the interrupt state is read back from the LR, force a
deactivation when the state is invalid (neither active nor
pending)

Interrupts requiring such treatment get the VGIC_SW_RESAMPLE flag.

Signed-off-by: Marc Zyngier <maz@kernel.org>

+40 -8
+15 -4
arch/arm64/kvm/vgic/vgic-v2.c
··· 108 108 * If this causes us to lower the level, we have to also clear 109 109 * the physical active state, since we will otherwise never be 110 110 * told when the interrupt becomes asserted again. 111 + * 112 + * Another case is when the interrupt requires a helping hand 113 + * on deactivation (no HW deactivation, for example). 111 114 */ 112 - if (vgic_irq_is_mapped_level(irq) && (val & GICH_LR_PENDING_BIT)) { 113 - irq->line_level = vgic_get_phys_line_level(irq); 115 + if (vgic_irq_is_mapped_level(irq)) { 116 + bool resample = false; 114 117 115 - if (!irq->line_level) 118 + if (val & GICH_LR_PENDING_BIT) { 119 + irq->line_level = vgic_get_phys_line_level(irq); 120 + resample = !irq->line_level; 121 + } else if (vgic_irq_needs_resampling(irq) && 122 + !(irq->active || irq->pending_latch)) { 123 + resample = true; 124 + } 125 + 126 + if (resample) 116 127 vgic_irq_set_phys_active(irq, false); 117 128 } 118 129 ··· 163 152 if (irq->group) 164 153 val |= GICH_LR_GROUP1; 165 154 166 - if (irq->hw) { 155 + if (irq->hw && !vgic_irq_needs_resampling(irq)) { 167 156 val |= GICH_LR_HW; 168 157 val |= irq->hwintid << GICH_LR_PHYSID_CPUID_SHIFT; 169 158 /*
+15 -4
arch/arm64/kvm/vgic/vgic-v3.c
··· 101 101 * If this causes us to lower the level, we have to also clear 102 102 * the physical active state, since we will otherwise never be 103 103 * told when the interrupt becomes asserted again. 104 + * 105 + * Another case is when the interrupt requires a helping hand 106 + * on deactivation (no HW deactivation, for example). 104 107 */ 105 - if (vgic_irq_is_mapped_level(irq) && (val & ICH_LR_PENDING_BIT)) { 106 - irq->line_level = vgic_get_phys_line_level(irq); 108 + if (vgic_irq_is_mapped_level(irq)) { 109 + bool resample = false; 107 110 108 - if (!irq->line_level) 111 + if (val & ICH_LR_PENDING_BIT) { 112 + irq->line_level = vgic_get_phys_line_level(irq); 113 + resample = !irq->line_level; 114 + } else if (vgic_irq_needs_resampling(irq) && 115 + !(irq->active || irq->pending_latch)) { 116 + resample = true; 117 + } 118 + 119 + if (resample) 109 120 vgic_irq_set_phys_active(irq, false); 110 121 } 111 122 ··· 147 136 } 148 137 } 149 138 150 - if (irq->hw) { 139 + if (irq->hw && !vgic_irq_needs_resampling(irq)) { 151 140 val |= ICH_LR_HW; 152 141 val |= ((u64)irq->hwintid) << ICH_LR_PHYS_ID_SHIFT; 153 142 /*
+10
include/kvm/arm_vgic.h
··· 99 99 * kvm_arm_get_running_vcpu() to get the vcpu pointer for private IRQs. 100 100 */ 101 101 struct irq_ops { 102 + /* Per interrupt flags for special-cased interrupts */ 103 + unsigned long flags; 104 + 105 + #define VGIC_IRQ_SW_RESAMPLE BIT(0) /* Clear the active state for resampling */ 106 + 102 107 /* 103 108 * Callback function pointer to in-kernel devices that can tell us the 104 109 * state of the input level of mapped level-triggered IRQ faster than ··· 154 149 void *owner; /* Opaque pointer to reserve an interrupt 155 150 for in-kernel devices. */ 156 151 }; 152 + 153 + static inline bool vgic_irq_needs_resampling(struct vgic_irq *irq) 154 + { 155 + return irq->ops && (irq->ops->flags & VGIC_IRQ_SW_RESAMPLE); 156 + } 157 157 158 158 struct vgic_register_region; 159 159 struct vgic_its;