KVM: s390: Add support for channel I/O instructions.
authorCornelia Huck <cornelia.huck@de.ibm.com>
Thu, 20 Dec 2012 14:32:12 +0000 (15:32 +0100)
committerMarcelo Tosatti <mtosatti@redhat.com>
Mon, 7 Jan 2013 21:53:43 +0000 (19:53 -0200)
Add a new capability, KVM_CAP_S390_CSS_SUPPORT, which will pass
intercepts for channel I/O instructions to userspace. Only I/O
instructions interacting with I/O interrupts need to be handled
in-kernel:

- TEST PENDING INTERRUPTION (tpi) dequeues and stores pending
  interrupts entirely in-kernel.
- TEST SUBCHANNEL (tsch) dequeues pending interrupts in-kernel
  and exits via KVM_EXIT_S390_TSCH to userspace for subchannel-
  related processing.

Reviewed-by: Marcelo Tosatti <mtosatti@redhat.com>
Reviewed-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Documentation/virtual/kvm/api.txt
arch/s390/include/asm/kvm_host.h
arch/s390/kvm/intercept.c
arch/s390/kvm/interrupt.c
arch/s390/kvm/kvm-s390.c
arch/s390/kvm/kvm-s390.h
arch/s390/kvm/priv.c
arch/s390/kvm/trace-s390.h
include/trace/events/kvm.h
include/uapi/linux/kvm.h

index 73bd159c5559027219bc3c355c2b9c768982075e..f2d6391178b97e6b1e772986bb0519fd0ed3354a 100644 (file)
@@ -2350,6 +2350,22 @@ The possible hypercalls are defined in the Power Architecture Platform
 Requirements (PAPR) document available from www.power.org (free
 developer registration required to access it).
 
+               /* KVM_EXIT_S390_TSCH */
+               struct {
+                       __u16 subchannel_id;
+                       __u16 subchannel_nr;
+                       __u32 io_int_parm;
+                       __u32 io_int_word;
+                       __u32 ipb;
+                       __u8 dequeued;
+               } s390_tsch;
+
+s390 specific. This exit occurs when KVM_CAP_S390_CSS_SUPPORT has been enabled
+and TEST SUBCHANNEL was intercepted. If dequeued is set, a pending I/O
+interrupt for the target subchannel has been dequeued and subchannel_id,
+subchannel_nr, io_int_parm and io_int_word contain the parameters for that
+interrupt. ipb is needed for instruction parameter decoding.
+
                /* Fix the size of the union. */
                char padding[256];
        };
@@ -2471,3 +2487,17 @@ For mmu types KVM_MMU_FSL_BOOKE_NOHV and KVM_MMU_FSL_BOOKE_HV:
    where "num_sets" is the tlb_sizes[] value divided by the tlb_ways[] value.
  - The tsize field of mas1 shall be set to 4K on TLB0, even though the
    hardware ignores this value for TLB0.
+
+6.4 KVM_CAP_S390_CSS_SUPPORT
+
+Architectures: s390
+Parameters: none
+Returns: 0 on success; -1 on error
+
+This capability enables support for handling of channel I/O instructions.
+
+TEST PENDING INTERRUPTION and the interrupt portion of TEST SUBCHANNEL are
+handled in-kernel, while the other I/O instructions are passed to userspace.
+
+When this capability is enabled, KVM_EXIT_S390_TSCH will occur on TEST
+SUBCHANNEL intercepts.
index 29363d155cd5f0ef57fff6a3ecfa05435cf67244..16bd5d169cdb4779c975cdd379160ff3ef6d637c 100644 (file)
@@ -262,6 +262,7 @@ struct kvm_arch{
        debug_info_t *dbf;
        struct kvm_s390_float_interrupt float_int;
        struct gmap *gmap;
+       int css_support;
 };
 
 extern int sie64a(struct kvm_s390_sie_block *, u64 *);
