Add amdgpu pp_dpm_* interfaces
This commit is contained in:
parent
60d038c79f
commit
c9f2f2849e
10 changed files with 345 additions and 3 deletions
|
@ -5,7 +5,7 @@ mod power_error;
|
|||
pub use power_error::PowerError;
|
||||
|
||||
mod power_trait;
|
||||
pub use power_trait::{Power, PowerOp, RatifiedPower};
|
||||
pub use power_trait::{Power, PowerOp, RatifiedPower, ManualRatifiedPower};
|
||||
|
||||
pub mod primitives;
|
||||
|
||||
|
|
|
@ -28,3 +28,12 @@ pub trait RatifiedPower<OP: PowerOp> {
|
|||
/// Returns false if that is not possible.
|
||||
fn clamp(&self, op: &mut OP) -> bool;
|
||||
}
|
||||
|
||||
/// Power control operation validation with provided limits
|
||||
pub trait ManualRatifiedPower<OP: PowerOp, L: ?Sized> {
|
||||
/// Is this operation within the provided limits?
|
||||
fn is_possible(&self, op: &OP, limits: &L) -> bool;
|
||||
/// Set operation parameters to nearest allowed values of the provided limits.
|
||||
/// Returns false if that is not possible
|
||||
fn clamp(&self, op: &mut OP, limits: &L) -> bool;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,21 @@ impl ClockFrequency {
|
|||
pub fn in_hz(&self) -> usize {
|
||||
self.value * self.si_prefix
|
||||
}
|
||||
|
||||
/// Create a clock frequency from a raw clock speed number, in Hz
|
||||
pub fn from_hz(hz: usize) -> Self {
|
||||
if hz > 1_000_000 {
|
||||
Self {
|
||||
value: hz / 1_000_000,
|
||||
si_prefix: 1_000_000,
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
value: hz,
|
||||
si_prefix: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clock speed parse errors
|
||||
|
@ -91,6 +106,26 @@ fn si_prefix_char_to_number(si_prefix: char) -> Option<usize> {
|
|||
}
|
||||
}
|
||||
|
||||
impl core::cmp::PartialOrd for ClockFrequency {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
if other.si_prefix == self.si_prefix {
|
||||
self.value.partial_cmp(&other.value)
|
||||
} else {
|
||||
self.in_hz().partial_cmp(&other.in_hz())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl core::cmp::Ord for ClockFrequency {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
if other.si_prefix == self.si_prefix {
|
||||
self.value.cmp(&other.value)
|
||||
} else {
|
||||
self.in_hz().cmp(&other.in_hz())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::str::FromStr;
|
||||
|
|
16
crates/core/src/primitives/get_set.rs
Normal file
16
crates/core/src/primitives/get_set.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
/// Getter and setter power operation
|
||||
pub enum GetSet<G, S> {
|
||||
/// Get power operation
|
||||
Get(G),
|
||||
/// Set power operation
|
||||
Set(S),
|
||||
}
|
||||
|
||||
impl <G: crate::PowerOp, S: crate::PowerOp> crate::PowerOp for GetSet<G, S> {
|
||||
fn is_eq_op(&self, other: &Self) -> bool {
|
||||
match self {
|
||||
Self::Get(getter) => if let Self::Get(other) = other { getter.is_eq_op(other) } else { false },
|
||||
Self::Set(setter) => if let Self::Set(other) = other { setter.is_eq_op(other) } else { false },
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,3 +17,6 @@ pub use space_separated_list::SpacedList;
|
|||
|
||||
mod value;
|
||||
pub use value::Value;
|
||||
|
||||
mod get_set;
|
||||
pub use get_set::GetSet;
|
||||
|
|
|
@ -17,6 +17,21 @@ impl <T: FromStr> FromStr for SpacedList<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl <T: core::fmt::Display> core::fmt::Display for SpacedList<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
if self.0.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let mut vec_iter = self.0.iter();
|
||||
let mut now = vec_iter.next().unwrap();
|
||||
while let Some(next) = vec_iter.next() {
|
||||
write!(f, "{} ", now)?;
|
||||
now = next;
|
||||
}
|
||||
write!(f, "{}", now)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
@ -38,4 +53,10 @@ mod test {
|
|||
let strings: SpacedList<usize> = "1 2 3 4\t5\t\t \t6 7 8 9 10 11".parse().expect("fail");
|
||||
assert_eq!(strings.0, vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn print_list() {
|
||||
let numbers = SpacedList(vec![0, 1, 4, 5]);
|
||||
assert_eq!(numbers.to_string(), "0 1 4 5");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! CPU power management implementation
|
||||
|
||||
use powerbox::{Power, PowerOp, RatifiedPower};
|
||||
use powerbox::{ManualRatifiedPower, Power, PowerOp, RatifiedPower};
|
||||
use powerbox::primitives::ClockFrequency;
|
||||
use sysfuss::SysEntityAttributesExt;
|
||||
|
||||
|
@ -174,6 +174,13 @@ impl Into<AmdGpuCpuFreq> for GetCoreMaxFrequency {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct SetCoreLimits {
|
||||
pub core_min: usize,
|
||||
pub core_max: usize,
|
||||
pub clock_min: ClockFrequency,
|
||||
pub clock_max: ClockFrequency,
|
||||
}
|
||||
|
||||
pub struct SetCoreMinFrequency {
|
||||
pub core: usize,
|
||||
pub clock: ClockFrequency,
|
||||
|
@ -278,6 +285,22 @@ impl RatifiedPower<SetCoreMinFrequency> for AmdGpu {
|
|||
}
|
||||
}
|
||||
|
||||
impl ManualRatifiedPower<SetCoreMinFrequency, SetCoreLimits> for AmdGpu {
|
||||
fn is_possible(&self, op: &SetCoreMinFrequency, limits: &SetCoreLimits) -> bool {
|
||||
let clock_hz = op.clock.in_hz();
|
||||
op.core >= limits.core_min
|
||||
&& op.core <= limits.core_max
|
||||
&& clock_hz >= limits.clock_min.in_hz()
|
||||
&& clock_hz <= limits.clock_max.in_hz()
|
||||
}
|
||||
|
||||
fn clamp(&self, op: &mut SetCoreMinFrequency, limits: &SetCoreLimits) -> bool {
|
||||
op.core = op.core.clamp(limits.core_min, limits.core_max);
|
||||
op.clock = op.clock.clamp(limits.clock_min, limits.clock_max);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<AmdGpuCpuFreq> for SetCoreMinFrequency {
|
||||
fn into(self) -> AmdGpuCpuFreq {
|
||||
AmdGpuCpuFreq::SetMin(self)
|
||||
|
@ -376,6 +399,22 @@ impl RatifiedPower<SetCoreMaxFrequency> for AmdGpu {
|
|||
}
|
||||
}
|
||||
|
||||
impl ManualRatifiedPower<SetCoreMaxFrequency, SetCoreLimits> for AmdGpu {
|
||||
fn is_possible(&self, op: &SetCoreMaxFrequency, limits: &SetCoreLimits) -> bool {
|
||||
let clock_hz = op.clock.in_hz();
|
||||
op.core >= limits.core_min
|
||||
&& op.core <= limits.core_max
|
||||
&& clock_hz >= limits.clock_min.in_hz()
|
||||
&& clock_hz <= limits.clock_max.in_hz()
|
||||
}
|
||||
|
||||
fn clamp(&self, op: &mut SetCoreMaxFrequency, limits: &SetCoreLimits) -> bool {
|
||||
op.core = op.core.clamp(limits.core_min, limits.core_max);
|
||||
op.clock = op.clock.clamp(limits.clock_min, limits.clock_max);
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<AmdGpuCpuFreq> for SetCoreMaxFrequency {
|
||||
fn into(self) -> AmdGpuCpuFreq {
|
||||
AmdGpuCpuFreq::SetMax(self)
|
||||
|
|
|
@ -7,12 +7,14 @@ use crate::gpu::{GpuPower, GpuPowerOp};
|
|||
use super::AmdGpu;
|
||||
|
||||
pub enum AmdGpuOp {
|
||||
PpDpm(super::AmdGpuPpDpm),
|
||||
Commit(AmdGpuCommit),
|
||||
}
|
||||
|
||||
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::Commit(_) => matches!(other, Self::Commit(_)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,11 +6,13 @@ mod common;
|
|||
mod cpus;
|
||||
mod gpu;
|
||||
mod power_dpm_force_performance_level;
|
||||
mod pp_dpm_star;
|
||||
mod pp_od_clk_voltage;
|
||||
|
||||
pub use cpus::{AmdGpuCpuOp, AmdGpuCpuFreq, GetCoreMinFrequency, GetCoreMaxFrequency, SetCoreMinFrequency, SetCoreMaxFrequency};
|
||||
pub use cpus::{AmdGpuCpuOp, AmdGpuCpuFreq, GetCoreMinFrequency, GetCoreMaxFrequency, SetCoreMinFrequency, SetCoreMaxFrequency, SetCoreLimits};
|
||||
pub use gpu::{AmdGpuOp, AmdGpuCommit};
|
||||
//pub use pp_od_clk_voltage::MinMax;
|
||||
pub use pp_dpm_star::{AmdGpuPpDpm, AmdGpuFclk, GetAmdGpuFclk, SetAmdGpuFclk, AmdGpuMclk, GetAmdGpuMclk, SetAmdGpuMclk, AmdGpuSclk, GetAmdGpuSclk, SetAmdGpuSclk, AmdGpuSocclk, GetAmdGpuSocclk, SetAmdGpuSocclk};
|
||||
|
||||
use sysfuss::{BasicEntityPath, SysEntityAttributesExt};
|
||||
|
||||
|
|
215
crates/procbox/src/combo/amdgpu/pp_dpm_star.rs
Normal file
215
crates/procbox/src/combo/amdgpu/pp_dpm_star.rs
Normal file
|
@ -0,0 +1,215 @@
|
|||
use powerbox::primitives::{GetSet, ClockFrequency, Value, ValueMap, Selectable, SpacedList};
|
||||
use powerbox::{Power, PowerOp};
|
||||
use sysfuss::SysEntityAttributesExt;
|
||||
|
||||
use crate::gpu::{GpuPower, GpuPowerOp};
|
||||
use super::AmdGpu;
|
||||
|
||||
//const DEVICE_PP_DPM_DCEFCLK: &str = "device/pp_dpm_dcefclk";
|
||||
const DEVICE_PP_DPM_FCLK: &str = "device/pp_dpm_fclk";
|
||||
const DEVICE_PP_DPM_MCLK: &str = "device/pp_dpm_mclk";
|
||||
//const DEVICE_PP_DPM_PCIE: &str = "device/pp_dpm_pcie";
|
||||
const DEVICE_PP_DPM_SCLK: &str = "device/pp_dpm_sclk";
|
||||
const DEVICE_PP_DPM_SOCCLK: &str = "device/pp_dpm_socclk";
|
||||
|
||||
pub enum AmdGpuPpDpm {
|
||||
Fclk(AmdGpuFclk),
|
||||
Mclk(AmdGpuMclk),
|
||||
Sclk(AmdGpuSclk),
|
||||
Socclk(AmdGpuSocclk),
|
||||
}
|
||||
|
||||
impl PowerOp for AmdGpuPpDpm {
|
||||
fn is_eq_op(&self, other: &Self) -> bool {
|
||||
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 AmdGpuPpDpm {}
|
||||
|
||||
impl Power<AmdGpuPpDpm, Value<ValueMap<usize, Selectable<ClockFrequency>>>> for AmdGpu {
|
||||
fn is_on(&self) -> bool {
|
||||
self.is_enabled()
|
||||
}
|
||||
|
||||
fn is_available(&self) -> bool {
|
||||
self.is_compatible()
|
||||
}
|
||||
|
||||
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=AmdGpuPpDpm>> {
|
||||
Box::new(
|
||||
Power::<AmdGpuFclk, _>::supported_operations(self).map(core::convert::Into::<AmdGpuPpDpm>::into)
|
||||
.chain(Power::<AmdGpuMclk, _>::supported_operations(self).map(core::convert::Into::<AmdGpuPpDpm>::into))
|
||||
.chain(Power::<AmdGpuSclk, _>::supported_operations(self).map(core::convert::Into::<AmdGpuPpDpm>::into))
|
||||
.chain(Power::<AmdGpuSocclk, _>::supported_operations(self).map(core::convert::Into::<AmdGpuPpDpm>::into))
|
||||
)
|
||||
}
|
||||
|
||||
fn act(&self, op: AmdGpuPpDpm) -> Result<Value<ValueMap<usize, Selectable<ClockFrequency>>>, powerbox::PowerError> {
|
||||
match op {
|
||||
AmdGpuPpDpm::Fclk(clk) => self.act(clk),
|
||||
AmdGpuPpDpm::Mclk(clk) => self.act(clk),
|
||||
AmdGpuPpDpm::Sclk(clk) => self.act(clk),
|
||||
AmdGpuPpDpm::Socclk(clk) => self.act(clk),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GpuPower<AmdGpuPpDpm, Value<ValueMap<usize, Selectable<ClockFrequency>>>> for AmdGpu {}
|
||||
|
||||
macro_rules! get_set_gen_impl {
|
||||
($alias:ident, $getter:ident, $setter:ident, $device_const:ident) => {
|
||||
pub struct $getter;
|
||||
pub struct $setter(Vec<usize>);
|
||||
|
||||
pub type $alias = GetSet<$getter, $setter>;
|
||||
|
||||
// GetSet impl
|
||||
impl GpuPowerOp for $alias {}
|
||||
|
||||
impl Power<$alias, Value<ValueMap<usize, Selectable<ClockFrequency>>>> for AmdGpu {
|
||||
fn is_on(&self) -> bool {
|
||||
self.is_enabled()
|
||||
}
|
||||
|
||||
fn is_available(&self) -> bool {
|
||||
self.is_compatible()
|
||||
}
|
||||
|
||||
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=$alias>> {
|
||||
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<Value<ValueMap<usize, Selectable<ClockFrequency>>>, 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<ValueMap<usize, Selectable<ClockFrequency>>>> for AmdGpu {}
|
||||
|
||||
// Get impl
|
||||
impl PowerOp for $getter {
|
||||
fn is_eq_op(&self, _: &Self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl GpuPowerOp for $getter {}
|
||||
|
||||
impl Power<$getter, ValueMap<usize, Selectable<ClockFrequency>>> for AmdGpu {
|
||||
fn is_on(&self) -> bool {
|
||||
self.is_enabled()
|
||||
}
|
||||
|
||||
fn is_available(&self) -> bool {
|
||||
self.is_compatible()
|
||||
}
|
||||
|
||||
fn supported_operations(&self) -> Box<dyn core::iter::Iterator<Item=$getter>> {
|
||||
if self.sysfs.exists(&$device_const) { Box::new(core::iter::once($getter)) } else { Box::new(core::iter::empty()) }
|
||||
}
|
||||
|
||||
fn act(&self, _: $getter) -> Result<ValueMap<usize, Selectable<ClockFrequency>>, powerbox::PowerError> {
|
||||
if self.sysfs.exists(&$device_const) {
|
||||
match self.sysfs.attribute::<ValueMap<usize, Selectable<ClockFrequency>>>($device_const) {
|
||||
Ok(vm) => Ok(vm),
|
||||
Err(sysfuss::EitherErr2::First(e)) => Err(powerbox::PowerError::Io(e)),
|
||||
Err(sysfuss::EitherErr2::Second(_)) => Err(powerbox::PowerError::Unknown),
|
||||
}
|
||||
} else {
|
||||
Err(powerbox::PowerError::Unknown)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GpuPower<$getter, ValueMap<usize, Selectable<ClockFrequency>>> 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<dyn core::iter::Iterator<Item=$setter>> {
|
||||
if self.sysfs.exists(&$device_const) { Box::new(core::iter::once($setter(Vec::new()))) } else { Box::new(core::iter::empty()) }
|
||||
}
|
||||
|
||||
fn act(&self, op: $setter) -> Result<(), powerbox::PowerError> {
|
||||
if self.sysfs.exists(&$device_const) {
|
||||
self.sysfs.set($device_const, SpacedList(op.0).to_string()).map_err(powerbox::PowerError::Io)
|
||||
} else {
|
||||
Err(powerbox::PowerError::Unknown)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GpuPower<$setter, ()> for AmdGpu {}
|
||||
|
||||
impl core::convert::Into<$alias> for $setter {
|
||||
fn into(self) -> $alias {
|
||||
$alias::Set(self)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
get_set_gen_impl!(AmdGpuFclk, GetAmdGpuFclk, SetAmdGpuFclk, DEVICE_PP_DPM_FCLK);
|
||||
|
||||
impl core::convert::Into<AmdGpuPpDpm> for AmdGpuFclk {
|
||||
fn into(self) -> AmdGpuPpDpm {
|
||||
AmdGpuPpDpm::Fclk(self)
|
||||
}
|
||||
}
|
||||
|
||||
get_set_gen_impl!(AmdGpuMclk, GetAmdGpuMclk, SetAmdGpuMclk, DEVICE_PP_DPM_MCLK);
|
||||
|
||||
impl core::convert::Into<AmdGpuPpDpm> for AmdGpuMclk {
|
||||
fn into(self) -> AmdGpuPpDpm {
|
||||
AmdGpuPpDpm::Mclk(self)
|
||||
}
|
||||
}
|
||||
|
||||
get_set_gen_impl!(AmdGpuSclk, GetAmdGpuSclk, SetAmdGpuSclk, DEVICE_PP_DPM_SCLK);
|
||||
|
||||
impl core::convert::Into<AmdGpuPpDpm> for AmdGpuSclk {
|
||||
fn into(self) -> AmdGpuPpDpm {
|
||||
AmdGpuPpDpm::Sclk(self)
|
||||
}
|
||||
}
|
||||
|
||||
get_set_gen_impl!(AmdGpuSocclk, GetAmdGpuSocclk, SetAmdGpuSocclk, DEVICE_PP_DPM_SOCCLK);
|
||||
|
||||
impl core::convert::Into<AmdGpuPpDpm> for AmdGpuSocclk {
|
||||
fn into(self) -> AmdGpuPpDpm {
|
||||
AmdGpuPpDpm::Socclk(self)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue