KVM: s390: interpretive execution of SIGP EXTERNAL CALL
authorDavid Hildenbrand <dahi@linux.vnet.ibm.com>
Fri, 21 Feb 2014 07:59:59 +0000 (08:59 +0100)
committerChristian Borntraeger <borntraeger@de.ibm.com>
Fri, 16 May 2014 12:57:28 +0000 (14:57 +0200)
If the sigp interpretation facility is installed, most SIGP EXTERNAL CALL
operations will be interpreted instead of intercepted. A partial execution
interception will occurr at the sending cpu only if the target cpu is in the
wait state ("W" bit in the cpuflags set). Instruction interception will only
happen in error cases (e.g. cpu addr invalid).

As a sending cpu might set the external call interrupt pending flags at the
target cpu at every point in time, we can't handle this kind of interrupt using
our kvm interrupt injection mechanism. The injection will be done automatically
by the SIE when preparing the start of the target cpu.

Signed-off-by: David Hildenbrand <dahi@linux.vnet.ibm.com>
Reviewed-by: Cornelia Huck <cornelia.huck@de.ibm.com>
CC: Thomas Huth <thuth@linux.vnet.ibm.com>
[Adopt external call injection to check for sigp interpretion]
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
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/sigp.c
arch/s390/kvm/trace.h

index 96b8a67ddaf8df0d6a12500f228764bf2e945b60..dc182a5b02b7055e707ef12e3fccf8859bf88a01 100644 (file)
 #define KVM_NR_IRQCHIPS 1
 #define KVM_IRQCHIP_NUM_PINS 4096
 
+#define SIGP_CTRL_C    0x00800000
+
 struct sca_entry {
-       atomic_t scn;
+       atomic_t ctrl;
        __u32   reserved;
        __u64   sda;
        __u64   reserved2[2];
index 147b87fefecdf57da9a4a56b7a77176fad53f8dd..a0b586c1913c18827b4a00e1b21f025d21c2515a 100644 (file)
@@ -272,6 +272,8 @@ static int handle_external_interrupt(struct kvm_vcpu *vcpu)
                irq.type = KVM_S390_INT_CPU_TIMER;
                break;
        case EXT_IRQ_EXTERNAL_CALL:
+               if (kvm_s390_si_ext_call_pending(vcpu))
+                       return 0;
                irq.type = KVM_S390_INT_EXTERNAL_CALL;
                irq.parm = vcpu->arch.sie_block->extcpuaddr;
                break;
@@ -323,6 +325,8 @@ static int handle_partial_execution(struct kvm_vcpu *vcpu)
 {
        if (vcpu->arch.sie_block->ipa == 0xb254)        /* MVPG */
                return handle_mvpg_pei(vcpu);
+       if (vcpu->arch.sie_block->ipa >> 8 == 0xae)     /* SIGP */
+               return kvm_s390_handle_sigp_pei(vcpu);
 
        return -EOPNOTSUPP;
 }
index 75cd3217cd5a9fdc20fb51acbc1043e57a3f4a4d..bf0d9bc15bcdc77cbaa579edd58214f901189c72 100644 (file)
@@ -148,9 +148,8 @@ static void __unset_cpu_idle(struct kvm_vcpu *vcpu)
 
 static void __reset_intercept_indicators(struct kvm_vcpu *vcpu)
 {
-       atomic_clear_mask(CPUSTAT_ECALL_PEND |
-               CPUSTAT_IO_INT | CPUSTAT_EXT_INT | CPUSTAT_STOP_INT,
-               &vcpu->arch.sie_block->cpuflags);
+       atomic_clear_mask(CPUSTAT_IO_INT | CPUSTAT_EXT_INT | CPUSTAT_STOP_INT,
+                         &vcpu->arch.sie_block->cpuflags);
        vcpu->arch.sie_block->lctl = 0x0000;
        vcpu->arch.sie_block->ictl &= ~(ICTL_LPSW | ICTL_STCTL | ICTL_PINT);
 
@@ -524,6 +523,20 @@ static void deliver_ckc_interrupt(struct kvm_vcpu *vcpu)
        }
 }
 
+/* Check whether SIGP interpretation facility has an external call pending */
+int kvm_s390_si_ext_call_pending(struct kvm_vcpu *vcpu)
+{
+       atomic_t *sigp_ctrl = &vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].ctrl;
+
+       if (!psw_extint_disabled(vcpu) &&
+           (vcpu->arch.sie_block->gcr[0] & 0x2000ul) &&
+           (atomic_read(sigp_ctrl) & SIGP_CTRL_C) &&
+           (atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_ECALL_PEND))
+               return 1;
+
+       return 0;
+}
+
 int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
 {
        struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
@@ -554,6 +567,9 @@ int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu)
        if (!rc && kvm_cpu_has_pending_timer(vcpu))
                rc = 1;
 
+       if (!rc && kvm_s390_si_ext_call_pending(vcpu))
+               rc = 1;
+
        return rc;
 }
 
@@ -610,7 +626,8 @@ no_timer:
        while (list_empty(&vcpu->arch.local_int.list) &&
                list_empty(&vcpu->arch.local_int.float_int->list) &&
                (!vcpu->arch.local_int.timer_due) &&
-               !signal_pending(current)) {
+               !signal_pending(current) &&
+               !kvm_s390_si_ext_call_pending(vcpu)) {
                set_current_state(TASK_INTERRUPTIBLE);
                spin_unlock_bh(&vcpu->arch.local_int.lock);
                spin_unlock(&vcpu->arch.local_int.float_int->lock);
@@ -667,6 +684,11 @@ void kvm_s390_clear_local_irqs(struct kvm_vcpu *vcpu)
        }
        atomic_set(&li->active, 0);
        spin_unlock_bh(&li->lock);
