From d434ab4266cdd46526a899cb49e3be242d5aaa77 Mon Sep 17 00:00:00 2001 From: Mitchell Augustin Date: Mon, 23 Sep 2024 17:07:43 -0500 Subject: [PATCH] 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. --- Cargo.lock | 99 ++++++++++++++++++++++++++++++++++++- rust/scx_loader/Cargo.toml | 1 + rust/scx_loader/src/main.rs | 72 +++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index cf2fe57..0b2d1fb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/rust/scx_loader/Cargo.toml b/rust/scx_loader/Cargo.toml index c675052..65276f5 100644 --- a/rust/scx_loader/Cargo.toml +++ b/rust/scx_loader/Cargo.toml @@ -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" diff --git a/rust/scx_loader/src/main.rs b/rust/scx_loader/src/main.rs index 837e833..68f1ee7 100644 --- a/rust/scx_loader/src/main.rs +++ b/rust/scx_loader/src/main.rs @@ -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, } +#[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 = None; + let mut cpu_above_threshold_since: Option = None; + let mut cpu_below_threshold_since: Option = 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::();