[PATCH bpf-next v2 1/8] bpf: Add UHMUL and SHMUL instructions

Yazhou Tang tangyazhou at zju.edu.cn
Thu Oct 22 00:25:11 PDT 2026



On 10/22/26 01:11, Alexei Starovoitov wrote:
> 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.

Thanks, Alexei. This is a cleaner approach. I'll add
bpf_jit_supports_hmul() as you suggested and remove
the opt-out checks from unsupported JITs in v3.

Apologies for the noise to the other JIT maintainers.

> 
> 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