index 71af87dbb42c045227e0d2feeecdb27af40af1a8..f26ff1e31bdb6415479444a4436328608b8bded0 100644 (file)
@@ -264,6 +264,7 @@ static const intercept_handler_t intercept_funcs[] = {
        [0x0C >> 2] = handle_instruction_and_prog,
        [0x10 >> 2] = handle_noop,
        [0x14 >> 2] = handle_noop,
+       [0x18 >> 2] = handle_noop,
        [0x1C >> 2] = kvm_s390_handle_wait,
        [0x20 >> 2] = handle_validity,
        [0x28 >> 2] = handle_stop,
index b3b4748485eefa072127b0ccc5d92e2dab6fb3a2..9a128357fd1569a3256750d197f476d0af22598f 100644 (file)
@@ -709,6 +709,43 @@ int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code)
        return 0;
 }
 
+struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm,
+                                                   u64 cr6, u64 schid)
+{
+       struct kvm_s390_float_interrupt *fi;
+       struct kvm_s390_interrupt_info *inti, *iter;
+
+       if ((!schid && !cr6) || (schid && cr6))
+               return NULL;
+       mutex_lock(&kvm->lock);
+       fi = &kvm->arch.float_int;
+       spin_lock(&fi->lock);
+       inti = NULL;
+       list_for_each_entry(iter, &fi->list, list) {
+               if (!is_ioint(iter->type))
+                       continue;
+               if (cr6 && ((cr6 & iter->io.io_int_word) == 0))
+                       continue;
+               if (schid) {
+                       if (((schid & 0x00000000ffff0000) >> 16) !=
+                           iter->io.subchannel_id)
+                               continue;
+                       if ((schid & 0x000000000000ffff) !=
+                           iter->io.subchannel_nr)
+                               continue;
+               }
+               inti = iter;
+               break;
+       }
+       if (inti)
+               list_del_init(&inti->list);
+       if (list_empty(&fi->list))
+               atomic_set(&fi->active, 0);
+       spin_unlock(&fi->lock);
+       mutex_unlock(&kvm->lock);
+       return inti;
+}
+
 int kvm_s390_inject_vm(struct kvm *kvm,
                       struct kvm_s390_interrupt *s390int)
 {
index 5ff26033825c031883d24cf9cc7d071145616891..5b01f0953900c87c858c039d1d20b7ae9dffb7e4 100644 (file)
@@ -141,6 +141,7 @@ int kvm_dev_ioctl_check_extension(long ext)
        case KVM_CAP_SYNC_REGS:
        case KVM_CAP_ONE_REG:
        case KVM_CAP_ENABLE_CAP:
+       case KVM_CAP_S390_CSS_SUPPORT:
                r = 1;
                break;
        case KVM_CAP_NR_VCPUS:
@@ -235,6 +236,9 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
                if (!kvm->arch.gmap)
                        goto out_nogmap;
        }
+
+       kvm->arch.css_support = 0;
+
        return 0;
 out_nogmap:
        debug_unregister(kvm->arch.dbf);
@@ -658,6 +662,7 @@ rerun_vcpu:
        case KVM_EXIT_INTR:
        case KVM_EXIT_S390_RESET:
        case KVM_EXIT_S390_UCONTROL:
+       case KVM_EXIT_S390_TSCH:
                break;
        default:
                BUG();
@@ -818,6 +823,13 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu,
                return -EINVAL;
 
        switch (cap->cap) {
+       case KVM_CAP_S390_CSS_SUPPORT:
+               if (!vcpu->kvm->arch.css_support) {
+                       vcpu->kvm->arch.css_support = 1;
+                       trace_kvm_s390_enable_css(vcpu->kvm);
+               }
+               r = 0;
+               break;
        default:
                r = -EINVAL;
                break;
index 211b340385a7f5de2ab4d46b1a1a797feff97679..3e05deff21b6be97d3e9551b022f0478f0ab5887 100644 (file)
@@ -113,6 +113,8 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
                struct kvm_s390_interrupt *s390int);
 int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code);
 int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action);
