public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH bpf-next v1 00/10] bpf: Kernel functions with KF_IMPLICIT_ARGS
@ 2026-01-09 18:48 Ihor Solodrai
  2026-01-09 18:48 ` [PATCH bpf-next v1 01/10] bpf: Refactor btf_kfunc_id_set_contains Ihor Solodrai
                   ` (9 more replies)
  0 siblings, 10 replies; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 18:48 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

This series implements a generic "implicit arguments" feature for BPF
kernel functions.

This is the next iteration of previous work [1][2], although it's not
really a v3, as parts of the implementation have changed
significantly.

A mechanism is created for kfuncs to have arguments that are not
visible to the BPF programs, and are provided to the kernel function
implementation by the verifier.

This mechanism is then used to define new APIs, that use
KF_IMPLICIT_ARGS flag, for the kfuncs that have a parameter with
__prog annotation [3], which is the current way of passing struct
bpf_prog_aux pointer to kfuncs.

The key difference in comparison to earlier work is that the necessary
BTF modifications are done by resolve_btfids (patch #4) instead of
pahole. The groundwork for this to be possible has been landed
recently [4].

This approach simplifies the implementation and future maintenance as
pahole is an out-of-tree tool, while resolve_btfids is not.

Another difference is in handling of the "legacy" case. This
implementation is more strict: the usage of <kfunc>_impl functions in
BPF is only allowed for kfuncs with an explicit kernel (or kmodule)
declaration (that is, existing _impl functions [5]). A <kfunc>_impl
function generated in BTF for a kfunc with implicit args does not have
a "bpf_kfunc" decl tag, and a kernel address. The verifier will reject
a program trying to call such an _impl kfunc.

The function with implicit arguments is defined by KF_IMPLICIT_ARGS
flag in BTF_IDS_FLAGS set. In this series, only a pointer to struct
bpf_prog_aux can be implicit, although it is simple to extend this to
more types.

The verifier handles a kfunc with KF_IMPLICIT_ARGS by resolving it to
a different (actual) BTF prototype early in verification (patch #3).

The series consists of the following patches:
  - patches #1 and #2 are non-functional refactoring in kernel/bpf
  - patch #3 defines KF_IMPLICIT_ARGS flag and teaches the verifier
    about it, enabling __prog args to also be implicit
  - patch #4 implements the necessary btf2btf transformation in
    resolve_btfids
  - patch #5 adds selftests specific to KF_IMPLICIT_ARGS feature
  - patches #6-#9 update the current users of __prog argument to use
    KF_IMPLICIT_ARGS
  - patch #10 updates relevant documentation

[1] https://lore.kernel.org/bpf/20251029190113.3323406-1-ihor.solodrai@linux.dev/
[2] https://lore.kernel.org/bpf/20250924211716.1287715-1-ihor.solodrai@linux.dev/
[3] https://docs.kernel.org/bpf/kfuncs.html#prog-annotation
[4] https://lore.kernel.org/bpf/20251219181321.1283664-1-ihor.solodrai@linux.dev/
[5] https://lore.kernel.org/bpf/20251104-implv2-v3-0-4772b9ae0e06@meta.com/

---

Ihor Solodrai (10):
  bpf: Refactor btf_kfunc_id_set_contains
  bpf: Introduce struct bpf_kfunc_meta
  bpf: Verifier support for KF_IMPLICIT_ARGS
  resolve_btfids: Support for KF_IMPLICIT_ARGS
  selftests/bpf: Add tests for KF_IMPLICIT_ARGS
  bpf: Add bpf_wq_set_callback kfunc with KF_IMPLICIT_ARGS
  HID: Use bpf_wq_set_callback kernel function
  bpf: Add bpf_task_work_schedule_* kfuncs with KF_IMPLICIT_ARGS
  bpf: Add bpf_stream_vprintk with KF_IMPLICIT_ARGS
  bpf,docs: Document KF_IMPLICIT_ARGS flag

 Documentation/bpf/kfuncs.rst                  |  44 ++-
 drivers/hid/bpf/progs/hid_bpf_helpers.h       |   8 +-
 include/linux/btf.h                           |   5 +-
 kernel/bpf/btf.c                              |  70 +++--
 kernel/bpf/helpers.c                          |  47 ++-
 kernel/bpf/stream.c                           |  11 +-
 kernel/bpf/verifier.c                         | 247 ++++++++++-----
 tools/bpf/resolve_btfids/main.c               | 282 ++++++++++++++++++
 tools/lib/bpf/bpf_helpers.h                   |   6 +-
 .../testing/selftests/bpf/bpf_experimental.h  |   5 -
 .../bpf/prog_tests/kfunc_implicit_args.c      |  10 +
 .../testing/selftests/bpf/progs/file_reader.c |   4 +-
 .../selftests/bpf/progs/kfunc_implicit_args.c |  41 +++
 .../testing/selftests/bpf/progs/stream_fail.c |   6 +-
 tools/testing/selftests/bpf/progs/task_work.c |  11 +-
 .../selftests/bpf/progs/task_work_fail.c      |  16 +-
 .../selftests/bpf/progs/task_work_stress.c    |   5 +-
 .../bpf/progs/verifier_async_cb_context.c     |   6 +-
 tools/testing/selftests/bpf/progs/wq.c        |   2 +-
 .../testing/selftests/bpf/progs/wq_failures.c |   4 +-
 .../selftests/bpf/test_kmods/bpf_testmod.c    |  26 ++
 tools/testing/selftests/hid/Makefile          |   4 +-
 .../selftests/hid/progs/hid_bpf_helpers.h     |   8 +-
 23 files changed, 724 insertions(+), 144 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c
 create mode 100644 tools/testing/selftests/bpf/progs/kfunc_implicit_args.c

-- 
2.52.0


^ permalink raw reply	[flat|nested] 44+ messages in thread

* [PATCH bpf-next v1 01/10] bpf: Refactor btf_kfunc_id_set_contains
  2026-01-09 18:48 [PATCH bpf-next v1 00/10] bpf: Kernel functions with KF_IMPLICIT_ARGS Ihor Solodrai
@ 2026-01-09 18:48 ` Ihor Solodrai
  2026-01-13 21:43   ` Eduard Zingerman
  2026-01-09 18:48 ` [PATCH bpf-next v1 02/10] bpf: Introduce struct bpf_kfunc_meta Ihor Solodrai
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 18:48 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

btf_kfunc_id_set_contains() is called by fetch_kfunc_meta() in the BPF
verifier to get the kfunc flags stored in the .BTF_ids ELF section.
If it returns NULL instead of a valid pointer, it's interpreted as an
illegal kfunc usage failing the verification.

There are two potential reasons for btf_kfunc_id_set_contains() to
return NULL:

  1. Provided kfunc BTF id is not present in relevant kfunc id sets.
  2. The kfunc is not allowed, as determined by the program type
     specific filter [1].

The filter functions accept a pointer to `struct bpf_prog`, so they
might implicitly depend on earlier stages of verification, when
bpf_prog members are set.

For example, bpf_qdisc_kfunc_filter() in linux/net/sched/bpf_qdisc.c
inspects prog->aux->st_ops [2], which is initialized in:

    check_attach_btf_id() -> check_struct_ops_btf_id()

So far this hasn't been an issue, because fetch_kfunc_meta() is the
only caller of btf_kfunc_id_set_contains().

However in subsequent patches of this series it is necessary to
inspect kfunc flags earlier in BPF verifier, in the add_kfunc_call().

To resolve this, refactor btf_kfunc_id_set_contains() into two
interface functions:
  * btf_kfunc_flags() that simply returns pointer to kfunc_flags
    without applying the filters
  * btf_kfunc_is_allowed() that both checks for kfunc_flags existence
    (which is a requirement for a kfunc to be allowed) and applies the
    prog filters

See [3] for the previous version of this patch.

[1] https://lore.kernel.org/all/20230519225157.760788-7-aditi.ghag@isovalent.com/
[2] https://lore.kernel.org/all/20250409214606.2000194-4-ameryhung@gmail.com/
[3] https://lore.kernel.org/bpf/20251029190113.3323406-3-ihor.solodrai@linux.dev/

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 include/linux/btf.h   |  4 +--
 kernel/bpf/btf.c      | 70 ++++++++++++++++++++++++++++++++-----------
 kernel/bpf/verifier.c |  6 ++--
 3 files changed, 58 insertions(+), 22 deletions(-)

diff --git a/include/linux/btf.h b/include/linux/btf.h
index 691f09784933..bd261015c4bc 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -574,8 +574,8 @@ const char *btf_name_by_offset(const struct btf *btf, u32 offset);
 const char *btf_str_by_offset(const struct btf *btf, u32 offset);
 struct btf *btf_parse_vmlinux(void);
 struct btf *bpf_prog_get_target_btf(const struct bpf_prog *prog);
-u32 *btf_kfunc_id_set_contains(const struct btf *btf, u32 kfunc_btf_id,
-			       const struct bpf_prog *prog);
+u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog);
+bool btf_kfunc_is_allowed(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog);
 u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
 				const struct bpf_prog *prog);
 int register_btf_kfunc_id_set(enum bpf_prog_type prog_type,
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index 539c9fdea41d..32974c11664d 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -8640,24 +8640,17 @@ static int btf_populate_kfunc_set(struct btf *btf, enum btf_kfunc_hook hook,
 	return ret;
 }
 
-static u32 *__btf_kfunc_id_set_contains(const struct btf *btf,
-					enum btf_kfunc_hook hook,
-					u32 kfunc_btf_id,
-					const struct bpf_prog *prog)
+static u32 *btf_kfunc_id_set_contains(const struct btf *btf,
+				      enum btf_kfunc_hook hook,
+				      u32 kfunc_btf_id)
 {
-	struct btf_kfunc_hook_filter *hook_filter;
 	struct btf_id_set8 *set;
-	u32 *id, i;
+	u32 *id;
 
 	if (hook >= BTF_KFUNC_HOOK_MAX)
 		return NULL;
 	if (!btf->kfunc_set_tab)
 		return NULL;
-	hook_filter = &btf->kfunc_set_tab->hook_filters[hook];
-	for (i = 0; i < hook_filter->nr_filters; i++) {
-		if (hook_filter->filters[i](prog, kfunc_btf_id))
-			return NULL;
-	}
 	set = btf->kfunc_set_tab->sets[hook];
 	if (!set)
 		return NULL;
@@ -8668,6 +8661,28 @@ static u32 *__btf_kfunc_id_set_contains(const struct btf *btf,
 	return id + 1;
 }
 
+static bool __btf_kfunc_is_allowed(const struct btf *btf,
+				   enum btf_kfunc_hook hook,
+				   u32 kfunc_btf_id,
+				   const struct bpf_prog *prog)
+{
+	struct btf_kfunc_hook_filter *hook_filter;
+	int i;
+
+	if (hook >= BTF_KFUNC_HOOK_MAX)
+		return false;
+	if (!btf->kfunc_set_tab)
+		return false;
+
+	hook_filter = &btf->kfunc_set_tab->hook_filters[hook];
+	for (i = 0; i < hook_filter->nr_filters; i++) {
+		if (hook_filter->filters[i](prog, kfunc_btf_id))
+			return false;
+	}
+
+	return true;
+}
+
 static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type)
 {
 	switch (prog_type) {
@@ -8715,6 +8730,26 @@ static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type)
 	}
 }
 
+bool btf_kfunc_is_allowed(const struct btf *btf,
+			  u32 kfunc_btf_id,
+			  const struct bpf_prog *prog)
+{
+	enum bpf_prog_type prog_type = resolve_prog_type(prog);
+	enum btf_kfunc_hook hook;
+	u32 *kfunc_flags;
+
+	kfunc_flags = btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id);
+	if (kfunc_flags && __btf_kfunc_is_allowed(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id, prog))
+		return true;
+
+	hook = bpf_prog_type_to_kfunc_hook(prog_type);
+	kfunc_flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
+	if (kfunc_flags && __btf_kfunc_is_allowed(btf, hook, kfunc_btf_id, prog))
+		return true;
+
+	return false;
+}
+
 /* Caution:
  * Reference to the module (obtained using btf_try_get_module) corresponding to
  * the struct btf *MUST* be held when calling this function from verifier
@@ -8722,26 +8757,27 @@ static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type)
  * keeping the reference for the duration of the call provides the necessary
  * protection for looking up a well-formed btf->kfunc_set_tab.
  */
-u32 *btf_kfunc_id_set_contains(const struct btf *btf,
-			       u32 kfunc_btf_id,
-			       const struct bpf_prog *prog)
+u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog)
 {
 	enum bpf_prog_type prog_type = resolve_prog_type(prog);
 	enum btf_kfunc_hook hook;
 	u32 *kfunc_flags;
 
-	kfunc_flags = __btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id, prog);
+	kfunc_flags = btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id);
 	if (kfunc_flags)
 		return kfunc_flags;
 
 	hook = bpf_prog_type_to_kfunc_hook(prog_type);
-	return __btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id, prog);
+	return btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
 }
 
 u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
 				const struct bpf_prog *prog)
 {
-	return __btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id, prog);
+	if (!__btf_kfunc_is_allowed(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id, prog))
+		return NULL;
+
+	return btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id);
 }
 
 static int __register_btf_kfunc_id_set(enum btf_kfunc_hook hook,
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 53635ea2e41b..bee49c9cde21 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -13722,10 +13722,10 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
 		*kfunc_name = func_name;
 	func_proto = btf_type_by_id(desc_btf, func->type);
 
-	kfunc_flags = btf_kfunc_id_set_contains(desc_btf, func_id, env->prog);
-	if (!kfunc_flags) {
+	if (!btf_kfunc_is_allowed(desc_btf, func_id, env->prog))
 		return -EACCES;
-	}
+
+	kfunc_flags = btf_kfunc_flags(desc_btf, func_id, env->prog);
 
 	memset(meta, 0, sizeof(*meta));
 	meta->btf = desc_btf;
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH bpf-next v1 02/10] bpf: Introduce struct bpf_kfunc_meta
  2026-01-09 18:48 [PATCH bpf-next v1 00/10] bpf: Kernel functions with KF_IMPLICIT_ARGS Ihor Solodrai
  2026-01-09 18:48 ` [PATCH bpf-next v1 01/10] bpf: Refactor btf_kfunc_id_set_contains Ihor Solodrai
@ 2026-01-09 18:48 ` Ihor Solodrai
  2026-01-13 21:46   ` Eduard Zingerman
  2026-01-09 18:48 ` [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS Ihor Solodrai
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 18:48 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

There is code duplication between add_kfunc_call() and
fetch_kfunc_meta() collecting information about a kfunc from BTF.

Introduce struct bpf_kfunc_meta to hold common kfunc BTF data and
implement fetch_kfunc_meta() to fill it in, instead of struct
bpf_kfunc_call_arg_meta directly.

Then use these in add_kfunc_call() and (new) fetch_kfunc_arg_meta()
functions, and fixup previous usages of fetch_kfunc_meta() to
fetch_kfunc_arg_meta().

Besides the code dedup, this change enables add_kfunc_call() to access
kfunc->flags.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 kernel/bpf/verifier.c | 156 ++++++++++++++++++++++++------------------
 1 file changed, 91 insertions(+), 65 deletions(-)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index bee49c9cde21..9618e4c37fce 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -294,6 +294,14 @@ struct bpf_call_arg_meta {
 	s64 const_map_key;
 };
 
+struct bpf_kfunc_meta {
+	struct btf *btf;
+	const struct btf_type *proto;
+	const char *name;
+	const u32 *flags;
+	s32 id;
+};
+
 struct bpf_kfunc_call_arg_meta {
 	/* In parameters */
 	struct btf *btf;
@@ -3263,16 +3271,68 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
 	return btf_vmlinux ?: ERR_PTR(-ENOENT);
 }
 
-static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, s16 offset)
+static int fetch_kfunc_meta(struct bpf_verifier_env *env,
+			    s32 func_id,
+			    s16 offset,
+			    struct bpf_kfunc_meta *kfunc)
 {
 	const struct btf_type *func, *func_proto;
+	const char *func_name;
+	u32 *kfunc_flags;
+	struct btf *btf;
+
+	if (func_id <= 0) {
+		verbose(env, "invalid kernel function btf_id %d\n", func_id);
+		return -EINVAL;
+	}
+
+	btf = find_kfunc_desc_btf(env, offset);
+	if (IS_ERR(btf)) {
+		verbose(env, "failed to find BTF for kernel function\n");
+		return PTR_ERR(btf);
+	}
+
+	/*
+	 * Note that kfunc_flags may be NULL at this point, which
+	 * means that we couldn't find func_id in any relevant
+	 * kfunc_id_set. This most likely indicates an invalid kfunc
+	 * call.  However we don't fail with an error here,
+	 * and let the caller decide what to do with NULL kfunc->flags.
+	 */
+	kfunc_flags = btf_kfunc_flags(btf, func_id, env->prog);
+
+	func = btf_type_by_id(btf, func_id);
+	if (!func || !btf_type_is_func(func)) {
+		verbose(env, "kernel btf_id %d is not a function\n", func_id);
+		return -EINVAL;
+	}
+
+	func_name = btf_name_by_offset(btf, func->name_off);
+	func_proto = btf_type_by_id(btf, func->type);
+	if (!func_proto || !btf_type_is_func_proto(func_proto)) {
+		verbose(env, "kernel function btf_id %d does not have a valid func_proto\n",
+			func_id);
+		return -EINVAL;
+	}
+
+	memset(kfunc, 0, sizeof(*kfunc));
+	kfunc->btf = btf;
+	kfunc->id = func_id;
+	kfunc->name = func_name;
+	kfunc->proto = func_proto;
+	kfunc->flags = kfunc_flags;
+
+	return 0;
+}
+
+static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, s16 offset)
+{
 	struct bpf_kfunc_btf_tab *btf_tab;
 	struct btf_func_model func_model;
 	struct bpf_kfunc_desc_tab *tab;
 	struct bpf_prog_aux *prog_aux;
+	struct bpf_kfunc_meta kfunc;
 	struct bpf_kfunc_desc *desc;
-	const char *func_name;
-	struct btf *desc_btf;
 	unsigned long addr;
 	int err;
 
@@ -3322,12 +3382,6 @@ static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, s16 offset)
 		prog_aux->kfunc_btf_tab = btf_tab;
 	}
 
-	desc_btf = find_kfunc_desc_btf(env, offset);
-	if (IS_ERR(desc_btf)) {
-		verbose(env, "failed to find BTF for kernel function\n");
-		return PTR_ERR(desc_btf);
-	}
-
 	if (find_kfunc_desc(env->prog, func_id, offset))
 		return 0;
 
@@ -3336,24 +3390,13 @@ static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, s16 offset)
 		return -E2BIG;
 	}
 
-	func = btf_type_by_id(desc_btf, func_id);
-	if (!func || !btf_type_is_func(func)) {
-		verbose(env, "kernel btf_id %u is not a function\n",
-			func_id);
-		return -EINVAL;
-	}
-	func_proto = btf_type_by_id(desc_btf, func->type);
-	if (!func_proto || !btf_type_is_func_proto(func_proto)) {
-		verbose(env, "kernel function btf_id %u does not have a valid func_proto\n",
-			func_id);
-		return -EINVAL;
-	}
+	err = fetch_kfunc_meta(env, func_id, offset, &kfunc);
+	if (err)
+		return err;
 
-	func_name = btf_name_by_offset(desc_btf, func->name_off);
-	addr = kallsyms_lookup_name(func_name);
+	addr = kallsyms_lookup_name(kfunc.name);
 	if (!addr) {
-		verbose(env, "cannot find address for kernel function %s\n",
-			func_name);
+		verbose(env, "cannot find address for kernel function %s\n", kfunc.name);
 		return -EINVAL;
 	}
 
@@ -3363,9 +3406,7 @@ static int add_kfunc_call(struct bpf_verifier_env *env, u32 func_id, s16 offset)
 			return err;
 	}
 
-	err = btf_distill_func_proto(&env->log, desc_btf,
-				     func_proto, func_name,
-				     &func_model);
+	err = btf_distill_func_proto(&env->log, kfunc.btf, kfunc.proto, kfunc.name, &func_model);
 	if (err)
 		return err;
 
@@ -13695,44 +13736,28 @@ static int check_kfunc_args(struct bpf_verifier_env *env, struct bpf_kfunc_call_
 	return 0;
 }
 
-static int fetch_kfunc_meta(struct bpf_verifier_env *env,
-			    struct bpf_insn *insn,
-			    struct bpf_kfunc_call_arg_meta *meta,
-			    const char **kfunc_name)
+static int fetch_kfunc_arg_meta(struct bpf_verifier_env *env,
+				s32 func_id,
+				s16 offset,
+				struct bpf_kfunc_call_arg_meta *meta)
 {
-	const struct btf_type *func, *func_proto;
-	u32 func_id, *kfunc_flags;
-	const char *func_name;
-	struct btf *desc_btf;
-
-	if (kfunc_name)
-		*kfunc_name = NULL;
-
-	if (!insn->imm)
-		return -EINVAL;
+	struct bpf_kfunc_meta kfunc;
+	int err;
 
-	desc_btf = find_kfunc_desc_btf(env, insn->off);
-	if (IS_ERR(desc_btf))
-		return PTR_ERR(desc_btf);
+	err = fetch_kfunc_meta(env, func_id, offset, &kfunc);
+	if (err)
+		return err;
 
-	func_id = insn->imm;
-	func = btf_type_by_id(desc_btf, func_id);
-	func_name = btf_name_by_offset(desc_btf, func->name_off);
-	if (kfunc_name)
-		*kfunc_name = func_name;
-	func_proto = btf_type_by_id(desc_btf, func->type);
+	memset(meta, 0, sizeof(*meta));
+	meta->btf = kfunc.btf;
+	meta->func_id = kfunc.id;
+	meta->func_proto = kfunc.proto;
+	meta->func_name = kfunc.name;
 
-	if (!btf_kfunc_is_allowed(desc_btf, func_id, env->prog))
+	if (!kfunc.flags || !btf_kfunc_is_allowed(kfunc.btf, kfunc.id, env->prog))
 		return -EACCES;
 
-	kfunc_flags = btf_kfunc_flags(desc_btf, func_id, env->prog);
-
-	memset(meta, 0, sizeof(*meta));
-	meta->btf = desc_btf;
-	meta->func_id = func_id;
-	meta->kfunc_flags = *kfunc_flags;
-	meta->func_proto = func_proto;
-	meta->func_name = func_name;
+	meta->kfunc_flags = *kfunc.flags;
 
 	return 0;
 }
@@ -13937,12 +13962,13 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 	if (!insn->imm)
 		return 0;
 
-	err = fetch_kfunc_meta(env, insn, &meta, &func_name);
-	if (err == -EACCES && func_name)
-		verbose(env, "calling kernel function %s is not allowed\n", func_name);
+	err = fetch_kfunc_arg_meta(env, insn->imm, insn->off, &meta);
+	if (err == -EACCES && meta.func_name)
+		verbose(env, "calling kernel function %s is not allowed\n", meta.func_name);
 	if (err)
 		return err;
 	desc_btf = meta.btf;
+	func_name = meta.func_name;
 	insn_aux = &env->insn_aux_data[insn_idx];
 
 	insn_aux->is_iter_next = is_iter_next_kfunc(&meta);
@@ -17729,7 +17755,7 @@ static bool get_call_summary(struct bpf_verifier_env *env, struct bpf_insn *call
 	if (bpf_pseudo_kfunc_call(call)) {
 		int err;
 
-		err = fetch_kfunc_meta(env, call, &meta, NULL);
+		err = fetch_kfunc_arg_meta(env, call->imm, call->off, &meta);
 		if (err < 0)
 			/* error would be reported later */
 			return false;
@@ -18237,7 +18263,7 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
 		} else if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) {
 			struct bpf_kfunc_call_arg_meta meta;
 
-			ret = fetch_kfunc_meta(env, insn, &meta, NULL);
+			ret = fetch_kfunc_arg_meta(env, insn->imm, insn->off, &meta);
 			if (ret == 0 && is_iter_next_kfunc(&meta)) {
 				mark_prune_point(env, t);
 				/* Checking and saving state checkpoints at iter_next() call
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
  2026-01-09 18:48 [PATCH bpf-next v1 00/10] bpf: Kernel functions with KF_IMPLICIT_ARGS Ihor Solodrai
  2026-01-09 18:48 ` [PATCH bpf-next v1 01/10] bpf: Refactor btf_kfunc_id_set_contains Ihor Solodrai
  2026-01-09 18:48 ` [PATCH bpf-next v1 02/10] bpf: Introduce struct bpf_kfunc_meta Ihor Solodrai
@ 2026-01-09 18:48 ` Ihor Solodrai
  2026-01-09 19:54   ` Alexei Starovoitov
                     ` (3 more replies)
  2026-01-09 18:48 ` [PATCH bpf-next v1 04/10] resolve_btfids: Support " Ihor Solodrai
                   ` (6 subsequent siblings)
  9 siblings, 4 replies; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 18:48 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

A kernel function bpf_foo marked with KF_IMPLICIT_ARGS flag is
expected to have two associated types in BTF:
  * `bpf_foo` with a function prototype that omits implicit arguments
  * `bpf_foo_impl` with a function prototype that matches the kernel
     declaration of `bpf_foo`, but doesn't have a ksym associated with
     its name

In order to support kfuncs with implicit arguments, the verifier has
to know how to resolve a call of `bpf_foo` to the correct BTF function
prototype and address.

To implement this, in add_kfunc_call() kfunc flags are checked for
KF_IMPLICIT_ARGS. For such kfuncs a BTF func prototype is adjusted to
the one found for `bpf_foo_impl` (func_name + "_impl" suffix, by
convention) function in BTF.

This effectively changes the signature of the `bpf_foo` kfunc in the
context of verification: from one without implicit args to the one
with full argument list.

Whether a kfunc argument is implicit or not is determined by
is_kfunc_arg_implicit(). The values of implicit arguments by design
are provided by the verifier, and so they can only be of particular
types. In this patch the only allowed implicit arg type is a pointer
to struct bpf_prog_aux. The __prog args (usually void *) are also
considered implicit for backwards compatibility.

In order to enable the verifier to correctly set an implicit
bpf_prog_aux arg value at runtime, is_kfunc_arg_prog() is extended to
check for the arg type. At a point when prog arg is determined in
check_kfunc_args() the kfunc with implicit args already has a
prototype with full argument list, so the existing value patch
mechanism just works.

If a new kfunc with KF_IMPLICIT_ARG is declared for an existing kfunc
that uses a __prog argument (a legacy case), the prototype
substitution works in exactly the same way, assuming the kfunc follows
the _impl naming convention. The difference is only in how _impl
prototype is added to the BTF, which is not the verifier's
concern. See a subsequent resolve_btfids patch for details.

In check_kfunc_call() reset the subreg_def of registers holding
implicit arguments to correctly track zero extensions.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 include/linux/btf.h   |  1 +
 kernel/bpf/verifier.c | 70 +++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 69 insertions(+), 2 deletions(-)

diff --git a/include/linux/btf.h b/include/linux/btf.h
index bd261015c4bc..f64cc40ab96f 100644
--- a/include/linux/btf.h
+++ b/include/linux/btf.h
@@ -78,6 +78,7 @@
 #define KF_ARENA_RET    (1 << 13) /* kfunc returns an arena pointer */
 #define KF_ARENA_ARG1   (1 << 14) /* kfunc takes an arena pointer as its first argument */
 #define KF_ARENA_ARG2   (1 << 15) /* kfunc takes an arena pointer as its second argument */
+#define KF_IMPLICIT_ARGS (1 << 16) /* kfunc has implicit arguments supplied by the verifier */
 
 /*
  * Tag marking a kernel function as a kfunc. This is meant to minimize the
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 9618e4c37fce..5541ab674e30 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -3271,6 +3271,38 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
 	return btf_vmlinux ?: ERR_PTR(-ENOENT);
 }
 
+#define KF_IMPL_SUFFIX "_impl"
+
+static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_env *env,
+						    struct btf *btf,
+						    const char *func_name)
+{
+	char impl_name[KSYM_SYMBOL_LEN];
+	const struct btf_type *func;
+	s32 impl_id;
+	int len;
+
+	len = snprintf(impl_name, sizeof(impl_name), "%s%s", func_name, KF_IMPL_SUFFIX);
+	if (len < 0 || len >= sizeof(impl_name)) {
+		verbose(env, "function name %s%s is too long\n", func_name, KF_IMPL_SUFFIX);
+		return NULL;
+	}
+
+	impl_id = btf_find_by_name_kind(btf, impl_name, BTF_KIND_FUNC);
+	if (impl_id <= 0) {
+		verbose(env, "cannot find function %s in BTF\n", impl_name);
+		return NULL;
+	}
+
+	func = btf_type_by_id(btf, impl_id);
+	if (!func || !btf_type_is_func(func)) {
+		verbose(env, "%s (btf_id %d) is not a function\n", impl_name, impl_id);
+		return NULL;
+	}
+
+	return btf_type_by_id(btf, func->type);
+}
+
 static int fetch_kfunc_meta(struct bpf_verifier_env *env,
 			    s32 func_id,
 			    s16 offset,
@@ -3308,7 +3340,16 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
 	}
 
 	func_name = btf_name_by_offset(btf, func->name_off);
-	func_proto = btf_type_by_id(btf, func->type);
+
+	/*
+	 * An actual prototype of a kfunc with KF_IMPLICIT_ARGS flag
+	 * can be found through the counterpart _impl kfunc.
+	 */
+	if (unlikely(kfunc_flags && KF_IMPLICIT_ARGS & *kfunc_flags))
+		func_proto = find_kfunc_impl_proto(env, btf, func_name);
+	else
+		func_proto = btf_type_by_id(btf, func->type);
+
 	if (!func_proto || !btf_type_is_func_proto(func_proto)) {
 		verbose(env, "kernel function btf_id %d does not have a valid func_proto\n",
 			func_id);
@@ -12173,9 +12214,16 @@ static bool is_kfunc_arg_irq_flag(const struct btf *btf, const struct btf_param
 	return btf_param_match_suffix(btf, arg, "__irq_flag");
 }
 
+static bool is_kfunc_arg_prog_aux(const struct btf *btf, const struct btf_param *arg);
+
 static bool is_kfunc_arg_prog(const struct btf *btf, const struct btf_param *arg)
 {
-	return btf_param_match_suffix(btf, arg, "__prog");
+	return btf_param_match_suffix(btf, arg, "__prog") || is_kfunc_arg_prog_aux(btf, arg);
+}
+
+static bool is_kfunc_arg_implicit(const struct btf *btf, const struct btf_param *arg)
+{
+	return is_kfunc_arg_prog(btf, arg);
 }
 
 static bool is_kfunc_arg_scalar_with_name(const struct btf *btf,
@@ -12206,6 +12254,7 @@ enum {
 	KF_ARG_WORKQUEUE_ID,
 	KF_ARG_RES_SPIN_LOCK_ID,
 	KF_ARG_TASK_WORK_ID,
+	KF_ARG_PROG_AUX_ID
 };
 
 BTF_ID_LIST(kf_arg_btf_ids)
@@ -12217,6 +12266,7 @@ BTF_ID(struct, bpf_rb_node)
 BTF_ID(struct, bpf_wq)
 BTF_ID(struct, bpf_res_spin_lock)
 BTF_ID(struct, bpf_task_work)
+BTF_ID(struct, bpf_prog_aux)
 
 static bool __is_kfunc_ptr_arg_type(const struct btf *btf,
 				    const struct btf_param *arg, int type)
@@ -12297,6 +12347,11 @@ static bool is_kfunc_arg_callback(struct bpf_verifier_env *env, const struct btf
 	return true;
 }
 
+static bool is_kfunc_arg_prog_aux(const struct btf *btf, const struct btf_param *arg)
+{
+	return __is_kfunc_ptr_arg_type(btf, arg, KF_ARG_PROG_AUX_ID);
+}
+
 /* Returns true if struct is composed of scalars, 4 levels of nesting allowed */
 static bool __btf_type_is_scalar_struct(struct bpf_verifier_env *env,
 					const struct btf *btf,
@@ -14303,6 +14358,17 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 	for (i = 0; i < nargs; i++) {
 		u32 regno = i + 1;
 
+		/*
+		 * Implicit kfunc arguments are set after main verification pass.
+		 * For correct tracking of zero-extensions we have to reset subreg_def for such
+		 * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
+		 * from an earlier (irrelevant) point in the program, which may lead to an error
+		 * in opt_subreg_zext_lo32_rnd_hi32().
+		 */
+		if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
+				&& is_kfunc_arg_implicit(desc_btf, &args[i])))
+			regs[regno].subreg_def = DEF_NOT_SUBREG;
+
 		t = btf_type_skip_modifiers(desc_btf, args[i].type, NULL);
 		if (btf_type_is_ptr(t))
 			mark_btf_func_reg_size(env, regno, sizeof(void *));
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
  2026-01-09 18:48 [PATCH bpf-next v1 00/10] bpf: Kernel functions with KF_IMPLICIT_ARGS Ihor Solodrai
                   ` (2 preceding siblings ...)
  2026-01-09 18:48 ` [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS Ihor Solodrai
@ 2026-01-09 18:48 ` Ihor Solodrai
  2026-01-09 19:15   ` bot+bpf-ci
  2026-01-09 23:25   ` Andrii Nakryiko
  2026-01-09 18:48 ` [PATCH bpf-next v1 05/10] selftests/bpf: Add tests " Ihor Solodrai
                   ` (5 subsequent siblings)
  9 siblings, 2 replies; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 18:48 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

Implement BTF modifications in resolve_btfids to support BPF kernel
functions with implicit arguments.

For a kfunc marked with KF_IMPLICIT_ARGS flag, a new function
prototype is added to BTF that does not have implicit arguments. The
kfunc's prototype is then updated to a new one in BTF. This prototype
is the intended interface for the BPF programs.

A <func_name>_impl function is added to BTF to make the original kfunc
prototype searchable for the BPF verifier. If a <func_name>_impl
function already exists in BTF, its interpreted as a legacy case, and
this step is skipped.

Whether an argument is implicit is determined by its type:
currently only `struct bpf_prog_aux *` is supported.

As a result, the BTF associated with kfunc is changed from

    __bpf_kfunc bpf_foo(int arg1, struct bpf_prog_aux *aux);

into

    bpf_foo_impl(int arg1, struct bpf_prog_aux *aux);
    __bpf_kfunc bpf_foo(int arg1);

For more context see previous discussions and patches [1][2].

[1] https://lore.kernel.org/dwarves/ba1650aa-fafd-49a8-bea4-bdddee7c38c9@linux.dev/
[2] https://lore.kernel.org/bpf/20251029190113.3323406-1-ihor.solodrai@linux.dev/

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 tools/bpf/resolve_btfids/main.c | 282 ++++++++++++++++++++++++++++++++
 1 file changed, 282 insertions(+)

diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index df39982f51df..b361e726fa36 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -152,6 +152,18 @@ struct object {
 	int nr_typedefs;
 };
 
+#define KF_IMPLICIT_ARGS (1 << 16)
+#define KF_IMPL_SUFFIX "_impl"
+#define MAX_BPF_FUNC_REG_ARGS 5
+#define MAX_KFUNCS 256
+#define MAX_DECL_TAGS (MAX_KFUNCS * 4)
+
+struct btf2btf_context {
+	struct btf *btf;
+	u32 nr_decl_tags;
+	s32 decl_tags[MAX_DECL_TAGS];
+};
+
 static int verbose;
 static int warnings;
 
@@ -972,6 +984,273 @@ static int patch_btfids(const char *btfids_path, const char *elf_path)
 	return err;
 }
 
+static s64 collect_kfunc_ids_by_flags(struct object *obj,
+				      u32 flags,
+				      s32 kfunc_ids[],
+				      const u32 kfunc_ids_sz)
+{
+	Elf_Data *data = obj->efile.idlist;
+	struct rb_node *next;
+	s64 nr_kfuncs = 0;
+	int i;
+
+	next = rb_first(&obj->sets);
+	while (next) {
+		struct btf_id_set8 *set8 = NULL;
+		unsigned long addr, off;
+		struct btf_id *id;
+
+		id = rb_entry(next, struct btf_id, rb_node);
+
+		if (id->kind != BTF_ID_KIND_SET8)
+			goto skip;
+
+		addr = id->addr[0];
+		off = addr - obj->efile.idlist_addr;
+		set8 = data->d_buf + off;
+
+		for (i = 0; i < set8->cnt; i++) {
+			if (set8->pairs[i].flags & flags) {
+				if (nr_kfuncs >= kfunc_ids_sz) {
+					pr_err("ERROR: resolve_btfids: too many kfuncs with flags %u - limit %d\n",
+					       flags, kfunc_ids_sz);
+					return -E2BIG;
+				}
+				kfunc_ids[nr_kfuncs++] = set8->pairs[i].id;
+			}
+		}
+skip:
+		next = rb_next(next);
+	}
+
+	return nr_kfuncs;
+}
+
+static const struct btf_type *btf__unqualified_type_by_id(const struct btf *btf, s32 type_id)
+{
+	const struct btf_type *t = btf__type_by_id(btf, type_id);
+
+	while (btf_is_mod(t))
+		t = btf__type_by_id(btf, t->type);
+
+	return t;
+}
+
+/* Implicit BPF kfunc arguments can only be of particular types */
+static bool btf__is_kf_implicit_arg(const struct btf *btf, const struct btf_param *p)
+{
+	static const char *const kf_implicit_arg_types[] = {
+		"bpf_prog_aux",
+	};
+	const struct btf_type *t;
+	const char *name;
+
+	t = btf__unqualified_type_by_id(btf, p->type);
+	if (!btf_is_ptr(t))
+		return false;
+
+	t = btf__unqualified_type_by_id(btf, t->type);
+	if (!btf_is_struct(t))
+		return false;
+
+	name = btf__name_by_offset(btf, t->name_off);
+	if (!name)
+		return false;
+
+	for (int i = 0; i < ARRAY_SIZE(kf_implicit_arg_types); i++)
+		if (strcmp(name, kf_implicit_arg_types[i]) == 0)
+			return true;
+
+	return false;
+}
+
+/*
+ * For a kfunc with KF_IMPLICIT_ARGS we do the following:
+ *   1. Add a new function with _impl suffix in the name, with the prototype
+ *      of the original kfunc.
+ *   2. Add all decl tags except "bpf_kfunc" for the _impl func.
+ *   3. Add a new function prototype with modified list of arguments:
+ *      omitting implicit args.
+ *   4. Change the prototype of the original kfunc to the new one.
+ *
+ * This way we transform the BTF associated with the kfunc from
+ *	__bpf_kfunc bpf_foo(int arg1, void *implicit_arg);
+ * into
+ *	bpf_foo_impl(int arg1, void *implicit_arg);
+ *	__bpf_kfunc bpf_foo(int arg1);
+ *
+ * If a kfunc with KF_IMPLICIT_ARGS already has an _impl counterpart
+ * in BTF, then it's a legacy case: an _impl function is declared in the
+ * source code. In this case, we can skip adding an _impl function, but we
+ * still have to add a func prototype that omits implicit args.
+ */
+static s64 process_kfunc_with_implicit_args(struct btf2btf_context *ctx, s32 kfunc_id)
+{
+	struct btf_param new_params[MAX_BPF_FUNC_REG_ARGS];
+	const char *kfunc_name, *param_name, *tag_name;
+	s32 proto_id, new_proto_id, new_func_id;
+	int err, len, name_len, nr_params;
+	const struct btf_param *params;
+	enum btf_func_linkage linkage;
+	char tmp_name[KSYM_NAME_LEN];
+	struct btf *btf = ctx->btf;
+	struct btf_type *t;
+
+	t = (struct btf_type *)btf__type_by_id(btf, kfunc_id);
+	if (!t || !btf_is_func(t)) {
+		pr_err("WARN: resolve_btfids: btf id %d is not a function\n", kfunc_id);
+		return -EINVAL;
+	}
+
+	kfunc_name = btf__name_by_offset(btf, t->name_off);
+	name_len = strlen(kfunc_name);
+	linkage = btf_vlen(t);
+
+	proto_id = t->type;
+	t = (struct btf_type *)btf__type_by_id(btf, proto_id);
+	if (!t || !btf_is_func_proto(t)) {
+		pr_err("ERROR: resolve_btfids: btf id %d is not a function prototype\n", proto_id);
+		return -EINVAL;
+	}
+
+	len = snprintf(tmp_name, sizeof(tmp_name), "%s%s", kfunc_name, KF_IMPL_SUFFIX);
+	if (len < 0 || len >= sizeof(tmp_name)) {
+		pr_err("ERROR: function name is too long: %s%s\n", kfunc_name, KF_IMPL_SUFFIX);
+		return -E2BIG;
+	}
+
+	if (btf__find_by_name_kind(btf, tmp_name, BTF_KIND_FUNC) > 0) {
+		pr_debug("resolve_btfids: function %s already exists in BTF\n", tmp_name);
+		goto add_new_proto;
+	}
+
+	/* Add a new function with _impl suffix and original prototype */
+	new_func_id = btf__add_func(btf, tmp_name, linkage, proto_id);
+	if (new_func_id < 0) {
+		pr_err("ERROR: resolve_btfids: failed to add func %s to BTF\n", tmp_name);
+		return new_func_id;
+	}
+
+	/* Copy all decl tags except "bpf_kfunc" from the original kfunc to the new one */
+	for (int i = 0; i < ctx->nr_decl_tags; i++) {
+		t = (struct btf_type *)btf__type_by_id(btf, ctx->decl_tags[i]);
+		if (t->type != kfunc_id)
+			continue;
+
+		tag_name = btf__name_by_offset(btf, t->name_off);
+		if (strcmp(tag_name, "bpf_kfunc") == 0)
+			continue;
+
+		err = btf__add_decl_tag(btf, tag_name, new_func_id, -1);
+		if (err < 0) {
+			pr_err("ERROR: resolve_btfids: failed to add decl tag %s for %s\n",
+			       tag_name, tmp_name);
+			return -EINVAL;
+		}
+	}
+
+add_new_proto:
+	/*
+	 * Drop the _impl suffix and point kfunc_name to the local buffer for later use.
+	 * When BTF is modified the original pointer is invalidated.
+	 */
+	tmp_name[name_len] = '\0';
+	kfunc_name = tmp_name;
+
+	/* Load non-implicit args from the original prototype */
+	t = (struct btf_type *)btf__type_by_id(btf, proto_id);
+	params = btf_params(t);
+	nr_params = 0;
+	for (int i = 0; i < btf_vlen(t); i++) {
+		if (btf__is_kf_implicit_arg(btf, &params[i]))
+			break;
+		new_params[nr_params++] = params[i];
+	}
+
+	new_proto_id = btf__add_func_proto(btf, t->type);
+	if (new_proto_id < 0) {
+		pr_err("ERROR: resolve_btfids: failed to add func proto for %s\n", kfunc_name);
+		return new_proto_id;
+	}
+
+	/* Add non-implicit args to the new prototype */
+	for (int i = 0; i < nr_params; i++) {
+		param_name = btf__name_by_offset(btf, new_params[i].name_off);
+		err = btf__add_func_param(btf, param_name, new_params[i].type);
+		if (err < 0) {
+			pr_err("ERROR: resolve_btfids: failed to add param %s for %s\n",
+			       param_name, kfunc_name);
+			return err;
+		}
+	}
+
+	/* Finally change the prototype of the original kfunc to the new one */
+	t = (struct btf_type *)btf__type_by_id(btf, kfunc_id);
+	t->type = new_proto_id;
+
+	pr_debug("resolve_btfids: updated BTF for kfunc with implicit args %s\n", kfunc_name);
+
+	return 0;
+}
+
+static s64 btf__collect_decl_tags(const struct btf *btf, s32 *decl_tags, u32 decl_tags_sz)
+{
+	const u32 type_cnt = btf__type_cnt(btf);
+	const struct btf_type *t;
+	s64 nr_decl_tags = 0;
+
+	for (u32 id = 1; id < type_cnt; id++) {
+		t = btf__type_by_id(btf, id);
+		if (!btf_is_decl_tag(t))
+			continue;
+		if (nr_decl_tags >= decl_tags_sz) {
+			pr_err("ERROR: resolve_btfids: too many decl tags in BTF - limit %s\n",
+				decl_tags_sz);
+			return -E2BIG;
+		}
+		decl_tags[nr_decl_tags++] = id;
+	}
+
+	return nr_decl_tags;
+}
+
+static s64 build_btf2btf_context(struct object *obj, struct btf2btf_context *ctx)
+{
+	s64 nr_decl_tags;
+
+	nr_decl_tags = btf__collect_decl_tags(obj->btf, ctx->decl_tags, ARRAY_SIZE(ctx->decl_tags));
+	if (nr_decl_tags < 0)
+		return nr_decl_tags;
+
+	ctx->btf = obj->btf;
+	ctx->nr_decl_tags = nr_decl_tags;
+
+	return 0;
+}
+
+static s64 finalize_btf(struct object *obj)
+{
+	struct btf2btf_context ctx = {};
+	s32 kfuncs[MAX_KFUNCS];
+	s64 err, nr_kfuncs;
+
+	err = build_btf2btf_context(obj, &ctx);
+	if (err < 0)
+		return err;
+
+	nr_kfuncs = collect_kfunc_ids_by_flags(obj, KF_IMPLICIT_ARGS, kfuncs, ARRAY_SIZE(kfuncs));
+	if (nr_kfuncs < 0)
+		return nr_kfuncs;
+
+	for (u32 i = 0; i < nr_kfuncs; i++) {
+		err = process_kfunc_with_implicit_args(&ctx, kfuncs[i]);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
 static const char * const resolve_btfids_usage[] = {
 	"resolve_btfids [<options>] <ELF object>",
 	"resolve_btfids --patch_btfids <.BTF_ids file> <ELF object>",
@@ -1047,6 +1326,9 @@ int main(int argc, const char **argv)
 	if (symbols_patch(&obj))
 		goto out;
 
+	if (finalize_btf(&obj))
+		goto out;
+
 	err = make_out_path(out_path, sizeof(out_path), obj.path, BTF_IDS_SECTION);
 	err = err ?: dump_raw_btf_ids(&obj, out_path);
 	if (err)
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH bpf-next v1 05/10] selftests/bpf: Add tests for KF_IMPLICIT_ARGS
  2026-01-09 18:48 [PATCH bpf-next v1 00/10] bpf: Kernel functions with KF_IMPLICIT_ARGS Ihor Solodrai
                   ` (3 preceding siblings ...)
  2026-01-09 18:48 ` [PATCH bpf-next v1 04/10] resolve_btfids: Support " Ihor Solodrai
@ 2026-01-09 18:48 ` Ihor Solodrai
  2026-01-09 23:25   ` Andrii Nakryiko
  2026-01-09 18:48 ` [PATCH bpf-next v1 06/10] bpf: Add bpf_wq_set_callback kfunc with KF_IMPLICIT_ARGS Ihor Solodrai
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 18:48 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

Add trivial end-to-end tests to validate that KF_IMPLICIT_ARGS flag is
properly handled by both resolve_btfids and the verifier.

Declare kfuncs in bpf_testmod. Check that bpf_prog_aux pointer is set
in the kfunc implementation. Verify that calls with implicit args and
a legacy case all work.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 .../bpf/prog_tests/kfunc_implicit_args.c      | 10 +++++
 .../selftests/bpf/progs/kfunc_implicit_args.c | 41 +++++++++++++++++++
 .../selftests/bpf/test_kmods/bpf_testmod.c    | 26 ++++++++++++
 3 files changed, 77 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c
 create mode 100644 tools/testing/selftests/bpf/progs/kfunc_implicit_args.c

diff --git a/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c
new file mode 100644
index 000000000000..5e4793c9c29a
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+
+#include <test_progs.h>
+#include "kfunc_implicit_args.skel.h"
+
+void test_kfunc_implicit_args(void)
+{
+	RUN_TESTS(kfunc_implicit_args);
+}
diff --git a/tools/testing/selftests/bpf/progs/kfunc_implicit_args.c b/tools/testing/selftests/bpf/progs/kfunc_implicit_args.c
new file mode 100644
index 000000000000..a1e456500442
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/kfunc_implicit_args.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
+
+extern int bpf_kfunc_implicit_arg(int a) __weak __ksym;
+extern int bpf_kfunc_implicit_arg_impl(int a, void *aux__prog) __weak __ksym; // illegal
+extern int bpf_kfunc_implicit_arg_legacy(int a, int b) __weak __ksym;
+extern int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, void *aux__prog) __weak __ksym;
+
+char _license[] SEC("license") = "GPL";
+
+SEC("syscall")
+__retval(5)
+int test_kfunc_implicit_arg(void *ctx)
+{
+	return bpf_kfunc_implicit_arg(5);
+}
+
+SEC("syscall")
+__failure
+int test_kfunc_implicit_arg_impl_illegal(void *ctx)
+{
+	return bpf_kfunc_implicit_arg_impl(5, NULL);
+}
+
+SEC("syscall")
+__retval(7)
+int test_kfunc_implicit_arg_legacy(void *ctx)
+{
+	return bpf_kfunc_implicit_arg_legacy(3, 4);
+}
+
+SEC("syscall")
+__retval(11)
+int test_kfunc_implicit_arg_legacy_impl(void *ctx)
+{
+	return bpf_kfunc_implicit_arg_legacy_impl(5, 6, NULL);
+}
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 1c41d03bd5a1..503451875d33 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -1136,6 +1136,10 @@ __bpf_kfunc int bpf_kfunc_st_ops_inc10(struct st_ops_args *args)
 __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id);
 __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1_impl(struct st_ops_args *args, void *aux_prog);
 
+__bpf_kfunc int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux);
+__bpf_kfunc int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux);
+__bpf_kfunc int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, void *aux__prog);
+
 BTF_KFUNCS_START(bpf_testmod_check_kfunc_ids)
 BTF_ID_FLAGS(func, bpf_testmod_test_mod_kfunc)
 BTF_ID_FLAGS(func, bpf_kfunc_call_test1)
