237 lines
11 KiB
ReStructuredText
237 lines
11 KiB
ReStructuredText
.. SPDX-License-Identifier: GPL-2.0
|
|
|
|
===============
|
|
libbpf Overview
|
|
===============
|
|
|
|
libbpf is a C-based library containing a BPF loader that takes compiled BPF
|
|
object files and prepares and loads them into the Linux kernel. libbpf takes the
|
|
heavy lifting of loading, verifying, and attaching BPF programs to various
|
|
kernel hooks, allowing BPF application developers to focus only on BPF program
|
|
correctness and performance.
|
|
|
|
The following are the high-level features supported by libbpf:
|
|
|
|
* Provides high-level and low-level APIs for user space programs to interact
|
|
with BPF programs. The low-level APIs wrap all the bpf system call
|
|
functionality, which is useful when users need more fine-grained control
|
|
over the interactions between user space and BPF programs.
|
|
* Provides overall support for the BPF object skeleton generated by bpftool.
|
|
The skeleton file simplifies the process for the user space programs to access
|
|
global variables and work with BPF programs.
|
|
* Provides BPF-side APIS, including BPF helper definitions, BPF maps support,
|
|
and tracing helpers, allowing developers to simplify BPF code writing.
|
|
* Supports BPF CO-RE mechanism, enabling BPF developers to write portable
|
|
BPF programs that can be compiled once and run across different kernel
|
|
versions.
|
|
|
|
This document will delve into the above concepts in detail, providing a deeper
|
|
understanding of the capabilities and advantages of libbpf and how it can help
|
|
you develop BPF applications efficiently.
|
|
|
|
BPF App Lifecycle and libbpf APIs
|
|
==================================
|
|
|
|
A BPF application consists of one or more BPF programs (either cooperating or
|
|
completely independent), BPF maps, and global variables. The global
|
|
variables are shared between all BPF programs, which allows them to cooperate on
|
|
a common set of data. libbpf provides APIs that user space programs can use to
|
|
manipulate the BPF programs by triggering different phases of a BPF application
|
|
lifecycle.
|
|
|
|
The following section provides a brief overview of each phase in the BPF life
|
|
cycle:
|
|
|
|
* **Open phase**: In this phase, libbpf parses the BPF
|
|
object file and discovers BPF maps, BPF programs, and global variables. After
|
|
a BPF app is opened, user space apps can make additional adjustments
|
|
(setting BPF program types, if necessary; pre-setting initial values for
|
|
global variables, etc.) before all the entities are created and loaded.
|
|
|
|
* **Load phase**: In the load phase, libbpf creates BPF
|
|
maps, resolves various relocations, and verifies and loads BPF programs into
|
|
the kernel. At this point, libbpf validates all the parts of a BPF application
|
|
and loads the BPF program into the kernel, but no BPF program has yet been
|
|
executed. After the load phase, it’s possible to set up the initial BPF map
|
|
state without racing with the BPF program code execution.
|
|
|
|
* **Attachment phase**: In this phase, libbpf
|
|
attaches BPF programs to various BPF hook points (e.g., tracepoints, kprobes,
|
|
cgroup hooks, network packet processing pipeline, etc.). During this
|
|
phase, BPF programs perform useful work such as processing
|
|
packets, or updating BPF maps and global variables that can be read from user
|
|
space.
|
|
|
|
* **Tear down phase**: In the tear down phase,
|
|
libbpf detaches BPF programs and unloads them from the kernel. BPF maps are
|
|
destroyed, and all the resources used by the BPF app are freed.
|
|
|
|
BPF Object Skeleton File
|
|
========================
|
|
|
|
BPF skeleton is an alternative interface to libbpf APIs for working with BPF
|
|
objects. Skeleton code abstract away generic libbpf APIs to significantly
|
|
simplify code for manipulating BPF programs from user space. Skeleton code
|
|
includes a bytecode representation of the BPF object file, simplifying the
|
|
process of distributing your BPF code. With BPF bytecode embedded, there are no
|
|
extra files to deploy along with your application binary.
|
|
|
|
You can generate the skeleton header file ``(.skel.h)`` for a specific object
|
|
file by passing the BPF object to the bpftool. The generated BPF skeleton
|
|
provides the following custom functions that correspond to the BPF lifecycle,
|
|
each of them prefixed with the specific object name:
|
|
|
|
* ``<name>__open()`` – creates and opens BPF application (``<name>`` stands for
|
|
the specific bpf object name)
|
|
* ``<name>__load()`` – instantiates, loads,and verifies BPF application parts
|
|
* ``<name>__attach()`` – attaches all auto-attachable BPF programs (it’s
|
|
optional, you can have more control by using libbpf APIs directly)
|
|
* ``<name>__destroy()`` – detaches all BPF programs and
|
|
frees up all used resources
|
|
|
|
Using the skeleton code is the recommended way to work with bpf programs. Keep
|
|
in mind, BPF skeleton provides access to the underlying BPF object, so whatever
|
|
was possible to do with generic libbpf APIs is still possible even when the BPF
|
|
skeleton is used. It's an additive convenience feature, with no syscalls, and no
|
|
cumbersome code.
|
|
|
|
Other Advantages of Using Skeleton File
|
|
---------------------------------------
|
|
|
|
* BPF skeleton provides an interface for user space programs to work with BPF
|
|
global variables. The skeleton code memory maps global variables as a struct
|
|
into user space. The struct interface allows user space programs to initialize
|
|
BPF programs before the BPF load phase and fetch and update data from user
|
|
space afterward.
|
|
|
|
* The ``skel.h`` file reflects the object file structure by listing out the
|
|
available maps, programs, etc. BPF skeleton provides direct access to all the
|
|
BPF maps and BPF programs as struct fields. This eliminates the need for
|
|
string-based lookups with ``bpf_object_find_map_by_name()`` and
|
|
``bpf_object_find_program_by_name()`` APIs, reducing errors due to BPF source
|
|
code and user-space code getting out of sync.
|
|
|
|
* The embedded bytecode representation of the object file ensures that the
|
|
skeleton and the BPF object file are always in sync.
|
|
|
|
BPF Helpers
|
|
===========
|
|
|
|
libbpf provides BPF-side APIs that BPF programs can use to interact with the
|
|
system. The BPF helpers definition allows developers to use them in BPF code as
|
|
any other plain C function. For example, there are helper functions to print
|
|
debugging messages, get the time since the system was booted, interact with BPF
|
|
maps, manipulate network packets, etc.
|
|
|
|
For a complete description of what the helpers do, the arguments they take, and
|
|
the return value, see the `bpf-helpers
|
|
<https://man7.org/linux/man-pages/man7/bpf-helpers.7.html>`_ man page.
|
|
|
|
BPF CO-RE (Compile Once – Run Everywhere)
|
|
=========================================
|
|
|
|
BPF programs work in the kernel space and have access to kernel memory and data
|
|
structures. One limitation that BPF applications come across is the lack of
|
|
portability across different kernel versions and configurations. `BCC
|
|
<https://github.com/iovisor/bcc/>`_ is one of the solutions for BPF
|
|
portability. However, it comes with runtime overhead and a large binary size
|
|
from embedding the compiler with the application.
|
|
|
|
libbpf steps up the BPF program portability by supporting the BPF CO-RE concept.
|
|
BPF CO-RE brings together BTF type information, libbpf, and the compiler to
|
|
produce a single executable binary that you can run on multiple kernel versions
|
|
and configurations.
|
|
|
|
To make BPF programs portable libbpf relies on the BTF type information of the
|
|
running kernel. Kernel also exposes this self-describing authoritative BTF
|
|
information through ``sysfs`` at ``/sys/kernel/btf/vmlinux``.
|
|
|
|
You can generate the BTF information for the running kernel with the following
|
|
command:
|
|
|
|
::
|
|
|
|
$ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
|
|
|
|
The command generates a ``vmlinux.h`` header file with all kernel types
|
|
(:doc:`BTF types <../btf>`) that the running kernel uses. Including
|
|
``vmlinux.h`` in your BPF program eliminates dependency on system-wide kernel
|
|
headers.
|
|
|
|
libbpf enables portability of BPF programs by looking at the BPF program’s
|
|
recorded BTF type and relocation information and matching them to BTF
|
|
information (vmlinux) provided by the running kernel. libbpf then resolves and
|
|
matches all the types and fields, and updates necessary offsets and other
|
|
relocatable data to ensure that BPF program’s logic functions correctly for a
|
|
specific kernel on the host. BPF CO-RE concept thus eliminates overhead
|
|
associated with BPF development and allows developers to write portable BPF
|
|
applications without modifications and runtime source code compilation on the
|
|
target machine.
|
|
|
|
The following code snippet shows how to read the parent field of a kernel
|
|
``task_struct`` using BPF CO-RE and libbf. The basic helper to read a field in a
|
|
CO-RE relocatable manner is ``bpf_core_read(dst, sz, src)``, which will read
|
|
``sz`` bytes from the field referenced by ``src`` into the memory pointed to by
|
|
``dst``.
|
|
|
|
.. code-block:: C
|
|
:emphasize-lines: 6
|
|
|
|
//...
|
|
struct task_struct *task = (void *)bpf_get_current_task();
|
|
struct task_struct *parent_task;
|
|
int err;
|
|
|
|
err = bpf_core_read(&parent_task, sizeof(void *), &task->parent);
|
|
if (err) {
|
|
/* handle error */
|
|
}
|
|
|
|
/* parent_task contains the value of task->parent pointer */
|
|
|
|
In the code snippet, we first get a pointer to the current ``task_struct`` using
|
|
``bpf_get_current_task()``. We then use ``bpf_core_read()`` to read the parent
|
|
field of task struct into the ``parent_task`` variable. ``bpf_core_read()`` is
|
|
just like ``bpf_probe_read_kernel()`` BPF helper, except it records information
|
|
about the field that should be relocated on the target kernel. i.e, if the
|
|
``parent`` field gets shifted to a different offset within
|
|
``struct task_struct`` due to some new field added in front of it, libbpf will
|
|
automatically adjust the actual offset to the proper value.
|
|
|
|
Getting Started with libbpf
|
|
===========================
|
|
|
|
Check out the `libbpf-bootstrap <https://github.com/libbpf/libbpf-bootstrap>`_
|
|
repository with simple examples of using libbpf to build various BPF
|
|
applications.
|
|
|
|
See also `libbpf API documentation
|
|
<https://libbpf.readthedocs.io/en/latest/api.html>`_.
|
|
|
|
libbpf and Rust
|
|
===============
|
|
|
|
If you are building BPF applications in Rust, it is recommended to use the
|
|
`Libbpf-rs <https://github.com/libbpf/libbpf-rs>`_ library instead of bindgen
|
|
bindings directly to libbpf. Libbpf-rs wraps libbpf functionality in
|
|
Rust-idiomatic interfaces and provides libbpf-cargo plugin to handle BPF code
|
|
compilation and skeleton generation. Using Libbpf-rs will make building user
|
|
space part of the BPF application easier. Note that the BPF program themselves
|
|
must still be written in plain C.
|
|
|
|
libbpf logging
|
|
==============
|
|
|
|
By default, libbpf logs informational and warning messages to stderr. The
|
|
verbosity of these messages can be controlled by setting the environment
|
|
variable LIBBPF_LOG_LEVEL to either warn, info, or debug. A custom log
|
|
callback can be set using ``libbpf_set_print()``.
|
|
|
|
Additional Documentation
|
|
========================
|
|
|
|
* `Program types and ELF Sections <https://libbpf.readthedocs.io/en/latest/program_types.html>`_
|
|
* `API naming convention <https://libbpf.readthedocs.io/en/latest/libbpf_naming_convention.html>`_
|
|
* `Building libbpf <https://libbpf.readthedocs.io/en/latest/libbpf_build.html>`_
|
|
* `API documentation Convention <https://libbpf.readthedocs.io/en/latest/libbpf_naming_convention.html#api-documentation-convention>`_
|