Skip to content

Commit b1e7cee

Browse files
puranjaymohanmpe
authored andcommitted
powerpc/bpf: enforce full ordering for ATOMIC operations with BPF_FETCH
The Linux Kernel Memory Model [1][2] requires RMW operations that have a return value to be fully ordered. BPF atomic operations with BPF_FETCH (including BPF_XCHG and BPF_CMPXCHG) return a value back so they need to be JITed to fully ordered operations. POWERPC currently emits relaxed operations for these. We can show this by running the following litmus-test: PPC SB+atomic_add+fetch { 0:r0=x; (* dst reg assuming offset is 0 *) 0:r1=2; (* src reg *) 0:r2=1; 0:r4=y; (* P0 writes to this, P1 reads this *) 0:r5=z; (* P1 writes to this, P0 reads this *) 0:r6=0; 1:r2=1; 1:r4=y; 1:r5=z; } P0 | P1 ; stw r2, 0(r4) | stw r2,0(r5) ; | ; loop:lwarx r3, r6, r0 | ; mr r8, r3 | ; add r3, r3, r1 | sync ; stwcx. r3, r6, r0 | ; bne loop | ; mr r1, r8 | ; | ; lwa r7, 0(r5) | lwa r7,0(r4) ; ~exists(0:r7=0 /\ 1:r7=0) Witnesses Positive: 9 Negative: 3 Condition ~exists (0:r7=0 /\ 1:r7=0) Observation SB+atomic_add+fetch Sometimes 3 9 This test shows that the older store in P0 is reordered with a newer load to a different address. Although there is a RMW operation with fetch between them. Adding a sync before and after RMW fixes the issue: Witnesses Positive: 9 Negative: 0 Condition ~exists (0:r7=0 /\ 1:r7=0) Observation SB+atomic_add+fetch Never 0 9 [1] https://www.kernel.org/doc/Documentation/memory-barriers.txt [2] https://www.kernel.org/doc/Documentation/atomic_t.txt Fixes: aea7ef8 ("powerpc/bpf/32: add support for BPF_ATOMIC bitwise operations") Fixes: 2d9206b ("powerpc/bpf/32: Add instructions for atomic_[cmp]xchg") Fixes: dbe6e24 ("powerpc/bpf/64: add support for atomic fetch operations") Fixes: 1e82dfa ("powerpc/bpf/64: Add instructions for atomic_[cmp]xchg") Cc: [email protected] # v6.0+ Signed-off-by: Puranjay Mohan <[email protected]> Reviewed-by: Christophe Leroy <[email protected]> Reviewed-by: Naveen N Rao <[email protected]> Acked-by: Paul E. McKenney <[email protected]> Signed-off-by: Michael Ellerman <[email protected]> Link: https://msgid.link/[email protected]
1 parent 1613e60 commit b1e7cee

File tree

2 files changed

+24
-0
lines changed

2 files changed

+24
-0
lines changed

arch/powerpc/net/bpf_jit_comp32.c

+12
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,15 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code
900900

901901
/* Get offset into TMP_REG */
902902
EMIT(PPC_RAW_LI(tmp_reg, off));
903+
/*
904+
* Enforce full ordering for operations with BPF_FETCH by emitting a 'sync'
905+
* before and after the operation.
906+
*
907+
* This is a requirement in the Linux Kernel Memory Model.
908+
* See __cmpxchg_u32() in asm/cmpxchg.h as an example.
909+
*/
910+
if ((imm & BPF_FETCH) && IS_ENABLED(CONFIG_SMP))
911+
EMIT(PPC_RAW_SYNC());
903912
tmp_idx = ctx->idx * 4;
904913
/* load value from memory into r0 */
905914
EMIT(PPC_RAW_LWARX(_R0, tmp_reg, dst_reg, 0));
@@ -953,6 +962,9 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code
953962

954963
/* For the BPF_FETCH variant, get old data into src_reg */
955964
if (imm & BPF_FETCH) {
965+
/* Emit 'sync' to enforce full ordering */
966+
if (IS_ENABLED(CONFIG_SMP))
967+
EMIT(PPC_RAW_SYNC());
956968
EMIT(PPC_RAW_MR(ret_reg, ax_reg));
957969
if (!fp->aux->verifier_zext)
958970
EMIT(PPC_RAW_LI(ret_reg - 1, 0)); /* higher 32-bit */

arch/powerpc/net/bpf_jit_comp64.c

+12
Original file line numberDiff line numberDiff line change
@@ -846,6 +846,15 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code
846846

847847
/* Get offset into TMP_REG_1 */
848848
EMIT(PPC_RAW_LI(tmp1_reg, off));
849+
/*
850+
* Enforce full ordering for operations with BPF_FETCH by emitting a 'sync'
851+
* before and after the operation.
852+
*
853+
* This is a requirement in the Linux Kernel Memory Model.
854+
* See __cmpxchg_u64() in asm/cmpxchg.h as an example.
855+
*/
856+
if ((imm & BPF_FETCH) && IS_ENABLED(CONFIG_SMP))
857+
EMIT(PPC_RAW_SYNC());
849858
tmp_idx = ctx->idx * 4;
850859
/* load value from memory into TMP_REG_2 */
851860
if (size == BPF_DW)
@@ -908,6 +917,9 @@ int bpf_jit_build_body(struct bpf_prog *fp, u32 *image, u32 *fimage, struct code
908917
PPC_BCC_SHORT(COND_NE, tmp_idx);
909918

910919
if (imm & BPF_FETCH) {
920+
/* Emit 'sync' to enforce full ordering */
921+
if (IS_ENABLED(CONFIG_SMP))
922+
EMIT(PPC_RAW_SYNC());
911923
EMIT(PPC_RAW_MR(ret_reg, _R0));
912924
/*
913925
* Skip unnecessary zero-extension for 32-bit cmpxchg.

0 commit comments

Comments
 (0)