aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390
diff options
context:
space:
mode:
authorCornelia Huck2012-12-20 08:32:08 -0600
committerMarcelo Tosatti2013-01-07 15:53:40 -0600
commitd8346b7d9bab37e6cc712ff1622c65ff98bdfef8 (patch)
tree60f36ff7f61757501f3560d8860ca217d7e92640 /arch/s390
parentb1c571a50dfacf25a24c23271e9b8bf18ff6b102 (diff)
downloadam43-linux-kernel-d8346b7d9bab37e6cc712ff1622c65ff98bdfef8.tar.gz
am43-linux-kernel-d8346b7d9bab37e6cc712ff1622c65ff98bdfef8.tar.xz
am43-linux-kernel-d8346b7d9bab37e6cc712ff1622c65ff98bdfef8.zip
KVM: s390: Support for I/O interrupts.
Add support for handling I/O interrupts (standard, subchannel-related ones and rudimentary adapter interrupts). The subchannel-identifying parameters are encoded into the interrupt type. I/O interrupts are floating, so they can't be injected on a specific vcpu. Reviewed-by: Alexander Graf <agraf@suse.de> Reviewed-by: Marcelo Tosatti <mtosatti@redhat.com> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Diffstat (limited to 'arch/s390')
-rw-r--r--arch/s390/include/asm/kvm_host.h2
-rw-r--r--arch/s390/kvm/interrupt.c103
2 files changed, 103 insertions, 2 deletions
diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h
index 711c5ab391c..a8e35c43df7 100644
--- a/arch/s390/include/asm/kvm_host.h
+++ b/arch/s390/include/asm/kvm_host.h
@@ -74,6 +74,7 @@ struct kvm_s390_sie_block {
74 __u64 epoch; /* 0x0038 */ 74 __u64 epoch; /* 0x0038 */
75 __u8 reserved40[4]; /* 0x0040 */ 75 __u8 reserved40[4]; /* 0x0040 */
76#define LCTL_CR0 0x8000 76#define LCTL_CR0 0x8000
77#define LCTL_CR6 0x0200
77 __u16 lctl; /* 0x0044 */ 78 __u16 lctl; /* 0x0044 */
78 __s16 icpua; /* 0x0046 */ 79 __s16 icpua; /* 0x0046 */
79 __u32 ictl; /* 0x0048 */ 80 __u32 ictl; /* 0x0048 */
@@ -125,6 +126,7 @@ struct kvm_vcpu_stat {
125 u32 deliver_prefix_signal; 126 u32 deliver_prefix_signal;
126 u32 deliver_restart_signal; 127 u32 deliver_restart_signal;
127 u32 deliver_program_int; 128 u32 deliver_program_int;
129 u32 deliver_io_int;
128 u32 exit_wait_state; 130 u32 exit_wait_state;
129 u32 instruction_stidp; 131 u32 instruction_stidp;
130 u32 instruction_spx; 132 u32 instruction_spx;
diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c
index c30615e605a..52cdf20906a 100644
--- a/arch/s390/kvm/interrupt.c
+++ b/arch/s390/kvm/interrupt.c
@@ -21,11 +21,26 @@
21#include "gaccess.h" 21#include "gaccess.h"
22#include "trace-s390.h" 22#include "trace-s390.h"
23 23
24#define IOINT_SCHID_MASK 0x0000ffff
25#define IOINT_SSID_MASK 0x00030000
26#define IOINT_CSSID_MASK 0x03fc0000
27#define IOINT_AI_MASK 0x04000000
28
29static int is_ioint(u64 type)
30{
31 return ((type & 0xfffe0000u) != 0xfffe0000u);
32}
33
24static int psw_extint_disabled(struct kvm_vcpu *vcpu) 34static int psw_extint_disabled(struct kvm_vcpu *vcpu)
25{ 35{
26 return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT); 36 return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT);
27} 37}
28 38
39static int psw_ioint_disabled(struct kvm_vcpu *vcpu)
40{
41 return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO);
42}
43
29static int psw_interrupts_disabled(struct kvm_vcpu *vcpu) 44static int psw_interrupts_disabled(struct kvm_vcpu *vcpu)
30{ 45{
31 if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) || 46 if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) ||
@@ -67,7 +82,15 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu,
67 case KVM_S390_SIGP_SET_PREFIX: 82 case KVM_S390_SIGP_SET_PREFIX:
68 case KVM_S390_RESTART: 83 case KVM_S390_RESTART:
69 return 1; 84 return 1;
85 case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
86 if (psw_ioint_disabled(vcpu))
87 return 0;
88 if (vcpu->arch.sie_block->gcr[6] & inti->io.io_int_word)
89 return 1;
90 return 0;
70 default: 91 default:
92 printk(KERN_WARNING "illegal interrupt type %llx\n",
93 inti->type);
71 BUG(); 94 BUG();
72 } 95 }
73 return 0; 96 return 0;
@@ -116,6 +139,12 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu,
116 case KVM_S390_SIGP_STOP: 139 case KVM_S390_SIGP_STOP:
117 __set_cpuflag(vcpu, CPUSTAT_STOP_INT); 140 __set_cpuflag(vcpu, CPUSTAT_STOP_INT);
118 break; 141 break;
142 case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
143 if (psw_ioint_disabled(vcpu))
144 __set_cpuflag(vcpu, CPUSTAT_IO_INT);
145 else
146 vcpu->arch.sie_block->lctl |= LCTL_CR6;
147 break;
119 default: 148 default:
120 BUG(); 149 BUG();
121 } 150 }
@@ -297,6 +326,47 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
297 exception = 1; 326 exception = 1;
298 break; 327 break;
299 328
329 case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
330 {
331 __u32 param0 = ((__u32)inti->io.subchannel_id << 16) |
332 inti->io.subchannel_nr;
333 __u64 param1 = ((__u64)inti->io.io_int_parm << 32) |
334 inti->io.io_int_word;
335 VCPU_EVENT(vcpu, 4, "interrupt: I/O %llx", inti->type);
336 vcpu->stat.deliver_io_int++;
337 trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
338 param0, param1);
339 rc = put_guest_u16(vcpu, __LC_SUBCHANNEL_ID,
340 inti->io.subchannel_id);
341 if (rc == -EFAULT)
342 exception = 1;
343
344 rc = put_guest_u16(vcpu, __LC_SUBCHANNEL_NR,
345 inti->io.subchannel_nr);
346 if (rc == -EFAULT)
347 exception = 1;
348
349 rc = put_guest_u32(vcpu, __LC_IO_INT_PARM,
350 inti->io.io_int_parm);
351 if (rc == -EFAULT)
352 exception = 1;
353
354 rc = put_guest_u32(vcpu, __LC_IO_INT_WORD,
355 inti->io.io_int_word);
356 if (rc == -EFAULT)
357 exception = 1;
358
359 rc = copy_to_guest(vcpu, __LC_IO_OLD_PSW,
360 &vcpu->arch.sie_block->gpsw, sizeof(psw_t));
361 if (rc == -EFAULT)
362 exception = 1;
363
364 rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
365 __LC_IO_NEW_PSW, sizeof(psw_t));
366 if (rc == -EFAULT)
367 exception = 1;
368 break;
369 }
300 default: 370 default:
301 BUG(); 371 BUG();
302 } 372 }
@@ -545,7 +615,7 @@ int kvm_s390_inject_vm(struct kvm *kvm,
545{ 615{
546 struct kvm_s390_local_interrupt *li; 616 struct kvm_s390_local_interrupt *li;
547 struct kvm_s390_float_interrupt *fi; 617 struct kvm_s390_float_interrupt *fi;
548 struct kvm_s390_interrupt_info *inti; 618 struct kvm_s390_interrupt_info *inti, *iter;
549 int sigcpu; 619 int sigcpu;
550 620
551 inti = kzalloc(sizeof(*inti), GFP_KERNEL); 621 inti = kzalloc(sizeof(*inti), GFP_KERNEL);
@@ -569,6 +639,22 @@ int kvm_s390_inject_vm(struct kvm *kvm,
569 case KVM_S390_SIGP_STOP: 639 case KVM_S390_SIGP_STOP:
570 case KVM_S390_INT_EXTERNAL_CALL: 640 case KVM_S390_INT_EXTERNAL_CALL:
571 case KVM_S390_INT_EMERGENCY: 641 case KVM_S390_INT_EMERGENCY:
642 kfree(inti);
643 return -EINVAL;
644 case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
645 if (s390int->type & IOINT_AI_MASK)
646 VM_EVENT(kvm, 5, "%s", "inject: I/O (AI)");
647 else
648 VM_EVENT(kvm, 5, "inject: I/O css %x ss %x schid %04x",
649 s390int->type & IOINT_CSSID_MASK,
650 s390int->type & IOINT_SSID_MASK,
651 s390int->type & IOINT_SCHID_MASK);
652 inti->type = s390int->type;
653 inti->io.subchannel_id = s390int->parm >> 16;
654 inti->io.subchannel_nr = s390int->parm & 0x0000ffffu;
655 inti->io.io_int_parm = s390int->parm64 >> 32;
656 inti->io.io_int_word = s390int->parm64 & 0x00000000ffffffffull;
657 break;
572 default: 658 default:
573 kfree(inti); 659 kfree(inti);
574 return -EINVAL; 660 return -EINVAL;
@@ -579,7 +665,19 @@ int kvm_s390_inject_vm(struct kvm *kvm,
579 mutex_lock(&kvm->lock); 665 mutex_lock(&kvm->lock);
580 fi = &kvm->arch.float_int; 666 fi = &kvm->arch.float_int;
581 spin_lock(&fi->lock); 667 spin_lock(&fi->lock);
582 list_add_tail(&inti->list, &fi->list); 668 if (!is_ioint(inti->type))
669 list_add_tail(&inti->list, &fi->list);
670 else {
671 /* Keep I/O interrupts sorted in isc order. */
672 list_for_each_entry(iter, &fi->list, list) {
673 if (!is_ioint(iter->type))
674 continue;
675 if (iter->io.io_int_word <= inti->io.io_int_word)
676 continue;
677 break;
678 }
679 list_add_tail(&inti->list, &iter->list);
680 }
583 atomic_set(&fi->active, 1); 681 atomic_set(&fi->active, 1);
584 sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS); 682 sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS);
585 if (sigcpu == KVM_MAX_VCPUS) { 683 if (sigcpu == KVM_MAX_VCPUS) {
@@ -653,6 +751,7 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
653 break; 751 break;
654 case KVM_S390_INT_VIRTIO: 752 case KVM_S390_INT_VIRTIO:
655 case KVM_S390_INT_SERVICE: 753 case KVM_S390_INT_SERVICE:
754 case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
656 default: 755 default:
657 kfree(inti); 756 kfree(inti);
658 return -EINVAL; 757 return -EINVAL;