KVM: async_pf: Async page fault support on s390
[pandora-kernel.git] / arch / s390 / kvm / sigp.c
index bec398c..fe9442d 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * handling interprocessor communication
  *
- * Copyright IBM Corp. 2008, 2009
+ * Copyright IBM Corp. 2008, 2013
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License (version 2 only)
@@ -89,6 +89,37 @@ unlock:
        return rc;
 }
 
+static int __sigp_conditional_emergency(struct kvm_vcpu *vcpu, u16 cpu_addr,
+                                       u16 asn, u64 *reg)
+{
+       struct kvm_vcpu *dst_vcpu = NULL;
+       const u64 psw_int_mask = PSW_MASK_IO | PSW_MASK_EXT;
+       u16 p_asn, s_asn;
+       psw_t *psw;
+       u32 flags;
+
+       if (cpu_addr < KVM_MAX_VCPUS)
+               dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
+       if (!dst_vcpu)
+               return SIGP_CC_NOT_OPERATIONAL;
+       flags = atomic_read(&dst_vcpu->arch.sie_block->cpuflags);
+       psw = &dst_vcpu->arch.sie_block->gpsw;
+       p_asn = dst_vcpu->arch.sie_block->gcr[4] & 0xffff;  /* Primary ASN */
+       s_asn = dst_vcpu->arch.sie_block->gcr[3] & 0xffff;  /* Secondary ASN */
+
+       /* Deliver the emergency signal? */
+       if (!(flags & CPUSTAT_STOPPED)
+           || (psw->mask & psw_int_mask) != psw_int_mask
+           || ((flags & CPUSTAT_WAIT) && psw->addr != 0)
+           || (!(flags & CPUSTAT_WAIT) && (asn == p_asn || asn == s_asn))) {
+               return __sigp_emergency(vcpu, cpu_addr);
+       } else {
+               *reg &= 0xffffffff00000000UL;
+               *reg |= SIGP_STATUS_INCORRECT_STATE;
+               return SIGP_CC_STATUS_STORED;
+       }
+}
+
 static int __sigp_external_call(struct kvm_vcpu *vcpu, u16 cpu_addr)
 {
        struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
@@ -130,6 +161,7 @@ unlock:
 static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action)
 {
        struct kvm_s390_interrupt_info *inti;
+       int rc = SIGP_CC_ORDER_CODE_ACCEPTED;
 
        inti = kzalloc(sizeof(*inti), GFP_ATOMIC);
        if (!inti)
@@ -139,6 +171,8 @@ static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action)
        spin_lock_bh(&li->lock);
        if ((atomic_read(li->cpuflags) & CPUSTAT_STOPPED)) {
                kfree(inti);
+               if ((action & ACTION_STORE_ON_STOP) != 0)
+                       rc = -ESHUTDOWN;
                goto out;
        }
        list_add_tail(&inti->list, &li->list);
@@ -150,7 +184,7 @@ static int __inject_sigp_stop(struct kvm_s390_local_interrupt *li, int action)
 out:
        spin_unlock_bh(&li->lock);
 
-       return SIGP_CC_ORDER_CODE_ACCEPTED;
+       return rc;
 }
 
 static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int action)
@@ -174,18 +208,24 @@ static int __sigp_stop(struct kvm_vcpu *vcpu, u16 cpu_addr, int action)
 unlock:
        spin_unlock(&fi->lock);
        VCPU_EVENT(vcpu, 4, "sent sigp stop to cpu %x", cpu_addr);
-       return rc;
-}
 
