All of lore.kernel.org
 help / color / mirror / Atom feed
From: Eduard Zingerman <eddyz87@gmail.com>
To: Kumar Kartikeya Dwivedi <memxor@gmail.com>, bpf@vger.kernel.org
Cc: Tejun Heo <tj@kernel.org>, Alexei Starovoitov <ast@kernel.org>,
	Andrii Nakryiko <andrii@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	Emil Tsalapatis	 <emil@etsalapatis.com>,
	kkd@meta.com, kernel-team@meta.com
Subject: Re: [PATCH bpf-next v3 2/9] bpf: Support __arena and __arena_nullable on struct_ops arguments
Date: Tue, 04 Aug 2026 22:14:41 -0700	[thread overview]
Message-ID: <0a7bc56a3753c9122ab421aa14d2daae08b08e30.camel@gmail.com> (raw)
In-Reply-To: <018d033499f61651ec8761fe0f7ac9662ef3c096.camel@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 3822 bytes --]

On Tue, 2026-08-04 at 16:55 -0700, Eduard Zingerman wrote:
> On Mon, 2026-08-03 at 14:51 +0200, Kumar Kartikeya Dwivedi wrote:
> 
> Lgtm except for several nits below.
> 
> ...
> 
> > diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c
> 
> ...
> 
> > @@ -226,26 +229,30 @@ static int prepare_arg_info(struct btf *btf,
> >  	info = info_buf;
> >  	for (arg_no = 0; arg_no < nargs; arg_no++) {
> >  		/* Skip arguments that is not suffixed with
> > -		 * "__nullable or __ref".
> > +		 * "__nullable", "__ref", "__arena" or "__arena_nullable".
> >  		 */
> >  		is_nullable = btf_param_match_suffix(btf, &stub_args[arg_no],
> >  						     MAYBE_NULL_SUFFIX);
> >  		is_refcounted = btf_param_match_suffix(btf, &stub_args[arg_no],
> >  						       REFCOUNTED_SUFFIX);
> > +		is_arena_nullable = btf_param_match_suffix(btf, &stub_args[arg_no],
> > +							   ARENA_MAYBE_NULL_SUFFIX);
> > +		is_arena = btf_param_match_suffix(btf, &stub_args[arg_no], ARENA_SUFFIX);
> >  
> >  		if (is_nullable)
> >  			suffix = MAYBE_NULL_SUFFIX;
> >  		else if (is_refcounted)
> >  			suffix = REFCOUNTED_SUFFIX;
> > +		else if (is_arena_nullable)
> > +			suffix = ARENA_MAYBE_NULL_SUFFIX;
> > +		else if (is_arena)
> > +			suffix = ARENA_SUFFIX;
> 
> As discussed already, that's unfortunate that p__arena and p__nullable
> are valid, but p__arena__nullable is not, substituted by
> p__arena_nullable. Same goes for global subprogs.
> 
> >  		else
> >  			continue;
> >  
> > -		/* Should be a pointer to struct */
> > -		pointed_type = btf_type_resolve_ptr(btf,
> > -						    args[arg_no].type,
> > -						    &arg_btf_id);
> > -		if (!pointed_type ||
> > -		    !btf_type_is_struct(pointed_type)) {
> > +		/* Should be a pointer to struct, or any pointer for __arena/__arena_nullable */
> > +		pointed_type = btf_type_resolve_ptr(btf, args[arg_no].type, &arg_btf_id);
> > +		if (!pointed_type || (!is_arena && !is_arena_nullable && !btf_type_is_struct(pointed_type))) {
> >  			pr_warn("stub function %s has %s tagging to an unsupported type\n",
> >  				stub_fname, suffix);
> >  			goto err_out;
> 
> ...
> 
> > diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
> > index ed7999ad6c66..d2d7a2904345 100644
> > --- a/kernel/bpf/trampoline.c
> > +++ b/kernel/bpf/trampoline.c
> 
> ...
> 
> > @@ -695,6 +743,22 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut
> >  		goto out;
> >  	}
> >  
> > +	/*
> > +	 * Arena ctx args are converted only by the struct_ops indirect
> > +	 * trampoline, which dispatches to a single known prog. Generic
> > +	 * trampolines can mix progs with different arenas, so no conversion
> > +	 * is possible here. Not reachable today: only struct_ops progs get
> > +	 * arena ctx args and they never ride generic trampolines.
> > +	 */
> > +	for (kind = 0; kind < BPF_TRAMP_MAX; kind++) {
> > +		for (i = 0; i < tnodes[kind].nr_nodes; i++) {
> > +			if (bpf_prog_has_arena_ctx_arg(tnodes[kind].nodes[i]->link->prog)) {
> > +				err = -ENOTSUPP;
> > +				goto out;
> > +			}
> > +		}
> > +	}
> 
> Nit: should this be done in __bpf_trampoline_link_prog() or
>      bpf_trampoline_add_prog()? To avoid doing the same check on unlink.
>      Since this is an invariant violation, do we want to add WARN_ONCE?
> 
> > +
> >  	/* clear all bits except SHARE_IPMODIFY and TAIL_CALL_CTX */
> >  	tr->flags &= (BPF_TRAMP_F_SHARE_IPMODIFY | BPF_TRAMP_F_TAIL_CALL_CTX);
> >  
> 
> ...

Thinking a bit more about this. Since kfuncs already use flags in
btf_func_model, I think it would be nice to use them for struct_ops as
well. E.g. as in the patch attached (applied on top of this series).
Wdyt?

[-- Attachment #2: struct-ops-flags.diff --]
[-- Type: text/x-patch, Size: 9423 bytes --]

diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
index 4b349ae35ebf..dcc68eb5632a 100644
--- a/arch/x86/net/bpf_jit_comp.c
+++ b/arch/x86/net/bpf_jit_comp.c
@@ -3071,12 +3071,12 @@ static void emit_arena_arg_conv(u8 **pprog, u32 src_reg, bool nullable, u32 base
 
 static void save_args(const struct btf_func_model *m, u8 **prog,
 		      int stack_size, bool for_call_origin, u32 flags,
-		      const struct bpf_tramp_arena_args *aargs)
+		      u64 arena_base)
 {
 	int arg_regs, first_off = 0, nr_regs = 0, nr_stack_slots = 0;
 	bool use_jmp = bpf_trampoline_use_jmp(flags);
 	int stack_args_off = (use_jmp || (flags & BPF_TRAMP_F_INDIRECT)) ? 16 : 24;
-	int i, j, slot = 0;
+	int i, j;
 
 	/* Store function arguments to stack.
 	 * For a function that accepts two pointers the sequence will be:
@@ -3084,6 +3084,9 @@ static void save_args(const struct btf_func_model *m, u8 **prog,
 	 * mov QWORD PTR [rbp-0x8],rsi
 	 */
 	for (i = 0; i < min_t(int, m->nr_args, MAX_BPF_FUNC_ARGS); i++) {
+		bool arena_arg = arena_base && (m->arg_flags[i] & BTF_FMODEL_ARENA_ARG);
+		bool nullable = m->arg_flags[i] & BTF_FMODEL_NULLABLE_ARG;
+
 		arg_regs = (m->arg_size[i] + 7) / 8;
 
 		/* According to the research of Yonghong, struct members
@@ -3117,10 +3120,9 @@ static void save_args(const struct btf_func_model *m, u8 **prog,
 			for (j = 0; j < arg_regs; j++) {
 				emit_ldx(prog, BPF_DW, BPF_REG_0, BPF_REG_FP,
 					 nr_stack_slots * 8 + stack_args_off);
-				if (aargs && (aargs->slots & BIT(slot)))
-					emit_arena_arg_conv(prog, BPF_REG_0,
-							    aargs->nullable_slots & BIT(slot),
-							    (u32)aargs->kern_vm_start);
+				if (arena_arg)
+					emit_arena_arg_conv(prog, BPF_REG_0, nullable,
+							    (u32)arena_base);
 				emit_stx(prog, BPF_DW, BPF_REG_FP, BPF_REG_0,
 					 -stack_size);
 
@@ -3128,7 +3130,6 @@ static void save_args(const struct btf_func_model *m, u8 **prog,
 					first_off = stack_size;
 				stack_size -= 8;
 				nr_stack_slots++;
-				slot++;
 			}
 		} else {
 			/* Only copy the arguments on-stack to current
@@ -3137,7 +3138,6 @@ static void save_args(const struct btf_func_model *m, u8 **prog,
 			 */
 			if (for_call_origin) {
 				nr_regs += arg_regs;
-				slot += arg_regs;
 				continue;
 			}
 
@@ -3145,16 +3145,13 @@ static void save_args(const struct btf_func_model *m, u8 **prog,
 			for (j = 0; j < arg_regs; j++) {
 				u32 src = nr_regs == 5 ? X86_REG_R9 : BPF_REG_1 + nr_regs;
 
-				if (aargs && (aargs->slots & BIT(slot))) {
-					emit_arena_arg_conv(prog, src,
-							    aargs->nullable_slots & BIT(slot),
-							    (u32)aargs->kern_vm_start);
+				if (arena_arg) {
+					emit_arena_arg_conv(prog, src, nullable, (u32)arena_base);
 					src = BPF_REG_0;
 				}
 				emit_stx(prog, BPF_DW, BPF_REG_FP, src, -stack_size);
 				stack_size -= 8;
 				nr_regs++;
-				slot++;
 			}
 		}
 	}
@@ -3445,13 +3442,12 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
 	struct bpf_tramp_nodes *fentry = &tnodes[BPF_TRAMP_FENTRY];
 	struct bpf_tramp_nodes *fexit = &tnodes[BPF_TRAMP_FEXIT];
 	struct bpf_tramp_nodes *fmod_ret = &tnodes[BPF_TRAMP_MODIFY_RETURN];
-	struct bpf_tramp_arena_args aargs;
 	void *orig_call = func_addr;
 	int cookie_off, cookie_cnt;
 	u8 **branches = NULL;
+	u64 arena_base;
 	u64 func_meta;
 	u8 *prog;
-	bool has_aargs;
 	bool save_ret;
 
 	/*
@@ -3462,7 +3458,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
 	WARN_ON_ONCE((flags & BPF_TRAMP_F_INDIRECT) &&
 		     (flags & ~(BPF_TRAMP_F_INDIRECT | BPF_TRAMP_F_RET_FENTRY_RET)));
 
-	has_aargs = bpf_tramp_collect_arena_args(tnodes, flags, &aargs);
+	arena_base = bpf_tramp_arena_base(m, tnodes, flags);
 
 	/* extra registers for struct arguments */
 	for (i = 0; i < m->nr_args; i++) {
@@ -3601,8 +3597,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
 		emit_store_stack_imm64(&prog, BPF_REG_0, -ip_off, (long)func_addr);
 	}
 
-	save_args(m, &prog, regs_off, false, flags,
-		  has_aargs ? &aargs : NULL);
+	save_args(m, &prog, regs_off, false, flags, arena_base);
 
 	if (flags & BPF_TRAMP_F_CALL_ORIG) {
 		/* arg1: mov rdi, im */
@@ -3644,7 +3639,7 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
 
 	if (flags & BPF_TRAMP_F_CALL_ORIG) {
 		restore_regs(m, &prog, regs_off);
-		save_args(m, &prog, arg_stack_off, true, flags, NULL);
+		save_args(m, &prog, arg_stack_off, true, flags, 0);
 
 		if (flags & BPF_TRAMP_F_TAIL_CALL_CTX) {
 			/* Before calling the original function, load the
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index c839f039729a..a959efc3468f 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1293,18 +1293,13 @@ struct bpf_tramp_nodes {
 };
 
 /*
- * Which 8-byte ctx slots of a struct_ops trampoline hold arena kernel
- * pointers that save_args() converts to the arena pointer form,
- * ctx[slot] = (u32)(kaddr - kern_vm_start).
+ * The arena base against which a struct_ops trampoline converts the
+ * arguments marked with BTF_FMODEL_ARENA_ARG while saving them into the BPF
+ * ctx, ctx[arg] = (u32)(kaddr - kern_vm_start). Zero when the trampoline
+ * converts nothing.
  */
-struct bpf_tramp_arena_args {
-	u32 slots;
-	u32 nullable_slots;	/* subset of @slots where NULL is preserved */
-	u64 kern_vm_start;
-};
-
-bool bpf_tramp_collect_arena_args(struct bpf_tramp_nodes *tnodes, u32 flags,
-				  struct bpf_tramp_arena_args *aargs);
+u64 bpf_tramp_arena_base(const struct btf_func_model *m,
+			 struct bpf_tramp_nodes *tnodes, u32 flags);
 
 struct bpf_tramp_run_ctx;
 
@@ -1707,11 +1702,6 @@ struct bpf_ctx_arg_aux {
 	u32 btf_id;
 	u32 ref_id;
 	bool refcounted;
-	/*
-	 * We don't encode NULL-ness in the type for the program, but still need
-	 * to distinguish it for the purposes of telling JITs what sequence to emit.
-	 */
-	bool arena_nullable;
 };
 
 struct btf_mod_pair {
diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c
index 827a6216a620..c6a7c0213bb0 100644
--- a/kernel/bpf/bpf_struct_ops.c
+++ b/kernel/bpf/bpf_struct_ops.c
@@ -174,6 +174,7 @@ static int prepare_arg_info(struct btf *btf,
 			    const char *st_ops_name,
 			    const char *member_name,
 			    const struct btf_type *func_proto, void *stub_func_addr,
+			    struct btf_func_model *model,
 			    struct bpf_struct_ops_arg_info *arg_info)
 {
 	const struct btf_type *stub_func_proto, *pointed_type;
@@ -288,7 +289,9 @@ static int prepare_arg_info(struct btf *btf,
 			 * precision around it, since it has no safety implication.
 			 */
 			info->reg_type = PTR_TO_ARENA;
-			info->arena_nullable = is_arena_nullable;
+			model->arg_flags[arg_no] |= BTF_FMODEL_ARENA_ARG;
+			if (is_arena_nullable)
+				model->arg_flags[arg_no] |= BTF_FMODEL_NULLABLE_ARG;
 		}
 
 		info++;
@@ -476,6 +479,7 @@ int bpf_struct_ops_desc_init(struct bpf_struct_ops_desc *st_ops_desc,
 		stub_func_addr = *(void **)(st_ops->cfi_stubs + moff);
 		err = prepare_arg_info(btf, st_ops->name, mname,
 				       func_proto, stub_func_addr,
+				       &st_ops->func_models[i],
 				       arg_info + i);
 		if (err)
 			goto errout;
diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index d2d7a2904345..412a7d32e94a 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -540,40 +540,33 @@ static bool bpf_prog_has_arena_ctx_arg(const struct bpf_prog *prog)
 }
 
 /*
- * Collect which ctx slots of a struct_ops trampoline hold arena kernel
- * pointers that save_args() must convert to the arena pointer form. Only
- * the struct_ops indirect trampoline converts: it dispatches to a single
- * prog whose arena is known at generation time. Return false when there
- * is nothing to convert.
+ * The arena base against which save_args() converts the arguments marked
+ * with BTF_FMODEL_ARENA_ARG. Only the struct_ops indirect trampoline
+ * converts: it dispatches to a single prog whose arena is known at
+ * generation time. Return 0 when there is nothing to convert.
  */
-bool bpf_tramp_collect_arena_args(struct bpf_tramp_nodes *tnodes, u32 flags,
-				  struct bpf_tramp_arena_args *aargs)
+u64 bpf_tramp_arena_base(const struct btf_func_model *m,
+			 struct bpf_tramp_nodes *tnodes, u32 flags)
 {
 	const struct bpf_prog *prog;
 	int i;
 
-	memset(aargs, 0, sizeof(*aargs));
-
 	if (!(flags & BPF_TRAMP_F_INDIRECT) ||
 	    tnodes[BPF_TRAMP_FENTRY].nr_nodes != 1)
-		return false;
+		return 0;
 
-	prog = tnodes[BPF_TRAMP_FENTRY].nodes[0]->link->prog;
-	for (i = 0; i < prog->aux->ctx_arg_info_size; i++) {
-		const struct bpf_ctx_arg_aux *info = &prog->aux->ctx_arg_info[i];
+	for (i = 0; i < m->nr_args; i++)
+		if (m->arg_flags[i] & BTF_FMODEL_ARENA_ARG)
+			break;
+	if (i == m->nr_args)
+		return 0;
 
-		if (base_type(info->reg_type) != PTR_TO_ARENA)
-			continue;
-		aargs->slots |= BIT(info->offset / 8);
-		if (info->arena_nullable)
-			aargs->nullable_slots |= BIT(info->offset / 8);
-	}
-	if (!aargs->slots)
-		return false;
+	/* Verification rejects an arena argument without an arena. */
+	prog = tnodes[BPF_TRAMP_FENTRY].nodes[0]->link->prog;
 	if (WARN_ON_ONCE(!prog->aux->arena))
-		return false;
-	aargs->kern_vm_start = bpf_arena_get_kern_vm_start(prog->aux->arena);
-	return true;
+		return 0;
+
+	return bpf_arena_get_kern_vm_start(prog->aux->arena);
 }
 
 static void bpf_tramp_image_free(struct bpf_tramp_image *im)

  reply	other threads:[~2026-08-05  5:14 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-03 12:51 [PATCH bpf-next v3 0/9] Add arena argument support to kfuncs and struct_ops Kumar Kartikeya Dwivedi
2026-08-03 12:51 ` [PATCH bpf-next v3 1/9] bpf: Support __arena and __arena_nullable kfunc argument suffixes Kumar Kartikeya Dwivedi
2026-08-03 13:19   ` sashiko-bot
2026-08-04 17:42   ` Amery Hung
2026-08-05 15:53     ` Kumar Kartikeya Dwivedi
2026-08-05 17:07       ` Amery Hung
2026-08-05 17:16         ` Kumar Kartikeya Dwivedi
2026-08-05 15:55     ` Kumar Kartikeya Dwivedi
2026-08-03 12:51 ` [PATCH bpf-next v3 2/9] bpf: Support __arena and __arena_nullable on struct_ops arguments Kumar Kartikeya Dwivedi
2026-08-03 14:19   ` sashiko-bot
2026-08-04 23:55   ` Eduard Zingerman
2026-08-05  5:14     ` Eduard Zingerman [this message]
2026-08-05 17:31       ` Amery Hung
2026-08-05 17:36         ` Kumar Kartikeya Dwivedi
2026-08-03 12:51 ` [PATCH bpf-next v3 3/9] bpf, x86: JIT __arena kfunc argument rebasing Kumar Kartikeya Dwivedi
2026-08-05  0:40   ` Eduard Zingerman
2026-08-03 12:51 ` [PATCH bpf-next v3 4/9] bpf, x86: Convert struct_ops arena arguments in the trampoline Kumar Kartikeya Dwivedi
2026-08-03 12:51 ` [PATCH bpf-next v3 5/9] selftests/bpf: Add kfunc __arena and __arena_nullable argument tests Kumar Kartikeya Dwivedi
2026-08-03 13:35   ` sashiko-bot
2026-08-04 20:01   ` Eduard Zingerman
2026-08-04 20:14     ` Kumar Kartikeya Dwivedi
2026-08-04 20:24       ` Eduard Zingerman
2026-08-04 20:26         ` Eduard Zingerman
2026-08-04 20:28           ` Kumar Kartikeya Dwivedi
2026-08-04 20:33             ` Eduard Zingerman
2026-08-04 20:36               ` Eduard Zingerman
2026-08-03 12:51 ` [PATCH bpf-next v3 6/9] selftests/bpf: Add JIT-sequence tests for __arena kfunc arguments Kumar Kartikeya Dwivedi
2026-08-03 13:35   ` sashiko-bot
2026-08-05  5:43   ` Eduard Zingerman
2026-08-03 12:51 ` [PATCH bpf-next v3 7/9] selftests/bpf: Add struct_ops __arena and __arena_nullable argument tests Kumar Kartikeya Dwivedi
2026-08-03 13:39   ` sashiko-bot
2026-08-05  6:52   ` Eduard Zingerman
2026-08-03 12:51 ` [PATCH bpf-next v3 8/9] bpf, x86: Fix stack-passed arguments for indirect trampolines Kumar Kartikeya Dwivedi
2026-08-03 13:42   ` sashiko-bot
2026-08-05  8:00   ` Eduard Zingerman
2026-08-03 12:51 ` [PATCH bpf-next v3 9/9] selftests/bpf: Test stack-passed struct_ops arena arguments Kumar Kartikeya Dwivedi
2026-08-05  8:00   ` Eduard Zingerman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=0a7bc56a3753c9122ab421aa14d2daae08b08e30.camel@gmail.com \
    --to=eddyz87@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=emil@etsalapatis.com \
    --cc=kernel-team@meta.com \
    --cc=kkd@meta.com \
    --cc=memxor@gmail.com \
    --cc=tj@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.