[PATCH bpf-next v2 2/9] bpf: split the kfunc, dtor kfunc and struct_ops registration bodies
From: Jay Wang
Date: Fri Sep 25 2026 - 17:15:17 EST
Split __register_btf_kfunc_id_set(), register_btf_id_dtor_kfuncs() and
__register_bpf_struct_ops() into the part that looks up the BTF for the
owner and the part that adds the registration to a given BTF:
btf_kfunc_id_set_add(), btf_dtor_kfuncs_add() and
btf_struct_ops_register().
In is_valid_value_type(), look up bpf_struct_ops_common_value in the btf
the function was given rather than in the btf_vmlinux global. The id is
a vmlinux id and a module BTF resolves it through its base, so the result
is the same; the function already uses the passed btf for every other
lookup.
No functional change. With CONFIG_DEBUG_INFO_BTF=m, registrations made
from initcalls before the vmlinux BTF is available are queued and applied
later by the BTF parsing code, which needs the add-to-this-btf half on
its own; the struct_ops ones are applied before the parsed vmlinux BTF is
published, i.e. while btf_vmlinux is still NULL.
Signed-off-by: Jay Wang <wanjay@xxxxxxxxxx>
---
kernel/bpf/bpf_struct_ops.c | 3 +-
kernel/bpf/btf.c | 91 ++++++++++++++++++++++---------------
2 files changed, 57 insertions(+), 37 deletions(-)
diff --git a/kernel/bpf/bpf_struct_ops.c b/kernel/bpf/bpf_struct_ops.c
index 1178acd72296..bf3004908d15 100644
--- a/kernel/bpf/bpf_struct_ops.c
+++ b/kernel/bpf/bpf_struct_ops.c
@@ -103,7 +103,8 @@ static bool is_valid_value_type(struct btf *btf, s32 value_id,
}
member = btf_type_member(vt);
mt = btf_type_by_id(btf, member->type);
- common_value_type = btf_type_by_id(btf_vmlinux,
+ /* a vmlinux id resolves through the base BTF of a module BTF too */
+ common_value_type = btf_type_by_id(btf,
st_ops_ids[IDX_ST_OPS_COMMON_VALUE_ID]);
if (mt != common_value_type) {
pr_warn("The first member of %s should be bpf_struct_ops_common_value\n",
diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c
index a6634237dc89..ae28d23d678b 100644
--- a/kernel/bpf/btf.c
+++ b/kernel/bpf/btf.c
@@ -9313,11 +9313,26 @@ u32 *btf_kfunc_is_modify_return(const struct btf *btf, u32 kfunc_btf_id,
return btf_kfunc_id_set_contains(btf, BTF_KFUNC_HOOK_FMODRET, kfunc_btf_id);
}
+static int btf_kfunc_id_set_add(struct btf *btf, enum btf_kfunc_hook hook,
+ const struct btf_kfunc_id_set *kset)
+{
+ int ret, i;
+
+ for (i = 0; i < kset->set->cnt; i++) {
+ ret = btf_check_kfunc_protos(btf, btf_relocate_id(btf, kset->set->pairs[i].id),
+ kset->set->pairs[i].flags);
+ if (ret)
+ return ret;
+ }
+
+ return btf_populate_kfunc_set(btf, hook, kset);
+}
+
static int __register_btf_kfunc_id_set(enum btf_kfunc_hook hook,
const struct btf_kfunc_id_set *kset)
{
struct btf *btf;
- int ret, i;
+ int ret;
btf = btf_get_module_btf(kset->owner);
if (!btf)
@@ -9325,16 +9340,7 @@ static int __register_btf_kfunc_id_set(enum btf_kfunc_hook hook,
if (IS_ERR(btf))
return PTR_ERR(btf);
- for (i = 0; i < kset->set->cnt; i++) {
- ret = btf_check_kfunc_protos(btf, btf_relocate_id(btf, kset->set->pairs[i].id),
- kset->set->pairs[i].flags);
- if (ret)
- goto err_out;
- }
-
- ret = btf_populate_kfunc_set(btf, hook, kset);
-
-err_out:
+ ret = btf_kfunc_id_set_add(btf, hook, kset);
btf_put(btf);
return ret;
}
@@ -9426,21 +9432,13 @@ static int btf_check_dtor_kfuncs(struct btf *btf, const struct btf_id_dtor_kfunc
return 0;
}
-/* This function must be invoked only from initcalls/module init functions */
-int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt,
- struct module *owner)
+static int btf_dtor_kfuncs_add(struct btf *btf, const struct btf_id_dtor_kfunc *dtors,
+ u32 add_cnt)
{
struct btf_id_dtor_kfunc_tab *tab;
- struct btf *btf;
u32 tab_cnt, i;
int ret;
- btf = btf_get_module_btf(owner);
- if (!btf)
- return check_btf_kconfigs(owner, "dtor kfuncs");
- if (IS_ERR(btf))
- return PTR_ERR(btf);
-
if (add_cnt >= BTF_DTOR_KFUNC_MAX_CNT) {
pr_err("cannot register more than %d kfunc destructors\n", BTF_DTOR_KFUNC_MAX_CNT);
ret = -E2BIG;
@@ -9497,6 +9495,23 @@ int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_c
end:
if (ret)
btf_free_dtor_kfunc_tab(btf);
+ return ret;
+}
+
+/* This function must be invoked only from initcalls/module init functions */
+int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt,
+ struct module *owner)
+{
+ struct btf *btf;
+ int ret;
+
+ btf = btf_get_module_btf(owner);
+ if (!btf)
+ return check_btf_kconfigs(owner, "dtor kfuncs");
+ if (IS_ERR(btf))
+ return PTR_ERR(btf);
+
+ ret = btf_dtor_kfuncs_add(btf, dtors, add_cnt);
btf_put(btf);
return ret;
}
@@ -10135,32 +10150,36 @@ bpf_struct_ops_find(struct btf *btf, u32 type_id)
return NULL;
}
-int __register_bpf_struct_ops(struct bpf_struct_ops *st_ops)
+static int btf_struct_ops_register(struct btf *btf, struct bpf_struct_ops *st_ops)
{
struct bpf_verifier_log *log;
- struct btf *btf;
- int err = 0;
-
- btf = btf_get_module_btf(st_ops->owner);
- if (!btf)
- return check_btf_kconfigs(st_ops->owner, "struct_ops");
- if (IS_ERR(btf))
- return PTR_ERR(btf);
+ int err;
log = kzalloc_obj(*log, GFP_KERNEL | __GFP_NOWARN);
- if (!log) {
- err = -ENOMEM;
- goto errout;
- }
+ if (!log)
+ return -ENOMEM;
log->level = BPF_LOG_KERNEL;
err = btf_add_struct_ops(btf, st_ops, log);
-errout:
kfree(log);
- btf_put(btf);
+ return err;
+}
+int __register_bpf_struct_ops(struct bpf_struct_ops *st_ops)
+{
+ struct btf *btf;
+ int err;
+
+ btf = btf_get_module_btf(st_ops->owner);
+ if (!btf)
+ return check_btf_kconfigs(st_ops->owner, "struct_ops");
+ if (IS_ERR(btf))
+ return PTR_ERR(btf);
+
+ err = btf_struct_ops_register(btf, st_ops);
+ btf_put(btf);
return err;
}
EXPORT_SYMBOL_GPL(__register_bpf_struct_ops);
--
2.47.3