Struct scx_utils::BpfBuilder

source ·
pub struct BpfBuilder { /* private fields */ }
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.

  1. 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.

  1. 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.

  1. 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 - including struct sched_ext_ops.

And then there are boilerplates to generate the bindings and make them available as modules to main.rs.

  • Cargo.toml: Includes scx_utils in the [build-dependencies] section.

  • build.rs: Uses scx_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 by bindgen into a module. Above, we told .enable_intf() to generate the bindings into bpf_intf.rs, so the file would look like the following. The allow directives are useful if the header is including vmlinux.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 by libbpf-cargo into a module. Above, we told .enable_skel() to use the skeleton name bpf, 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 other CFLAGS 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 generic cargo 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

Implementations§

source§

impl BpfBuilder

source

pub fn vmlinux_h_ver_sha1() -> (String, String)

Return (VER, SHA1) from which the bulit-in vmlinux.h is generated.

source

pub fn new() -> Result<Self>

Create a new BpfBuilder struct. Call enable and set methods to configure and build method to compile and generate bindings. See the struct documentation for details.

source

pub fn enable_intf(&mut self, input: &str, output: &str) -> &mut Self

Enable generation of header bindings using bindgen. @input is the .h file defining the constants and types to be shared between BPF and Rust components. @output is the .rs file to be generated.

source

pub fn enable_skel(&mut self, input: &str, name: &str) -> &mut Self

Enable compilation of BPF code and generation of the skeleton and its Rust bindings. @input is the .bpf.c file containing the BPF source code and @output is the .rs file to be generated.

source

pub fn set_skel_deps<'a, I>(&mut self, deps: I) -> &mut Self
where I: IntoIterator<Item = &'a str>,

By default, all .[hc] files in the same directory as the source BPF .c file are treated as dependencies and the skeleton is regenerated if any has changed. This method replaces the automatic dependencies with @deps.

source

pub fn build(&self) -> Result<()>

Build and generate the enabled bindings.

Trait Implementations§

source§

impl Debug for BpfBuilder

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> Conv for T

source§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
source§

impl<T> FmtForward for T

source§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
source§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
source§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
source§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
source§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
source§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
source§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
source§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
source§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> IntoEither for T

source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
source§

impl<T> Pipe for T
where T: ?Sized,

source§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
source§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
source§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
source§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
source§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
source§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
source§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
source§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
source§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
source§

impl<T> Tap for T

source§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
source§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
source§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
source§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
source§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
source§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
source§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
source§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
source§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
source§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
source§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
source§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
source§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
source§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
source§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
source§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
source§

impl<T> TryConv for T

source§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.