@@ -1178,6 +1182,9 @@ BTF_ID_FLAGS(func, bpf_kfunc_st_ops_test_pro_epilogue, KF_SLEEPABLE)
 BTF_ID_FLAGS(func, bpf_kfunc_st_ops_inc10)
 BTF_ID_FLAGS(func, bpf_kfunc_multi_st_ops_test_1)
 BTF_ID_FLAGS(func, bpf_kfunc_multi_st_ops_test_1_impl)
+BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg, KF_IMPLICIT_ARGS)
+BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy, KF_IMPLICIT_ARGS)
+BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy_impl)
 BTF_KFUNCS_END(bpf_testmod_check_kfunc_ids)
 
 static int bpf_testmod_ops_init(struct btf *btf)
@@ -1669,6 +1676,25 @@ int bpf_kfunc_multi_st_ops_test_1_impl(struct st_ops_args *args, void *aux__prog
 	return ret;
 }
 
+int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux)
+{
+	if (aux && a > 0)
+		return a;
+	return -EINVAL;
+}
+
+int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux)
+{
+	if (aux)
+		return a + b;
+	return -EINVAL;
+}
+
+int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, void *aux__prog)
+{
+	return bpf_kfunc_implicit_arg_legacy(a, b, aux__prog);
+}
+
 static int multi_st_ops_reg(void *kdata, struct bpf_link *link)
 {
 	struct bpf_testmod_multi_st_ops *st_ops =
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH bpf-next v1 06/10] bpf: Add bpf_wq_set_callback kfunc with KF_IMPLICIT_ARGS
  2026-01-09 18:48 [PATCH bpf-next v1 00/10] bpf: Kernel functions with KF_IMPLICIT_ARGS Ihor Solodrai
                   ` (4 preceding siblings ...)
  2026-01-09 18:48 ` [PATCH bpf-next v1 05/10] selftests/bpf: Add tests " Ihor Solodrai
@ 2026-01-09 18:48 ` Ihor Solodrai
  2026-01-09 18:48 ` [PATCH bpf-next v1 07/10] HID: Use bpf_wq_set_callback kernel function Ihor Solodrai
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 18:48 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

Implement bpf_wq_set_callback() with an implicit bpf_prog_aux
argument, and change bpf_wq_set_callback_impl() to call the new kfunc.

Update special kfunc checks in the verifier to accept both versions of
the function.

In the selftests, a bpf_wq_set_callback_impl() call is intentionally
introduced to confirm that both signatures are handled correctly.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 kernel/bpf/helpers.c                           | 18 +++++++++++++-----
 kernel/bpf/verifier.c                          | 15 +++++++++------
 tools/testing/selftests/bpf/bpf_experimental.h |  5 -----
 tools/testing/selftests/bpf/progs/wq.c         |  2 +-
 .../testing/selftests/bpf/progs/wq_failures.c  |  4 ++--
 5 files changed, 25 insertions(+), 19 deletions(-)

diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 9eaa4185e0a7..23aa785c0f99 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -3120,12 +3120,11 @@ __bpf_kfunc int bpf_wq_start(struct bpf_wq *wq, unsigned int flags)
 	return 0;
 }
 
