The sysctl file /proc/sys/s390/iam_clear_strategy defines under which condition the GISA IAM is cleared before entering the SIE in routine vcpu_pre_run(). The following IAM clear strategies are available: 0 : the mask is never cleared 1 : the mask is cleared if I/O interruptions are not disabled by the PSW of the vcpu entering the SIE 2 : the mask is unconditionally cleared The default IAM clear strategy after system boot is 0. Set the new strategy by means of the sysctl command or write to the file. sysctl s390.iam_clear_strategy=1 echo 2 >/proc/sys/s390/iam_clear_strategy Signed-off-by: Michael Mueller <mimu@xxxxxxxxxxxxx> --- arch/s390/kvm/interrupt.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++ arch/s390/kvm/kvm-s390.c | 1 + arch/s390/kvm/kvm-s390.h | 1 + 3 files changed, 71 insertions(+) diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index dae78d91fa53..76fb37ed98f8 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -33,6 +33,7 @@ #define VIRTIO_PARAM 0x0d00 static struct kvm_s390_gib *gib; +static unsigned int iam_clear_strategy; /* handle external calls via sigp interpretation facility */ static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id) @@ -2895,6 +2896,73 @@ int kvm_s390_get_irq_state(struct kvm_vcpu *vcpu, __u8 __user *buf, int len) return n; } +static int iam_clear_strategy_ctl_handler(struct ctl_table *ctl, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + unsigned int strategy = iam_clear_strategy; + unsigned int min = 0; + unsigned int max = 2; + int rc; + struct ctl_table ctl_entry = { + .procname = ctl->procname, + .data = &strategy, + .maxlen = sizeof(int), + .extra1 = &min, + .extra2 = &max, + }; + + rc = proc_douintvec_minmax(&ctl_entry, write, buffer, lenp, ppos); + if (rc < 0 || !write) + return rc; + + if (iam_clear_strategy != strategy) + iam_clear_strategy = strategy; + + return rc; +} + +static struct ctl_table iam_clear_strategy_ctl_table[] = { + { + .procname = "iam_clear_strategy", + .mode = 0644, + .proc_handler = iam_clear_strategy_ctl_handler, + }, + { }, +}; + +static struct ctl_table s390_dir_table[] = { + { + .procname = "s390", + .maxlen = 0, + .mode = 0555, + .child = iam_clear_strategy_ctl_table, + }, + { }, +}; + +void kvm_s390_try_clear_iam(struct kvm_vcpu *vcpu) +{ + if (!vcpu->kvm->arch.gib_in_use) + return; + + switch (iam_clear_strategy) { + case 0: /* never clear IAM on SIE entry */ + return; + case 1: /* + * clear IAM on SIE entry when I/O interruptions + * are not disabled by the vcpu PSW + */ + if (psw_ioint_disabled(vcpu)) + return; + case 2: /* always clear IAM on SIE entry */ + vcpu->kvm->arch.gisa->iam = 0; + return; + default: + break; + } +} + static void nullify_gisa(struct kvm_s390_gisa *gisa) { memset(gisa, 0, sizeof(struct kvm_s390_gisa)); @@ -3078,6 +3146,7 @@ int kvm_s390_gib_init(u8 nisc) goto out_unreg; } + register_sysctl_table(s390_dir_table); KVM_EVENT(3, "gib 0x%pK (nisc=%d) initialized", gib, gib->nisc); return rc; diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 9b20017dda29..0b34be612172 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -3474,6 +3474,7 @@ static int vcpu_pre_run(struct kvm_vcpu *vcpu) } atomic_inc(&vcpu->kvm->arch.vcpus_in_sie); + kvm_s390_try_clear_iam(vcpu); vcpu->arch.sie_block->icptcode = 0; cpuflags = atomic_read(&vcpu->arch.sie_block->cpuflags); diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index 1c93ceaeb9e4..09fa5a559858 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -388,6 +388,7 @@ void kvm_s390_gisa_clear(struct kvm *kvm); void kvm_s390_gisa_destroy(struct kvm *kvm); int kvm_s390_gib_init(u8 nisc); void kvm_s390_gib_destroy(void); +void kvm_s390_try_clear_iam(struct kvm_vcpu *vcpu); /* implemented in guestdbg.c */ void kvm_s390_backup_guest_per_regs(struct kvm_vcpu *vcpu); -- 2.13.4