Commit d8346b7d authored by Cornelia Huck's avatar Cornelia Huck Committed by Marcelo Tosatti

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: default avatarAlexander Graf <agraf@suse.de>
Reviewed-by: default avatarMarcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
Signed-off-by: default avatarMarcelo Tosatti <mtosatti@redhat.com>
parent b1c571a5
...@@ -2069,6 +2069,10 @@ KVM_S390_INT_VIRTIO (vm) - virtio external interrupt; external interrupt ...@@ -2069,6 +2069,10 @@ KVM_S390_INT_VIRTIO (vm) - virtio external interrupt; external interrupt
KVM_S390_INT_SERVICE (vm) - sclp external interrupt; sclp parameter in parm KVM_S390_INT_SERVICE (vm) - sclp external interrupt; sclp parameter in parm
KVM_S390_INT_EMERGENCY (vcpu) - sigp emergency; source cpu in parm KVM_S390_INT_EMERGENCY (vcpu) - sigp emergency; source cpu in parm
KVM_S390_INT_EXTERNAL_CALL (vcpu) - sigp external call; source cpu in parm KVM_S390_INT_EXTERNAL_CALL (vcpu) - sigp external call; source cpu in parm
KVM_S390_INT_IO(ai,cssid,ssid,schid) (vm) - compound value to indicate an
I/O interrupt (ai - adapter interrupt; cssid,ssid,schid - subchannel);
I/O interruption parameters in parm (subchannel) and parm64 (intparm,
interruption subclass)
Note that the vcpu ioctl is asynchronous to vcpu execution. Note that the vcpu ioctl is asynchronous to vcpu execution.
......
...@@ -74,6 +74,7 @@ struct kvm_s390_sie_block { ...@@ -74,6 +74,7 @@ struct kvm_s390_sie_block {
__u64 epoch; /* 0x0038 */ __u64 epoch; /* 0x0038 */
__u8 reserved40[4]; /* 0x0040 */ __u8 reserved40[4]; /* 0x0040 */
#define LCTL_CR0 0x8000 #define LCTL_CR0 0x8000
#define LCTL_CR6 0x0200
__u16 lctl; /* 0x0044 */ __u16 lctl; /* 0x0044 */
__s16 icpua; /* 0x0046 */ __s16 icpua; /* 0x0046 */
__u32 ictl; /* 0x0048 */ __u32 ictl; /* 0x0048 */
...@@ -125,6 +126,7 @@ struct kvm_vcpu_stat { ...@@ -125,6 +126,7 @@ struct kvm_vcpu_stat {
u32 deliver_prefix_signal; u32 deliver_prefix_signal;
u32 deliver_restart_signal; u32 deliver_restart_signal;
u32 deliver_program_int; u32 deliver_program_int;
u32 deliver_io_int;
u32 exit_wait_state; u32 exit_wait_state;
u32 instruction_stidp; u32 instruction_stidp;
u32 instruction_spx; u32 instruction_spx;
......
...@@ -21,11 +21,26 @@ ...@@ -21,11 +21,26 @@
#include "gaccess.h" #include "gaccess.h"
#include "trace-s390.h" #include "trace-s390.h"
#define IOINT_SCHID_MASK 0x0000ffff
#define IOINT_SSID_MASK 0x00030000
#define IOINT_CSSID_MASK 0x03fc0000
#define IOINT_AI_MASK 0x04000000
static int is_ioint(u64 type)
{
return ((type & 0xfffe0000u) != 0xfffe0000u);
}
static int psw_extint_disabled(struct kvm_vcpu *vcpu) static int psw_extint_disabled(struct kvm_vcpu *vcpu)
{ {
return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT); return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_EXT);
} }
static int psw_ioint_disabled(struct kvm_vcpu *vcpu)
{
return !(vcpu->arch.sie_block->gpsw.mask & PSW_MASK_IO);
}
static int psw_interrupts_disabled(struct kvm_vcpu *vcpu) static int psw_interrupts_disabled(struct kvm_vcpu *vcpu)
{ {
if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) || if ((vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PER) ||
...@@ -67,7 +82,15 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu, ...@@ -67,7 +82,15 @@ static int __interrupt_is_deliverable(struct kvm_vcpu *vcpu,
case KVM_S390_SIGP_SET_PREFIX: case KVM_S390_SIGP_SET_PREFIX:
case KVM_S390_RESTART: case KVM_S390_RESTART:
return 1; return 1;
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
if (psw_ioint_disabled(vcpu))
return 0;
if (vcpu->arch.sie_block->gcr[6] & inti->io.io_int_word)
return 1;
return 0;
default: default:
printk(KERN_WARNING "illegal interrupt type %llx\n",
inti->type);
BUG(); BUG();
} }
return 0; return 0;
...@@ -116,6 +139,12 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu, ...@@ -116,6 +139,12 @@ static void __set_intercept_indicator(struct kvm_vcpu *vcpu,
case KVM_S390_SIGP_STOP: case KVM_S390_SIGP_STOP:
__set_cpuflag(vcpu, CPUSTAT_STOP_INT); __set_cpuflag(vcpu, CPUSTAT_STOP_INT);
break; break;
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
if (psw_ioint_disabled(vcpu))
__set_cpuflag(vcpu, CPUSTAT_IO_INT);
else
vcpu->arch.sie_block->lctl |= LCTL_CR6;
break;
default: default:
BUG(); BUG();
} }
...@@ -297,6 +326,47 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu, ...@@ -297,6 +326,47 @@ static void __do_deliver_interrupt(struct kvm_vcpu *vcpu,
exception = 1; exception = 1;
break; break;
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
{
__u32 param0 = ((__u32)inti->io.subchannel_id << 16) |
inti->io.subchannel_nr;
__u64 param1 = ((__u64)inti->io.io_int_parm << 32) |
inti->io.io_int_word;
VCPU_EVENT(vcpu, 4, "interrupt: I/O %llx", inti->type);
vcpu->stat.deliver_io_int++;
trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type,
param0, param1);
rc = put_guest_u16(vcpu, __LC_SUBCHANNEL_ID,
inti->io.subchannel_id);
if (rc == -EFAULT)
exception = 1;
rc = put_guest_u16(vcpu, __LC_SUBCHANNEL_NR,
inti->io.subchannel_nr);
if (rc == -EFAULT)
exception = 1;
rc = put_guest_u32(vcpu, __LC_IO_INT_PARM,
inti->io.io_int_parm);
if (rc == -EFAULT)
exception = 1;
rc = put_guest_u32(vcpu, __LC_IO_INT_WORD,
inti->io.io_int_word);
if (rc == -EFAULT)
exception = 1;
rc = copy_to_guest(vcpu, __LC_IO_OLD_PSW,
&vcpu->arch.sie_block->gpsw, sizeof(psw_t));
if (rc == -EFAULT)
exception = 1;
rc = copy_from_guest(vcpu, &vcpu->arch.sie_block->gpsw,
__LC_IO_NEW_PSW, sizeof(psw_t));
if (rc == -EFAULT)
exception = 1;
break;
}
default: default:
BUG(); BUG();
} }
...@@ -545,7 +615,7 @@ int kvm_s390_inject_vm(struct kvm *kvm, ...@@ -545,7 +615,7 @@ int kvm_s390_inject_vm(struct kvm *kvm,
{ {
struct kvm_s390_local_interrupt *li; struct kvm_s390_local_interrupt *li;
struct kvm_s390_float_interrupt *fi; struct kvm_s390_float_interrupt *fi;
struct kvm_s390_interrupt_info *inti; struct kvm_s390_interrupt_info *inti, *iter;
int sigcpu; int sigcpu;
inti = kzalloc(sizeof(*inti), GFP_KERNEL); inti = kzalloc(sizeof(*inti), GFP_KERNEL);
...@@ -569,6 +639,22 @@ int kvm_s390_inject_vm(struct kvm *kvm, ...@@ -569,6 +639,22 @@ int kvm_s390_inject_vm(struct kvm *kvm,
case KVM_S390_SIGP_STOP: case KVM_S390_SIGP_STOP:
case KVM_S390_INT_EXTERNAL_CALL: case KVM_S390_INT_EXTERNAL_CALL:
case KVM_S390_INT_EMERGENCY: case KVM_S390_INT_EMERGENCY:
kfree(inti);
return -EINVAL;
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
if (s390int->type & IOINT_AI_MASK)
VM_EVENT(kvm, 5, "%s", "inject: I/O (AI)");
else
VM_EVENT(kvm, 5, "inject: I/O css %x ss %x schid %04x",
s390int->type & IOINT_CSSID_MASK,
s390int->type & IOINT_SSID_MASK,
s390int->type & IOINT_SCHID_MASK);
inti->type = s390int->type;
inti->io.subchannel_id = s390int->parm >> 16;
inti->io.subchannel_nr = s390int->parm & 0x0000ffffu;
inti->io.io_int_parm = s390int->parm64 >> 32;
inti->io.io_int_word = s390int->parm64 & 0x00000000ffffffffull;
break;
default: default:
kfree(inti); kfree(inti);
return -EINVAL; return -EINVAL;
...@@ -579,7 +665,19 @@ int kvm_s390_inject_vm(struct kvm *kvm, ...@@ -579,7 +665,19 @@ int kvm_s390_inject_vm(struct kvm *kvm,
mutex_lock(&kvm->lock); mutex_lock(&kvm->lock);
fi = &kvm->arch.float_int; fi = &kvm->arch.float_int;
spin_lock(&fi->lock); spin_lock(&fi->lock);
if (!is_ioint(inti->type))
list_add_tail(&inti->list, &fi->list); list_add_tail(&inti->list, &fi->list);
else {
/* Keep I/O interrupts sorted in isc order. */
list_for_each_entry(iter, &fi->list, list) {
if (!is_ioint(iter->type))
continue;
if (iter->io.io_int_word <= inti->io.io_int_word)
continue;
break;
}
list_add_tail(&inti->list, &iter->list);
}
atomic_set(&fi->active, 1); atomic_set(&fi->active, 1);
sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS); sigcpu = find_first_bit(fi->idle_mask, KVM_MAX_VCPUS);
if (sigcpu == KVM_MAX_VCPUS) { if (sigcpu == KVM_MAX_VCPUS) {
...@@ -653,6 +751,7 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, ...@@ -653,6 +751,7 @@ int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
break; break;
case KVM_S390_INT_VIRTIO: case KVM_S390_INT_VIRTIO:
case KVM_S390_INT_SERVICE: case KVM_S390_INT_SERVICE:
case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX:
default: default:
kfree(inti); kfree(inti);
return -EINVAL; return -EINVAL;
......
...@@ -401,6 +401,15 @@ struct kvm_s390_psw { ...@@ -401,6 +401,15 @@ struct kvm_s390_psw {
#define KVM_S390_INT_SERVICE 0xffff2401u #define KVM_S390_INT_SERVICE 0xffff2401u
#define KVM_S390_INT_EMERGENCY 0xffff1201u #define KVM_S390_INT_EMERGENCY 0xffff1201u
#define KVM_S390_INT_EXTERNAL_CALL 0xffff1202u #define KVM_S390_INT_EXTERNAL_CALL 0xffff1202u
/* Anything below 0xfffe0000u is taken by INT_IO */
#define KVM_S390_INT_IO(ai,cssid,ssid,schid) \
(((schid)) | \
((ssid) << 16) | \
((cssid) << 18) | \
((ai) << 26))
#define KVM_S390_INT_IO_MIN 0x00000000u
#define KVM_S390_INT_IO_MAX 0xfffdffffu
struct kvm_s390_interrupt { struct kvm_s390_interrupt {
__u32 type; __u32 type;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment