[PATCH 1/3] target/riscv: Fix RCsc ordering of Zalasr store-release

LIU Zhiwei <[email protected]>
Newsgroups org.nongnu.qemu-arm,org.nongnu.qemu-devel,org.nongnu.qemu-riscv
Message-ID <[email protected]>
The Zalasr load-acquire instructions carry an acquire-RCsc annotation
and the store-release instructions carry a release-RCsc annotation;
the extension has no RCpc variant (Zalasr specification, chapter 2).

Per Rule 7 of the RVWMO preserved program order rules ("a and b both
have RCsc annotations"), a Zalasr store-release must precede any
subsequent RCsc memory operation of the same hart in the global
memory order.  In particular "sw.rl X=1; lw.aq r1=Y" must be ordered,
which is what forbids the [0,0] outcome of a Store Buffering litmus
test that runs this pair on two harts.

gen_store_release() only emits a barrier before the store, which
orders the memory operations preceding the store before the store
itself (Rule 6, release semantics).  Nothing prevents the store from
being reordered with the memory operations that follow it, so under
MTTCG the host CPU may execute the load-acquire that immediately
follows a store-release before the store has left its store buffer:

  * on an x86 host (TSO), store-load is the only reordering the
    hardware may perform, and it is exactly the ordering that is
    missing here;

  * on an aarch64 host, no dmb follows the store either.

The defect is easily observed with a bare-metal Store Buffering
litmus test on a 4 vCPU guest: "sw.rl X=1; lw.aq r1=Y" on hart 0 and
"sw.rl Y=1; lw.aq r2=X" on hart 1 produce the architecturally
forbidden [0,0] result in 89881 out of 100000 iterations.

Fix this by emitting a full barrier after the store as well, which
mirrors the FENCE translation.  The TCG_MO_ST_LD bit included in
TCG_MO_ALL makes the x86 backend emit a serializing lock-prefixed
instruction after the store, draining the store buffer before any
subsequent load executes, and the aarch64 backend emit dmb ish; the
store therefore becomes globally visible before any subsequent memory
operation of the same vCPU, and Rule 7 is satisfied.  This is
stronger than Rule 7 strictly requires, since the barrier also orders
the store with respect to subsequent non-RCsc operations, but this
matches how QEMU already implements FENCE and keeps the fix simple.

The load-acquire side needs no change: the barrier emitted after the
load already orders it with all subsequent memory operations, which
covers Rule 5 and the remaining Rule 7 combinations (load-load and
load-store).

Signed-off-by: LIU Zhiwei <[email protected]>
Co-authored-by: TANG TianCheng <[email protected]>
Signed-off-by: TANG TianCheng <[email protected]>
---
 target/riscv/tcg/insn_trans/trans_rvzalasr.c.inc | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/target/riscv/tcg/insn_trans/trans_rvzalasr.c.inc b/target/riscv/tcg/insn_trans/trans_rvzalasr.c.inc
index 79b0b2c63b..28d9b13c55 100644
--- a/target/riscv/tcg/insn_trans/trans_rvzalasr.c.inc
+++ b/target/riscv/tcg/insn_trans/trans_rvzalasr.c.inc
@@ -86,6 +86,14 @@ static bool gen_store_release(DisasContext *ctx, arg_sb_aqrl *a, MemOp memop)
     tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL | bar);
 
     tcg_gen_qemu_st_tl(data, addr, ctx->mem_idx, memop);
+
+    /*
+     * Zalasr annotations are always RCsc: per RVWMO Rule 7, the store
+     * must also be ordered before any subsequent memory operation,
+     * e.g. a load-acquire in a Store Buffering litmus test.  The
+     * barrier above only orders previous memory ops before the store.
+     */
+    tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC);
     return true;
 }
 
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.