From 867b937cd892cbc77a205ab53fcbd69d86b4d07e Mon Sep 17 00:00:00 2001 From: "NGnius (Graham)" Date: Tue, 21 Jan 2025 18:11:59 -0500 Subject: [PATCH] Add missing functionality needed for PowerTools drivers --- crates/procbox/src/combo/amdgpu/common.rs | 5 + crates/procbox/src/combo/amdgpu/gpu.rs | 57 ++- crates/procbox/src/combo/amdgpu/mod.rs | 8 +- .../procbox/src/combo/amdgpu/pp_dpm_star.rs | 8 +- .../src/combo/amdgpu/pp_od_clk_voltage.rs | 345 +++++++++++++++++- crates/procbox/src/cpu/amd.rs | 9 + crates/procbox/src/cpu/basic_general.rs | 4 +- crates/procbox/src/cpu/basic_single.rs | 12 +- 8 files changed, 427 insertions(+), 21 deletions(-) diff --git a/crates/procbox/src/combo/amdgpu/common.rs b/crates/procbox/src/combo/amdgpu/common.rs index 91df31b..756b15f 100644 --- a/crates/procbox/src/combo/amdgpu/common.rs +++ b/crates/procbox/src/combo/amdgpu/common.rs @@ -4,3 +4,8 @@ pub fn write_confirm_pp_od_clk_voltage(sysfs: &BasicEntityPath) -> Result<(), po sysfs.set(super::DEVICE_PP_OD_CLK_VOLTAGE, super::pp_od_clk_voltage::PpOdClkVoltageWriteValues::C.sysfs_str()) .map_err(powerbox::PowerError::Io) } + +pub fn write_reset_pp_od_clk_voltage(sysfs: &BasicEntityPath) -> Result<(), powerbox::PowerError> { + sysfs.set(super::DEVICE_PP_OD_CLK_VOLTAGE, super::pp_od_clk_voltage::PpOdClkVoltageWriteValues::R.sysfs_str()) + .map_err(powerbox::PowerError::Io) +} diff --git a/crates/procbox/src/combo/amdgpu/gpu.rs b/crates/procbox/src/combo/amdgpu/gpu.rs index 610b55b..8406245 100644 --- a/crates/procbox/src/combo/amdgpu/gpu.rs +++ b/crates/procbox/src/combo/amdgpu/gpu.rs @@ -8,14 +8,18 @@ use super::AmdGpu; pub enum AmdGpuOp { PpDpm(super::AmdGpuPpDpm), + PpOdClk(super::AmdGpuPpOdClkVoltage), Commit(AmdGpuCommit), + Reset(AmdGpuReset), } impl PowerOp for AmdGpuOp { fn is_eq_op(&self, other: &Self) -> bool { match self { Self::PpDpm(x) => if let Self::PpDpm(other) = other { x.is_eq_op(other) } else { false }, + Self::PpOdClk(x) => if let Self::PpOdClk(other) = other { x.is_eq_op(other) } else { false }, Self::Commit(_) => matches!(other, Self::Commit(_)), + Self::Reset(_) => matches!(other, Self::Reset(_)), } } } @@ -32,13 +36,20 @@ impl Power for AmdGpu { } fn supported_operations(&self) -> Box> { - // TODO - Box::new(core::iter::empty()) + Box::new( + Power::::supported_operations(self).map(core::convert::Into::::into) + .chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + .chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + .chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + ) } fn act(&self, op: AmdGpuOp) -> Result { match op { - _ => Err(powerbox::PowerError::Unknown), + AmdGpuOp::PpDpm(freq) => self.act(freq).map(powerbox::primitives::Value::into_any), + AmdGpuOp::PpOdClk(clk) => self.act(clk).map(powerbox::primitives::Value::into_any), + AmdGpuOp::Commit(c) => self.act(c).map(|_| powerbox::primitives::Value::Unknown), + AmdGpuOp::Reset(r) => self.act(r).map(|_| powerbox::primitives::Value::Unknown), } } } @@ -85,3 +96,43 @@ impl Into for AmdGpuCommit { } } +pub struct AmdGpuReset; + +impl PowerOp for AmdGpuReset { + fn is_eq_op(&self, _other: &Self) -> bool { + true + } +} + +impl GpuPowerOp for AmdGpuReset {} + +impl Power for AmdGpu { + fn is_on(&self) -> bool { + self.is_enabled() + } + + fn is_available(&self) -> bool { + self.is_compatible() + } + + fn supported_operations(&self) -> Box> { + if self.sysfs.exists(&super::DEVICE_PP_OD_CLK_VOLTAGE) { + Box::new(core::iter::once(AmdGpuReset)) + } else { + Box::new(core::iter::empty()) + } + } + + fn act(&self, _: AmdGpuReset) -> Result<(), powerbox::PowerError> { + super::common::write_reset_pp_od_clk_voltage(&self.sysfs) + } +} + +impl GpuPower for AmdGpu {} + +impl Into for AmdGpuReset { + fn into(self) -> AmdGpuOp { + AmdGpuOp::Reset(self) + } +} + diff --git a/crates/procbox/src/combo/amdgpu/mod.rs b/crates/procbox/src/combo/amdgpu/mod.rs index 82a7704..827d3da 100644 --- a/crates/procbox/src/combo/amdgpu/mod.rs +++ b/crates/procbox/src/combo/amdgpu/mod.rs @@ -10,8 +10,8 @@ mod pp_dpm_star; mod pp_od_clk_voltage; pub use cpus::{AmdGpuCpuOp, AmdGpuCpuFreq, GetCoreMinFrequency, GetCoreMaxFrequency, SetCoreMinFrequency, SetCoreMaxFrequency, SetCoreLimits}; -pub use gpu::{AmdGpuOp, AmdGpuCommit}; -//pub use pp_od_clk_voltage::MinMax; +pub use gpu::{AmdGpuOp, AmdGpuCommit, AmdGpuReset}; +pub use pp_od_clk_voltage::{AmdGpuPpOdInfo, AmdGpuPpOdClkVoltage, AmdGpuPpOdSclk, SetAmdGpuPpOdSclk, GetAmdGpuPpOdSclk}; pub use pp_dpm_star::{AmdGpuPpDpm, AmdGpuFclk, GetAmdGpuFclk, SetAmdGpuFclk, AmdGpuMclk, GetAmdGpuMclk, SetAmdGpuMclk, AmdGpuSclk, GetAmdGpuSclk, SetAmdGpuSclk, AmdGpuSocclk, GetAmdGpuSocclk, SetAmdGpuSocclk}; use sysfuss::{BasicEntityPath, SysEntityAttributesExt}; @@ -39,6 +39,10 @@ impl AmdGpu { } } + pub fn sysfs_path(&self) -> &'_ std::path::Path { + self.sysfs.as_ref() + } + pub(super) fn is_enabled(&self) -> bool { self.sysfs.attribute::(DEVICE_ENABLE).is_ok_and(|val: String| val == "1") } diff --git a/crates/procbox/src/combo/amdgpu/pp_dpm_star.rs b/crates/procbox/src/combo/amdgpu/pp_dpm_star.rs index df6d5c9..c2307f0 100644 --- a/crates/procbox/src/combo/amdgpu/pp_dpm_star.rs +++ b/crates/procbox/src/combo/amdgpu/pp_dpm_star.rs @@ -62,10 +62,16 @@ impl Power>>> for impl GpuPower>>> for AmdGpu {} +impl Into for AmdGpuPpDpm { + fn into(self) -> super::AmdGpuOp { + super::AmdGpuOp::PpDpm(self) + } +} + macro_rules! get_set_gen_impl { ($alias:ident, $getter:ident, $setter:ident, $device_const:ident) => { pub struct $getter; - pub struct $setter(Vec); + pub struct $setter(pub Vec); pub type $alias = GetSet<$getter, $setter>; diff --git a/crates/procbox/src/combo/amdgpu/pp_od_clk_voltage.rs b/crates/procbox/src/combo/amdgpu/pp_od_clk_voltage.rs index ebfe080..d0b7a65 100644 --- a/crates/procbox/src/combo/amdgpu/pp_od_clk_voltage.rs +++ b/crates/procbox/src/combo/amdgpu/pp_od_clk_voltage.rs @@ -1,6 +1,11 @@ use std::{borrow::Cow, collections::HashMap}; -use powerbox::primitives::{ValueMap, ClockFrequency, Selectable, SpacedList, ValueMapParseErr}; +use powerbox::primitives::{ValueMap, Value, ClockFrequency, Selectable, SpacedList, ValueMapParseErr, GetSet}; +use powerbox::{Power, PowerOp, RatifiedPower}; +use sysfuss::SysEntityAttributesExt; + +use crate::gpu::{GpuPower, GpuPowerOp}; +use super::AmdGpu; #[allow(non_camel_case_types)] #[derive(PartialEq, Eq, Hash, Clone)] @@ -55,6 +60,7 @@ impl OdTableName { } } + #[allow(dead_code)] fn table_name(&self) -> Cow<'_, str> { match self { Self::SCLK => Cow::from("OD_SCLK"), @@ -115,10 +121,10 @@ impl std::str::FromStr for PpOdClkVoltageReadValues { let mut table_end = table_start; let table_name = table_name_line.trim_end().trim_end_matches(|c: char| c == ':'); let table_name = OdTableName::from_table_name(table_name); - println!("table name: {}", table_name.table_name()); + //println!("table name: {}", table_name.table_name()); let next_table_name_line; if let OdTableName::RANGE = table_name { - println!("range table special case!"); + //println!("range table special case!"); 'range_table_loop: loop { let line = lines_iter.next(); if let Some(line) = line.and_then(|line| is_valid_range_table_entry_line(line).then_some(line)) { @@ -130,7 +136,7 @@ impl std::str::FromStr for PpOdClkVoltageReadValues { } let table_str = &s[table_start..table_end-1]; - println!("table str: '{}'", table_str); + //println!("table str: '{}'", table_str); match table_str.parse() { Ok(r_t) => range_table = Some(r_t), Err(ValueMapParseErr::InnerVal(v)) => return Err(ValueMapParseErr::InnerVal(v)), @@ -150,7 +156,7 @@ impl std::str::FromStr for PpOdClkVoltageReadValues { } let table_str = &s[table_start..table_end-1]; - println!("table str: '{}'", table_str); + //println!("table str: '{}'", table_str); let new_table = table_str.parse()?; tables.insert(table_name, new_table); table_start = table_end; @@ -191,13 +197,15 @@ pub(super) enum MinMax { pub(super) enum PpOdClkVoltageWriteValues { /// Something clock SCLK { - limit: MinMax, + point: usize, clockspeed: ClockFrequency, + millivolts: Option, }, /// Memory clock MCLK { - limit: MinMax, + point: usize, clockspeed: ClockFrequency, + millivolts: usize, }, /// Core clock CCLK { @@ -217,17 +225,24 @@ pub(super) enum PpOdClkVoltageWriteValues { }, /// Confirm (required to apply another other written value) C, + /// Reset all related settings + R, } impl PpOdClkVoltageWriteValues { pub fn sysfs_str(&self) -> String { match self { - Self::SCLK { limit, clockspeed } => format!("s {} {}", Self::limit_num(limit), Self::mhz_clock(clockspeed)), - Self::MCLK { limit, clockspeed } => format!("m {} {}", Self::limit_num(limit), Self::mhz_clock(clockspeed)), + Self::SCLK { point, clockspeed, millivolts } => if let Some(millivolts) = millivolts { + format!("s {} {} {}", point, Self::mhz_clock(clockspeed), millivolts) + } else { + format!("s {} {}", point, Self::mhz_clock(clockspeed)) + }, + Self::MCLK { point, clockspeed, millivolts } => format!("m {} {} {}", point, Self::mhz_clock(clockspeed), millivolts), Self::CCLK { core, limit, clockspeed } => format!("p {} {} {}", core, Self::limit_num(limit), Self::mhz_clock(clockspeed)), Self::VC { point, clockspeed, millivolts } => format!("vc {} {} {}", point, Self::mhz_clock(clockspeed), millivolts), Self::VO { millivolts } => format!("vo {}", millivolts), Self::C => "c".to_owned(), + Self::R => "r".to_owned(), } } @@ -247,6 +262,318 @@ impl PpOdClkVoltageWriteValues { } } +// Power impls + +pub struct AmdGpuPpOdInfo { + pub range: Option>, + pub levels: ValueMap>, +} + +macro_rules! get_set_gen_impl { + ($alias:ident, $getter:ident, $setter:ident, $table:ident, $variant:ident) => { + pub struct $getter; + + pub type $alias = GetSet<$getter, $setter>; + + // GetSet impl + impl GpuPowerOp for $alias {} + + impl Power<$alias, Value> for AmdGpu { + fn is_on(&self) -> bool { + self.is_enabled() + } + + fn is_available(&self) -> bool { + self.is_compatible() + } + + fn supported_operations(&self) -> Box> { + Box::new( + Power::<$getter, _>::supported_operations(self).map(core::convert::Into::<$alias>::into) + .chain(Power::<$setter, _>::supported_operations(self).map(core::convert::Into::<$alias>::into)) + ) + } + + fn act(&self, op: $alias) -> Result, powerbox::PowerError> { + match op { + $alias::Get(clk) => self.act(clk).map(Value::Custom), + $alias::Set(clk) => self.act(clk).map(|_| Value::Unknown), + } + } + } + + impl GpuPower<$alias, Value> for AmdGpu {} + + impl core::convert::Into for $alias { + fn into(self) -> AmdGpuPpOdClkVoltage { + AmdGpuPpOdClkVoltage::$variant(self) + } + } + + // Get impl + impl PowerOp for $getter { + fn is_eq_op(&self, _: &Self) -> bool { + true + } + } + + impl GpuPowerOp for $getter {} + + impl Power<$getter, AmdGpuPpOdInfo> for AmdGpu { + fn is_on(&self) -> bool { + self.is_enabled() + } + + fn is_available(&self) -> bool { + self.is_compatible() + } + + fn supported_operations(&self) -> Box> { + if let Ok(rv) = self.read_pp_od_clk_voltage() { + if rv.tables.contains_key(&OdTableName::$table) { + Box::new(core::iter::once($getter)) + } else { + Box::new(core::iter::empty()) + } + } else { Box::new(core::iter::empty()) } + } + + fn act(&self, _: $getter) -> Result { + match self.read_pp_od_clk_voltage() { + Ok(mut rv) => { + Ok(AmdGpuPpOdInfo { + range: rv.ranges.map(|mut ranges| ranges.0.remove(&OdTableName::$table)).flatten(), + levels: rv.tables.remove(&OdTableName::$table).unwrap_or_else(|| ValueMap(Default::default())) + }) + }, + Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)), + Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown), + } + } + } + + impl GpuPower<$getter, AmdGpuPpOdInfo> for AmdGpu {} + + impl core::convert::Into<$alias> for $getter { + fn into(self) -> $alias { + $alias::Get(self) + } + } + + // Set impl + impl PowerOp for $setter { + fn is_eq_op(&self, _: &Self) -> bool { + true + } + } + + impl GpuPowerOp for $setter {} + + impl Power<$setter, ()> for AmdGpu { + fn is_on(&self) -> bool { + self.is_enabled() + } + + fn is_available(&self) -> bool { + self.is_compatible() + } + + fn supported_operations(&self) -> Box> { + if let Ok(rv) = self.read_pp_od_clk_voltage() { + if rv.ranges.is_some_and(|ranges| ranges.0.contains_key(&OdTableName::$table)) { + Box::new(core::iter::once($setter::filler_default())) + } else { + Box::new(core::iter::empty()) + } + } else { Box::new(core::iter::empty()) } + } + + fn act(&self, op: $setter) -> Result<(), powerbox::PowerError> { + let payload = op.write_payload(); + if !self.is_pdfpl_manual() { + self.sysfs.set( + super::DEVICE_POWER_DPM_FORCE_LIMITS_ATTRIBUTE, + super::power_dpm_force_performance_level::PowerDpmForcePerformanceLevel::Manual.sysfs_str()) + .map_err(powerbox::PowerError::Io)?; + } + self.sysfs.set(super::DEVICE_PP_OD_CLK_VOLTAGE, payload.sysfs_str()) + .map_err(powerbox::PowerError::Io)?; + if op.should_commit() { + super::common::write_confirm_pp_od_clk_voltage(&self.sysfs) + } else { + Ok(()) + } + } + } + + impl RatifiedPower<$setter> for AmdGpu { + fn is_possible(&self, op: &$setter) -> bool { + if let Ok(gotten) = self.act($getter) { + op.is_within_range(&gotten) + } else { + false + } + } + + fn clamp(&self, op: &mut $setter) -> bool { + if let Ok(gotten) = self.act($getter) { + op.clamp_to(&gotten); + true + } else { + false + } + } + } + + impl GpuPower<$setter, ()> for AmdGpu {} + + impl core::convert::Into<$alias> for $setter { + fn into(self) -> $alias { + $alias::Set(self) + } + } + }; +} + +pub enum AmdGpuPpOdClkVoltage { + Sclk(AmdGpuPpOdSclk), + //MCLK(AmdGpuPpOdMclk), + //VC(AmdGpuPpOdVc), + //VO(AmdGpuPpOdVo), +} + +impl PowerOp for AmdGpuPpOdClkVoltage { + fn is_eq_op(&self, other: &Self) -> bool { + #[allow(irrefutable_let_patterns)] + match self { + //Self::Fclk(f) => if let Self::Fclk(other) = other { f.is_eq_op(other) } else { false }, + //Self::Mclk(m) => if let Self::Mclk(other) = other { m.is_eq_op(other) } else { false }, + Self::Sclk(s) => if let Self::Sclk(other) = other { s.is_eq_op(other) } else { false }, + //Self::Socclk(soc) => if let Self::Socclk(other) = other { soc.is_eq_op(other) } else { false }, + } + } +} + +impl GpuPowerOp for AmdGpuPpOdClkVoltage {} + +impl Power> for AmdGpu { + fn is_on(&self) -> bool { + self.is_enabled() + } + + fn is_available(&self) -> bool { + self.is_compatible() + } + + fn supported_operations(&self) -> Box> { + Box::new( + Power::::supported_operations(self).map(core::convert::Into::::into) + //.chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + //.chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + //.chain(Power::::supported_operations(self).map(core::convert::Into::::into)) + ) + } + + fn act(&self, op: AmdGpuPpOdClkVoltage) -> Result, powerbox::PowerError> { + match op { + //AmdGpuPpOdClkVoltage::Fclk(clk) => self.act(clk), + //AmdGpuPpOdClkVoltage::Mclk(clk) => self.act(clk), + AmdGpuPpOdClkVoltage::Sclk(clk) => self.act(clk), + //AmdGpuPpOdClkVoltage::Socclk(clk) => self.act(clk), + } + } +} + +impl GpuPower> for AmdGpu {} + +impl Into for AmdGpuPpOdClkVoltage { + fn into(self) -> super::AmdGpuOp { + super::AmdGpuOp::PpOdClk(self) + } +} + +pub struct SetAmdGpuPpOdSclk { + pub level: usize, + pub clockspeed: ClockFrequency, + pub millivolts: Option, + pub no_commit: bool, +} + +impl SetAmdGpuPpOdSclk { + fn filler_default() -> Self { + Self { + level: 0, + clockspeed: ClockFrequency { + value: 1_000, + si_prefix: 1_000_000, + }, + millivolts: None, + no_commit: false, + } + } + + fn write_payload(&self) -> PpOdClkVoltageWriteValues { + PpOdClkVoltageWriteValues::SCLK { + point: self.level, + clockspeed: self.clockspeed, + millivolts: self.millivolts, + } + } + + #[inline] + fn should_commit(&self) -> bool { + !self.no_commit + } + + fn is_within_range(&self, get: &AmdGpuPpOdInfo) -> bool { + get.levels.0.contains_key(&self.level) + && get.range.as_ref().is_some_and(|range| + !range.0.is_empty() + && self.clockspeed.in_hz() > range.0[0].in_hz() + && self.clockspeed.in_hz() > range.0[range.0.len()-1].in_hz() + ) + } + + fn clamp_to(&mut self, get: &AmdGpuPpOdInfo) { + if !get.levels.0.contains_key(&self.level) { + let min = get.levels.0.keys().min().map(|x| *x).unwrap_or_default(); + let max = get.levels.0.keys().max().map(|x| *x).unwrap_or_default(); + if self.level > max { + self.level = max; + } else { + self.level = min; + } + } + } +} + +get_set_gen_impl!(AmdGpuPpOdSclk, GetAmdGpuPpOdSclk, SetAmdGpuPpOdSclk, SCLK, Sclk); + +/* +// System/engine clock (SCLK) +pub struct EngineClock { + limit: MinMax, + clockspeed: ClockFrequency, +} + +/// Memory clock (MCLK) +pub struct MemoryClock { + limit: MinMax, + clockspeed: ClockFrequency, +} + +/// Voltage curve (VC) +pub struct VoltageCurve { + point: usize, + clockspeed: ClockFrequency, + millivolts: usize, +} + +/// Voltage offset (VO) +pub struct VoltageOffset { + millivolts: isize, +}*/ + #[cfg(test)] mod test { use super::*; diff --git a/crates/procbox/src/cpu/amd.rs b/crates/procbox/src/cpu/amd.rs index 01bdded..945572e 100644 --- a/crates/procbox/src/cpu/amd.rs +++ b/crates/procbox/src/cpu/amd.rs @@ -31,6 +31,15 @@ impl AmdCpu { #[cfg(not(any(target_arch = "x86_64", target_arch = "x86")))] {false} } + + pub fn as_basic(&self) -> &'_ BasicCpu { + &self.basic + } + + /// Create an AmdCpu without checking if it's compatible (use try_into() instead) + pub fn new(basic: BasicCpu) -> Self { + Self { basic, } + } } impl core::ops::Deref for AmdCpu { diff --git a/crates/procbox/src/cpu/basic_general.rs b/crates/procbox/src/cpu/basic_general.rs index a1dd886..81932d2 100644 --- a/crates/procbox/src/cpu/basic_general.rs +++ b/crates/procbox/src/cpu/basic_general.rs @@ -140,7 +140,7 @@ impl Power for BasicCpus { } fn is_available(&self) -> bool { - self.is_exists() + self.sysfs.exists(&CPUS_SMT_CONTROL) } fn supported_operations(&self) -> Box> { @@ -195,7 +195,7 @@ impl Power for BasicCpus { } fn is_available(&self) -> bool { - self.is_exists() + self.sysfs.exists(&CPUS_SMT_ACTIVE) } fn supported_operations(&self) -> Box> { diff --git a/crates/procbox/src/cpu/basic_single.rs b/crates/procbox/src/cpu/basic_single.rs index 9cbd020..12aa4fb 100644 --- a/crates/procbox/src/cpu/basic_single.rs +++ b/crates/procbox/src/cpu/basic_single.rs @@ -49,6 +49,10 @@ impl BasicCpu { index, } } + + pub fn sysfs_path(&self) -> &std::path::Path { + self.sysfs.as_ref() + } } fn parse_cpu_index(path: &str) -> Option { @@ -611,7 +615,7 @@ impl core::convert::Into for GetEnergyPerfPreference { } } -pub struct SetEnergyPerfPreference(String); +pub struct SetEnergyPerfPreference(pub String); impl PowerOp for SetEnergyPerfPreference { fn is_eq_op(&self, _: &Self) -> bool { @@ -906,7 +910,7 @@ impl core::convert::Into for GetScalingGovernor { } } -pub struct SetScalingGovernor(String); +pub struct SetScalingGovernor(pub String); impl PowerOp for SetScalingGovernor { fn is_eq_op(&self, _: &Self) -> bool { @@ -1024,7 +1028,7 @@ impl core::convert::Into for GetScalingMaxFreq { } } -pub struct SetScalingMaxFreq(usize); +pub struct SetScalingMaxFreq(pub usize); impl PowerOp for SetScalingMaxFreq { fn is_eq_op(&self, _: &Self) -> bool { @@ -1142,7 +1146,7 @@ impl core::convert::Into for GetScalingMinFreq { } } -pub struct SetScalingMinFreq(usize); +pub struct SetScalingMinFreq(pub usize); impl PowerOp for SetScalingMinFreq { fn is_eq_op(&self, _: &Self) -> bool {