diff --git a/rust/scx_loader/Cargo.toml b/rust/scx_loader/Cargo.toml index c2867884a..9d0ad11b7 100644 --- a/rust/scx_loader/Cargo.toml +++ b/rust/scx_loader/Cargo.toml @@ -19,3 +19,10 @@ tokio = { version = "1.39", features = ["macros", "sync", "rt-multi-thread", "pr toml = "0.8.19" zbus = { version = "5", features = ["tokio"], default-features = false } zvariant = "5.1" + +[lib] +path = "src/lib.rs" + +[[bin]] +name = "scx_loader" +path = "src/main.rs" diff --git a/rust/scx_loader/src/config.rs b/rust/scx_loader/src/config.rs index 4dfb4f8f9..f6050417b 100644 --- a/rust/scx_loader/src/config.rs +++ b/rust/scx_loader/src/config.rs @@ -11,12 +11,12 @@ use std::path::Path; use anyhow::Result; use serde::Deserialize; +use serde::Serialize; -use crate::get_name_from_scx; use crate::SchedMode; use crate::SupportedSched; -#[derive(Debug, PartialEq, Default, Deserialize)] +#[derive(Debug, PartialEq, Default, Serialize, Deserialize)] #[serde(default)] pub struct Config { pub default_sched: Option, @@ -24,7 +24,7 @@ pub struct Config { pub scheds: HashMap, } -#[derive(Debug, PartialEq, Default, Deserialize)] +#[derive(Debug, PartialEq, Default, Serialize, Deserialize)] pub struct Sched { pub auto_mode: Option>, pub gaming_mode: Option>, @@ -102,7 +102,8 @@ pub fn get_scx_flags_for_mode( scx_sched: &SupportedSched, sched_mode: SchedMode, ) -> Vec { - if let Some(sched_config) = config.scheds.get(get_name_from_scx(scx_sched)) { + let scx_name: &str = scx_sched.into(); + if let Some(sched_config) = config.scheds.get(scx_name) { let scx_flags = extract_scx_flags_from_config(sched_config, &sched_mode); // try to exact flags from config, otherwise fallback to hardcoded default diff --git a/rust/scx_loader/src/dbus.rs b/rust/scx_loader/src/dbus.rs new file mode 100644 index 000000000..ed688bc4c --- /dev/null +++ b/rust/scx_loader/src/dbus.rs @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2024 Vladislav Nepogodin + +// This software may be used and distributed according to the terms of the +// GNU General Public License version 2. + +use crate::SchedMode; +use crate::SupportedSched; + +#[zbus::proxy( + interface = "org.scx.Loader", + default_service = "org.scx.Loader", + default_path = "/org/scx/Loader" +)] +pub trait LoaderClient { + /// Starts the specified scheduler with the given mode. + fn start_scheduler(&self, scx_name: SupportedSched, sched_mode: SchedMode) -> zbus::Result<()>; + + /// Starts the specified scheduler with the provided arguments. + fn start_scheduler_with_args( + &self, + scx_name: SupportedSched, + scx_args: &[String], + ) -> zbus::Result<()>; + + /// Stops the currently running scheduler. + fn stop_scheduler(&self) -> zbus::Result<()>; + + /// Method for switching to the specified scheduler with the given mode. + /// This method will stop the currently running scheduler (if any) and + /// then start the new scheduler. + fn switch_scheduler(&self, scx_name: SupportedSched, sched_mode: SchedMode) + -> zbus::Result<()>; + + /// Switches to the specified scheduler with the provided arguments. This + /// method will stop the currently running scheduler (if any) and then + /// start the new scheduler with the given arguments. + fn switch_scheduler_with_args( + &self, + scx_name: SupportedSched, + scx_args: &[String], + ) -> zbus::Result<()>; + + /// The name of the currently running scheduler. If no scheduler is active, + /// this property will be set to "unknown". + #[zbus(property)] + fn current_scheduler(&self) -> zbus::Result; + + /// The currently active scheduler mode. Scheduler modes allow you to + /// apply pre-defined configurations to a scheduler that are + /// optimized for different use cases. If no scheduler is active, + /// this property will be set to 0 (Auto). + #[zbus(property)] + fn scheduler_mode(&self) -> zbus::Result; + + /// A list of the schedulers currently supported by the Scheduler Loader. + /// The names of the supported schedulers will be listed as strings in + /// this array. + #[zbus(property)] + fn supported_schedulers(&self) -> zbus::Result>; +} diff --git a/rust/scx_loader/src/lib.rs b/rust/scx_loader/src/lib.rs new file mode 100644 index 000000000..e1bfff6f6 --- /dev/null +++ b/rust/scx_loader/src/lib.rs @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2024 Vladislav Nepogodin + +// This software may be used and distributed according to the terms of the +// GNU General Public License version 2. + +pub mod config; +pub mod dbus; + +use std::str::FromStr; + +use serde::Deserialize; +use serde::Serialize; +use zvariant::OwnedValue; +use zvariant::Type; +use zvariant::Value; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Type)] +#[zvariant(signature = "s")] +#[serde(rename_all = "lowercase")] +pub enum SupportedSched { + #[serde(rename = "scx_bpfland")] + Bpfland, + #[serde(rename = "scx_rusty")] + Rusty, + #[serde(rename = "scx_lavd")] + Lavd, + #[serde(rename = "scx_flash")] + Flash, +} + +#[derive(Debug, Clone, Deserialize, Serialize, Type, Value, OwnedValue, PartialEq)] +pub enum SchedMode { + /// Default values for the scheduler + Auto = 0, + /// Applies flags for better gaming experience + Gaming = 1, + /// Applies flags for lower power usage + PowerSave = 2, + /// Starts scheduler in low latency mode + LowLatency = 3, +} + +impl From<&SupportedSched> for &str { + fn from(scx_name: &SupportedSched) -> &'static str { + match scx_name { + SupportedSched::Bpfland => "scx_bpfland", + SupportedSched::Rusty => "scx_rusty", + SupportedSched::Lavd => "scx_lavd", + SupportedSched::Flash => "scx_flash", + } + } +} + +impl From for &str { + fn from(scx_name: SupportedSched) -> &'static str { + scx_name.into() + } +} + +impl FromStr for SupportedSched { + type Err = anyhow::Error; + + fn from_str(scx_name: &str) -> anyhow::Result { + match scx_name { + "scx_bpfland" => Ok(SupportedSched::Bpfland), + "scx_rusty" => Ok(SupportedSched::Rusty), + "scx_lavd" => Ok(SupportedSched::Lavd), + "scx_flash" => Ok(SupportedSched::Flash), + _ => Err(anyhow::anyhow!("{scx_name} is not supported")), + } + } +} diff --git a/rust/scx_loader/src/main.rs b/rust/scx_loader/src/main.rs index c04ad3927..07f915bac 100644 --- a/rust/scx_loader/src/main.rs +++ b/rust/scx_loader/src/main.rs @@ -5,9 +5,11 @@ // This software may be used and distributed according to the terms of the // GNU General Public License version 2. -mod config; mod logger; +use scx_loader::dbus::LoaderClientProxy; +use scx_loader::*; + use std::process::Stdio; use std::sync::atomic::AtomicU32; use std::sync::atomic::Ordering; @@ -16,8 +18,6 @@ use std::sync::Arc; use anyhow::Context; use anyhow::Result; use clap::Parser; -use serde::Deserialize; -use serde::Serialize; use sysinfo::System; use tokio::process::Child; use tokio::process::Command; @@ -27,21 +27,6 @@ use tokio::time::Duration; use tokio::time::Instant; use zbus::interface; use zbus::Connection; -use zvariant::Type; -use zvariant::Value; - -#[derive(Debug, Clone, PartialEq, Deserialize)] -#[serde(field_identifier, rename_all = "lowercase")] -enum SupportedSched { - #[serde(rename = "scx_bpfland")] - Bpfland, - #[serde(rename = "scx_rusty")] - Rusty, - #[serde(rename = "scx_lavd")] - Lavd, - #[serde(rename = "scx_flash")] - Flash, -} #[derive(Debug, PartialEq)] enum ScxMessage { @@ -69,18 +54,6 @@ enum RunnerMessage { Stop, } -#[derive(Debug, Clone, Deserialize, Serialize, Type, Value, PartialEq)] -enum SchedMode { - /// Default values for the scheduler - Auto = 0, - /// Applies flags for better gaming experience - Gaming = 1, - /// Applies flags for lower power usage - PowerSave = 2, - /// Starts scheduler in low latency mode - LowLatency = 3, -} - struct ScxLoader { current_scx: Option, current_mode: SchedMode, @@ -100,9 +73,9 @@ impl ScxLoader { #[zbus(property)] async fn current_scheduler(&self) -> String { if let Some(current_scx) = &self.current_scx { - let current_scx = get_name_from_scx(current_scx).into(); + let current_scx: &str = current_scx.into(); log::info!("called {current_scx:?}"); - return current_scx; + return current_scx.to_owned(); } "unknown".to_owned() } @@ -121,11 +94,9 @@ impl ScxLoader { async fn start_scheduler( &mut self, - scx_name: &str, + scx_name: SupportedSched, sched_mode: SchedMode, ) -> zbus::fdo::Result<()> { - let scx_name = get_scx_from_str(scx_name)?; - log::info!("starting {scx_name:?} with mode {sched_mode:?}.."); let _ = self.channel.send(ScxMessage::StartSched(( @@ -140,11 +111,9 @@ impl ScxLoader { async fn start_scheduler_with_args( &mut self, - scx_name: &str, + scx_name: SupportedSched, scx_args: Vec, ) -> zbus::fdo::Result<()> { - let scx_name = get_scx_from_str(scx_name)?; - log::info!("starting {scx_name:?} with args {scx_args:?}.."); let _ = self @@ -159,11 +128,9 @@ impl ScxLoader { async fn switch_scheduler( &mut self, - scx_name: &str, + scx_name: SupportedSched, sched_mode: SchedMode, ) -> zbus::fdo::Result<()> { - let scx_name = get_scx_from_str(scx_name)?; - log::info!("switching {scx_name:?} with mode {sched_mode:?}.."); let _ = self.channel.send(ScxMessage::SwitchSched(( @@ -178,11 +145,9 @@ impl ScxLoader { async fn switch_scheduler_with_args( &mut self, - scx_name: &str, + scx_name: SupportedSched, scx_args: Vec, ) -> zbus::fdo::Result<()> { - let scx_name = get_scx_from_str(scx_name)?; - log::info!("switching {scx_name:?} with args {scx_args:?}.."); let _ = self @@ -197,7 +162,7 @@ impl ScxLoader { async fn stop_scheduler(&mut self) -> zbus::fdo::Result<()> { if let Some(current_scx) = &self.current_scx { - let scx_name = get_name_from_scx(current_scx); + let scx_name: &str = current_scx.into(); log::info!("stopping {scx_name:?}.."); let _ = self.channel.send(ScxMessage::StopSched); @@ -208,18 +173,6 @@ impl ScxLoader { } } -#[zbus::proxy( - interface = "org.scx.Loader", - default_service = "org.scx.Loader", - default_path = "/org/scx/Loader" -)] -pub trait LoaderClient { - /// Method for switching to the specified scheduler with the given mode. - /// This method will stop the currently running scheduler (if any) and - /// then start the new scheduler. - fn switch_scheduler(&self, scx_name: &str, sched_mode: SchedMode) -> zbus::Result<()>; -} - // 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(); @@ -247,8 +200,10 @@ async fn monitor_cpu_util() -> Result<()> { if cpu_above_threshold_since.unwrap().elapsed() > high_utilization_trigger_duration { if running_sched.is_none() { log::info!("CPU Utilization exceeded 90% for 5 seconds, starting scx_lavd"); + + let scx_name: &str = SupportedSched::Lavd.into(); running_sched = Some( - Command::new(get_name_from_scx(&SupportedSched::Lavd)) + Command::new(scx_name) .spawn() .expect("Failed to start scx_lavd"), ); @@ -339,7 +294,7 @@ async fn main() -> Result<()> { let loader_client = LoaderClientProxy::new(&connection).await?; loader_client - .switch_scheduler(get_name_from_scx(default_sched), default_mode) + .switch_scheduler(default_sched.clone(), default_mode) .await?; } @@ -530,7 +485,7 @@ async fn spawn_scheduler( args: Vec, child_id: Arc, ) -> Result { - let sched_bin_name = get_name_from_scx(&scx_crate); + let sched_bin_name: &str = scx_crate.into(); log::info!("starting {sched_bin_name} command"); let mut cmd = Command::new(sched_bin_name); @@ -580,26 +535,3 @@ async fn stop_scheduler(child_id: Arc) -> Result<()> { Ok(()) } - -/// Get the scx trait from the given scx name or return error if the given scx name is not supported -fn get_scx_from_str(scx_name: &str) -> zbus::fdo::Result { - match scx_name { - "scx_bpfland" => Ok(SupportedSched::Bpfland), - "scx_rusty" => Ok(SupportedSched::Rusty), - "scx_lavd" => Ok(SupportedSched::Lavd), - "scx_flash" => Ok(SupportedSched::Flash), - _ => Err(zbus::fdo::Error::Failed(format!( - "{scx_name} is not supported" - ))), - } -} - -/// Get the scx name from the given scx trait -fn get_name_from_scx(supported_sched: &SupportedSched) -> &'static str { - match supported_sched { - SupportedSched::Bpfland => "scx_bpfland", - SupportedSched::Rusty => "scx_rusty", - SupportedSched::Lavd => "scx_lavd", - SupportedSched::Flash => "scx_flash", - } -}