summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorAlexei Starovoitov <ast@kernel.org>2026-01-21 03:15:58 +0300
committerAlexei Starovoitov <ast@kernel.org>2026-01-21 03:22:38 +0300
commitb236134f70ba1e98a85d00623ea8fafb41dacf7f (patch)
tree8d9bf4462f03a5f2599978c564601043240e62ad /tools
parent2e6690d4f7fc41c4fae7d0a4c0bf11f1973e5650 (diff)
parent74bc4f6127207624ec06f0d0984b280a390992aa (diff)
downloadlinux-b236134f70ba1e98a85d00623ea8fafb41dacf7f.tar.xz
Merge branch 'bpf-kernel-functions-with-kf_implicit_args'
Ihor Solodrai says: ==================== bpf: Kernel functions with KF_IMPLICIT_ARGS This series implements a generic "implicit arguments" feature for BPF kernel functions. For context see prior work [1][2]. 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 in 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 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). 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 usage of <kfunc>_impl functions in BPF is only allowed for kfuncs with an explicit kernel (or kmodule) declaration, that is in "legacy" cases. As of this series, there are no legacy kernel functions, as all __prog users are migrated to KF_IMPLICIT_ARGS. However the implementation allows for legacy cases support in principle. The series removes the following BPF kernel functions: - bpf_stream_vprintk_impl - bpf_task_work_schedule_resume_impl - bpf_task_work_schedule_signal_impl - bpf_wq_set_callback_impl This will break existing BPF programs calling these functions (the verifier will not load them) on new kernels. To mitigate, BPF users are advised to use the following pattern [4]: if (xxx_impl) xxx_impl(..., NULL); else xxx(...); Which can be wrapped in a macro. 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 - patches #4-#5 implement btf2btf transformation in resolve_btfids - patch #6 adds selftests specific to KF_IMPLICIT_ARGS feature - patches #7-#11 migrate the current users of __prog argument to KF_IMPLICIT_ARGS - patch #12 removes __prog arg suffix support from the kernel - patch #13 updates the docs [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/CAEf4BzbgPfRm9BX=TsZm-TsHFAHcwhPY4vTt=9OT-uhWqf8tqw@mail.gmail.com/ --- v2->v3: - resolve_btfids: Use dynamic reallocation for btf2btf_context arrays (Andrii) - resolve_btfids: Add missing free() for btf2btf_context arrays (AI) - Other nits in resolve_btfids (Andrii, Eduard) v2: https://lore.kernel.org/bpf/20260116201700.864797-1-ihor.solodrai@linux.dev/ v1->v2: - Replace the following kernel functions with KF_IMPLICIT_ARGS version: - bpf_stream_vprintk_impl -> bpf_stream_vprintk - bpf_task_work_schedule_resume_impl -> bpf_task_work_schedule_resume - bpf_task_work_schedule_signal_impl -> bpf_task_work_schedule_signal - bpf_wq_set_callback_impl -> bpf_wq_set_callback_impl - Remove __prog arg suffix support from the verifier - Rework btf2btf implementation in resolve_btfids - Do distill base and sort before BTF_ids patching - Collect kfuncs based on BTF decl tags, before BTF_ids are patched - resolve_btfids: use dynamic memory for intermediate data (Andrii) - verifier: reset .subreg_def for caller saved registers on kfunc call (Eduard) - selftests/hid: remove Makefile changes (Benjamin) - selftests/bpf: Add a patch (#11) migrating struct_ops_assoc test to KF_IMPLICIT_ARGS - Various nits across the series (Alexei, Andrii, Eduard) v1: https://lore.kernel.org/bpf/20260109184852.1089786-1-ihor.solodrai@linux.dev/ --- ==================== Link: https://patch.msgid.link/20260120222638.3976562-1-ihor.solodrai@linux.dev Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Diffstat (limited to 'tools')
-rw-r--r--tools/bpf/resolve_btfids/main.c451
-rw-r--r--tools/lib/bpf/bpf_helpers.h6
-rw-r--r--tools/testing/selftests/bpf/bpf_experimental.h5
-rw-r--r--tools/testing/selftests/bpf/prog_tests/kfunc_implicit_args.c10
-rw-r--r--tools/testing/selftests/bpf/progs/file_reader.c2
-rw-r--r--tools/testing/selftests/bpf/progs/kfunc_implicit_args.c41
-rw-r--r--tools/testing/selftests/bpf/progs/stream_fail.c6
-rw-r--r--tools/testing/selftests/bpf/progs/struct_ops_assoc.c8
-rw-r--r--tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c4
-rw-r--r--tools/testing/selftests/bpf/progs/struct_ops_assoc_reuse.c6
-rw-r--r--tools/testing/selftests/bpf/progs/task_work.c7
-rw-r--r--tools/testing/selftests/bpf/progs/task_work_fail.c8
-rw-r--r--tools/testing/selftests/bpf/progs/task_work_stress.c4
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_async_cb_context.c8
-rw-r--r--tools/testing/selftests/bpf/progs/wq_failures.c4
-rw-r--r--tools/testing/selftests/bpf/test_kmods/bpf_testmod.c35
-rw-r--r--tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h6
-rw-r--r--tools/testing/selftests/hid/progs/hid_bpf_helpers.h8
18 files changed, 549 insertions, 70 deletions
diff --git a/tools/bpf/resolve_btfids/main.c b/tools/bpf/resolve_btfids/main.c
index 343d08050116..db8d1554bdcc 100644
--- a/tools/bpf/resolve_btfids/main.c
+++ b/tools/bpf/resolve_btfids/main.c
@@ -152,6 +152,25 @@ struct object {
int nr_typedefs;
};
+#define KF_IMPLICIT_ARGS (1 << 16)
+#define KF_IMPL_SUFFIX "_impl"
+
+struct kfunc {
+ const char *name;
+ u32 btf_id;
+ u32 flags;
+};
+
+struct btf2btf_context {
+ struct btf *btf;
+ u32 *decl_tags;
+ u32 nr_decl_tags;
+ u32 max_decl_tags;
+ struct kfunc *kfuncs;
+ u32 nr_kfuncs;
+ u32 max_kfuncs;
+};
+
static int verbose;
static int warnings;
@@ -563,19 +582,6 @@ static int load_btf(struct object *obj)
obj->base_btf = base_btf;
obj->btf = btf;
- if (obj->base_btf && obj->distill_base) {
- err = btf__distill_base(obj->btf, &base_btf, &btf);
- if (err) {
- pr_err("FAILED to distill base BTF: %s\n", strerror(errno));
- goto out_err;
- }
-
- btf__free(obj->base_btf);
- btf__free(obj->btf);
- obj->base_btf = base_btf;
- obj->btf = btf;
- }
-
return 0;
out_err:
@@ -850,6 +856,366 @@ static int dump_raw_btf(struct btf *btf, const char *out_path)
return 0;
}
+static const struct btf_type *btf_type_skip_qualifiers(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;
+}
+
+static int push_decl_tag_id(struct btf2btf_context *ctx, u32 decl_tag_id)
+{
+ u32 *arr = ctx->decl_tags;
+ u32 cap = ctx->max_decl_tags;
+
+ if (ctx->nr_decl_tags + 1 > cap) {
+ cap = max(cap + 256, cap * 2);
+ arr = realloc(arr, sizeof(u32) * cap);
+ if (!arr)
+ return -ENOMEM;
+ ctx->max_decl_tags = cap;
+ ctx->decl_tags = arr;
+ }
+
+ ctx->decl_tags[ctx->nr_decl_tags++] = decl_tag_id;
+
+ return 0;
+}
+
+static int push_kfunc(struct btf2btf_context *ctx, struct kfunc *kfunc)
+{
+ struct kfunc *arr = ctx->kfuncs;
+ u32 cap = ctx->max_kfuncs;
+
+ if (ctx->nr_kfuncs + 1 > cap) {
+ cap = max(cap + 256, cap * 2);
+ arr = realloc(arr, sizeof(struct kfunc) * cap);
+ if (!arr)
+ return -ENOMEM;
+ ctx->max_kfuncs = cap;
+ ctx->kfuncs = arr;
+ }
+
+ ctx->kfuncs[ctx->nr_kfuncs++] = *kfunc;
+
+ return 0;
+}
+
+static int collect_decl_tags(struct btf2btf_context *ctx)
+{
+ const u32 type_cnt = btf__type_cnt(ctx->btf);
+ struct btf *btf = ctx->btf;
+ const struct btf_type *t;
+ int err;
+
+ for (u32 id = 1; id < type_cnt; id++) {
+ t = btf__type_by_id(btf, id);
+ if (!btf_is_decl_tag(t))
+ continue;
+ err = push_decl_tag_id(ctx, id);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+/*
+ * To find the kfunc flags having its struct btf_id (with ELF addresses)
+ * we need to find the address that is in range of a set8.
+ * If a set8 is found, then the flags are located at addr + 4 bytes.
+ * Return 0 (no flags!) if not found.
+ */
+static u32 find_kfunc_flags(struct object *obj, struct btf_id *kfunc_id)
+{
+ const u32 *elf_data_ptr = obj->efile.idlist->d_buf;
+ u64 set_lower_addr, set_upper_addr, addr;
+ struct btf_id *set_id;
+ struct rb_node *next;
+ u32 flags;
+ u64 idx;
+
+ for (next = rb_first(&obj->sets); next; next = rb_next(next)) {
+ set_id = rb_entry(next, struct btf_id, rb_node);
+ if (set_id->kind != BTF_ID_KIND_SET8 || set_id->addr_cnt != 1)
+ continue;
+
+ set_lower_addr = set_id->addr[0];
+ set_upper_addr = set_lower_addr + set_id->cnt * sizeof(u64);
+
+ for (u32 i = 0; i < kfunc_id->addr_cnt; i++) {
+ addr = kfunc_id->addr[i];
+ /*
+ * Lower bound is exclusive to skip the 8-byte header of the set.
+ * Upper bound is inclusive to capture the last entry at offset 8*cnt.
+ */
+ if (set_lower_addr < addr && addr <= set_upper_addr) {
+ pr_debug("found kfunc %s in BTF_ID_FLAGS %s\n",
+ kfunc_id->name, set_id->name);
+ idx = addr - obj->efile.idlist_addr;
+ idx = idx / sizeof(u32) + 1;
+ flags = elf_data_ptr[idx];
+
+ return flags;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int collect_kfuncs(struct object *obj, struct btf2btf_context *ctx)
+{
+ const char *tag_name, *func_name;
+ struct btf *btf = ctx->btf;
+ const struct btf_type *t;
+ u32 flags, func_id;
+ struct kfunc kfunc;
+ struct btf_id *id;
+ int err;
+
+ if (ctx->nr_decl_tags == 0)
+ return 0;
+
+ for (u32 i = 0; i < ctx->nr_decl_tags; i++) {
+ t = btf__type_by_id(btf, ctx->decl_tags[i]);
+ if (btf_kflag(t) || btf_decl_tag(t)->component_idx != -1)
+ continue;
+
+ tag_name = btf__name_by_offset(btf, t->name_off);
+ if (strcmp(tag_name, "bpf_kfunc") != 0)
+ continue;
+
+ func_id = t->type;
+ t = btf__type_by_id(btf, func_id);
+ if (!btf_is_func(t))
+ continue;
+
+ func_name = btf__name_by_offset(btf, t->name_off);
+ if (!func_name)
+ continue;
+
+ id = btf_id__find(&obj->funcs, func_name);
+ if (!id || id->kind != BTF_ID_KIND_SYM)
+ continue;
+
+ flags = find_kfunc_flags(obj, id);
+
+ kfunc.name = id->name;
+ kfunc.btf_id = func_id;
+ kfunc.flags = flags;
+
+ err = push_kfunc(ctx, &kfunc);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int build_btf2btf_context(struct object *obj, struct btf2btf_context *ctx)
+{
+ int err;
+
+ ctx->btf = obj->btf;
+
+ err = collect_decl_tags(ctx);
+ if (err) {
+ pr_err("ERROR: resolve_btfids: failed to collect decl tags from BTF\n");
+ return err;
+ }
+
+ err = collect_kfuncs(obj, ctx);
+ if (err) {
+ pr_err("ERROR: resolve_btfids: failed to collect kfuncs from BTF\n");
+ return err;
+ }
+
+ return 0;
+}
+
+
+/* Implicit BPF kfunc arguments can only be of particular types */
+static bool 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_type_skip_qualifiers(btf, p->type);
+ if (!btf_is_ptr(t))
+ return false;
+
+ t = btf_type_skip_qualifiers(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 int process_kfunc_with_implicit_args(struct btf2btf_context *ctx, struct kfunc *kfunc)
+{
+ s32 idx, new_proto_id, new_func_id, proto_id;
+ const char *param_name, *tag_name;
+ const struct btf_param *params;
+ enum btf_func_linkage linkage;
+ char tmp_name[KSYM_NAME_LEN];
+ struct btf *btf = ctx->btf;
+ int err, len, nr_params;
+ struct btf_type *t;
+
+ t = (struct btf_type *)btf__type_by_id(btf, kfunc->btf_id);
+ if (!t || !btf_is_func(t)) {
+ pr_err("ERROR: resolve_btfids: btf id %d is not a function\n", kfunc->btf_id);
+ return -EINVAL;
+ }
+
+ 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->btf_id)
+ continue;
+
+ tag_name = btf__name_by_offset(btf, t->name_off);
+ if (strcmp(tag_name, "bpf_kfunc") == 0)
+ continue;
+
+ idx = btf_decl_tag(t)->component_idx;
+
+ if (btf_kflag(t))
+ err = btf__add_decl_attr(btf, tag_name, new_func_id, idx);
+ else
+ err = btf__add_decl_tag(btf, tag_name, new_func_id, idx);
+
+ 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:
+ t = (struct btf_type *)btf__type_by_id(btf, proto_id);
+ 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 */
+ t = (struct btf_type *)btf__type_by_id(btf, proto_id);
+ nr_params = btf_vlen(t);
+ for (int i = 0; i < nr_params; i++) {
+ params = btf_params(t);
+ if (is_kf_implicit_arg(btf, &params[i]))
+ break;
+ param_name = btf__name_by_offset(btf, params[i].name_off);
+ err = btf__add_func_param(btf, param_name, 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;
+ }
+ t = (struct btf_type *)btf__type_by_id(btf, proto_id);
+ }
+
+ /* Finally change the prototype of the original kfunc to the new one */
+ t = (struct btf_type *)btf__type_by_id(btf, kfunc->btf_id);
+ t->type = new_proto_id;
+
+ pr_debug("resolve_btfids: updated BTF for kfunc with implicit args %s\n", kfunc->name);
+
+ return 0;
+}
+
+static int btf2btf(struct object *obj)
+{
+ struct btf2btf_context ctx = {};
+ int err;
+
+ err = build_btf2btf_context(obj, &ctx);
+ if (err)
+ goto out;
+
+ for (u32 i = 0; i < ctx.nr_kfuncs; i++) {
+ struct kfunc *kfunc = &ctx.kfuncs[i];
+
+ if (!(kfunc->flags & KF_IMPLICIT_ARGS))
+ continue;
+
+ err = process_kfunc_with_implicit_args(&ctx, kfunc);
+ if (err)
+ goto out;
+ }
+
+ err = 0;
+out:
+ free(ctx.decl_tags);
+ free(ctx.kfuncs);
+
+ return err;
+}
+
/*
* Sort types by name in ascending order resulting in all
* anonymous types being placed before named types.
@@ -911,6 +1277,41 @@ out:
return err;
}
+static int finalize_btf(struct object *obj)
+{
+ struct btf *base_btf = obj->base_btf, *btf = obj->btf;
+ int err;
+
+ if (obj->base_btf && obj->distill_base) {
+ err = btf__distill_base(obj->btf, &base_btf, &btf);
+ if (err) {
+ pr_err("FAILED to distill base BTF: %s\n", strerror(errno));
+ goto out_err;
+ }
+
+ btf__free(obj->base_btf);
+ btf__free(obj->btf);
+ obj->base_btf = base_btf;
+ obj->btf = btf;
+ }
+
+ err = sort_btf_by_name(obj->btf);
+ if (err) {
+ pr_err("FAILED to sort BTF: %s\n", strerror(errno));
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ btf__free(base_btf);
+ btf__free(btf);
+ obj->base_btf = NULL;
+ obj->btf = NULL;
+
+ return err;
+}
+
static inline int make_out_path(char *buf, u32 buf_sz, const char *in_path, const char *suffix)
{
int len = snprintf(buf, buf_sz, "%s%s", in_path, suffix);
@@ -1054,6 +1455,7 @@ int main(int argc, const char **argv)
};
const char *btfids_path = NULL;
bool fatal_warnings = false;
+ bool resolve_btfids = true;
char out_path[PATH_MAX];
struct option btfid_options[] = {
@@ -1083,12 +1485,6 @@ int main(int argc, const char **argv)
if (btfids_path)
return patch_btfids(btfids_path, obj.path);
- if (load_btf(&obj))
- goto out;
-
- if (sort_btf_by_name(obj.btf))
- goto out;
-
if (elf_collect(&obj))
goto out;
@@ -1099,12 +1495,25 @@ int main(int argc, const char **argv)
if (obj.efile.idlist_shndx == -1 ||
obj.efile.symbols_shndx == -1) {
pr_debug("Cannot find .BTF_ids or symbols sections, skip symbols resolution\n");
- goto dump_btf;
+ resolve_btfids = false;
}
- if (symbols_collect(&obj))
+ if (resolve_btfids)
+ if (symbols_collect(&obj))
+ goto out;
+
+ if (load_btf(&obj))
goto out;
+ if (btf2btf(&obj))
+ goto out;
+
+ if (finalize_btf(&obj))
+ goto out;
+
+ if (!resolve_btfids)
+ goto dump_btf;
+
if (symbols_resolve(&obj))
goto out;
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/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/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/file_reader.c b/tools/testing/selftests/bpf/progs/file_reader.c
index 4d756b623557..462712ff3b8a 100644
--- a/tools/testing/selftests/bpf/progs/file_reader.c
+++ b/tools/testing/selftests/bpf/progs/file_reader.c
@@ -77,7 +77,7 @@ 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/kfunc_implicit_args.c b/tools/testing/selftests/bpf/progs/kfunc_implicit_args.c
new file mode 100644
index 000000000000..89b6a47e22dd
--- /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, struct bpf_prog_aux *aux) __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, struct bpf_prog_aux *aux) __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 __msg("cannot find address for kernel function bpf_kfunc_implicit_arg_impl")
+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/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;
}
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
index 8f1097903e22..68842e3f936b 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc.c
@@ -32,7 +32,7 @@ int BPF_PROG(sys_enter_prog_a, struct pt_regs *regs, long id)
if (!test_pid || task->pid != test_pid)
return 0;
- ret = bpf_kfunc_multi_st_ops_test_1_impl(&args, NULL);
+ ret = bpf_kfunc_multi_st_ops_test_1_assoc(&args);
if (ret != MAP_A_MAGIC)
test_err_a++;
@@ -45,7 +45,7 @@ int syscall_prog_a(void *ctx)
struct st_ops_args args = {};
int ret;
- ret = bpf_kfunc_multi_st_ops_test_1_impl(&args, NULL);
+ ret = bpf_kfunc_multi_st_ops_test_1_assoc(&args);
if (ret != MAP_A_MAGIC)
test_err_a++;
@@ -79,7 +79,7 @@ int BPF_PROG(sys_enter_prog_b, struct pt_regs *regs, long id)
if (!test_pid || task->pid != test_pid)
return 0;
- ret = bpf_kfunc_multi_st_ops_test_1_impl(&args, NULL);
+ ret = bpf_kfunc_multi_st_ops_test_1_assoc(&args);
if (ret != MAP_B_MAGIC)
test_err_b++;
@@ -92,7 +92,7 @@ int syscall_prog_b(void *ctx)
struct st_ops_args args = {};
int ret;
- ret = bpf_kfunc_multi_st_ops_test_1_impl(&args, NULL);
+ ret = bpf_kfunc_multi_st_ops_test_1_assoc(&args);
if (ret != MAP_B_MAGIC)
test_err_b++;
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c
index d5a2ea934284..0bed49e9f217 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c
@@ -31,7 +31,7 @@ __noinline static int timer_cb(void *map, int *key, struct bpf_timer *timer)
struct st_ops_args args = {};
recur++;
- timer_test_1_ret = bpf_kfunc_multi_st_ops_test_1_impl(&args, NULL);
+ timer_test_1_ret = bpf_kfunc_multi_st_ops_test_1_assoc(&args);
recur--;
timer_cb_run++;
@@ -64,7 +64,7 @@ int syscall_prog(void *ctx)
struct st_ops_args args = {};
int ret;
- ret = bpf_kfunc_multi_st_ops_test_1_impl(&args, NULL);
+ ret = bpf_kfunc_multi_st_ops_test_1_assoc(&args);
if (ret != MAP_MAGIC)
test_err++;
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc_reuse.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc_reuse.c
index 5bb6ebf5eed4..396b3e58c729 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_assoc_reuse.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc_reuse.c
@@ -23,7 +23,7 @@ int BPF_PROG(test_1_a, struct st_ops_args *args)
if (!recur) {
recur++;
- ret = bpf_kfunc_multi_st_ops_test_1_impl(args, NULL);
+ ret = bpf_kfunc_multi_st_ops_test_1_assoc(args);
if (ret != -1)
test_err_a++;
recur--;
@@ -40,7 +40,7 @@ int syscall_prog_a(void *ctx)
struct st_ops_args args = {};
int ret;
- ret = bpf_kfunc_multi_st_ops_test_1_impl(&args, NULL);
+ ret = bpf_kfunc_multi_st_ops_test_1_assoc(&args);
if (ret != MAP_A_MAGIC)
test_err_a++;
@@ -62,7 +62,7 @@ int syscall_prog_b(void *ctx)
struct st_ops_args args = {};
int ret;
- ret = bpf_kfunc_multi_st_ops_test_1_impl(&args, NULL);
+ ret = bpf_kfunc_multi_st_ops_test_1_assoc(&args);
if (ret != MAP_A_MAGIC)
test_err_b++;
diff --git a/tools/testing/selftests/bpf/progs/task_work.c b/tools/testing/selftests/bpf/progs/task_work.c
index 663a80990f8f..a6009d105158 100644
--- a/tools/testing/selftests/bpf/progs/task_work.c
+++ b/tools/testing/selftests/bpf/progs/task_work.c
@@ -65,8 +65,7 @@ int oncpu_hash_map(struct pt_regs *args)
work = bpf_map_lookup_elem(&hmap, &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;
}
@@ -80,7 +79,7 @@ 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 +101,6 @@ 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..82e4b8913333 100644
--- a/tools/testing/selftests/bpf/progs/task_work_fail.c
+++ b/tools/testing/selftests/bpf/progs/task_work_fail.c
@@ -53,7 +53,7 @@ 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 +65,7 @@ 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 +76,7 @@ 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 +91,6 @@ 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..1d4378f351ef 100644
--- a/tools/testing/selftests/bpf/progs/task_work_stress.c
+++ b/tools/testing/selftests/bpf/progs/task_work_stress.c
@@ -51,8 +51,8 @@ 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..39aff82549c9 100644
--- a/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
+++ b/tools/testing/selftests/bpf/progs/verifier_async_cb_context.c
@@ -96,7 +96,7 @@ int wq_non_sleepable_prog(void *ctx)
if (bpf_wq_init(&val->w, &wq_map, 0) != 0)
return 0;
- if (bpf_wq_set_callback_impl(&val->w, wq_cb, 0, NULL) != 0)
+ if (bpf_wq_set_callback(&val->w, wq_cb, 0) != 0)
return 0;
return 0;
}
@@ -114,7 +114,7 @@ int wq_sleepable_prog(void *ctx)
if (bpf_wq_init(&val->w, &wq_map, 0) != 0)
return 0;
- if (bpf_wq_set_callback_impl(&val->w, wq_cb, 0, NULL) != 0)
+ if (bpf_wq_set_callback(&val->w, wq_cb, 0) != 0)
return 0;
return 0;
}
@@ -156,7 +156,7 @@ 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 +176,6 @@ 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;
}
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)
{
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index bc07ce9d5477..0d542ba64365 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -1140,7 +1140,11 @@ __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_multi_st_ops_test_1_assoc(struct st_ops_args *args, struct bpf_prog_aux *aux);
+
+__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, struct bpf_prog_aux *aux);
BTF_KFUNCS_START(bpf_testmod_check_kfunc_ids)
BTF_ID_FLAGS(func, bpf_testmod_test_mod_kfunc)
@@ -1183,7 +1187,10 @@ BTF_ID_FLAGS(func, bpf_kfunc_st_ops_test_epilogue, KF_SLEEPABLE)
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_multi_st_ops_test_1_assoc, KF_IMPLICIT_ARGS)
+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)
@@ -1662,19 +1669,37 @@ int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id)
}
/* Call test_1() of the associated struct_ops map */
-int bpf_kfunc_multi_st_ops_test_1_impl(struct st_ops_args *args, void *aux__prog)
+int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args, struct bpf_prog_aux *aux)
{
- struct bpf_prog_aux *prog_aux = (struct bpf_prog_aux *)aux__prog;
struct bpf_testmod_multi_st_ops *st_ops;
int ret = -1;
- st_ops = (struct bpf_testmod_multi_st_ops *)bpf_prog_get_assoc_struct_ops(prog_aux);
+ st_ops = (struct bpf_testmod_multi_st_ops *)bpf_prog_get_assoc_struct_ops(aux);
if (st_ops)
ret = st_ops->test_1(args);
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, struct bpf_prog_aux *aux)
+{
+ return bpf_kfunc_implicit_arg_legacy(a, b, aux);
+}
+
static int multi_st_ops_reg(void *kdata, struct bpf_link *link)
{
struct bpf_testmod_multi_st_ops *st_ops =
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
index 2357a0340ffe..225ea30c4e3d 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
@@ -161,7 +161,9 @@ void bpf_kfunc_rcu_task_test(struct task_struct *ptr) __ksym;
struct task_struct *bpf_kfunc_ret_rcu_test(void) __ksym;
int *bpf_kfunc_ret_rcu_test_nostruct(int rdonly_buf_size) __ksym;
-int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id) __ksym;
-int bpf_kfunc_multi_st_ops_test_1_impl(struct st_ops_args *args, void *aux__prog) __ksym;
+#ifndef __KERNEL__
+extern int bpf_kfunc_multi_st_ops_test_1(struct st_ops_args *args, u32 id) __weak __ksym;
+extern int bpf_kfunc_multi_st_ops_test_1_assoc(struct st_ops_args *args) __weak __ksym;
+#endif
#endif /* _BPF_TESTMOD_KFUNC_H */
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 */