pub struct BpfBuilder {
clang: ClangInfo,
cflags: Vec<String>,
out_dir: PathBuf,
sources: BTreeSet<String>,
intf_input_output: Option<(String, String)>,
skel_input_name: Option<(String, String)>,
}
Expand description
§Build helpers for sched_ext schedulers with Rust userspace component
This is to be used from build.rs
of a cargo project which implements a
sched_ext scheduler with C BPF
component and Rust userspace component. BpfBuilder
provides everything
necessary to build the BPF component and generate Rust bindings.
BpfBuilder provides the followings.
vmlinux.h
and other common BPF header files
All sched_ext BPF implementations require vmlinux.h
and many make use
of common constructs such as
user_exit_info
.
BpfBuilder
makes these headers available when compiling BPF source
code and generating bindings for it. The included headers can be browsed
at https://github.com/sched-ext/scx/tree/main/scheds/include.
These headers can be superseded using environment variables which will be discussed later.
- Header bindings using
bindgen
If enabled with .enable_intf()
, the input .h
file is processed by
bindgen
to generate Rust bindings. This is useful in establishing
shared constants and data types between the BPF and user components.
Note that the types generated with bindgen
are different from the
types used by the BPF skeleton even when they are the same types in BPF.
This is a source of ugliness and we are hoping to address it by
improving libbpf-cargo
in the future.
- BPF compilation and generation of the skeleton and its bindings
If enabled with .enable_skel()
, the input .bpf.c
file is compiled
and its skeleton and bindings are generated using libbpf-cargo
.
§An Example
This section shows how BpfBuilder
can be used in an example project.
For a concrete example, take a look at
scx_rusty
.
A minimal source tree using all features would look like the following:
scx_hello_world
|-- Cargo.toml
|-- build.rs
\-- src
|-- main.rs
|-- bpf_intf.rs
|-- bpf_skel.rs
\-- bpf
|-- intf.h
\-- main.c
The following three files would contain the actual implementation:
-
src/main.rs
: Rust userspace component which loads the BPF blob and interacts it using the generated bindings. -
src/bpf/intf.h
: C header file definining constants and structs that will be used by both the BPF and userspace components. -
src/bpf/main.c
: C source code implementing the BPF component - includingstruct sched_ext_ops
.
And then there are boilerplates to generate the bindings and make them
available as modules to main.rs
.
-
Cargo.toml
: Includesscx_utils
in the[build-dependencies]
section. -
build.rs
: Usesscx_utils::BpfBuilder
to build and generate bindings for the BPF component. For this project, it can look like the following.
fn main() {
scx_utils::BpfBuilder::new()
.unwrap()
.enable_intf("src/bpf/intf.h", "bpf_intf.rs")
.enable_skel("src/bpf/main.bpf.c", "bpf")
.build()
.unwrap();
}
bpf_intf.rs
: Import the bindings generated bybindgen
into a module. Above, we told.enable_intf()
to generate the bindings intobpf_intf.rs
, so the file would look like the following. Theallow
directives are useful if the header is includingvmlinux.h
.
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]
include!(concat!(env!("OUT_DIR"), "/bpf_intf.rs"));
bpf_skel.rs
: Import the BPF skeleton bindings generated bylibbpf-cargo
into a module. Above, we told.enable_skel()
to use the skeleton namebpf
, so the file would look like the following.
include!(concat!(env!("OUT_DIR"), "/bpf_skel.rs"));
§Compiler Flags and Environment Variables
BPF being its own CPU architecture and independent runtime environment,
build environment and steps are already rather complex. The need to
interface between two different languages - C and Rust - adds further
complexities. BpfBuilder
automates most of the process. The determined
build environment is recorded in the build.rs
output and can be
obtained with a command like the following:
$ grep '^scx_utils:clang=' target/release/build/scx_rusty-*/output
While the automatic settings should work most of the time, there can be times when overriding them is necessary. The following environment variables can be used to customize the build environment.
-
BPF_CLANG
: The clang command to use. (Default:clang
) -
BPF_CFLAGS
: Compiler flags to use when building BPF source code. If specified, the flags from this variable are the only flags passed to the compiler.BpfBuilder
won’t generate any flags including-I
flags for the common header files and otherCFLAGS
related variables are ignored. -
BPF_BASE_CFLAGS
: Override the non-include part of cflags. -
BPF_EXTRA_CFLAGS_PRE_INCL
: Add cflags before the automic include search path options. Header files in the search paths added by this variable will supercede the automatic ones. -
BPF_EXTRA_CFLAGS_POST_INCL
: Add cflags after the automic include search path options. Header paths added by this variable will be searched only if the target header file can’t be found in the automatic header paths. -
RUSTFLAGS
: This is a genericcargo
flag and can be useful for specifying extra linker flags.
A common case for using the above flags is using the latest libbpf
from the kernel tree. Let’s say the kernel tree is at $KERNEL
and
libbpf
. The following builds libbpf
shipped with the kernel:
$ cd $KERNEL
$ make -C tools/bpf/bpftool
To link the scheduler against the resulting libbpf
:
$ env BPF_EXTRA_CFLAGS_POST_INCL=$KERNEL/tools/bpf/bpftool/libbpf/include \
RUSTFLAGS="-C link-args=-lelf -C link-args=-lz -C link-args=-lzstd \
-L$KERNEL/tools/bpf/bpftool/libbpf" cargo build --release
Fields§
§clang: ClangInfo
§cflags: Vec<String>
§out_dir: PathBuf
§sources: BTreeSet<String>
§intf_input_output: Option<(String, String)>
§skel_input_name: Option<(String, String)>