-__bpf_kfunc int bpf_wq_set_callback_impl(struct bpf_wq *wq,
-					 int (callback_fn)(void *map, int *key, void *value),
-					 unsigned int flags,
-					 void *aux__prog)
+__bpf_kfunc int bpf_wq_set_callback(struct bpf_wq *wq,
+				    int (callback_fn)(void *map, int *key, void *value),
+				    unsigned int flags,
+				    struct bpf_prog_aux *aux)
 {
-	struct bpf_prog_aux *aux = (struct bpf_prog_aux *)aux__prog;
 	struct bpf_async_kern *async = (struct bpf_async_kern *)wq;
 
 	if (flags)
@@ -3134,6 +3133,14 @@ __bpf_kfunc int bpf_wq_set_callback_impl(struct bpf_wq *wq,
 	return __bpf_async_set_callback(async, callback_fn, aux, flags, BPF_ASYNC_TYPE_WQ);
 }
 
+__bpf_kfunc int bpf_wq_set_callback_impl(struct bpf_wq *wq,
+					 int (callback_fn)(void *map, int *key, void *value),
+					 unsigned int flags,
+					 void *aux__prog)
+{
+	return bpf_wq_set_callback(wq, callback_fn, flags, aux__prog);
+}
+
 __bpf_kfunc void bpf_preempt_disable(void)
 {
 	preempt_disable();
@@ -4488,6 +4495,7 @@ BTF_ID_FLAGS(func, bpf_dynptr_memset)
 BTF_ID_FLAGS(func, bpf_modify_return_test_tp)
 #endif
 BTF_ID_FLAGS(func, bpf_wq_init)
+BTF_ID_FLAGS(func, bpf_wq_set_callback, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_wq_set_callback_impl)
 BTF_ID_FLAGS(func, bpf_wq_start)
 BTF_ID_FLAGS(func, bpf_preempt_disable)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 5541ab674e30..e5aeee554377 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -520,7 +520,7 @@ static bool is_async_callback_calling_kfunc(u32 btf_id);
 static bool is_callback_calling_kfunc(u32 btf_id);
 static bool is_bpf_throw_kfunc(struct bpf_insn *insn);
 
-static bool is_bpf_wq_set_callback_impl_kfunc(u32 btf_id);
+static bool is_bpf_wq_set_callback_kfunc(u32 btf_id);
 static bool is_task_work_add_kfunc(u32 func_id);
 
 static bool is_sync_callback_calling_function(enum bpf_func_id func_id)
@@ -562,7 +562,7 @@ static bool is_async_cb_sleepable(struct bpf_verifier_env *env, struct bpf_insn
 
 	/* bpf_wq and bpf_task_work callbacks are always sleepable. */
 	if (bpf_pseudo_kfunc_call(insn) && insn->off == 0 &&
-	    (is_bpf_wq_set_callback_impl_kfunc(insn->imm) || is_task_work_add_kfunc(insn->imm)))
+	    (is_bpf_wq_set_callback_kfunc(insn->imm) || is_task_work_add_kfunc(insn->imm)))
 		return true;
 
 	verifier_bug(env, "unhandled async callback in is_async_cb_sleepable");
@@ -12470,6 +12470,7 @@ enum special_kfunc_type {
 	KF_bpf_arena_alloc_pages,
 	KF_bpf_arena_free_pages,
 	KF_bpf_arena_reserve_pages,
+	KF_bpf_wq_set_callback,
 };
 
 BTF_ID_LIST(special_kfunc_list)
@@ -12547,6 +12548,7 @@ BTF_ID(func, bpf_task_work_schedule_resume_impl)
 BTF_ID(func, bpf_arena_alloc_pages)
 BTF_ID(func, bpf_arena_free_pages)
 BTF_ID(func, bpf_arena_reserve_pages)
+BTF_ID(func, bpf_wq_set_callback)
 
 static bool is_task_work_add_kfunc(u32 func_id)
 {
@@ -13002,7 +13004,7 @@ static bool is_sync_callback_calling_kfunc(u32 btf_id)
 
 static bool is_async_callback_calling_kfunc(u32 btf_id)
 {
-	return btf_id == special_kfunc_list[KF_bpf_wq_set_callback_impl] ||
+	return is_bpf_wq_set_callback_kfunc(btf_id) ||
 	       is_task_work_add_kfunc(btf_id);
 }
 
@@ -13012,9 +13014,10 @@ static bool is_bpf_throw_kfunc(struct bpf_insn *insn)
 	       insn->imm == special_kfunc_list[KF_bpf_throw];
 }
 
-static bool is_bpf_wq_set_callback_impl_kfunc(u32 btf_id)
+static bool is_bpf_wq_set_callback_kfunc(u32 btf_id)
 {
-	return btf_id == special_kfunc_list[KF_bpf_wq_set_callback_impl];
+	return btf_id == special_kfunc_list[KF_bpf_wq_set_callback_impl] ||
+	       btf_id == special_kfunc_list[KF_bpf_wq_set_callback];
 }
 
 static bool is_callback_calling_kfunc(u32 btf_id)
@@ -14093,7 +14096,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
 		meta.r0_rdonly = false;
 	}
 
-	if (is_bpf_wq_set_callback_impl_kfunc(meta.func_id)) {
+	if (is_bpf_wq_set_callback_kfunc(meta.func_id)) {
 		err = push_callback_call(env, insn, insn_idx, meta.subprogno,
 					 set_timer_callback_state);
 		if (err) {
diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h
index 2cd9165c7348..68a49b1f77ae 100644
--- a/tools/testing/selftests/bpf/bpf_experimental.h
+++ b/tools/testing/selftests/bpf/bpf_experimental.h
@@ -580,11 +580,6 @@ extern void bpf_iter_css_destroy(struct bpf_iter_css *it) __weak __ksym;
 
 extern int bpf_wq_init(struct bpf_wq *wq, void *p__map, unsigned int flags) __weak __ksym;
 extern int bpf_wq_start(struct bpf_wq *wq, unsigned int flags) __weak __ksym;
-extern int bpf_wq_set_callback_impl(struct bpf_wq *wq,
-		int (callback_fn)(void *map, int *key, void *value),
-		unsigned int flags__k, void *aux__ign) __ksym;
-#define bpf_wq_set_callback(timer, cb, flags) \
-	bpf_wq_set_callback_impl(timer, cb, flags, NULL)
 
 struct bpf_iter_kmem_cache;
 extern int bpf_iter_kmem_cache_new(struct bpf_iter_kmem_cache *it) __weak __ksym;
diff --git a/tools/testing/selftests/bpf/progs/wq.c b/tools/testing/selftests/bpf/progs/wq.c
index 25be2cd9d42c..f265242b954d 100644
--- a/tools/testing/selftests/bpf/progs/wq.c
+++ b/tools/testing/selftests/bpf/progs/wq.c
@@ -107,7 +107,7 @@ static int test_hmap_elem_callback(void *map, int *key,
 	if (bpf_wq_init(wq, map, 0) != 0)
 		return -3;
 
-	if (bpf_wq_set_callback(wq, callback_fn, 0))
+	if (bpf_wq_set_callback_impl(wq, callback_fn, 0, NULL))
 		return -4;
 
 	if (bpf_wq_start(wq, 0))
diff --git a/tools/testing/selftests/bpf/progs/wq_failures.c b/tools/testing/selftests/bpf/progs/wq_failures.c
index d06f6d40594a..3767f5595bbc 100644
--- a/tools/testing/selftests/bpf/progs/wq_failures.c
+++ b/tools/testing/selftests/bpf/progs/wq_failures.c
@@ -97,7 +97,7 @@ __failure
 /* check that the first argument of bpf_wq_set_callback()
  * is a correct bpf_wq pointer.
  */
-__msg(": (85) call bpf_wq_set_callback_impl#") /* anchor message */
+__msg(": (85) call bpf_wq_set_callback#") /* anchor message */
 __msg("arg#0 doesn't point to a map value")
 long test_wrong_wq_pointer(void *ctx)
 {
@@ -123,7 +123,7 @@ __failure
 /* check that the first argument of bpf_wq_set_callback()
  * is a correct bpf_wq pointer.
  */
-__msg(": (85) call bpf_wq_set_callback_impl#") /* anchor message */
+__msg(": (85) call bpf_wq_set_callback#") /* anchor message */
 __msg("off 1 doesn't point to 'struct bpf_wq' that is at 0")
 long test_wrong_wq_pointer_offset(void *ctx)
 {
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH bpf-next v1 07/10] HID: Use bpf_wq_set_callback kernel function
  2026-01-09 18:48 [PATCH bpf-next v1 00/10] bpf: Kernel functions with KF_IMPLICIT_ARGS Ihor Solodrai
                   ` (5 preceding siblings ...)
  2026-01-09 18:48 ` [PATCH bpf-next v1 06/10] bpf: Add bpf_wq_set_callback kfunc with KF_IMPLICIT_ARGS Ihor Solodrai
@ 2026-01-09 18:48 ` Ihor Solodrai
  2026-01-09 21:34   ` Benjamin Tissoires
  2026-01-09 18:48 ` [PATCH bpf-next v1 08/10] bpf: Add bpf_task_work_schedule_* kfuncs with KF_IMPLICIT_ARGS Ihor Solodrai
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 18:48 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

Remove extern declaration of bpf_wq_set_callback_impl() from
hid_bpf_helpers.h and replace bpf_wq_set_callback macro with a
corresponding new declaration.

Fix selftests/hid build failure caused by missing BPF_CFLAGS.

Tested with:
  # append tools/testing/selftests/hid/config and build the kernel
  $ make -C tools/testing/selftests/hid
  # in built kernel
  $ ./tools/testing/selftests/hid/hid_bpf -t test_multiply_events_wq

  TAP version 13
  1..1
  # Starting 1 tests from 1 test cases.
  #  RUN           hid_bpf.test_multiply_events_wq ...
  [    2.575520] hid-generic 0003:0001:0A36.0001: hidraw0: USB HID v0.00 Device [test-uhid-device-138] on 138
  #            OK  hid_bpf.test_multiply_events_wq
  ok 1 hid_bpf.test_multiply_events_wq
  # PASSED: 1 / 1 tests passed.
  # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0
  PASS

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 drivers/hid/bpf/progs/hid_bpf_helpers.h             | 8 +++-----
 tools/testing/selftests/hid/Makefile                | 4 +++-
 tools/testing/selftests/hid/progs/hid_bpf_helpers.h | 8 +++-----
 3 files changed, 9 insertions(+), 11 deletions(-)

diff --git a/drivers/hid/bpf/progs/hid_bpf_helpers.h b/drivers/hid/bpf/progs/hid_bpf_helpers.h
index bf19785a6b06..228f8d787567 100644
--- a/drivers/hid/bpf/progs/hid_bpf_helpers.h
+++ b/drivers/hid/bpf/progs/hid_bpf_helpers.h
@@ -33,11 +33,9 @@ extern int hid_bpf_try_input_report(struct hid_bpf_ctx *ctx,
 /* bpf_wq implementation */
 extern int bpf_wq_init(struct bpf_wq *wq, void *p__map, unsigned int flags) __weak __ksym;
 extern int bpf_wq_start(struct bpf_wq *wq, unsigned int flags) __weak __ksym;
-extern int bpf_wq_set_callback_impl(struct bpf_wq *wq,
-		int (callback_fn)(void *map, int *key, void *value),
-		unsigned int flags__k, void *aux__ign) __ksym;
-#define bpf_wq_set_callback(wq, cb, flags) \
-	bpf_wq_set_callback_impl(wq, cb, flags, NULL)
+extern int bpf_wq_set_callback(struct bpf_wq *wq,
+		int (*callback_fn)(void *, int *, void *),
+		unsigned int flags) __weak __ksym;
 
 #define HID_MAX_DESCRIPTOR_SIZE	4096
 #define HID_IGNORE_EVENT	-1
diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile
index 2839d2612ce3..4c01bb649913 100644
--- a/tools/testing/selftests/hid/Makefile
+++ b/tools/testing/selftests/hid/Makefile
@@ -184,7 +184,9 @@ MENDIAN=$(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian)
 
 CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG))
 BPF_CFLAGS = -g -Werror -D__TARGET_ARCH_$(SRCARCH) $(MENDIAN) 		\
-	     -I$(INCLUDE_DIR)
+	     -I$(INCLUDE_DIR)						\
+	     -Wno-microsoft-anon-tag					\
+	     -fms-extensions
 
 CLANG_CFLAGS = $(CLANG_SYS_INCLUDES) \
 	       -Wno-compare-distinct-pointer-types
diff --git a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
index 531228b849da..80ab60905865 100644
--- a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
+++ b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
@@ -116,10 +116,8 @@ extern int hid_bpf_try_input_report(struct hid_bpf_ctx *ctx,
 /* bpf_wq implementation */
 extern int bpf_wq_init(struct bpf_wq *wq, void *p__map, unsigned int flags) __weak __ksym;
 extern int bpf_wq_start(struct bpf_wq *wq, unsigned int flags) __weak __ksym;
-extern int bpf_wq_set_callback_impl(struct bpf_wq *wq,
-		int (callback_fn)(void *map, int *key, void *wq),
-		unsigned int flags__k, void *aux__ign) __weak __ksym;
-#define bpf_wq_set_callback(timer, cb, flags) \
-	bpf_wq_set_callback_impl(timer, cb, flags, NULL)
+extern int bpf_wq_set_callback(struct bpf_wq *wq,
+		int (*callback_fn)(void *, int *, void *),
+		unsigned int flags) __weak __ksym;
 
 #endif /* __HID_BPF_HELPERS_H */
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH bpf-next v1 08/10] bpf: Add bpf_task_work_schedule_* kfuncs with KF_IMPLICIT_ARGS
  2026-01-09 18:48 [PATCH bpf-next v1 00/10] bpf: Kernel functions with KF_IMPLICIT_ARGS Ihor Solodrai
                   ` (6 preceding siblings ...)
  2026-01-09 18:48 ` [PATCH bpf-next v1 07/10] HID: Use bpf_wq_set_callback kernel function Ihor Solodrai
@ 2026-01-09 18:48 ` Ihor Solodrai
  2026-01-09 19:58   ` Alexei Starovoitov
  2026-01-09 18:48 ` [PATCH bpf-next v1 09/10] bpf: Add bpf_stream_vprintk " Ihor Solodrai
  2026-01-09 18:48 ` [PATCH bpf-next v1 10/10] bpf,docs: Document KF_IMPLICIT_ARGS flag Ihor Solodrai
  9 siblings, 1 reply; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 18:48 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

Implement bpf_task_work_schedule_* with an implicit bpf_prog_aux
argument, and change corresponding _impl funcs to call the new kfunc.

Update special kfunc checks in the verifier to accept both new and old
variants of the functions.

Update the selftests to use the new API with implicit argument.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 kernel/bpf/helpers.c                          | 28 +++++++++++++++----
 kernel/bpf/verifier.c                         |  8 +++++-
 .../testing/selftests/bpf/progs/file_reader.c |  4 ++-
 tools/testing/selftests/bpf/progs/task_work.c | 11 ++++++--
 .../selftests/bpf/progs/task_work_fail.c      | 16 ++++++++---
 .../selftests/bpf/progs/task_work_stress.c    |  5 ++--
 .../bpf/progs/verifier_async_cb_context.c     |  6 ++--
 7 files changed, 59 insertions(+), 19 deletions(-)

diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 23aa785c0f99..2e01973f2c18 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -4282,41 +4282,55 @@ static int bpf_task_work_schedule(struct task_struct *task, struct bpf_task_work
 }
 
 /**
- * bpf_task_work_schedule_signal_impl - Schedule BPF callback using task_work_add with TWA_SIGNAL
+ * bpf_task_work_schedule_signal - Schedule BPF callback using task_work_add with TWA_SIGNAL
  * mode
  * @task: Task struct for which callback should be scheduled
  * @tw: Pointer to struct bpf_task_work in BPF map value for internal bookkeeping
  * @map__map: bpf_map that embeds struct bpf_task_work in the values
  * @callback: pointer to BPF subprogram to call
- * @aux__prog: user should pass NULL
+ * @aux: pointer to bpf_prog_aux of the caller BPF program, implicitly set by the verifier
  *
  * Return: 0 if task work has been scheduled successfully, negative error code otherwise
  */
+__bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct bpf_task_work *tw,
+					      void *map__map, bpf_task_work_callback_t callback,
+					      struct bpf_prog_aux *aux)
+{
+	return bpf_task_work_schedule(task, tw, map__map, callback, aux, TWA_SIGNAL);
+}
+
 __bpf_kfunc int bpf_task_work_schedule_signal_impl(struct task_struct *task,
 						   struct bpf_task_work *tw, void *map__map,
 						   bpf_task_work_callback_t callback,
 						   void *aux__prog)
 {
-	return bpf_task_work_schedule(task, tw, map__map, callback, aux__prog, TWA_SIGNAL);
+	return bpf_task_work_schedule_signal(task, tw, map__map, callback, aux__prog);
 }
 
 /**
- * bpf_task_work_schedule_resume_impl - Schedule BPF callback using task_work_add with TWA_RESUME
+ * bpf_task_work_schedule_resume - Schedule BPF callback using task_work_add with TWA_RESUME
  * mode
  * @task: Task struct for which callback should be scheduled
  * @tw: Pointer to struct bpf_task_work in BPF map value for internal bookkeeping
  * @map__map: bpf_map that embeds struct bpf_task_work in the values
  * @callback: pointer to BPF subprogram to call
- * @aux__prog: user should pass NULL
+ * @aux: pointer to bpf_prog_aux of the caller BPF program, implicitly set by the verifier
  *
  * Return: 0 if task work has been scheduled successfully, negative error code otherwise
  */
+__bpf_kfunc int bpf_task_work_schedule_resume(struct task_struct *task, struct bpf_task_work *tw,
+					      void *map__map, bpf_task_work_callback_t callback,
+					      struct bpf_prog_aux *aux)
+{
+	return bpf_task_work_schedule(task, tw, map__map, callback, aux, TWA_RESUME);
+}
+
 __bpf_kfunc int bpf_task_work_schedule_resume_impl(struct task_struct *task,
 						   struct bpf_task_work *tw, void *map__map,
 						   bpf_task_work_callback_t callback,
 						   void *aux__prog)
 {
-	return bpf_task_work_schedule(task, tw, map__map, callback, aux__prog, TWA_RESUME);
+	return bpf_task_work_schedule_resume(task, tw, map__map, callback, aux__prog);
 }
 
 static int make_file_dynptr(struct file *file, u32 flags, bool may_sleep,
@@ -4545,7 +4559,9 @@ BTF_ID_FLAGS(func, bpf_strncasestr);
 BTF_ID_FLAGS(func, bpf_cgroup_read_xattr, KF_RCU)
 #endif
 BTF_ID_FLAGS(func, bpf_stream_vprintk_impl)
+BTF_ID_FLAGS(func, bpf_task_work_schedule_signal, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_task_work_schedule_signal_impl)
+BTF_ID_FLAGS(func, bpf_task_work_schedule_resume, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_task_work_schedule_resume_impl)
 BTF_ID_FLAGS(func, bpf_dynptr_from_file)
 BTF_ID_FLAGS(func, bpf_dynptr_file_discard)
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index e5aeee554377..fdd17d19d5be 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -12471,6 +12471,8 @@ enum special_kfunc_type {
 	KF_bpf_arena_free_pages,
 	KF_bpf_arena_reserve_pages,
 	KF_bpf_wq_set_callback,
+	KF_bpf_task_work_schedule_signal,
+	KF_bpf_task_work_schedule_resume,
 };
 
 BTF_ID_LIST(special_kfunc_list)
@@ -12549,10 +12551,14 @@ BTF_ID(func, bpf_arena_alloc_pages)
 BTF_ID(func, bpf_arena_free_pages)
 BTF_ID(func, bpf_arena_reserve_pages)
 BTF_ID(func, bpf_wq_set_callback)
+BTF_ID(func, bpf_task_work_schedule_signal)
+BTF_ID(func, bpf_task_work_schedule_resume)
 
 static bool is_task_work_add_kfunc(u32 func_id)
 {
-	return func_id == special_kfunc_list[KF_bpf_task_work_schedule_signal_impl] ||
+	return func_id == special_kfunc_list[KF_bpf_task_work_schedule_signal] ||
+	       func_id == special_kfunc_list[KF_bpf_task_work_schedule_signal_impl] ||
+	       func_id == special_kfunc_list[KF_bpf_task_work_schedule_resume] ||
 	       func_id == special_kfunc_list[KF_bpf_task_work_schedule_resume_impl];
 }
 
diff --git a/tools/testing/selftests/bpf/progs/file_reader.c b/tools/testing/selftests/bpf/progs/file_reader.c
index 4d756b623557..ff3270a0cb9b 100644
--- a/tools/testing/selftests/bpf/progs/file_reader.c
+++ b/tools/testing/selftests/bpf/progs/file_reader.c
@@ -77,7 +77,9 @@ int on_open_validate_file_read(void *c)
 		err = 1;
 		return 0;
 	}
-	bpf_task_work_schedule_signal_impl(task, &work->tw, &arrmap, task_work_callback, NULL);
+
+	bpf_task_work_schedule_signal(task, &work->tw, &arrmap, task_work_callback);
+
 	return 0;
 }
 
diff --git a/tools/testing/selftests/bpf/progs/task_work.c b/tools/testing/selftests/bpf/progs/task_work.c
index 663a80990f8f..eec422af20b8 100644
--- a/tools/testing/selftests/bpf/progs/task_work.c
+++ b/tools/testing/selftests/bpf/progs/task_work.c
@@ -66,7 +66,8 @@ int oncpu_hash_map(struct pt_regs *args)
 	if (!work)
 		return 0;
 
-	bpf_task_work_schedule_resume_impl(task, &work->tw, &hmap, process_work, NULL);
+	bpf_task_work_schedule_resume(task, &work->tw, &hmap, process_work);
+
 	return 0;
 }
 
@@ -80,7 +81,9 @@ int oncpu_array_map(struct pt_regs *args)
 	work = bpf_map_lookup_elem(&arrmap, &key);
 	if (!work)
 		return 0;
-	bpf_task_work_schedule_signal_impl(task, &work->tw, &arrmap, process_work, NULL);
+
+	bpf_task_work_schedule_signal(task, &work->tw, &arrmap, process_work);
+
 	return 0;
 }
 
@@ -102,6 +105,8 @@ int oncpu_lru_map(struct pt_regs *args)
 	work = bpf_map_lookup_elem(&lrumap, &key);
 	if (!work || work->data[0])
 		return 0;
-	bpf_task_work_schedule_resume_impl(task, &work->tw, &lrumap, process_work, NULL);
+
+	bpf_task_work_schedule_resume(task, &work->tw, &lrumap, process_work);
+
 	return 0;
 }
diff --git a/tools/testing/selftests/bpf/progs/task_work_fail.c b/tools/testing/selftests/bpf/progs/task_work_fail.c
index 1270953fd092..557bdf9eb0fc 100644
--- a/tools/testing/selftests/bpf/progs/task_work_fail.c
+++ b/tools/testing/selftests/bpf/progs/task_work_fail.c
@@ -53,7 +53,9 @@ int mismatch_map(struct pt_regs *args)
 	work = bpf_map_lookup_elem(&arrmap, &key);
 	if (!work)
 		return 0;
-	bpf_task_work_schedule_resume_impl(task, &work->tw, &hmap, process_work, NULL);
+
+	bpf_task_work_schedule_resume(task, &work->tw, &hmap, process_work);
+
 	return 0;
 }
 
@@ -65,7 +67,9 @@ int no_map_task_work(struct pt_regs *args)
 	struct bpf_task_work tw;
 
 	task = bpf_get_current_task_btf();
-	bpf_task_work_schedule_resume_impl(task, &tw, &hmap, process_work, NULL);
+
+	bpf_task_work_schedule_resume(task, &tw, &hmap, process_work);
+
 	return 0;
 }
 
@@ -76,7 +80,9 @@ int task_work_null(struct pt_regs *args)
 	struct task_struct *task;
 
 	task = bpf_get_current_task_btf();
-	bpf_task_work_schedule_resume_impl(task, NULL, &hmap, process_work, NULL);
+
+	bpf_task_work_schedule_resume(task, NULL, &hmap, process_work);
+
 	return 0;
 }
 
@@ -91,6 +97,8 @@ int map_null(struct pt_regs *args)
 	work = bpf_map_lookup_elem(&arrmap, &key);
 	if (!work)
 		return 0;
-	bpf_task_work_schedule_resume_impl(task, &work->tw, NULL, process_work, NULL);
+
+	bpf_task_work_schedule_resume(task, &work->tw, NULL, process_work);
+
 	return 0;
 }
diff --git a/tools/testing/selftests/bpf/progs/task_work_stress.c b/tools/testing/selftests/bpf/progs/task_work_stress.c
index 55e555f7f41b..0cba36569714 100644
--- a/tools/testing/selftests/bpf/progs/task_work_stress.c
+++ b/tools/testing/selftests/bpf/progs/task_work_stress.c
@@ -51,8 +51,9 @@ int schedule_task_work(void *ctx)
 		if (!work)
 			return 0;
 	}
-	err = bpf_task_work_schedule_signal_impl(bpf_get_current_task_btf(), &work->tw, &hmap,
-						 process_work, NULL);
+	err = bpf_task_work_schedule_signal(bpf_get_current_task_btf(), &work->tw, &hmap,
+					    process_work);
+
 	if (err)
 		__sync_fetch_and_add(&schedule_error, 1);
 	else
diff --git a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
index 7efa9521105e..6c50aff03baa 100644
--- a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
+++ b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
@@ -156,7 +156,8 @@ int task_work_non_sleepable_prog(void *ctx)
 	if (!task)
 		return 0;
 
-	bpf_task_work_schedule_resume_impl(task, &val->tw, &task_work_map, task_work_cb, NULL);
+	bpf_task_work_schedule_resume(task, &val->tw, &task_work_map, task_work_cb);
+
 	return 0;
 }
 
@@ -176,6 +177,7 @@ int task_work_sleepable_prog(void *ctx)
 	if (!task)
 		return 0;
 
-	bpf_task_work_schedule_resume_impl(task, &val->tw, &task_work_map, task_work_cb, NULL);
+	bpf_task_work_schedule_resume(task, &val->tw, &task_work_map, task_work_cb);
+
 	return 0;
 }
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH bpf-next v1 09/10] bpf: Add bpf_stream_vprintk with KF_IMPLICIT_ARGS
  2026-01-09 18:48 [PATCH bpf-next v1 00/10] bpf: Kernel functions with KF_IMPLICIT_ARGS Ihor Solodrai
                   ` (7 preceding siblings ...)
  2026-01-09 18:48 ` [PATCH bpf-next v1 08/10] bpf: Add bpf_task_work_schedule_* kfuncs with KF_IMPLICIT_ARGS Ihor Solodrai
@ 2026-01-09 18:48 ` Ihor Solodrai
  2026-01-09 18:48 ` [PATCH bpf-next v1 10/10] bpf,docs: Document KF_IMPLICIT_ARGS flag Ihor Solodrai
  9 siblings, 0 replies; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 18:48 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

Implement bpf_stream_vprintk with an implicit bpf_prog_aux argument,
and change bpf_stream_vprintk_impl to call the new kfunc.

Update the selftests to use the new API with implicit argument.

bpf_stream_vprintk macro is changed to use the new bpf_stream_vprintk
kfunc, and the extern definition of bpf_stream_vprintk_impl is
replaced accordingly.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 kernel/bpf/helpers.c                            |  1 +
 kernel/bpf/stream.c                             | 11 ++++++++---
 tools/lib/bpf/bpf_helpers.h                     |  6 +++---
 tools/testing/selftests/bpf/progs/stream_fail.c |  6 +++---
 4 files changed, 15 insertions(+), 9 deletions(-)

diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index 2e01973f2c18..b35b78c08583 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -4558,6 +4558,7 @@ BTF_ID_FLAGS(func, bpf_strncasestr);
 #if defined(CONFIG_BPF_LSM) && defined(CONFIG_CGROUPS)
 BTF_ID_FLAGS(func, bpf_cgroup_read_xattr, KF_RCU)
 #endif
+BTF_ID_FLAGS(func, bpf_stream_vprintk, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_stream_vprintk_impl)
 BTF_ID_FLAGS(func, bpf_task_work_schedule_signal, KF_IMPLICIT_ARGS)
 BTF_ID_FLAGS(func, bpf_task_work_schedule_signal_impl)
diff --git a/kernel/bpf/stream.c b/kernel/bpf/stream.c
index 0b6bc3f30335..4e4713d9c771 100644
--- a/kernel/bpf/stream.c
+++ b/kernel/bpf/stream.c
@@ -212,14 +212,13 @@ __bpf_kfunc_start_defs();
  * Avoid using enum bpf_stream_id so that kfunc users don't have to pull in the
  * enum in headers.
  */
-__bpf_kfunc int bpf_stream_vprintk_impl(int stream_id, const char *fmt__str, const void *args,
-					u32 len__sz, void *aux__prog)
+__bpf_kfunc int bpf_stream_vprintk(int stream_id, const char *fmt__str, const void *args,
+				   u32 len__sz, struct bpf_prog_aux *aux)
 {
 	struct bpf_bprintf_data data = {
 		.get_bin_args	= true,
 		.get_buf	= true,
 	};
-	struct bpf_prog_aux *aux = aux__prog;
 	u32 fmt_size = strlen(fmt__str) + 1;
 	struct bpf_stream *stream;
 	u32 data_len = len__sz;
@@ -246,6 +245,12 @@ __bpf_kfunc int bpf_stream_vprintk_impl(int stream_id, const char *fmt__str, con
 	return ret;
 }
 
+__bpf_kfunc int bpf_stream_vprintk_impl(int stream_id, const char *fmt__str, const void *args,
+					u32 len__sz, void *aux__prog)
+{
+	return bpf_stream_vprintk(stream_id, fmt__str, args, len__sz, aux__prog);
+}
+
 __bpf_kfunc_end_defs();
 
 /* Added kfunc to common_btf_ids */
diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h
index d4e4e388e625..c145da05a67c 100644
--- a/tools/lib/bpf/bpf_helpers.h
+++ b/tools/lib/bpf/bpf_helpers.h
@@ -315,8 +315,8 @@ enum libbpf_tristate {
 			  ___param, sizeof(___param));		\
 })
 
-extern int bpf_stream_vprintk_impl(int stream_id, const char *fmt__str, const void *args,
-				   __u32 len__sz, void *aux__prog) __weak __ksym;
+extern int bpf_stream_vprintk(int stream_id, const char *fmt__str, const void *args,
+			      __u32 len__sz) __weak __ksym;
 
 #define bpf_stream_printk(stream_id, fmt, args...)					\
 ({											\
@@ -328,7 +328,7 @@ extern int bpf_stream_vprintk_impl(int stream_id, const char *fmt__str, const vo
 	___bpf_fill(___param, args);							\
 	_Pragma("GCC diagnostic pop")							\
 											\
-	bpf_stream_vprintk_impl(stream_id, ___fmt, ___param, sizeof(___param), NULL);	\
+	bpf_stream_vprintk(stream_id, ___fmt, ___param, sizeof(___param));		\
 })
 
 /* Use __bpf_printk when bpf_printk call has 3 or fewer fmt args
diff --git a/tools/testing/selftests/bpf/progs/stream_fail.c b/tools/testing/selftests/bpf/progs/stream_fail.c
index 3662515f0107..8e8249f3521c 100644
--- a/tools/testing/selftests/bpf/progs/stream_fail.c
+++ b/tools/testing/selftests/bpf/progs/stream_fail.c
@@ -10,7 +10,7 @@ SEC("syscall")
 __failure __msg("Possibly NULL pointer passed")
 int stream_vprintk_null_arg(void *ctx)
 {
-	bpf_stream_vprintk_impl(BPF_STDOUT, "", NULL, 0, NULL);
+	bpf_stream_vprintk(BPF_STDOUT, "", NULL, 0);
 	return 0;
 }
 
@@ -18,7 +18,7 @@ SEC("syscall")
 __failure __msg("R3 type=scalar expected=")
 int stream_vprintk_scalar_arg(void *ctx)
 {
-	bpf_stream_vprintk_impl(BPF_STDOUT, "", (void *)46, 0, NULL);
+	bpf_stream_vprintk(BPF_STDOUT, "", (void *)46, 0);
 	return 0;
 }
 
@@ -26,7 +26,7 @@ SEC("syscall")
 __failure __msg("arg#1 doesn't point to a const string")
 int stream_vprintk_string_arg(void *ctx)
 {
-	bpf_stream_vprintk_impl(BPF_STDOUT, ctx, NULL, 0, NULL);
+	bpf_stream_vprintk(BPF_STDOUT, ctx, NULL, 0);
 	return 0;
 }
 
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* [PATCH bpf-next v1 10/10] bpf,docs: Document KF_IMPLICIT_ARGS flag
  2026-01-09 18:48 [PATCH bpf-next v1 00/10] bpf: Kernel functions with KF_IMPLICIT_ARGS Ihor Solodrai
                   ` (8 preceding siblings ...)
  2026-01-09 18:48 ` [PATCH bpf-next v1 09/10] bpf: Add bpf_stream_vprintk " Ihor Solodrai
@ 2026-01-09 18:48 ` Ihor Solodrai
  9 siblings, 0 replies; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 18:48 UTC (permalink / raw)
  To: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

Add sections explaining KF_IMPLICIT_ARGS kfunc flag. Mark __prog
annotation as deprecated.

Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
---
 Documentation/bpf/kfuncs.rst | 44 +++++++++++++++++++++++++++++++++++-
 1 file changed, 43 insertions(+), 1 deletion(-)

diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
index 3eb59a8f9f34..b849598271d2 100644
--- a/Documentation/bpf/kfuncs.rst
+++ b/Documentation/bpf/kfuncs.rst
@@ -232,7 +232,7 @@ Or::
                 ...
         }
 
-2.3.6 __prog Annotation
+2.3.6 __prog Annotation (deprecated, use KF_IMPLICIT_ARGS instead)
 ---------------------------
 This annotation is used to indicate that the argument needs to be fixed up to
 the bpf_prog_aux of the caller BPF program. Any value passed into this argument
@@ -381,6 +381,48 @@ encouraged to make their use-cases known as early as possible, and participate
 in upstream discussions regarding whether to keep, change, deprecate, or remove
 those kfuncs if and when such discussions occur.
 
+2.5.9 KF_IMPLICIT_ARGS flag
+------------------------------------
+
+The KF_IMPLICIT_ARGS flag is used to indicate that the BPF signature
+of the kfunc is different from it's kernel signature, and the values
+for implicit arguments are provided at load time by the verifier.
+
+Only arguments of specific types are implicit.
+Currently only ``struct bpf_prog_aux *`` type is supported.
+
+A kfunc with KF_IMPLICIT_ARGS flag therefore has two types in BTF: one
+function matching the kernel declaration (with _impl suffix in the
+name by convention), and another matching the intended BPF API.
+
+Verifier only allows calls to the non-_impl version of a kfunc, that
+uses a signature without the implicit arguments.
+
+Example declaration:
+
+.. code-block:: c
+
+	__bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct bpf_task_work *tw,
+						      void *map__map, bpf_task_work_callback_t callback,
+						      struct bpf_prog_aux *aux) { ... }
+
+Example usage in BPF program:
+
+.. code-block:: c
+
+	/* note that the last argument is omitted */
+        bpf_task_work_schedule_signal(task, &work->tw, &arrmap, task_work_callback);
+
+An exception to this are kfuncs that use __prog argument, and were
+implemented before KF_IMPLICIT_ARGS mechanism was introduced:
+  * bpf_stream_vprintk_impl
+  * bpf_task_work_schedule_resume_impl
+  * bpf_task_work_schedule_signal_impl
+  * bpf_wq_set_callback_impl
+
+These are allowed for backwards compatibility, however BPF programs
+should use newer API that omits implicit arguments in BPF.
+
 2.6 Registering the kfuncs
 --------------------------
 
-- 
2.52.0


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
  2026-01-09 18:48 ` [PATCH bpf-next v1 04/10] resolve_btfids: Support " Ihor Solodrai
@ 2026-01-09 19:15   ` bot+bpf-ci
  2026-01-09 19:34     ` Ihor Solodrai
  2026-01-09 23:25   ` Andrii Nakryiko
  1 sibling, 1 reply; 44+ messages in thread
From: bot+bpf-ci @ 2026-01-09 19:15 UTC (permalink / raw)
  To: ihor.solodrai, ast, andrii, daniel, martin.lau, eddyz87
  Cc: yatsenko, tj, alan.maguire, bentiss, jikos, bpf, linux-kernel,
	linux-input, sched-ext, ast, andrii, daniel, martin.lau, eddyz87,
	yonghong.song, clm, ihor.solodrai

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

> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index df39982f51df..b361e726fa36 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c

[ ... ]

> +static s64 btf__collect_decl_tags(const struct btf *btf, s32 *decl_tags, u32 decl_tags_sz)
> +{
> +	const u32 type_cnt = btf__type_cnt(btf);
> +	const struct btf_type *t;
> +	s64 nr_decl_tags = 0;
> +
> +	for (u32 id = 1; id < type_cnt; id++) {
> +		t = btf__type_by_id(btf, id);
> +		if (!btf_is_decl_tag(t))
> +			continue;
> +		if (nr_decl_tags >= decl_tags_sz) {
> +			pr_err("ERROR: resolve_btfids: too many decl tags in BTF - limit %s\n",
> +				decl_tags_sz);
                                ^^^^^^^^^^^^^^^^^

Should this be %u instead of %s? The decl_tags_sz parameter is a u32, but
the format specifier %s expects a string pointer. When the error path is
triggered (more than MAX_DECL_TAGS decl tags in BTF), this will cause
undefined behavior as printf tries to interpret the integer value as a
memory address to read a string from.

> +			return -E2BIG;
> +		}
> +		decl_tags[nr_decl_tags++] = id;
> +	}
> +
> +	return nr_decl_tags;
> +}

[ ... ]


---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/20862592244

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
  2026-01-09 19:15   ` bot+bpf-ci
@ 2026-01-09 19:34     ` Ihor Solodrai
  0 siblings, 0 replies; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 19:34 UTC (permalink / raw)
  To: bot+bpf-ci, ast, andrii, daniel, martin.lau, eddyz87
  Cc: yatsenko, tj, alan.maguire, bentiss, jikos, bpf, linux-kernel,
	linux-input, sched-ext, martin.lau, yonghong.song, clm

On 1/9/26 11:15 AM, bot+bpf-ci@kernel.org wrote:
>> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
>> index df39982f51df..b361e726fa36 100644
>> --- a/tools/bpf/resolve_btfids/main.c
>> +++ b/tools/bpf/resolve_btfids/main.c
> 
> [ ... ]
> 
>> +static s64 btf__collect_decl_tags(const struct btf *btf, s32 *decl_tags, u32 decl_tags_sz)
>> +{
>> +	const u32 type_cnt = btf__type_cnt(btf);
>> +	const struct btf_type *t;
>> +	s64 nr_decl_tags = 0;
>> +
>> +	for (u32 id = 1; id < type_cnt; id++) {
>> +		t = btf__type_by_id(btf, id);
>> +		if (!btf_is_decl_tag(t))
>> +			continue;
>> +		if (nr_decl_tags >= decl_tags_sz) {
>> +			pr_err("ERROR: resolve_btfids: too many decl tags in BTF - limit %s\n",
>> +				decl_tags_sz);
>                                 ^^^^^^^^^^^^^^^^^
> 
> Should this be %u instead of %s? The decl_tags_sz parameter is a u32, but
> the format specifier %s expects a string pointer. When the error path is
> triggered (more than MAX_DECL_TAGS decl tags in BTF), this will cause
> undefined behavior as printf tries to interpret the integer value as a
> memory address to read a string from.

Correct, it should be %u or %d.

> 
>> +			return -E2BIG;
>> +		}
>> +		decl_tags[nr_decl_tags++] = id;
>> +	}
>> +
>> +	return nr_decl_tags;
>> +}
> 
> [ ... ]
> 
> 
> ---
> AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
> See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
> 
> CI run summary: https://github.com/kernel-patches/bpf/actions/runs/20862592244


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
  2026-01-09 18:48 ` [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS Ihor Solodrai
@ 2026-01-09 19:54   ` Alexei Starovoitov
  2026-01-09 23:25   ` Andrii Nakryiko
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 44+ messages in thread
From: Alexei Starovoitov @ 2026-01-09 19:54 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, LKML,
	open list:HID CORE LAYER, sched-ext

On Fri, Jan 9, 2026 at 10:49 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> +       if (unlikely(kfunc_flags && KF_IMPLICIT_ARGS & *kfunc_flags))

Please add extra () around &

that's the style we use elsewhere. Like:
if (!first && (env->log.level & BPF_LOG_LEVEL2))

> +               if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
> +                               && is_kfunc_arg_implicit(desc_btf, &args[i])))

same

pw-bot: cr

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 08/10] bpf: Add bpf_task_work_schedule_* kfuncs with KF_IMPLICIT_ARGS
  2026-01-09 18:48 ` [PATCH bpf-next v1 08/10] bpf: Add bpf_task_work_schedule_* kfuncs with KF_IMPLICIT_ARGS Ihor Solodrai
@ 2026-01-09 19:58   ` Alexei Starovoitov
  2026-01-09 20:02     ` Ihor Solodrai
  0 siblings, 1 reply; 44+ messages in thread
From: Alexei Starovoitov @ 2026-01-09 19:58 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, LKML,
	open list:HID CORE LAYER, sched-ext

On Fri, Jan 9, 2026 at 10:50 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> +__bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct bpf_task_work *tw,
> +                                             void *map__map, bpf_task_work_callback_t callback,
> +                                             struct bpf_prog_aux *aux)
> +{
> +       return bpf_task_work_schedule(task, tw, map__map, callback, aux, TWA_SIGNAL);
> +}
> +
>  __bpf_kfunc int bpf_task_work_schedule_signal_impl(struct task_struct *task,
>                                                    struct bpf_task_work *tw, void *map__map,
>                                                    bpf_task_work_callback_t callback,
>                                                    void *aux__prog)
>  {
> -       return bpf_task_work_schedule(task, tw, map__map, callback, aux__prog, TWA_SIGNAL);
> +       return bpf_task_work_schedule_signal(task, tw, map__map, callback, aux__prog);
>  }

I thought we decided that _impl() will not be marked as __bpf_kfunc
and will not be in BTF_ID(func, _impl).
We can mark it as __weak noinline and it will be in kallsyms.
That's all we need for the verifier and resolve_btfid, no?

Sorry, it's been a long time. I must have forgotten something.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 08/10] bpf: Add bpf_task_work_schedule_* kfuncs with KF_IMPLICIT_ARGS
  2026-01-09 19:58   ` Alexei Starovoitov
@ 2026-01-09 20:02     ` Ihor Solodrai
  2026-01-09 20:47       ` Alexei Starovoitov
  0 siblings, 1 reply; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 20:02 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, LKML,
	open list:HID CORE LAYER, sched-ext

On 1/9/26 11:58 AM, Alexei Starovoitov wrote:
> On Fri, Jan 9, 2026 at 10:50 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>
>> +__bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct bpf_task_work *tw,
>> +                                             void *map__map, bpf_task_work_callback_t callback,
>> +                                             struct bpf_prog_aux *aux)
>> +{
>> +       return bpf_task_work_schedule(task, tw, map__map, callback, aux, TWA_SIGNAL);
>> +}
>> +
>>  __bpf_kfunc int bpf_task_work_schedule_signal_impl(struct task_struct *task,
>>                                                    struct bpf_task_work *tw, void *map__map,
>>                                                    bpf_task_work_callback_t callback,
>>                                                    void *aux__prog)
>>  {
>> -       return bpf_task_work_schedule(task, tw, map__map, callback, aux__prog, TWA_SIGNAL);
>> +       return bpf_task_work_schedule_signal(task, tw, map__map, callback, aux__prog);
>>  }
> 
> I thought we decided that _impl() will not be marked as __bpf_kfunc
> and will not be in BTF_ID(func, _impl).
> We can mark it as __weak noinline and it will be in kallsyms.
> That's all we need for the verifier and resolve_btfid, no?
> 
> Sorry, it's been a long time. I must have forgotten something.

For the *generated* _impl kfuncs there is no decl tags and the ids are
absent from BTF_ID sets, yes.

However for the "legacy" cases it must be there for backwards
compatibility, as well as relevant verifier checks.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 08/10] bpf: Add bpf_task_work_schedule_* kfuncs with KF_IMPLICIT_ARGS
  2026-01-09 20:02     ` Ihor Solodrai
@ 2026-01-09 20:47       ` Alexei Starovoitov
  2026-01-09 21:39         ` Ihor Solodrai
  0 siblings, 1 reply; 44+ messages in thread
From: Alexei Starovoitov @ 2026-01-09 20:47 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, LKML,
	open list:HID CORE LAYER, sched-ext

On Fri, Jan 9, 2026 at 12:02 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> On 1/9/26 11:58 AM, Alexei Starovoitov wrote:
> > On Fri, Jan 9, 2026 at 10:50 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
> >>
> >> +__bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct bpf_task_work *tw,
> >> +                                             void *map__map, bpf_task_work_callback_t callback,
> >> +                                             struct bpf_prog_aux *aux)
> >> +{
> >> +       return bpf_task_work_schedule(task, tw, map__map, callback, aux, TWA_SIGNAL);
> >> +}
> >> +
> >>  __bpf_kfunc int bpf_task_work_schedule_signal_impl(struct task_struct *task,
> >>                                                    struct bpf_task_work *tw, void *map__map,
> >>                                                    bpf_task_work_callback_t callback,
> >>                                                    void *aux__prog)
> >>  {
> >> -       return bpf_task_work_schedule(task, tw, map__map, callback, aux__prog, TWA_SIGNAL);
> >> +       return bpf_task_work_schedule_signal(task, tw, map__map, callback, aux__prog);
> >>  }
> >
> > I thought we decided that _impl() will not be marked as __bpf_kfunc
> > and will not be in BTF_ID(func, _impl).
> > We can mark it as __weak noinline and it will be in kallsyms.
> > That's all we need for the verifier and resolve_btfid, no?
> >
> > Sorry, it's been a long time. I must have forgotten something.
>
> For the *generated* _impl kfuncs there is no decl tags and the ids are
> absent from BTF_ID sets, yes.
>
> However for the "legacy" cases it must be there for backwards
> compatibility, as well as relevant verifier checks.

I see.
I feel bpf_task_work_schedule_resume() is ok to break, since it's so new.
We can remove bpf_task_work_schedule_[resume|singal]_impl()
to avoid carrying forward forever.

bpf_stream_vprintk_impl() is not that clear. I would remove it too.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 07/10] HID: Use bpf_wq_set_callback kernel function
  2026-01-09 18:48 ` [PATCH bpf-next v1 07/10] HID: Use bpf_wq_set_callback kernel function Ihor Solodrai
@ 2026-01-09 21:34   ` Benjamin Tissoires
  0 siblings, 0 replies; 44+ messages in thread
From: Benjamin Tissoires @ 2026-01-09 21:34 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Jiri Kosina, bpf, linux-kernel, linux-input,
	sched-ext

On Jan 09 2026, Ihor Solodrai wrote:
> Remove extern declaration of bpf_wq_set_callback_impl() from
> hid_bpf_helpers.h and replace bpf_wq_set_callback macro with a
> corresponding new declaration.
> 
> Fix selftests/hid build failure caused by missing BPF_CFLAGS.

Already fixed in e03fb369b083 ("selftests/hid: fix bpf compilations due to -fms-extensions")

> 
> Tested with:
>   # append tools/testing/selftests/hid/config and build the kernel
>   $ make -C tools/testing/selftests/hid
>   # in built kernel
>   $ ./tools/testing/selftests/hid/hid_bpf -t test_multiply_events_wq
> 
>   TAP version 13
>   1..1
>   # Starting 1 tests from 1 test cases.
>   #  RUN           hid_bpf.test_multiply_events_wq ...
>   [    2.575520] hid-generic 0003:0001:0A36.0001: hidraw0: USB HID v0.00 Device [test-uhid-device-138] on 138
>   #            OK  hid_bpf.test_multiply_events_wq
>   ok 1 hid_bpf.test_multiply_events_wq
>   # PASSED: 1 / 1 tests passed.
>   # Totals: pass:1 fail:0 xfail:0 xpass:0 skip:0 error:0
>   PASS
> 
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  drivers/hid/bpf/progs/hid_bpf_helpers.h             | 8 +++-----
>  tools/testing/selftests/hid/Makefile                | 4 +++-
>  tools/testing/selftests/hid/progs/hid_bpf_helpers.h | 8 +++-----
>  3 files changed, 9 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/hid/bpf/progs/hid_bpf_helpers.h b/drivers/hid/bpf/progs/hid_bpf_helpers.h
> index bf19785a6b06..228f8d787567 100644
> --- a/drivers/hid/bpf/progs/hid_bpf_helpers.h
> +++ b/drivers/hid/bpf/progs/hid_bpf_helpers.h
> @@ -33,11 +33,9 @@ extern int hid_bpf_try_input_report(struct hid_bpf_ctx *ctx,
>  /* bpf_wq implementation */
>  extern int bpf_wq_init(struct bpf_wq *wq, void *p__map, unsigned int flags) __weak __ksym;
>  extern int bpf_wq_start(struct bpf_wq *wq, unsigned int flags) __weak __ksym;
> -extern int bpf_wq_set_callback_impl(struct bpf_wq *wq,
> -		int (callback_fn)(void *map, int *key, void *value),
> -		unsigned int flags__k, void *aux__ign) __ksym;
> -#define bpf_wq_set_callback(wq, cb, flags) \
> -	bpf_wq_set_callback_impl(wq, cb, flags, NULL)
> +extern int bpf_wq_set_callback(struct bpf_wq *wq,
> +		int (*callback_fn)(void *, int *, void *),
> +		unsigned int flags) __weak __ksym;

FWIW, if I'm the only one using bpf_wq_set_callback_impl() that you are
aware of, I'm fine removing the _impl kfunc from the kernel, I can deal
with this when loading the programs.

>  
>  #define HID_MAX_DESCRIPTOR_SIZE	4096
>  #define HID_IGNORE_EVENT	-1
> diff --git a/tools/testing/selftests/hid/Makefile b/tools/testing/selftests/hid/Makefile
> index 2839d2612ce3..4c01bb649913 100644
> --- a/tools/testing/selftests/hid/Makefile
> +++ b/tools/testing/selftests/hid/Makefile
> @@ -184,7 +184,9 @@ MENDIAN=$(if $(IS_LITTLE_ENDIAN),-mlittle-endian,-mbig-endian)
>  
>  CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG))
>  BPF_CFLAGS = -g -Werror -D__TARGET_ARCH_$(SRCARCH) $(MENDIAN) 		\
> -	     -I$(INCLUDE_DIR)
> +	     -I$(INCLUDE_DIR)						\
> +	     -Wno-microsoft-anon-tag					\
> +	     -fms-extensions