+
+       /* clear pending external calls set by sigp interpretation facility */
+       atomic_clear_mask(CPUSTAT_ECALL_PEND, &vcpu->arch.sie_block->cpuflags);
+       atomic_clear_mask(SIGP_CTRL_C,
+                         &vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].ctrl);
 }
 
 void kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu)
index d91feb2f03ea14435355a1de2d28b1ceecaa768e..2c243124a4e29be59ddaa2f28b57089bbb095bc5 100644 (file)
@@ -633,7 +633,7 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu)
                vcpu->arch.sie_block->ecb |= 0x10;
 
        vcpu->arch.sie_block->ecb2  = 8;
-       vcpu->arch.sie_block->eca   = 0xC1002000U;
+       vcpu->arch.sie_block->eca   = 0xD1002000U;
        if (sclp_has_siif())
                vcpu->arch.sie_block->eca |= 1;
        vcpu->arch.sie_block->fac   = (int) (long) vfacilities;
index e489945921acfb5a4fc0b8e11d44ba0e8da6dc34..87edfc919db98c21b2bb5cdd8282a8e6db201491 100644 (file)
@@ -154,6 +154,7 @@ int kvm_s390_handle_eb(struct kvm_vcpu *vcpu);
 
 /* implemented in sigp.c */
 int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu);
+int kvm_s390_handle_sigp_pei(struct kvm_vcpu *vcpu);
 
 /* implemented in kvm-s390.c */
 long kvm_arch_fault_in_page(struct kvm_vcpu *vcpu, gpa_t gpa, int writable);
@@ -214,6 +215,7 @@ static inline int kvm_s390_inject_prog_cond(struct kvm_vcpu *vcpu, int rc)
 int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu);
 int psw_extint_disabled(struct kvm_vcpu *vcpu);
 void kvm_s390_destroy_adapters(struct kvm *kvm);
+int kvm_s390_si_ext_call_pending(struct kvm_vcpu *vcpu);
 
 /* implemented in guestdbg.c */
 void kvm_s390_backup_guest_per_regs(struct kvm_vcpu *vcpu);
index c0b99e0f6b63a52bcf0d037da403319878c6817a..d0341d2e54b14349d430e8a31436340a0d44545e 100644 (file)
@@ -458,3 +458,38 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu)
        kvm_s390_set_psw_cc(vcpu, rc);
        return 0;
 }
+
+/*
+ * Handle SIGP partial execution interception.
+ *
+ * This interception will occur at the source cpu when a source cpu sends an
+ * external call to a target cpu and the target cpu has the WAIT bit set in
+ * its cpuflags. Interception will occurr after the interrupt indicator bits at
+ * the target cpu have been set. All error cases will lead to instruction
+ * interception, therefore nothing is to be checked or prepared.
+ */
+int kvm_s390_handle_sigp_pei(struct kvm_vcpu *vcpu)
+{
+       int r3 = vcpu->arch.sie_block->ipa & 0x000f;
+       u16 cpu_addr = vcpu->run->s.regs.gprs[r3];
+       struct kvm_vcpu *dest_vcpu;
+       u8 order_code = kvm_s390_get_base_disp_rs(vcpu);
+
+       trace_kvm_s390_handle_sigp_pei(vcpu, order_code, cpu_addr);
+
+       if (order_code == SIGP_EXTERNAL_CALL) {
+               dest_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
+               BUG_ON(dest_vcpu == NULL);
+
+               spin_lock_bh(&dest_vcpu->arch.local_int.lock);
+               if (waitqueue_active(&dest_vcpu->wq))
+                       wake_up_interruptible(&dest_vcpu->wq);
+               dest_vcpu->preempted = true;
+               spin_unlock_bh(&dest_vcpu->arch.local_int.lock);
+
+               kvm_s390_set_psw_cc(vcpu, SIGP_CC_ORDER_CODE_ACCEPTED);
+               return 0;
+       }
+
+       return -EOPNOTSUPP;
+}
index 579b42afab4083c1e6ee352e7230a3773c551f6f..916834d7a73a760da5b7fd9beba5c267ad437240 100644 (file)
@@ -239,6 +239,29 @@ TRACE_EVENT(kvm_s390_handle_sigp,
                           __entry->cpu_addr, __entry->parameter)
        );
 
+TRACE_EVENT(kvm_s390_handle_sigp_pei,
+           TP_PROTO(VCPU_PROTO_COMMON, __u8 order_code, __u16 cpu_addr),
+           TP_ARGS(VCPU_ARGS_COMMON, order_code, cpu_addr),
+
+           TP_STRUCT__entry(
+                   VCPU_FIELD_COMMON
+                   __field(__u8, order_code)
+                   __field(__u16, cpu_addr)
+                   ),
+
+           TP_fast_assign(
+                   VCPU_ASSIGN_COMMON
+                   __entry->order_code = order_code;
+                   __entry->cpu_addr = cpu_addr;
+                   ),
+
+           VCPU_TP_PRINTK("handle sigp pei order %02x (%s), cpu address %04x",
+                          __entry->order_code,
+                          __print_symbolic(__entry->order_code,
+                                           sigp_order_codes),
+                          __entry->cpu_addr)
+       );
+
 TRACE_EVENT(kvm_s390_handle_diag,
            TP_PROTO(VCPU_PROTO_COMMON, __u16 code),
            TP_ARGS(VCPU_ARGS_COMMON, code),