scx_loader: Add initial automatic scheduler switching via --monitor-no-dbus

Exposes an option --monitor-no-dbus in scx_loader that will monitor CPU
utilization and start scx_lavd when any CPU exceeds 90% for more than 5
seconds. scx_lavd will be terminated if all CPUs are below 90% for
more than 30 seconds. When this flag is specified, scx_loader's
dbus functionality is not utilized.
This commit is contained in:
Mitchell Augustin 2024-09-23 17:07:43 -05:00
parent ad8536b4a4
commit d434ab4266
3 changed files with 171 additions and 1 deletions

99
Cargo.lock generated
View File

@ -953,7 +953,7 @@ dependencies = [
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows-core",
"windows-core 0.52.0",
]
[[package]]
@ -1247,6 +1247,15 @@ dependencies = [
"minimal-lexical",
]
[[package]]
name = "ntapi"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4"
dependencies = [
"winapi",
]
[[package]]
name = "num-conv"
version = "0.1.0"
@ -1546,6 +1555,26 @@ dependencies = [
"bitflags 2.6.0",
]
[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "redox_syscall"
version = "0.5.3"
@ -1714,6 +1743,7 @@ dependencies = [
"log",
"nix 0.29.0",
"serde",
"sysinfo",
"tokio",
"zbus",
"zvariant",
@ -2038,6 +2068,20 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "sysinfo"
version = "0.31.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "355dbe4f8799b304b05e1b0f05fc59b2a18d36645cf169607da45bde2f69a1be"
dependencies = [
"core-foundation-sys",
"libc",
"memchr",
"ntapi",
"rayon",
"windows",
]
[[package]]
name = "tap"
version = "1.0.1"
@ -2442,6 +2486,16 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
dependencies = [
"windows-core 0.57.0",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-core"
version = "0.52.0"
@ -2451,6 +2505,49 @@ dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-core"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
dependencies = [
"windows-implement",
"windows-interface",
"windows-result",
"windows-targets 0.52.6",
]
[[package]]
name = "windows-implement"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-result"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.48.0"

View File

@ -14,6 +14,7 @@ ctrlc = { version = "3.1", features = ["termination"] }
log = "0.4.17"
nix = { features = ["process", "signal"], default-features = false, version = "0.29" }
serde = { version = "1.0", features = ["derive"] }
sysinfo = "0.31.4"
tokio = { version = "1.39", features = ["macros", "sync", "rt-multi-thread", "process"] }
zbus = { version = "4", features = ["tokio"], default-features = false }
zvariant = "4.2"

View File

@ -23,6 +23,9 @@ use zbus::interface;
use zbus::Connection;
use zvariant::Type;
use zvariant::Value;
use clap::Parser;
use sysinfo::{System};
use std::{process::{Child}, time::{Duration, Instant}, thread};
#[derive(Debug, Clone, PartialEq)]
enum SupportedSched {
@ -63,6 +66,13 @@ struct ScxLoader {
channel: UnboundedSender<ScxMessage>,
}
#[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)]
struct Args {
#[clap(long, short, action)]
monitor_no_dbus: bool,
}
#[interface(name = "org.scx.Loader")]
impl ScxLoader {
/// Get currently running scheduler, in case non is running return "unknown"
@ -139,11 +149,73 @@ impl ScxLoader {
}
}
// Monitors CPU utilization and enables scx_lavd when utilization of any CPUs is > 90%
async fn monitor_cpu_util() -> Result<()> {
let mut system = System::new_all();
let mut running_sched: Option<Child> = None;
let mut cpu_above_threshold_since: Option<Instant> = None;
let mut cpu_below_threshold_since: Option<Instant> = None;
let high_utilization_threshold = 90.0;
let low_utilization_threshold_duration = Duration::from_secs(30);
let high_utilization_trigger_duration = Duration::from_secs(5);
loop {
system.refresh_cpu_all();
let any_cpu_above_threshold = system.cpus().iter().any(|cpu| cpu.cpu_usage() > high_utilization_threshold);
if any_cpu_above_threshold {
if cpu_above_threshold_since.is_none() {
cpu_above_threshold_since = Some(Instant::now());
}
if cpu_above_threshold_since.unwrap().elapsed() > high_utilization_trigger_duration {
if running_sched.is_none() {
println!("CPU Utilization exceeded 90% for 5 seconds, starting scx_lavd");
running_sched = Some(std::process::Command::new("scx_lavd").spawn().expect("Failed to start scx_lavd"));
}
cpu_below_threshold_since = None;
}
} else {
cpu_above_threshold_since = None;
if cpu_below_threshold_since.is_none() {
cpu_below_threshold_since = Some(Instant::now());
}
if cpu_below_threshold_since.unwrap().elapsed() > low_utilization_threshold_duration {
if let Some(mut running_sched_loc) = running_sched.take() {
println!("CPU utilization dropped below 90% for more than 30 seconds, exiting latency-aware scheduler");
running_sched_loc.kill().expect("Failed to kill scx_lavd");
let lavd_exit_status = running_sched_loc.wait().expect("Failed to wait on scx_lavd");
println!("scx_lavd exited with status: {}", lavd_exit_status);
}
}
}
thread::sleep(Duration::from_secs(1));
}
}
#[tokio::main]
async fn main() -> Result<()> {
// initialize the logger
logger::init_logger().expect("Failed to initialize logger");
let args = Args::parse();
// If --monitor_no_dbus is passed, start scx_loader as a standard background process
// that swaps schedulers out automatically
// based on CPU utilization without registering a dbus interface.
if args.monitor_no_dbus {
println!("Starting scx_loader monitor as standard process without dbus interface");
monitor_cpu_util().await?;
return Ok(());
}
println!("Starting as dbus interface");
// setup channel
let (channel, rx) = tokio::sync::mpsc::unbounded_channel::<ScxMessage>();