Already in Linus' tree.

>  
>  CLANG_CFLAGS = $(CLANG_SYS_INCLUDES) \
>  	       -Wno-compare-distinct-pointer-types
> diff --git a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
> index 531228b849da..80ab60905865 100644
> --- a/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
> +++ b/tools/testing/selftests/hid/progs/hid_bpf_helpers.h
> @@ -116,10 +116,8 @@ extern int hid_bpf_try_input_report(struct hid_bpf_ctx *ctx,
>  /* bpf_wq implementation */
>  extern int bpf_wq_init(struct bpf_wq *wq, void *p__map, unsigned int flags) __weak __ksym;
>  extern int bpf_wq_start(struct bpf_wq *wq, unsigned int flags) __weak __ksym;
> -extern int bpf_wq_set_callback_impl(struct bpf_wq *wq,
> -		int (callback_fn)(void *map, int *key, void *wq),
> -		unsigned int flags__k, void *aux__ign) __weak __ksym;
> -#define bpf_wq_set_callback(timer, cb, flags) \
> -	bpf_wq_set_callback_impl(timer, cb, flags, NULL)
> +extern int bpf_wq_set_callback(struct bpf_wq *wq,
> +		int (*callback_fn)(void *, int *, void *),
> +		unsigned int flags) __weak __ksym;
>  
>  #endif /* __HID_BPF_HELPERS_H */
> -- 
> 2.52.0
> 

Acked-by: Benjamin Tissoires <bentiss@kernel.org>

Cheers,
Benjamin

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 08/10] bpf: Add bpf_task_work_schedule_* kfuncs with KF_IMPLICIT_ARGS
  2026-01-09 20:47       ` Alexei Starovoitov
@ 2026-01-09 21:39         ` Ihor Solodrai
  2026-01-09 21:49           ` Alexei Starovoitov
  0 siblings, 1 reply; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 21:39 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, LKML,
	open list:HID CORE LAYER, sched-ext

On 1/9/26 12:47 PM, Alexei Starovoitov wrote:
> On Fri, Jan 9, 2026 at 12:02 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>
>> On 1/9/26 11:58 AM, Alexei Starovoitov wrote:
>>> On Fri, Jan 9, 2026 at 10:50 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>>>
>>>> +__bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct bpf_task_work *tw,
>>>> +                                             void *map__map, bpf_task_work_callback_t callback,
>>>> +                                             struct bpf_prog_aux *aux)
>>>> +{
>>>> +       return bpf_task_work_schedule(task, tw, map__map, callback, aux, TWA_SIGNAL);
>>>> +}
>>>> +
>>>>  __bpf_kfunc int bpf_task_work_schedule_signal_impl(struct task_struct *task,
>>>>                                                    struct bpf_task_work *tw, void *map__map,
>>>>                                                    bpf_task_work_callback_t callback,
>>>>                                                    void *aux__prog)
>>>>  {
>>>> -       return bpf_task_work_schedule(task, tw, map__map, callback, aux__prog, TWA_SIGNAL);
>>>> +       return bpf_task_work_schedule_signal(task, tw, map__map, callback, aux__prog);
>>>>  }
>>>
>>> I thought we decided that _impl() will not be marked as __bpf_kfunc
>>> and will not be in BTF_ID(func, _impl).
>>> We can mark it as __weak noinline and it will be in kallsyms.
>>> That's all we need for the verifier and resolve_btfid, no?
>>>
>>> Sorry, it's been a long time. I must have forgotten something.
>>
>> For the *generated* _impl kfuncs there is no decl tags and the ids are
>> absent from BTF_ID sets, yes.
>>
>> However for the "legacy" cases it must be there for backwards
>> compatibility, as well as relevant verifier checks.
> 
> I see.
> I feel bpf_task_work_schedule_resume() is ok to break, since it's so new.
> We can remove bpf_task_work_schedule_[resume|singal]_impl()
> to avoid carrying forward forever.
> 
> bpf_stream_vprintk_impl() is not that clear. I would remove it too.

That leaves only bpf_wq_set_callback_impl(). Can we break that too?

Then there won't be legacy cases at all. It was introduced in v6.16
along the with __prog suffix [1][2].

If we go this route, we could clean up __prog support/docs too.

I think it's worth it to make an "all or nothing" decision here:
either break all 4 existing kfuncs, or backwards-support all of them.

git tag --contains bc049387b41f | grep -v rc
v6.16
v6.17
v6.18

[1] https://lore.kernel.org/all/20250513142812.1021591-1-memxor@gmail.com/
[2] https://lore.kernel.org/all/20240420-bpf_wq-v2-13-6c986a5a741f@kernel.org/



^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 08/10] bpf: Add bpf_task_work_schedule_* kfuncs with KF_IMPLICIT_ARGS
  2026-01-09 21:39         ` Ihor Solodrai
@ 2026-01-09 21:49           ` Alexei Starovoitov
  2026-01-09 21:56             ` Ihor Solodrai
  0 siblings, 1 reply; 44+ messages in thread
From: Alexei Starovoitov @ 2026-01-09 21:49 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, LKML,
	open list:HID CORE LAYER, sched-ext

On Fri, Jan 9, 2026 at 1:39 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> On 1/9/26 12:47 PM, Alexei Starovoitov wrote:
> > On Fri, Jan 9, 2026 at 12:02 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
> >>
> >> On 1/9/26 11:58 AM, Alexei Starovoitov wrote:
> >>> On Fri, Jan 9, 2026 at 10:50 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
> >>>>
> >>>> +__bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct bpf_task_work *tw,
> >>>> +                                             void *map__map, bpf_task_work_callback_t callback,
> >>>> +                                             struct bpf_prog_aux *aux)
> >>>> +{
> >>>> +       return bpf_task_work_schedule(task, tw, map__map, callback, aux, TWA_SIGNAL);
> >>>> +}
> >>>> +
> >>>>  __bpf_kfunc int bpf_task_work_schedule_signal_impl(struct task_struct *task,
> >>>>                                                    struct bpf_task_work *tw, void *map__map,
> >>>>                                                    bpf_task_work_callback_t callback,
> >>>>                                                    void *aux__prog)
> >>>>  {
> >>>> -       return bpf_task_work_schedule(task, tw, map__map, callback, aux__prog, TWA_SIGNAL);
> >>>> +       return bpf_task_work_schedule_signal(task, tw, map__map, callback, aux__prog);
> >>>>  }
> >>>
> >>> I thought we decided that _impl() will not be marked as __bpf_kfunc
> >>> and will not be in BTF_ID(func, _impl).
> >>> We can mark it as __weak noinline and it will be in kallsyms.
> >>> That's all we need for the verifier and resolve_btfid, no?
> >>>
> >>> Sorry, it's been a long time. I must have forgotten something.
> >>
> >> For the *generated* _impl kfuncs there is no decl tags and the ids are
> >> absent from BTF_ID sets, yes.
> >>
> >> However for the "legacy" cases it must be there for backwards
> >> compatibility, as well as relevant verifier checks.
> >
> > I see.
> > I feel bpf_task_work_schedule_resume() is ok to break, since it's so new.
> > We can remove bpf_task_work_schedule_[resume|singal]_impl()
> > to avoid carrying forward forever.
> >
> > bpf_stream_vprintk_impl() is not that clear. I would remove it too.
>
> That leaves only bpf_wq_set_callback_impl(). Can we break that too?

Sounds like Benjamin is ok removing it.
So I think we can indeed remove them all.

> Then there won't be legacy cases at all. It was introduced in v6.16
> along the with __prog suffix [1][2].
>
> If we go this route, we could clean up __prog support/docs too.
>
> I think it's worth it to make an "all or nothing" decision here:
> either break all 4 existing kfuncs, or backwards-support all of them.

I don't see why "all or nothing" is a good thing.
It won't be "all" anyway.
We have bpf_rbtree_add_impl(), bpf_list_push_front_impl(), etc.
And those we cannot remove. sched-ext is using them.
Another few categories are bpf_obj_new_impl(), bpf_obj_drop_impl().
There are not __prog type, but conceptually the same thing and
KF_IMPLICIT_ARGS should support them too eventually.