-int kvm_s390_inject_sigp_stop(struct kvm_vcpu *vcpu, int action)
-{
-       struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
-       return __inject_sigp_stop(li, action);
+       if ((action & ACTION_STORE_ON_STOP) != 0 && rc == -ESHUTDOWN) {
+               /* If the CPU has already been stopped, we still have
+                * to save the status when doing stop-and-store. This
+                * has to be done after unlocking all spinlocks. */
+               struct kvm_vcpu *dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_addr);
+               rc = kvm_s390_store_status_unloaded(dst_vcpu,
+                                               KVM_S390_STORE_STATUS_NOADDR);
+       }
+
+       return rc;
 }
 
 static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter)
 {
        int rc;
+       unsigned int i;
+       struct kvm_vcpu *v;
 
        switch (parameter & 0xff) {
        case 0:
@@ -193,6 +233,11 @@ static int __sigp_set_arch(struct kvm_vcpu *vcpu, u32 parameter)
                break;
        case 1:
        case 2:
+               kvm_for_each_vcpu(i, v, vcpu->kvm) {
+                       v->arch.pfault_token = KVM_S390_PFAULT_TOKEN_INVALID;
+                       kvm_clear_async_pf_completion_queue(v);
+               }
+
                rc = SIGP_CC_ORDER_CODE_ACCEPTED;
                break;
        default:
@@ -262,6 +307,37 @@ out_fi:
        return rc;
 }
 
+static int __sigp_store_status_at_addr(struct kvm_vcpu *vcpu, u16 cpu_id,
+                                       u32 addr, u64 *reg)
+{
+       struct kvm_vcpu *dst_vcpu = NULL;
+       int flags;
+       int rc;
+
+       if (cpu_id < KVM_MAX_VCPUS)
+               dst_vcpu = kvm_get_vcpu(vcpu->kvm, cpu_id);
+       if (!dst_vcpu)
+               return SIGP_CC_NOT_OPERATIONAL;
+
+       spin_lock_bh(&dst_vcpu->arch.local_int.lock);
+       flags = atomic_read(dst_vcpu->arch.local_int.cpuflags);
+       spin_unlock_bh(&dst_vcpu->arch.local_int.lock);
+       if (!(flags & CPUSTAT_STOPPED)) {
+               *reg &= 0xffffffff00000000UL;
+               *reg |= SIGP_STATUS_INCORRECT_STATE;
+               return SIGP_CC_STATUS_STORED;
+       }
+
+       addr &= 0x7ffffe00;
+       rc = kvm_s390_store_status_unloaded(dst_vcpu, addr);
+       if (rc == -EFAULT) {
+               *reg &= 0xffffffff00000000UL;
+               *reg |= SIGP_STATUS_INVALID_PARAMETER;
+               rc = SIGP_CC_STATUS_STORED;
+       }
+       return rc;
+}
+
 static int __sigp_sense_running(struct kvm_vcpu *vcpu, u16 cpu_addr,
                                u64 *reg)
 {
@@ -294,7 +370,8 @@ static int __sigp_sense_running(struct kvm_vcpu *vcpu, u16 cpu_addr,
        return rc;
 }
 
-static int __sigp_restart(struct kvm_vcpu *vcpu, u16 cpu_addr)
+/* Test whether the destination CPU is available and not busy */
+static int sigp_check_callable(struct kvm_vcpu *vcpu, u16 cpu_addr)
 {
        struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
        struct kvm_s390_local_interrupt *li;
@@ -313,9 +390,6 @@ static int __sigp_restart(struct kvm_vcpu *vcpu, u16 cpu_addr)
        spin_lock_bh(&li->lock);
        if (li->action_bits & ACTION_STOP_ON_STOP)
                rc = SIGP_CC_BUSY;
-       else
-               VCPU_EVENT(vcpu, 4, "sigp restart %x to handle userspace",
-                       cpu_addr);
        spin_unlock_bh(&li->lock);
 out:
        spin_unlock(&fi->lock);
@@ -366,6 +440,10 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu)
                rc = __sigp_stop(vcpu, cpu_addr, ACTION_STORE_ON_STOP |
                                                 ACTION_STOP_ON_STOP);
                break;
+       case SIGP_STORE_STATUS_AT_ADDRESS:
+               rc = __sigp_store_status_at_addr(vcpu, cpu_addr, parameter,
+                                                &vcpu->run->s.regs.gprs[r1]);
+               break;
        case SIGP_SET_ARCHITECTURE:
                vcpu->stat.instruction_sigp_arch++;
                rc = __sigp_set_arch(vcpu, parameter);
@@ -375,17 +453,31 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu)
                rc = __sigp_set_prefix(vcpu, cpu_addr, parameter,
                                       &vcpu->run->s.regs.gprs[r1]);
                break;
+       case SIGP_COND_EMERGENCY_SIGNAL:
+               rc = __sigp_conditional_emergency(vcpu, cpu_addr, parameter,
+                                                 &vcpu->run->s.regs.gprs[r1]);
+               break;
        case SIGP_SENSE_RUNNING:
                vcpu->stat.instruction_sigp_sense_running++;
                rc = __sigp_sense_running(vcpu, cpu_addr,
                                          &vcpu->run->s.regs.gprs[r1]);
                break;
+       case SIGP_START:
+               rc = sigp_check_callable(vcpu, cpu_addr);
+               if (rc == SIGP_CC_ORDER_CODE_ACCEPTED)
+                       rc = -EOPNOTSUPP;    /* Handle START in user space */
+               break;
        case SIGP_RESTART:
                vcpu->stat.instruction_sigp_restart++;
-               rc = __sigp_restart(vcpu, cpu_addr);
-               if (rc == SIGP_CC_BUSY)
-                       break;
-               /* user space must know about restart */
+               rc = sigp_check_callable(vcpu, cpu_addr);
+               if (rc == SIGP_CC_ORDER_CODE_ACCEPTED) {
+                       VCPU_EVENT(vcpu, 4,
+                                  "sigp restart %x to handle userspace",
+                                  cpu_addr);
+                       /* user space must know about restart */
+                       rc = -EOPNOTSUPP;
+               }
+               break;
        default:
                return -EOPNOTSUPP;
        }
@@ -393,7 +485,6 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu)
        if (rc < 0)
                return rc;
 
-       vcpu->arch.sie_block->gpsw.mask &= ~(3ul << 44);
-       vcpu->arch.sie_block->gpsw.mask |= (rc & 3ul) << 44;
+       kvm_s390_set_psw_cc(vcpu, rc);
        return 0;
 }