On Sat, Oct 31, 2026 at 06:09 AM Jay Wang <[email protected]> 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.

Reply via email to