diff options
author | Cornelia Huck <cornelia.huck@de.ibm.com> | 2012-12-20 09:32:08 -0500 |
---|---|---|
committer | Marcelo Tosatti <mtosatti@redhat.com> | 2013-01-07 16:53:40 -0500 |
commit | d8346b7d9bab37e6cc712ff1622c65ff98bdfef8 (patch) | |
tree | 60f36ff7f61757501f3560d8860ca217d7e92640 /arch/s390 | |
parent | b1c571a50dfacf25a24c23271e9b8bf18ff6b102 (diff) |
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.h | 2 | ||||
-rw-r--r-- | arch/s390/kvm/interrupt.c | 103 |
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 711c5ab391cf..a8e35c43df78 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 c30615e605ac..52cdf20906ab 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 | |||
29 | static int is_ioint(u64 type) | ||
30 | { | ||
31 | return ((type & 0xfffe0000u) != 0xfffe0000u); | ||
32 | } | ||
33 | |||
24 | static int psw_extint_disabled(struct kvm_vcpu *vcpu) | 34 | static 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 | ||
39 | static int psw_ioint_disabled(struct kvm_vcpu *vcpu) | ||
40 | { | ||
41 | return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO); | ||
42 | } | ||
43 | |||
29 | static int psw_interrupts_disabled(struct kvm_vcpu *vcpu) | 44 | static 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; |