> git tag --contains bc049387b41f | grep -v rc
> v6.16
> v6.17
> v6.18
>
> [1] https://lore.kernel.org/all/20250513142812.1021591-1-memxor@gmail.com/
> [2] https://lore.kernel.org/all/20240420-bpf_wq-v2-13-6c986a5a741f@kernel.org/
>
>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 08/10] bpf: Add bpf_task_work_schedule_* kfuncs with KF_IMPLICIT_ARGS
  2026-01-09 21:49           ` Alexei Starovoitov
@ 2026-01-09 21:56             ` Ihor Solodrai
  2026-01-12 18:53               ` Ihor Solodrai
  0 siblings, 1 reply; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-09 21:56 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, LKML,
	open list:HID CORE LAYER, sched-ext

On 1/9/26 1:49 PM, Alexei Starovoitov wrote:
> On Fri, Jan 9, 2026 at 1:39 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>
>> On 1/9/26 12:47 PM, Alexei Starovoitov wrote:
>>> On Fri, Jan 9, 2026 at 12:02 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>>>
>>>> On 1/9/26 11:58 AM, Alexei Starovoitov wrote:
>>>>> On Fri, Jan 9, 2026 at 10:50 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>>>>>
>>>>>> +__bpf_kfunc int bpf_task_work_schedule_signal(struct task_struct *task, struct bpf_task_work *tw,
>>>>>> +                                             void *map__map, bpf_task_work_callback_t callback,
>>>>>> +                                             struct bpf_prog_aux *aux)
>>>>>> +{
>>>>>> +       return bpf_task_work_schedule(task, tw, map__map, callback, aux, TWA_SIGNAL);
>>>>>> +}
>>>>>> +
>>>>>>  __bpf_kfunc int bpf_task_work_schedule_signal_impl(struct task_struct *task,
>>>>>>                                                    struct bpf_task_work *tw, void *map__map,
>>>>>>                                                    bpf_task_work_callback_t callback,
>>>>>>                                                    void *aux__prog)
>>>>>>  {
>>>>>> -       return bpf_task_work_schedule(task, tw, map__map, callback, aux__prog, TWA_SIGNAL);
>>>>>> +       return bpf_task_work_schedule_signal(task, tw, map__map, callback, aux__prog);
>>>>>>  }
>>>>>
>>>>> I thought we decided that _impl() will not be marked as __bpf_kfunc
>>>>> and will not be in BTF_ID(func, _impl).
>>>>> We can mark it as __weak noinline and it will be in kallsyms.
>>>>> That's all we need for the verifier and resolve_btfid, no?
>>>>>
>>>>> Sorry, it's been a long time. I must have forgotten something.
>>>>
>>>> For the *generated* _impl kfuncs there is no decl tags and the ids are
>>>> absent from BTF_ID sets, yes.
>>>>
>>>> However for the "legacy" cases it must be there for backwards
>>>> compatibility, as well as relevant verifier checks.
>>>
>>> I see.
>>> I feel bpf_task_work_schedule_resume() is ok to break, since it's so new.
>>> We can remove bpf_task_work_schedule_[resume|singal]_impl()
>>> to avoid carrying forward forever.
>>>
>>> bpf_stream_vprintk_impl() is not that clear. I would remove it too.
>>
>> That leaves only bpf_wq_set_callback_impl(). Can we break that too?
> 
> Sounds like Benjamin is ok removing it.
> So I think we can indeed remove them all.
> 
>> Then there won't be legacy cases at all. It was introduced in v6.16
>> along the with __prog suffix [1][2].
>>
>> If we go this route, we could clean up __prog support/docs too.
>>
>> I think it's worth it to make an "all or nothing" decision here:
>> either break all 4 existing kfuncs, or backwards-support all of them.
> 
> I don't see why "all or nothing" is a good thing.
> It won't be "all" anyway.
> We have bpf_rbtree_add_impl(), bpf_list_push_front_impl(), etc.
> And those we cannot remove. sched-ext is using them.
> Another few categories are bpf_obj_new_impl(), bpf_obj_drop_impl().
> There are not __prog type, but conceptually the same thing and
> KF_IMPLICIT_ARGS should support them too eventually.

I was thinking we could remove/simplify code relevant to backwards
compat of existing _impl kfuncs. But you're right, if we start using
implicit args for other types/kfuncs, the "legacy" case still has to
work.

Ok, in the next revision I'll remove all the __prog users, but leave
the "legacy" case support in place for future use.

> 
> 
>> git tag --contains bc049387b41f | grep -v rc
>> v6.16
>> v6.17
>> v6.18
>>
>> [1] https://lore.kernel.org/all/20250513142812.1021591-1-memxor@gmail.com/
>> [2] https://lore.kernel.org/all/20240420-bpf_wq-v2-13-6c986a5a741f@kernel.org/
>>
>>


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
  2026-01-09 18:48 ` [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS Ihor Solodrai
  2026-01-09 19:54   ` Alexei Starovoitov
@ 2026-01-09 23:25   ` Andrii Nakryiko
  2026-01-13 20:39   ` Eduard Zingerman
  2026-01-13 21:59   ` Eduard Zingerman
  3 siblings, 0 replies; 44+ messages in thread
From: Andrii Nakryiko @ 2026-01-09 23:25 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, linux-kernel,
	linux-input, sched-ext

On Fri, Jan 9, 2026 at 10:49 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> A kernel function bpf_foo marked with KF_IMPLICIT_ARGS flag is
> expected to have two associated types in BTF:
>   * `bpf_foo` with a function prototype that omits implicit arguments
>   * `bpf_foo_impl` with a function prototype that matches the kernel
>      declaration of `bpf_foo`, but doesn't have a ksym associated with
>      its name
>
> In order to support kfuncs with implicit arguments, the verifier has
> to know how to resolve a call of `bpf_foo` to the correct BTF function
> prototype and address.
>
> To implement this, in add_kfunc_call() kfunc flags are checked for
> KF_IMPLICIT_ARGS. For such kfuncs a BTF func prototype is adjusted to
> the one found for `bpf_foo_impl` (func_name + "_impl" suffix, by
> convention) function in BTF.
>
> This effectively changes the signature of the `bpf_foo` kfunc in the
> context of verification: from one without implicit args to the one
> with full argument list.
>
> Whether a kfunc argument is implicit or not is determined by
> is_kfunc_arg_implicit(). The values of implicit arguments by design
> are provided by the verifier, and so they can only be of particular
> types. In this patch the only allowed implicit arg type is a pointer
> to struct bpf_prog_aux. The __prog args (usually void *) are also
> considered implicit for backwards compatibility.
>
> In order to enable the verifier to correctly set an implicit
> bpf_prog_aux arg value at runtime, is_kfunc_arg_prog() is extended to
> check for the arg type. At a point when prog arg is determined in
> check_kfunc_args() the kfunc with implicit args already has a
> prototype with full argument list, so the existing value patch
> mechanism just works.
>
> If a new kfunc with KF_IMPLICIT_ARG is declared for an existing kfunc
> that uses a __prog argument (a legacy case), the prototype
> substitution works in exactly the same way, assuming the kfunc follows
> the _impl naming convention. The difference is only in how _impl
> prototype is added to the BTF, which is not the verifier's
> concern. See a subsequent resolve_btfids patch for details.
>
> In check_kfunc_call() reset the subreg_def of registers holding
> implicit arguments to correctly track zero extensions.
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  include/linux/btf.h   |  1 +
>  kernel/bpf/verifier.c | 70 +++++++++++++++++++++++++++++++++++++++++--
>  2 files changed, 69 insertions(+), 2 deletions(-)
>

[...]

> +       impl_id = btf_find_by_name_kind(btf, impl_name, BTF_KIND_FUNC);
> +       if (impl_id <= 0) {
> +               verbose(env, "cannot find function %s in BTF\n", impl_name);
> +               return NULL;
> +       }
> +
> +       func = btf_type_by_id(btf, impl_id);
> +       if (!func || !btf_type_is_func(func)) {

btf_find_by_name_kind() above guarantees that we both will have
non-NULL func and it will be BTF_KIND_FUNC, drop these defensive
checks.

> +               verbose(env, "%s (btf_id %d) is not a function\n", impl_name, impl_id);
> +               return NULL;
> +       }
> +
> +       return btf_type_by_id(btf, func->type);
> +}
> +
>  static int fetch_kfunc_meta(struct bpf_verifier_env *env,
>                             s32 func_id,
>                             s16 offset,
> @@ -3308,7 +3340,16 @@ static int fetch_kfunc_meta(struct bpf_verifier_env *env,
>         }
>
>         func_name = btf_name_by_offset(btf, func->name_off);
> -       func_proto = btf_type_by_id(btf, func->type);
> +
> +       /*
> +        * An actual prototype of a kfunc with KF_IMPLICIT_ARGS flag
> +        * can be found through the counterpart _impl kfunc.
> +        */
> +       if (unlikely(kfunc_flags && KF_IMPLICIT_ARGS & *kfunc_flags))

drop unlikely(), it's unnecessary micro-optimization (if at all)

(I'd also swap order to more conventional: `*kfunc_flags & KF_IMPLICIT_ARGS`)

> +               func_proto = find_kfunc_impl_proto(env, btf, func_name);
> +       else
> +               func_proto = btf_type_by_id(btf, func->type);
> +
>         if (!func_proto || !btf_type_is_func_proto(func_proto)) {
>                 verbose(env, "kernel function btf_id %d does not have a valid func_proto\n",
>                         func_id);

[...]

> @@ -14303,6 +14358,17 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>         for (i = 0; i < nargs; i++) {
>                 u32 regno = i + 1;
>
> +               /*
> +                * Implicit kfunc arguments are set after main verification pass.
> +                * For correct tracking of zero-extensions we have to reset subreg_def for such
> +                * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
> +                * from an earlier (irrelevant) point in the program, which may lead to an error
> +                * in opt_subreg_zext_lo32_rnd_hi32().
> +                */
> +               if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
> +                               && is_kfunc_arg_implicit(desc_btf, &args[i])))
> +                       regs[regno].subreg_def = DEF_NOT_SUBREG;

ditto about unlikely(), this is used for rare cases where performance
matters a lot (and it's obvious which case is "common", so should be
kept linear in assembly code)


> +
>                 t = btf_type_skip_modifiers(desc_btf, args[i].type, NULL);
>                 if (btf_type_is_ptr(t))
>                         mark_btf_func_reg_size(env, regno, sizeof(void *));
> --
> 2.52.0
>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
  2026-01-09 18:48 ` [PATCH bpf-next v1 04/10] resolve_btfids: Support " Ihor Solodrai
  2026-01-09 19:15   ` bot+bpf-ci
@ 2026-01-09 23:25   ` Andrii Nakryiko
  2026-01-10  1:15     ` Ihor Solodrai
  1 sibling, 1 reply; 44+ messages in thread
From: Andrii Nakryiko @ 2026-01-09 23:25 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, linux-kernel,
	linux-input, sched-ext

On Fri, Jan 9, 2026 at 10:49 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> Implement BTF modifications in resolve_btfids to support BPF kernel
> functions with implicit arguments.
>
> For a kfunc marked with KF_IMPLICIT_ARGS flag, a new function
> prototype is added to BTF that does not have implicit arguments. The
> kfunc's prototype is then updated to a new one in BTF. This prototype
> is the intended interface for the BPF programs.
>
> A <func_name>_impl function is added to BTF to make the original kfunc
> prototype searchable for the BPF verifier. If a <func_name>_impl
> function already exists in BTF, its interpreted as a legacy case, and
> this step is skipped.
>
> Whether an argument is implicit is determined by its type:
> currently only `struct bpf_prog_aux *` is supported.
>
> As a result, the BTF associated with kfunc is changed from
>
>     __bpf_kfunc bpf_foo(int arg1, struct bpf_prog_aux *aux);
>
> into
>
>     bpf_foo_impl(int arg1, struct bpf_prog_aux *aux);
>     __bpf_kfunc bpf_foo(int arg1);
>
> For more context see previous discussions and patches [1][2].
>
> [1] https://lore.kernel.org/dwarves/ba1650aa-fafd-49a8-bea4-bdddee7c38c9@linux.dev/
> [2] https://lore.kernel.org/bpf/20251029190113.3323406-1-ihor.solodrai@linux.dev/
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  tools/bpf/resolve_btfids/main.c | 282 ++++++++++++++++++++++++++++++++
>  1 file changed, 282 insertions(+)
>
> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> index df39982f51df..b361e726fa36 100644
> --- a/tools/bpf/resolve_btfids/main.c
> +++ b/tools/bpf/resolve_btfids/main.c
> @@ -152,6 +152,18 @@ struct object {
>         int nr_typedefs;
>  };
>
> +#define KF_IMPLICIT_ARGS (1 << 16)
> +#define KF_IMPL_SUFFIX "_impl"
> +#define MAX_BPF_FUNC_REG_ARGS 5
> +#define MAX_KFUNCS 256
> +#define MAX_DECL_TAGS (MAX_KFUNCS * 4)

can't we get that from include/linux/bpf.h? seems like
resolve_btfids's main.c include internal headers just fine, so why
duplicate definitions?

> +
> +struct btf2btf_context {
> +       struct btf *btf;
> +       u32 nr_decl_tags;
> +       s32 decl_tags[MAX_DECL_TAGS];
> +};
> +
>  static int verbose;
>  static int warnings;
>
> @@ -972,6 +984,273 @@ static int patch_btfids(const char *btfids_path, const char *elf_path)
>         return err;
>  }
>
> +static s64 collect_kfunc_ids_by_flags(struct object *obj,
> +                                     u32 flags,
> +                                     s32 kfunc_ids[],
> +                                     const u32 kfunc_ids_sz)
> +{
> +       Elf_Data *data = obj->efile.idlist;
> +       struct rb_node *next;
> +       s64 nr_kfuncs = 0;
> +       int i;
> +
> +       next = rb_first(&obj->sets);
> +       while (next) {
> +               struct btf_id_set8 *set8 = NULL;
> +               unsigned long addr, off;
> +               struct btf_id *id;
> +
> +               id = rb_entry(next, struct btf_id, rb_node);
> +
> +               if (id->kind != BTF_ID_KIND_SET8)
> +                       goto skip;
> +
> +               addr = id->addr[0];
> +               off = addr - obj->efile.idlist_addr;
> +               set8 = data->d_buf + off;
> +
> +               for (i = 0; i < set8->cnt; i++) {
> +                       if (set8->pairs[i].flags & flags) {

invert condition and continue, reduce nesting?

> +                               if (nr_kfuncs >= kfunc_ids_sz) {

it's silly to set static limits like this: we are not in NMI, you have
memory allocator, use it

> +                                       pr_err("ERROR: resolve_btfids: too many kfuncs with flags %u - limit %d\n",
> +                                              flags, kfunc_ids_sz);
> +                                       return -E2BIG;
> +                               }
> +                               kfunc_ids[nr_kfuncs++] = set8->pairs[i].id;
> +                       }
> +               }
> +skip:
> +               next = rb_next(next);
> +       }
> +
> +       return nr_kfuncs;
> +}
> +
> +static const struct btf_type *btf__unqualified_type_by_id(const struct btf *btf, s32 type_id)
> +{
> +       const struct btf_type *t = btf__type_by_id(btf, type_id);
> +
> +       while (btf_is_mod(t))
> +               t = btf__type_by_id(btf, t->type);
> +
> +       return t;
> +}
> +
> +/* Implicit BPF kfunc arguments can only be of particular types */
> +static bool btf__is_kf_implicit_arg(const struct btf *btf, const struct btf_param *p)

nit: not sure why you use double underscore convention, this is not
really an "API" of btf, it's just a helper... I'd just call it
"is_kf_implicit_arg", btf is just and input argument

> +{
> +       static const char *const kf_implicit_arg_types[] = {
> +               "bpf_prog_aux",
> +       };
> +       const struct btf_type *t;
> +       const char *name;
> +

[...]

> +       /* Copy all decl tags except "bpf_kfunc" from the original kfunc to the new one */
> +       for (int i = 0; i < ctx->nr_decl_tags; i++) {
> +               t = (struct btf_type *)btf__type_by_id(btf, ctx->decl_tags[i]);
> +               if (t->type != kfunc_id)
> +                       continue;
> +
> +               tag_name = btf__name_by_offset(btf, t->name_off);
> +               if (strcmp(tag_name, "bpf_kfunc") == 0)
> +                       continue;
> +
> +               err = btf__add_decl_tag(btf, tag_name, new_func_id, -1);

decl_tag can apply to arguments as well (that -1 will be actually >=
0), we should copy those as well, no?

> +               if (err < 0) {
> +                       pr_err("ERROR: resolve_btfids: failed to add decl tag %s for %s\n",
> +                              tag_name, tmp_name);
> +                       return -EINVAL;
> +               }
> +       }
> +
> +add_new_proto:
> +       /*
> +        * Drop the _impl suffix and point kfunc_name to the local buffer for later use.
> +        * When BTF is modified the original pointer is invalidated.
> +        */
> +       tmp_name[name_len] = '\0';
> +       kfunc_name = tmp_name;
> +
> +       /* Load non-implicit args from the original prototype */
> +       t = (struct btf_type *)btf__type_by_id(btf, proto_id);
> +       params = btf_params(t);
> +       nr_params = 0;
> +       for (int i = 0; i < btf_vlen(t); i++) {
> +               if (btf__is_kf_implicit_arg(btf, &params[i]))
> +                       break;
> +               new_params[nr_params++] = params[i];
> +       }

this is unnecessary, just when you working with original prototype and
its arguments, just re-fetch it back from IDs, all those pointers will
be valid until next btf__add_xxx() invocation, no need for this
awkward local copies

> +
> +       new_proto_id = btf__add_func_proto(btf, t->type);
> +       if (new_proto_id < 0) {
> +               pr_err("ERROR: resolve_btfids: failed to add func proto for %s\n", kfunc_name);
> +               return new_proto_id;
> +       }
> +
> +       /* Add non-implicit args to the new prototype */
> +       for (int i = 0; i < nr_params; i++) {
> +               param_name = btf__name_by_offset(btf, new_params[i].name_off);
> +               err = btf__add_func_param(btf, param_name, new_params[i].type);
> +               if (err < 0) {
> +                       pr_err("ERROR: resolve_btfids: failed to add param %s for %s\n",
> +                              param_name, kfunc_name);
> +                       return err;
> +               }
> +       }
> +
> +       /* Finally change the prototype of the original kfunc to the new one */
> +       t = (struct btf_type *)btf__type_by_id(btf, kfunc_id);
> +       t->type = new_proto_id;
> +
> +       pr_debug("resolve_btfids: updated BTF for kfunc with implicit args %s\n", kfunc_name);
> +
> +       return 0;
> +}
> +
> +static s64 btf__collect_decl_tags(const struct btf *btf, s32 *decl_tags, u32 decl_tags_sz)
> +{
> +       const u32 type_cnt = btf__type_cnt(btf);
> +       const struct btf_type *t;
> +       s64 nr_decl_tags = 0;
> +
> +       for (u32 id = 1; id < type_cnt; id++) {
> +               t = btf__type_by_id(btf, id);
> +               if (!btf_is_decl_tag(t))
> +                       continue;
> +               if (nr_decl_tags >= decl_tags_sz) {
> +                       pr_err("ERROR: resolve_btfids: too many decl tags in BTF - limit %s\n",

realloc is your friend, no fixed-sized arrays

> +                               decl_tags_sz);
> +                       return -E2BIG;
> +               }
> +               decl_tags[nr_decl_tags++] = id;
> +       }
> +
> +       return nr_decl_tags;
> +}
> +

[...]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 05/10] selftests/bpf: Add tests for KF_IMPLICIT_ARGS
  2026-01-09 18:48 ` [PATCH bpf-next v1 05/10] selftests/bpf: Add tests " Ihor Solodrai
@ 2026-01-09 23:25   ` Andrii Nakryiko
  2026-01-10  1:29     ` Ihor Solodrai
  0 siblings, 1 reply; 44+ messages in thread
From: Andrii Nakryiko @ 2026-01-09 23:25 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, linux-kernel,
	linux-input, sched-ext

On Fri, Jan 9, 2026 at 10:49 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> Add trivial end-to-end tests to validate that KF_IMPLICIT_ARGS flag is
> properly handled by both resolve_btfids and the verifier.
>
> Declare kfuncs in bpf_testmod. Check that bpf_prog_aux pointer is set
> in the kfunc implementation. Verify that calls with implicit args and
> a legacy case all work.
>
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---
>  .../bpf/prog_tests/kfunc_implicit_args.c      | 10 +++++
>  .../selftests/bpf/progs/kfunc_implicit_args.c | 41 +++++++++++++++++++
>  .../selftests/bpf/test_kmods/bpf_testmod.c    | 26 ++++++++++++
>  3 files changed, 77 insertions(+)
>  create mode 100644 tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c
>  create mode 100644 tools/testing/selftests/bpf/progs/kfunc_implicit_args.c
>

[...]

> @@ -0,0 +1,41 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
> +
> +#include <vmlinux.h>
> +#include <bpf/bpf_helpers.h>
> +#include "bpf_misc.h"
> +
> +extern int bpf_kfunc_implicit_arg(int a) __weak __ksym;
> +extern int bpf_kfunc_implicit_arg_impl(int a, void *aux__prog) __weak __ksym; // illegal

C++ comment

> +extern int bpf_kfunc_implicit_arg_legacy(int a, int b) __weak __ksym;
> +extern int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, void *aux__prog) __weak __ksym;
> +
> +char _license[] SEC("license") = "GPL";
> +

[...]

> diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> index 1c41d03bd5a1..503451875d33 100644
> --- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> +++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> @@ -1136,6 +1136,10 @@ __bpf_kfunc int bpf_kfunc_st_ops_inc10(struct st_ops_args *args)
>  __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id);
>  __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1_impl(struct st_ops_args *args, void *aux_prog);
>
> +__bpf_kfunc int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux);
> +__bpf_kfunc int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux);
> +__bpf_kfunc int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, void *aux__prog);
> +
>  BTF_KFUNCS_START(bpf_testmod_check_kfunc_ids)
>  BTF_ID_FLAGS(func, bpf_testmod_test_mod_kfunc)
>  BTF_ID_FLAGS(func, bpf_kfunc_call_test1)
> @@ -1178,6 +1182,9 @@ BTF_ID_FLAGS(func, bpf_kfunc_st_ops_test_pro_epilogue, KF_SLEEPABLE)
>  BTF_ID_FLAGS(func, bpf_kfunc_st_ops_inc10)
>  BTF_ID_FLAGS(func, bpf_kfunc_multi_st_ops_test_1)
>  BTF_ID_FLAGS(func, bpf_kfunc_multi_st_ops_test_1_impl)
> +BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg, KF_IMPLICIT_ARGS)
> +BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy, KF_IMPLICIT_ARGS)
> +BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy_impl)

(irrelevant, now that I saw patch #8 discussion, but for the future
the point will stand and we can decide how resolve_btfids handles this
upfront)

I'm wondering, should we add KF_IMPLICIT_ARGS to legacy xxx_impl
kfuncs as well to explicitly mark them to resolve_btfids as legacy
implementations? And if we somehow find xxx_impl without it, then
resolve_btfids complains louds and fails, this should never happen?



>  BTF_KFUNCS_END(bpf_testmod_check_kfunc_ids)
>
>  static int bpf_testmod_ops_init(struct btf *btf)
> @@ -1669,6 +1676,25 @@ int bpf_kfunc_multi_st_ops_test_1_impl(struct st_ops_args *args, void *aux__prog
>         return ret;
>  }
>
> +int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux)
> +{
> +       if (aux && a > 0)
> +               return a;
> +       return -EINVAL;
> +}
> +
> +int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux)
> +{
> +       if (aux)
> +               return a + b;
> +       return -EINVAL;
> +}
> +
> +int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, void *aux__prog)
> +{
> +       return bpf_kfunc_implicit_arg_legacy(a, b, aux__prog);
> +}
> +
>  static int multi_st_ops_reg(void *kdata, struct bpf_link *link)
>  {
>         struct bpf_testmod_multi_st_ops *st_ops =
> --
> 2.52.0
>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
  2026-01-09 23:25   ` Andrii Nakryiko
@ 2026-01-10  1:15     ` Ihor Solodrai
  2026-01-12 16:51       ` Andrii Nakryiko
  0 siblings, 1 reply; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-10  1:15 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, linux-kernel,
	linux-input, sched-ext

On 1/9/26 3:25 PM, Andrii Nakryiko wrote:
> On Fri, Jan 9, 2026 at 10:49 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>
>> Implement BTF modifications in resolve_btfids to support BPF kernel
>> functions with implicit arguments.
>>
>> For a kfunc marked with KF_IMPLICIT_ARGS flag, a new function
>> prototype is added to BTF that does not have implicit arguments. The
>> kfunc's prototype is then updated to a new one in BTF. This prototype
>> is the intended interface for the BPF programs.
>>
>> A <func_name>_impl function is added to BTF to make the original kfunc
>> prototype searchable for the BPF verifier. If a <func_name>_impl
>> function already exists in BTF, its interpreted as a legacy case, and
>> this step is skipped.
>>
>> Whether an argument is implicit is determined by its type:
>> currently only `struct bpf_prog_aux *` is supported.
>>
>> As a result, the BTF associated with kfunc is changed from
>>
>>     __bpf_kfunc bpf_foo(int arg1, struct bpf_prog_aux *aux);
>>
>> into
>>
>>     bpf_foo_impl(int arg1, struct bpf_prog_aux *aux);
>>     __bpf_kfunc bpf_foo(int arg1);
>>
>> For more context see previous discussions and patches [1][2].
>>
>> [1] https://lore.kernel.org/dwarves/ba1650aa-fafd-49a8-bea4-bdddee7c38c9@linux.dev/
>> [2] https://lore.kernel.org/bpf/20251029190113.3323406-1-ihor.solodrai@linux.dev/
>>
>> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
>> ---
>>  tools/bpf/resolve_btfids/main.c | 282 ++++++++++++++++++++++++++++++++
>>  1 file changed, 282 insertions(+)
>>
>> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
>> index df39982f51df..b361e726fa36 100644
>> --- a/tools/bpf/resolve_btfids/main.c
>> +++ b/tools/bpf/resolve_btfids/main.c
>> @@ -152,6 +152,18 @@ struct object {
>>         int nr_typedefs;
>>  };
>>
>> +#define KF_IMPLICIT_ARGS (1 << 16)
>> +#define KF_IMPL_SUFFIX "_impl"
>> +#define MAX_BPF_FUNC_REG_ARGS 5
>> +#define MAX_KFUNCS 256
>> +#define MAX_DECL_TAGS (MAX_KFUNCS * 4)
> 
> can't we get that from include/linux/bpf.h? seems like
> resolve_btfids's main.c include internal headers just fine, so why
> duplicate definitions?

Hi Andrii, thank you for a quick review.

Including internal include/linux/btf.h directly doesn't work, which is
probably expected.

resolve_btfids is currently built with:

HOSTCFLAGS_resolve_btfids += -g \
          -I$(srctree)/tools/include \
          -I$(srctree)/tools/include/uapi \
          -I$(LIBBPF_INCLUDE) \
          -I$(SUBCMD_INCLUDE) \
          $(LIBELF_FLAGS) \
          -Wall -Werror

If I add -I$(srctree)/include option and then

    #include <linux/btf.h>

A bunch of build errors happen.

AFAIU we'd have to create a stripped copy of relevant headers in
tools/include first.  Is that what you're suggesting?

