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 following.
vmlinux.hand 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.cThe 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_cargoin the[build-dependencies]section. -
build.rs: Usesscx_cargo::BpfBuilderto build and generate bindings for the BPF component. For this project, it can look like the following.
fn main() {
scx_cargo::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 bybindgeninto a module. Above, we told.enable_intf()to generate the bindings intobpf_intf.rs, so the file would look like the following. Theallowdirectives 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-cargointo 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-*/outputWhile 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.BpfBuilderwon’t generate any flags including-Iflags for the common header files and otherCFLAGSrelated 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 genericcargoflag 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/bpftoolTo 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 --releaseFields§
§clang: ClangInfo§cflags: Vec<String>§out_dir: PathBuf§sources: BTreeSet<String>§intf_input_output: Option<(String, String)>§skel_input_name: Option<(String, String)>