Re: [PATCH bpf-next 10/13] bpf: defer kfunc and struct_ops registrations until first BTF use
From: Alexei Starovoitov
Date: Sat Oct 31 2026 - 13:07:16 EST
On Sat, Oct 31, 2026 at 06:09 AM Jay Wang <wanjay@xxxxxxxxxx> wrote:
> The first use can run out of memory, also only because its task is
> being killed: vmalloc() gives up then. A round that fails with
> -ENOMEM, in the parse or in a replayed registration, is not published:
> the new BTF is dropped with whatever was applied to it, the queue stays
> as it is, and the next caller starts over (a struct_ops ->init() then
> runs again with the new BTF; the in-tree ones only look types up or
> store the BTF pointer, which nothing can use before the BTF is
> published).
can we do the first use from a work item ?
A kworker has no signals and is never an OOM victim.
Then -ENOMEM is as unlikely as in an initcall today, and an error can
be final, like without the option.
workqueue_init() runs before the first initcall, so it works at boot.
No need for the retry, for btf_unparse_kept_modules() in patch 11 and
for ->init() that runs twice.
Something like this (not even compile tested):
static void btf_vmlinux_first_use(struct work_struct *work)
{
struct btf *btf;
guard(mutex)(&btf_vmlinux_lock);
if (btf_vmlinux)
return;
btf = btf_parse_vmlinux();
if (IS_ERR(btf)) {
btf_drop_all_deferred(PTR_ERR(btf));
} else {
btf_vmlinux_new = btf;
WRITE_ONCE(btf_vmlinux_builder, current);
btf_apply_deferred();
WRITE_ONCE(btf_vmlinux_builder, NULL);
btf_vmlinux_new = NULL;
}
smp_store_release(&btf_vmlinux, btf);
}
static DECLARE_WORK(btf_vmlinux_work, btf_vmlinux_first_use);
struct btf *bpf_get_btf_vmlinux(void)
{
struct btf *btf = smp_load_acquire(&btf_vmlinux);
if (btf || !IS_ENABLED(CONFIG_DEBUG_INFO_BTF))
return btf;
if (btf_vmlinux_building())
return btf_vmlinux_new;
queue_work(system_dfl_wq, &btf_vmlinux_work);
flush_work(&btf_vmlinux_work);
return smp_load_acquire(&btf_vmlinux);
}
The undo has a hole already.
bpf_io_init() and bm_bpf_init() don't only look types up. They register
kfuncs. And bpf_struct_ops_desc_init() turns every ->init() error into
-EINVAL:
btf_apply_deferred()
__register_bpf_struct_ops(&bpf_ring_ops)
bpf_struct_ops_desc_init()
bpf_io_init()
register_btf_kfunc_id_set()
btf_populate_kfunc_set() krealloc() fails
btf_free_kfunc_set_tab() frees all vmlinux kfunc sets
returns -ENOMEM
returns -EINVAL
pr_warn(), next entry
The BTF is published. The kfunc sets that were applied before this
entry are gone until reboot.