> 
>> +
>> +struct btf2btf_context {
>> +       struct btf *btf;
>> +       u32 nr_decl_tags;
>> +       s32 decl_tags[MAX_DECL_TAGS];
>> +};
>> +
>>  static int verbose;
>>  static int warnings;
>>
>> @@ -972,6 +984,273 @@ static int patch_btfids(const char *btfids_path, const char *elf_path)
>>         return err;
>>  }
>>
>> +static s64 collect_kfunc_ids_by_flags(struct object *obj,
>> +                                     u32 flags,
>> +                                     s32 kfunc_ids[],
>> +                                     const u32 kfunc_ids_sz)
>> +{
>> +       Elf_Data *data = obj->efile.idlist;
>> +       struct rb_node *next;
>> +       s64 nr_kfuncs = 0;
>> +       int i;
>> +
>> +       next = rb_first(&obj->sets);
>> +       while (next) {
>> +               struct btf_id_set8 *set8 = NULL;
>> +               unsigned long addr, off;
>> +               struct btf_id *id;
>> +
>> +               id = rb_entry(next, struct btf_id, rb_node);
>> +
>> +               if (id->kind != BTF_ID_KIND_SET8)
>> +                       goto skip;
>> +
>> +               addr = id->addr[0];
>> +               off = addr - obj->efile.idlist_addr;
>> +               set8 = data->d_buf + off;
>> +
>> +               for (i = 0; i < set8->cnt; i++) {
>> +                       if (set8->pairs[i].flags & flags) {
> 
> invert condition and continue, reduce nesting?
> 
>> +                               if (nr_kfuncs >= kfunc_ids_sz) {
> 
> it's silly to set static limits like this: we are not in NMI, you have
> memory allocator, use it

I kinda like that btf2btf_context is stack allocated, but I see your
point. It's not necessary to set hard limits in resolve_btfids.

> 
>> +                                       pr_err("ERROR: resolve_btfids: too many kfuncs with flags %u - limit %d\n",
>> +                                              flags, kfunc_ids_sz);
>> +                                       return -E2BIG;
>> +                               }
>> +                               kfunc_ids[nr_kfuncs++] = set8->pairs[i].id;
>> +                       }
>> +               }
>> +skip:
>> +               next = rb_next(next);
>> +       }
>> +
>> +       return nr_kfuncs;
>> +}
>> +
>> +static const struct btf_type *btf__unqualified_type_by_id(const struct btf *btf, s32 type_id)
>> +{
>> +       const struct btf_type *t = btf__type_by_id(btf, type_id);
>> +
>> +       while (btf_is_mod(t))
>> +               t = btf__type_by_id(btf, t->type);
>> +
>> +       return t;
>> +}
>> +
>> +/* Implicit BPF kfunc arguments can only be of particular types */
>> +static bool btf__is_kf_implicit_arg(const struct btf *btf, const struct btf_param *p)
> 
> nit: not sure why you use double underscore convention, this is not
> really an "API" of btf, it's just a helper... I'd just call it
> "is_kf_implicit_arg", btf is just and input argument
> 
>> +{
>> +       static const char *const kf_implicit_arg_types[] = {
>> +               "bpf_prog_aux",
>> +       };
>> +       const struct btf_type *t;
>> +       const char *name;
>> +
> 
> [...]
> 
>> +       /* Copy all decl tags except "bpf_kfunc" from the original kfunc to the new one */
>> +       for (int i = 0; i < ctx->nr_decl_tags; i++) {
>> +               t = (struct btf_type *)btf__type_by_id(btf, ctx->decl_tags[i]);
>> +               if (t->type != kfunc_id)
>> +                       continue;
>> +
>> +               tag_name = btf__name_by_offset(btf, t->name_off);
>> +               if (strcmp(tag_name, "bpf_kfunc") == 0)
>> +                       continue;
>> +
>> +               err = btf__add_decl_tag(btf, tag_name, new_func_id, -1);
> 
> decl_tag can apply to arguments as well (that -1 will be actually >=
> 0), we should copy those as well, no?

I think you're right. Technically decl_tags can point to parameters as
well.  Is this actually used in kernel BTF?

For the type tags we don't have to do anything though, because the
param type should point to the top type tag, right?

> 
>> +               if (err < 0) {
>> +                       pr_err("ERROR: resolve_btfids: failed to add decl tag %s for %s\n",
>> +                              tag_name, tmp_name);
>> +                       return -EINVAL;
>> +               }
>> +       }
>> +
>> +add_new_proto:
>> +       /*
>> +        * Drop the _impl suffix and point kfunc_name to the local buffer for later use.
>> +        * When BTF is modified the original pointer is invalidated.
>> +        */
>> +       tmp_name[name_len] = '\0';
>> +       kfunc_name = tmp_name;
>> +
>> +       /* Load non-implicit args from the original prototype */
>> +       t = (struct btf_type *)btf__type_by_id(btf, proto_id);
>> +       params = btf_params(t);
>> +       nr_params = 0;
>> +       for (int i = 0; i < btf_vlen(t); i++) {
>> +               if (btf__is_kf_implicit_arg(btf, &params[i]))
>> +                       break;
>> +               new_params[nr_params++] = params[i];
>> +       }
> 
> this is unnecessary, just when you working with original prototype and
> its arguments, just re-fetch it back from IDs, all those pointers will
> be valid until next btf__add_xxx() invocation, no need for this
> awkward local copies
> 
>> +
>> +       new_proto_id = btf__add_func_proto(btf, t->type);
>> +       if (new_proto_id < 0) {
>> +               pr_err("ERROR: resolve_btfids: failed to add func proto for %s\n", kfunc_name);
>> +               return new_proto_id;
>> +       }
>> +
>> +       /* Add non-implicit args to the new prototype */
>> +       for (int i = 0; i < nr_params; i++) {
>> +               param_name = btf__name_by_offset(btf, new_params[i].name_off);
>> +               err = btf__add_func_param(btf, param_name, new_params[i].type);
>> +               if (err < 0) {
>> +                       pr_err("ERROR: resolve_btfids: failed to add param %s for %s\n",
>> +                              param_name, kfunc_name);
>> +                       return err;
>> +               }
>> +       }
>> +
>> +       /* Finally change the prototype of the original kfunc to the new one */
>> +       t = (struct btf_type *)btf__type_by_id(btf, kfunc_id);
>> +       t->type = new_proto_id;
>> +
>> +       pr_debug("resolve_btfids: updated BTF for kfunc with implicit args %s\n", kfunc_name);
>> +
>> +       return 0;
>> +}
>> +
>> +static s64 btf__collect_decl_tags(const struct btf *btf, s32 *decl_tags, u32 decl_tags_sz)
>> +{
>> +       const u32 type_cnt = btf__type_cnt(btf);
>> +       const struct btf_type *t;
>> +       s64 nr_decl_tags = 0;
>> +
>> +       for (u32 id = 1; id < type_cnt; id++) {
>> +               t = btf__type_by_id(btf, id);
>> +               if (!btf_is_decl_tag(t))
>> +                       continue;
>> +               if (nr_decl_tags >= decl_tags_sz) {
>> +                       pr_err("ERROR: resolve_btfids: too many decl tags in BTF - limit %s\n",
> 
> realloc is your friend, no fixed-sized arrays
> 
>> +                               decl_tags_sz);
>> +                       return -E2BIG;
>> +               }
>> +               decl_tags[nr_decl_tags++] = id;
>> +       }
>> +
>> +       return nr_decl_tags;
>> +}
>> +
> 
> [...]


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 05/10] selftests/bpf: Add tests for KF_IMPLICIT_ARGS
  2026-01-09 23:25   ` Andrii Nakryiko
@ 2026-01-10  1:29     ` Ihor Solodrai
  2026-01-12 16:55       ` Andrii Nakryiko
  0 siblings, 1 reply; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-10  1:29 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, linux-kernel,
	linux-input, sched-ext

On 1/9/26 3:25 PM, Andrii Nakryiko wrote:
> On Fri, Jan 9, 2026 at 10:49 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
> 
> [...]
> 
>> diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
>> index 1c41d03bd5a1..503451875d33 100644
>> --- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
>> +++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
>> @@ -1136,6 +1136,10 @@ __bpf_kfunc int bpf_kfunc_st_ops_inc10(struct st_ops_args *args)
>>  __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id);
>>  __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1_impl(struct st_ops_args *args, void *aux_prog);
>>
>> +__bpf_kfunc int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux);
>> +__bpf_kfunc int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux);
>> +__bpf_kfunc int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, void *aux__prog);
>> +
>>  BTF_KFUNCS_START(bpf_testmod_check_kfunc_ids)
>>  BTF_ID_FLAGS(func, bpf_testmod_test_mod_kfunc)
>>  BTF_ID_FLAGS(func, bpf_kfunc_call_test1)
>> @@ -1178,6 +1182,9 @@ BTF_ID_FLAGS(func, bpf_kfunc_st_ops_test_pro_epilogue, KF_SLEEPABLE)
>>  BTF_ID_FLAGS(func, bpf_kfunc_st_ops_inc10)
>>  BTF_ID_FLAGS(func, bpf_kfunc_multi_st_ops_test_1)
>>  BTF_ID_FLAGS(func, bpf_kfunc_multi_st_ops_test_1_impl)
>> +BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg, KF_IMPLICIT_ARGS)
>> +BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy, KF_IMPLICIT_ARGS)
>> +BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy_impl)
> 
> (irrelevant, now that I saw patch #8 discussion, but for the future
> the point will stand and we can decide how resolve_btfids handles this
> upfront)
> 
> I'm wondering, should we add KF_IMPLICIT_ARGS to legacy xxx_impl
> kfuncs as well to explicitly mark them to resolve_btfids as legacy
> implementations? And if we somehow find xxx_impl without it, then
> resolve_btfids complains louds and fails, this should never happen?

Eh... I don't like the idea of flagging both foo and foo_impl.

If we use the same flag for legacy funcs, the flag becomes
insufficient to determine whether a function is legacy or not: we also
have to check the name (or something). This could be a different flag,
but I don't like that either.

For legacy kfuncs that we want to support, I don't think we have to
enforce anything. We allow to use old API, and the new one if it's
implemented.

Are you suggesting to ban _impl suffix in names of new kfuncs?
Fail build on accidental name collision?

We could implement sanity checks like these as separate passes in
resolve_btfids, for example.

> 
> 
> 
>>  BTF_KFUNCS_END(bpf_testmod_check_kfunc_ids)
>>
>>  static int bpf_testmod_ops_init(struct btf *btf)
>> @@ -1669,6 +1676,25 @@ int bpf_kfunc_multi_st_ops_test_1_impl(struct st_ops_args *args, void *aux__prog
>>         return ret;
>>  }
>>
>> +int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux)
>> +{
>> +       if (aux && a > 0)
>> +               return a;
>> +       return -EINVAL;
>> +}
>> +
>> +int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux)
>> +{
>> +       if (aux)
>> +               return a + b;
>> +       return -EINVAL;
>> +}
>> +
>> +int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, void *aux__prog)
>> +{
>> +       return bpf_kfunc_implicit_arg_legacy(a, b, aux__prog);
>> +}
>> +
>>  static int multi_st_ops_reg(void *kdata, struct bpf_link *link)
>>  {
>>         struct bpf_testmod_multi_st_ops *st_ops =
>> --
>> 2.52.0
>>


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
  2026-01-10  1:15     ` Ihor Solodrai
@ 2026-01-12 16:51       ` Andrii Nakryiko
  2026-01-13  1:49         ` Ihor Solodrai
  0 siblings, 1 reply; 44+ messages in thread
From: Andrii Nakryiko @ 2026-01-12 16:51 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, linux-kernel,
	linux-input, sched-ext

On Fri, Jan 9, 2026 at 5:15 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> On 1/9/26 3:25 PM, Andrii Nakryiko wrote:
> > On Fri, Jan 9, 2026 at 10:49 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
> >>
> >> Implement BTF modifications in resolve_btfids to support BPF kernel
> >> functions with implicit arguments.
> >>
> >> For a kfunc marked with KF_IMPLICIT_ARGS flag, a new function
> >> prototype is added to BTF that does not have implicit arguments. The
> >> kfunc's prototype is then updated to a new one in BTF. This prototype
> >> is the intended interface for the BPF programs.
> >>
> >> A <func_name>_impl function is added to BTF to make the original kfunc
> >> prototype searchable for the BPF verifier. If a <func_name>_impl
> >> function already exists in BTF, its interpreted as a legacy case, and
> >> this step is skipped.
> >>
> >> Whether an argument is implicit is determined by its type:
> >> currently only `struct bpf_prog_aux *` is supported.
> >>
> >> As a result, the BTF associated with kfunc is changed from
> >>
> >>     __bpf_kfunc bpf_foo(int arg1, struct bpf_prog_aux *aux);
> >>
> >> into
> >>
> >>     bpf_foo_impl(int arg1, struct bpf_prog_aux *aux);
> >>     __bpf_kfunc bpf_foo(int arg1);
> >>
> >> For more context see previous discussions and patches [1][2].
> >>
> >> [1] https://lore.kernel.org/dwarves/ba1650aa-fafd-49a8-bea4-bdddee7c38c9@linux.dev/
> >> [2] https://lore.kernel.org/bpf/20251029190113.3323406-1-ihor.solodrai@linux.dev/
> >>
> >> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> >> ---
> >>  tools/bpf/resolve_btfids/main.c | 282 ++++++++++++++++++++++++++++++++
> >>  1 file changed, 282 insertions(+)
> >>
> >> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> >> index df39982f51df..b361e726fa36 100644
> >> --- a/tools/bpf/resolve_btfids/main.c
> >> +++ b/tools/bpf/resolve_btfids/main.c
> >> @@ -152,6 +152,18 @@ struct object {
> >>         int nr_typedefs;
> >>  };
> >>
> >> +#define KF_IMPLICIT_ARGS (1 << 16)
> >> +#define KF_IMPL_SUFFIX "_impl"
> >> +#define MAX_BPF_FUNC_REG_ARGS 5
> >> +#define MAX_KFUNCS 256
> >> +#define MAX_DECL_TAGS (MAX_KFUNCS * 4)
> >
> > can't we get that from include/linux/bpf.h? seems like
> > resolve_btfids's main.c include internal headers just fine, so why
> > duplicate definitions?
>
> Hi Andrii, thank you for a quick review.
>
> Including internal include/linux/btf.h directly doesn't work, which is
> probably expected.
>
> resolve_btfids is currently built with:
>
> HOSTCFLAGS_resolve_btfids += -g \
>           -I$(srctree)/tools/include \
>           -I$(srctree)/tools/include/uapi \

so I don't know if that will solve the issue, but I don't think it
makes sense to build resolve_btfids using tools' version of includes.
tools/include is mostly for perf's benefit (maybe so that they don't
accidentally take some kernel-internal dependency, not sure). But
resolve_btfids is built for the kernel during the kernel build, we
should have access to full kernel headers. Try changing this and see
if build errors go away?

>           -I$(LIBBPF_INCLUDE) \
>           -I$(SUBCMD_INCLUDE) \
>           $(LIBELF_FLAGS) \
>           -Wall -Werror
>
> If I add -I$(srctree)/include option and then
>
>     #include <linux/btf.h>
>
> A bunch of build errors happen.
>
> AFAIU we'd have to create a stripped copy of relevant headers in
> tools/include first.  Is that what you're suggesting?

see above, the opposite -- just use -I$(srctree)/include directly

[...]

> >> +               addr = id->addr[0];
> >> +               off = addr - obj->efile.idlist_addr;
> >> +               set8 = data->d_buf + off;
> >> +
> >> +               for (i = 0; i < set8->cnt; i++) {
> >> +                       if (set8->pairs[i].flags & flags) {
> >
> > invert condition and continue, reduce nesting?
> >
> >> +                               if (nr_kfuncs >= kfunc_ids_sz) {
> >
> > it's silly to set static limits like this: we are not in NMI, you have
> > memory allocator, use it
>
> I kinda like that btf2btf_context is stack allocated, but I see your
> point. It's not necessary to set hard limits in resolve_btfids.
>

I don't think we'll notice the performance difference. We had similar
statically-sized things for BTF processing in pahole, and very quickly
ran into limitations and had to change them to dynamically allocated.
Let's not do this again. realloc() is a bit more annoying to use, but
no big deal, it's C after all.

> >
> >> +                                       pr_err("ERROR: resolve_btfids: too many kfuncs with flags %u - limit %d\n",
> >> +                                              flags, kfunc_ids_sz);
> >> +                                       return -E2BIG;
> >> +                               }
> >> +                               kfunc_ids[nr_kfuncs++] = set8->pairs[i].id;

[...]

> >> +               err = btf__add_decl_tag(btf, tag_name, new_func_id, -1);
> >
> > decl_tag can apply to arguments as well (that -1 will be actually >=
> > 0), we should copy those as well, no?
>
> I think you're right. Technically decl_tags can point to parameters as
> well.  Is this actually used in kernel BTF?

I don't remember, but it doesn't matter. We have to clone them as well.

>
> For the type tags we don't have to do anything though, because the
> param type should point to the top type tag, right?

Yeah, type tags are part of type chains, if we reuse types, we'll be
reusing type_tags.

>
> >
> >> +               if (err < 0) {
> >> +                       pr_err("ERROR: resolve_btfids: failed to add decl tag %s for %s\n",
> >> +                              tag_name, tmp_name);
> >> +                       return -EINVAL;
> >> +               }
> >> +       }
> >> +

[...]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 05/10] selftests/bpf: Add tests for KF_IMPLICIT_ARGS
  2026-01-10  1:29     ` Ihor Solodrai
@ 2026-01-12 16:55       ` Andrii Nakryiko
  0 siblings, 0 replies; 44+ messages in thread
From: Andrii Nakryiko @ 2026-01-12 16:55 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, linux-kernel,
	linux-input, sched-ext

On Fri, Jan 9, 2026 at 5:30 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> On 1/9/26 3:25 PM, Andrii Nakryiko wrote:
> > On Fri, Jan 9, 2026 at 10:49 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
> >
> > [...]
> >
> >> diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> >> index 1c41d03bd5a1..503451875d33 100644
> >> --- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> >> +++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
> >> @@ -1136,6 +1136,10 @@ __bpf_kfunc int bpf_kfunc_st_ops_inc10(struct st_ops_args *args)
> >>  __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id);
> >>  __bpf_kfunc int bpf_kfunc_multi_st_ops_test_1_impl(struct st_ops_args *args, void *aux_prog);
> >>
> >> +__bpf_kfunc int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux);
> >> +__bpf_kfunc int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux);
> >> +__bpf_kfunc int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, void *aux__prog);
> >> +
> >>  BTF_KFUNCS_START(bpf_testmod_check_kfunc_ids)
> >>  BTF_ID_FLAGS(func, bpf_testmod_test_mod_kfunc)
> >>  BTF_ID_FLAGS(func, bpf_kfunc_call_test1)
> >> @@ -1178,6 +1182,9 @@ BTF_ID_FLAGS(func, bpf_kfunc_st_ops_test_pro_epilogue, KF_SLEEPABLE)
> >>  BTF_ID_FLAGS(func, bpf_kfunc_st_ops_inc10)
> >>  BTF_ID_FLAGS(func, bpf_kfunc_multi_st_ops_test_1)
> >>  BTF_ID_FLAGS(func, bpf_kfunc_multi_st_ops_test_1_impl)
> >> +BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg, KF_IMPLICIT_ARGS)
> >> +BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy, KF_IMPLICIT_ARGS)
> >> +BTF_ID_FLAGS(func, bpf_kfunc_implicit_arg_legacy_impl)
> >
> > (irrelevant, now that I saw patch #8 discussion, but for the future
> > the point will stand and we can decide how resolve_btfids handles this
> > upfront)
> >
> > I'm wondering, should we add KF_IMPLICIT_ARGS to legacy xxx_impl
> > kfuncs as well to explicitly mark them to resolve_btfids as legacy
> > implementations? And if we somehow find xxx_impl without it, then
> > resolve_btfids complains louds and fails, this should never happen?
>
> Eh... I don't like the idea of flagging both foo and foo_impl.
>
> If we use the same flag for legacy funcs, the flag becomes
> insufficient to determine whether a function is legacy or not: we also
> have to check the name (or something). This could be a different flag,
> but I don't like that either.
>
> For legacy kfuncs that we want to support, I don't think we have to
> enforce anything. We allow to use old API, and the new one if it's
> implemented.
>
> Are you suggesting to ban _impl suffix in names of new kfuncs?
> Fail build on accidental name collision?

This is super low probability, but yes, I was trying to avoid
accidental _impl-named functions to be reused. But as I said, I don't
believe this will happen in practice, so might as well just ignore
this, if you don't like KF_IMPLICIT_ARGS for legacy stuff.

>
> We could implement sanity checks like these as separate passes in
> resolve_btfids, for example.
>
> >
> >
> >
> >>  BTF_KFUNCS_END(bpf_testmod_check_kfunc_ids)
> >>
> >>  static int bpf_testmod_ops_init(struct btf *btf)
> >> @@ -1669,6 +1676,25 @@ int bpf_kfunc_multi_st_ops_test_1_impl(struct st_ops_args *args, void *aux__prog
> >>         return ret;
> >>  }
> >>
> >> +int bpf_kfunc_implicit_arg(int a, struct bpf_prog_aux *aux)
> >> +{
> >> +       if (aux && a > 0)
> >> +               return a;
> >> +       return -EINVAL;
> >> +}
> >> +
> >> +int bpf_kfunc_implicit_arg_legacy(int a, int b, struct bpf_prog_aux *aux)
> >> +{
> >> +       if (aux)
> >> +               return a + b;
> >> +       return -EINVAL;
> >> +}
> >> +
> >> +int bpf_kfunc_implicit_arg_legacy_impl(int a, int b, void *aux__prog)
> >> +{
> >> +       return bpf_kfunc_implicit_arg_legacy(a, b, aux__prog);
> >> +}
> >> +
> >>  static int multi_st_ops_reg(void *kdata, struct bpf_link *link)
> >>  {
> >>         struct bpf_testmod_multi_st_ops *st_ops =
> >> --
> >> 2.52.0
> >>
>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 08/10] bpf: Add bpf_task_work_schedule_* kfuncs with KF_IMPLICIT_ARGS
  2026-01-09 21:56             ` Ihor Solodrai
@ 2026-01-12 18:53               ` Ihor Solodrai
  2026-01-12 22:43                 ` Andrii Nakryiko
  0 siblings, 1 reply; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-12 18:53 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, LKML,
	open list:HID CORE LAYER, sched-ext

On 1/9/26 1:56 PM, Ihor Solodrai wrote:
> On 1/9/26 1:49 PM, Alexei Starovoitov wrote:
>> On Fri, Jan 9, 2026 at 1:39 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>>
>>> [...]
>>>
>>>> I feel bpf_task_work_schedule_resume() is ok to break, since it's so new.
>>>> We can remove bpf_task_work_schedule_[resume|singal]_impl()
>>>> to avoid carrying forward forever.
>>>>
>>>> bpf_stream_vprintk_impl() is not that clear. I would remove it too.
>>>
>>> That leaves only bpf_wq_set_callback_impl(). Can we break that too?
>>
>> Sounds like Benjamin is ok removing it.
>> So I think we can indeed remove them all.
>>
>>> Then there won't be legacy cases at all. It was introduced in v6.16
>>> along the with __prog suffix [1][2].
>>>
>>> If we go this route, we could clean up __prog support/docs too.
>>>
>>> I think it's worth it to make an "all or nothing" decision here:
>>> either break all 4 existing kfuncs, or backwards-support all of them.
>>
>> I don't see why "all or nothing" is a good thing.
>> It won't be "all" anyway.
>> We have bpf_rbtree_add_impl(), bpf_list_push_front_impl(), etc.
>> And those we cannot remove. sched-ext is using them.
>> Another few categories are bpf_obj_new_impl(), bpf_obj_drop_impl().
>> There are not __prog type, but conceptually the same thing and
>> KF_IMPLICIT_ARGS should support them too eventually.
> 
> I was thinking we could remove/simplify code relevant to backwards
> compat of existing _impl kfuncs. But you're right, if we start using
> implicit args for other types/kfuncs, the "legacy" case still has to
> work.
> 
> Ok, in the next revision I'll remove all the __prog users, but leave
> the "legacy" case support in place for future use.

I just had an off-list chat with Andrii, and we agreed that leaving
the existing _impl kfuncs supported may be a good idea.

It doesn't cost us much: we keep the mechanism for legacy functions
anyways, so supporting bpf_wq_set_callback_impl() and co only requires
keeping definitions in the kernel.

The only benefit of *removing* these _impl functions is that we could
clean up __prog support.

But having backwards compat seems like a better deal.
What do you think?


> 
>>
>>
>>> git tag --contains bc049387b41f | grep -v rc
>>> v6.16
>>> v6.17
>>> v6.18
>>>
>>> [1] https://lore.kernel.org/all/20250513142812.1021591-1-memxor@gmail.com/
>>> [2] https://lore.kernel.org/all/20240420-bpf_wq-v2-13-6c986a5a741f@kernel.org/
>>>
>>>
> 


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 08/10] bpf: Add bpf_task_work_schedule_* kfuncs with KF_IMPLICIT_ARGS
  2026-01-12 18:53               ` Ihor Solodrai
@ 2026-01-12 22:43                 ` Andrii Nakryiko
  0 siblings, 0 replies; 44+ messages in thread
From: Andrii Nakryiko @ 2026-01-12 22:43 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Alexei Starovoitov, Andrii Nakryiko,
	Daniel Borkmann, Martin KaFai Lau, Eduard Zingerman,
	Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, LKML, open list:HID CORE LAYER, sched-ext

On Mon, Jan 12, 2026 at 10:54 AM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> On 1/9/26 1:56 PM, Ihor Solodrai wrote:
> > On 1/9/26 1:49 PM, Alexei Starovoitov wrote:
> >> On Fri, Jan 9, 2026 at 1:39 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
> >>>
> >>> [...]
> >>>
> >>>> I feel bpf_task_work_schedule_resume() is ok to break, since it's so new.
> >>>> We can remove bpf_task_work_schedule_[resume|singal]_impl()
> >>>> to avoid carrying forward forever.
> >>>>
> >>>> bpf_stream_vprintk_impl() is not that clear. I would remove it too.
> >>>
> >>> That leaves only bpf_wq_set_callback_impl(). Can we break that too?
> >>
> >> Sounds like Benjamin is ok removing it.
> >> So I think we can indeed remove them all.
> >>
> >>> Then there won't be legacy cases at all. It was introduced in v6.16
> >>> along the with __prog suffix [1][2].
> >>>
> >>> If we go this route, we could clean up __prog support/docs too.
> >>>
> >>> I think it's worth it to make an "all or nothing" decision here:
> >>> either break all 4 existing kfuncs, or backwards-support all of them.
> >>
> >> I don't see why "all or nothing" is a good thing.
> >> It won't be "all" anyway.
> >> We have bpf_rbtree_add_impl(), bpf_list_push_front_impl(), etc.
> >> And those we cannot remove. sched-ext is using them.
> >> Another few categories are bpf_obj_new_impl(), bpf_obj_drop_impl().
> >> There are not __prog type, but conceptually the same thing and
> >> KF_IMPLICIT_ARGS should support them too eventually.
> >
> > I was thinking we could remove/simplify code relevant to backwards
> > compat of existing _impl kfuncs. But you're right, if we start using
> > implicit args for other types/kfuncs, the "legacy" case still has to
> > work.
> >
> > Ok, in the next revision I'll remove all the __prog users, but leave
> > the "legacy" case support in place for future use.
>
> I just had an off-list chat with Andrii, and we agreed that leaving
> the existing _impl kfuncs supported may be a good idea.
>
> It doesn't cost us much: we keep the mechanism for legacy functions
> anyways, so supporting bpf_wq_set_callback_impl() and co only requires
> keeping definitions in the kernel.
>
> The only benefit of *removing* these _impl functions is that we could
> clean up __prog support.
>
> But having backwards compat seems like a better deal.
> What do you think?
>

I think that bit of __prog clean up might be worth doing after all.
The biggest difference for users if we remove _impl stuff from new
kernels would be an extra explicit xxx_impl kfunc declaration (it
won't come from vmlinux.h anymore) and then the following call pattern
(which probably will be hidden in the macro anyways):

if (xxx_impl)
    xxx_impl(..., NULL);
else
    xxx(...);


This will work on old and new kernels alike, so ok, let's just drop
all the _impl stuff. Can't do that for bpf_obj_new_impl() because
that's already used more widely, but for task_work, wq, stream -- they
are all fresh and not yet properly used in production.


>
> >
> >>
> >>
> >>> git tag --contains bc049387b41f | grep -v rc
> >>> v6.16
> >>> v6.17
> >>> v6.18
> >>>
> >>> [1] https://lore.kernel.org/all/20250513142812.1021591-1-memxor@gmail.com/
> >>> [2] https://lore.kernel.org/all/20240420-bpf_wq-v2-13-6c986a5a741f@kernel.org/
> >>>
> >>>
> >
>

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
  2026-01-12 16:51       ` Andrii Nakryiko
@ 2026-01-13  1:49         ` Ihor Solodrai
  2026-01-13 16:55           ` Andrii Nakryiko
  0 siblings, 1 reply; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-13  1:49 UTC (permalink / raw)
  To: Andrii Nakryiko
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, linux-kernel,
	linux-input, sched-ext

On 1/12/26 8:51 AM, Andrii Nakryiko wrote:
> On Fri, Jan 9, 2026 at 5:15 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>
>> [...]
>>>>
>>>> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
>>>> index df39982f51df..b361e726fa36 100644
>>>> --- a/tools/bpf/resolve_btfids/main.c
>>>> +++ b/tools/bpf/resolve_btfids/main.c
>>>> @@ -152,6 +152,18 @@ struct object {
>>>>         int nr_typedefs;
>>>>  };
>>>>
>>>> +#define KF_IMPLICIT_ARGS (1 << 16)
>>>> +#define KF_IMPL_SUFFIX "_impl"
>>>> +#define MAX_BPF_FUNC_REG_ARGS 5
>>>> +#define MAX_KFUNCS 256
>>>> +#define MAX_DECL_TAGS (MAX_KFUNCS * 4)
>>>
>>> can't we get that from include/linux/bpf.h? seems like
>>> resolve_btfids's main.c include internal headers just fine, so why
>>> duplicate definitions?
>>
>> Hi Andrii, thank you for a quick review.
>>
>> Including internal include/linux/btf.h directly doesn't work, which is
>> probably expected.
>>
>> resolve_btfids is currently built with:
>>
>> HOSTCFLAGS_resolve_btfids += -g \
>>           -I$(srctree)/tools/include \
>>           -I$(srctree)/tools/include/uapi \
> 
> so I don't know if that will solve the issue, but I don't think it
> makes sense to build resolve_btfids using tools' version of includes.
> tools/include is mostly for perf's benefit (maybe so that they don't
> accidentally take some kernel-internal dependency, not sure). But
> resolve_btfids is built for the kernel during the kernel build, we
> should have access to full kernel headers. Try changing this and see
> if build errors go away?
> 
>>           -I$(LIBBPF_INCLUDE) \
>>           -I$(SUBCMD_INCLUDE) \
>>           $(LIBELF_FLAGS) \
>>           -Wall -Werror
>>
>> If I add -I$(srctree)/include option and then
>>
>>     #include <linux/btf.h>
>>
>> A bunch of build errors happen.
>>
>> AFAIU we'd have to create a stripped copy of relevant headers in
>> tools/include first.  Is that what you're suggesting?
> 
> see above, the opposite -- just use -I$(srctree)/include directly

Andrii,

I made a low-effort attempt to switch away from tools/include and it
looks like too much trouble. See a sample splat below.

I think the issue is that resolve_btfids uses a couple of inherently
user-space things (stdlib, libelf), which themselves may include
system headers. And there is actually a difference between the kernel
and tools/include headers. For example, check

  ./include/linux/rbtree.h
vs
  ./tools/include/linux/rbtree.h

Maybe we can make it work (with our own local tools/include?), but it
doesn't look worth it for just a couple of constant #define-s.

Let me know if I am missing something.


$ make
  INSTALL libsubcmd_headers
  HOSTCC  /home/isolodrai/workspace/prog-aux/linux/tools/bpf/resolve_btfids/main.o
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:20:33: error: conflicting types for ‘fd_set’; have ‘__kernel_fd_set’
   20 | typedef __kernel_fd_set         fd_set;
      |                                 ^~~~~~
In file included from /usr/include/sys/types.h:179,
                 from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/sys/select.h:70:5: note: previous declaration of ‘fd_set’ with type ‘fd_set’
   70 |   } fd_set;
      |     ^~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:21:33: error: conflicting types for ‘dev_t’; have ‘__kernel_dev_t’ {aka ‘unsigned int’}
   21 | typedef __kernel_dev_t          dev_t;
      |                                 ^~~~~
In file included from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/sys/types.h:59:17: note: previous declaration of ‘dev_t’ with type ‘dev_t’ {aka ‘long unsigned int’}
   59 | typedef __dev_t dev_t;
      |                 ^~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:25:33: error: conflicting types for ‘nlink_t’; have ‘u32’ {aka ‘unsigned int’}
   25 | typedef u32                     nlink_t;
      |                                 ^~~~~~~
In file included from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/sys/types.h:74:19: note: previous declaration of ‘nlink_t’ with type ‘nlink_t’ {aka ‘long unsigned int’}
   74 | typedef __nlink_t nlink_t;
      |                   ^~~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:31:33: error: conflicting types for ‘timer_t’; have ‘__kernel_timer_t’ {aka ‘int’}
   31 | typedef __kernel_timer_t        timer_t;
      |                                 ^~~~~~~
In file included from /usr/include/sys/types.h:130,
                 from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/bits/types/timer_t.h:7:19: note: previous declaration of ‘timer_t’ with type ‘timer_t’ {aka ‘void *’}
    7 | typedef __timer_t timer_t;
      |                   ^~~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:52:33: error: conflicting types for ‘loff_t’; have ‘__kernel_loff_t’ {aka ‘long long int’}
   52 | typedef __kernel_loff_t         loff_t;
      |                                 ^~~~~~
