aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Mueller <mimu@linux.vnet.ibm.com>2017-06-12 08:15:19 -0400
committerChristian Borntraeger <borntraeger@de.ibm.com>2018-01-26 05:11:39 -0500
commitd7c5cb0105ddeff56694f4c6222ee7221824bad3 (patch)
tree35cd08dec40a1908dcc8a2dba534c212dd7eb87b
parent72b523a30d9b9c7f749c3209e13de983aeb919c1 (diff)
KVM: s390: exploit GISA and AIV for emulated interrupts
The adapter interruption virtualization (AIV) facility is an optional facility that comes with functionality expected to increase the performance of adapter interrupt handling for both emulated and passed-through adapter interrupts. With AIV, adapter interrupts can be delivered to the guest without exiting SIE. This patch provides some preparations for using AIV for emulated adapter interrupts (including virtio) if it's available. When using AIV, the interrupts are delivered at the so called GISA by setting the bit corresponding to its Interruption Subclass (ISC) in the Interruption Pending Mask (IPM) instead of inserting a node into the floating interrupt list. To keep the change reasonably small, the handling of this new state is deferred in get_all_floating_irqs and handle_tpi. This patch concentrates on the code handling enqueuement of emulated adapter interrupts, and their delivery to the guest. Note that care is still required for adapter interrupts using AIV, because there is no guarantee that AIV is going to deliver the adapter interrupts pending at the GISA (consider all vcpus idle). When delivering GISA adapter interrupts by the host (usual mechanism) special attention is required to honor interrupt priorities. Empirical results show that the time window between making an interrupt pending at the GISA and doing kvm_s390_deliver_pending_interrupts is sufficient for a guest with at least moderate cpu activity to get adapter interrupts delivered within the SIE, and potentially save some SIE exits (if not other deliverable interrupts). The code will be activated with a follow-up patch. Signed-off-by: Michael Mueller <mimu@linux.vnet.ibm.com> Acked-by: Christian Borntraeger <borntraeger@de.ibm.com> Reviewed-by: David Hildenbrand <david@redhat.com> Reviewed-by: Cornelia Huck <cohuck@redhat.com> Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
-rw-r--r--arch/s390/include/asm/kvm_host.h1
-rw-r--r--arch/s390/kvm/interrupt.c93
-rw-r--r--arch/s390/kvm/kvm-s390.c8
-rw-r--r--arch/s390/kvm/kvm-s390.h3
4 files changed, 87 insertions, 18 deletions
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index 4f89b2783512..76d6f0ef3dbd 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -785,6 +785,7 @@ struct kvm_arch{
785 struct kvm_s390_migration_state *migration_state; 785 struct kvm_s390_migration_state *migration_state;
786 /* subset of available cpu features enabled by user space */ 786 /* subset of available cpu features enabled by user space */
787 DECLARE_BITMAP(cpu_feat, KVM_S390_VM_CPU_FEAT_NR_BITS); 787 DECLARE_BITMAP(cpu_feat, KVM_S390_VM_CPU_FEAT_NR_BITS);
788 struct kvm_s390_gisa *gisa;
788}; 789};
789 790
790#define KVM_HVA_ERR_BAD (-1UL) 791#define KVM_HVA_ERR_BAD (-1UL)
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index 8985ce51f687..f293c956e6db 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -234,7 +234,8 @@ static inline int kvm_s390_gisa_tac_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gis
234static inline unsigned long pending_irqs(struct kvm_vcpu *vcpu) 234static inline unsigned long pending_irqs(struct kvm_vcpu *vcpu)
235{ 235{
236 return vcpu->kvm->arch.float_int.pending_irqs | 236 return vcpu->kvm->arch.float_int.pending_irqs |
237 vcpu->arch.local_int.pending_irqs; 237 vcpu->arch.local_int.pending_irqs |
238 kvm_s390_gisa_get_ipm(vcpu->kvm->arch.gisa) << IRQ_PEND_IO_ISC_7;
238} 239}
239 240
240static inline int isc_to_irq_type(unsigned long isc) 241static inline int isc_to_irq_type(unsigned long isc)
@@ -919,18 +920,38 @@ static int __must_check __deliver_virtio(struct kvm_vcpu *vcpu)
919 return rc ? -EFAULT : 0; 920 return rc ? -EFAULT : 0;
920} 921}
921 922
923static int __do_deliver_io(struct kvm_vcpu *vcpu, struct kvm_s390_io_info *io)
924{
925 int rc;
926
927 rc = put_guest_lc(vcpu, io->subchannel_id, (u16 *)__LC_SUBCHANNEL_ID);
928 rc |= put_guest_lc(vcpu, io->subchannel_nr, (u16 *)__LC_SUBCHANNEL_NR);
929 rc |= put_guest_lc(vcpu, io->io_int_parm, (u32 *)__LC_IO_INT_PARM);
930 rc |= put_guest_lc(vcpu, io->io_int_word, (u32 *)__LC_IO_INT_WORD);
931 rc |= write_guest_lc(vcpu, __LC_IO_OLD_PSW,
932 &vcpu->arch.sie_block->gpsw,
933 sizeof(psw_t));
934 rc |= read_guest_lc(vcpu, __LC_IO_NEW_PSW,
935 &vcpu->arch.sie_block->gpsw,
936 sizeof(psw_t));
937 return rc ? -EFAULT : 0;
938}
939
922static int __must_check __deliver_io(struct kvm_vcpu *vcpu, 940static int __must_check __deliver_io(struct kvm_vcpu *vcpu,
923 unsigned long irq_type) 941 unsigned long irq_type)
924{ 942{
925 struct list_head *isc_list; 943 struct list_head *isc_list;
926 struct kvm_s390_float_interrupt *fi; 944 struct kvm_s390_float_interrupt *fi;
927 struct kvm_s390_interrupt_info *inti = NULL; 945 struct kvm_s390_interrupt_info *inti = NULL;
946 struct kvm_s390_io_info io;
947 u32 isc;
928 int rc = 0; 948 int rc = 0;
929 949
930 fi = &vcpu->kvm->arch.float_int; 950 fi = &vcpu->kvm->arch.float_int;
931 951
932 spin_lock(&fi->lock); 952 spin_lock(&fi->lock);
933 isc_list = &fi->lists[irq_type_to_isc(irq_type)]; 953 isc = irq_type_to_isc(irq_type);
954 isc_list = &fi->lists[isc];
934 inti = list_first_entry_or_null(isc_list, 955 inti = list_first_entry_or_null(isc_list,
935 struct kvm_s390_interrupt_info, 956 struct kvm_s390_interrupt_info,
936 list); 957 list);
@@ -958,24 +979,31 @@ static int __must_check __deliver_io(struct kvm_vcpu *vcpu,
958 spin_unlock(&fi->lock); 979 spin_unlock(&fi->lock);
959 980
960 if (inti) { 981 if (inti) {
961 rc = put_guest_lc(vcpu, inti->io.subchannel_id, 982 rc = __do_deliver_io(vcpu, &(inti->io));
962 (u16 *)__LC_SUBCHANNEL_ID);
963 rc |= put_guest_lc(vcpu, inti->io.subchannel_nr,
964 (u16 *)__LC_SUBCHANNEL_NR);
965 rc |= put_guest_lc(vcpu, inti->io.io_int_parm,
966 (u32 *)__LC_IO_INT_PARM);
967 rc |= put_guest_lc(vcpu, inti->io.io_int_word,
968 (u32 *)__LC_IO_INT_WORD);
969 rc |= write_guest_lc(vcpu, __LC_IO_OLD_PSW,
970 &vcpu->arch.sie_block->gpsw,
971 sizeof(psw_t));
972 rc |= read_guest_lc(vcpu, __LC_IO_NEW_PSW,
973 &vcpu->arch.sie_block->gpsw,
974 sizeof(psw_t));
975 kfree(inti); 983 kfree(inti);
984 goto out;
976 } 985 }
977 986
978 return rc ? -EFAULT : 0; 987 if (vcpu->kvm->arch.gisa &&
988 kvm_s390_gisa_tac_ipm_gisc(vcpu->kvm->arch.gisa, isc)) {
989 /*
990 * in case an adapter interrupt was not delivered
991 * in SIE context KVM will handle the delivery
992 */
993 VCPU_EVENT(vcpu, 4, "%s isc %u", "deliver: I/O (AI/gisa)", isc);
994 memset(&io, 0, sizeof(io));
995 io.io_int_word = (isc << 27) | 0x80000000;
996 vcpu->stat.deliver_io_int++;
997 trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id,
998 KVM_S390_INT_IO(1, 0, 0, 0),
999 ((__u32)io.subchannel_id << 16) |
1000 io.subchannel_nr,
1001 ((__u64)io.io_int_parm << 32) |
1002 io.io_int_word);
1003 rc = __do_deliver_io(vcpu, &io);
1004 }
1005out:
1006 return rc;
979} 1007}
980 1008
981typedef int (*deliver_irq_t)(struct kvm_vcpu *vcpu); 1009typedef int (*deliver_irq_t)(struct kvm_vcpu *vcpu);
@@ -1539,6 +1567,15 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
1539 struct list_head *list; 1567 struct list_head *list;
1540 int isc; 1568 int isc;
1541 1569
1570 isc = int_word_to_isc(inti->io.io_int_word);
1571
1572 if (kvm->arch.gisa && inti->type & KVM_S390_INT_IO_AI_MASK) {
1573 VM_EVENT(kvm, 4, "%s isc %1u", "inject: I/O (AI/gisa)", isc);
1574 kvm_s390_gisa_set_ipm_gisc(kvm->arch.gisa, isc);
1575 kfree(inti);
1576 return 0;
1577 }
1578
1542 fi = &kvm->arch.float_int; 1579 fi = &kvm->arch.float_int;
1543 spin_lock(&fi->lock); 1580 spin_lock(&fi->lock);
1544 if (fi->counters[FIRQ_CNTR_IO] >= KVM_S390_MAX_FLOAT_IRQS) { 1581 if (fi->counters[FIRQ_CNTR_IO] >= KVM_S390_MAX_FLOAT_IRQS) {
@@ -1554,7 +1591,6 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
1554 inti->io.subchannel_id >> 8, 1591 inti->io.subchannel_id >> 8,
1555 inti->io.subchannel_id >> 1 & 0x3, 1592 inti->io.subchannel_id >> 1 & 0x3,
1556 inti->io.subchannel_nr); 1593 inti->io.subchannel_nr);
1557 isc = int_word_to_isc(inti->io.io_int_word);
1558 list = &fi->lists[FIRQ_LIST_IO_ISC_0 + isc]; 1594 list = &fi->lists[FIRQ_LIST_IO_ISC_0 + isc];
1559 list_add_tail(&inti->list, list); 1595 list_add_tail(&inti->list, list);
1560 set_bit(isc_to_irq_type(isc), &fi->pending_irqs); 1596 set_bit(isc_to_irq_type(isc), &fi->pending_irqs);
@@ -2705,3 +2741,24 @@ int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu, __u8 __user *buf, int len)
2705 2741
2706 return n; 2742 return n;
2707} 2743}
2744
2745void kvm_s390_gisa_clear(struct kvm *kvm)
2746{
2747 if (kvm->arch.gisa) {
2748 memset(kvm->arch.gisa, 0, sizeof(struct kvm_s390_gisa));
2749 kvm->arch.gisa->next_alert = (u32)(u64)kvm->arch.gisa;
2750 VM_EVENT(kvm, 3, "gisa 0x%pK cleared", kvm->arch.gisa);
2751 }
2752}
2753
2754void kvm_s390_gisa_init(struct kvm *kvm)
2755{
2756 /* not implemented yet */
2757}
2758
2759void kvm_s390_gisa_destroy(struct kvm *kvm)
2760{
2761 if (!kvm->arch.gisa)
2762 return;
2763 kvm->arch.gisa = NULL;
2764}
diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c
index 2fbdb1601089..2c5e25b39435 100644
--- a/arch/s390/kvm/kvm-s390.c
+++ b/arch/s390/kvm/kvm-s390.c
@@ -1999,6 +1999,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
1999 1999
2000 spin_lock_init(&kvm->arch.start_stop_lock); 2000 spin_lock_init(&kvm->arch.start_stop_lock);
2001 kvm_s390_vsie_init(kvm); 2001 kvm_s390_vsie_init(kvm);
2002 kvm_s390_gisa_init(kvm);
2002 KVM_EVENT(3, "vm 0x%pK created by pid %u", kvm, current->pid); 2003 KVM_EVENT(3, "vm 0x%pK created by pid %u", kvm, current->pid);
2003 2004
2004 return 0; 2005 return 0;
@@ -2061,6 +2062,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
2061 kvm_free_vcpus(kvm); 2062 kvm_free_vcpus(kvm);
2062 sca_dispose(kvm); 2063 sca_dispose(kvm);
2063 debug_unregister(kvm->arch.dbf); 2064 debug_unregister(kvm->arch.dbf);
2065 kvm_s390_gisa_destroy(kvm);
2064 free_page((unsigned long)kvm->arch.sie_page2); 2066 free_page((unsigned long)kvm->arch.sie_page2);
2065 if (!kvm_is_ucontrol(kvm)) 2067 if (!kvm_is_ucontrol(kvm))
2066 gmap_remove(kvm->arch.gmap); 2068 gmap_remove(kvm->arch.gmap);
@@ -2471,6 +2473,11 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
2471 if (test_kvm_facility(vcpu->kvm, 139)) 2473 if (test_kvm_facility(vcpu->kvm, 139))
2472 vcpu->arch.sie_block->ecd |= ECD_MEF; 2474 vcpu->arch.sie_block->ecd |= ECD_MEF;
2473 2475
2476 if (vcpu->arch.sie_block->gd) {
2477 vcpu->arch.sie_block->eca |= ECA_AIV;
2478 VCPU_EVENT(vcpu, 3, "AIV gisa format-%u enabled for cpu %03u",
2479 vcpu->arch.sie_block->gd & 0x3, vcpu->vcpu_id);
2480 }
2474 vcpu->arch.sie_block->sdnxo = ((unsigned long) &vcpu->run->s.regs.sdnx) 2481 vcpu->arch.sie_block->sdnxo = ((unsigned long) &vcpu->run->s.regs.sdnx)
2475 | SDNXC; 2482 | SDNXC;
2476 vcpu->arch.sie_block->riccbd = (unsigned long) &vcpu->run->s.regs.riccb; 2483 vcpu->arch.sie_block->riccbd = (unsigned long) &vcpu->run->s.regs.riccb;
@@ -2523,6 +2530,7 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
2523 2530
2524 vcpu->arch.sie_block->icpua = id; 2531 vcpu->arch.sie_block->icpua = id;
2525 spin_lock_init(&vcpu->arch.local_int.lock); 2532 spin_lock_init(&vcpu->arch.local_int.lock);
2533 vcpu->arch.sie_block->gd = (u32)(u64)kvm->arch.gisa;
2526 seqcount_init(&vcpu->arch.cputm_seqcount); 2534 seqcount_init(&vcpu->arch.cputm_seqcount);
2527 2535
2528 rc = kvm_vcpu_init(vcpu, kvm, id); 2536 rc = kvm_vcpu_init(vcpu, kvm, id);
diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h
index f110fa96807e..bd31b37b0e6f 100644
--- a/arch/s390/kvm/kvm-s390.h
+++ b/arch/s390/kvm/kvm-s390.h
@@ -382,6 +382,9 @@ int kvm_s390_set_irq_state(struct kvm_vcpu *vcpu,
382 void __user *buf, int len); 382 void __user *buf, int len);
383int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu, 383int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu,
384 __u8 __user *buf, int len); 384 __u8 __user *buf, int len);
385void kvm_s390_gisa_init(struct kvm *kvm);
386void kvm_s390_gisa_clear(struct kvm *kvm);
387void kvm_s390_gisa_destroy(struct kvm *kvm);
385 388
386/* implemented in guestdbg.c */ 389/* implemented in guestdbg.c */
387void kvm_s390_backup_guest_per_regs(struct kvm_vcpu *vcpu); 390void kvm_s390_backup_guest_per_regs(struct kvm_vcpu *vcpu);