From 27afa31520d6dd45bd5ccf10ec0ab254bdb29368 Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Mon, 27 Mar 2023 20:24:21 -0400 Subject: [PATCH] Only apply manual power_dpm_force_performance_level when actually necessary #87 --- backend/src/settings/steam_deck/cpu.rs | 103 +++++++-------- backend/src/settings/steam_deck/gpu.rs | 117 +++++++++++------- backend/src/settings/steam_deck/mod.rs | 2 + .../settings/steam_deck/power_dpm_force.rs | 107 ++++++++++++++++ 4 files changed, 234 insertions(+), 95 deletions(-) create mode 100644 backend/src/settings/steam_deck/power_dpm_force.rs diff --git a/backend/src/settings/steam_deck/cpu.rs b/backend/src/settings/steam_deck/cpu.rs index 2f76790..df1ae45 100644 --- a/backend/src/settings/steam_deck/cpu.rs +++ b/backend/src/settings/steam_deck/cpu.rs @@ -6,6 +6,7 @@ use crate::settings::{OnResume, OnSet, SettingError}; use crate::settings::{TCpus, TCpu}; use crate::persist::CpuJson; use super::oc_limits::{OverclockLimits, CpusLimits, CpuLimits}; +use super::POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT; const CPU_PRESENT_PATH: &str = "/sys/devices/system/cpu/present"; const CPU_SMT_PATH: &str = "/sys/devices/system/cpu/smt/control"; @@ -94,6 +95,7 @@ impl Cpus { } pub fn system_default() -> Self { + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.reset(); let (oc_limits, is_default) = OverclockLimits::load_or_default(); let oc_limits = oc_limits.cpus; let driver = if is_default { crate::persist::DriverJson::SteamDeck } else { crate::persist::DriverJson::SteamDeckAdvance }; @@ -123,6 +125,7 @@ impl Cpus { #[inline] pub fn from_json(mut other: Vec, version: u64) -> Self { + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.reset(); let (oc_limits, is_default) = OverclockLimits::load_or_default(); let oc_limits = oc_limits.cpus; let driver = if is_default { crate::persist::DriverJson::SteamDeck } else { crate::persist::DriverJson::SteamDeckAdvance }; @@ -208,7 +211,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"; impl Cpu { #[inline] @@ -237,21 +239,11 @@ impl Cpu { let mut errors = Vec::new(); // set clock limits - log::debug!("Setting {} to manual", CPU_FORCE_LIMITS_PATH); - let mode: String = usdpl_back::api::files::read_single(CPU_FORCE_LIMITS_PATH.to_owned()).unwrap(); - if mode != "manual" { - // set manual control - usdpl_back::api::files::write_single(CPU_FORCE_LIMITS_PATH, "manual").map_err(|e| { - vec![SettingError { - msg: format!( - "Failed to write `manual` to `{}`: {}", - CPU_FORCE_LIMITS_PATH, e - ), - setting: crate::settings::SettingVariant::Cpu, - }] - })?; - } + //log::debug!("Setting {} to manual", CPU_FORCE_LIMITS_PATH); + //let mode: String = usdpl_back::api::files::read_single(CPU_FORCE_LIMITS_PATH.to_owned()).unwrap(); if let Some(clock_limits) = &self.clock_limits { + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_cpu(true, self.index); + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level()?; log::debug!("Setting CPU {} (min, max) clockspeed to ({}, {})", self.index, clock_limits.min, clock_limits.max); self.state.clock_limits_set = true; // max clock @@ -277,41 +269,54 @@ impl Cpu { setting: crate::settings::SettingVariant::Cpu, }, ).unwrap_or_else(|e| errors.push(e)); - } else if self.state.clock_limits_set || (self.state.is_resuming && !self.limits.skip_resume_reclock) { - self.state.clock_limits_set = false; - // disable manual clock limits - log::debug!("Setting CPU {} to default clockspeed", self.index); - // max clock - let payload_max = format!("p {} 1 {}\n", self.index / 2, self.limits.clock_max.max); - usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_max).map_err( - |e| SettingError { - msg: format!( - "Failed to write `{}` to `{}`: {}", - &payload_max, CPU_CLOCK_LIMITS_PATH, e - ), - setting: crate::settings::SettingVariant::Cpu, - }, - ).unwrap_or_else(|e| errors.push(e)); - // min clock - let payload_min = format!("p {} 0 {}\n", self.index / 2, self.limits.clock_min.min); - usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_min).map_err( - |e| SettingError { - msg: format!( - "Failed to write `{}` to `{}`: {}", - &payload_min, CPU_CLOCK_LIMITS_PATH, e - ), - setting: crate::settings::SettingVariant::Cpu, - }, - ).unwrap_or_else(|e| errors.push(e)); - } - - usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, "c\n") - .unwrap_or_else(|e| { - errors.push(SettingError { - msg: format!("Failed to write `c` to `{}`: {}", CPU_CLOCK_LIMITS_PATH, e), - setting: crate::settings::SettingVariant::Cpu, + usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, "c\n") + .unwrap_or_else(|e| { + errors.push(SettingError { + msg: format!("Failed to write `c` to `{}`: {}", CPU_CLOCK_LIMITS_PATH, e), + setting: crate::settings::SettingVariant::Cpu, + }); }); - }); + } else if self.state.clock_limits_set || + (self.state.is_resuming && !self.limits.skip_resume_reclock) + || POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() { + self.state.clock_limits_set = false; + if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() { + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level()?; + // disable manual clock limits + log::debug!("Setting CPU {} to default clockspeed", self.index); + // max clock + let payload_max = format!("p {} 1 {}\n", self.index / 2, self.limits.clock_max.max); + usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_max).map_err( + |e| SettingError { + msg: format!( + "Failed to write `{}` to `{}`: {}", + &payload_max, CPU_CLOCK_LIMITS_PATH, e + ), + setting: crate::settings::SettingVariant::Cpu, + }, + ).unwrap_or_else(|e| errors.push(e)); + // min clock + let payload_min = format!("p {} 0 {}\n", self.index / 2, self.limits.clock_min.min); + usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, &payload_min).map_err( + |e| SettingError { + msg: format!( + "Failed to write `{}` to `{}`: {}", + &payload_min, CPU_CLOCK_LIMITS_PATH, e + ), + setting: crate::settings::SettingVariant::Cpu, + }, + ).unwrap_or_else(|e| errors.push(e)); + usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, "c\n") + .unwrap_or_else(|e| { + errors.push(SettingError { + msg: format!("Failed to write `c` to `{}`: {}", CPU_CLOCK_LIMITS_PATH, e), + setting: crate::settings::SettingVariant::Cpu, + }); + }); + } + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_cpu(false, self.index); + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level().unwrap_or_else(|mut e| errors.append(&mut e)); + } // commit changes (if no errors have already occured) if errors.is_empty() { usdpl_back::api::files::write_single(CPU_CLOCK_LIMITS_PATH, "c\n").map_err(|e| { diff --git a/backend/src/settings/steam_deck/gpu.rs b/backend/src/settings/steam_deck/gpu.rs index 11179d4..0670cd6 100644 --- a/backend/src/settings/steam_deck/gpu.rs +++ b/backend/src/settings/steam_deck/gpu.rs @@ -6,6 +6,7 @@ use crate::settings::{OnResume, OnSet, SettingError}; use crate::settings::TGpu; use crate::persist::GpuJson; use super::oc_limits::{OverclockLimits, GpuLimits}; +use super::POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT; const SLOW_PPT: u8 = 1; const FAST_PPT: u8 = 2; @@ -23,7 +24,6 @@ pub struct Gpu { // same as CPU const GPU_CLOCK_LIMITS_PATH: &str = "/sys/class/drm/card0/device/pp_od_clk_voltage"; -const GPU_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level"; const GPU_MEMORY_DOWNCLOCK_PATH: &str = "/sys/class/drm/card0/device/pp_dpm_fclk"; impl Gpu { @@ -56,6 +56,8 @@ impl Gpu { fn set_clocks(&mut self) -> Result<(), Vec> { let mut errors = Vec::new(); if let Some(clock_limits) = &self.clock_limits { + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(true); + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level()?; // set clock limits self.state.clock_limits_set = true; // max clock @@ -80,31 +82,50 @@ impl Gpu { setting: crate::settings::SettingVariant::Gpu, }, ).unwrap_or_else(|e| errors.push(e)); - } else if self.state.clock_limits_set || (self.state.is_resuming && !self.limits.skip_resume_reclock) { + usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, "c\n").unwrap_or_else(|e| { + errors.push(SettingError { + msg: format!("Failed to write `c` to `{}`: {}", GPU_CLOCK_LIMITS_PATH, e), + setting: crate::settings::SettingVariant::Gpu, + }) + }); + } else if self.state.clock_limits_set || + (self.state.is_resuming && !self.limits.skip_resume_reclock) + || POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() { self.state.clock_limits_set = false; - // disable manual clock limits - // max clock - let payload_max = format!("s 1 {}\n", self.limits.clock_max.max); - usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_max).map_err( - |e| SettingError { - msg: format!( - "Failed to write `{}` to `{}`: {}", - &payload_max, GPU_CLOCK_LIMITS_PATH, e - ), - setting: crate::settings::SettingVariant::Gpu, - }, - ).unwrap_or_else(|e| errors.push(e)); - // min clock - let payload_min = format!("s 0 {}\n", self.limits.clock_min.min); - usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_min).map_err( - |e| SettingError { - msg: format!( - "Failed to write `{}` to `{}`: {}", - &payload_min, GPU_CLOCK_LIMITS_PATH, e - ), - setting: crate::settings::SettingVariant::Gpu, - }, - ).unwrap_or_else(|e| errors.push(e)); + if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() { + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level()?; + // disable manual clock limits + // max clock + let payload_max = format!("s 1 {}\n", self.limits.clock_max.max); + usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_max).map_err( + |e| SettingError { + msg: format!( + "Failed to write `{}` to `{}`: {}", + &payload_max, GPU_CLOCK_LIMITS_PATH, e + ), + setting: crate::settings::SettingVariant::Gpu, + }, + ).unwrap_or_else(|e| errors.push(e)); + // min clock + let payload_min = format!("s 0 {}\n", self.limits.clock_min.min); + usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, &payload_min).map_err( + |e| SettingError { + msg: format!( + "Failed to write `{}` to `{}`: {}", + &payload_min, GPU_CLOCK_LIMITS_PATH, e + ), + setting: crate::settings::SettingVariant::Gpu, + }, + ).unwrap_or_else(|e| errors.push(e)); + usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, "c\n").unwrap_or_else(|e| { + errors.push(SettingError { + msg: format!("Failed to write `c` to `{}`: {}", GPU_CLOCK_LIMITS_PATH, e), + setting: crate::settings::SettingVariant::Gpu, + }) + }); + } + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(self.slow_memory); + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level().unwrap_or_else(|mut e| errors.append(&mut e)); } if errors.is_empty() { Ok(()) @@ -115,38 +136,42 @@ impl Gpu { fn set_force_performance_related(&mut self) -> Result<(), Vec> { let mut errors = Vec::new(); - // settings using force_performance_level - let mode: String = usdpl_back::api::files::read_single(GPU_FORCE_LIMITS_PATH.to_owned()).unwrap(); - if mode != "manual" { - // set manual control - usdpl_back::api::files::write_single(GPU_FORCE_LIMITS_PATH, "manual").map_err(|e| { - vec![SettingError { - msg: format!( - "Failed to write `manual` to `{}`: {}", - GPU_FORCE_LIMITS_PATH, e - ), - setting: crate::settings::SettingVariant::Gpu, - }] - })?; - } // enable/disable downclock of GPU memory (to 400Mhz?) - usdpl_back::api::files::write_single(GPU_MEMORY_DOWNCLOCK_PATH, self.slow_memory as u8) + if self.slow_memory { + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(true); + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level().unwrap_or_else(|mut e| errors.append(&mut e)); + usdpl_back::api::files::write_single(GPU_MEMORY_DOWNCLOCK_PATH, self.slow_memory as u8) .unwrap_or_else(|e| { errors.push(SettingError { msg: format!("Failed to write to `{}`: {}", GPU_MEMORY_DOWNCLOCK_PATH, e), setting: crate::settings::SettingVariant::Gpu, }); }); + } else if POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.needs_manual() { + usdpl_back::api::files::write_single(GPU_MEMORY_DOWNCLOCK_PATH, self.slow_memory as u8) + .unwrap_or_else(|e| { + errors.push(SettingError { + msg: format!("Failed to write to `{}`: {}", GPU_MEMORY_DOWNCLOCK_PATH, e), + setting: crate::settings::SettingVariant::Gpu, + }); + }); + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.set_gpu(self.clock_limits.is_some()); + POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT.enforce_level().unwrap_or_else(|mut e| errors.append(&mut e)); + } self.set_clocks() .unwrap_or_else(|mut e| errors.append(&mut e)); // commit changes (if no errors have already occured) if errors.is_empty() { - usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, "c\n").map_err(|e| { - vec![SettingError { - msg: format!("Failed to write `c` to `{}`: {}", GPU_CLOCK_LIMITS_PATH, e), - setting: crate::settings::SettingVariant::Gpu, - }] - }) + if self.slow_memory || self.clock_limits.is_some() { + usdpl_back::api::files::write_single(GPU_CLOCK_LIMITS_PATH, "c\n").map_err(|e| { + vec![SettingError { + msg: format!("Failed to write `c` to `{}`: {}", GPU_CLOCK_LIMITS_PATH, e), + setting: crate::settings::SettingVariant::Gpu, + }] + }) + } else { + Ok(()) + } } else { Err(errors) } diff --git a/backend/src/settings/steam_deck/mod.rs b/backend/src/settings/steam_deck/mod.rs index 9c325b6..49481f7 100644 --- a/backend/src/settings/steam_deck/mod.rs +++ b/backend/src/settings/steam_deck/mod.rs @@ -2,10 +2,12 @@ mod battery; mod cpu; mod gpu; mod oc_limits; +mod power_dpm_force; mod util; pub use battery::Battery; pub use cpu::{Cpu, Cpus}; pub use gpu::Gpu; +pub(self) use power_dpm_force::POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT; pub use util::flash_led; diff --git a/backend/src/settings/steam_deck/power_dpm_force.rs b/backend/src/settings/steam_deck/power_dpm_force.rs new file mode 100644 index 0000000..dc23a48 --- /dev/null +++ b/backend/src/settings/steam_deck/power_dpm_force.rs @@ -0,0 +1,107 @@ +//! Be very careful when using this. +//! This influences Steam Deck CPU and GPU driver behaviour, +//! so familiarize yourself with those before messing with this functionality. +//! Refer to https://docs.kernel.org/5.19/gpu/amdgpu/thermal.html for kernel stuff. + +use std::sync::atomic::{AtomicU64, Ordering}; + +use crate::settings::SettingError; + +const DEFAULT_BITS: u64 = 0; + +/// Global usage tracker for the sysfs file by the same name +pub static POWER_DPM_FORCE_PERFORMANCE_LEVEL_MGMT: PDFPLManager = PDFPLManager(AtomicU64::new(DEFAULT_BITS)); + +pub struct PDFPLManager(AtomicU64); + +//const OVERRIDE_BIT: usize = 0; +const GPU_BIT: usize = 1; +const CPU_BITS_START: usize = 2; + +const DPM_FORCE_LIMITS_PATH: &str = "/sys/class/drm/card0/device/power_dpm_force_performance_level"; + +impl PDFPLManager { + #[inline] + fn get(&self) -> u64 { + self.0.load(Ordering::SeqCst) + } + + #[inline] + fn set(&self, val: u64) { + self.0.store(val, Ordering::SeqCst); + } + + #[inline] + fn set_bit(&self, val: bool, bit: usize) { + let bitmask: u64 = !(1 << bit); + let val: u64 = (val as u64) << bit; + let new_val = (self.get() & bitmask) | val; + self.set(new_val); + } + + pub fn set_gpu(&self, manual: bool) { + self.set_bit(manual, GPU_BIT); + } + + pub fn set_cpu(&self, manual: bool, cpu: usize) { + self.set_bit(manual, CPU_BITS_START + cpu); + } + + pub fn needs_manual(&self) -> bool { + self.get() != 0 + } + + pub fn reset(&self) { + self.set(DEFAULT_BITS); + } + + pub fn enforce_level(&self) -> Result<(), Vec> { + let needs = self.needs_manual(); + let mut errors = Vec::new(); + let mode: String = usdpl_back::api::files::read_single(DPM_FORCE_LIMITS_PATH.to_owned()).map_err(|e| { + vec![SettingError { + msg: format!( + "Failed to write `manual` to `{}`: {}", + DPM_FORCE_LIMITS_PATH, e + ), + setting: crate::settings::SettingVariant::General, + }] + })?; + if mode != "manual" && needs { + log::info!("Setting `{}` to manual", DPM_FORCE_LIMITS_PATH); + // set manual control + usdpl_back::api::files::write_single(DPM_FORCE_LIMITS_PATH, "manual").map_err(|e| { + errors.push(SettingError { + msg: format!( + "Failed to write `manual` to `{}`: {}", + DPM_FORCE_LIMITS_PATH, e + ), + setting: crate::settings::SettingVariant::General, + }) + }).unwrap_or(()); + } else if mode != "auto" && !needs { + log::info!("Setting `{}` to auto", DPM_FORCE_LIMITS_PATH); + // unset manual control + usdpl_back::api::files::write_single(DPM_FORCE_LIMITS_PATH, "auto").map_err(|e| { + errors.push(SettingError { + msg: format!( + "Failed to write `auto` to `{}`: {}", + DPM_FORCE_LIMITS_PATH, e + ), + setting: crate::settings::SettingVariant::General, + }) + }).unwrap_or(()); + } + if let Ok(mode_now) = usdpl_back::api::files::read_single::<_, String, _>(DPM_FORCE_LIMITS_PATH.to_owned()) { + log::debug!("Mode for `{}` is now `{}`", DPM_FORCE_LIMITS_PATH, mode_now); + } else { + log::debug!("Error getting new mode for debugging purposes"); + } + + if errors.is_empty() { + Ok(()) + } else { + Err(errors) + } + } +}