In file included from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/sys/types.h:42:18: note: previous declaration of ‘loff_t’ with type ‘loff_t’ {aka ‘long int’}
   42 | typedef __loff_t loff_t;
      |                  ^~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:53:9: error: unknown type name ‘__kernel_uoff_t’
   53 | typedef __kernel_uoff_t         uoff_t;
      |         ^~~~~~~~~~~~~~~
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:115:33: error: conflicting types for ‘uint64_t’; have ‘u64’ {aka ‘long long unsigned int’}
  115 | typedef u64                     uint64_t;
      |                                 ^~~~~~~~
In file included from /usr/include/stdint.h:37,
                 from /usr/lib/gcc/x86_64-redhat-linux/11/include/stdint.h:9,
                 from /usr/include/libelf.h:32,
                 from main.c:68:
/usr/include/bits/stdint-uintn.h:27:20: note: previous declaration of ‘uint64_t’ with type ‘uint64_t’ {aka ‘long unsigned int’}
   27 | typedef __uint64_t uint64_t;
      |                    ^~~~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:116:33: error: conflicting types for ‘u_int64_t’; have ‘u64’ {aka ‘long long unsigned int’}
  116 | typedef u64                     u_int64_t;
      |                                 ^~~~~~~~~
In file included from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/sys/types.h:161:20: note: previous declaration of ‘u_int64_t’ with type ‘u_int64_t’ {aka ‘long unsigned int’}
  161 | typedef __uint64_t u_int64_t;
      |                    ^~~~~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:117:33: error: conflicting types for ‘int64_t’; have ‘s64’ {aka ‘long long int’}
  117 | typedef s64                     int64_t;
      |                                 ^~~~~~~
In file included from /usr/include/sys/types.h:155,
                 from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/bits/stdint-intn.h:27:19: note: previous declaration of ‘int64_t’ with type ‘int64_t’ {aka ‘long int’}
   27 | typedef __int64_t int64_t;
      |                   ^~~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:138:13: error: conflicting types for ‘blkcnt_t’; have ‘u64’ {aka ‘long long unsigned int’}
  138 | typedef u64 blkcnt_t;
      |             ^~~~~~~~
In file included from /usr/include/stdlib.h:394,
                 from main.c:67:
/usr/include/sys/types.h:192:20: note: previous declaration of ‘blkcnt_t’ with type ‘blkcnt_t’ {aka ‘long int’}
  192 | typedef __blkcnt_t blkcnt_t;     /* Type to count number of disk blocks.  */
      |                    ^~~~~~~~
In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:266:34: error: expected ‘:’, ‘,’, ‘;’, ‘}’ or ‘__attribute__’ before ‘*’ token
  266 |         struct task_struct __rcu *task;
      |                                  ^
In file included from /home/isolodrai/workspace/prog-aux/linux/include/linux/cache.h:6,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/time.h:5,
                 from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:19,
                 from main.c:70:
/home/isolodrai/workspace/prog-aux/linux/include/vdso/cache.h:5:10: fatal error: asm/cache.h: No such file or directory
    5 | #include <asm/cache.h>
      |          ^~~~~~~~~~~~~
compilation terminated.
make[1]: *** [/home/isolodrai/workspace/prog-aux/linux/tools/build/Makefile.build:86: /home/isolodrai/workspace/prog-aux/linux/tools/bpf/resolve_btfids/main.o] Error 1
make: *** [Makefile:81: /home/isolodrai/workspace/prog-aux/linux/tools/bpf/resolve_btfids//resolve_btfids-in.o] Error 2


> 
> [...]
> 

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 04/10] resolve_btfids: Support for KF_IMPLICIT_ARGS
  2026-01-13  1:49         ` Ihor Solodrai
@ 2026-01-13 16:55           ` Andrii Nakryiko
  0 siblings, 0 replies; 44+ messages in thread
From: Andrii Nakryiko @ 2026-01-13 16:55 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Alexei Starovoitov, Andrii Nakryiko, Daniel Borkmann,
	Martin KaFai Lau, Eduard Zingerman, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, linux-kernel,
	linux-input, sched-ext

On Mon, Jan 12, 2026 at 5:49 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> On 1/12/26 8:51 AM, Andrii Nakryiko wrote:
> > On Fri, Jan 9, 2026 at 5:15 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
> >>
> >> [...]
> >>>>
> >>>> diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
> >>>> index df39982f51df..b361e726fa36 100644
> >>>> --- a/tools/bpf/resolve_btfids/main.c
> >>>> +++ b/tools/bpf/resolve_btfids/main.c
> >>>> @@ -152,6 +152,18 @@ struct object {
> >>>>         int nr_typedefs;
> >>>>  };
> >>>>
> >>>> +#define KF_IMPLICIT_ARGS (1 << 16)
> >>>> +#define KF_IMPL_SUFFIX "_impl"
> >>>> +#define MAX_BPF_FUNC_REG_ARGS 5
> >>>> +#define MAX_KFUNCS 256
> >>>> +#define MAX_DECL_TAGS (MAX_KFUNCS * 4)
> >>>
> >>> can't we get that from include/linux/bpf.h? seems like
> >>> resolve_btfids's main.c include internal headers just fine, so why
> >>> duplicate definitions?
> >>
> >> Hi Andrii, thank you for a quick review.
> >>
> >> Including internal include/linux/btf.h directly doesn't work, which is
> >> probably expected.
> >>
> >> resolve_btfids is currently built with:
> >>
> >> HOSTCFLAGS_resolve_btfids += -g \
> >>           -I$(srctree)/tools/include \
> >>           -I$(srctree)/tools/include/uapi \
> >
> > so I don't know if that will solve the issue, but I don't think it
> > makes sense to build resolve_btfids using tools' version of includes.
> > tools/include is mostly for perf's benefit (maybe so that they don't
> > accidentally take some kernel-internal dependency, not sure). But
> > resolve_btfids is built for the kernel during the kernel build, we
> > should have access to full kernel headers. Try changing this and see
> > if build errors go away?
> >
> >>           -I$(LIBBPF_INCLUDE) \
> >>           -I$(SUBCMD_INCLUDE) \
> >>           $(LIBELF_FLAGS) \
> >>           -Wall -Werror
> >>
> >> If I add -I$(srctree)/include option and then
> >>
> >>     #include <linux/btf.h>
> >>
> >> A bunch of build errors happen.
> >>
> >> AFAIU we'd have to create a stripped copy of relevant headers in
> >> tools/include first.  Is that what you're suggesting?
> >
> > see above, the opposite -- just use -I$(srctree)/include directly
>
> Andrii,
>
> I made a low-effort attempt to switch away from tools/include and it
> looks like too much trouble. See a sample splat below.
>
> I think the issue is that resolve_btfids uses a couple of inherently
> user-space things (stdlib, libelf), which themselves may include
> system headers. And there is actually a difference between the kernel
> and tools/include headers. For example, check
>
>   ./include/linux/rbtree.h
> vs
>   ./tools/include/linux/rbtree.h
>
> Maybe we can make it work (with our own local tools/include?), but it
> doesn't look worth it for just a couple of constant #define-s.
>
> Let me know if I am missing something.

No, it's fine, no big deal, at least we know that it's not as simple.
Thanks for trying!

>
>
> $ make
>   INSTALL libsubcmd_headers
>   HOSTCC  /home/isolodrai/workspace/prog-aux/linux/tools/bpf/resolve_btfids/main.o
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:20:33: error: conflicting types for ‘fd_set’; have ‘__kernel_fd_set’
>    20 | typedef __kernel_fd_set         fd_set;
>       |                                 ^~~~~~
> In file included from /usr/include/sys/types.h:179,
>                  from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/sys/select.h:70:5: note: previous declaration of ‘fd_set’ with type ‘fd_set’
>    70 |   } fd_set;
>       |     ^~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:21:33: error: conflicting types for ‘dev_t’; have ‘__kernel_dev_t’ {aka ‘unsigned int’}
>    21 | typedef __kernel_dev_t          dev_t;
>       |                                 ^~~~~
> In file included from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/sys/types.h:59:17: note: previous declaration of ‘dev_t’ with type ‘dev_t’ {aka ‘long unsigned int’}
>    59 | typedef __dev_t dev_t;
>       |                 ^~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:25:33: error: conflicting types for ‘nlink_t’; have ‘u32’ {aka ‘unsigned int’}
>    25 | typedef u32                     nlink_t;
>       |                                 ^~~~~~~
> In file included from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/sys/types.h:74:19: note: previous declaration of ‘nlink_t’ with type ‘nlink_t’ {aka ‘long unsigned int’}
>    74 | typedef __nlink_t nlink_t;
>       |                   ^~~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:31:33: error: conflicting types for ‘timer_t’; have ‘__kernel_timer_t’ {aka ‘int’}
>    31 | typedef __kernel_timer_t        timer_t;
>       |                                 ^~~~~~~
> In file included from /usr/include/sys/types.h:130,
>                  from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/bits/types/timer_t.h:7:19: note: previous declaration of ‘timer_t’ with type ‘timer_t’ {aka ‘void *’}
>     7 | typedef __timer_t timer_t;
>       |                   ^~~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:52:33: error: conflicting types for ‘loff_t’; have ‘__kernel_loff_t’ {aka ‘long long int’}
>    52 | typedef __kernel_loff_t         loff_t;
>       |                                 ^~~~~~
> In file included from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/sys/types.h:42:18: note: previous declaration of ‘loff_t’ with type ‘loff_t’ {aka ‘long int’}
>    42 | typedef __loff_t loff_t;
>       |                  ^~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:53:9: error: unknown type name ‘__kernel_uoff_t’
>    53 | typedef __kernel_uoff_t         uoff_t;
>       |         ^~~~~~~~~~~~~~~
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:115:33: error: conflicting types for ‘uint64_t’; have ‘u64’ {aka ‘long long unsigned int’}
>   115 | typedef u64                     uint64_t;
>       |                                 ^~~~~~~~
> In file included from /usr/include/stdint.h:37,
>                  from /usr/lib/gcc/x86_64-redhat-linux/11/include/stdint.h:9,
>                  from /usr/include/libelf.h:32,
>                  from main.c:68:
> /usr/include/bits/stdint-uintn.h:27:20: note: previous declaration of ‘uint64_t’ with type ‘uint64_t’ {aka ‘long unsigned int’}
>    27 | typedef __uint64_t uint64_t;
>       |                    ^~~~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:116:33: error: conflicting types for ‘u_int64_t’; have ‘u64’ {aka ‘long long unsigned int’}
>   116 | typedef u64                     u_int64_t;
>       |                                 ^~~~~~~~~
> In file included from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/sys/types.h:161:20: note: previous declaration of ‘u_int64_t’ with type ‘u_int64_t’ {aka ‘long unsigned int’}
>   161 | typedef __uint64_t u_int64_t;
>       |                    ^~~~~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:117:33: error: conflicting types for ‘int64_t’; have ‘s64’ {aka ‘long long int’}
>   117 | typedef s64                     int64_t;
>       |                                 ^~~~~~~
> In file included from /usr/include/sys/types.h:155,
>                  from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/bits/stdint-intn.h:27:19: note: previous declaration of ‘int64_t’ with type ‘int64_t’ {aka ‘long int’}
>    27 | typedef __int64_t int64_t;
>       |                   ^~~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:138:13: error: conflicting types for ‘blkcnt_t’; have ‘u64’ {aka ‘long long unsigned int’}
>   138 | typedef u64 blkcnt_t;
>       |             ^~~~~~~~
> In file included from /usr/include/stdlib.h:394,
>                  from main.c:67:
> /usr/include/sys/types.h:192:20: note: previous declaration of ‘blkcnt_t’ with type ‘blkcnt_t’ {aka ‘long int’}
>   192 | typedef __blkcnt_t blkcnt_t;     /* Type to count number of disk blocks.  */
>       |                    ^~~~~~~~
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/uapi/linux/stat.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:7,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/linux/types.h:266:34: error: expected ‘:’, ‘,’, ‘;’, ‘}’ or ‘__attribute__’ before ‘*’ token
>   266 |         struct task_struct __rcu *task;
>       |                                  ^
> In file included from /home/isolodrai/workspace/prog-aux/linux/include/linux/cache.h:6,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/time.h:5,
>                  from /home/isolodrai/workspace/prog-aux/linux/include/linux/stat.h:19,
>                  from main.c:70:
> /home/isolodrai/workspace/prog-aux/linux/include/vdso/cache.h:5:10: fatal error: asm/cache.h: No such file or directory
>     5 | #include <asm/cache.h>
>       |          ^~~~~~~~~~~~~
> compilation terminated.
> make[1]: *** [/home/isolodrai/workspace/prog-aux/linux/tools/build/Makefile.build:86: /home/isolodrai/workspace/prog-aux/linux/tools/bpf/resolve_btfids/main.o] Error 1
> make: *** [Makefile:81: /home/isolodrai/workspace/prog-aux/linux/tools/bpf/resolve_btfids//resolve_btfids-in.o] Error 2
>
>
> >
> > [...]
> >

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
  2026-01-09 18:48 ` [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS Ihor Solodrai
  2026-01-09 19:54   ` Alexei Starovoitov
  2026-01-09 23:25   ` Andrii Nakryiko
@ 2026-01-13 20:39   ` Eduard Zingerman
  2026-01-13 22:03     ` Ihor Solodrai
  2026-01-13 21:59   ` Eduard Zingerman
  3 siblings, 1 reply; 44+ messages in thread
From: Eduard Zingerman @ 2026-01-13 20:39 UTC (permalink / raw)
  To: Ihor Solodrai, Alexei Starovoitov, Andrii Nakryiko,
	Daniel Borkmann, Martin KaFai Lau
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
> A kernel function bpf_foo marked with KF_IMPLICIT_ARGS flag is
> expected to have two associated types in BTF:
>   * `bpf_foo` with a function prototype that omits implicit arguments
>   * `bpf_foo_impl` with a function prototype that matches the kernel
>      declaration of `bpf_foo`, but doesn't have a ksym associated with
>      its name
> 
> In order to support kfuncs with implicit arguments, the verifier has
> to know how to resolve a call of `bpf_foo` to the correct BTF function
> prototype and address.
> 
> To implement this, in add_kfunc_call() kfunc flags are checked for
> KF_IMPLICIT_ARGS. For such kfuncs a BTF func prototype is adjusted to
> the one found for `bpf_foo_impl` (func_name + "_impl" suffix, by
> convention) function in BTF.
> 
> This effectively changes the signature of the `bpf_foo` kfunc in the
> context of verification: from one without implicit args to the one
> with full argument list.
> 
> Whether a kfunc argument is implicit or not is determined by
> is_kfunc_arg_implicit(). The values of implicit arguments by design
> are provided by the verifier, and so they can only be of particular
> types. In this patch the only allowed implicit arg type is a pointer
> to struct bpf_prog_aux. The __prog args (usually void *) are also
> considered implicit for backwards compatibility.
> 
> In order to enable the verifier to correctly set an implicit
> bpf_prog_aux arg value at runtime, is_kfunc_arg_prog() is extended to
> check for the arg type. At a point when prog arg is determined in
> check_kfunc_args() the kfunc with implicit args already has a
> prototype with full argument list, so the existing value patch
> mechanism just works.
> 
> If a new kfunc with KF_IMPLICIT_ARG is declared for an existing kfunc
> that uses a __prog argument (a legacy case), the prototype
> substitution works in exactly the same way, assuming the kfunc follows
> the _impl naming convention. The difference is only in how _impl
> prototype is added to the BTF, which is not the verifier's
> concern. See a subsequent resolve_btfids patch for details.
> 
> In check_kfunc_call() reset the subreg_def of registers holding
> implicit arguments to correctly track zero extensions.
> 
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---

Overall lgtm.

[...]

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c

[...]

> @@ -14303,6 +14358,17 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>  	for (i = 0; i < nargs; i++) {
>  		u32 regno = i + 1;
>  
> +		/*
> +		 * Implicit kfunc arguments are set after main verification pass.
> +		 * For correct tracking of zero-extensions we have to reset subreg_def for such
> +		 * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
> +		 * from an earlier (irrelevant) point in the program, which may lead to an error
> +		 * in opt_subreg_zext_lo32_rnd_hi32().
> +		 */
> +		if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
> +				&& is_kfunc_arg_implicit(desc_btf, &args[i])))
> +			regs[regno].subreg_def = DEF_NOT_SUBREG;
> +

Did you try doing this in `mark_reg_not_init()`?
This function is called for R1-R5 some time prior this hunk.
What I don't like from structural point of view is:
- `is_kfunc_arg_implicit()` depends on KF_IMPLICIT_ARGS, but that
  check is done externally. Hence, the naming is misleading or 'meta'
  should be passed to `is_kfunc_arg_implicit()`.
- doing DEF_NOT_SUBREG logically has not much to do with implicit args,
  so it is a bit confusing that is pre-conditioned like that.

>  		t = btf_type_skip_modifiers(desc_btf, args[i].type, NULL);
>  		if (btf_type_is_ptr(t))
>  			mark_btf_func_reg_size(env, regno, sizeof(void *));

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 01/10] bpf: Refactor btf_kfunc_id_set_contains
  2026-01-09 18:48 ` [PATCH bpf-next v1 01/10] bpf: Refactor btf_kfunc_id_set_contains Ihor Solodrai
@ 2026-01-13 21:43   ` Eduard Zingerman
  0 siblings, 0 replies; 44+ messages in thread
From: Eduard Zingerman @ 2026-01-13 21:43 UTC (permalink / raw)
  To: Ihor Solodrai, Alexei Starovoitov, Andrii Nakryiko,
	Daniel Borkmann, Martin KaFai Lau
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
> btf_kfunc_id_set_contains() is called by fetch_kfunc_meta() in the BPF
> verifier to get the kfunc flags stored in the .BTF_ids ELF section.
> If it returns NULL instead of a valid pointer, it's interpreted as an
> illegal kfunc usage failing the verification.
> 
> There are two potential reasons for btf_kfunc_id_set_contains() to
> return NULL:
> 
>   1. Provided kfunc BTF id is not present in relevant kfunc id sets.
>   2. The kfunc is not allowed, as determined by the program type
>      specific filter [1].
> 
> The filter functions accept a pointer to `struct bpf_prog`, so they
> might implicitly depend on earlier stages of verification, when
> bpf_prog members are set.
> 
> For example, bpf_qdisc_kfunc_filter() in linux/net/sched/bpf_qdisc.c
> inspects prog->aux->st_ops [2], which is initialized in:
> 
>     check_attach_btf_id() -> check_struct_ops_btf_id()
> 
> So far this hasn't been an issue, because fetch_kfunc_meta() is the
> only caller of btf_kfunc_id_set_contains().
> 
> However in subsequent patches of this series it is necessary to
> inspect kfunc flags earlier in BPF verifier, in the add_kfunc_call().
> 
> To resolve this, refactor btf_kfunc_id_set_contains() into two
> interface functions:
>   * btf_kfunc_flags() that simply returns pointer to kfunc_flags
>     without applying the filters
>   * btf_kfunc_is_allowed() that both checks for kfunc_flags existence
>     (which is a requirement for a kfunc to be allowed) and applies the
>     prog filters
> 
> See [3] for the previous version of this patch.
> 
> [1] https://lore.kernel.org/all/20230519225157.760788-7-aditi.ghag@isovalent.com/
> [2] https://lore.kernel.org/all/20250409214606.2000194-4-ameryhung@gmail.com/
> [3] https://lore.kernel.org/bpf/20251029190113.3323406-3-ihor.solodrai@linux.dev/
> 
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---

Reviewed-by: Eduard Zingerman <eddyz87@gmail.com>

> @@ -8715,6 +8730,26 @@ static int bpf_prog_type_to_kfunc_hook(enum bpf_prog_type prog_type)
>  	}
>  }
>  
> +bool btf_kfunc_is_allowed(const struct btf *btf,
> +			  u32 kfunc_btf_id,
> +			  const struct bpf_prog *prog)
> +{

Nit: I'd just add hook parameter to btf_kfunc_flags():

     u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog,
                          enum btf_kfunc_hook *hook)

     and allow passing NULL there, thus avoiding duplicating logic for common hook.

> +	enum bpf_prog_type prog_type = resolve_prog_type(prog);
> +	enum btf_kfunc_hook hook;
> +	u32 *kfunc_flags;
> +
> +	kfunc_flags = btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id);
> +	if (kfunc_flags && __btf_kfunc_is_allowed(btf, BTF_KFUNC_HOOK_COMMON, kfunc_btf_id, prog))
> +		return true;
> +
> +	hook = bpf_prog_type_to_kfunc_hook(prog_type);
> +	kfunc_flags = btf_kfunc_id_set_contains(btf, hook, kfunc_btf_id);
> +	if (kfunc_flags && __btf_kfunc_is_allowed(btf, hook, kfunc_btf_id, prog))
> +		return true;
> +
> +	return false;
> +}
> +
>  /* Caution:
>   * Reference to the module (obtained using btf_try_get_module) corresponding to
>   * the struct btf *MUST* be held when calling this function from verifier

[...]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 02/10] bpf: Introduce struct bpf_kfunc_meta
  2026-01-09 18:48 ` [PATCH bpf-next v1 02/10] bpf: Introduce struct bpf_kfunc_meta Ihor Solodrai
@ 2026-01-13 21:46   ` Eduard Zingerman
  0 siblings, 0 replies; 44+ messages in thread
From: Eduard Zingerman @ 2026-01-13 21:46 UTC (permalink / raw)
  To: Ihor Solodrai, Alexei Starovoitov, Andrii Nakryiko,
	Daniel Borkmann, Martin KaFai Lau
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
> There is code duplication between add_kfunc_call() and
> fetch_kfunc_meta() collecting information about a kfunc from BTF.
> 
> Introduce struct bpf_kfunc_meta to hold common kfunc BTF data and
> implement fetch_kfunc_meta() to fill it in, instead of struct
> bpf_kfunc_call_arg_meta directly.
> 
> Then use these in add_kfunc_call() and (new) fetch_kfunc_arg_meta()
> functions, and fixup previous usages of fetch_kfunc_meta() to
> fetch_kfunc_arg_meta().
> 
> Besides the code dedup, this change enables add_kfunc_call() to access
> kfunc->flags.
> 
> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
> ---

Acked-by: Eduard Zingerman <eddyz87@gmail.com>

[...]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
  2026-01-09 18:48 ` [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS Ihor Solodrai
                     ` (2 preceding siblings ...)
  2026-01-13 20:39   ` Eduard Zingerman
@ 2026-01-13 21:59   ` Eduard Zingerman
  2026-01-14  0:03     ` Ihor Solodrai
  3 siblings, 1 reply; 44+ messages in thread
From: Eduard Zingerman @ 2026-01-13 21:59 UTC (permalink / raw)
  To: Ihor Solodrai, Alexei Starovoitov, Andrii Nakryiko,
	Daniel Borkmann, Martin KaFai Lau
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:

[...]

> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -3271,6 +3271,38 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
>  	return btf_vmlinux ?: ERR_PTR(-ENOENT);
>  }
>  
> +#define KF_IMPL_SUFFIX "_impl"
> +
> +static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_env *env,
> +						    struct btf *btf,
> +						    const char *func_name)
> +{
> +	char impl_name[KSYM_SYMBOL_LEN];

Oh, as we discussed already, this should use env->tmp_str_buf.

> +	const struct btf_type *func;
> +	s32 impl_id;
> +	int len;
> +
> +	len = snprintf(impl_name, sizeof(impl_name), "%s%s", func_name, KF_IMPL_SUFFIX);
> +	if (len < 0 || len >= sizeof(impl_name)) {
> +		verbose(env, "function name %s%s is too long\n", func_name, KF_IMPL_SUFFIX);
> +		return NULL;
> +	}

[...]

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
  2026-01-13 20:39   ` Eduard Zingerman
@ 2026-01-13 22:03     ` Ihor Solodrai
  2026-01-13 23:48       ` Ihor Solodrai
  0 siblings, 1 reply; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-13 22:03 UTC (permalink / raw)
  To: Eduard Zingerman, Alexei Starovoitov, Andrii Nakryiko,
	Daniel Borkmann, Martin KaFai Lau
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

On 1/13/26 12:39 PM, Eduard Zingerman wrote:
> On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
>> A kernel function bpf_foo marked with KF_IMPLICIT_ARGS flag is
>> expected to have two associated types in BTF:
>>   * `bpf_foo` with a function prototype that omits implicit arguments
>>   * `bpf_foo_impl` with a function prototype that matches the kernel
>>      declaration of `bpf_foo`, but doesn't have a ksym associated with
>>      its name
>>
>> In order to support kfuncs with implicit arguments, the verifier has
>> to know how to resolve a call of `bpf_foo` to the correct BTF function
>> prototype and address.
>>
>> To implement this, in add_kfunc_call() kfunc flags are checked for
>> KF_IMPLICIT_ARGS. For such kfuncs a BTF func prototype is adjusted to
>> the one found for `bpf_foo_impl` (func_name + "_impl" suffix, by
>> convention) function in BTF.
>>
>> This effectively changes the signature of the `bpf_foo` kfunc in the
>> context of verification: from one without implicit args to the one
>> with full argument list.
>>
>> Whether a kfunc argument is implicit or not is determined by
>> is_kfunc_arg_implicit(). The values of implicit arguments by design
>> are provided by the verifier, and so they can only be of particular
>> types. In this patch the only allowed implicit arg type is a pointer
>> to struct bpf_prog_aux. The __prog args (usually void *) are also
>> considered implicit for backwards compatibility.
>>
>> In order to enable the verifier to correctly set an implicit
>> bpf_prog_aux arg value at runtime, is_kfunc_arg_prog() is extended to
>> check for the arg type. At a point when prog arg is determined in
>> check_kfunc_args() the kfunc with implicit args already has a
>> prototype with full argument list, so the existing value patch
>> mechanism just works.
>>
>> If a new kfunc with KF_IMPLICIT_ARG is declared for an existing kfunc
>> that uses a __prog argument (a legacy case), the prototype
>> substitution works in exactly the same way, assuming the kfunc follows
>> the _impl naming convention. The difference is only in how _impl
>> prototype is added to the BTF, which is not the verifier's
>> concern. See a subsequent resolve_btfids patch for details.
>>
>> In check_kfunc_call() reset the subreg_def of registers holding
>> implicit arguments to correctly track zero extensions.
>>
>> Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
>> ---
> 
> Overall lgtm.
> 
> [...]
> 
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> 
> [...]
> 
>> @@ -14303,6 +14358,17 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>>  	for (i = 0; i < nargs; i++) {
>>  		u32 regno = i + 1;
>>  
>> +		/*
>> +		 * Implicit kfunc arguments are set after main verification pass.
>> +		 * For correct tracking of zero-extensions we have to reset subreg_def for such
>> +		 * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
>> +		 * from an earlier (irrelevant) point in the program, which may lead to an error
>> +		 * in opt_subreg_zext_lo32_rnd_hi32().
>> +		 */
>> +		if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
>> +				&& is_kfunc_arg_implicit(desc_btf, &args[i])))
>> +			regs[regno].subreg_def = DEF_NOT_SUBREG;
>> +
> 
> Did you try doing this in `mark_reg_not_init()`?
> This function is called for R1-R5 some time prior this hunk.

> Did you try doing this in `mark_reg_not_init()`?

Just tried, it doesn't work because REG0 is considered a caller saved
register, and so it breaks the zext tracking:

        #define CALLER_SAVED_REGS 6
        static const int caller_saved[CALLER_SAVED_REGS] = {
	     BPF_REG_0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5
        };

        [...]

	for (i = 0; i < CALLER_SAVED_REGS; i++)
		mark_reg_not_init(env, regs, caller_saved[i]);

CI run for the diff below (on top of this series):
https://github.com/kernel-patches/bpf/actions/runs/20972520708


diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index b4e40b87e8fa..8bbcd1466815 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -2784,6 +2784,8 @@ static void __reg_assign_32_into_64(struct bpf_reg_state *reg)
        }
 }
 
+#define DEF_NOT_SUBREG (0)
+
 /* Mark a register as having a completely unknown (scalar) value. */
 static void __mark_reg_unknown_imprecise(struct bpf_reg_state *reg)
 {
@@ -2798,6 +2800,7 @@ static void __mark_reg_unknown_imprecise(struct bpf_reg_state *reg)
        reg->var_off = tnum_unknown;
        reg->frameno = 0;
        reg->precise = false;
+       reg->subreg_def = DEF_NOT_SUBREG;
        __mark_reg_unbounded(reg);
 }
 
@@ -2892,7 +2895,6 @@ static int mark_btf_ld_reg(struct bpf_verifier_env *env,
        }
 }
 
-#define DEF_NOT_SUBREG (0)
 static void init_reg_state(struct bpf_verifier_env *env,
                           struct bpf_func_state *state)
 {
@@ -14363,17 +14365,6 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
        for (i = 0; i < nargs; i++) {
                u32 regno = i + 1;
 
-               /*
-                * Implicit kfunc arguments are set after main verification pass.
-                * For correct tracking of zero-extensions we have to reset subreg_def for such
-                * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
-                * from an earlier (irrelevant) point in the program, which may lead to an error
-                * in opt_subreg_zext_lo32_rnd_hi32().
-                */
-               if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
-                               && is_kfunc_arg_implicit(desc_btf, &args[i])))
-                       regs[regno].subreg_def = DEF_NOT_SUBREG;
-
                t = btf_type_skip_modifiers(desc_btf, args[i].type, NULL);
                if (btf_type_is_ptr(t))
                        mark_btf_func_reg_size(env, regno, sizeof(void *));

