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

Configure Feed

Select the types of activity you want to include in your feed.

KVM: arm64: GICv3: Handle deactivation via ICV_DIR_EL1 traps

Deactivation via ICV_DIR_EL1 is both relatively straightforward
(we have the interrupt that needs deactivation) and really awkward.

The main issue is that the interrupt may either be in an LR on
another CPU, or ourside of any LR.

In the former case, we process the deactivation is if ot was
a write to GICD_CACTIVERn, which is already implemented as a big
hammer IPI'ing all vcpus. In the latter case, we just perform
a normal deactivation, similar to what we do for EOImode==0.

Another annoying aspect is that we need to tell the CPU owning
the interrupt that its ap_list needs laudering. We use a brand new
vcpu request to that effect.

Note that this doesn't address deactivation via the GICV MMIO view,
which will be taken care of in a later change.

Tested-by: Fuad Tabba <tabba@google.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Tested-by: Mark Brown <broonie@kernel.org>
Link: https://msgid.link/20251120172540.2267180-29-maz@kernel.org
Signed-off-by: Oliver Upton <oupton@kernel.org>

authored by

Marc Zyngier and committed by
Oliver Upton
cd4f6ee9 3cfd59f8

+123 -2
+1
arch/arm64/include/asm/kvm_host.h
··· 54 54 #define KVM_REQ_NESTED_S2_UNMAP KVM_ARCH_REQ(8) 55 55 #define KVM_REQ_GUEST_HYP_IRQ_PENDING KVM_ARCH_REQ(9) 56 56 #define KVM_REQ_MAP_L1_VNCR_EL2 KVM_ARCH_REQ(10) 57 + #define KVM_REQ_VGIC_PROCESS_UPDATE KVM_ARCH_REQ(11) 57 58 58 59 #define KVM_DIRTY_LOG_MANUAL_CAPS (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | \ 59 60 KVM_DIRTY_LOG_INITIALLY_SET)
+4
arch/arm64/kvm/arm.c
··· 1041 1041 */ 1042 1042 kvm_check_request(KVM_REQ_IRQ_PENDING, vcpu); 1043 1043 1044 + /* Process interrupts deactivated through a trap */ 1045 + if (kvm_check_request(KVM_REQ_VGIC_PROCESS_UPDATE, vcpu)) 1046 + kvm_vgic_process_async_update(vcpu); 1047 + 1044 1048 if (kvm_check_request(KVM_REQ_RECORD_STEAL, vcpu)) 1045 1049 kvm_update_stolen_time(vcpu); 1046 1050
+3
arch/arm64/kvm/hyp/vgic-v3-sr.c
··· 1247 1247 case SYS_ICC_DIR_EL1: 1248 1248 if (unlikely(is_read)) 1249 1249 return 0; 1250 + /* Full exit if required to handle overflow deactivation... */ 1251 + if (vcpu->arch.vgic_cpu.vgic_v3.vgic_hcr & ICH_HCR_EL2_TDIR) 1252 + return 0; 1250 1253 fn = __vgic_v3_write_dir; 1251 1254 break; 1252 1255 case SYS_ICC_RPR_EL1:
+17 -2
arch/arm64/kvm/sys_regs.c
··· 666 666 return true; 667 667 } 668 668 669 + static bool access_gic_dir(struct kvm_vcpu *vcpu, 670 + struct sys_reg_params *p, 671 + const struct sys_reg_desc *r) 672 + { 673 + if (!kvm_has_gicv3(vcpu->kvm)) 674 + return undef_access(vcpu, p, r); 675 + 676 + if (!p->is_write) 677 + return undef_access(vcpu, p, r); 678 + 679 + vgic_v3_deactivate(vcpu, p->regval); 680 + 681 + return true; 682 + } 683 + 669 684 static bool trap_raz_wi(struct kvm_vcpu *vcpu, 670 685 struct sys_reg_params *p, 671 686 const struct sys_reg_desc *r) ··· 3385 3370 { SYS_DESC(SYS_ICC_AP1R1_EL1), undef_access }, 3386 3371 { SYS_DESC(SYS_ICC_AP1R2_EL1), undef_access }, 3387 3372 { SYS_DESC(SYS_ICC_AP1R3_EL1), undef_access }, 3388 - { SYS_DESC(SYS_ICC_DIR_EL1), undef_access }, 3373 + { SYS_DESC(SYS_ICC_DIR_EL1), access_gic_dir }, 3389 3374 { SYS_DESC(SYS_ICC_RPR_EL1), undef_access }, 3390 3375 { SYS_DESC(SYS_ICC_SGI1R_EL1), access_gic_sgi }, 3391 3376 { SYS_DESC(SYS_ICC_ASGI1R_EL1), access_gic_sgi }, ··· 4510 4495 { CP15_SYS_DESC(SYS_ICC_AP1R1_EL1), undef_access }, 4511 4496 { CP15_SYS_DESC(SYS_ICC_AP1R2_EL1), undef_access }, 4512 4497 { CP15_SYS_DESC(SYS_ICC_AP1R3_EL1), undef_access }, 4513 - { CP15_SYS_DESC(SYS_ICC_DIR_EL1), undef_access }, 4498 + { CP15_SYS_DESC(SYS_ICC_DIR_EL1), access_gic_dir }, 4514 4499 { CP15_SYS_DESC(SYS_ICC_RPR_EL1), undef_access }, 4515 4500 { CP15_SYS_DESC(SYS_ICC_IAR1_EL1), undef_access }, 4516 4501 { CP15_SYS_DESC(SYS_ICC_EOIR1_EL1), undef_access },
+85
arch/arm64/kvm/vgic/vgic-v3.c
··· 12 12 #include <asm/kvm_mmu.h> 13 13 #include <asm/kvm_asm.h> 14 14 15 + #include "vgic-mmio.h" 15 16 #include "vgic.h" 16 17 17 18 static bool group0_trap; ··· 170 169 } 171 170 172 171 cpuif->used_lrs = 0; 172 + } 173 + 174 + void vgic_v3_deactivate(struct kvm_vcpu *vcpu, u64 val) 175 + { 176 + struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; 177 + struct vgic_v3_cpu_if *cpuif = &vgic_cpu->vgic_v3; 178 + struct kvm_vcpu *target_vcpu = NULL; 179 + struct vgic_irq *irq; 180 + unsigned long flags; 181 + bool mmio = false; 182 + u64 lr = 0; 183 + 184 + /* 185 + * We only deal with DIR when EOIMode==1, and only for SGI, 186 + * PPI or SPI. 187 + */ 188 + if (!(cpuif->vgic_vmcr & ICH_VMCR_EOIM_MASK) || 189 + val >= vcpu->kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS) 190 + return; 191 + 192 + /* Make sure we're in the same context as LR handling */ 193 + local_irq_save(flags); 194 + 195 + irq = vgic_get_vcpu_irq(vcpu, val); 196 + if (WARN_ON_ONCE(!irq)) 197 + goto out; 198 + 199 + /* 200 + * EOIMode=1: we must rely on traps to handle deactivate of 201 + * overflowing interrupts, as there is no ordering guarantee and 202 + * EOIcount isn't being incremented. Priority drop will have taken 203 + * place, as ICV_EOIxR_EL1 only affects the APRs and not the LRs. 204 + * 205 + * Three possibities: 206 + * 207 + * - The irq is not queued on any CPU, and there is nothing to 208 + * do, 209 + * 210 + * - Or the irq is in an LR, meaning that its state is not 211 + * directly observable. Treat it bluntly by making it as if 212 + * this was a write to GICD_ICACTIVER, which will force an 213 + * exit on all vcpus. If it hurts, don't do that. 214 + * 215 + * - Or the irq is active, but not in an LR, and we can 216 + * directly deactivate it by building a pseudo-LR, fold it, 217 + * and queue a request to prune the resulting ap_list, 218 + */ 219 + scoped_guard(raw_spinlock, &irq->irq_lock) { 220 + target_vcpu = irq->vcpu; 221 + 222 + /* Not on any ap_list? */ 223 + if (!target_vcpu) 224 + goto put; 225 + 226 + /* 227 + * Urgh. We're deactivating something that we cannot 228 + * observe yet... Big hammer time. 229 + */ 230 + if (irq->on_lr) { 231 + mmio = true; 232 + goto put; 233 + } 234 + 235 + /* (with a Dalek voice) DEACTIVATE!!!! */ 236 + lr = vgic_v3_compute_lr(vcpu, irq) & ~ICH_LR_ACTIVE_BIT; 237 + } 238 + 239 + if (lr & ICH_LR_HW) 240 + vgic_v3_deactivate_phys(FIELD_GET(ICH_LR_PHYS_ID_MASK, lr)); 241 + 242 + vgic_v3_fold_lr(vcpu, lr); 243 + 244 + put: 245 + vgic_put_irq(vcpu->kvm, irq); 246 + 247 + out: 248 + local_irq_restore(flags); 249 + 250 + if (mmio) 251 + vgic_mmio_write_cactive(vcpu, (val / 32) * 4, 4, BIT(val % 32)); 252 + 253 + /* Force the ap_list to be pruned */ 254 + if (target_vcpu) 255 + kvm_make_request(KVM_REQ_VGIC_PROCESS_UPDATE, target_vcpu); 173 256 } 174 257 175 258 /* Requires the irq to be locked already */
+11
arch/arm64/kvm/vgic/vgic.c
··· 990 990 vgic_prune_ap_list(vcpu); 991 991 } 992 992 993 + /* Sync interrupts that were deactivated through a DIR trap */ 994 + void kvm_vgic_process_async_update(struct kvm_vcpu *vcpu) 995 + { 996 + unsigned long flags; 997 + 998 + /* Make sure we're in the same context as LR handling */ 999 + local_irq_save(flags); 1000 + vgic_prune_ap_list(vcpu); 1001 + local_irq_restore(flags); 1002 + } 1003 + 993 1004 static inline void vgic_restore_state(struct kvm_vcpu *vcpu) 994 1005 { 995 1006 if (!static_branch_unlikely(&kvm_vgic_global_state.gicv3_cpuif))
+1
arch/arm64/kvm/vgic/vgic.h
··· 318 318 void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu); 319 319 void vgic_v3_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr); 320 320 void vgic_v3_clear_lr(struct kvm_vcpu *vcpu, int lr); 321 + void vgic_v3_deactivate(struct kvm_vcpu *vcpu, u64 val); 321 322 void vgic_v3_configure_hcr(struct kvm_vcpu *vcpu, struct ap_list_summary *als); 322 323 void vgic_v3_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); 323 324 void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr);
+1
include/kvm/arm_vgic.h
··· 421 421 void kvm_vgic_sync_hwstate(struct kvm_vcpu *vcpu); 422 422 void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu); 423 423 void kvm_vgic_reset_mapped_irq(struct kvm_vcpu *vcpu, u32 vintid); 424 + void kvm_vgic_process_async_update(struct kvm_vcpu *vcpu); 424 425 425 426 void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg, bool allow_group1); 426 427