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

KVM: s390: pci: enable host forwarding of Adapter Event Notifications

In cases where interrupts are not forwarded to the guest via firmware,
KVM is responsible for ensuring delivery. When an interrupt presents
with the forwarding bit, we must process the forwarding tables until
all interrupts are delivered.

Reviewed-by: Christian Borntraeger <borntraeger@linux.ibm.com>
Reviewed-by: Pierre Morel <pmorel@linux.ibm.com>
Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
Link: https://lore.kernel.org/r/20220606203325.110625-14-mjrosato@linux.ibm.com
Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>

authored by

Matthew Rosato and committed by
Christian Borntraeger
73f91b00 98b1d33d

+103 -2
+1
arch/s390/include/asm/kvm_host.h
··· 759 759 u64 inject_pfault_done; 760 760 u64 inject_service_signal; 761 761 u64 inject_virtio; 762 + u64 aen_forward; 762 763 }; 763 764 764 765 struct kvm_arch_memory_slot {
+13
arch/s390/include/asm/tpi.h
··· 19 19 u32 :12; 20 20 } __packed __aligned(4); 21 21 22 + /* I/O-Interruption Code as stored by TPI for an Adapter I/O */ 23 + struct tpi_adapter_info { 24 + u32 aism:8; 25 + u32 :22; 26 + u32 error:1; 27 + u32 forward:1; 28 + u32 reserved; 29 + u32 adapter_IO:1; 30 + u32 directed_irq:1; 31 + u32 isc:3; 32 + u32 :27; 33 + } __packed __aligned(4); 34 + 22 35 #endif /* __ASSEMBLY__ */ 23 36 24 37 #endif /* _ASM_S390_TPI_H */
+77 -1
arch/s390/kvm/interrupt.c
··· 3313 3313 } 3314 3314 EXPORT_SYMBOL_GPL(kvm_s390_gisc_unregister); 3315 3315 3316 + static void aen_host_forward(unsigned long si) 3317 + { 3318 + struct kvm_s390_gisa_interrupt *gi; 3319 + struct zpci_gaite *gaite; 3320 + struct kvm *kvm; 3321 + 3322 + gaite = (struct zpci_gaite *)aift->gait + 3323 + (si * sizeof(struct zpci_gaite)); 3324 + if (gaite->count == 0) 3325 + return; 3326 + if (gaite->aisb != 0) 3327 + set_bit_inv(gaite->aisbo, (unsigned long *)gaite->aisb); 3328 + 3329 + kvm = kvm_s390_pci_si_to_kvm(aift, si); 3330 + if (!kvm) 3331 + return; 3332 + gi = &kvm->arch.gisa_int; 3333 + 3334 + if (!(gi->origin->g1.simm & AIS_MODE_MASK(gaite->gisc)) || 3335 + !(gi->origin->g1.nimm & AIS_MODE_MASK(gaite->gisc))) { 3336 + gisa_set_ipm_gisc(gi->origin, gaite->gisc); 3337 + if (hrtimer_active(&gi->timer)) 3338 + hrtimer_cancel(&gi->timer); 3339 + hrtimer_start(&gi->timer, 0, HRTIMER_MODE_REL); 3340 + kvm->stat.aen_forward++; 3341 + } 3342 + } 3343 + 3344 + static void aen_process_gait(u8 isc) 3345 + { 3346 + bool found = false, first = true; 3347 + union zpci_sic_iib iib = {{0}}; 3348 + unsigned long si, flags; 3349 + 3350 + spin_lock_irqsave(&aift->gait_lock, flags); 3351 + 3352 + if (!aift->gait) { 3353 + spin_unlock_irqrestore(&aift->gait_lock, flags); 3354 + return; 3355 + } 3356 + 3357 + for (si = 0;;) { 3358 + /* Scan adapter summary indicator bit vector */ 3359 + si = airq_iv_scan(aift->sbv, si, airq_iv_end(aift->sbv)); 3360 + if (si == -1UL) { 3361 + if (first || found) { 3362 + /* Re-enable interrupts. */ 3363 + zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, isc, 3364 + &iib); 3365 + first = found = false; 3366 + } else { 3367 + /* Interrupts on and all bits processed */ 3368 + break; 3369 + } 3370 + found = false; 3371 + si = 0; 3372 + /* Scan again after re-enabling interrupts */ 3373 + continue; 3374 + } 3375 + found = true; 3376 + aen_host_forward(si); 3377 + } 3378 + 3379 + spin_unlock_irqrestore(&aift->gait_lock, flags); 3380 + } 3381 + 3316 3382 static void gib_alert_irq_handler(struct airq_struct *airq, 3317 3383 struct tpi_info *tpi_info) 3318 3384 { 3385 + struct tpi_adapter_info *info = (struct tpi_adapter_info *)tpi_info; 3386 + 3319 3387 inc_irq_stat(IRQIO_GAL); 3320 - process_gib_alert_list(); 3388 + 3389 + if ((info->forward || info->error) && 3390 + IS_ENABLED(CONFIG_VFIO_PCI_ZDEV_KVM)) { 3391 + aen_process_gait(info->isc); 3392 + if (info->aism != 0) 3393 + process_gib_alert_list(); 3394 + } else { 3395 + process_gib_alert_list(); 3396 + } 3321 3397 } 3322 3398 3323 3399 static struct airq_struct gib_alert_irq = {
+2 -1
arch/s390/kvm/kvm-s390.c
··· 64 64 STATS_DESC_COUNTER(VM, inject_float_mchk), 65 65 STATS_DESC_COUNTER(VM, inject_pfault_done), 66 66 STATS_DESC_COUNTER(VM, inject_service_signal), 67 - STATS_DESC_COUNTER(VM, inject_virtio) 67 + STATS_DESC_COUNTER(VM, inject_virtio), 68 + STATS_DESC_COUNTER(VM, aen_forward) 68 69 }; 69 70 70 71 const struct kvm_stats_header kvm_vm_stats_header = {
+10
arch/s390/kvm/pci.h
··· 13 13 #include <linux/kvm_host.h> 14 14 #include <linux/pci.h> 15 15 #include <linux/mutex.h> 16 + #include <linux/kvm_host.h> 16 17 #include <asm/airq.h> 17 18 #include <asm/cpu.h> 18 19 ··· 40 39 }; 41 40 42 41 extern struct zpci_aift *aift; 42 + 43 + static inline struct kvm *kvm_s390_pci_si_to_kvm(struct zpci_aift *aift, 44 + unsigned long si) 45 + { 46 + if (!IS_ENABLED(CONFIG_VFIO_PCI_ZDEV_KVM) || aift->kzdev == 0 || 47 + aift->kzdev[si] == 0) 48 + return 0; 49 + return aift->kzdev[si]->kvm; 50 + }; 43 51 44 52 int kvm_s390_pci_aen_init(u8 nisc); 45 53 void kvm_s390_pci_aen_exit(void);