---

Resetting all reg args appears to be working however (see below).
CI: https://github.com/kernel-patches/bpf/actions/runs/20973490221

Should I send this as a separate patch?

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 8bbcd1466815..9dfcf3149841 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -2800,7 +2800,6 @@ static void __mark_reg_unknown_imprecise(struct bpf_reg_state *reg)
        reg->var_off = tnum_unknown;
        reg->frameno = 0;
        reg->precise = false;
-       reg->subreg_def = DEF_NOT_SUBREG;
        __mark_reg_unbounded(reg);
 }
 
@@ -14241,6 +14240,11 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
        for (i = 0; i < CALLER_SAVED_REGS; i++)
                mark_reg_not_init(env, regs, caller_saved[i]);
 
+       for (i = 0; i < MAX_BPF_FUNC_REG_ARGS; i++) {
+               u32 regno = i + 1;
+               regs[regno].subreg_def = DEF_NOT_SUBREG;
+       }
+
        /* Check return type */
        t = btf_type_skip_modifiers(desc_btf, meta.func_proto->type, NULL);


> What I don't like from structural point of view is:
> - `is_kfunc_arg_implicit()` depends on KF_IMPLICIT_ARGS, but that
>   check is done externally. Hence, the naming is misleading or 'meta'
>   should be passed to `is_kfunc_arg_implicit()`.
> - doing DEF_NOT_SUBREG logically has not much to do with implicit args,
>   so it is a bit confusing that is pre-conditioned like that.
> 
>>  		t = btf_type_skip_modifiers(desc_btf, args[i].type, NULL);
>>  		if (btf_type_is_ptr(t))
>>  			mark_btf_func_reg_size(env, regno, sizeof(void *));


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
  2026-01-13 22:03     ` Ihor Solodrai
@ 2026-01-13 23:48       ` Ihor Solodrai
  2026-01-14  0:55         ` Alexei Starovoitov
  2026-01-14  1:35         ` Eduard Zingerman
  0 siblings, 2 replies; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-13 23:48 UTC (permalink / raw)
  To: Eduard Zingerman, Alexei Starovoitov, Andrii Nakryiko,
	Daniel Borkmann, Martin KaFai Lau
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

On 1/13/26 2:03 PM, Ihor Solodrai wrote:
> On 1/13/26 12:39 PM, Eduard Zingerman wrote:
>> On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
>>> 
>>
>> [...]
>>
>>> @@ -14303,6 +14358,17 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>>>  	for (i = 0; i < nargs; i++) {
>>>  		u32 regno = i + 1;
>>>  
>>> +		/*
>>> +		 * Implicit kfunc arguments are set after main verification pass.
>>> +		 * For correct tracking of zero-extensions we have to reset subreg_def for such
>>> +		 * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
>>> +		 * from an earlier (irrelevant) point in the program, which may lead to an error
>>> +		 * in opt_subreg_zext_lo32_rnd_hi32().
>>> +		 */
>>> +		if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
>>> +				&& is_kfunc_arg_implicit(desc_btf, &args[i])))
>>> +			regs[regno].subreg_def = DEF_NOT_SUBREG;
>>> +
>>
>> Did you try doing this in `mark_reg_not_init()`?
>> This function is called for R1-R5 some time prior this hunk.
> 
>> Did you try doing this in `mark_reg_not_init()`?
> 
> Just tried, it doesn't work because REG0 is considered a caller saved
> register, and so it breaks the zext tracking:
> 
>         #define CALLER_SAVED_REGS 6
>         static const int caller_saved[CALLER_SAVED_REGS] = {
> 	     BPF_REG_0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5
>         };
> 
>         [...]
> 
> 	for (i = 0; i < CALLER_SAVED_REGS; i++)
> 		mark_reg_not_init(env, regs, caller_saved[i]);
> 
> CI run for the diff below (on top of this series):
> https://github.com/kernel-patches/bpf/actions/runs/20972520708
> 
>
> [...]
> 
> ---
> 
> Resetting all reg args appears to be working however (see below).
> CI: https://github.com/kernel-patches/bpf/actions/runs/20973490221
> 

A follow up after a chat with Eduard.

This change in check_kfunc_call() appears to be working:

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 092003cc7841..ff743335111c 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -13958,8 +13958,11 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
                regs = branch->frame[branch->curframe]->regs;
 
                /* Clear r0-r5 registers in forked state */
-               for (i = 0; i < CALLER_SAVED_REGS; i++)
-                       mark_reg_not_init(env, regs, caller_saved[i]);
+               for (i = 0; i < CALLER_SAVED_REGS; i++) {
+                       u32 regno = caller_saved[i];
+                       mark_reg_not_init(env, regs, regno);
+                       regs[regno].subreg_def = DEF_NOT_SUBREG;
+               }
 
                mark_reg_unknown(env, regs, BPF_REG_0);
                err = __mark_reg_s32_range(env, regs, BPF_REG_0, -MAX_ERRNO, -1);

https://github.com/kernel-patches/bpf/actions/runs/20975419422

Apparently, doing .subreg_def = DEF_NOT_SUBREG in mark_reg_not_init()
breaks zero-extension tracking somewhere else.  But this is not
directly relevant to the series.

Eduard, Alexei, any concerns with this diff? Should I send a separate
patch?


>  [...]


^ permalink raw reply related	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
  2026-01-13 21:59   ` Eduard Zingerman
@ 2026-01-14  0:03     ` Ihor Solodrai
  2026-01-14  1:06       ` Eduard Zingerman
  0 siblings, 1 reply; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-14  0:03 UTC (permalink / raw)
  To: Eduard Zingerman, Alexei Starovoitov, Andrii Nakryiko,
	Daniel Borkmann, Martin KaFai Lau
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

On 1/13/26 1:59 PM, Eduard Zingerman wrote:
> On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
> 
> [...]
> 
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
>> @@ -3271,6 +3271,38 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
>>  	return btf_vmlinux ?: ERR_PTR(-ENOENT);
>>  }
>>  
>> +#define KF_IMPL_SUFFIX "_impl"
>> +
>> +static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_env *env,
>> +						    struct btf *btf,
>> +						    const char *func_name)
>> +{
>> +	char impl_name[KSYM_SYMBOL_LEN];
> 
> Oh, as we discussed already, this should use env->tmp_str_buf.

The env->tmp_str_buf size is smaller:

	#define TMP_STR_BUF_LEN 320

*And* there is already a local char buffer of size KSYM_SYMBOL_LEN
already in use in verifier.c:

	int bpf_check_attach_target(...) {
		bool prog_extension = prog->type == BPF_PROG_TYPE_EXT;
		bool prog_tracing = prog->type == BPF_PROG_TYPE_TRACING;
		char trace_symbol[KSYM_SYMBOL_LEN];
	[...]

Since these are function names, the real limit is KSYM_SYMBOL_LEN,
right?

Sure >320 chars long kfunc name is unlikely, but technically possible.

> 
>> +	const struct btf_type *func;
>> +	s32 impl_id;
>> +	int len;
>> +
>> +	len = snprintf(impl_name, sizeof(impl_name), "%s%s", func_name, KF_IMPL_SUFFIX);
>> +	if (len < 0 || len >= sizeof(impl_name)) {
>> +		verbose(env, "function name %s%s is too long\n", func_name, KF_IMPL_SUFFIX);
>> +		return NULL;
>> +	}
> 
> [...]


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
  2026-01-13 23:48       ` Ihor Solodrai
@ 2026-01-14  0:55         ` Alexei Starovoitov
  2026-01-14  3:57           ` Ihor Solodrai
  2026-01-14  1:35         ` Eduard Zingerman
  1 sibling, 1 reply; 44+ messages in thread
From: Alexei Starovoitov @ 2026-01-14  0:55 UTC (permalink / raw)
  To: Ihor Solodrai
  Cc: Eduard Zingerman, Alexei Starovoitov, Andrii Nakryiko,
	Daniel Borkmann, Martin KaFai Lau, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, LKML,
	open list:HID CORE LAYER, sched-ext

On Tue, Jan 13, 2026 at 3:48 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>
> On 1/13/26 2:03 PM, Ihor Solodrai wrote:
> > On 1/13/26 12:39 PM, Eduard Zingerman wrote:
> >> On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
> >>>
> >>
> >> [...]
> >>
> >>> @@ -14303,6 +14358,17 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
> >>>     for (i = 0; i < nargs; i++) {
> >>>             u32 regno = i + 1;
> >>>
> >>> +           /*
> >>> +            * Implicit kfunc arguments are set after main verification pass.
> >>> +            * For correct tracking of zero-extensions we have to reset subreg_def for such
> >>> +            * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
> >>> +            * from an earlier (irrelevant) point in the program, which may lead to an error
> >>> +            * in opt_subreg_zext_lo32_rnd_hi32().
> >>> +            */
> >>> +           if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
> >>> +                           && is_kfunc_arg_implicit(desc_btf, &args[i])))
> >>> +                   regs[regno].subreg_def = DEF_NOT_SUBREG;
> >>> +
> >>
> >> Did you try doing this in `mark_reg_not_init()`?
> >> This function is called for R1-R5 some time prior this hunk.
> >
> >> Did you try doing this in `mark_reg_not_init()`?
> >
> > Just tried, it doesn't work because REG0 is considered a caller saved
> > register, and so it breaks the zext tracking:
> >
> >         #define CALLER_SAVED_REGS 6
> >         static const int caller_saved[CALLER_SAVED_REGS] = {
> >            BPF_REG_0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5
> >         };
> >
> >         [...]
> >
> >       for (i = 0; i < CALLER_SAVED_REGS; i++)
> >               mark_reg_not_init(env, regs, caller_saved[i]);
> >
> > CI run for the diff below (on top of this series):
> > https://github.com/kernel-patches/bpf/actions/runs/20972520708
> >
> >
> > [...]
> >
> > ---
> >
> > Resetting all reg args appears to be working however (see below).
> > CI: https://github.com/kernel-patches/bpf/actions/runs/20973490221
> >
>
> A follow up after a chat with Eduard.
>
> This change in check_kfunc_call() appears to be working:
>
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 092003cc7841..ff743335111c 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -13958,8 +13958,11 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>                 regs = branch->frame[branch->curframe]->regs;
>
>                 /* Clear r0-r5 registers in forked state */
> -               for (i = 0; i < CALLER_SAVED_REGS; i++)
> -                       mark_reg_not_init(env, regs, caller_saved[i]);
> +               for (i = 0; i < CALLER_SAVED_REGS; i++) {
> +                       u32 regno = caller_saved[i];
> +                       mark_reg_not_init(env, regs, regno);
> +                       regs[regno].subreg_def = DEF_NOT_SUBREG;
> +               }
>
>                 mark_reg_unknown(env, regs, BPF_REG_0);
>                 err = __mark_reg_s32_range(env, regs, BPF_REG_0, -MAX_ERRNO, -1);
>
> https://github.com/kernel-patches/bpf/actions/runs/20975419422
>
> Apparently, doing .subreg_def = DEF_NOT_SUBREG in mark_reg_not_init()
> breaks zero-extension tracking somewhere else.  But this is not
> directly relevant to the series.
>
> Eduard, Alexei, any concerns with this diff? Should I send a separate
> patch?

This is odd. Clear it only for res_spin_lock() processing?!
Should be around lines 14149 instead?

First, need to investigate why clearing it in mark_reg_not_init()
breaks things.
That's what clear_caller_saved_regs() is doing already.
Maybe these two loops in check_kfunc_call() should be doing
clear_caller_saved_regs() instead...
Needs proper investigation.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
  2026-01-14  0:03     ` Ihor Solodrai
@ 2026-01-14  1:06       ` Eduard Zingerman
  2026-01-14  4:08         ` Ihor Solodrai
  0 siblings, 1 reply; 44+ messages in thread
From: Eduard Zingerman @ 2026-01-14  1:06 UTC (permalink / raw)
  To: Ihor Solodrai, Alexei Starovoitov, Andrii Nakryiko,
	Daniel Borkmann, Martin KaFai Lau
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

On Tue, 2026-01-13 at 16:03 -0800, Ihor Solodrai wrote:
> On 1/13/26 1:59 PM, Eduard Zingerman wrote:
> > On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
> > 
> > [...]
> > 
> > > --- a/kernel/bpf/verifier.c
> > > +++ b/kernel/bpf/verifier.c
> > > @@ -3271,6 +3271,38 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
> > >  	return btf_vmlinux ?: ERR_PTR(-ENOENT);
> > >  }
> > >  
> > > +#define KF_IMPL_SUFFIX "_impl"
> > > +
> > > +static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_env *env,
> > > +						    struct btf *btf,
> > > +						    const char *func_name)
> > > +{
> > > +	char impl_name[KSYM_SYMBOL_LEN];
> > 
> > Oh, as we discussed already, this should use env->tmp_str_buf.
> 
> The env->tmp_str_buf size is smaller:
> 
> 	#define TMP_STR_BUF_LEN 320
> 
> *And* there is already a local char buffer of size KSYM_SYMBOL_LEN
> already in use in verifier.c:
> 
> 	int bpf_check_attach_target(...) {
> 		bool prog_extension = prog->type == BPF_PROG_TYPE_EXT;
> 		bool prog_tracing = prog->type == BPF_PROG_TYPE_TRACING;
> 		char trace_symbol[KSYM_SYMBOL_LEN];
> 	[...]
> 
> Since these are function names, the real limit is KSYM_SYMBOL_LEN,
> right?
> 
> Sure >320 chars long kfunc name is unlikely, but technically possible.

320 is good enough, you'll be able to cover this:

kfunc_trace_long_descriptive_kernel_symbol_for_tracing_scheduler_memory_io_and_interrupt_paths_during_runtime_analysis_of_latency_throughput_and_resource_contention_on_large_scale_multiprocessor_linux_systems_using_bpf_and_kprobes_without_requiring_kernel_recompilation_or_system_restart_for_production_use_cases_v2x

But not this:

kfunc_trace_kernel_scheduler_and_memory_management_path_for_observing_task_lifecycle_events_context_switches_page_fault_handling_and_io_wait_states_while_debugging_performance_regressions_on_large_multiprocessor_systems_running_preemptible_linux_kernels_with_bpf_tracing_and_dynamic_instrumentation_enabled_for_deep_visibility_into_runtime_behavior_and_latency_sensitive_code_paths_without_recompilation.

Should suffice, I think.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
  2026-01-13 23:48       ` Ihor Solodrai
  2026-01-14  0:55         ` Alexei Starovoitov
@ 2026-01-14  1:35         ` Eduard Zingerman
  1 sibling, 0 replies; 44+ messages in thread
From: Eduard Zingerman @ 2026-01-14  1:35 UTC (permalink / raw)
  To: Ihor Solodrai, Alexei Starovoitov, Andrii Nakryiko,
	Daniel Borkmann, Martin KaFai Lau
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext

On Tue, 2026-01-13 at 15:48 -0800, Ihor Solodrai wrote:

[...]

> A follow up after a chat with Eduard.
> 
> This change in check_kfunc_call() appears to be working:
> 
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 092003cc7841..ff743335111c 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -13958,8 +13958,11 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>                 regs = branch->frame[branch->curframe]->regs;
>  
>                 /* Clear r0-r5 registers in forked state */
> -               for (i = 0; i < CALLER_SAVED_REGS; i++)
> -                       mark_reg_not_init(env, regs, caller_saved[i]);
> +               for (i = 0; i < CALLER_SAVED_REGS; i++) {
> +                       u32 regno = caller_saved[i];
> +                       mark_reg_not_init(env, regs, regno);
> +                       regs[regno].subreg_def = DEF_NOT_SUBREG;
> +               }
>  
>                 mark_reg_unknown(env, regs, BPF_REG_0);
>                 err = __mark_reg_s32_range(env, regs, BPF_REG_0, -MAX_ERRNO, -1);
> 
> https://github.com/kernel-patches/bpf/actions/runs/20975419422
> 
> Apparently, doing .subreg_def = DEF_NOT_SUBREG in mark_reg_not_init()
> breaks zero-extension tracking somewhere else.  But this is not
> directly relevant to the series.
> 
> Eduard, Alexei, any concerns with this diff? Should I send a separate
> patch?

Imo this is acceptable to land this series but follow up investigation
is definitely needed. Either there is a bug and mark_reg_not_init() is
called in a context where upper 32-bits are still significant, or zero
extension related code can be improved to avoid patching in some cases.

Additional context for other reviewers, Ihor did two experiments:
- added '.subreg_def = DEF_NOT_SUBREG' to mark_reg_not_init(),
  which resulted in selftests failure;
- added '.subreg_def = DEF_NOT_SUBREG' as above, which worked fine.

Meaning that code in check_kfunc_call() is not a culprit.

^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
  2026-01-14  0:55         ` Alexei Starovoitov
@ 2026-01-14  3:57           ` Ihor Solodrai
  0 siblings, 0 replies; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-14  3:57 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Eduard Zingerman, Alexei Starovoitov, Andrii Nakryiko,
	Daniel Borkmann, Martin KaFai Lau, Mykyta Yatsenko, Tejun Heo,
	Alan Maguire, Benjamin Tissoires, Jiri Kosina, bpf, LKML,
	open list:HID CORE LAYER, sched-ext



On 1/13/26 4:55 PM, Alexei Starovoitov wrote:
> On Tue, Jan 13, 2026 at 3:48 PM Ihor Solodrai <ihor.solodrai@linux.dev> wrote:
>>
>> On 1/13/26 2:03 PM, Ihor Solodrai wrote:
>>> On 1/13/26 12:39 PM, Eduard Zingerman wrote:
>>>> On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
>>>>>
>>>>
>>>> [...]
>>>>
>>>>> @@ -14303,6 +14358,17 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>>>>>     for (i = 0; i < nargs; i++) {
>>>>>             u32 regno = i + 1;
>>>>>
>>>>> +           /*
>>>>> +            * Implicit kfunc arguments are set after main verification pass.
>>>>> +            * For correct tracking of zero-extensions we have to reset subreg_def for such
>>>>> +            * args. Otherwise mark_btf_func_reg_size() will be inspecting subreg_def of regs
>>>>> +            * from an earlier (irrelevant) point in the program, which may lead to an error
>>>>> +            * in opt_subreg_zext_lo32_rnd_hi32().
>>>>> +            */
>>>>> +           if (unlikely(KF_IMPLICIT_ARGS & meta.kfunc_flags
>>>>> +                           && is_kfunc_arg_implicit(desc_btf, &args[i])))
>>>>> +                   regs[regno].subreg_def = DEF_NOT_SUBREG;
>>>>> +
>>>>
>>>> Did you try doing this in `mark_reg_not_init()`?
>>>> This function is called for R1-R5 some time prior this hunk.
>>>
>>>> Did you try doing this in `mark_reg_not_init()`?
>>>
>>> Just tried, it doesn't work because REG0 is considered a caller saved
>>> register, and so it breaks the zext tracking:
>>>
>>>         #define CALLER_SAVED_REGS 6
>>>         static const int caller_saved[CALLER_SAVED_REGS] = {
>>>            BPF_REG_0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5
>>>         };
>>>
>>>         [...]
>>>
>>>       for (i = 0; i < CALLER_SAVED_REGS; i++)
>>>               mark_reg_not_init(env, regs, caller_saved[i]);
>>>
>>> CI run for the diff below (on top of this series):
>>> https://github.com/kernel-patches/bpf/actions/runs/20972520708
>>>
>>>
>>> [...]
>>>
>>> ---
>>>
>>> Resetting all reg args appears to be working however (see below).
>>> CI: https://github.com/kernel-patches/bpf/actions/runs/20973490221
>>>
>>
>> A follow up after a chat with Eduard.
>>
>> This change in check_kfunc_call() appears to be working:
>>
>> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
>> index 092003cc7841..ff743335111c 100644
>> --- a/kernel/bpf/verifier.c
>> +++ b/kernel/bpf/verifier.c
>> @@ -13958,8 +13958,11 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
>>                 regs = branch->frame[branch->curframe]->regs;
>>
>>                 /* Clear r0-r5 registers in forked state */
>> -               for (i = 0; i < CALLER_SAVED_REGS; i++)
>> -                       mark_reg_not_init(env, regs, caller_saved[i]);
>> +               for (i = 0; i < CALLER_SAVED_REGS; i++) {
>> +                       u32 regno = caller_saved[i];
>> +                       mark_reg_not_init(env, regs, regno);
>> +                       regs[regno].subreg_def = DEF_NOT_SUBREG;
>> +               }
>>
>>                 mark_reg_unknown(env, regs, BPF_REG_0);
>>                 err = __mark_reg_s32_range(env, regs, BPF_REG_0, -MAX_ERRNO, -1);
>>
>> https://github.com/kernel-patches/bpf/actions/runs/20975419422
>>
>> Apparently, doing .subreg_def = DEF_NOT_SUBREG in mark_reg_not_init()
>> breaks zero-extension tracking somewhere else.  But this is not
>> directly relevant to the series.
>>
>> Eduard, Alexei, any concerns with this diff? Should I send a separate
>> patch?
> 
> This is odd. Clear it only for res_spin_lock() processing?!
> Should be around lines 14149 instead?

Yes. Sorry, this was a messed up local diff. The commits tested on CI
are correct though. I'll have this fix in v2, since it is necessary
for KF_IMPLICIT_ARGS to work.

I'll look into this problem more after implicit args land, unless
someone beats me to it.

> 
> First, need to investigate why clearing it in mark_reg_not_init()
> breaks things.
> That's what clear_caller_saved_regs() is doing already.
> Maybe these two loops in check_kfunc_call() should be doing
> clear_caller_saved_regs() instead...
> Needs proper investigation.


^ permalink raw reply	[flat|nested] 44+ messages in thread

* Re: [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS
  2026-01-14  1:06       ` Eduard Zingerman
@ 2026-01-14  4:08         ` Ihor Solodrai
  0 siblings, 0 replies; 44+ messages in thread
From: Ihor Solodrai @ 2026-01-14  4:08 UTC (permalink / raw)
  To: Eduard Zingerman, Alexei Starovoitov, Andrii Nakryiko,
	Daniel Borkmann, Martin KaFai Lau
  Cc: Mykyta Yatsenko, Tejun Heo, Alan Maguire, Benjamin Tissoires,
	Jiri Kosina, bpf, linux-kernel, linux-input, sched-ext



On 1/13/26 5:06 PM, Eduard Zingerman wrote:
> On Tue, 2026-01-13 at 16:03 -0800, Ihor Solodrai wrote:
>> On 1/13/26 1:59 PM, Eduard Zingerman wrote:
>>> On Fri, 2026-01-09 at 10:48 -0800, Ihor Solodrai wrote:
>>>
>>> [...]
>>>
>>>> --- a/kernel/bpf/verifier.c
>>>> +++ b/kernel/bpf/verifier.c
>>>> @@ -3271,6 +3271,38 @@ static struct btf *find_kfunc_desc_btf(struct bpf_verifier_env *env, s16 offset)
>>>>  	return btf_vmlinux ?: ERR_PTR(-ENOENT);
>>>>  }
>>>>  
>>>> +#define KF_IMPL_SUFFIX "_impl"
>>>> +
>>>> +static const struct btf_type *find_kfunc_impl_proto(struct bpf_verifier_env *env,
>>>> +						    struct btf *btf,
>>>> +						    const char *func_name)
>>>> +{
>>>> +	char impl_name[KSYM_SYMBOL_LEN];
>>>
>>> Oh, as we discussed already, this should use env->tmp_str_buf.
>>
>> The env->tmp_str_buf size is smaller:
>>
>> 	#define TMP_STR_BUF_LEN 320
>>
>> *And* there is already a local char buffer of size KSYM_SYMBOL_LEN
>> already in use in verifier.c:
>>
>> 	int bpf_check_attach_target(...) {
>> 		bool prog_extension = prog->type == BPF_PROG_TYPE_EXT;
>> 		bool prog_tracing = prog->type == BPF_PROG_TYPE_TRACING;
>> 		char trace_symbol[KSYM_SYMBOL_LEN];
>> 	[...]
>>
>> Since these are function names, the real limit is KSYM_SYMBOL_LEN,
>> right?
>>
>> Sure >320 chars long kfunc name is unlikely, but technically possible.
> 
> 320 is good enough, you'll be able to cover this:
> 
> kfunc_trace_long_descriptive_kernel_symbol_for_tracing_scheduler_memory_io_and_interrupt_paths_during_runtime_analysis_of_latency_throughput_and_resource_contention_on_large_scale_multiprocessor_linux_systems_using_bpf_and_kprobes_without_requiring_kernel_recompilation_or_system_restart_for_production_use_cases_v2x
> 
> But not this:
> 
> kfunc_trace_kernel_scheduler_and_memory_management_path_for_observing_task_lifecycle_events_context_switches_page_fault_handling_and_io_wait_states_while_debugging_performance_regressions_on_large_multiprocessor_systems_running_preemptible_linux_kernels_with_bpf_tracing_and_dynamic_instrumentation_enabled_for_deep_visibility_into_runtime_behavior_and_latency_sensitive_code_paths_without_recompilation.
> 
> Should suffice, I think.

I will laugh for at least 321 seconds when the size of this buffer
will have to be increased, and will make sure you hear it :)

They thought 640K of memory is enough, you know.


^ permalink raw reply	[flat|nested] 44+ messages in thread

end of thread, other threads:[~2026-01-14  4:08 UTC | newest]

Thread overview: 44+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-09 18:48 [PATCH bpf-next v1 00/10] bpf: Kernel functions with KF_IMPLICIT_ARGS Ihor Solodrai
2026-01-09 18:48 ` [PATCH bpf-next v1 01/10] bpf: Refactor btf_kfunc_id_set_contains Ihor Solodrai
2026-01-13 21:43   ` Eduard Zingerman
2026-01-09 18:48 ` [PATCH bpf-next v1 02/10] bpf: Introduce struct bpf_kfunc_meta Ihor Solodrai
2026-01-13 21:46   ` Eduard Zingerman
2026-01-09 18:48 ` [PATCH bpf-next v1 03/10] bpf: Verifier support for KF_IMPLICIT_ARGS Ihor Solodrai
2026-01-09 19:54   ` Alexei Starovoitov
2026-01-09 23:25   ` Andrii Nakryiko
2026-01-13 20:39   ` Eduard Zingerman
2026-01-13 22:03     ` Ihor Solodrai
2026-01-13 23:48       ` Ihor Solodrai
2026-01-14  0:55         ` Alexei Starovoitov
2026-01-14  3:57           ` Ihor Solodrai
2026-01-14  1:35         ` Eduard Zingerman
2026-01-13 21:59   ` Eduard Zingerman
2026-01-14  0:03     ` Ihor Solodrai
2026-01-14  1:06       ` Eduard Zingerman
2026-01-14  4:08         ` Ihor Solodrai
2026-01-09 18:48 ` [PATCH bpf-next v1 04/10] resolve_btfids: Support " Ihor Solodrai
2026-01-09 19:15   ` bot+bpf-ci
2026-01-09 19:34     ` Ihor Solodrai
2026-01-09 23:25   ` Andrii Nakryiko
2026-01-10  1:15     ` Ihor Solodrai
2026-01-12 16:51       ` Andrii Nakryiko
2026-01-13  1:49         ` Ihor Solodrai
2026-01-13 16:55           ` Andrii Nakryiko
2026-01-09 18:48 ` [PATCH bpf-next v1 05/10] selftests/bpf: Add tests " Ihor Solodrai
2026-01-09 23:25   ` Andrii Nakryiko
2026-01-10  1:29     ` Ihor Solodrai
2026-01-12 16:55       ` Andrii Nakryiko
2026-01-09 18:48 ` [PATCH bpf-next v1 06/10] bpf: Add bpf_wq_set_callback kfunc with KF_IMPLICIT_ARGS Ihor Solodrai
2026-01-09 18:48 ` [PATCH bpf-next v1 07/10] HID: Use bpf_wq_set_callback kernel function Ihor Solodrai
2026-01-09 21:34   ` Benjamin Tissoires
2026-01-09 18:48 ` [PATCH bpf-next v1 08/10] bpf: Add bpf_task_work_schedule_* kfuncs with KF_IMPLICIT_ARGS Ihor Solodrai
2026-01-09 19:58   ` Alexei Starovoitov
2026-01-09 20:02     ` Ihor Solodrai
2026-01-09 20:47       ` Alexei Starovoitov
2026-01-09 21:39         ` Ihor Solodrai
2026-01-09 21:49           ` Alexei Starovoitov
2026-01-09 21:56             ` Ihor Solodrai
2026-01-12 18:53               ` Ihor Solodrai
2026-01-12 22:43                 ` Andrii Nakryiko
2026-01-09 18:48 ` [PATCH bpf-next v1 09/10] bpf: Add bpf_stream_vprintk " Ihor Solodrai
2026-01-09 18:48 ` [PATCH bpf-next v1 10/10] bpf,docs: Document KF_IMPLICIT_ARGS flag Ihor Solodrai

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox