BPF List
 help / color / mirror / Atom feed
From: Andrey Grodzovsky <andrey.grodzovsky@crowdstrike.com>
To: <bpf@vger.kernel.org>, <andrii@kernel.org>
Cc: <ast@kernel.org>, <martin.kelly@crowdstrike.com>,
	<slava.imameev@crowdstrike.com>,
	<linux-open-source@crowdstrike.com>
Subject: [PATCH bpf-next v4 0/7] libbpf: BPF program manual loading
Date: Mon, 21 Sep 2026 18:39:30 -0400	[thread overview]
Message-ID: <20260921223937.3203093-1-andrey.grodzovsky@crowdstrike.com> (raw)

This series was originally posted in January 2025 [1] as a two-patch RFC
introducing a per-program tri-state load strategy (disabled/auto/dynamic)
to let large BPF applications load a subset of their programs lazily,
after the initial bpf_object load. This v4 addresses all outstanding review
feedback from v3's two CI bots, folds bpf_program__load_manually()/
unload_manually() into a single load()/unload() naming pair, and adds
a new patch that properly versions bpf_program__set_autoattach()'s
ABI change instead of leaving it as a silent break for out-of-tree
consumers (see changelog below).


Motivation:

Security tools built on top of libbpf commonly ship as a single large
BPF object containing many programs, only a subset of which are needed
on any given system -- the rest are gated on optional features or on
kernel/runtime capabilities that vary across deployments. For this class
of application, per-program manual loading helps in two ways:

  - it shortens the initial load, since only the programs actually
    needed for the running configuration are loaded and attached up
    front instead of the whole object; and
  - it lets a single failing program be unloaded and possibly reloaded
    (or a feature toggled at runtime) without tearing down and reloading
    the entire bpf_object.

Both reduce the time window during which the tool is partially loaded or
inactive -- for a security tool specifically, that is also the time
window during which the system it protects is unprotected.

We have been running this with our internal fork of libbpf in production
for about a year. Depending on kernel and configuration support, around
250 of our programs are potentially loadable on any given system; with
manual loading, only around 90 of those are actually loaded by default,
growing to the full 250 only when every optional feature is enabled.
Cutting the default set of loaded programs from 250 down to 90 measurably
reduces load time (we observed ~60% reduction in our own measurements),
and since unload time scales with the number of loaded programs, it
correspondingly shortens unload time as well -- which matters, since
slow unload can delay system shutdown. We believe this functionality
would benefit other libbpf consumers with similarly large, modular BPF
applications.

Patch overview:

 1/7: replace bpf_program's boolean autoload field with an enum
      (bpf_prog_load_strategy: DISABLED/AUTO), so a third state can be
      added later without an ABI break

 2/7: add BPF_PROG_LOAD_STRATEGY_MANUAL and bpf_program__load()/
      bpf_program__unload(), letting a program be loaded and attached
      independently of the bulk bpf_object load/attach pass, and
      reloaded/reattached multiple times; unload() is now a single,
      MANUAL-aware verb instead of a separate load()/load_manually()
      and unload()/unload_manually() pair (see changelog)

 3/7: let a program mark itself MANUAL from its own section name via a
      SEC("!...") prefix, instead of requiring an imperative
      bpf_program__set_load_strategy() call, mirroring the existing
      SEC("?...") convention

 4/7: reject bpf_object__gen_loader() for an object with a MANUAL
      program, which would otherwise silently corrupt every later
      program's generated prog_fd slot

 5/7: version bpf_program__set_autoattach()'s void->int return-type and
      behavior change via ELF symbol versioning (COMPAT_VERSION()/
      DEFAULT_VERSION()), modeled on the xsk_umem__create and
      bpf_prog_load precedents, so binaries already linked against the
      old void-returning ABI keep that behavior instead of silently
      hitting the new MANUAL-rejection logic underneath them

 6/7: selftest covering every load-strategy transition
      (DISABLED/AUTO/MANUAL), bpf_program__set_autoload()'s bool
      compatibility, and autoattach restoration -- now including a case
      that discriminates the restore logic from a hard-coded `true`

 7/7: selftest covering the manual load/attach/detach/reload cycle, the
      declarative SEC("!...") marker, prepare()-only load sufficiency,
      a module BTF deferred load, and gen_loader rejection -- updated
      for the merged load()/unload() API and with all four scenarios
      now individually subtested