+struct kvm_s390_interrupt_info *kvm_s390_get_io_int(struct kvm *kvm,
+                                                   u64 cr6, u64 schid);
 
 /* implemented in priv.c */
 int kvm_s390_handle_b2(struct kvm_vcpu *vcpu);
index 8ad776f8785660f9edad604feb8dba598d18de44..0ef9894606e519874fb531bb890b4d36badb75aa 100644 (file)
@@ -127,15 +127,98 @@ static int handle_skey(struct kvm_vcpu *vcpu)
        return 0;
 }
 
-static int handle_io_inst(struct kvm_vcpu *vcpu)
+static int handle_tpi(struct kvm_vcpu *vcpu)
 {
-       VCPU_EVENT(vcpu, 4, "%s", "I/O instruction");
-       /* condition code 3 */
+       u64 addr;
+       struct kvm_s390_interrupt_info *inti;
+       int cc;
+
+       addr = kvm_s390_get_base_disp_s(vcpu);
+
+       inti = kvm_s390_get_io_int(vcpu->kvm, vcpu->run->s.regs.crs[6], 0);
+       if (inti) {
+               if (addr) {
+                       /*
+                        * Store the two-word I/O interruption code into the
+                        * provided area.
+                        */
+                       put_guest_u16(vcpu, addr, inti->io.subchannel_id);
+                       put_guest_u16(vcpu, addr + 2, inti->io.subchannel_nr);
+                       put_guest_u32(vcpu, addr + 4, inti->io.io_int_parm);
+               } else {
+                       /*
+                        * Store the three-word I/O interruption code into
+                        * the appropriate lowcore area.
+                        */
+                       put_guest_u16(vcpu, 184, inti->io.subchannel_id);
+                       put_guest_u16(vcpu, 186, inti->io.subchannel_nr);
+                       put_guest_u32(vcpu, 188, inti->io.io_int_parm);
+                       put_guest_u32(vcpu, 192, inti->io.io_int_word);
+               }
+               cc = 1;
+       } else
+               cc = 0;
+       kfree(inti);
+       /* Set condition code and we're done. */
        vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
-       vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44;
+       vcpu->arch.sie_block->gpsw.mask |= (cc & 3ul) << 44;
        return 0;
 }
 
