[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[RFC PATCH v2 2/2] s390x: Implement the USER_SIGP_BUSY capability
From: |
Eric Farman |
Subject: |
[RFC PATCH v2 2/2] s390x: Implement the USER_SIGP_BUSY capability |
Date: |
Tue, 2 Nov 2021 21:11:22 +0100 |
With the USER_SIGP capability, the kernel will pass most (but not all)
SIGP orders to userspace for processing. But that means that the kernel
is unable to determine if/when the order has been completed by userspace,
and could potentially return an incorrect answer (CC1 with status bits
versus CC2 indicating BUSY) for one of the remaining in-kernel orders.
With a new USER_SIGP_BUSY capability, the kernel will automatically
flag a vcpu as busy for a SIGP order, and block further orders directed
to the same vcpu until userspace resets it to indicate the order has
been fully processed.
Let's use the new capability in QEMU.
Signed-off-by: Eric Farman <farman@linux.ibm.com>
---
target/s390x/cpu-sysemu.c | 7 +++++++
target/s390x/cpu.h | 4 ++++
target/s390x/kvm/kvm.c | 9 ++++++++
target/s390x/kvm/kvm_s390x.h | 1 +
target/s390x/sigp.c | 40 ++++++++++++++++++++++++++++++++++++
5 files changed, 61 insertions(+)
diff --git a/target/s390x/cpu-sysemu.c b/target/s390x/cpu-sysemu.c
index 5471e01ee8..9b01662226 100644
--- a/target/s390x/cpu-sysemu.c
+++ b/target/s390x/cpu-sysemu.c
@@ -254,6 +254,13 @@ unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU
*cpu)
return s390_count_running_cpus();
}
+void s390_cpu_reset_sigp_busy(S390CPU *cpu)
+{
+ if (kvm_enabled()) {
+ kvm_s390_vcpu_reset_busy(cpu);
+ }
+}
+
int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit)
{
if (kvm_enabled()) {
diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h
index ca3845d023..ca7f8fe3fe 100644
--- a/target/s390x/cpu.h
+++ b/target/s390x/cpu.h
@@ -780,11 +780,15 @@ int s390_assign_subch_ioeventfd(EventNotifier *notifier,
uint32_t sch_id,
int vq, bool assign);
#ifndef CONFIG_USER_ONLY
unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu);
+void s390_cpu_reset_sigp_busy(S390CPU *cpu);
#else
static inline unsigned int s390_cpu_set_state(uint8_t cpu_state, S390CPU *cpu)
{
return 0;
}
+static inline void s390_cpu_reset_sigp_busy(S390CPU *cpu)
+{
+}
#endif /* CONFIG_USER_ONLY */
static inline uint8_t s390_cpu_get_state(S390CPU *cpu)
{
diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c
index 5b1fdb55c4..e177ef1d26 100644
--- a/target/s390x/kvm/kvm.c
+++ b/target/s390x/kvm/kvm.c
@@ -360,6 +360,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
cap_protected = kvm_check_extension(s, KVM_CAP_S390_PROTECTED);
kvm_vm_enable_cap(s, KVM_CAP_S390_USER_SIGP, 0);
+ kvm_vm_enable_cap(s, KVM_CAP_S390_USER_SIGP_BUSY, 0);
kvm_vm_enable_cap(s, KVM_CAP_S390_VECTOR_REGISTERS, 0);
kvm_vm_enable_cap(s, KVM_CAP_S390_USER_STSI, 0);
if (ri_allowed()) {
@@ -2558,6 +2559,14 @@ void kvm_s390_stop_interrupt(S390CPU *cpu)
kvm_s390_vcpu_interrupt(cpu, &irq);
}
+void kvm_s390_vcpu_reset_busy(S390CPU *cpu)
+{
+ CPUState *cs = CPU(cpu);
+
+ /* Don't care about the response from this */
+ kvm_vcpu_ioctl(cs, KVM_S390_VCPU_RESET_SIGP_BUSY);
+}
+
bool kvm_arch_cpu_check_are_resettable(void)
{
return true;
diff --git a/target/s390x/kvm/kvm_s390x.h b/target/s390x/kvm/kvm_s390x.h
index 05a5e1e6f4..64ce220f1b 100644
--- a/target/s390x/kvm/kvm_s390x.h
+++ b/target/s390x/kvm/kvm_s390x.h
@@ -45,5 +45,6 @@ void kvm_s390_crypto_reset(void);
void kvm_s390_restart_interrupt(S390CPU *cpu);
void kvm_s390_stop_interrupt(S390CPU *cpu);
void kvm_s390_set_diag318(CPUState *cs, uint64_t diag318_info);
+void kvm_s390_vcpu_reset_busy(S390CPU *cpu);
#endif /* KVM_S390X_H */
diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c
index 51c727834c..44291b34be 100644
--- a/target/s390x/sigp.c
+++ b/target/s390x/sigp.c
@@ -43,6 +43,7 @@ static void sigp_sense(S390CPU *dst_cpu, SigpInfo *si)
if (!tcg_enabled()) {
/* handled in KVM */
set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
+ s390_cpu_reset_sigp_busy(dst_cpu);
return;
}
@@ -58,6 +59,7 @@ static void sigp_sense(S390CPU *dst_cpu, SigpInfo *si)
}
set_sigp_status(si, status);
}
+ s390_cpu_reset_sigp_busy(dst_cpu);
}
static void sigp_external_call(S390CPU *src_cpu, S390CPU *dst_cpu, SigpInfo
*si)
@@ -67,6 +69,7 @@ static void sigp_external_call(S390CPU *src_cpu, S390CPU
*dst_cpu, SigpInfo *si)
if (!tcg_enabled()) {
/* handled in KVM */
set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
+ s390_cpu_reset_sigp_busy(dst_cpu);
return;
}
@@ -76,6 +79,7 @@ static void sigp_external_call(S390CPU *src_cpu, S390CPU
*dst_cpu, SigpInfo *si)
} else {
set_sigp_status(si, SIGP_STAT_EXT_CALL_PENDING);
}
+ s390_cpu_reset_sigp_busy(dst_cpu);
}
static void sigp_emergency(S390CPU *src_cpu, S390CPU *dst_cpu, SigpInfo *si)
@@ -83,11 +87,13 @@ static void sigp_emergency(S390CPU *src_cpu, S390CPU
*dst_cpu, SigpInfo *si)
if (!tcg_enabled()) {
/* handled in KVM */
set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
+ s390_cpu_reset_sigp_busy(dst_cpu);
return;
}
cpu_inject_emergency_signal(dst_cpu, src_cpu->env.core_id);
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+ s390_cpu_reset_sigp_busy(dst_cpu);
}
static void sigp_start(CPUState *cs, run_on_cpu_data arg)
@@ -97,11 +103,13 @@ static void sigp_start(CPUState *cs, run_on_cpu_data arg)
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+ s390_cpu_reset_sigp_busy(cpu);
return;
}
s390_cpu_set_state(S390_CPU_STATE_OPERATING, cpu);
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+ s390_cpu_reset_sigp_busy(cpu);
}
static void sigp_stop(CPUState *cs, run_on_cpu_data arg)
@@ -111,12 +119,14 @@ static void sigp_stop(CPUState *cs, run_on_cpu_data arg)
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_OPERATING) {
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+ s390_cpu_reset_sigp_busy(cpu);
return;
}
/* disabled wait - sleeping in user space */
if (cs->halted) {
s390_cpu_set_state(S390_CPU_STATE_STOPPED, cpu);
+ s390_cpu_reset_sigp_busy(cpu);
} else {
/* execute the stop function */
cpu->env.sigp_order = SIGP_STOP;
@@ -145,6 +155,7 @@ static void sigp_stop_and_store_status(CPUState *cs,
run_on_cpu_data arg)
/* already stopped, just store the status */
cpu_synchronize_state(cs);
s390_store_status(cpu, S390_STORE_STATUS_DEF_ADDR, true);
+ s390_cpu_reset_sigp_busy(cpu);
break;
}
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
@@ -159,6 +170,7 @@ static void sigp_store_status_at_address(CPUState *cs,
run_on_cpu_data arg)
/* cpu has to be stopped */
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
+ s390_cpu_reset_sigp_busy(cpu);
return;
}
@@ -166,9 +178,11 @@ static void sigp_store_status_at_address(CPUState *cs,
run_on_cpu_data arg)
if (s390_store_status(cpu, address, false)) {
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
+ s390_cpu_reset_sigp_busy(cpu);
return;
}
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+ s390_cpu_reset_sigp_busy(cpu);
}
#define ADTL_SAVE_LC_MASK 0xfUL
@@ -183,18 +197,21 @@ static void sigp_store_adtl_status(CPUState *cs,
run_on_cpu_data arg)
if (!s390_has_feat(S390_FEAT_VECTOR) &&
!s390_has_feat(S390_FEAT_GUARDED_STORAGE)) {
set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
+ s390_cpu_reset_sigp_busy(cpu);
return;
}
/* cpu has to be stopped */
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
+ s390_cpu_reset_sigp_busy(cpu);
return;
}
/* address must be aligned to length */
if (addr & (len - 1)) {
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
+ s390_cpu_reset_sigp_busy(cpu);
return;
}
@@ -202,6 +219,7 @@ static void sigp_store_adtl_status(CPUState *cs,
run_on_cpu_data arg)
if (!s390_has_feat(S390_FEAT_GUARDED_STORAGE) &&
lc != 0) {
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
+ s390_cpu_reset_sigp_busy(cpu);
return;
}
@@ -212,6 +230,7 @@ static void sigp_store_adtl_status(CPUState *cs,
run_on_cpu_data arg)
lc != 11 &&
lc != 12) {
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
+ s390_cpu_reset_sigp_busy(cpu);
return;
}
@@ -219,9 +238,11 @@ static void sigp_store_adtl_status(CPUState *cs,
run_on_cpu_data arg)
if (s390_store_adtl_status(cpu, addr, len)) {
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
+ s390_cpu_reset_sigp_busy(cpu);
return;
}
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+ s390_cpu_reset_sigp_busy(cpu);
}
static void sigp_restart(CPUState *cs, run_on_cpu_data arg)
@@ -246,6 +267,7 @@ static void sigp_restart(CPUState *cs, run_on_cpu_data arg)
break;
}
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+ s390_cpu_reset_sigp_busy(cpu);
}
static void sigp_initial_cpu_reset(CPUState *cs, run_on_cpu_data arg)
@@ -258,6 +280,7 @@ static void sigp_initial_cpu_reset(CPUState *cs,
run_on_cpu_data arg)
scc->reset(cs, S390_CPU_RESET_INITIAL);
cpu_synchronize_post_reset(cs);
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+ s390_cpu_reset_sigp_busy(cpu);
}
static void sigp_cpu_reset(CPUState *cs, run_on_cpu_data arg)
@@ -270,6 +293,7 @@ static void sigp_cpu_reset(CPUState *cs, run_on_cpu_data
arg)
scc->reset(cs, S390_CPU_RESET_NORMAL);
cpu_synchronize_post_reset(cs);
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+ s390_cpu_reset_sigp_busy(cpu);
}
static void sigp_set_prefix(CPUState *cs, run_on_cpu_data arg)
@@ -284,12 +308,14 @@ static void sigp_set_prefix(CPUState *cs, run_on_cpu_data
arg)
sizeof(struct LowCore), false,
MEMTXATTRS_UNSPECIFIED)) {
set_sigp_status(si, SIGP_STAT_INVALID_PARAMETER);
+ s390_cpu_reset_sigp_busy(cpu);
return;
}
/* cpu has to be stopped */
if (s390_cpu_get_state(cpu) != S390_CPU_STATE_STOPPED) {
set_sigp_status(si, SIGP_STAT_INCORRECT_STATE);
+ s390_cpu_reset_sigp_busy(cpu);
return;
}
@@ -297,6 +323,7 @@ static void sigp_set_prefix(CPUState *cs, run_on_cpu_data
arg)
tlb_flush(cs);
cpu_synchronize_post_init(cs);
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+ s390_cpu_reset_sigp_busy(cpu);
}
static void sigp_cond_emergency(S390CPU *src_cpu, S390CPU *dst_cpu,
@@ -310,6 +337,7 @@ static void sigp_cond_emergency(S390CPU *src_cpu, S390CPU
*dst_cpu,
if (!tcg_enabled()) {
/* handled in KVM */
set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
+ s390_cpu_reset_sigp_busy(dst_cpu);
return;
}
@@ -331,6 +359,7 @@ static void sigp_cond_emergency(S390CPU *src_cpu, S390CPU
*dst_cpu,
}
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
+ s390_cpu_reset_sigp_busy(dst_cpu);
}
static void sigp_sense_running(S390CPU *dst_cpu, SigpInfo *si)
@@ -338,12 +367,14 @@ static void sigp_sense_running(S390CPU *dst_cpu, SigpInfo
*si)
if (!tcg_enabled()) {
/* handled in KVM */
set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
+ s390_cpu_reset_sigp_busy(dst_cpu);
return;
}
/* sensing without locks is racy, but it's the same for real hw */
if (!s390_has_feat(S390_FEAT_SENSE_RUNNING_STATUS)) {
set_sigp_status(si, SIGP_STAT_INVALID_ORDER);
+ s390_cpu_reset_sigp_busy(dst_cpu);
return;
}
@@ -353,6 +384,7 @@ static void sigp_sense_running(S390CPU *dst_cpu, SigpInfo
*si)
} else {
si->cc = SIGP_CC_ORDER_CODE_ACCEPTED;
}
+ s390_cpu_reset_sigp_busy(dst_cpu);
}
static int handle_sigp_single_dst(S390CPU *cpu, S390CPU *dst_cpu, uint8_t
order,
@@ -420,6 +452,7 @@ static int handle_sigp_single_dst(S390CPU *cpu, S390CPU
*dst_cpu, uint8_t order,
break;
default:
set_sigp_status(&si, SIGP_STAT_INVALID_ORDER);
+ s390_cpu_reset_sigp_busy(dst_cpu);
}
return si.cc;
@@ -444,6 +477,12 @@ int handle_sigp(CPUS390XState *env, uint8_t order,
uint64_t r1, uint64_t r3)
int ret;
if (qemu_mutex_trylock(&qemu_sigp_mutex)) {
+ if (order != SIGP_SET_ARCH) {
+ dst_cpu = s390_cpu_addr2state(env->regs[r3]);
+ if (dst_cpu) {
+ s390_cpu_reset_sigp_busy(dst_cpu);
+ }
+ }
ret = SIGP_CC_BUSY;
goto out;
}
@@ -487,6 +526,7 @@ void do_stop_interrupt(CPUS390XState *env)
}
env->sigp_order = 0;
env->pending_int &= ~INTERRUPT_STOP;
+ s390_cpu_reset_sigp_busy(cpu);
}
void s390_init_sigp(void)
--
2.25.1