Changes since v3 [2]:
  - Log the raw error via errstr(err) instead of a bare %d in
    bpf_program__load()'s failure message (sashiko-bot)
  - Let MANUAL programs use the caller-supplied kernel log buffer
    instead of always falling back to libbpf's own realloc'd buffer
    (bot+bpf-ci)
  - Reject BPF_PROG_LOAD_STRATEGY_MANUAL for struct_ops programs:
    bpf_map_prepare_vdata() bakes every member's fd into kern_vdata
    during bpf_object__load(), before a MANUAL member could ever be
    loaded, and nothing re-bakes it afterwards (bot+bpf-ci)
  - Fix bpf_link leaks on every assertion-failure path in load_type.c
    and dynamicload.c (sashiko-bot, bot+bpf-ci)
  - Wrap all 4 dynamicload.c scenarios in test__start_subtest(), so
    bpf_testmod's absence only skips the one scenario that needs it
    instead of masking the other three as SKIP (bot+bpf-ci)
  - Drop the dead !obj->fd_array_cnt disjunct in
    bpf_object_post_load_cleanup()'s fd_array free guard (Andrii)
  - Rename bpf_program__load_manually() to bpf_program__load(), and
    fold bpf_program__unload_manually() into bpf_program__unload():
    split the existing full-free body into an internal
    bpf_program_unload_full() helper used unconditionally by
    object-teardown paths and make the public unload() skip the helper for
    manual programs (Andrii)
  - New: version bpf_program__set_autoattach()'s void->int ABI change
    via COMPAT_VERSION()/DEFAULT_VERSION() instead of leaving the
    symbol under its original LIBBPF_1.0.0 node -- an already-linked
    binary now keeps the old unconditional-set behavior at runtime
    instead of silently gaining the new MANUAL-rejection behavior
    underneath it after a libbpf upgrade (bot+bpf-ci)
  - Misc. cosmetic fixes pointed out by the bots (Sashiko + bpf-ci)

[1] https://lore.kernel.org/bpf/20250122215206.59859-1-slava.imameev@crowdstrike.com/
[2] https://lore.kernel.org/bpf/20260917203040.3150212-1-andrey.grodzovsky@crowdstrike.com/

Andrey Grodzovsky (4):
  libbpf: Support declarative manual load via SEC("!...") prefix
  libbpf: Reject gen_loader for objects with already-manual programs
  libbpf: Version bpf_program__set_autoattach() ABI change
  selftests/bpf: Cover BPF program manual loading

Slava Imameev (3):
  libbpf: BPF program load strategy enum
  libbpf: BPF programs manual loading and attaching
  selftests/bpf: Cover BPF program load strategy transitions

 tools/lib/bpf/libbpf.c                        | 278 ++++++++++---
 tools/lib/bpf/libbpf.h                        |  69 +++-
 tools/lib/bpf/libbpf.map                      |   5 +
 tools/lib/bpf/libbpf_common.h                 |   6 +
 .../selftests/bpf/prog_tests/dynamicload.c    | 365 ++++++++++++++++++
 .../selftests/bpf/prog_tests/load_type.c      | 186 +++++++++
 .../selftests/bpf/prog_tests/signed_loader.c  |  28 ++
 .../selftests/bpf/progs/test_dynamicload.c    |  54 +++
 .../selftests/bpf/progs/test_load_type.c      |  31 ++
 9 files changed, 976 insertions(+), 46 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/dynamicload.c
 create mode 100644 tools/testing/selftests/bpf/prog_tests/load_type.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_dynamicload.c
 create mode 100644 tools/testing/selftests/bpf/progs/test_load_type.c

-- 
2.34.1


             reply	other threads:[~2026-09-21 22:39 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-21 22:39 Andrey Grodzovsky [this message]
2026-09-21 22:39 ` [PATCH bpf-next v4 1/7] libbpf: BPF program load strategy enum Andrey Grodzovsky
2026-09-21 23:21   ` bot+bpf-ci
2026-09-21 22:39 ` [PATCH bpf-next v4 2/7] libbpf: BPF programs manual loading and attaching Andrey Grodzovsky
2026-09-21 23:03   ` sashiko-bot
2026-09-22 23:51     ` Andrey Grodzovsky
2026-09-21 22:39 ` [PATCH bpf-next v4 3/7] libbpf: Support declarative manual load via SEC("!...") prefix Andrey Grodzovsky
2026-09-21 23:12   ` sashiko-bot
2026-09-21 23:21   ` bot+bpf-ci
2026-09-21 22:39 ` [PATCH bpf-next v4 4/7] libbpf: Reject gen_loader for objects with already-manual programs Andrey Grodzovsky
2026-09-21 22:39 ` [PATCH bpf-next v4 5/7] libbpf: Version bpf_program__set_autoattach() ABI change Andrey Grodzovsky
2026-09-21 23:31   ` sashiko-bot
2026-09-21 22:39 ` [PATCH bpf-next v4 6/7] selftests/bpf: Cover BPF program load strategy transitions Andrey Grodzovsky
2026-09-21 23:37   ` sashiko-bot
2026-09-21 22:39 ` [PATCH bpf-next v4 7/7] selftests/bpf: Cover BPF program manual loading Andrey Grodzovsky
2026-09-22  1:37 ` [PATCH bpf-next v4 0/7] libbpf: " Alexei Starovoitov
2026-09-22 14:34   ` Andrey Grodzovsky

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260921223937.3203093-1-andrey.grodzovsky@crowdstrike.com \
    --to=andrey.grodzovsky@crowdstrike.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=linux-open-source@crowdstrike.com \
    --cc=martin.kelly@crowdstrike.com \
    --cc=slava.imameev@crowdstrike.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox