[PATCH bpf-next v2 1/8] bpf: Add UHMUL and SHMUL instructions
Alexei Starovoitov
alexei.starovoitov at gmail.com
Wed Oct 21 10:11:42 PDT 2026
On Tue, Oct 20, 2026 at 10:40 PM tangyazhou at zju.edu.cn <tangyazhou at zju.edu.cn> wrote:
> +static inline bool bpf_insn_is_hmul(const struct bpf_insn *insn)
> +{
> + return BPF_CLASS(insn->code) == BPF_ALU64 &&
> + BPF_OP(insn->code) == BPF_MUL &&
> + bpf_is_hmul_variant(insn->off);
> +}
Patch 5 adds this check to 13 JITs and nfp to opt them out.
Lets make JITs opt in instead, like bpf_jit_supports_callx().
The verifier checks it once. x86, arm64 and riscv patches add
bpf_jit_supports_hmul() that returns true. bpf_prog_is_offloaded()
covers nfp.
When JIT is on and it doesn't support them the prog is rejected with
a message in the log instead of silent fallback to the interpreter.
Then patch 5 goes away and only arm64 and riscv acks are needed.
Like:
diff --git a/include/linux/filter.h b/include/linux/filter.h
index e0218e6..6ee8d1a 100644
--- a/include/linux/filter.h
+++ b/include/linux/filter.h
@@ -152,6 +152,13 @@ struct ctl_table_header;
#define BPF_ALU32_IMM(OP, DST, IMM) \
BPF_ALU32_IMM_OFF(OP, DST, IMM, 0)
+static inline bool bpf_insn_is_hmul(const struct bpf_insn *insn)
+{
+ return BPF_CLASS(insn->code) == BPF_ALU64 &&
+ BPF_OP(insn->code) == BPF_MUL &&
+ (insn->off == BPF_UHMUL || insn->off == BPF_SHMUL);
+}
+
/* Endianess conversion, cpu_to_{l,b}e(), {l,b}e_to_cpu() */
#define BPF_ENDIAN(TYPE, DST, LEN) \
@@ -1281,6 +1288,7 @@ bool bpf_jit_supports_kfunc_call(void);
bool bpf_jit_supports_callx(void);
bool bpf_jit_supports_kfunc_ret_reg_pair(void);
bool bpf_jit_supports_unwind(void);
+bool bpf_jit_supports_hmul(void);
bool bpf_jit_supports_stack_args(void);
bool bpf_jit_supports_arena_args(void);
bool bpf_jit_supports_far_kfunc_call(void);
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index f29b0aa..90c30e5 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -34,6 +34,12 @@
#define BPF_FROM_LE BPF_TO_LE
#define BPF_FROM_BE BPF_TO_BE
+/* BPF_MUL off values for high-half multiplication; BPF_ALU64 only. */
+enum bpf_mul_variant {
+ BPF_UHMUL = 1,
+ BPF_SHMUL = 2,
+};
+
/* jmp encodings */
#define BPF_JNE 0x50 /* jump != */
#define BPF_JLT 0xa0 /* LT is unsigned, '<' */
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
index 3030beb..e17704a 100644
--- a/kernel/bpf/core.c
+++ b/kernel/bpf/core.c
@@ -3405,6 +3405,12 @@ bool __weak bpf_jit_supports_unwind(void)
return false;
}
+/* uhmul and shmul */
+bool __weak bpf_jit_supports_hmul(void)
+{
+ return false;
+}
+
/*
* How this arch places a by-value kfunc argument, or NULL for one that has
* not opted in and so only takes an argument of a single eightbyte, which
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 5601d20..9a3c97d 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -21194,6 +21194,17 @@ static int fd_array_get_map_idx(struct bpf_verifier_env *env, u32 idx)
return -EPROTO;
}
+/* The interpreter supports uhmul and shmul. JITs opt in. */
+static int check_hmul_jit(struct bpf_verifier_env *env)
+{
+ if (bpf_prog_is_offloaded(env->prog->aux) ||
+ (env->prog->jit_requested && !bpf_jit_supports_hmul())) {
+ verbose(env, "JIT doesn't support uhmul and shmul\n");
+ return -EOPNOTSUPP;
+ }
+ return 0;
+}
+
static int check_alu_fields(struct bpf_verifier_env *env, struct bpf_insn *insn)
{
u8 class = BPF_CLASS(insn->code);
@@ -21249,6 +21260,13 @@ static int check_alu_fields(struct bpf_verifier_env *env, struct bpf_insn *insn)
case BPF_MUL:
case BPF_DIV:
case BPF_MOD:
+ if (bpf_insn_is_hmul(insn)) {
+ if (BPF_SRC(insn->code) == BPF_X ? insn->imm : insn->src_reg) {
+ verbose(env, "BPF_ALU uses reserved fields\n");
+ return -EINVAL;
+ }
+ return check_hmul_jit(env);
+ }
if (BPF_SRC(insn->code) == BPF_X) {
if (insn->imm != 0 || (insn->off != 0 && insn->off != 1) ||
(insn->off == 1 && opcode != BPF_MOD && opcode != BPF_DIV)) {
pw-bot: cr
More information about the linux-riscv
mailing list