+static int handle_tsch(struct kvm_vcpu *vcpu)
+{
+       struct kvm_s390_interrupt_info *inti;
+
+       inti = kvm_s390_get_io_int(vcpu->kvm, 0,
+                                  vcpu->run->s.regs.gprs[1]);
+
+       /*
+        * Prepare exit to userspace.
+        * We indicate whether we dequeued a pending I/O interrupt
+        * so that userspace can re-inject it if the instruction gets
+        * a program check. While this may re-order the pending I/O
+        * interrupts, this is no problem since the priority is kept
+        * intact.
+        */
+       vcpu->run->exit_reason = KVM_EXIT_S390_TSCH;
+       vcpu->run->s390_tsch.dequeued = !!inti;
+       if (inti) {
+               vcpu->run->s390_tsch.subchannel_id = inti->io.subchannel_id;
+               vcpu->run->s390_tsch.subchannel_nr = inti->io.subchannel_nr;
+               vcpu->run->s390_tsch.io_int_parm = inti->io.io_int_parm;
+               vcpu->run->s390_tsch.io_int_word = inti->io.io_int_word;
+       }
+       vcpu->run->s390_tsch.ipb = vcpu->arch.sie_block->ipb;
+       kfree(inti);
+       return -EREMOTE;
+}
+
+static int handle_io_inst(struct kvm_vcpu *vcpu)
+{
+       VCPU_EVENT(vcpu, 4, "%s", "I/O instruction");
+
+       if (vcpu->kvm->arch.css_support) {
+               /*
+                * Most I/O instructions will be handled by userspace.
+                * Exceptions are tpi and the interrupt portion of tsch.
+                */
+               if (vcpu->arch.sie_block->ipa == 0xb236)
+                       return handle_tpi(vcpu);
+               if (vcpu->arch.sie_block->ipa == 0xb235)
+                       return handle_tsch(vcpu);
+               /* Handle in userspace. */
+               return -EOPNOTSUPP;
+       } else {
+               /*
+                * Set condition code 3 to stop the guest from issueing channel
+                * I/O instructions.
+                */
+               vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
+               vcpu->arch.sie_block->gpsw.mask |= (3 & 3ul) << 44;
+               return 0;
+       }
+}
+
 static int handle_stfl(struct kvm_vcpu *vcpu)
 {
        unsigned int facility_list;
index 95fbc1ab88dcbcdab01bb5a77dcfd0ed6b34488c..13f30f58a2df7d94ba8f91d8113f49c8d7938d27 100644 (file)
@@ -204,6 +204,26 @@ TRACE_EVENT(kvm_s390_stop_request,
        );
 
 
+/*
+ * Trace point for enabling channel I/O instruction support.
+ */
+TRACE_EVENT(kvm_s390_enable_css,
+           TP_PROTO(void *kvm),
+           TP_ARGS(kvm),
+
+           TP_STRUCT__entry(
+                   __field(void *, kvm)
+                   ),
+
+           TP_fast_assign(
+                   __entry->kvm = kvm;
+                   ),
+
+           TP_printk("enabling channel I/O support (kvm @ %p)\n",
+                     __entry->kvm)
+       );
+
+
 #endif /* _TRACE_KVMS390_H */
 
 /* This part must be outside protection */
index 7ef9e759f49907ce0ebbf2ab345ec571a0b023a0..a23f47c884cfc5f41e28f74e23329022e79aea4b 100644 (file)
@@ -14,7 +14,7 @@
        ERSN(SHUTDOWN), ERSN(FAIL_ENTRY), ERSN(INTR), ERSN(SET_TPR),    \
        ERSN(TPR_ACCESS), ERSN(S390_SIEIC), ERSN(S390_RESET), ERSN(DCR),\
        ERSN(NMI), ERSN(INTERNAL_ERROR), ERSN(OSI), ERSN(PAPR_HCALL),   \
-       ERSN(S390_UCONTROL)
+       ERSN(S390_UCONTROL), ERSN(S390_TSCH)
 
 TRACE_EVENT(kvm_userspace_exit,
            TP_PROTO(__u32 reason, int errno),
index 80bb3b8011164207c0512f7f0ff93c547d97e7bb..8bb0bf83afc557a84ecda9932b385df27a024d1f 100644 (file)
@@ -168,6 +168,7 @@ struct kvm_pit_config {
 #define KVM_EXIT_PAPR_HCALL      19
 #define KVM_EXIT_S390_UCONTROL   20
 #define KVM_EXIT_WATCHDOG         21
+#define KVM_EXIT_S390_TSCH        22
 
 /* For KVM_EXIT_INTERNAL_ERROR */
 /* Emulate instruction failed. */
@@ -285,6 +286,15 @@ struct kvm_run {
                        __u64 ret;
                        __u64 args[9];
                } papr_hcall;
+               /* KVM_EXIT_S390_TSCH */
+               struct {
+                       __u16 subchannel_id;
+                       __u16 subchannel_nr;
+                       __u32 io_int_parm;
+                       __u32 io_int_word;
+                       __u32 ipb;
+                       __u8 dequeued;
+               } s390_tsch;
                /* Fix the size of the union. */
                char padding[256];
        };
@@ -645,6 +655,7 @@ struct kvm_ppc_smmu_info {
 #define KVM_CAP_IRQFD_RESAMPLE 82
 #define KVM_CAP_PPC_BOOKE_WATCHDOG 83
 #define KVM_CAP_PPC_HTAB_FD 84
+#define KVM_CAP_S390_CSS_SUPPORT 85
 
 #ifdef KVM_CAP_IRQ_ROUTING