diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index ead52bfb4c1dff66c6a21411366eb122243b41a2..8f50f8ccced3d9637e7e7b457ceb36f8d0a760bd 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -719,6 +719,16 @@ int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu) return rc; } +static int __inject_prog_irq(struct kvm_vcpu *vcpu, + struct kvm_s390_interrupt_info *inti) +{ + struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; + + list_add(&inti->list, &li->list); + atomic_set(&li->active, 1); + return 0; +} + int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code) { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; @@ -746,6 +756,7 @@ int kvm_s390_inject_prog_irq(struct kvm_vcpu *vcpu, { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; struct kvm_s390_interrupt_info *inti; + int rc; inti = kzalloc(sizeof(*inti), GFP_KERNEL); if (!inti) @@ -759,10 +770,133 @@ int kvm_s390_inject_prog_irq(struct kvm_vcpu *vcpu, inti->type = KVM_S390_PROGRAM_INT; memcpy(&inti->pgm, pgm_info, sizeof(inti->pgm)); spin_lock(&li->lock); - list_add(&inti->list, &li->list); - atomic_set(&li->active, 1); + rc = __inject_prog_irq(vcpu, inti); BUG_ON(waitqueue_active(li->wq)); spin_unlock(&li->lock); + return rc; +} + +static int __inject_pfault_init(struct kvm_vcpu *vcpu, + struct kvm_s390_interrupt *s390int, + struct kvm_s390_interrupt_info *inti) +{ + struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; + + inti->ext.ext_params2 = s390int->parm64; + list_add_tail(&inti->list, &li->list); + atomic_set(&li->active, 1); + atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); + return 0; +} + +static int __inject_extcall(struct kvm_vcpu *vcpu, + struct kvm_s390_interrupt *s390int, + struct kvm_s390_interrupt_info *inti) +{ + struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; + + VCPU_EVENT(vcpu, 3, "inject: external call source-cpu:%u", + s390int->parm); + if (s390int->parm & 0xffff0000) + return -EINVAL; + inti->extcall.code = s390int->parm; + list_add_tail(&inti->list, &li->list); + atomic_set(&li->active, 1); + atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); + return 0; +} + +static int __inject_set_prefix(struct kvm_vcpu *vcpu, + struct kvm_s390_interrupt *s390int, + struct kvm_s390_interrupt_info *inti) +{ + struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; + + VCPU_EVENT(vcpu, 3, "inject: set prefix to %x (from user)", + s390int->parm); + inti->prefix.address = s390int->parm; + list_add_tail(&inti->list, &li->list); + atomic_set(&li->active, 1); + return 0; +} + +static int __inject_sigp_stop(struct kvm_vcpu *vcpu, + struct kvm_s390_interrupt *s390int, + struct kvm_s390_interrupt_info *inti) +{ + struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; + + list_add_tail(&inti->list, &li->list); + atomic_set(&li->active, 1); + li->action_bits |= ACTION_STOP_ON_STOP; + return 0; +} + +static int __inject_sigp_restart(struct kvm_vcpu *vcpu, + struct kvm_s390_interrupt *s390int, + struct kvm_s390_interrupt_info *inti) +{ + struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; + + VCPU_EVENT(vcpu, 3, "inject: type %x", s390int->type); + list_add_tail(&inti->list, &li->list); + atomic_set(&li->active, 1); + return 0; +} + +static int __inject_sigp_emergency(struct kvm_vcpu *vcpu, + struct kvm_s390_interrupt *s390int, + struct kvm_s390_interrupt_info *inti) +{ + struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; + + VCPU_EVENT(vcpu, 3, "inject: emergency %u\n", s390int->parm); + if (s390int->parm & 0xffff0000) + return -EINVAL; + inti->emerg.code = s390int->parm; + list_add_tail(&inti->list, &li->list); + atomic_set(&li->active, 1); + atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); + return 0; +} + +static int __inject_mchk(struct kvm_vcpu *vcpu, + struct kvm_s390_interrupt *s390int, + struct kvm_s390_interrupt_info *inti) +{ + struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; + + VCPU_EVENT(vcpu, 5, "inject: machine check parm64:%llx", + s390int->parm64); + inti->mchk.mcic = s390int->parm64; + list_add_tail(&inti->list, &li->list); + atomic_set(&li->active, 1); + return 0; +} + +static int __inject_ckc(struct kvm_vcpu *vcpu, + struct kvm_s390_interrupt *s390int, + struct kvm_s390_interrupt_info *inti) +{ + struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; + + VCPU_EVENT(vcpu, 3, "inject: type %x", s390int->type); + list_add_tail(&inti->list, &li->list); + atomic_set(&li->active, 1); + atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); + return 0; +} + +static int __inject_cpu_timer(struct kvm_vcpu *vcpu, + struct kvm_s390_interrupt *s390int, + struct kvm_s390_interrupt_info *inti) +{ + struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; + + VCPU_EVENT(vcpu, 3, "inject: type %x", s390int->type); + list_add_tail(&inti->list, &li->list); + atomic_set(&li->active, 1); + atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); return 0; } @@ -933,89 +1067,68 @@ void kvm_s390_reinject_io_int(struct kvm *kvm, int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, struct kvm_s390_interrupt *s390int) { - struct kvm_s390_local_interrupt *li; + struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; struct kvm_s390_interrupt_info *inti; + int rc; inti = kzalloc(sizeof(*inti), GFP_KERNEL); if (!inti) return -ENOMEM; - switch (s390int->type) { + inti->type = s390int->type; + + trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, s390int->type, + s390int->parm, 0, 2); + spin_lock(&li->lock); + switch (inti->type) { case KVM_S390_PROGRAM_INT: - if (s390int->parm & 0xffff0000) { - kfree(inti); - return -EINVAL; - } - inti->type = s390int->type; - inti->pgm.code = s390int->parm; VCPU_EVENT(vcpu, 3, "inject: program check %d (from user)", s390int->parm); + inti->pgm.code = s390int->parm; + if (s390int->parm & 0xffff0000) + rc = -EINVAL; + else + rc = __inject_prog_irq(vcpu, inti); break; case KVM_S390_SIGP_SET_PREFIX: - inti->prefix.address = s390int->parm; - inti->type = s390int->type; - VCPU_EVENT(vcpu, 3, "inject: set prefix to %x (from user)", - s390int->parm); + rc = __inject_set_prefix(vcpu, s390int, inti); break; case KVM_S390_SIGP_STOP: + rc = __inject_sigp_stop(vcpu, s390int, inti); + break; case KVM_S390_RESTART: + rc = __inject_sigp_restart(vcpu, s390int, inti); + break; case KVM_S390_INT_CLOCK_COMP: + rc = __inject_ckc(vcpu, s390int, inti); + break; case KVM_S390_INT_CPU_TIMER: - VCPU_EVENT(vcpu, 3, "inject: type %x", s390int->type); - inti->type = s390int->type; + rc = __inject_cpu_timer(vcpu, s390int, inti); break; case KVM_S390_INT_EXTERNAL_CALL: - if (s390int->parm & 0xffff0000) { - kfree(inti); - return -EINVAL; - } - VCPU_EVENT(vcpu, 3, "inject: external call source-cpu:%u", - s390int->parm); - inti->type = s390int->type; - inti->extcall.code = s390int->parm; + rc = __inject_extcall(vcpu, s390int, inti); break; case KVM_S390_INT_EMERGENCY: - if (s390int->parm & 0xffff0000) { - kfree(inti); - return -EINVAL; - } - VCPU_EVENT(vcpu, 3, "inject: emergency %u\n", s390int->parm); - inti->type = s390int->type; - inti->emerg.code = s390int->parm; + rc = __inject_sigp_emergency(vcpu, s390int, inti); break; case KVM_S390_MCHK: - VCPU_EVENT(vcpu, 5, "inject: machine check parm64:%llx", - s390int->parm64); - inti->type = s390int->type; - inti->mchk.mcic = s390int->parm64; + rc = __inject_mchk(vcpu, s390int, inti); break; case KVM_S390_INT_PFAULT_INIT: - inti->type = s390int->type; - inti->ext.ext_params2 = s390int->parm64; + rc = __inject_pfault_init(vcpu, s390int, inti); break; case KVM_S390_INT_VIRTIO: case KVM_S390_INT_SERVICE: case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: default: - kfree(inti); - return -EINVAL; + rc = -EINVAL; } - trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, s390int->type, s390int->parm, - s390int->parm64, 2); - - li = &vcpu->arch.local_int; - spin_lock(&li->lock); - if (inti->type == KVM_S390_PROGRAM_INT) - list_add(&inti->list, &li->list); - else - list_add_tail(&inti->list, &li->list); - atomic_set(&li->active, 1); - if (inti->type == KVM_S390_SIGP_STOP) - li->action_bits |= ACTION_STOP_ON_STOP; - atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); spin_unlock(&li->lock); - kvm_s390_vcpu_wakeup(vcpu); - return 0; + if (!rc) + kvm_s390_vcpu_wakeup(vcpu); + else + kfree(inti); + return rc; } void kvm_s390_clear_float_irqs(struct kvm *kvm)