target/s390x: Use atomic operations for COMPARE SWAP PURGE

Also provide the cross-cpu tlb flushing required by the PoO.

Reviewed-by: Aurelien Jarno <aurelien@aurel32.net>
Signed-off-by: Richard Henderson <rth@twiddle.net>
This commit is contained in:
Richard Henderson 2017-05-22 18:34:42 -07:00
parent a72da8b7f5
commit 31a18b4575
4 changed files with 48 additions and 30 deletions

View File

@ -107,13 +107,13 @@ DEF_HELPER_FLAGS_2(tprot, TCG_CALL_NO_RWG, i32, i64, i64)
DEF_HELPER_FLAGS_2(iske, TCG_CALL_NO_RWG_SE, i64, env, i64)
DEF_HELPER_FLAGS_3(sske, TCG_CALL_NO_RWG, void, env, i64, i64)
DEF_HELPER_FLAGS_2(rrbe, TCG_CALL_NO_RWG, i32, env, i64)
DEF_HELPER_3(csp, i32, env, i32, i64)
DEF_HELPER_4(mvcs, i32, env, i64, i64, i64)
DEF_HELPER_4(mvcp, i32, env, i64, i64, i64)
DEF_HELPER_4(sigp, i32, env, i64, i32, i64)
DEF_HELPER_FLAGS_2(sacf, TCG_CALL_NO_WG, void, env, i64)
DEF_HELPER_FLAGS_3(ipte, TCG_CALL_NO_RWG, void, env, i64, i64)
DEF_HELPER_FLAGS_1(ptlb, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_FLAGS_1(purge, TCG_CALL_NO_RWG, void, env)
DEF_HELPER_2(lra, i64, env, i64)
DEF_HELPER_FLAGS_2(lura, TCG_CALL_NO_WG, i64, env, i64)
DEF_HELPER_FLAGS_2(lurag, TCG_CALL_NO_WG, i64, env, i64)

View File

@ -837,7 +837,7 @@
#ifndef CONFIG_USER_ONLY
/* COMPARE AND SWAP AND PURGE */
C(0xb250, CSP, RRE, Z, 0, ra2, 0, 0, csp, 0)
D(0xb250, CSP, RRE, Z, r1_32u, ra2, r1_P, 0, csp, 0, MO_TEUL)
/* DIAGNOSE (KVM hypercall) */
C(0x8300, DIAG, RSI, Z, 0, 0, 0, 0, diag, 0)
/* INSERT STORAGE KEY EXTENDED */

View File

@ -1056,30 +1056,6 @@ uint32_t HELPER(rrbe)(CPUS390XState *env, uint64_t r2)
return re >> 1;
}
/* compare and swap and purge */
uint32_t HELPER(csp)(CPUS390XState *env, uint32_t r1, uint64_t r2)
{
S390CPU *cpu = s390_env_get_cpu(env);
uint32_t cc;
uint32_t o1 = env->regs[r1];
uint64_t a2 = r2 & ~3ULL;
uint32_t o2 = cpu_ldl_data(env, a2);
if (o1 == o2) {
cpu_stl_data(env, a2, env->regs[(r1 + 1) & 15]);
if (r2 & 0x3) {
/* flush TLB / ALB */
tlb_flush(CPU(cpu));
}
cc = 0;
} else {
env->regs[r1] = (env->regs[r1] & 0xffffffff00000000ULL) | o2;
cc = 1;
}
return cc;
}
uint32_t HELPER(mvcs)(CPUS390XState *env, uint64_t l, uint64_t a1, uint64_t a2)
{
uintptr_t ra = GETPC();
@ -1161,6 +1137,14 @@ void HELPER(ptlb)(CPUS390XState *env)
tlb_flush(CPU(cpu));
}
/* flush global tlb */
void HELPER(purge)(CPUS390XState *env)
{
S390CPU *cpu = s390_env_get_cpu(env);
tlb_flush_all_cpus_synced(CPU(cpu));
}
/* load using real address */
uint64_t HELPER(lura)(CPUS390XState *env, uint64_t addr)
{

View File

@ -2006,11 +2006,45 @@ static ExitStatus op_cdsg(DisasContext *s, DisasOps *o)
#ifndef CONFIG_USER_ONLY
static ExitStatus op_csp(DisasContext *s, DisasOps *o)
{
TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1));
TCGMemOp mop = s->insn->data;
TCGv_i64 addr, old, cc;
TCGLabel *lab = gen_new_label();
/* Note that in1 = R1 (zero-extended expected value),
out = R1 (original reg), out2 = R1+1 (new value). */
check_privileged(s);
gen_helper_csp(cc_op, cpu_env, r1, o->in2);
tcg_temp_free_i32(r1);
set_cc_static(s);
addr = tcg_temp_new_i64();
old = tcg_temp_new_i64();
tcg_gen_andi_i64(addr, o->in2, -1ULL << (mop & MO_SIZE));
tcg_gen_atomic_cmpxchg_i64(old, addr, o->in1, o->out2,
get_mem_index(s), mop | MO_ALIGN);
tcg_temp_free_i64(addr);
/* Are the memory and expected values (un)equal? */
cc = tcg_temp_new_i64();
tcg_gen_setcond_i64(TCG_COND_NE, cc, o->in1, old);
tcg_gen_extrl_i64_i32(cc_op, cc);
/* Write back the output now, so that it happens before the
following branch, so that we don't need local temps. */
if ((mop & MO_SIZE) == MO_32) {
tcg_gen_deposit_i64(o->out, o->out, old, 0, 32);
} else {
tcg_gen_mov_i64(o->out, old);
}
tcg_temp_free_i64(old);
/* If the comparison was equal, and the LSB of R2 was set,
then we need to flush the TLB (for all cpus). */
tcg_gen_xori_i64(cc, cc, 1);
tcg_gen_and_i64(cc, cc, o->in2);
tcg_gen_brcondi_i64(TCG_COND_EQ, cc, 0, lab);
tcg_temp_free_i64(cc);
gen_helper_purge(cpu_env);
gen_set_label(lab);
return NO_EXIT;
}
#endif