bpf, riscv: Add and use bpf_atomic_is_load_acq() helper

A load-acquire is the only BPF_STX class instruction that reads from
src_reg into dst_reg, that is, it has the operand roles of a BPF_LDX.
JIT code which tells loads from stores apart by instruction class alone
has to special case it, for example when deciding which register holds
the faulting address and which one to clear from an exception handler.

riscv64 already does so, open coded as a bare insn->imm test. Add a
bpf_atomic_is_load_acq() helper and convert riscv64 over to it, so that
the x86-64 and arm64 JITs can use the same helper in subsequent patches.

Unlike bpf_atomic_is_load_store(), which presumes that its argument is
already known to be a BPF_ATOMIC instruction, the new helper is called
from code which still sees all instruction classes, so it checks class
and mode itself.

Also, move bpf_atomic_is_load_store() to filter.h next to BPF_ATOMIC_OP,
so that both helpers stay together. No functional change intended.

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/bpf/20260806201047.333389-2-daniel@iogearbox.net
Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
This commit is contained in:
Daniel Borkmann 2026-08-06 22:10:43 +02:00 committed by Kumar Kartikeya Dwivedi
parent 7db0a00445
commit e2577cd620
No known key found for this signature in database
GPG Key ID: 472D377B63542F83
3 changed files with 32 additions and 16 deletions

View File

@ -1994,7 +1994,7 @@ int bpf_jit_emit_insn(const struct bpf_insn *insn, struct rv_jit_context *ctx,
/* ret can be 1 (skip-zext); extable entry still needs to be added */
if (ret >= 0)
ret = add_exception_handler(insn,
insn->imm == BPF_LOAD_ACQ ? rd : REG_DONT_CLEAR_MARKER,
bpf_atomic_is_load_acq(insn) ? rd : REG_DONT_CLEAR_MARKER,
ctx) ?: ret;
if (ret)

View File

@ -1132,21 +1132,6 @@ static inline bool bpf_pseudo_func(const struct bpf_insn *insn)
return bpf_is_ldimm64(insn) && insn->src_reg == BPF_PSEUDO_FUNC;
}
/* Given a BPF_ATOMIC instruction @atomic_insn, return true if it is an
* atomic load or store, and false if it is a read-modify-write instruction.
*/
static inline bool
bpf_atomic_is_load_store(const struct bpf_insn *atomic_insn)
{
switch (atomic_insn->imm) {
case BPF_LOAD_ACQ:
case BPF_STORE_REL:
return true;
default:
return false;
}
}
struct bpf_prog_ops {
int (*test_run)(struct bpf_prog *prog, const union bpf_attr *kattr,
union bpf_attr __user *uattr);

View File

@ -383,6 +383,37 @@ static inline bool insn_is_cast_user(const struct bpf_insn *insn)
/* Legacy alias */
#define BPF_STX_XADD(SIZE, DST, SRC, OFF) BPF_ATOMIC_OP(SIZE, BPF_ADD, DST, SRC, OFF)
/*
* Given a BPF_ATOMIC instruction @atomic_insn, return true if it is an
* atomic load or store, and false if it is a read-modify-write instruction.
*/
static inline bool
bpf_atomic_is_load_store(const struct bpf_insn *atomic_insn)
{
switch (atomic_insn->imm) {
case BPF_LOAD_ACQ:
case BPF_STORE_REL:
return true;
default:
return false;
}
}
/*
* A load-acquire is the only BPF_STX class instruction that reads into
* dst_reg from src_reg + off16, i.e. it has the operand roles of a BPF_LDX.
* Unlike bpf_atomic_is_load_store(), @insn is not assumed to be a BPF_ATOMIC
* instruction here, so that callers which walk all instruction classes can
* use this directly.
*/
static inline bool bpf_atomic_is_load_acq(const struct bpf_insn *insn)
{
return BPF_CLASS(insn->code) == BPF_STX &&
(BPF_MODE(insn->code) == BPF_ATOMIC ||
BPF_MODE(insn->code) == BPF_PROBE_ATOMIC) &&
insn->imm == BPF_LOAD_ACQ;
}
/* Memory store, *(uint *) (dst_reg + off16) = imm32 */
#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \