diff options
Diffstat (limited to 'arch/s390/kvm/interrupt.c')
-rw-r--r-- | arch/s390/kvm/interrupt.c | 57 |
1 files changed, 42 insertions, 15 deletions
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index 1ba917638bba..0ad1f7500abd 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/bitmap.h> | 19 | #include <linux/bitmap.h> |
20 | #include <asm/asm-offsets.h> | 20 | #include <asm/asm-offsets.h> |
21 | #include <asm/uaccess.h> | 21 | #include <asm/uaccess.h> |
22 | #include <asm/sclp.h> | ||
22 | #include "kvm-s390.h" | 23 | #include "kvm-s390.h" |
23 | #include "gaccess.h" | 24 | #include "gaccess.h" |
24 | #include "trace-s390.h" | 25 | #include "trace-s390.h" |
@@ -735,18 +736,17 @@ static int __must_check __deliver_floating_interrupt(struct kvm_vcpu *vcpu, | |||
735 | return rc; | 736 | return rc; |
736 | } | 737 | } |
737 | 738 | ||
738 | /* Check whether SIGP interpretation facility has an external call pending */ | 739 | /* Check whether an external call is pending (deliverable or not) */ |
739 | int kvm_s390_si_ext_call_pending(struct kvm_vcpu *vcpu) | 740 | int kvm_s390_ext_call_pending(struct kvm_vcpu *vcpu) |
740 | { | 741 | { |
741 | atomic_t *sigp_ctrl = &vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].ctrl; | 742 | struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; |
743 | uint8_t sigp_ctrl = vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].sigp_ctrl; | ||
742 | 744 | ||
743 | if (!psw_extint_disabled(vcpu) && | 745 | if (!sclp_has_sigpif()) |
744 | (vcpu->arch.sie_block->gcr[0] & 0x2000ul) && | 746 | return test_bit(IRQ_PEND_EXT_EXTERNAL, &li->pending_irqs); |
745 | (atomic_read(sigp_ctrl) & SIGP_CTRL_C) && | ||
746 | (atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_ECALL_PEND)) | ||
747 | return 1; | ||
748 | 747 | ||
749 | return 0; | 748 | return (sigp_ctrl & SIGP_CTRL_C) && |
749 | (atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_ECALL_PEND); | ||
750 | } | 750 | } |
751 | 751 | ||
752 | int kvm_s390_vcpu_has_irq(struct kvm_vcpu *vcpu, int exclude_stop) | 752 | int kvm_s390_vcpu_has_irq(struct kvm_vcpu *vcpu, int exclude_stop) |
@@ -770,7 +770,10 @@ int kvm_s390_vcpu_has_irq(struct kvm_vcpu *vcpu, int exclude_stop) | |||
770 | if (!rc && kvm_cpu_has_pending_timer(vcpu)) | 770 | if (!rc && kvm_cpu_has_pending_timer(vcpu)) |
771 | rc = 1; | 771 | rc = 1; |
772 | 772 | ||
773 | if (!rc && kvm_s390_si_ext_call_pending(vcpu)) | 773 | /* external call pending and deliverable */ |
774 | if (!rc && kvm_s390_ext_call_pending(vcpu) && | ||
775 | !psw_extint_disabled(vcpu) && | ||
776 | (vcpu->arch.sie_block->gcr[0] & 0x2000ul)) | ||
774 | rc = 1; | 777 | rc = 1; |
775 | 778 | ||
776 | if (!rc && !exclude_stop && kvm_s390_is_stop_irq_pending(vcpu)) | 779 | if (!rc && !exclude_stop && kvm_s390_is_stop_irq_pending(vcpu)) |
@@ -875,8 +878,7 @@ void kvm_s390_clear_local_irqs(struct kvm_vcpu *vcpu) | |||
875 | 878 | ||
876 | /* clear pending external calls set by sigp interpretation facility */ | 879 | /* clear pending external calls set by sigp interpretation facility */ |
877 | atomic_clear_mask(CPUSTAT_ECALL_PEND, li->cpuflags); | 880 | atomic_clear_mask(CPUSTAT_ECALL_PEND, li->cpuflags); |
878 | atomic_clear_mask(SIGP_CTRL_C, | 881 | vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].sigp_ctrl = 0; |
879 | &vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].ctrl); | ||
880 | } | 882 | } |
881 | 883 | ||
882 | int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu) | 884 | int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu) |
@@ -1000,18 +1002,43 @@ static int __inject_pfault_init(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq) | |||
1000 | return 0; | 1002 | return 0; |
1001 | } | 1003 | } |
1002 | 1004 | ||
1005 | static int __inject_extcall_sigpif(struct kvm_vcpu *vcpu, uint16_t src_id) | ||
1006 | { | ||
1007 | unsigned char new_val, old_val; | ||
1008 | uint8_t *sigp_ctrl = &vcpu->kvm->arch.sca->cpu[vcpu->vcpu_id].sigp_ctrl; | ||
1009 | |||
1010 | new_val = SIGP_CTRL_C | (src_id & SIGP_CTRL_SCN_MASK); | ||
1011 | old_val = *sigp_ctrl & ~SIGP_CTRL_C; | ||
1012 | if (cmpxchg(sigp_ctrl, old_val, new_val) != old_val) { | ||
1013 | /* another external call is pending */ | ||
1014 | return -EBUSY; | ||
1015 | } | ||
1016 | atomic_set_mask(CPUSTAT_ECALL_PEND, &vcpu->arch.sie_block->cpuflags); | ||
1017 | return 0; | ||
1018 | } | ||
1019 | |||
1003 | static int __inject_extcall(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq) | 1020 | static int __inject_extcall(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq) |
1004 | { | 1021 | { |
1005 | struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; | 1022 | struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; |
1006 | struct kvm_s390_extcall_info *extcall = &li->irq.extcall; | 1023 | struct kvm_s390_extcall_info *extcall = &li->irq.extcall; |
1024 | uint16_t src_id = irq->u.extcall.code; | ||
1007 | 1025 | ||
1008 | VCPU_EVENT(vcpu, 3, "inject: external call source-cpu:%u", | 1026 | VCPU_EVENT(vcpu, 3, "inject: external call source-cpu:%u", |
1009 | irq->u.extcall.code); | 1027 | src_id); |
1010 | trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_INT_EXTERNAL_CALL, | 1028 | trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_INT_EXTERNAL_CALL, |
1011 | irq->u.extcall.code, 0, 2); | 1029 | src_id, 0, 2); |
1012 | 1030 | ||
1031 | /* sending vcpu invalid */ | ||
1032 | if (src_id >= KVM_MAX_VCPUS || | ||
1033 | kvm_get_vcpu(vcpu->kvm, src_id) == NULL) | ||
1034 | return -EINVAL; | ||
1035 | |||
1036 | if (sclp_has_sigpif()) | ||
1037 | return __inject_extcall_sigpif(vcpu, src_id); | ||
1038 | |||
1039 | if (!test_and_set_bit(IRQ_PEND_EXT_EXTERNAL, &li->pending_irqs)) | ||
1040 | return -EBUSY; | ||
1013 | *extcall = irq->u.extcall; | 1041 | *extcall = irq->u.extcall; |
1014 | set_bit(IRQ_PEND_EXT_EXTERNAL, &li->pending_irqs); | ||
1015 | atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); | 1042 | atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); |
1016 | return 0; | 1043 | return 0; |
1017 | } | 1044 | } |