From 01c86eeafef7e6fc4462a82692c80dc679dcf2bb Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Sat, 12 Nov 2022 23:17:13 -0500 Subject: [PATCH] Add some additional APIs endpoints for #48 and more --- backend/src/api/async_utils.rs | 29 +++--- backend/src/api/cpu.rs | 36 +++++++- backend/src/api/handler.rs | 33 ++++--- backend/src/main.rs | 4 + backend/src/settings/cpu.rs | 154 ++++++++++++++++++++++++++------ backend/src/settings/general.rs | 49 +++------- backend/src/settings/mod.rs | 2 +- backend/src/state/cpu.rs | 2 + src/backend.ts | 8 ++ 9 files changed, 224 insertions(+), 93 deletions(-) diff --git a/backend/src/api/async_utils.rs b/backend/src/api/async_utils.rs index 689ac9b..7afdaa6 100644 --- a/backend/src/api/async_utils.rs +++ b/backend/src/api/async_utils.rs @@ -1,33 +1,38 @@ //use usdpl_back::core::serdes::Primitive; use usdpl_back::AsyncCallable; -/*pub struct AsyncIsh Result) + Send + Sync, - SG: (Fn(T) -> T) + Send + Sync + 'static, - TG: (Fn(T) -> super::ApiParameterType) + Send + Sync> { +pub struct AsyncIsh Result) + Send + Sync, + Gen: (Fn() -> SG) + Send + Sync, + SG: (Fn(In) -> Out) + Send + Sync + 'static, + TG: (Fn(Out) -> super::ApiParameterType) + Send + Sync> { pub trans_setter: TS, // assumed to be pretty fast - pub set_get: SG, // probably has locks (i.e. slow) + pub set_get: Gen, // probably has locks (i.e. slow) pub trans_getter: TG, // assumed to be pretty fast } #[async_trait::async_trait] -impl Result) + Send + Sync, - SG: (Fn(T) -> T) + Send + Sync + 'static, - TG: (Fn(T) -> super::ApiParameterType) + Send + Sync> - AsyncCallable for AsyncIsh { +impl Result) + Send + Sync, + Gen: (Fn() -> SG) + Send + Sync, + SG: (Fn(In) -> Out) + Send + Sync + 'static, + TG: (Fn(Out) -> super::ApiParameterType) + Send + Sync> + AsyncCallable for AsyncIsh { async fn call(&self, params: super::ApiParameterType) -> super::ApiParameterType { let t_to_set = match (self.trans_setter)(params) { Ok(t) => t, Err(e) => return vec![e.into()] }; - let t_got = match tokio::task::spawn_blocking(|| (self.set_get)(t_to_set)).await { + let setter = (self.set_get)(); + let t_got = match tokio::task::spawn_blocking(move || setter(t_to_set)).await { Ok(t) => t, Err(e) => return vec![e.to_string().into()], }; (self.trans_getter)(t_got) } -}*/ +} pub struct AsyncIshGetter G) + Send + Sync, diff --git a/backend/src/api/cpu.rs b/backend/src/api/cpu.rs index 0e55c4c..c4c1922 100644 --- a/backend/src/api/cpu.rs +++ b/backend/src/api/cpu.rs @@ -3,14 +3,14 @@ use std::sync::{Arc, Mutex}; use usdpl_back::core::serdes::Primitive; use usdpl_back::AsyncCallable; -use crate::settings::{Cpu, SettingError, SettingVariant, MinMax}; +use crate::settings::{Cpus, SettingError, SettingVariant, MinMax}; //use crate::utility::{unwrap_lock, unwrap_maybe_fatal}; use super::handler::{ApiMessage, CpuMessage}; /// Available CPUs web method pub fn max_cpus(_: super::ApiParameterType) -> super::ApiParameterType { super::utility::map_result( - Cpu::cpu_count() + Cpus::cpu_count() .map(|x| x as u64) .ok_or_else( || SettingError { @@ -90,6 +90,38 @@ pub fn set_cpus_online( } }*/ +pub fn set_smt( + sender: Sender, +) -> impl AsyncCallable { + let sender = Arc::new(Mutex::new(sender)); // Sender is not Sync; this is required for safety + let getter = move || { + let sender2 = sender.clone(); + move |smt: bool| { + let (tx, rx) = mpsc::channel(); + let callback = move |values: Vec| tx.send(values).expect("set_smt callback send failed"); + sender2.lock().unwrap().send(ApiMessage::Cpu(CpuMessage::SetSmt(smt, Box::new(callback)))).expect("set_smt send failed"); + rx.recv().expect("set_smt callback recv failed") + } + }; + super::async_utils::AsyncIsh { + trans_setter: |params| { + if let Some(&Primitive::Bool(smt_value)) = params.get(0) { + Ok(smt_value) + } else { + Err("set_smt missing/invalid parameter 0".to_owned()) + } + }, + set_get: getter, + trans_getter: |result| { + let mut output = Vec::with_capacity(result.len()); + for &status in result.as_slice() { + output.push(status.into()); + } + output + } + } +} + pub fn get_cpus_online( sender: Sender, ) -> impl AsyncCallable { diff --git a/backend/src/api/handler.rs b/backend/src/api/handler.rs index 46d6bd2..3ceef2e 100644 --- a/backend/src/api/handler.rs +++ b/backend/src/api/handler.rs @@ -1,6 +1,6 @@ use std::sync::mpsc::{self, Receiver, Sender}; -use crate::settings::{Settings, Cpu, Gpu, Battery, General, OnSet, OnResume, MinMax}; +use crate::settings::{Settings, Cpus, Gpu, Battery, General, OnSet, OnResume, MinMax}; use crate::persist::SettingsJson; use crate::utility::unwrap_maybe_fatal; @@ -35,6 +35,7 @@ impl BatteryMessage { pub enum CpuMessage { SetCpuOnline(usize, bool), SetCpusOnline(Vec), + SetSmt(bool, Callback>), GetCpusOnline(Callback>), SetClockLimits(usize, Option>), GetClockLimits(usize, Callback>>), @@ -44,32 +45,40 @@ pub enum CpuMessage { } impl CpuMessage { - fn process(self, settings: &mut Vec) { + fn process(self, settings: &mut Cpus) { match self { - Self::SetCpuOnline(index, status) => {settings.get_mut(index).map(|c| c.online = status);}, + Self::SetCpuOnline(index, status) => {settings.cpus.get_mut(index).map(|c| c.online = status);}, Self::SetCpusOnline(cpus) => { for i in 0..cpus.len() { - settings.get_mut(i).map(|c| c.online = cpus[i]); + settings.cpus.get_mut(i).map(|c| c.online = cpus[i]); } }, + Self::SetSmt(status, cb) => { + let mut result = Vec::with_capacity(settings.cpus.len()); + for i in 0..settings.cpus.len() { + settings.cpus[i].online = settings.cpus[i].online && (status || i % 2 == 0); + result.push(settings.cpus[i].online); + } + cb(result); + } Self::GetCpusOnline(cb) => { - let mut result = Vec::with_capacity(settings.len()); - for cpu in settings { + let mut result = Vec::with_capacity(settings.cpus.len()); + for cpu in &settings.cpus { result.push(cpu.online); } cb(result); }, - Self::SetClockLimits(index, clocks) => {settings.get_mut(index).map(|c| c.clock_limits = clocks);}, - Self::GetClockLimits(index, cb) => {settings.get(index).map(|c| cb(c.clock_limits.clone()));}, - Self::SetCpuGovernor(index, gov) => {settings.get_mut(index).map(|c| c.governor = gov);}, + Self::SetClockLimits(index, clocks) => {settings.cpus.get_mut(index).map(|c| c.clock_limits = clocks);}, + Self::GetClockLimits(index, cb) => {settings.cpus.get(index).map(|c| cb(c.clock_limits.clone()));}, + Self::SetCpuGovernor(index, gov) => {settings.cpus.get_mut(index).map(|c| c.governor = gov);}, Self::SetCpusGovernor(govs) => { for i in 0..govs.len() { - settings.get_mut(i).map(|c| c.governor = govs[i].clone()); + settings.cpus.get_mut(i).map(|c| c.governor = govs[i].clone()); } }, Self::GetCpusGovernor(cb) => { - let mut result = Vec::with_capacity(settings.len()); - for cpu in settings { + let mut result = Vec::with_capacity(settings.cpus.len()); + for cpu in &settings.cpus { result.push(cpu.governor.clone()); } cb(result); diff --git a/backend/src/main.rs b/backend/src/main.rs index ed9586f..aa53e00 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -94,6 +94,10 @@ fn main() -> Result<(), ()> { "CPU_get_onlines", api::cpu::get_cpus_online(api_sender.clone()) ) + .register_async( + "CPU_set_smt", + api::cpu::set_smt(api_sender.clone()) + ) .register( "CPU_set_clock_limits", api::cpu::set_clock_limits(api_sender.clone()) diff --git a/backend/src/settings/cpu.rs b/backend/src/settings/cpu.rs index dd27ab7..b2d1efe 100644 --- a/backend/src/settings/cpu.rs +++ b/backend/src/settings/cpu.rs @@ -4,6 +4,131 @@ use super::MinMax; use super::{OnResume, OnSet, SettingError, SettingsRange}; use crate::persist::CpuJson; +const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present"; +const CPU_SMT_PATH: &str = "/sys/devices/system/cpu/smt/control"; + +#[derive(Debug, Clone)] +pub struct Cpus { + pub cpus: Vec, + pub smt: bool, + pub smt_capable: bool, +} + +impl OnSet for Cpus { + fn on_set(&mut self) -> Result<(), SettingError> { + if self.smt_capable { + // toggle SMT + if self.smt { + usdpl_back::api::files::write_single(CPU_SMT_PATH, "on").map_err(|e| { + SettingError { + msg: format!( + "Failed to write `on` to `{}`: {}", + CPU_SMT_PATH, e + ), + setting: super::SettingVariant::Cpu, + } + })?; + } else { + usdpl_back::api::files::write_single(CPU_SMT_PATH, "off").map_err(|e| { + SettingError { + msg: format!( + "Failed to write `off` to `{}`: {}", + CPU_SMT_PATH, e + ), + setting: super::SettingVariant::Cpu, + } + })?; + } + } + for (i, cpu) in self.cpus.as_mut_slice().iter_mut().enumerate() { + cpu.state.do_set_online = self.smt || i % 2 == 0; + cpu.on_set()?; + } + Ok(()) + } +} + +impl OnResume for Cpus { + fn on_resume(&self) -> Result<(), SettingError> { + for cpu in &self.cpus { + cpu.on_resume()?; + } + Ok(()) + } +} + +impl Cpus { + pub fn cpu_count() -> Option { + let mut data: String = usdpl_back::api::files::read_single(CPU_PRESENT_PATH) + .unwrap_or_else(|_| "0-7".to_string() /* Steam Deck's default */); + if let Some(dash_index) = data.find('-') { + let data = data.split_off(dash_index + 1); + if let Ok(max_cpu) = data.parse::() { + return Some(max_cpu + 1); + } + } + log::warn!("Failed to parse CPU info from kernel, is Tux evil?"); + None + } + + fn system_smt_capabilities() -> (bool, bool) { + match usdpl_back::api::files::read_single::<_, String, _>(CPU_SMT_PATH) { + Ok(val) => (val.trim().to_lowercase() == "on", true), + Err(_) => (false, false) + } + } + + pub fn system_default() -> Self { + if let Some(max_cpu) = Self::cpu_count() { + let mut sys_cpus = Vec::with_capacity(max_cpu); + for i in 0..max_cpu { + sys_cpus.push(Cpu::from_sys(i)); + } + let (smt_status, can_smt) = Self::system_smt_capabilities(); + Self { + cpus: sys_cpus, + smt: smt_status, + smt_capable: can_smt, + } + } else { + Self { + cpus: vec![], + smt: false, + smt_capable: false, + } + } + } + + #[inline] + pub fn from_json(mut other: Vec, version: u64) -> Self { + let (_, can_smt) = Self::system_smt_capabilities(); + let mut result = Vec::with_capacity(other.len()); + let max_cpus = Self::cpu_count(); + for (i, cpu) in other.drain(..).enumerate() { + // prevent having more CPUs than available + if let Some(max_cpus) = max_cpus { + if i == max_cpus { + break; + } + } + result.push(Cpu::from_json(cpu, version, i)); + } + if let Some(max_cpus) = max_cpus { + if result.len() != max_cpus { + let mut sys_cpus = Cpus::system_default(); + for i in result.len()..sys_cpus.cpus.len() { + result.push(sys_cpus.cpus.remove(i)); + } + } + } + Self { + cpus: result, + smt: true, + smt_capable: can_smt, + } + } +} + #[derive(Debug, Clone)] pub struct Cpu { pub online: bool, @@ -16,8 +141,6 @@ pub struct Cpu { const CPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage"; const CPU_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level"; -const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present"; - impl Cpu { #[inline] pub fn from_json(other: CpuJson, version: u64, i: usize) -> Self { @@ -41,7 +164,7 @@ impl Cpu { fn set_all(&mut self) -> Result<(), SettingError> { // set cpu online/offline - if self.index != 0 { // cpu0 cannot be disabled + if self.index != 0 && self.state.do_set_online { // cpu0 cannot be disabled let online_path = cpu_online_path(self.index); usdpl_back::api::files::write_single(&online_path, self.online as u8).map_err(|e| { SettingError { @@ -162,31 +285,6 @@ impl Cpu { state: crate::state::Cpu::default(), } } - - pub fn cpu_count() -> Option { - let mut data: String = usdpl_back::api::files::read_single(CPU_PRESENT_PATH) - .unwrap_or_else(|_| "0-7".to_string() /* Steam Deck's default */); - if let Some(dash_index) = data.find('-') { - let data = data.split_off(dash_index + 1); - if let Ok(max_cpu) = data.parse::() { - return Some(max_cpu + 1); - } - } - log::warn!("Failed to parse CPU info from kernel, is Tux evil?"); - None - } - - pub fn system_default() -> Vec { - if let Some(max_cpu) = Self::cpu_count() { - let mut cpus = Vec::with_capacity(max_cpu); - for i in 0..max_cpu { - cpus.push(Self::from_sys(i)); - } - cpus - } else { - Vec::with_capacity(0) - } - } } impl Into for Cpu { diff --git a/backend/src/settings/general.rs b/backend/src/settings/general.rs index bb3ff17..fb95f52 100644 --- a/backend/src/settings/general.rs +++ b/backend/src/settings/general.rs @@ -2,9 +2,9 @@ use std::convert::Into; use std::path::PathBuf; //use std::sync::{Arc, Mutex}; -use super::{Battery, Cpu, Gpu}; +use super::{Battery, Cpus, Gpu}; use super::{OnResume, OnSet, SettingError}; -use crate::persist::{CpuJson, SettingsJson}; +use crate::persist::SettingsJson; //use crate::utility::unwrap_lock; const LATEST_VERSION: u64 = 0; @@ -44,7 +44,7 @@ impl OnSet for General { #[derive(Debug, Clone)] pub struct Settings { pub general: General, - pub cpus: Vec, + pub cpus: Cpus, pub gpu: Gpu, pub battery: Battery, } @@ -52,9 +52,7 @@ pub struct Settings { impl OnSet for Settings { fn on_set(&mut self) -> Result<(), SettingError> { self.battery.on_set()?; - for cpu in self.cpus.iter_mut() { - cpu.on_set()?; - } + self.cpus.on_set()?; self.gpu.on_set()?; self.general.on_set()?; Ok(()) @@ -71,7 +69,7 @@ impl Settings { path: json_path, name: other.name, }, - cpus: Self::convert_cpus(other.cpus, other.version), + cpus: Cpus::from_json(other.cpus, other.version), gpu: Gpu::from_json(other.gpu, other.version), battery: Battery::from_json(other.battery, other.version), }, @@ -81,36 +79,13 @@ impl Settings { path: json_path, name: other.name, }, - cpus: Self::convert_cpus(other.cpus, other.version), + cpus: Cpus::from_json(other.cpus, other.version), gpu: Gpu::from_json(other.gpu, other.version), battery: Battery::from_json(other.battery, other.version), }, } } - fn convert_cpus(mut cpus: Vec, version: u64) -> Vec { - let mut result = Vec::with_capacity(cpus.len()); - let max_cpus = Cpu::cpu_count(); - for (i, cpu) in cpus.drain(..).enumerate() { - // prevent having more CPUs than available - if let Some(max_cpus) = max_cpus { - if i == max_cpus { - break; - } - } - result.push(Cpu::from_json(cpu, version, i)); - } - if let Some(max_cpus) = max_cpus { - if result.len() != max_cpus { - let mut sys_cpus = Cpu::system_default(); - for i in result.len()..sys_cpus.len() { - result.push(sys_cpus.remove(i)); - } - } - } - result - } - pub fn system_default(json_path: PathBuf) -> Self { Self { general: General { @@ -118,14 +93,14 @@ impl Settings { path: json_path, name: crate::consts::DEFAULT_SETTINGS_NAME.to_owned(), }, - cpus: Cpu::system_default(), + cpus: Cpus::system_default(), gpu: Gpu::system_default(), battery: Battery::system_default(), } } pub fn load_system_default(&mut self) { - self.cpus = Cpu::system_default(); + self.cpus = Cpus::system_default(); self.gpu = Gpu::system_default(); self.battery = Battery::system_default(); } @@ -143,7 +118,7 @@ impl Settings { self.general.persistent = false; self.general.name = name; } else { - self.cpus = Self::convert_cpus(settings_json.cpus, settings_json.version); + self.cpus = Cpus::from_json(settings_json.cpus, settings_json.version); self.gpu = Gpu::from_json(settings_json.gpu, settings_json.version); self.battery = Battery::from_json(settings_json.battery, settings_json.version); self.general.persistent = true; @@ -166,9 +141,7 @@ impl OnResume for Settings { log::debug!("Applying settings for on_resume"); self.battery.on_resume()?; log::debug!("Resumed battery"); - for cpu in self.cpus.iter() { - cpu.on_resume()?; - } + self.cpus.on_resume()?; log::debug!("Resumed CPUs"); self.gpu.on_resume()?; log::debug!("Resumed GPU"); @@ -184,7 +157,7 @@ impl Into for Settings { version: LATEST_VERSION, name: self.general.name.clone(), persistent: self.general.persistent, - cpus: self.cpus + cpus: self.cpus.cpus .clone() .drain(..) .map(|cpu| cpu.into()) diff --git a/backend/src/settings/mod.rs b/backend/src/settings/mod.rs index 9f327af..2cef68c 100644 --- a/backend/src/settings/mod.rs +++ b/backend/src/settings/mod.rs @@ -7,7 +7,7 @@ mod min_max; mod traits; pub use battery::Battery; -pub use cpu::Cpu; +pub use cpu::{Cpu, Cpus}; pub use general::{SettingVariant, Settings, General}; pub use gpu::Gpu; pub use min_max::MinMax; diff --git a/backend/src/state/cpu.rs b/backend/src/state/cpu.rs index e122115..54b423f 100644 --- a/backend/src/state/cpu.rs +++ b/backend/src/state/cpu.rs @@ -2,6 +2,7 @@ pub struct Cpu { pub clock_limits_set: bool, pub is_resuming: bool, + pub do_set_online: bool, } impl std::default::Default for Cpu { @@ -9,6 +10,7 @@ impl std::default::Default for Cpu { Self { clock_limits_set: false, is_resuming: false, + do_set_online: true, } } } diff --git a/src/backend.ts b/src/backend.ts index 29d53b8..e2c5746 100644 --- a/src/backend.ts +++ b/src/backend.ts @@ -62,6 +62,10 @@ export async function unsetBatteryChargeRate(): Promise { // CPU +export async function setCpuSmt(status: boolean): Promise { + return (await call_backend("CPU_set_smt", [status]))[0]; +} + export async function getCpuCount(): Promise { return (await call_backend("CPU_count", []))[0]; } @@ -150,6 +154,10 @@ export async function loadGeneralDefaultSettings(): Promise { return (await call_backend("GENERAL_load_default_settings", []))[0]; } +export async function loadGeneralSystemSettings(): Promise { + return (await call_backend("GENERAL_load_system_settings", []))[0]; +} + export async function getGeneralSettingsName(): Promise { return (await call_backend("GENERAL_get_name", []))[0]; }