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 | |
| 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>
| -rw-r--r-- | Documentation/virtual/kvm/api.txt | 4 | ||||
| -rw-r--r-- | arch/s390/include/asm/kvm_host.h | 2 | ||||
| -rw-r--r-- | arch/s390/kvm/interrupt.c | 103 | ||||
| -rw-r--r-- | include/uapi/linux/kvm.h | 9 |
4 files changed, 116 insertions, 2 deletions
diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index a4df5535996b..83bd92b52936 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt | |||
| @@ -2069,6 +2069,10 @@ KVM_S390_INT_VIRTIO (vm) - virtio external interrupt; external interrupt | |||
| 2069 | KVM_S390_INT_SERVICE (vm) - sclp external interrupt; sclp parameter in parm | 2069 | KVM_S390_INT_SERVICE (vm) - sclp external interrupt; sclp parameter in parm |
| 2070 | KVM_S390_INT_EMERGENCY (vcpu) - sigp emergency; source cpu in parm | 2070 | KVM_S390_INT_EMERGENCY (vcpu) - sigp emergency; source cpu in parm |
| 2071 | KVM_S390_INT_EXTERNAL_CALL (vcpu) - sigp external call; source cpu in parm | 2071 | KVM_S390_INT_EXTERNAL_CALL (vcpu) - sigp external call; source cpu in parm |
| 2072 | KVM_S390_INT_IO(ai,cssid,ssid,schid) (vm) - compound value to indicate an | ||
| 2073 | I/O interrupt (ai - adapter interrupt; cssid,ssid,schid - subchannel); | ||
| 2074 | I/O interruption parameters in parm (subchannel) and parm64 (intparm, | ||
| 2075 | interruption subclass) | ||
| 2072 | 2076 | ||
| 2073 | Note that the vcpu ioctl is asynchronous to vcpu execution. | 2077 | Note that the vcpu ioctl is asynchronous to vcpu execution. |
| 2074 | 2078 | ||
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 | ||
