mirror of
https://github.com/JakeHillion/scx.git
synced 2024-11-27 20:00:23 +00:00
rust: introduce scx_rustland_core crate
Introduce a separate crate (scx_rustland_core) that can be used to implement sched-ext schedulers in Rust that run in user-space. This commit only provides the basic layout for the new crate and the abstraction to the custom allocator. In general, any scheduler that has a user-space component needs to use the custom allocator to prevent potential deadlock conditions, caused by page faults (a kthread needs to run to resolve the page fault, but the scheduler is blocked waiting for the user-space page fault to be resolved => deadlock). However, we don't want to necessarily enforce this constraint to all the existing Rust schedulers, some of them may do all user-space allocations in safe paths, hence the separate scx_rustland_core crate. Merging this code in scx_utils would force all the Rust schedulers to use the custom allocator. In a future commit the scx_rustland backend will be moved to scx_rustland_core, making it a totally generic BPF scheduler framework that can be used to implement user-space schedulers in Rust. Signed-off-by: Andrea Righi <andrea.righi@canonical.com>
This commit is contained in:
parent
4dfb898a08
commit
416d6a940f
@ -1 +1,2 @@
|
|||||||
subdir('scx_utils')
|
subdir('scx_utils')
|
||||||
|
subdir('scx_rustland_core')
|
||||||
|
1
rust/scx_rustland_core/.gitignore
vendored
Normal file
1
rust/scx_rustland_core/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
Cargo.lock
|
32
rust/scx_rustland_core/Cargo.toml
Normal file
32
rust/scx_rustland_core/Cargo.toml
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
[package]
|
||||||
|
name = "scx_rustland_core"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Andrea Righi <andrea.righi@canonical.com>"]
|
||||||
|
license = "GPL-2.0-only"
|
||||||
|
repository = "https://github.com/sched-ext/scx"
|
||||||
|
description = "Framework to implement sched_ext schedulers running in user space"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0"
|
||||||
|
bitvec = { version = "1.0", features = ["serde"] }
|
||||||
|
# FIXME - We need to allow both 0.68 and 0.69 to accommodate fedora. See the
|
||||||
|
# comment in BpfBuilder::bindgen_bpf_intf() for details.
|
||||||
|
bindgen = ">=0.68, <0.70"
|
||||||
|
glob = "0.3"
|
||||||
|
hex = "0.4.3"
|
||||||
|
lazy_static = "1.4"
|
||||||
|
libbpf-cargo = "0.22"
|
||||||
|
libbpf-rs = "0.22.0"
|
||||||
|
libc = "0.2.137"
|
||||||
|
buddy-alloc = "0.5.1"
|
||||||
|
log = "0.4.17"
|
||||||
|
regex = "1.10"
|
||||||
|
sscanf = "0.4"
|
||||||
|
tar = "0.4"
|
||||||
|
version-compare = "0.1"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
bindgen = ">=0.68, <0.70"
|
||||||
|
tar = "0.4"
|
||||||
|
walkdir = "2.4"
|
1
rust/scx_rustland_core/LICENSE
Symbolic link
1
rust/scx_rustland_core/LICENSE
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../LICENSE
|
8
rust/scx_rustland_core/README.md
Normal file
8
rust/scx_rustland_core/README.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Framework to implement sched_ext schedulers running in user-space
|
||||||
|
|
||||||
|
[sched_ext](https://github.com/sched-ext/scx) is a Linux kernel feature
|
||||||
|
which enables implementing kernel thread schedulers in BPF and dynamically
|
||||||
|
loading them.
|
||||||
|
|
||||||
|
Thie crate provides a generic framework that allows to implement sched_ext
|
||||||
|
schedulers that are running in user-space.
|
1
rust/scx_rustland_core/bindings.h
Normal file
1
rust/scx_rustland_core/bindings.h
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "bpf_h/vmlinux/vmlinux.h"
|
1
rust/scx_rustland_core/bpf_h
Symbolic link
1
rust/scx_rustland_core/bpf_h
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../../scheds/include
|
53
rust/scx_rustland_core/build.rs
Normal file
53
rust/scx_rustland_core/build.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
//
|
||||||
|
// This software may be used and distributed according to the terms of the
|
||||||
|
// GNU General Public License version 2.
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::fs::File;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
const BPF_H: &str = "bpf_h";
|
||||||
|
|
||||||
|
fn gen_bpf_h() {
|
||||||
|
let file =
|
||||||
|
File::create(PathBuf::from(env::var("OUT_DIR").unwrap()).join(format!("{}.tar", BPF_H)))
|
||||||
|
.unwrap();
|
||||||
|
let mut ar = tar::Builder::new(file);
|
||||||
|
|
||||||
|
ar.follow_symlinks(false);
|
||||||
|
ar.append_dir_all(".", BPF_H).unwrap();
|
||||||
|
ar.finish().unwrap();
|
||||||
|
|
||||||
|
for ent in walkdir::WalkDir::new(BPF_H) {
|
||||||
|
let ent = ent.unwrap();
|
||||||
|
if !ent.file_type().is_dir() {
|
||||||
|
println!("cargo:rerun-if-changed={}", ent.path().to_string_lossy());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_bindings() {
|
||||||
|
// FIXME - bindgen's API changed between 0.68 and 0.69 so that
|
||||||
|
// `bindgen::CargoCallbacks::new()` should be used instead of
|
||||||
|
// `bindgen::CargoCallbacks`. Unfortunately, as of Dec 2023, fedora is
|
||||||
|
// shipping 0.68. To accommodate fedora, allow both 0.68 and 0.69 of
|
||||||
|
// bindgen and suppress deprecation warning. Remove the following once
|
||||||
|
// fedora can be updated to bindgen >= 0.69.
|
||||||
|
#[allow(deprecated)]
|
||||||
|
let bindings = bindgen::Builder::default()
|
||||||
|
.header("bindings.h")
|
||||||
|
.allowlist_type("scx_exit_kind")
|
||||||
|
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
|
||||||
|
.generate()
|
||||||
|
.expect("Unable to generate bindings");
|
||||||
|
|
||||||
|
bindings
|
||||||
|
.write_to_file(PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs"))
|
||||||
|
.expect("Couldn't write bindings");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
gen_bpf_h();
|
||||||
|
gen_bindings();
|
||||||
|
}
|
7
rust/scx_rustland_core/meson.build
Normal file
7
rust/scx_rustland_core/meson.build
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
custom_target('scx_rustland_core',
|
||||||
|
output: '@PLAINNAME@.__PHONY__',
|
||||||
|
input: 'Cargo.toml',
|
||||||
|
command: [cargo, 'build', '--manifest-path=@INPUT@', '--target-dir=@OUTDIR@',
|
||||||
|
cargo_build_args],
|
||||||
|
env: cargo_env,
|
||||||
|
build_by_default: true)
|
@ -15,7 +15,7 @@ const HEAP_SIZE: usize = 64 * 1024 * 1024; // 64M
|
|||||||
const LEAF_SIZE: usize = 64;
|
const LEAF_SIZE: usize = 64;
|
||||||
|
|
||||||
#[repr(align(4096))]
|
#[repr(align(4096))]
|
||||||
pub struct AlignedHeap<const N: usize>([u8; N]);
|
struct AlignedHeap<const N: usize>([u8; N]);
|
||||||
|
|
||||||
// Statically pre-allocated memory arena.
|
// Statically pre-allocated memory arena.
|
||||||
static mut FAST_HEAP: AlignedHeap<FAST_HEAP_SIZE> = AlignedHeap([0u8; FAST_HEAP_SIZE]);
|
static mut FAST_HEAP: AlignedHeap<FAST_HEAP_SIZE> = AlignedHeap([0u8; FAST_HEAP_SIZE]);
|
||||||
@ -26,25 +26,25 @@ static mut HEAP: AlignedHeap<HEAP_SIZE> = AlignedHeap([0u8; HEAP_SIZE]);
|
|||||||
// To prevent potential deadlock conditions under heavy loads, any scheduler that delegates
|
// To prevent potential deadlock conditions under heavy loads, any scheduler that delegates
|
||||||
// scheduling decisions to user-space should avoid triggering page faults.
|
// scheduling decisions to user-space should avoid triggering page faults.
|
||||||
//
|
//
|
||||||
// To address this issue, replace the global allocator with a custom one (RustLandAllocator),
|
// To address this issue, replace the global allocator with a custom one (UserAllocator),
|
||||||
// designed to operate on a pre-allocated buffer. This, coupled with the memory locking achieved
|
// designed to operate on a pre-allocated buffer. This, coupled with the memory locking achieved
|
||||||
// through mlockall(), prevents page faults from occurring during the execution of the user-space
|
// through mlockall(), prevents page faults from occurring during the execution of the user-space
|
||||||
// scheduler.
|
// scheduler.
|
||||||
#[cfg_attr(not(test), global_allocator)]
|
#[cfg_attr(not(test), global_allocator)]
|
||||||
pub static ALLOCATOR: RustLandAllocator = unsafe {
|
pub static ALLOCATOR: UserAllocator = unsafe {
|
||||||
let fast_param = FastAllocParam::new(FAST_HEAP.0.as_ptr(), FAST_HEAP_SIZE);
|
let fast_param = FastAllocParam::new(FAST_HEAP.0.as_ptr(), FAST_HEAP_SIZE);
|
||||||
let buddy_param = BuddyAllocParam::new(HEAP.0.as_ptr(), HEAP_SIZE, LEAF_SIZE);
|
let buddy_param = BuddyAllocParam::new(HEAP.0.as_ptr(), HEAP_SIZE, LEAF_SIZE);
|
||||||
RustLandAllocator {
|
UserAllocator {
|
||||||
arena: NonThreadsafeAlloc::new(fast_param, buddy_param),
|
arena: NonThreadsafeAlloc::new(fast_param, buddy_param),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Main allocator class.
|
// Main allocator class.
|
||||||
pub struct RustLandAllocator {
|
pub struct UserAllocator {
|
||||||
pub arena: NonThreadsafeAlloc,
|
arena: NonThreadsafeAlloc,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RustLandAllocator {
|
impl UserAllocator {
|
||||||
pub fn lock_memory(&self) {
|
pub fn lock_memory(&self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
VM.save();
|
VM.save();
|
||||||
@ -75,7 +75,7 @@ impl RustLandAllocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Override global allocator methods.
|
// Override global allocator methods.
|
||||||
unsafe impl GlobalAlloc for RustLandAllocator {
|
unsafe impl GlobalAlloc for UserAllocator {
|
||||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
self.arena.alloc(layout)
|
self.arena.alloc(layout)
|
||||||
}
|
}
|
6
rust/scx_rustland_core/src/bindings.rs
Normal file
6
rust/scx_rustland_core/src/bindings.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
4
rust/scx_rustland_core/src/lib.rs
Normal file
4
rust/scx_rustland_core/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
mod bindings;
|
||||||
|
|
||||||
|
mod alloc;
|
||||||
|
pub use alloc::ALLOCATOR;
|
@ -18,6 +18,7 @@ hex = "0.4.3"
|
|||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
libbpf-cargo = "0.22"
|
libbpf-cargo = "0.22"
|
||||||
libbpf-rs = "0.22.0"
|
libbpf-rs = "0.22.0"
|
||||||
|
buddy-alloc = "0.5.1"
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
regex = "1.10"
|
regex = "1.10"
|
||||||
sscanf = "0.4"
|
sscanf = "0.4"
|
||||||
|
@ -15,10 +15,10 @@ fb_procfs = "0.7.0"
|
|||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
libbpf-rs = "0.22.0"
|
libbpf-rs = "0.22.0"
|
||||||
libc = "0.2.137"
|
libc = "0.2.137"
|
||||||
buddy-alloc = "0.5.1"
|
|
||||||
log = "0.4.17"
|
log = "0.4.17"
|
||||||
ordered-float = "3.4.0"
|
ordered-float = "3.4.0"
|
||||||
scx_utils = { path = "../../../rust/scx_utils", version = "0.6" }
|
scx_utils = { path = "../../../rust/scx_utils", version = "0.6" }
|
||||||
|
scx_rustland_core = { path = "../../../rust/scx_rustland_core", version = "0.1" }
|
||||||
simplelog = "0.12.0"
|
simplelog = "0.12.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
@ -15,13 +15,12 @@ use libbpf_rs::skel::SkelBuilder as _;
|
|||||||
|
|
||||||
use libc::{sched_param, sched_setscheduler};
|
use libc::{sched_param, sched_setscheduler};
|
||||||
|
|
||||||
mod alloc;
|
|
||||||
use alloc::*;
|
|
||||||
|
|
||||||
use scx_utils::init_libbpf_logging;
|
use scx_utils::init_libbpf_logging;
|
||||||
use scx_utils::uei_exited;
|
use scx_utils::uei_exited;
|
||||||
use scx_utils::uei_report;
|
use scx_utils::uei_report;
|
||||||
|
|
||||||
|
use scx_rustland_core::ALLOCATOR;
|
||||||
|
|
||||||
// Defined in UAPI
|
// Defined in UAPI
|
||||||
const SCHED_EXT: i32 = 7;
|
const SCHED_EXT: i32 = 7;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user