KVM: s390: implement sigp external call
authorChristian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
Tue, 18 Oct 2011 10:27:15 +0000 (12:27 +0200)
committerAvi Kivity <avi@redhat.com>
Sun, 30 Oct 2011 10:24:05 +0000 (12:24 +0200)
Implement sigp external call, which might be required for guests that
issue an external call instead of an emergency signal for IPI.

This fixes an issue with "KVM: unknown SIGP: 0x02" when booting
such an SMP guest.

Signed-off-by: Christian Ehrhardt <ehrhardt@linux.vnet.ibm.com>
Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
arch/s390/include/asm/kvm_host.h
arch/s390/kvm/interrupt.c
arch/s390/kvm/kvm-s390.c
arch/s390/kvm/sigp.c
include/linux/kvm.h

index 00ff00dfb24ce34917772a231d9607a56e15e9e9..1ca5de07ac36eec92068dc4baa62ffd669cf1c1e 100644 (file)
@@ -119,6 +119,7 @@ struct kvm_vcpu_stat {
        u32 instruction_lctlg;
        u32 exit_program_interruption;
        u32 exit_instr_and_program;
+       u32 deliver_external_call;
        u32 deliver_emergency_signal;
        u32 deliver_service_signal;
        u32 deliver_virtio_interrupt;
@@ -138,6 +139,7 @@ struct kvm_vcpu_stat {
        u32 instruction_stfl;
        u32 instruction_tprot;
        u32 instruction_sigp_sense;
+       u32 instruction_sigp_external_call;
        u32 instruction_sigp_emergency;
        u32 instruction_sigp_stop;
        u32 instruction_sigp_arch;
@@ -174,6 +176,10 @@ struct kvm_s390_prefix_info {
        __u32 address;
 };
 
+struct kvm_s390_extcall_info {
+       __u16 code;
+};
+
 struct kvm_s390_emerg_info {
        __u16 code;
 };
@@ -186,6 +192,7 @@ struct kvm_s390_interrupt_info {
                struct kvm_s390_ext_info ext;
                struct kvm_s390_pgm_info pgm;
                struct kvm_s390_emerg_info emerg;
+               struct kvm_s390_extcall_info extcall;
                struct kvm_s390_prefix_info prefix;
        };
 };
index c9aeb4b4d0b84e67c9cc5ae1bf6648d31ea7c6dd..87c16705b381396f796e7fbacbb079d62cab8d9d 100644 (file)
@@ -38,6 +38,11 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu,
                                      struct kvm_s390_interrupt_info *inti)
 {
        switch (inti->type) {
+       case KVM_S390_INT_EXTERNAL_CALL:
+               if (psw_extint_disabled(vcpu))
+                       return 0;
+               if (vcpu->arch.sie_block->gcr[0] & 0x2000ul)
+                       return 1;
        case KVM_S390_INT_EMERGENCY:
                if (psw_extint_disabled(vcpu))
                        return 0;
@@ -98,6 +103,7 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu,
                                      struct kvm_s390_interrupt_info *inti)
 {
        switch (inti->type) {
+       case KVM_S390_INT_EXTERNAL_CALL:
        case KVM_S390_INT_EMERGENCY:
        case KVM_S390_INT_SERVICE:
        case KVM_S390_INT_VIRTIO:
@@ -143,6 +149,28 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
                        exception = 1;
                break;
 
+       case KVM_S390_INT_EXTERNAL_CALL:
+               VCPU_EVENT(vcpu, 4, "%s", "interrupt: sigp ext call");
+               vcpu->stat.deliver_external_call++;
+               rc = put_guest_u16(vcpu, __LC_EXT_INT_CODE, 0x1202);
+               if (rc == -EFAULT)
+                       exception = 1;
+
+               rc = put_guest_u16(vcpu, __LC_CPU_ADDRESS, inti->extcall.code);
+               if (rc == -EFAULT)
+                       exception = 1;
+
+               rc = copy_to_guest(vcpu, __LC_EXT_OLD_PSW,
+                        &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
+               if (rc == -EFAULT)
+                       exception = 1;
+
+               rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
+                       __LC_EXT_NEW_PSW, sizeof(psw_t));
+               if (rc == -EFAULT)
+                       exception = 1;
+               break;
+
        case KVM_S390_INT_SERVICE:
                VCPU_EVENT(vcpu, 4, "interrupt: sclp parm:%x",
                           inti->ext.ext_params);
@@ -522,6 +550,7 @@ int kvm_s390_inject_vm(struct kvm *kvm,
                break;
        case KVM_S390_PROGRAM_INT:
        case KVM_S390_SIGP_STOP:
+       case KVM_S390_INT_EXTERNAL_CALL:
        case KVM_S390_INT_EMERGENCY:
        default:
                kfree(inti);
@@ -581,6 +610,7 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
                break;
        case KVM_S390_SIGP_STOP:
        case KVM_S390_RESTART:
+       case KVM_S390_INT_EXTERNAL_CALL:
        case KVM_S390_INT_EMERGENCY:
                VCPU_EVENT(vcpu, 3, "inject: type %x", s390int->type);
                inti->type = s390int->type;
index 29635678b5ece0a550c198aa227b56ed4a3d6d4b..9610ba41b974310df032813e94bd1f870e18c1a0 100644 (file)
@@ -46,6 +46,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
        { "instruction_lctlg", VCPU_STAT(instruction_lctlg) },
        { "instruction_lctl", VCPU_STAT(instruction_lctl) },
        { "deliver_emergency_signal", VCPU_STAT(deliver_emergency_signal) },
+       { "deliver_external_call", VCPU_STAT(deliver_external_call) },
        { "deliver_service_signal", VCPU_STAT(deliver_service_signal) },
        { "deliver_virtio_interrupt", VCPU_STAT(deliver_virtio_interrupt) },
        { "deliver_stop_signal", VCPU_STAT(deliver_stop_signal) },
@@ -64,6 +65,7 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
        { "instruction_stfl", VCPU_STAT(instruction_stfl) },
        { "instruction_tprot", VCPU_STAT(instruction_tprot) },
        { "instruction_sigp_sense", VCPU_STAT(instruction_sigp_sense) },
+       { "instruction_sigp_external_call", VCPU_STAT(instruction_sigp_external_call) },
        { "instruction_sigp_emergency", VCPU_STAT(instruction_sigp_emergency) },
        { "instruction_sigp_stop", VCPU_STAT(instruction_sigp_stop) },
        { "instruction_sigp_set_arch", VCPU_STAT(instruction_sigp_arch) },
index d6a50c1fb2e68dbb9f45654ab6fab1af6f760585..f815118835f3c3221932c5e01c86847cc1ec9ba8 100644 (file)
@@ -87,6 +87,7 @@ static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr)
                return -ENOMEM;
 
        inti->type = KVM_S390_INT_EMERGENCY;
+       inti->emerg.code = vcpu->vcpu_id;
 
        spin_lock(&fi->lock);
        li = fi->local_int[cpu_addr];
@@ -103,9 +104,47 @@ static int __sigp_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr)
                wake_up_interruptible(&li->wq);
        spin_unlock_bh(&li->lock);
        rc = 0; /* order accepted */
+       VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr);
+unlock:
+       spin_unlock(&fi->lock);
+       return rc;
+}
+
+static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr)
+{
+       struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
+       struct kvm_s390_local_interrupt *li;
+       struct kvm_s390_interrupt_info *inti;
+       int rc;
+
+       if (cpu_addr >= KVM_MAX_VCPUS)
+               return 3; /* not operational */
+
+       inti = kzalloc(sizeof(*inti), GFP_KERNEL);
+       if (!inti)
+               return -ENOMEM;
+
+       inti->type = KVM_S390_INT_EXTERNAL_CALL;
+       inti->extcall.code = vcpu->vcpu_id;
+
+       spin_lock(&fi->lock);
+       li = fi->local_int[cpu_addr];
+       if (li == NULL) {
+               rc = 3; /* not operational */
+               kfree(inti);
+               goto unlock;
+       }
+       spin_lock_bh(&li->lock);
+       list_add_tail(&inti->list, &li->list);
+       atomic_set(&li->active, 1);
+       atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags);
+       if (waitqueue_active(&li->wq))
+               wake_up_interruptible(&li->wq);
+       spin_unlock_bh(&li->lock);
+       rc = 0; /* order accepted */
+       VCPU_EVENT(vcpu, 4, "sent sigp ext call to cpu %x", cpu_addr);
 unlock:
        spin_unlock(&fi->lock);
-       VCPU_EVENT(vcpu, 4, "sent sigp emerg to cpu %x", cpu_addr);
        return rc;
 }
 
@@ -267,6 +306,10 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu)
                rc = __sigp_sense(vcpu, cpu_addr,
                                  &vcpu->arch.guest_gprs[r1]);
                break;
+       case SIGP_EXTERNAL_CALL:
+               vcpu->stat.instruction_sigp_external_call++;
+               rc = __sigp_external_call(vcpu, cpu_addr);
+               break;
        case SIGP_EMERGENCY:
                vcpu->stat.instruction_sigp_emergency++;
                rc = __sigp_emergency(vcpu, cpu_addr);
index 68840544006d1fe55250f217b8c83d692c2bfbf1..f47fcd30273dd2ce904b6ac0cf11fb963c04826c 100644 (file)
@@ -371,6 +371,7 @@ struct kvm_s390_psw {
 #define KVM_S390_INT_VIRTIO            0xffff2603u
 #define KVM_S390_INT_SERVICE           0xffff2401u
 #define KVM_S390_INT_EMERGENCY         0xffff1201u
+#define KVM_S390_INT_EXTERNAL_CALL     0xffff1202u
 
 struct kvm_s390_interrupt {
        __u32 type;