Only apply manual power_dpm_force_performance_level when actually necessary #87

This commit is contained in:
NGnius (Graham) 2023-03-27 20:24:21 -04:00
parent 69f6b800be
commit 27afa31520
4 changed files with 234 additions and 95 deletions

View file

@ -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<CpuJson>, 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| {

View file

@ -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<SettingError>> {
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<SettingError>> {
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)
}

View file

@ -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;

View file

@ -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<SettingError>> {
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)
}
}
}